diff options
Diffstat (limited to 'linux/drivers')
73 files changed, 5520 insertions, 2981 deletions
diff --git a/linux/drivers/media/Kconfig b/linux/drivers/media/Kconfig index 9f7e1fe8c..87410dbd3 100644 --- a/linux/drivers/media/Kconfig +++ b/linux/drivers/media/Kconfig @@ -67,6 +67,7 @@ source "drivers/media/common/Kconfig" config VIDEO_TUNER tristate + depends on I2C config VIDEO_BUF tristate @@ -82,6 +83,7 @@ config VIDEO_IR config VIDEO_TVEEPROM tristate + depends on I2C config USB_DABUSB tristate "DABUSB driver" diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c index 4744950bf..2006c5578 100644 --- a/linux/drivers/media/common/ir-keymaps.c +++ b/linux/drivers/media/common/ir-keymaps.c @@ -1595,3 +1595,58 @@ IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_norwood); + +/* From reading the following remotes: + * Zenith Universal 7 / TV Mode 807 / VCR Mode 837 + * Hauppauge (from NOVA-CI-s box product) + * This is a "middle of the road" approach, differences are noted + */ +IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE] = { + [ 0x00 ] = KEY_0, + [ 0x01 ] = KEY_1, + [ 0x02 ] = KEY_2, + [ 0x03 ] = KEY_3, + [ 0x04 ] = KEY_4, + [ 0x05 ] = KEY_5, + [ 0x06 ] = KEY_6, + [ 0x07 ] = KEY_7, + [ 0x08 ] = KEY_8, + [ 0x09 ] = KEY_9, + [ 0x0a ] = KEY_ENTER, + [ 0x0b ] = KEY_RED, + [ 0x0c ] = KEY_POWER, /* RADIO on Hauppauge */ + [ 0x0d ] = KEY_MUTE, + [ 0x0f ] = KEY_A, /* TV on Hauppauge */ + [ 0x10 ] = KEY_VOLUMEUP, + [ 0x11 ] = KEY_VOLUMEDOWN, + [ 0x14 ] = KEY_B, + [ 0x1c ] = KEY_UP, + [ 0x1d ] = KEY_DOWN, + [ 0x1e ] = KEY_OPTION, /* RESERVED on Hauppauge */ + [ 0x1f ] = KEY_BREAK, + [ 0x20 ] = KEY_CHANNELUP, + [ 0x21 ] = KEY_CHANNELDOWN, + [ 0x22 ] = KEY_PREVIOUS, /* Prev. Ch on Zenith, SOURCE on Hauppauge */ + [ 0x24 ] = KEY_RESTART, + [ 0x25 ] = KEY_OK, + [ 0x26 ] = KEY_CYCLEWINDOWS, /* MINIMIZE on Hauppauge */ + [ 0x28 ] = KEY_ENTER, /* VCR mode on Zenith */ + [ 0x29 ] = KEY_PAUSE, + [ 0x2b ] = KEY_RIGHT, + [ 0x2c ] = KEY_LEFT, + [ 0x2e ] = KEY_MENU, /* FULL SCREEN on Hauppauge */ + [ 0x30 ] = KEY_SLOW, + [ 0x31 ] = KEY_PREVIOUS, /* VCR mode on Zenith */ + [ 0x32 ] = KEY_REWIND, + [ 0x34 ] = KEY_FASTFORWARD, + [ 0x35 ] = KEY_PLAY, + [ 0x36 ] = KEY_STOP, + [ 0x37 ] = KEY_RECORD, + [ 0x38 ] = KEY_TUNER, /* TV/VCR on Zenith */ + [ 0x3a ] = KEY_C, + [ 0x3c ] = KEY_EXIT, + [ 0x3d ] = KEY_POWER2, + [ 0x3e ] = KEY_TUNER, +}; + +EXPORT_SYMBOL_GPL(ir_codes_budget_ci_old); diff --git a/linux/drivers/media/dvb/b2c2/Kconfig b/linux/drivers/media/dvb/b2c2/Kconfig index a0dcd59da..798759589 100644 --- a/linux/drivers/media/dvb/b2c2/Kconfig +++ b/linux/drivers/media/dvb/b2c2/Kconfig @@ -9,6 +9,7 @@ config DVB_B2C2_FLEXCOP select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_BCM3510 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index fbca9ed4b..a63387705 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -18,7 +18,7 @@ #endif #include "mt312.h" #include "lgdt330x.h" -#include "lg_h06xf.h" +#include "lgh06xf.h" #include "dvb-pll.h" /* lnb control */ @@ -307,12 +307,6 @@ static int flexcop_fe_request_firmware(struct dvb_frontend* fe, const struct fir return request_firmware(fw, name, fc->dev); } -static int lgdt3303_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) -{ - struct flexcop_device *fc = fe->dvb->priv; - return lg_h06xf_pll_set(fe, &fc->i2c_adap, params); -} - static struct lgdt330x_config air2pc_atsc_hd5000_config = { .demod_address = 0x59, .demod_chip = LGDT3303, @@ -544,7 +538,7 @@ int flexcop_frontend_init(struct flexcop_device *fc) /* try the air atsc 3nd generation (lgdt3303) */ if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC3; - fc->fe->ops.tuner_ops.set_params = lgdt3303_tuner_set_params; + dvb_attach(lgh06xf_attach, fc->fe, &fc->i2c_adap); info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address); } else /* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */ diff --git a/linux/drivers/media/dvb/b2c2/flexcop-usb.c b/linux/drivers/media/dvb/b2c2/flexcop-usb.c index 8d16d6e72..397d0d6a7 100644 --- a/linux/drivers/media/dvb/b2c2/flexcop-usb.c +++ b/linux/drivers/media/dvb/b2c2/flexcop-usb.c @@ -246,7 +246,7 @@ static int flexcop_usb_i2c_req(struct flexcop_usb *fc_usb, wIndex = (chipaddr << 8 ) | addr; deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",func,request_type,req, - ((wValue && 0xff) << 8),wValue >> 8,((wIndex && 0xff) << 8),wIndex >> 8); + wValue & 0xff, wValue >> 8, wIndex & 0xff, wIndex >> 8); len = usb_control_msg(fc_usb->udev,pipe, req, diff --git a/linux/drivers/media/dvb/bt8xx/Kconfig b/linux/drivers/media/dvb/bt8xx/Kconfig index ae2ff5dc2..dd66b60fb 100644 --- a/linux/drivers/media/dvb/bt8xx/Kconfig +++ b/linux/drivers/media/dvb/bt8xx/Kconfig @@ -1,13 +1,13 @@ config DVB_BT8XX tristate "BT8xx based PCI cards" depends on DVB_CORE && PCI && I2C && VIDEO_BT848 - select DVB_PLL select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_SP887X if !DVB_FE_CUSTOMISE select DVB_NXT6000 if !DVB_FE_CUSTOMISE select DVB_CX24110 if !DVB_FE_CUSTOMISE select DVB_OR51211 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select FW_LOADER help diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c index dfe79310a..742828084 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -35,7 +35,6 @@ #include "dvb_frontend.h" #include "dvb-bt8xx.h" #include "bt878.h" -#include "dvb-pll.h" static int debug; @@ -569,12 +568,6 @@ static struct mt352_config digitv_alps_tded4_config = { .demod_init = digitv_alps_tded4_demod_init, }; -static int tdvs_tua6034_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) -{ - struct dvb_bt8xx_card *card = (struct dvb_bt8xx_card *) fe->dvb->priv; - return lg_h06xf_pll_set(fe, card->i2c_adapter, params); -} - static struct lgdt330x_config tdvs_tua6034_config = { .demod_address = 0x0e, .demod_chip = LGDT3303, @@ -617,7 +610,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) lgdt330x_reset(card); card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter); if (card->fe != NULL) { - card->fe->ops.tuner_ops.set_params = tdvs_tua6034_tuner_set_params; + dvb_attach(lgh06xf_attach, card->fe, card->i2c_adapter); dprintk ("dvb_bt8xx: lgdt330x detected\n"); } break; diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h index a7c1e7867..a18b98ca9 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.h @@ -40,7 +40,7 @@ #include "cx24110.h" #include "or51211.h" #include "lgdt330x.h" -#include "lg_h06xf.h" +#include "lgh06xf.h" #include "zl10353.h" struct dvb_bt8xx_card { diff --git a/linux/drivers/media/dvb/cinergyT2/cinergyT2.c b/linux/drivers/media/dvb/cinergyT2/cinergyT2.c index 90672efea..52e242d1d 100644 --- a/linux/drivers/media/dvb/cinergyT2/cinergyT2.c +++ b/linux/drivers/media/dvb/cinergyT2/cinergyT2.c @@ -771,6 +771,7 @@ static void cinergyt2_query_rc (void *data) dprintk(1, "rc_input_event=%d Up\n", cinergyt2->rc_input_event); input_report_key(cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 0); + input_sync(cinergyt2->rc_input_dev); cinergyt2->rc_input_event = KEY_MAX; } cinergyt2->rc_last_code = ~0; @@ -808,6 +809,7 @@ static void cinergyt2_query_rc (void *data) dprintk(1, "rc_input_event=%d\n", cinergyt2->rc_input_event); input_report_key(cinergyt2->rc_input_dev, cinergyt2->rc_input_event, 1); + input_sync(cinergyt2->rc_input_dev); cinergyt2->rc_last_code = rc_events[n].value; } } @@ -823,8 +825,9 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) { struct input_dev *input_dev; int i; + int err; - cinergyt2->rc_input_dev = input_dev = input_allocate_device(); + input_dev = input_allocate_device(); if (!input_dev) return -ENOMEM; @@ -842,7 +845,13 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) input_dev->keycodesize = 0; input_dev->keycodemax = 0; - input_register_device(cinergyt2->rc_input_dev); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + + cinergyt2->rc_input_dev = input_dev; schedule_delayed_work(&cinergyt2->rc_query_work, HZ/2); return 0; diff --git a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c index 3bc3a37e6..52008bc96 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -348,7 +348,7 @@ static int dvb_frontend_swzigzag_autotune(struct dvb_frontend *fe, int check_wra static void dvb_frontend_swzigzag(struct dvb_frontend *fe) { - fe_status_t s; + fe_status_t s = 0; struct dvb_frontend_private *fepriv = fe->frontend_priv; /* if we've got no parameters, just keep idling */ diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig index dfd53d5e7..ad5214360 100644 --- a/linux/drivers/media/dvb/dvb-usb/Kconfig +++ b/linux/drivers/media/dvb/dvb-usb/Kconfig @@ -98,6 +98,7 @@ config DVB_USB_CXUSB depends on DVB_USB select DVB_CX22702 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE help @@ -159,6 +160,17 @@ config DVB_USB_NOVA_T_USB2 help Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. +config DVB_USB_TTUSB2 + tristate "Pinnacle 400e DVB-S USB2.0 support" + depends on DVB_USB + select DVB_TDA10086 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_TDA826X if !DVB_FE_CUSTOMISE + help + Say Y here to support the Pinnacle 400e DVB-S USB2.0 receiver. The + firmware protocol used by this module is similar to the one used by the + old ttusb-driver - that's why the module is called dvb-usb-ttusb2.ko. + config DVB_USB_DTT200U tristate "WideView WT-200U and WT-220U (pen) DVB-T USB2.0 support (Yakumo/Hama/Typhoon/Yuan)" depends on DVB_USB diff --git a/linux/drivers/media/dvb/dvb-usb/Makefile b/linux/drivers/media/dvb/dvb-usb/Makefile index e23910799..154d593bb 100644 --- a/linux/drivers/media/dvb/dvb-usb/Makefile +++ b/linux/drivers/media/dvb/dvb-usb/Makefile @@ -36,6 +36,9 @@ obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o dvb-usb-cxusb-objs = cxusb.o obj-$(CONFIG_DVB_USB_CXUSB) += dvb-usb-cxusb.o +dvb-usb-ttusb2-objs = ttusb2.o +obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o + dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c index ecc764c85..e474ae657 100644 --- a/linux/drivers/media/dvb/dvb-usb/cxusb.c +++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c @@ -27,7 +27,7 @@ #include "cx22702.h" #include "lgdt330x.h" -#include "lg_h06xf.h" +#include "lgh06xf.h" #include "mt352.h" #include "mt352_priv.h" #include "zl10353.h" @@ -324,13 +324,6 @@ static int cxusb_mt352_demod_init(struct dvb_frontend* fe) return 0; } -static int cxusb_lgh064f_tuner_set_params(struct dvb_frontend *fe, - struct dvb_frontend_parameters *fep) -{ - struct dvb_usb_adapter *adap = fe->dvb->priv; - return lg_h06xf_pll_set(fe, &adap->dev->i2c_adap, fep); -} - static struct cx22702_config cxusb_cx22702_config = { .demod_address = 0x63, @@ -398,7 +391,7 @@ static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap) static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap) { - adap->fe->ops.tuner_ops.set_params = cxusb_lgh064f_tuner_set_params; + dvb_attach(lgh06xf_attach, adap->fe, &adap->dev->i2c_adap); return 0; } diff --git a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c index 1b1598d08..2208757d9 100644 --- a/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/linux/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -277,6 +277,9 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR) }, { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_VIDEOMATE_U500) }, { USB_DEVICE(USB_VID_UNIWILL, USB_PID_UNIWILL_STK7700P) }, + { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_STK7700P) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK_2) }, + { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_2) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -318,18 +321,18 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 5, + .num_device_descs = 6, .devices = { { "DiBcom STK7700P reference design", { &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] }, { NULL }, }, { "Hauppauge Nova-T Stick", - { &dib0700_usb_id_table[4], NULL }, + { &dib0700_usb_id_table[4], &dib0700_usb_id_table[9], NULL }, { NULL }, }, { "AVerMedia AVerTV DVB-T Volar", - { &dib0700_usb_id_table[5], NULL }, + { &dib0700_usb_id_table[5], &dib0700_usb_id_table[10] }, { NULL }, }, { "Compro Videomate U500", @@ -339,6 +342,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { "Uniwill STK7700P based (Hama and others)", { &dib0700_usb_id_table[7], NULL }, { NULL }, + }, + { "Leadtek Winfast DTV Dongle (STK7700P based)", + { &dib0700_usb_id_table[8], NULL }, + { NULL }, } } }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 58e1a449b..6a55ea222 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -101,7 +101,9 @@ #define USB_PID_HAUPPAUGE_NOVA_T_500 0x9941 #define USB_PID_HAUPPAUGE_NOVA_T_500_2 0x9950 #define USB_PID_HAUPPAUGE_NOVA_T_STICK 0x7050 +#define USB_PID_HAUPPAUGE_NOVA_T_STICK_2 0x7060 #define USB_PID_AVERMEDIA_VOLAR 0xa807 +#define USB_PID_AVERMEDIA_VOLAR_2 0xb808 #define USB_PID_NEBULA_DIGITV 0x0201 #define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820 #define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500 @@ -129,6 +131,7 @@ #define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7 #define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025 #define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026 +#define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00 #define USB_PID_GENPIX_8PSK_COLD 0x0200 #define USB_PID_GENPIX_8PSK_WARM 0x0201 diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index ed4537045..5fbb70c80 100644 --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -95,7 +95,9 @@ schedule: int dvb_usb_remote_init(struct dvb_usb_device *d) { + struct input_dev *input_dev; int i; + int err; if (d->props.rc_key_map == NULL || d->props.rc_query == NULL || @@ -105,25 +107,26 @@ int dvb_usb_remote_init(struct dvb_usb_device *d) usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys)); strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys)); - d->rc_input_dev = input_allocate_device(); - if (!d->rc_input_dev) + input_dev = input_allocate_device(); + if (!input_dev) return -ENOMEM; - d->rc_input_dev->evbit[0] = BIT(EV_KEY); - d->rc_input_dev->keycodesize = sizeof(unsigned char); - d->rc_input_dev->keycodemax = KEY_MAX; - d->rc_input_dev->name = "IR-receiver inside an USB DVB receiver"; - d->rc_input_dev->phys = d->rc_phys; - usb_to_input_id(d->udev, &d->rc_input_dev->id); + input_dev->evbit[0] = BIT(EV_KEY); + input_dev->keycodesize = sizeof(unsigned char); + input_dev->keycodemax = KEY_MAX; + input_dev->name = "IR-receiver inside an USB DVB receiver"; + input_dev->phys = d->rc_phys; + usb_to_input_id(d->udev, &input_dev->id); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) - d->rc_input_dev->cdev.dev = &d->udev->dev; + input_dev->cdev.dev = &d->udev->dev; #endif /* set the bits for the keys */ deb_rc("key map size: %d\n", d->props.rc_key_map_size); for (i = 0; i < d->props.rc_key_map_size; i++) { - deb_rc("setting bit for event %d item %d\n",d->props.rc_key_map[i].event, i); - set_bit(d->props.rc_key_map[i].event, d->rc_input_dev->keybit); + deb_rc("setting bit for event %d item %d\n", + d->props.rc_key_map[i].event, i); + set_bit(d->props.rc_key_map[i].event, input_dev->keybit); } /* Start the remote-control polling. */ @@ -131,10 +134,16 @@ int dvb_usb_remote_init(struct dvb_usb_device *d) d->props.rc_interval = 100; /* default */ /* setting these two values to non-zero, we have to manage key repeats */ - d->rc_input_dev->rep[REP_PERIOD] = d->props.rc_interval; - d->rc_input_dev->rep[REP_DELAY] = d->props.rc_interval + 150; + input_dev->rep[REP_PERIOD] = d->props.rc_interval; + input_dev->rep[REP_DELAY] = d->props.rc_interval + 150; - input_register_device(d->rc_input_dev); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + + d->rc_input_dev = input_dev; INIT_WORK(&d->rc_query_work, dvb_usb_read_remote_control, d); diff --git a/linux/drivers/media/dvb/dvb-usb/ttusb2.c b/linux/drivers/media/dvb/dvb-usb/ttusb2.c new file mode 100644 index 000000000..afc0b7efb --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/ttusb2.c @@ -0,0 +1,276 @@ +/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones + * (e.g. Pinnacle 400e DVB-S USB2.0). + * + * The Pinnacle 400e uses the same protocol as the Technotrend USB1.1 boxes. + * + * TDA8263 + TDA10086 + * + * I2C addresses: + * 0x08 - LNBP21PD - LNB power supply + * 0x0e - TDA10086 - Demodulator + * 0x50 - FX2 eeprom + * 0x60 - TDA8263 - Tuner + * 0x78 ??? + * + * Copyright (c) 2002 Holger Waechtler <holger@convergence.de> + * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net> + * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#define DVB_USB_LOG_PREFIX "ttusb2" +#include "dvb-usb.h" + +#include "ttusb2.h" + +#include "tda826x.h" +#include "tda10086.h" +#include "lnbp21.h" + +/* debug */ +static int dvb_usb_ttusb2_debug; +#define deb_info(args...) dprintk(dvb_usb_ttusb2_debug,0x01,args) +module_param_named(debug,dvb_usb_ttusb2_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))." DVB_USB_DEBUG_STATUS); + +struct ttusb2_state { + u8 id; +}; + +static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd, + u8 *wbuf, int wlen, u8 *rbuf, int rlen) +{ + struct ttusb2_state *st = d->priv; + u8 s[wlen+4],r[64] = { 0 }; + int ret = 0; + + memset(s,0,wlen+4); + + s[0] = 0xaa; + s[1] = ++st->id; + s[2] = cmd; + s[3] = wlen; + memcpy(&s[4],wbuf,wlen); + + ret = dvb_usb_generic_rw(d, s, wlen+4, r, 64, 0); + + if (ret != 0 || + r[0] != 0x55 || + r[1] != s[1] || + r[2] != cmd || + (rlen > 0 && r[3] != rlen)) { + warn("there might have been an error during control message transfer. (rlen = %d, was %d)",rlen,r[3]); + return -EIO; + } + + if (rlen > 0) + memcpy(rbuf, &r[4], rlen); + + return 0; +} + +static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + static u8 obuf[60], ibuf[60]; + int i,read; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num > 2) + warn("more than 2 i2c messages at a time is not handled yet. TODO."); + + for (i = 0; i < num; i++) { + read = i+1 < num && (msg[i+1].flags & I2C_M_RD); + + obuf[0] = (msg[i].addr << 1) | read; + obuf[1] = msg[i].len; + + /* read request */ + if (read) + obuf[2] = msg[i+1].len; + else + obuf[2] = 0; + + memcpy(&obuf[3],msg[i].buf,msg[i].len); + + if (ttusb2_msg(d, CMD_I2C_XFER, obuf, msg[i].len+3, ibuf, obuf[2] + 3) < 0) { + err("i2c transfer failed."); + break; + } + + if (read) { + memcpy(msg[i+1].buf,&ibuf[3],msg[i+1].len); + i++; + } + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 ttusb2_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm ttusb2_i2c_algo = { + .master_xfer = ttusb2_i2c_xfer, + .functionality = ttusb2_i2c_func, +}; + +/* Callbacks for DVB USB */ +static int ttusb2_identify_state (struct usb_device *udev, struct + dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, + int *cold) +{ + *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0; + return 0; +} + +static int ttusb2_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 b = onoff; + ttusb2_msg(d, CMD_POWER, &b, 0, NULL, 0); + return ttusb2_msg(d, CMD_POWER, &b, 1, NULL, 0); +} + +#if 0 +static int ttusb2_streaming_ctrl(struct dvb_usb_device *d, int onoff) +{ + return 0; +} +#endif + +static struct tda10086_config tda10086_config = { + .demod_address = 0x0e, + .invert = 0, +}; + +static int ttusb2_frontend_attach(struct dvb_usb_adapter *adap) +{ + if (usb_set_interface(adap->dev->udev,0,3) < 0) + err("set interface to alts=3 failed"); + + if ((adap->fe = dvb_attach(tda10086_attach, &tda10086_config, &adap->dev->i2c_adap)) == NULL) { + deb_info("TDA10086 attach failed\n"); + return -ENODEV; + } + + return 0; +} + +static int ttusb2_tuner_attach(struct dvb_usb_adapter *adap) +{ + if (dvb_attach(tda826x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, 0) == NULL) { + deb_info("TDA8263 attach failed\n"); + return -ENODEV; + } + + if (dvb_attach(lnbp21_attach, adap->fe, &adap->dev->i2c_adap, 0, 0) == NULL) { + deb_info("LNBP21 attach failed\n"); + return -ENODEV; + } + return 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties ttusb2_properties; + +static int ttusb2_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf,&ttusb2_properties,THIS_MODULE,NULL); +} + +static struct usb_device_id ttusb2_table [] = { + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) }, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, ttusb2_table); + +static struct dvb_usb_device_properties ttusb2_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-pctv-400e-01.fw", + + .size_of_priv = sizeof(struct ttusb2_state), + + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = NULL, // ttusb2_streaming_ctrl, + + .frontend_attach = ttusb2_frontend_attach, + .tuner_attach = ttusb2_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_ISOC, + .count = 5, + .endpoint = 0x02, + .u = { + .isoc = { + .framesperurb = 4, + .framesize = 940, + .interval = 1, + } + } + } + } + }, + + .power_ctrl = ttusb2_power_ctrl, + .identify_state = ttusb2_identify_state, + + .i2c_algo = &ttusb2_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 1, + .devices = { + { "Pinnacle 400e DVB-S USB2.0", + { &ttusb2_table[0], NULL }, + { NULL }, + }, + } +}; + +static struct usb_driver ttusb2_driver = { + .name = "dvb_usb_ttusb2", + .probe = ttusb2_probe, + .disconnect = dvb_usb_device_exit, + .id_table = ttusb2_table, +}; + +/* module stuff */ +static int __init ttusb2_module_init(void) +{ + int result; + if ((result = usb_register(&ttusb2_driver))) { + err("usb_register failed. Error number %d",result); + return result; + } + + return 0; +} + +static void __exit ttusb2_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&ttusb2_driver); +} + +module_init (ttusb2_module_init); +module_exit (ttusb2_module_exit); + +MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>"); +MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/media/dvb/dvb-usb/ttusb2.h b/linux/drivers/media/dvb/dvb-usb/ttusb2.h new file mode 100644 index 000000000..52a63af40 --- /dev/null +++ b/linux/drivers/media/dvb/dvb-usb/ttusb2.h @@ -0,0 +1,70 @@ +/* DVB USB compliant linux driver for Technotrend DVB USB boxes and clones + * (e.g. Pinnacle 400e DVB-S USB2.0). + * + * Copyright (c) 2002 Holger Waechtler <holger@convergence.de> + * Copyright (c) 2003 Felix Domke <tmbinc@elitedvb.net> + * Copyright (C) 2005-6 Patrick Boettcher <pb@linuxtv.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_TTUSB2_H_ +#define _DVB_USB_TTUSB2_H_ + +/* TTUSB protocol + * + * always to messages (out/in) + * out message: + * 0xaa <id> <cmdbyte> <datalen> <data...> + * + * in message (complete block is always 0x40 bytes long) + * 0x55 <id> <cmdbyte> <datalen> <data...> + * + * id is incremented for each transaction + */ + +#define CMD_DSP_DOWNLOAD 0x13 +/* out data: <byte>[28] + * last block must be empty */ + +#define CMD_DSP_BOOT 0x14 +/* out data: nothing */ + +#define CMD_POWER 0x15 +/* out data: <on=1/off=0> */ + +#define CMD_LNB 0x16 +/* out data: <power=1> <18V=0,13V=1> <tone> <??=1> <??=1> */ + +#define CMD_GET_VERSION 0x17 +/* in data: <version_byte>[5] */ + +#define CMD_DISEQC 0x18 +/* out data: <master=0xff/burst=??> <cmdlen> <cmdbytes>[cmdlen] */ + +#define CMD_PID_ENABLE 0x22 +/* out data: <index> <type: ts=1/sec=2> <pid msb> <pid lsb> */ + +#define CMD_PID_DISABLE 0x23 +/* out data: <index> */ + +#define CMD_FILTER_ENABLE 0x24 +/* out data: <index> <pid_idx> <filter>[12] <mask>[12] */ + +#define CMD_FILTER_DISABLE 0x25 +/* out data: <index> */ + +#define CMD_GET_DSP_VERSION 0x26 +/* in data: <version_byte>[28] */ + +#define CMD_I2C_XFER 0x31 +/* out data: <addr << 1> <sndlen> <rcvlen> <data>[sndlen] + * in data: <addr << 1> <sndlen> <rcvlen> <data>[rcvlen] */ + +#define CMD_I2C_BITRATE 0x32 +/* out data: <default=0> */ + +#endif diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig index 7279e587e..af314bb1d 100644 --- a/linux/drivers/media/dvb/frontends/Kconfig +++ b/linux/drivers/media/dvb/frontends/Kconfig @@ -297,6 +297,14 @@ config DVB_TUNER_MT2060 help A driver for the silicon IF tuner MT2060 from Microtune. +config DVB_TUNER_LGH06XF + tristate "LG TDVS-H06xF ATSC tuner" + depends on DVB_CORE && I2C + select DVB_PLL + default m if DVB_FE_CUSTOMISE + help + A driver for the LG TDVS-H06xF ATSC tuner family. + comment "Miscellaneous devices" depends on DVB_CORE diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile index 593152ecf..3fa6e5d32 100644 --- a/linux/drivers/media/dvb/frontends/Makefile +++ b/linux/drivers/media/dvb/frontends/Makefile @@ -39,3 +39,4 @@ obj-$(CONFIG_DVB_TDA10086) += tda10086.o obj-$(CONFIG_DVB_TDA826X) += tda826x.o obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o obj-$(CONFIG_DVB_TUA6100) += tua6100.o +obj-$(CONFIG_DVB_TUNER_LGH06XF) += lgh06xf.o diff --git a/linux/drivers/media/dvb/frontends/dibx000_common.c b/linux/drivers/media/dvb/frontends/dibx000_common.c index bdc6d7a7d..1f6dc06a3 100644 --- a/linux/drivers/media/dvb/frontends/dibx000_common.c +++ b/linux/drivers/media/dvb/frontends/dibx000_common.c @@ -85,7 +85,7 @@ static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c struct i2c_msg m[2 + num]; u8 tx_open[4], tx_close[4]; - memset(m,0, sizeof(struct i2c_msg) * (2 + num)), + memset(m,0, sizeof(struct i2c_msg) * (2 + num)); dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER); diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.c b/linux/drivers/media/dvb/frontends/dvb-pll.c index b7e7108ee..62de760c8 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.c +++ b/linux/drivers/media/dvb/frontends/dvb-pll.c @@ -472,14 +472,14 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n", desc->name, div, buf[0], buf[1], buf[2], buf[3]); - return 0; + // calculate the frequency we set it to + return (div * desc->entries[i].stepsize) - desc->entries[i].offset; } EXPORT_SYMBOL(dvb_pll_configure); static int dvb_pll_release(struct dvb_frontend *fe) { - if (fe->tuner_priv) - kfree(fe->tuner_priv); + kfree(fe->tuner_priv); fe->tuner_priv = NULL; return 0; } @@ -489,7 +489,8 @@ static int dvb_pll_sleep(struct dvb_frontend *fe) struct dvb_pll_priv *priv = fe->tuner_priv; u8 buf[4]; struct i2c_msg msg = - { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) }; + { .addr = priv->pll_i2c_address, .flags = 0, + .buf = buf, .len = sizeof(buf) }; int i; int result; @@ -517,16 +518,16 @@ static int dvb_pll_sleep(struct dvb_frontend *fe) return 0; } -static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int dvb_pll_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) { struct dvb_pll_priv *priv = fe->tuner_priv; u8 buf[4]; struct i2c_msg msg = - { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) }; + { .addr = priv->pll_i2c_address, .flags = 0, + .buf = buf, .len = sizeof(buf) }; int result; - u32 div; - int i; - u32 bandwidth = 0; + u32 bandwidth = 0, frequency = 0; if (priv->i2c == NULL) return -EINVAL; @@ -536,8 +537,11 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_param bandwidth = params->u.ofdm.bandwidth; } - if ((result = dvb_pll_configure(priv->pll_desc, buf, params->frequency, bandwidth)) != 0) + if ((result = dvb_pll_configure(priv->pll_desc, buf, + params->frequency, bandwidth)) < 0) return result; + else + frequency = result; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -545,26 +549,19 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_param return result; } - // calculate the frequency we set it to - for (i = 0; i < priv->pll_desc->count; i++) { - if (params->frequency > priv->pll_desc->entries[i].limit) - continue; - break; - } - div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize; - priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset; + priv->frequency = frequency; priv->bandwidth = bandwidth; return 0; } -static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8 *buf, int buf_len) +static int dvb_pll_calc_regs(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params, + u8 *buf, int buf_len) { struct dvb_pll_priv *priv = fe->tuner_priv; int result; - u32 div; - int i; - u32 bandwidth = 0; + u32 bandwidth = 0, frequency = 0; if (buf_len < 5) return -EINVAL; @@ -574,18 +571,15 @@ static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parame bandwidth = params->u.ofdm.bandwidth; } - if ((result = dvb_pll_configure(priv->pll_desc, buf+1, params->frequency, bandwidth)) != 0) + if ((result = dvb_pll_configure(priv->pll_desc, buf+1, + params->frequency, bandwidth)) < 0) return result; + else + frequency = result; + buf[0] = priv->pll_i2c_address; - // calculate the frequency we set it to - for (i = 0; i < priv->pll_desc->count; i++) { - if (params->frequency > priv->pll_desc->entries[i].limit) - continue; - break; - } - div = (params->frequency + priv->pll_desc->entries[i].offset) / priv->pll_desc->entries[i].stepsize; - priv->frequency = (div * priv->pll_desc->entries[i].stepsize) - priv->pll_desc->entries[i].offset; + priv->frequency = frequency; priv->bandwidth = bandwidth; return 5; @@ -614,10 +608,13 @@ static struct dvb_tuner_ops dvb_pll_tuner_ops = { .get_bandwidth = dvb_pll_get_bandwidth, }; -struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc) +struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, + struct i2c_adapter *i2c, + struct dvb_pll_desc *desc) { u8 b1 [] = { 0 }; - struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }; + struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, + .buf = b1, .len = 1 }; struct dvb_pll_priv *priv = NULL; int ret; @@ -640,7 +637,9 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struc priv->i2c = i2c; priv->pll_desc = desc; - memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, sizeof(struct dvb_tuner_ops)); + memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, + sizeof(struct dvb_tuner_ops)); + strncpy(fe->ops.tuner_ops.info.name, desc->name, 128); fe->ops.tuner_ops.info.frequency_min = desc->min; fe->ops.tuner_ops.info.frequency_min = desc->max; diff --git a/linux/drivers/media/dvb/frontends/dvb-pll.h b/linux/drivers/media/dvb/frontends/dvb-pll.h index ed5ac5a36..681186a5e 100644 --- a/linux/drivers/media/dvb/frontends/dvb-pll.h +++ b/linux/drivers/media/dvb/frontends/dvb-pll.h @@ -48,7 +48,7 @@ extern struct dvb_pll_desc dvb_pll_philips_td1316; extern struct dvb_pll_desc dvb_pll_thomson_fe6600; extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, - u32 freq, int bandwidth); + u32 freq, int bandwidth); /** * Attach a dvb-pll to the supplied frontend structure. @@ -59,6 +59,9 @@ extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, * @param desc dvb_pll_desc to use. * @return Frontend pointer on success, NULL on failure */ -extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc); +extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, + int pll_addr, + struct i2c_adapter *i2c, + struct dvb_pll_desc *desc); #endif diff --git a/linux/drivers/media/dvb/frontends/lg_h06xf.h b/linux/drivers/media/dvb/frontends/lg_h06xf.h deleted file mode 100644 index d41e0299f..000000000 --- a/linux/drivers/media/dvb/frontends/lg_h06xf.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * lg_h06xf.h - ATSC Tuner support for LG TDVS-H06xF - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _LG_H06XF_H_ -#define _LG_H06XF_H_ -#include "dvb-pll.h" - -static int lg_h06xf_pll_set(struct dvb_frontend* fe, struct i2c_adapter* i2c_adap, - struct dvb_frontend_parameters* params) -{ - u8 buf[4]; - struct i2c_msg msg = { .addr = 0x61, .flags = 0, - .buf = buf, .len = sizeof(buf) }; - int err; - - dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, params->frequency, 0); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "lg_h06xf: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - /* Set the Auxiliary Byte. */ -#if 0 - buf[2] &= ~0x20; - buf[2] |= 0x18; - buf[3] = 0x50; -#else - buf[0] = buf[2]; - buf[0] &= ~0x20; - buf[0] |= 0x18; - buf[1] = 0x50; - msg.len = 2; -#endif - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((err = i2c_transfer(i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "lg_h06xf: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - return 0; -} -#endif diff --git a/linux/drivers/media/dvb/frontends/lgh06xf.c b/linux/drivers/media/dvb/frontends/lgh06xf.c new file mode 100644 index 000000000..25396d199 --- /dev/null +++ b/linux/drivers/media/dvb/frontends/lgh06xf.c @@ -0,0 +1,140 @@ +/* + * lgh06xf.c - ATSC Tuner support for LG TDVS-H06xF + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "dvb-pll.h" +#include "lgh06xf.h" + +#define LG_H06XF_PLL_I2C_ADDR 0x61 + +struct lgh06xf_priv { + struct i2c_adapter *i2c; + u32 frequency; +}; + +static int lgh06xf_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int lgh06xf_set_params(struct dvb_frontend* fe, + struct dvb_frontend_parameters* params) +{ + struct lgh06xf_priv *priv = fe->tuner_priv; + u8 buf[4]; + struct i2c_msg msg = { .addr = LG_H06XF_PLL_I2C_ADDR, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + u32 frequency; + int result; + + if ((result = dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, + params->frequency, 0)) < 0) + return result; + else + frequency = result; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + printk(KERN_WARNING "lgh06xf: %s error " + "(addr %02x <- %02x, result = %i)\n", + __FUNCTION__, buf[0], buf[1], result); + if (result < 0) + return result; + else + return -EREMOTEIO; + } + + /* Set the Auxiliary Byte. */ +#if 0 + buf[2] &= ~0x20; + buf[2] |= 0x18; + buf[3] = 0x50; +#else + buf[0] = buf[2]; + buf[0] &= ~0x20; + buf[0] |= 0x18; + buf[1] = 0x50; + msg.len = 2; +#endif + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + printk(KERN_WARNING "lgh06xf: %s error " + "(addr %02x <- %02x, result = %i)\n", + __FUNCTION__, buf[0], buf[1], result); + if (result < 0) + return result; + else + return -EREMOTEIO; + } + + priv->frequency = frequency; + + return 0; +} + +static int lgh06xf_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct lgh06xf_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static struct dvb_tuner_ops lgh06xf_tuner_ops = { + .release = lgh06xf_release, + .set_params = lgh06xf_set_params, + .get_frequency = lgh06xf_get_frequency, +}; + +struct dvb_frontend* lgh06xf_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c) +{ + struct lgh06xf_priv *priv = NULL; + + priv = kzalloc(sizeof(struct lgh06xf_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c = i2c; + + memcpy(&fe->ops.tuner_ops, &lgh06xf_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + strlcpy(fe->ops.tuner_ops.info.name, dvb_pll_lg_tdvs_h06xf.name, + sizeof(fe->ops.tuner_ops.info.name)); + + fe->ops.tuner_ops.info.frequency_min = dvb_pll_lg_tdvs_h06xf.min; + fe->ops.tuner_ops.info.frequency_max = dvb_pll_lg_tdvs_h06xf.max; + + fe->tuner_priv = priv; + return fe; +} + +EXPORT_SYMBOL(lgh06xf_attach); + +MODULE_DESCRIPTION("LG TDVS-H06xF ATSC Tuner support"); +MODULE_AUTHOR("Michael Krufky"); +MODULE_LICENSE("GPL"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/linux/drivers/media/dvb/frontends/lgh06xf.h b/linux/drivers/media/dvb/frontends/lgh06xf.h new file mode 100644 index 000000000..510b4bedf --- /dev/null +++ b/linux/drivers/media/dvb/frontends/lgh06xf.h @@ -0,0 +1,35 @@ +/* + * lgh06xf.h - ATSC Tuner support for LG TDVS-H06xF + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _LGH06XF_H_ +#define _LGH06XF_H_ +#include "dvb_frontend.h" + +#if defined(CONFIG_DVB_TUNER_LGH06XF) || (defined(CONFIG_DVB_TUNER_LGH06XF_MODULE) && defined(MODULE)) +extern struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif /* CONFIG_DVB_TUNER_LGH06XF */ + +#endif /* _LGH06XF_H_ */ diff --git a/linux/drivers/media/dvb/frontends/tda1004x.c b/linux/drivers/media/dvb/frontends/tda1004x.c index 11e0dca9a..00e4bcd9f 100644 --- a/linux/drivers/media/dvb/frontends/tda1004x.c +++ b/linux/drivers/media/dvb/frontends/tda1004x.c @@ -648,18 +648,24 @@ static int tda10046_init(struct dvb_frontend* fe) tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x00); // set AGC polarities break; - case TDA10046_AGC_TDA827X: + case TDA10046_AGC_TDA827X_GP11: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x6a); // set AGC polarities break; - case TDA10046_AGC_TDA827X_GPL: + case TDA10046_AGC_TDA827X_GP00: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities break; + case TDA10046_AGC_TDA827X_GP01: + tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup + tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold + tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize + tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x62); // set AGC polarities + break; } tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38); tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on diff --git a/linux/drivers/media/dvb/frontends/tda1004x.h b/linux/drivers/media/dvb/frontends/tda1004x.h index 605ad2dfc..ec502d71b 100644 --- a/linux/drivers/media/dvb/frontends/tda1004x.h +++ b/linux/drivers/media/dvb/frontends/tda1004x.h @@ -35,8 +35,9 @@ enum tda10046_agc { TDA10046_AGC_DEFAULT, /* original configuration */ TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negtive */ TDA10046_AGC_IFO_AUTO_POS, /* IF AGC only, automatic, positive */ - TDA10046_AGC_TDA827X, /* IF AGC only, special setup for tda827x */ - TDA10046_AGC_TDA827X_GPL, /* same as above, but GPIOs 0 */ + TDA10046_AGC_TDA827X_GP11, /* IF AGC only, special setup for tda827x */ + TDA10046_AGC_TDA827X_GP00, /* same as above, but GPIOs 0 */ + TDA10046_AGC_TDA827X_GP01, /* same as above, but GPIO3=0 GPIO1=1*/ }; enum tda10046_if { diff --git a/linux/drivers/media/dvb/frontends/tda10086.c b/linux/drivers/media/dvb/frontends/tda10086.c index 7456b0b99..4c27a2d90 100644 --- a/linux/drivers/media/dvb/frontends/tda10086.c +++ b/linux/drivers/media/dvb/frontends/tda10086.c @@ -441,6 +441,10 @@ static int tda10086_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_pa dprintk ("%s\n", __FUNCTION__); + // check for invalid symbol rate + if (fe_params->u.qpsk.symbol_rate < 500000) + return -EINVAL; + // calculate the updated frequency (note: we convert from Hz->kHz) tmp64 = tda10086_read_byte(state, 0x52); tmp64 |= (tda10086_read_byte(state, 0x51) << 8); diff --git a/linux/drivers/media/dvb/frontends/tda8083.c b/linux/drivers/media/dvb/frontends/tda8083.c index 3aa45ebba..67415c9db 100644 --- a/linux/drivers/media/dvb/frontends/tda8083.c +++ b/linux/drivers/media/dvb/frontends/tda8083.c @@ -262,12 +262,29 @@ static int tda8083_read_status(struct dvb_frontend* fe, fe_status_t* status) if (sync & 0x10) *status |= FE_HAS_SYNC; + if (sync & 0x20) /* frontend can not lock */ + *status |= FE_TIMEDOUT; + if ((sync & 0x1f) == 0x1f) *status |= FE_HAS_LOCK; return 0; } +static int tda8083_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda8083_state* state = fe->demodulator_priv; + int ret; + u8 buf[3]; + + if ((ret = tda8083_readregs(state, 0x0b, buf, sizeof(buf)))) + return ret; + + *ber = ((buf[0] & 0x1f) << 16) | (buf[1] << 8) | buf[2]; + + return 0; +} + static int tda8083_read_signal_strength(struct dvb_frontend* fe, u16* strength) { struct tda8083_state* state = fe->demodulator_priv; @@ -288,6 +305,17 @@ static int tda8083_read_snr(struct dvb_frontend* fe, u16* snr) return 0; } +static int tda8083_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda8083_state* state = fe->demodulator_priv; + + *ucblocks = tda8083_readreg(state, 0x0f); + if (*ucblocks == 0xff) + *ucblocks = 0xffffffff; + + return 0; +} + static int tda8083_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) { struct tda8083_state* state = fe->demodulator_priv; @@ -440,6 +468,8 @@ static struct dvb_frontend_ops tda8083_ops = { .read_status = tda8083_read_status, .read_signal_strength = tda8083_read_signal_strength, .read_snr = tda8083_read_snr, + .read_ber = tda8083_read_ber, + .read_ucblocks = tda8083_read_ucblocks, .diseqc_send_master_cmd = tda8083_send_diseqc_msg, .diseqc_send_burst = tda8083_diseqc_send_burst, diff --git a/linux/drivers/media/dvb/frontends/tda826x.c b/linux/drivers/media/dvb/frontends/tda826x.c index eeab26bd3..79f971dc5 100644 --- a/linux/drivers/media/dvb/frontends/tda826x.c +++ b/linux/drivers/media/dvb/frontends/tda826x.c @@ -42,8 +42,7 @@ struct tda826x_priv { static int tda826x_release(struct dvb_frontend *fe) { - if (fe->tuner_priv) - kfree(fe->tuner_priv); + kfree(fe->tuner_priv); fe->tuner_priv = NULL; return 0; } @@ -121,7 +120,7 @@ static struct dvb_tuner_ops tda826x_tuner_ops = { .info = { .name = "Philips TDA826X", .frequency_min = 950000, - .frequency_min = 2175000 + .frequency_max = 2175000 }, .release = tda826x_release, .sleep = tda826x_sleep, @@ -133,18 +132,21 @@ struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2 { struct tda826x_priv *priv = NULL; u8 b1 [] = { 0, 0 }; - struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 }; + struct i2c_msg msg[2] = { + { .addr = addr, .flags = 0, .buf = NULL, .len = 0 }, + { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 } + }; int ret; dprintk("%s:\n", __FUNCTION__); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - ret = i2c_transfer (i2c, &msg, 1); + ret = i2c_transfer (i2c, msg, 2); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); - if (ret != 1) + if (ret != 2) return NULL; if (!(b1[1] & 0x80)) return NULL; diff --git a/linux/drivers/media/dvb/frontends/tua6100.c b/linux/drivers/media/dvb/frontends/tua6100.c index 88554393a..6ba0029dc 100644 --- a/linux/drivers/media/dvb/frontends/tua6100.c +++ b/linux/drivers/media/dvb/frontends/tua6100.c @@ -43,8 +43,7 @@ struct tua6100_priv { static int tua6100_release(struct dvb_frontend *fe) { - if (fe->tuner_priv) - kfree(fe->tuner_priv); + kfree(fe->tuner_priv); fe->tuner_priv = NULL; return 0; } diff --git a/linux/drivers/media/dvb/ttpci/Kconfig b/linux/drivers/media/dvb/ttpci/Kconfig index 95531a624..eec7ccf41 100644 --- a/linux/drivers/media/dvb/ttpci/Kconfig +++ b/linux/drivers/media/dvb/ttpci/Kconfig @@ -92,6 +92,7 @@ config DVB_BUDGET_CI select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select VIDEO_IR help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard diff --git a/linux/drivers/media/dvb/ttpci/av7110_ir.c b/linux/drivers/media/dvb/ttpci/av7110_ir.c index d54bbcdde..e4544ea2b 100644 --- a/linux/drivers/media/dvb/ttpci/av7110_ir.c +++ b/linux/drivers/media/dvb/ttpci/av7110_ir.c @@ -48,7 +48,8 @@ static void av7110_emit_keyup(unsigned long data) if (!data || !test_bit(data, input_dev->key)) return; - input_event(input_dev, EV_KEY, data, !!0); + input_report_key(input_dev, data, 0); + input_sync(input_dev); } @@ -115,14 +116,17 @@ static void av7110_emit_key(unsigned long parm) del_timer(&keyup_timer); if (keyup_timer.data != keycode || new_toggle != old_toggle) { delay_timer_finished = 0; - input_event(input_dev, EV_KEY, keyup_timer.data, !!0); - input_event(input_dev, EV_KEY, keycode, !0); - } else - if (delay_timer_finished) - input_event(input_dev, EV_KEY, keycode, 2); + input_event(input_dev, EV_KEY, keyup_timer.data, 0); + input_event(input_dev, EV_KEY, keycode, 1); + input_sync(input_dev); + } else if (delay_timer_finished) { + input_event(input_dev, EV_KEY, keycode, 2); + input_sync(input_dev); + } } else { delay_timer_finished = 0; - input_event(input_dev, EV_KEY, keycode, !0); + input_event(input_dev, EV_KEY, keycode, 1); + input_sync(input_dev); } keyup_timer.expires = jiffies + UP_TIMEOUT; @@ -211,6 +215,7 @@ static void ir_handler(struct av7110 *av7110, u32 ircom) int __devinit av7110_ir_init(struct av7110 *av7110) { static struct proc_dir_entry *e; + int err; if (av_cnt >= sizeof av_list/sizeof av_list[0]) return -ENOSPC; @@ -231,7 +236,11 @@ int __devinit av7110_ir_init(struct av7110 *av7110) set_bit(EV_KEY, input_dev->evbit); set_bit(EV_REP, input_dev->evbit); input_register_keys(); - input_register_device(input_dev); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } input_dev->timer.function = input_repeat_key; e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL); diff --git a/linux/drivers/media/dvb/ttpci/budget-av.c b/linux/drivers/media/dvb/ttpci/budget-av.c index 2235ff8b8..8bdc90a70 100644 --- a/linux/drivers/media/dvb/ttpci/budget-av.c +++ b/linux/drivers/media/dvb/ttpci/budget-av.c @@ -655,6 +655,10 @@ static struct tda10021_config philips_cu1216_config = { .demod_address = 0x0c, }; +static struct tda10021_config philips_cu1216_config_altaddress = { + .demod_address = 0x0d, +}; + @@ -831,7 +835,7 @@ static int philips_sd1878_tda8261_tuner_set_params(struct dvb_frontend *fe, return -EINVAL; rc=dvb_pll_configure(&dvb_pll_philips_sd1878_tda8261, buf, - params->frequency, 0); + params->frequency, 0); if(rc < 0) return rc; if (fe->ops.i2c_gate_ctrl) @@ -914,6 +918,7 @@ static u8 read_pwm(struct budget_av *budget_av) #define SUBID_DVBS_TV_STAR_CI 0x0016 #define SUBID_DVBS_EASYWATCH_1 0x001a #define SUBID_DVBS_EASYWATCH 0x001e +#define SUBID_DVBC_EASYWATCH 0x002a #define SUBID_DVBC_KNC1 0x0020 #define SUBID_DVBC_KNC1_PLUS 0x0021 #define SUBID_DVBC_CINERGY1200 0x1156 @@ -952,6 +957,7 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBS_KNC1_PLUS: case SUBID_DVBC_KNC1_PLUS: case SUBID_DVBT_KNC1_PLUS: + case SUBID_DVBC_EASYWATCH: saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); break; } @@ -1006,10 +1012,15 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBC_KNC1: case SUBID_DVBC_KNC1_PLUS: case SUBID_DVBC_CINERGY1200: + case SUBID_DVBC_EASYWATCH: budget_av->reinitialise_demod = 1; fe = dvb_attach(tda10021_attach, &philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); + if (fe == NULL) + fe = dvb_attach(tda10021_attach, &philips_cu1216_config_altaddress, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); if (fe) { budget_av->tda10021_poclkp = 1; budget_av->tda10021_set_frontend = fe->ops.set_frontend; @@ -1242,6 +1253,7 @@ MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T); MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); +MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP); MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP); @@ -1260,6 +1272,7 @@ static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), + MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c index ac0cecb14..8dae16f88 100644 --- a/linux/drivers/media/dvb/ttpci/budget-ci.c +++ b/linux/drivers/media/dvb/ttpci/budget-ci.c @@ -37,6 +37,7 @@ #include <linux/interrupt.h> #include <linux/input.h> #include <linux/spinlock.h> +#include <media/ir-common.h> #include "dvb_ca_en50221.h" #include "stv0299.h" @@ -72,162 +73,228 @@ #define SLOTSTATUS_READY 8 #define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) +/* Milliseconds during which key presses are regarded as key repeat and during + * which the debounce logic is active + */ +#define IR_REPEAT_TIMEOUT 350 + +/* RC5 device wildcard */ +#define IR_DEVICE_ANY 255 + +/* Some remotes sends multiple sequences per keypress (e.g. Zenith sends two), + * this setting allows the superflous sequences to be ignored + */ +static int debounce = 0; +module_param(debounce, int, 0644); +MODULE_PARM_DESC(debounce, "ignore repeated IR sequences (default: 0 = ignore no sequences)"); + +static int rc5_device = -1; +module_param(rc5_device, int, 0644); +MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)"); + +static int ir_debug = 0; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); + +struct budget_ci_ir { + struct input_dev *dev; + struct tasklet_struct msp430_irq_tasklet; + char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ + char phys[32]; + struct ir_input_state state; + int rc5_device; +}; + struct budget_ci { struct budget budget; - struct input_dev *input_dev; - struct tasklet_struct msp430_irq_tasklet; struct tasklet_struct ciintf_irq_tasklet; int slot_status; int ci_irq; struct dvb_ca_en50221 ca; - char ir_dev_name[50]; + struct budget_ci_ir ir; u8 tuner_pll_address; /* used for philips_tdm1316l configs */ }; -/* from reading the following remotes: - Zenith Universal 7 / TV Mode 807 / VCR Mode 837 - Hauppauge (from NOVA-CI-s box product) - i've taken a "middle of the road" approach and note the differences -*/ -static u16 key_map[64] = { - /* 0x0X */ - KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, - KEY_9, - KEY_ENTER, - KEY_RED, - KEY_POWER, /* RADIO on Hauppauge */ - KEY_MUTE, - 0, - KEY_A, /* TV on Hauppauge */ - /* 0x1X */ - KEY_VOLUMEUP, KEY_VOLUMEDOWN, - 0, 0, - KEY_B, - 0, 0, 0, 0, 0, 0, 0, - KEY_UP, KEY_DOWN, - KEY_OPTION, /* RESERVED on Hauppauge */ - KEY_BREAK, - /* 0x2X */ - KEY_CHANNELUP, KEY_CHANNELDOWN, - KEY_PREVIOUS, /* Prev. Ch on Zenith, SOURCE on Hauppauge */ - 0, KEY_RESTART, KEY_OK, - KEY_CYCLEWINDOWS, /* MINIMIZE on Hauppauge */ - 0, - KEY_ENTER, /* VCR mode on Zenith */ - KEY_PAUSE, - 0, - KEY_RIGHT, KEY_LEFT, - 0, - KEY_MENU, /* FULL SCREEN on Hauppauge */ - 0, - /* 0x3X */ - KEY_SLOW, - KEY_PREVIOUS, /* VCR mode on Zenith */ - KEY_REWIND, - 0, - KEY_FASTFORWARD, - KEY_PLAY, KEY_STOP, - KEY_RECORD, - KEY_TUNER, /* TV/VCR on Zenith */ - 0, - KEY_C, - 0, - KEY_EXIT, - KEY_POWER2, - KEY_TUNER, /* VCR mode on Zenith */ - 0, -}; - -static void msp430_ir_debounce(unsigned long data) +static void msp430_ir_keyup(unsigned long data) { - struct input_dev *dev = (struct input_dev *) data; - - if (dev->rep[0] == 0 || dev->rep[0] == ~0) { - input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0); - return; - } - - dev->rep[0] = 0; - dev->timer.expires = jiffies + HZ * 350 / 1000; - add_timer(&dev->timer); - input_event(dev, EV_KEY, key_map[dev->repeat_key], 2); /* REPEAT */ + struct budget_ci_ir *ir = (struct budget_ci_ir *) data; + ir_input_nokey(ir->dev, &ir->state); } static void msp430_ir_interrupt(unsigned long data) { struct budget_ci *budget_ci = (struct budget_ci *) data; - struct input_dev *dev = budget_ci->input_dev; - unsigned int code = - ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; + struct input_dev *dev = budget_ci->ir.dev; + static int bounces = 0; + int device; + int toggle; + static int prev_toggle = -1; + static u32 ir_key; + u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; + + /* + * The msp430 chip can generate two different bytes, command and device + * + * type1: X1CCCCCC, C = command bits (0 - 63) + * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit + * + * More than one command byte may be generated before the device byte + * Only when we have both, a correct keypress is generated + */ + + /* Is this a RC5 command byte? */ + if (command & 0x40) { + if (ir_debug) + printk("budget_ci: received command byte 0x%02x\n", command); + ir_key = command & 0x3f; + return; + } - if (code & 0x40) { - code &= 0x3f; + /* It's a RC5 device byte */ + if (ir_debug) + printk("budget_ci: received device byte 0x%02x\n", command); + device = command & 0x1f; + toggle = command & 0x20; - if (timer_pending(&dev->timer)) { - if (code == dev->repeat_key) { - ++dev->rep[0]; - return; - } - del_timer(&dev->timer); - input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0); - } + if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != device) + return; - if (!key_map[code]) { - printk("DVB (%s): no key for %02x!\n", __FUNCTION__, code); - return; - } + /* Ignore repeated key sequences if requested */ + if (toggle == prev_toggle && ir_key == dev->repeat_key && + bounces > 0 && timer_pending(&dev->timer)) { + if (ir_debug) + printk("budget_ci: debounce logic ignored IR command\n"); + bounces--; + return; + } + prev_toggle = toggle; + + /* Are we still waiting for a keyup event? */ + if (del_timer(&dev->timer)) + ir_input_nokey(dev, &budget_ci->ir.state); + + /* Generate keypress */ + if (ir_debug) + printk("budget_ci: generating keypress 0x%02x\n", ir_key); + ir_input_keydown(dev, &budget_ci->ir.state, ir_key, (ir_key & (command << 8))); - /* initialize debounce and repeat */ - dev->repeat_key = code; - /* Zenith remote _always_ sends 2 sequences */ - dev->rep[0] = ~0; - /* 350 milliseconds */ - dev->timer.expires = jiffies + HZ * 350 / 1000; - /* MAKE */ - input_event(dev, EV_KEY, key_map[code], !0); - add_timer(&dev->timer); + /* Do we want to delay the keyup event? */ + if (debounce) { + bounces = debounce; + mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT)); + } else { + ir_input_nokey(dev, &budget_ci->ir.state); } } static int msp430_ir_init(struct budget_ci *budget_ci) { struct saa7146_dev *saa = budget_ci->budget.dev; - struct input_dev *input_dev; - int i; + struct input_dev *input_dev = budget_ci->ir.dev; + int error; + + budget_ci->ir.dev = input_dev = input_allocate_device(); + if (!input_dev) { + printk(KERN_ERR "budget_ci: IR interface initialisation failed\n"); + error = -ENOMEM; + goto out1; + } - budget_ci->input_dev = input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; + snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name), + "Budget-CI dvb ir receiver %s", saa->name); + snprintf(budget_ci->ir.phys, sizeof(budget_ci->ir.phys), + "pci-%s/ir0", pci_name(saa->pci)); - sprintf(budget_ci->ir_dev_name, "Budget-CI dvb ir receiver %s", saa->name); + input_dev->name = budget_ci->ir.name; - input_dev->name = budget_ci->ir_dev_name; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + input_dev->phys = budget_ci->ir.phys; + input_dev->id.bustype = BUS_PCI; + input_dev->id.version = 1; + if (saa->pci->subsystem_vendor) { + input_dev->id.vendor = saa->pci->subsystem_vendor; + input_dev->id.product = saa->pci->subsystem_device; + } else { + input_dev->id.vendor = saa->pci->vendor; + input_dev->id.product = saa->pci->device; + } +# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) + input_dev->cdev.dev = &saa->pci->dev; +# else + input_dev->dev = &saa->pci->dev; +# endif +#endif + + /* Select keymap and address */ + switch (budget_ci->budget.dev->pci->subsystem_device) { + case 0x100c: + case 0x100f: + case 0x1010: + case 0x1011: + case 0x1012: + case 0x1017: + /* The hauppauge keymap is a superset of these remotes */ + ir_input_init(input_dev, &budget_ci->ir.state, + IR_TYPE_RC5, ir_codes_hauppauge_new); + + if (rc5_device < 0) + budget_ci->ir.rc5_device = 0x1f; + else + budget_ci->ir.rc5_device = rc5_device; + break; + default: + /* unknown remote */ + ir_input_init(input_dev, &budget_ci->ir.state, + IR_TYPE_RC5, ir_codes_budget_ci_old); + + if (rc5_device < 0) + budget_ci->ir.rc5_device = IR_DEVICE_ANY; + else + budget_ci->ir.rc5_device = rc5_device; + break; + } - set_bit(EV_KEY, input_dev->evbit); - for (i = 0; i < ARRAY_SIZE(key_map); i++) - if (key_map[i]) - set_bit(key_map[i], input_dev->keybit); + /* initialise the key-up debounce timeout handler */ + input_dev->timer.function = msp430_ir_keyup; + input_dev->timer.data = (unsigned long) &budget_ci->ir; - input_register_device(budget_ci->input_dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15) + error = input_register_device(input_dev); + if (error) { + printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); + goto out2; + } +#else + input_register_device(input_dev); +#endif - input_dev->timer.function = msp430_ir_debounce; + tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt, + (unsigned long) budget_ci); saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); return 0; + +out2: + input_free_device(input_dev); +out1: + return error; } static void msp430_ir_deinit(struct budget_ci *budget_ci) { struct saa7146_dev *saa = budget_ci->budget.dev; - struct input_dev *dev = budget_ci->input_dev; + struct input_dev *dev = budget_ci->ir.dev; saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); + tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); - if (del_timer(&dev->timer)) - input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0); + if (del_timer(&dev->timer)) { + ir_input_nokey(dev, &budget_ci->ir.state); + input_sync(dev); + } input_unregister_device(dev); } @@ -540,7 +607,7 @@ static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci); if (*isr & MASK_06) - tasklet_schedule(&budget_ci->msp430_irq_tasklet); + tasklet_schedule(&budget_ci->ir.msp430_irq_tasklet); if (*isr & MASK_10) ttpci_budget_irq10_handler(dev, isr); @@ -835,7 +902,7 @@ static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struc band = 1; } else if (tuner_frequency < 200000000) { cp = 6; - band = 1; + band = 2; } else if (tuner_frequency < 290000000) { cp = 3; band = 2; @@ -1035,6 +1102,7 @@ static void frontend_init(struct budget_ci *budget_ci) case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt)) budget_ci->tuner_pll_address = 0x60; + philips_tdm1316l_config.invert = 1; budget_ci->budget.dvb_frontend = dvb_attach(tda10046_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { @@ -1082,24 +1150,23 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio struct budget_ci *budget_ci; int err; - if (!(budget_ci = kmalloc(sizeof(struct budget_ci), GFP_KERNEL))) - return -ENOMEM; + budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL); + if (!budget_ci) { + err = -ENOMEM; + goto out1; + } dprintk(2, "budget_ci: %p\n", budget_ci); - budget_ci->budget.ci_present = 0; - dev->ext_priv = budget_ci; - if ((err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE))) { - kfree(budget_ci); - return err; - } - - tasklet_init(&budget_ci->msp430_irq_tasklet, msp430_ir_interrupt, - (unsigned long) budget_ci); + err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE); + if (err) + goto out2; - msp430_ir_init(budget_ci); + err = msp430_ir_init(budget_ci); + if (err) + goto out3; ciintf_init(budget_ci); @@ -1109,6 +1176,13 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio ttpci_budget_init_hooks(&budget_ci->budget); return 0; + +out3: + ttpci_budget_deinit(&budget_ci->budget); +out2: + kfree(budget_ci); +out1: + return err; } static int budget_ci_detach(struct saa7146_dev *dev) @@ -1119,16 +1193,13 @@ static int budget_ci_detach(struct saa7146_dev *dev) if (budget_ci->budget.ci_present) ciintf_deinit(budget_ci); + msp430_ir_deinit(budget_ci); if (budget_ci->budget.dvb_frontend) { dvb_unregister_frontend(budget_ci->budget.dvb_frontend); dvb_frontend_detach(budget_ci->budget.dvb_frontend); } err = ttpci_budget_deinit(&budget_ci->budget); - tasklet_kill(&budget_ci->msp430_irq_tasklet); - - msp430_ir_deinit(budget_ci); - // disable frontend and CI interface saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); diff --git a/linux/drivers/media/dvb/ttpci/budget.c b/linux/drivers/media/dvb/ttpci/budget.c index e58f0391e..56f1c80de 100644 --- a/linux/drivers/media/dvb/ttpci/budget.c +++ b/linux/drivers/media/dvb/ttpci/budget.c @@ -46,6 +46,10 @@ #include "lnbp21.h" #include "bsru6.h" +static int diseqc_method; +module_param(diseqc_method, int, 0444); +MODULE_PARM_DESC(diseqc_method, "Select DiSEqC method for subsystem id 13c2:1003, 0: default, 1: more reliable (for newer revisions only)"); + static void Set22K (struct budget *budget, int state) { struct saa7146_dev *dev=budget->dev; @@ -382,6 +386,11 @@ static void frontend_init(struct budget *budget) if (budget->dvb_frontend) { budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params; budget->dvb_frontend->tuner_priv = &budget->i2c_adap; + if (budget->dev->pci->subsystem_device == 0x1003 && diseqc_method == 0) { + budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd; + budget->dvb_frontend->ops.diseqc_send_burst = budget_diseqc_send_burst; + budget->dvb_frontend->ops.set_tone = budget_set_tone; + } break; } break; diff --git a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c index f2d18a5fb..aab71ee63 100644 --- a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -253,6 +253,7 @@ static void ttusb_dec_handle_irq( struct urb *urb) * for now lets report each signal as a key down and up*/ dprintk("%s:rc signal:%d\n", __FUNCTION__, buffer[4]); input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 1); + input_sync(dec->rc_input_dev); input_report_key(dec->rc_input_dev, rc_keys[buffer[4] - 1], 0); input_sync(dec->rc_input_dev); } @@ -1207,11 +1208,12 @@ static int ttusb_init_rc( struct ttusb_dec *dec) struct input_dev *input_dev; u8 b[] = { 0x00, 0x01 }; int i; + int err; usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys)); strlcpy(dec->rc_phys, "/input0", sizeof(dec->rc_phys)); - dec->rc_input_dev = input_dev = input_allocate_device(); + input_dev = input_allocate_device(); if (!input_dev) return -ENOMEM; @@ -1225,8 +1227,13 @@ static int ttusb_init_rc( struct ttusb_dec *dec) for (i = 0; i < ARRAY_SIZE(rc_keys); i++) set_bit(rc_keys[i], input_dev->keybit); - input_register_device(input_dev); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + dec->rc_input_dev = input_dev; if (usb_submit_urb(dec->irq_urb, GFP_KERNEL)) printk("%s: usb_submit_urb failed\n",__FUNCTION__); /* enable irq pipe */ diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index fbe5b6168..dee356cd1 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -184,9 +184,17 @@ config VIDEO_KS0127 To compile this driver as a module, choose M here: the module will be called ks0127. +config VIDEO_OV7670 + tristate "OmniVision OV7670 sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV7670 VGA camera. It currently only works with the M88ALP01 + controller. + config VIDEO_SAA7110 tristate "Philips SAA7110 video decoder" - depends on VIDEO_V4L1 + depends on VIDEO_V4L1 && I2C ---help--- Support for the Philips SAA7110 video decoders. @@ -567,18 +575,6 @@ config VIDEO_ZORAN_AVS6EYES help Support for the AverMedia 6 Eyes video surveillance card. -config VIDEO_ZR36120 - tristate "Zoran ZR36120/36125 Video For Linux" - depends on PCI && I2C && VIDEO_V4L1 && BROKEN - help - Support for ZR36120/ZR36125 based frame grabber/overlay boards. - This includes the Victor II, WaveWatcher, Video Wonder, Maxi-TV, - and Buster boards. Please read the material in - <file:Documentation/video4linux/zr36120.txt> for more information. - - To compile this driver as a module, choose M here: the - module will be called zr36120. - config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" depends on PCI && SONYPI && VIDEO_V4L1 @@ -670,6 +666,15 @@ config VIDEO_M32R_AR_M64278 To compile this driver as a module, choose M here: the module will be called arv. +config VIDEO_CAFE_CCIC + tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support" + depends on I2C && VIDEO_V4L2 + select VIDEO_OV7670 + ---help--- + This is a video4linux2 driver for the Marvell 88ALP01 integrated + CMOS camera controller. This is the controller found on first- + generation OLPC systems. + # # USB Multimedia device configuration # diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index af57abce8..20b9cbc14 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -2,7 +2,6 @@ # Makefile for the video capture/playback device drivers. # -zoran-objs := zr36120.o zr36120_i2c.o zr36120_mem.o zr36067-objs := zoran_procfs.o zoran_device.o \ zoran_driver.o zoran_card.o tuner-objs := tuner-core.o tuner-types.o tuner-simple.o \ @@ -23,7 +22,6 @@ obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o -obj-$(CONFIG_VIDEO_ZR36120) += zoran.o obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o @@ -92,6 +90,9 @@ obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o +obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o +obj-$(CONFIG_VIDEO_OV7670) += ov7670.o + obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index 2cdb6f340..2fecc20f3 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -4075,7 +4075,7 @@ static void __devinit init_PXC200(struct bttv *btv) * - sleep 1ms * - write 0x0E * read from GPIO_DATA into buf (uint_32) - * - if ( buf>>18 & 0x01 ) || ( buf>>19 && 0x01 != 0 ) + * - if ( buf>>18 & 0x01 ) || ( buf>>19 & 0x01 != 0 ) * error. ERROR_CPLD_Check_Failed. */ /* ----------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index 88f7f65f4..c07725e7f 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -1818,7 +1818,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) memset(i,0,sizeof(*i)); i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; - i->audioset = 0; + i->audioset = 1; if (i->index == bttv_tvcards[btv->c.type].tuner) { sprintf(i->name, "Television"); i->type = V4L2_INPUT_TYPE_TUNER; diff --git a/linux/drivers/media/video/bt8xx/bttv-input.c b/linux/drivers/media/video/bt8xx/bttv-input.c index 43bc6805b..45e884b08 100644 --- a/linux/drivers/media/video/bt8xx/bttv-input.c +++ b/linux/drivers/media/video/bt8xx/bttv-input.c @@ -260,24 +260,59 @@ static void bttv_rc5_timer_keyup(unsigned long data) /* ---------------------------------------------------------------------- */ +static void bttv_ir_start(struct bttv *btv, struct bttv_ir *ir) +{ + if (ir->polling) { + init_timer(&ir->timer); + ir->timer.function = bttv_input_timer; + ir->timer.data = (unsigned long)btv; + ir->timer.expires = jiffies + HZ; + add_timer(&ir->timer); + } else if (ir->rc5_gpio) { + /* set timer_end for code completion */ + init_timer(&ir->timer_end); + ir->timer_end.function = bttv_rc5_timer_end; + ir->timer_end.data = (unsigned long)ir; + + init_timer(&ir->timer_keyup); + ir->timer_keyup.function = bttv_rc5_timer_keyup; + ir->timer_keyup.data = (unsigned long)ir; + } +} + +static void bttv_ir_stop(struct bttv *btv) +{ + if (btv->remote->polling) { + del_timer_sync(&btv->remote->timer); + flush_scheduled_work(); + } + + if (btv->remote->rc5_gpio) { + u32 gpio; + + del_timer_sync(&btv->remote->timer_end); + flush_scheduled_work(); + + gpio = bttv_gpio_read(&btv->c); + bttv_gpio_write(&btv->c, gpio & ~(1 << 4)); + } +} + int bttv_input_init(struct bttv *btv) { struct bttv_ir *ir; IR_KEYTAB_TYPE *ir_codes = NULL; struct input_dev *input_dev; int ir_type = IR_TYPE_OTHER; + int err = -ENOMEM; if (!btv->has_remote) return -ENODEV; ir = kzalloc(sizeof(*ir),GFP_KERNEL); input_dev = input_allocate_device(); - if (!ir || !input_dev) { - kfree(ir); - input_free_device(input_dev); - return -ENOMEM; - } - memset(ir,0,sizeof(*ir)); + if (!ir || !input_dev) + goto err_out_free; /* detect & configure */ switch (btv->c.type) { @@ -349,10 +384,9 @@ int bttv_input_init(struct bttv *btv) break; } if (NULL == ir_codes) { - dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n",btv->c.type); - kfree(ir); - input_free_device(input_dev); - return -ENODEV; + dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n", btv->c.type); + err = -ENODEV; + goto err_out_free; } if (ir->rc5_gpio) { @@ -396,32 +430,26 @@ int bttv_input_init(struct bttv *btv) #endif btv->remote = ir; - if (ir->polling) { - init_timer(&ir->timer); - ir->timer.function = bttv_input_timer; - ir->timer.data = (unsigned long)btv; - ir->timer.expires = jiffies + HZ; - add_timer(&ir->timer); - } else if (ir->rc5_gpio) { - /* set timer_end for code completion */ - init_timer(&ir->timer_end); - ir->timer_end.function = bttv_rc5_timer_end; - ir->timer_end.data = (unsigned long)ir; - - init_timer(&ir->timer_keyup); - ir->timer_keyup.function = bttv_rc5_timer_keyup; - ir->timer_keyup.data = (unsigned long)ir; - } + bttv_ir_start(btv, ir); /* all done */ - input_register_device(btv->remote->dev); - printk(DEVNAME ": %s detected at %s\n",ir->name,ir->phys); + err = input_register_device(btv->remote->dev); + if (err) + goto err_out_stop; /* the remote isn't as bouncy as a keyboard */ ir->dev->rep[REP_DELAY] = repeat_delay; ir->dev->rep[REP_PERIOD] = repeat_period; return 0; + + err_out_stop: + bttv_ir_stop(btv); + btv->remote = NULL; + err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; } void bttv_input_fini(struct bttv *btv) @@ -429,22 +457,7 @@ void bttv_input_fini(struct bttv *btv) if (btv->remote == NULL) return; - if (btv->remote->polling) { - del_timer_sync(&btv->remote->timer); - flush_scheduled_work(); - } - - - if (btv->remote->rc5_gpio) { - u32 gpio; - - del_timer_sync(&btv->remote->timer_end); - flush_scheduled_work(); - - gpio = bttv_gpio_read(&btv->c); - bttv_gpio_write(&btv->c, gpio & ~(1 << 4)); - } - + bttv_ir_stop(btv); input_unregister_device(btv->remote->dev); kfree(btv->remote); btv->remote = NULL; diff --git a/linux/drivers/media/video/cafe_ccic-regs.h b/linux/drivers/media/video/cafe_ccic-regs.h new file mode 100644 index 000000000..b2c22a0d6 --- /dev/null +++ b/linux/drivers/media/video/cafe_ccic-regs.h @@ -0,0 +1,160 @@ +/* + * Register definitions for the m88alp01 camera interface. Offsets in bytes + * as given in the spec. + * + * Copyright 2006 One Laptop Per Child Association, Inc. + * + * Written by Jonathan Corbet, corbet@lwn.net. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#define REG_Y0BAR 0x00 +#define REG_Y1BAR 0x04 +#define REG_Y2BAR 0x08 +/* ... */ + +#define REG_IMGPITCH 0x24 /* Image pitch register */ +#define IMGP_YP_SHFT 2 /* Y pitch params */ +#define IMGP_YP_MASK 0x00003ffc /* Y pitch field */ +#define IMGP_UVP_SHFT 18 /* UV pitch (planar) */ +#define IMGP_UVP_MASK 0x3ffc0000 +#define REG_IRQSTATRAW 0x28 /* RAW IRQ Status */ +#define IRQ_EOF0 0x00000001 /* End of frame 0 */ +#define IRQ_EOF1 0x00000002 /* End of frame 1 */ +#define IRQ_EOF2 0x00000004 /* End of frame 2 */ +#define IRQ_SOF0 0x00000008 /* Start of frame 0 */ +#define IRQ_SOF1 0x00000010 /* Start of frame 1 */ +#define IRQ_SOF2 0x00000020 /* Start of frame 2 */ +#define IRQ_OVERFLOW 0x00000040 /* FIFO overflow */ +#define IRQ_TWSIW 0x00010000 /* TWSI (smbus) write */ +#define IRQ_TWSIR 0x00020000 /* TWSI read */ +#define IRQ_TWSIE 0x00040000 /* TWSI error */ +#define TWSIIRQS (IRQ_TWSIW|IRQ_TWSIR|IRQ_TWSIE) +#define FRAMEIRQS (IRQ_EOF0|IRQ_EOF1|IRQ_EOF2|IRQ_SOF0|IRQ_SOF1|IRQ_SOF2) +#define ALLIRQS (TWSIIRQS|FRAMEIRQS|IRQ_OVERFLOW) +#define REG_IRQMASK 0x2c /* IRQ mask - same bits as IRQSTAT */ +#define REG_IRQSTAT 0x30 /* IRQ status / clear */ + +#define REG_IMGSIZE 0x34 /* Image size */ +#define IMGSZ_V_MASK 0x1fff0000 +#define IMGSZ_V_SHIFT 16 +#define IMGSZ_H_MASK 0x00003fff +#define REG_IMGOFFSET 0x38 /* IMage offset */ + +#define REG_CTRL0 0x3c /* Control 0 */ +#define C0_ENABLE 0x00000001 /* Makes the whole thing go */ + +/* Mask for all the format bits */ +#define C0_DF_MASK 0x00fffffc /* Bits 2-23 */ + +/* RGB ordering */ +#define C0_RGB4_RGBX 0x00000000 +#define C0_RGB4_XRGB 0x00000004 +#define C0_RGB4_BGRX 0x00000008 +#define C0_RGB4_XBGR 0x0000000c +#define C0_RGB5_RGGB 0x00000000 +#define C0_RGB5_GRBG 0x00000004 +#define C0_RGB5_GBRG 0x00000008 +#define C0_RGB5_BGGR 0x0000000c + +/* Spec has two fields for DIN and DOUT, but they must match, so + combine them here. */ +#define C0_DF_YUV 0x00000000 /* Data is YUV */ +#define C0_DF_RGB 0x000000a0 /* ... RGB */ +#define C0_DF_BAYER 0x00000140 /* ... Bayer */ +/* 8-8-8 must be missing from the below - ask */ +#define C0_RGBF_565 0x00000000 +#define C0_RGBF_444 0x00000800 +#define C0_RGB_BGR 0x00001000 /* Blue comes first */ +#define C0_YUV_PLANAR 0x00000000 /* YUV 422 planar format */ +#define C0_YUV_PACKED 0x00008000 /* YUV 422 packed */ +#define C0_YUV_420PL 0x0000a000 /* YUV 420 planar */ +/* Think that 420 packed must be 111 - ask */ +#define C0_YUVE_YUYV 0x00000000 /* Y1CbY0Cr */ +#define C0_YUVE_YVYU 0x00010000 /* Y1CrY0Cb */ +#define C0_YUVE_VYUY 0x00020000 /* CrY1CbY0 */ +#define C0_YUVE_UYVY 0x00030000 /* CbY1CrY0 */ +#define C0_YUVE_XYUV 0x00000000 /* 420: .YUV */ +#define C0_YUVE_XYVU 0x00010000 /* 420: .YVU */ +#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */ +#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */ +/* Bayer bits 18,19 if needed */ +#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */ +#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */ +#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */ +#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */ +#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */ +#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */ +#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */ + + +#define REG_CTRL1 0x40 /* Control 1 */ +#define C1_444ALPHA 0x00f00000 /* Alpha field in RGB444 */ +#define C1_ALPHA_SHFT 20 +#define C1_DMAB32 0x00000000 /* 32-byte DMA burst */ +#define C1_DMAB16 0x02000000 /* 16-byte DMA burst */ +#define C1_DMAB64 0x04000000 /* 64-byte DMA burst */ +#define C1_DMAB_MASK 0x06000000 +#define C1_TWOBUFS 0x08000000 /* Use only two DMA buffers */ +#define C1_PWRDWN 0x10000000 /* Power down */ + +#define REG_CLKCTRL 0x88 /* Clock control */ +#define CLK_DIV_MASK 0x0000ffff /* Upper bits RW "reserved" */ + +#define REG_GPR 0xb4 /* General purpose register. This + controls inputs to the power and reset + pins on the OV7670 used with OLPC; + other deployments could differ. */ +#define GPR_C1EN 0x00000020 /* Pad 1 (power down) enable */ +#define GPR_C0EN 0x00000010 /* Pad 0 (reset) enable */ +#define GPR_C1 0x00000002 /* Control 1 value */ +/* + * Control 0 is wired to reset on OLPC machines. For ov7x sensors, + * it is active low, for 0v6x, instead, it's active high. What + * fun. + */ +#define GPR_C0 0x00000001 /* Control 0 value */ + +#define REG_TWSIC0 0xb8 /* TWSI (smbus) control 0 */ +#define TWSIC0_EN 0x00000001 /* TWSI enable */ +#define TWSIC0_MODE 0x00000002 /* 1 = 16-bit, 0 = 8-bit */ +#define TWSIC0_SID 0x000003fc /* Slave ID */ +#define TWSIC0_SID_SHIFT 2 +#define TWSIC0_CLKDIV 0x0007fc00 /* Clock divider */ +#define TWSIC0_MASKACK 0x00400000 /* Mask ack from sensor */ +#define TWSIC0_OVMAGIC 0x00800000 /* Make it work on OV sensors */ + +#define REG_TWSIC1 0xbc /* TWSI control 1 */ +#define TWSIC1_DATA 0x0000ffff /* Data to/from camchip */ +#define TWSIC1_ADDR 0x00ff0000 /* Address (register) */ +#define TWSIC1_ADDR_SHIFT 16 +#define TWSIC1_READ 0x01000000 /* Set for read op */ +#define TWSIC1_WSTAT 0x02000000 /* Write status */ +#define TWSIC1_RVALID 0x04000000 /* Read data valid */ +#define TWSIC1_ERROR 0x08000000 /* Something screwed up */ + + +#define REG_UBAR 0xc4 /* Upper base address register */ + +/* + * Here's the weird global control registers which are said to live + * way up here. + */ +#define REG_GL_CSR 0x3004 /* Control/status register */ +#define GCSR_SRS 0x00000001 /* SW Reset set */ +#define GCSR_SRC 0x00000002 /* SW Reset clear */ +#define GCSR_MRS 0x00000004 /* Master reset set */ +#define GCSR_MRC 0x00000008 /* HW Reset clear */ +#define GCSR_CCIC_EN 0x00004000 /* CCIC Clock enable */ +#define REG_GL_IMASK 0x300c /* Interrupt mask register */ +#define GIMSK_CCIC_EN 0x00000004 /* CCIC Interrupt enable */ + +#define REG_LEN REG_GL_IMASK + 4 + + +/* + * Useful stuff that probably belongs somewhere global. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 diff --git a/linux/drivers/media/video/cafe_ccic.c b/linux/drivers/media/video/cafe_ccic.c new file mode 100644 index 000000000..d849ef103 --- /dev/null +++ b/linux/drivers/media/video/cafe_ccic.c @@ -0,0 +1,2250 @@ +/* + * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe" + * multifunction chip. Currently works with the Omnivision OV7670 + * sensor. + * + * Copyright 2006 One Laptop Per Child Association, Inc. + * + * Written by Jonathan Corbet, corbet@lwn.net. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/pci.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/videodev2.h> +#include "compat.h" +#include <media/v4l2-common.h> +#include <linux/device.h> +#include <linux/wait.h> +#include <linux/list.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/debugfs.h> +#include <linux/jiffies.h> +#include <linux/vmalloc.h> + +#include <asm/uaccess.h> +#include <asm/io.h> + +#include "cafe_ccic-regs.h" + +#define CAFE_VERSION 0x000001 + + +/* + * Parameters. + */ +MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); +MODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("Video"); + +/* + * Internal DMA buffer management. Since the controller cannot do S/G I/O, + * we must have physically contiguous buffers to bring frames into. + * These parameters control how many buffers we use, whether we + * allocate them at load time (better chance of success, but nails down + * memory) or when somebody tries to use the camera (riskier), and, + * for load-time allocation, how big they should be. + * + * The controller can cycle through three buffers. We could use + * more by flipping pointers around, but it probably makes little + * sense. + */ + +#define MAX_DMA_BUFS 3 +static int alloc_bufs_at_load = 0; +module_param(alloc_bufs_at_load, bool, 0444); +MODULE_PARM_DESC(alloc_bufs_at_load, + "Non-zero value causes DMA buffers to be allocated at module " + "load time. This increases the chances of successfully getting " + "those buffers, but at the cost of nailing down the memory from " + "the outset."); + +static int n_dma_bufs = 3; +module_param(n_dma_bufs, uint, 0644); +MODULE_PARM_DESC(n_dma_bufs, + "The number of DMA buffers to allocate. Can be either two " + "(saves memory, makes timing tighter) or three."); + +static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2; /* Worst case */ +module_param(dma_buf_size, uint, 0444); +MODULE_PARM_DESC(dma_buf_size, + "The size of the allocated DMA buffers. If actual operating " + "parameters require larger buffers, an attempt to reallocate " + "will be made."); + +static int min_buffers = 1; +module_param(min_buffers, uint, 0644); +MODULE_PARM_DESC(min_buffers, + "The minimum number of streaming I/O buffers we are willing " + "to work with."); + +static int max_buffers = 10; +module_param(max_buffers, uint, 0644); +MODULE_PARM_DESC(max_buffers, + "The maximum number of streaming I/O buffers an application " + "will be allowed to allocate. These buffers are big and live " + "in vmalloc space."); + +static int flip = 0; +module_param(flip, bool, 0444); +MODULE_PARM_DESC(flip, + "If set, the sensor will be instructed to flip the image " + "vertically."); + + +enum cafe_state { + S_NOTREADY, /* Not yet initialized */ + S_IDLE, /* Just hanging around */ + S_FLAKED, /* Some sort of problem */ + S_SINGLEREAD, /* In read() */ + S_SPECREAD, /* Speculative read (for future read()) */ + S_STREAMING /* Streaming data */ +}; + +/* + * Tracking of streaming I/O buffers. + */ +struct cafe_sio_buffer { + struct list_head list; + struct v4l2_buffer v4lbuf; + char *buffer; /* Where it lives in kernel space */ + int mapcount; + struct cafe_camera *cam; +}; + +/* + * A description of one of our devices. + * Locking: controlled by s_mutex. Certain fields, however, require + * the dev_lock spinlock; they are marked as such by comments. + * dev_lock is also required for access to device registers. + */ +struct cafe_camera +{ + enum cafe_state state; + unsigned long flags; /* Buffer status, mainly (dev_lock) */ + int users; /* How many open FDs */ + struct file *owner; /* Who has data access (v4l2) */ + + /* + * Subsystem structures. + */ + struct pci_dev *pdev; + struct video_device v4ldev; + struct i2c_adapter i2c_adapter; + struct i2c_client *sensor; + + unsigned char __iomem *regs; + struct list_head dev_list; /* link to other devices */ + + /* DMA buffers */ + unsigned int nbufs; /* How many are alloc'd */ + int next_buf; /* Next to consume (dev_lock) */ + unsigned int dma_buf_size; /* allocated size */ + void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */ + dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */ + unsigned int specframes; /* Unconsumed spec frames (dev_lock) */ + unsigned int sequence; /* Frame sequence number */ + unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual buffers */ + + /* Streaming buffers */ + unsigned int n_sbufs; /* How many we have */ + struct cafe_sio_buffer *sb_bufs; /* The array of housekeeping structs */ + struct list_head sb_avail; /* Available for data (we own) (dev_lock) */ + struct list_head sb_full; /* With data (user space owns) (dev_lock) */ + struct tasklet_struct s_tasklet; + + /* Current operating parameters */ + enum v4l2_chip_ident sensor_type; /* Currently ov7670 only */ + struct v4l2_pix_format pix_format; + + /* Locks */ + struct mutex s_mutex; /* Access to this structure */ + spinlock_t dev_lock; /* Access to device */ + + /* Misc */ + wait_queue_head_t smbus_wait; /* Waiting on i2c events */ + wait_queue_head_t iowait; /* Waiting on frame data */ +#ifdef CONFIG_VIDEO_ADV_DEBUG + struct dentry *dfs_regs; + struct dentry *dfs_cam_regs; +#endif +}; + +/* + * Status flags. Always manipulated with bit operations. + */ +#define CF_BUF0_VALID 0 /* Buffers valid - first three */ +#define CF_BUF1_VALID 1 +#define CF_BUF2_VALID 2 +#define CF_DMA_ACTIVE 3 /* A frame is incoming */ +#define CF_CONFIG_NEEDED 4 /* Must configure hardware */ + + + +/* + * Start over with DMA buffers - dev_lock needed. + */ +static void cafe_reset_buffers(struct cafe_camera *cam) +{ + int i; + + cam->next_buf = -1; + for (i = 0; i < cam->nbufs; i++) + clear_bit(i, &cam->flags); + cam->specframes = 0; +} + +static inline int cafe_needs_config(struct cafe_camera *cam) +{ + return test_bit(CF_CONFIG_NEEDED, &cam->flags); +} + +static void cafe_set_config_needed(struct cafe_camera *cam, int needed) +{ + if (needed) + set_bit(CF_CONFIG_NEEDED, &cam->flags); + else + clear_bit(CF_CONFIG_NEEDED, &cam->flags); +} + + + + +/* + * Debugging and related. + */ +#define cam_err(cam, fmt, arg...) \ + dev_err(&(cam)->pdev->dev, fmt, ##arg); +#define cam_warn(cam, fmt, arg...) \ + dev_warn(&(cam)->pdev->dev, fmt, ##arg); +#define cam_dbg(cam, fmt, arg...) \ + dev_dbg(&(cam)->pdev->dev, fmt, ##arg); + + +/* ---------------------------------------------------------------------*/ +/* + * We keep a simple list of known devices to search at open time. + */ +static LIST_HEAD(cafe_dev_list); +static DEFINE_MUTEX(cafe_dev_list_lock); + +static void cafe_add_dev(struct cafe_camera *cam) +{ + mutex_lock(&cafe_dev_list_lock); + list_add_tail(&cam->dev_list, &cafe_dev_list); + mutex_unlock(&cafe_dev_list_lock); +} + +static void cafe_remove_dev(struct cafe_camera *cam) +{ + mutex_lock(&cafe_dev_list_lock); + list_del(&cam->dev_list); + mutex_unlock(&cafe_dev_list_lock); +} + +static struct cafe_camera *cafe_find_dev(int minor) +{ + struct cafe_camera *cam; + + mutex_lock(&cafe_dev_list_lock); + list_for_each_entry(cam, &cafe_dev_list, dev_list) { + if (cam->v4ldev.minor == minor) + goto done; + } + cam = NULL; + done: + mutex_unlock(&cafe_dev_list_lock); + return cam; +} + + +static struct cafe_camera *cafe_find_by_pdev(struct pci_dev *pdev) +{ + struct cafe_camera *cam; + + mutex_lock(&cafe_dev_list_lock); + list_for_each_entry(cam, &cafe_dev_list, dev_list) { + if (cam->pdev == pdev) + goto done; + } + cam = NULL; + done: + mutex_unlock(&cafe_dev_list_lock); + return cam; +} + + +/* ------------------------------------------------------------------------ */ +/* + * Device register I/O + */ +static inline void cafe_reg_write(struct cafe_camera *cam, unsigned int reg, + unsigned int val) +{ + iowrite32(val, cam->regs + reg); +} + +static inline unsigned int cafe_reg_read(struct cafe_camera *cam, + unsigned int reg) +{ + return ioread32(cam->regs + reg); +} + + +static inline void cafe_reg_write_mask(struct cafe_camera *cam, unsigned int reg, + unsigned int val, unsigned int mask) +{ + unsigned int v = cafe_reg_read(cam, reg); + + v = (v & ~mask) | (val & mask); + cafe_reg_write(cam, reg, v); +} + +static inline void cafe_reg_clear_bit(struct cafe_camera *cam, + unsigned int reg, unsigned int val) +{ + cafe_reg_write_mask(cam, reg, 0, val); +} + +static inline void cafe_reg_set_bit(struct cafe_camera *cam, + unsigned int reg, unsigned int val) +{ + cafe_reg_write_mask(cam, reg, val, val); +} + + + +/* -------------------------------------------------------------------- */ +/* + * The I2C/SMBUS interface to the camera itself starts here. The + * controller handles SMBUS itself, presenting a relatively simple register + * interface; all we have to do is to tell it where to route the data. + */ +#define CAFE_SMBUS_TIMEOUT (HZ) /* generous */ + +static int cafe_smbus_write_done(struct cafe_camera *cam) +{ + unsigned long flags; + int c1; + + /* + * We must delay after the interrupt, or the controller gets confused + * and never does give us good status. Fortunately, we don't do this + * often. + */ + udelay(20); + spin_lock_irqsave(&cam->dev_lock, flags); + c1 = cafe_reg_read(cam, REG_TWSIC1); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return (c1 & (TWSIC1_WSTAT|TWSIC1_ERROR)) != TWSIC1_WSTAT; +} + +static int cafe_smbus_write_data(struct cafe_camera *cam, + u16 addr, u8 command, u8 value) +{ + unsigned int rval; + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID); + rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */ + /* + * Marvell sez set clkdiv to all 1's for now. + */ + rval |= TWSIC0_CLKDIV; + cafe_reg_write(cam, REG_TWSIC0, rval); + (void) cafe_reg_read(cam, REG_TWSIC1); /* force write */ + rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR); + cafe_reg_write(cam, REG_TWSIC1, rval); + spin_unlock_irqrestore(&cam->dev_lock, flags); + msleep(2); /* Required or things flake */ + + wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(cam), + CAFE_SMBUS_TIMEOUT); + spin_lock_irqsave(&cam->dev_lock, flags); + rval = cafe_reg_read(cam, REG_TWSIC1); + spin_unlock_irqrestore(&cam->dev_lock, flags); + + if (rval & TWSIC1_WSTAT) { + cam_err(cam, "SMBUS write (%02x/%02x/%02x) timed out\n", addr, + command, value); + return -EIO; + } + if (rval & TWSIC1_ERROR) { + cam_err(cam, "SMBUS write (%02x/%02x/%02x) error\n", addr, + command, value); + return -EIO; + } + return 0; +} + + + +static int cafe_smbus_read_done(struct cafe_camera *cam) +{ + unsigned long flags; + int c1; + + /* + * We must delay after the interrupt, or the controller gets confused + * and never does give us good status. Fortunately, we don't do this + * often. + */ + udelay(20); + spin_lock_irqsave(&cam->dev_lock, flags); + c1 = cafe_reg_read(cam, REG_TWSIC1); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return c1 & (TWSIC1_RVALID|TWSIC1_ERROR); +} + + + +static int cafe_smbus_read_data(struct cafe_camera *cam, + u16 addr, u8 command, u8 *value) +{ + unsigned int rval; + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID); + rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */ + /* + * Marvel sez set clkdiv to all 1's for now. + */ + rval |= TWSIC0_CLKDIV; + cafe_reg_write(cam, REG_TWSIC0, rval); + (void) cafe_reg_read(cam, REG_TWSIC1); /* force write */ + rval = TWSIC1_READ | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR); + cafe_reg_write(cam, REG_TWSIC1, rval); + spin_unlock_irqrestore(&cam->dev_lock, flags); + + wait_event_timeout(cam->smbus_wait, + cafe_smbus_read_done(cam), CAFE_SMBUS_TIMEOUT); + spin_lock_irqsave(&cam->dev_lock, flags); + rval = cafe_reg_read(cam, REG_TWSIC1); + spin_unlock_irqrestore(&cam->dev_lock, flags); + + if (rval & TWSIC1_ERROR) { + cam_err(cam, "SMBUS read (%02x/%02x) error\n", addr, command); + return -EIO; + } + if (! (rval & TWSIC1_RVALID)) { + cam_err(cam, "SMBUS read (%02x/%02x) timed out\n", addr, + command); + return -EIO; + } + *value = rval & 0xff; + return 0; +} + +/* + * Perform a transfer over SMBUS. This thing is called under + * the i2c bus lock, so we shouldn't race with ourselves... + */ +static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char rw, u8 command, + int size, union i2c_smbus_data *data) +{ + struct cafe_camera *cam = i2c_get_adapdata(adapter); + int ret = -EINVAL; + + /* + * Refuse to talk to anything but OV cam chips. We should + * never even see an attempt to do so, but one never knows. + */ +#if 0 /* client needs to talk during attach process */ + if (! cam->sensor) { + cam_err(cam, "SMBUS xfer with no client\n"); + return -EINVAL; + } +#endif + if (cam->sensor && addr != cam->sensor->addr) { + cam_err(cam, "funky smbus addr %d\n", addr); + return -EINVAL; + } + /* + * This interface would appear to only do byte data ops. OK + * it can do word too, but the cam chip has no use for that. + */ + if (size != I2C_SMBUS_BYTE_DATA) { + cam_err(cam, "funky xfer size %d\n", size); + return -EINVAL; + } + + if (rw == I2C_SMBUS_WRITE) + ret = cafe_smbus_write_data(cam, addr, command, data->byte); + else if (rw == I2C_SMBUS_READ) + ret = cafe_smbus_read_data(cam, addr, command, &data->byte); + return ret; +} + + +static void cafe_smbus_enable_irq(struct cafe_camera *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_reg_set_bit(cam, REG_IRQMASK, TWSIIRQS); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + +static u32 cafe_smbus_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA; +} + +static struct i2c_algorithm cafe_smbus_algo = { + .smbus_xfer = cafe_smbus_xfer, + .functionality = cafe_smbus_func +}; + +/* Somebody is on the bus */ +static int cafe_cam_init(struct cafe_camera *cam); +static void cafe_ctlr_stop_dma(struct cafe_camera *cam); +static void cafe_ctlr_power_down(struct cafe_camera *cam); + +static int cafe_smbus_attach(struct i2c_client *client) +{ + struct cafe_camera *cam = i2c_get_adapdata(client->adapter); + + /* + * Don't talk to chips we don't recognize. + */ + if (client->driver->id == I2C_DRIVERID_OV7670) { + cam->sensor = client; + return cafe_cam_init(cam); + } + return -EINVAL; +} + +static int cafe_smbus_detach(struct i2c_client *client) +{ + struct cafe_camera *cam = i2c_get_adapdata(client->adapter); + + if (cam->sensor == client) { + cafe_ctlr_stop_dma(cam); + cafe_ctlr_power_down(cam); + cam_err(cam, "lost the sensor!\n"); + cam->sensor = NULL; /* Bummer, no camera */ + cam->state = S_NOTREADY; + } + return 0; +} + +static int cafe_smbus_setup(struct cafe_camera *cam) +{ + struct i2c_adapter *adap = &cam->i2c_adapter; + int ret; + + cafe_smbus_enable_irq(cam); + adap->id = I2C_HW_SMBUS_CAFE; + adap->class = I2C_CLASS_CAM_DIGITAL; + adap->owner = THIS_MODULE; + adap->client_register = cafe_smbus_attach; + adap->client_unregister = cafe_smbus_detach; + adap->algo = &cafe_smbus_algo; + strcpy(adap->name, "cafe_ccic"); + i2c_set_adapdata(adap, cam); + ret = i2c_add_adapter(adap); + if (ret) + printk(KERN_ERR "Unable to register cafe i2c adapter\n"); + return ret; +} + +static void cafe_smbus_shutdown(struct cafe_camera *cam) +{ + i2c_del_adapter(&cam->i2c_adapter); +} + + +/* ------------------------------------------------------------------- */ +/* + * Deal with the controller. + */ + +/* + * Do everything we think we need to have the interface operating + * according to the desired format. + */ +static void cafe_ctlr_dma(struct cafe_camera *cam) +{ + /* + * Store the first two Y buffers (we aren't supporting + * planar formats for now, so no UV bufs). Then either + * set the third if it exists, or tell the controller + * to just use two. + */ + cafe_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]); + cafe_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]); + if (cam->nbufs > 2) { + cafe_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]); + cafe_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS); + } + else + cafe_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS); + cafe_reg_write(cam, REG_UBAR, 0); /* 32 bits only for now */ +} + +static void cafe_ctlr_image(struct cafe_camera *cam) +{ + int imgsz; + struct v4l2_pix_format *fmt = &cam->pix_format; + + imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) | + (fmt->bytesperline & IMGSZ_H_MASK); + cafe_reg_write(cam, REG_IMGSIZE, imgsz); + cafe_reg_write(cam, REG_IMGOFFSET, 0); + /* YPITCH just drops the last two bits */ + cafe_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline, + IMGP_YP_MASK); + /* + * Tell the controller about the image format we are using. + */ + switch (cam->pix_format.pixelformat) { + case V4L2_PIX_FMT_YUYV: + cafe_reg_write_mask(cam, REG_CTRL0, + C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV, + C0_DF_MASK); + break; + + /* + * For "fake rgb32" get the image pitch right. + */ + case V4L2_PIX_FMT_RGB32: + cafe_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline/2, + IMGP_YP_MASK); + imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) | + ((fmt->bytesperline/2) & IMGSZ_H_MASK); + cafe_reg_write(cam, REG_IMGSIZE, imgsz); + /* fall into ... */ + case V4L2_PIX_FMT_RGB444: + cafe_reg_write_mask(cam, REG_CTRL0, + C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB, + C0_DF_MASK); + /* Alpha value? */ + break; + + case V4L2_PIX_FMT_RGB565: + cafe_reg_write_mask(cam, REG_CTRL0, + C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR, + C0_DF_MASK); + break; + + default: + cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat); + break; + } + /* + * Make sure it knows we want to use hsync/vsync. + */ + cafe_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, + C0_SIFM_MASK); +} + + +/* + * Configure the controller for operation; caller holds the + * device mutex. + */ +static int cafe_ctlr_configure(struct cafe_camera *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_ctlr_dma(cam); + cafe_ctlr_image(cam); + cafe_set_config_needed(cam, 0); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return 0; +} + +static void cafe_ctlr_irq_enable(struct cafe_camera *cam) +{ + /* + * Clear any pending interrupts, since we do not + * expect to have I/O active prior to enabling. + */ + cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); + cafe_reg_set_bit(cam, REG_IRQMASK, FRAMEIRQS); +} + +static void cafe_ctlr_irq_disable(struct cafe_camera *cam) +{ + cafe_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS); +} + +/* + * Make the controller start grabbing images. Everything must + * be set up before doing this. + */ +static void cafe_ctlr_start(struct cafe_camera *cam) +{ + /* set_bit performs a read, so no other barrier should be + needed here */ + cafe_reg_set_bit(cam, REG_CTRL0, C0_ENABLE); +} + +static void cafe_ctlr_stop(struct cafe_camera *cam) +{ + cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); +} + +static void cafe_ctlr_init(struct cafe_camera *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + /* + * Added magic to bring up the hardware on the B-Test board + */ + cafe_reg_write(cam, 0x3038, 0x8); + cafe_reg_write(cam, 0x315c, 0x80008); + /* + * Go through the dance needed to wake the device up. + * Note that these registers are global and shared + * with the NAND and SD devices. Interaction between the + * three still needs to be examined. + */ + cafe_reg_write(cam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */ + cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRC); + cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRS); + mdelay(5); /* FIXME revisit this */ + cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC); + cafe_reg_set_bit(cam, REG_GL_IMASK, GIMSK_CCIC_EN); + /* + * Make sure it's not powered down. + */ + cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); + /* + * Turn off the enable bit. It sure should be off anyway, + * but it's good to be sure. + */ + cafe_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); + /* + * Mask all interrupts. + */ + cafe_reg_write(cam, REG_IRQMASK, 0); + /* + * Clock the sensor appropriately. Controller clock should + * be 48MHz, sensor "typical" value is half that. + */ + cafe_reg_write_mask(cam, REG_CLKCTRL, 2, CLK_DIV_MASK); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + + +/* + * Stop the controller, and don't return until we're really sure that no + * further DMA is going on. + */ +static void cafe_ctlr_stop_dma(struct cafe_camera *cam) +{ + unsigned long flags; + + /* + * Theory: stop the camera controller (whether it is operating + * or not). Delay briefly just in case we race with the SOF + * interrupt, then wait until no DMA is active. + */ + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_ctlr_stop(cam); + spin_unlock_irqrestore(&cam->dev_lock, flags); + mdelay(1); + wait_event_timeout(cam->iowait, + !test_bit(CF_DMA_ACTIVE, &cam->flags), HZ); + if (test_bit(CF_DMA_ACTIVE, &cam->flags)) + cam_err(cam, "Timeout waiting for DMA to end\n"); + /* This would be bad news - what now? */ + spin_lock_irqsave(&cam->dev_lock, flags); + cam->state = S_IDLE; + cafe_ctlr_irq_disable(cam); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + +/* + * Power up and down. + */ +static void cafe_ctlr_power_up(struct cafe_camera *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); + /* + * Put the sensor into operational mode (assumes OLPC-style + * wiring). Control 0 is reset - set to 1 to operate. + * Control 1 is power down, set to 0 to operate. + */ + cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */ + mdelay(1); /* Marvell says 1ms will do it */ + cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0); + mdelay(1); /* Enough? */ + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + +static void cafe_ctlr_power_down(struct cafe_camera *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1); + cafe_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + +/* -------------------------------------------------------------------- */ +/* + * Communications with the sensor. + */ + +static int __cafe_cam_cmd(struct cafe_camera *cam, int cmd, void *arg) +{ + struct i2c_client *sc = cam->sensor; + int ret; + + if (sc == NULL || sc->driver == NULL || sc->driver->command == NULL) + return -EINVAL; + ret = sc->driver->command(sc, cmd, arg); + if (ret == -EPERM) /* Unsupported command */ + return 0; + return ret; +} + +static int __cafe_cam_reset(struct cafe_camera *cam) +{ + int zero = 0; + return __cafe_cam_cmd(cam, VIDIOC_INT_RESET, &zero); +} + +/* + * We have found the sensor on the i2c. Let's try to have a + * conversation. + */ +static int cafe_cam_init(struct cafe_camera *cam) +{ + int ret; + + mutex_lock(&cam->s_mutex); + if (cam->state != S_NOTREADY) + cam_warn(cam, "Cam init with device in funky state %d", + cam->state); + ret = __cafe_cam_reset(cam); + if (ret) + goto out; + ret = __cafe_cam_cmd(cam, VIDIOC_INT_G_CHIP_IDENT, &cam->sensor_type); + if (ret) + goto out; +// if (cam->sensor->addr != OV7xx0_SID) { + if (cam->sensor_type != V4L2_IDENT_OV7670) { + cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr); + ret = -EINVAL; + goto out; + } +/* Get/set parameters? */ + ret = 0; + cam->state = S_IDLE; + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +/* + * Configure the sensor to match the parameters we have. Caller should + * hold s_mutex + */ +static int cafe_cam_set_flip(struct cafe_camera *cam) +{ + struct v4l2_control ctrl; + + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = V4L2_CID_VFLIP; + ctrl.value = flip; + return __cafe_cam_cmd(cam, VIDIOC_S_CTRL, &ctrl); +} + + +static int cafe_cam_configure(struct cafe_camera *cam) +{ + struct v4l2_format fmt; + int ret, zero = 0; + + if (cam->state != S_IDLE) + return -EINVAL; + fmt.fmt.pix = cam->pix_format; + ret = __cafe_cam_cmd(cam, VIDIOC_INT_INIT, &zero); + if (ret == 0) + ret = __cafe_cam_cmd(cam, VIDIOC_S_FMT, &fmt); + /* + * OV7670 does weird things if flip is set *before* format... + */ + ret += cafe_cam_set_flip(cam); + return ret; +} + +/* -------------------------------------------------------------------- */ +/* + * DMA buffer management. These functions need s_mutex held. + */ + +/* FIXME: this is inefficient as hell, since dma_alloc_coherent just + * does a get_free_pages() call, and we waste a good chunk of an orderN + * allocation. Should try to allocate the whole set in one chunk. + */ +static int cafe_alloc_dma_bufs(struct cafe_camera *cam, int loadtime) +{ + int i; + + cafe_set_config_needed(cam, 1); + if (loadtime) + cam->dma_buf_size = dma_buf_size; + else { + cam->dma_buf_size = cam->pix_format.sizeimage; + if (cam->pix_format.pixelformat == V4L2_PIX_FMT_RGB32) + cam->dma_buf_size /= 2; + } + if (n_dma_bufs > 3) + n_dma_bufs = 3; + + cam->nbufs = 0; + for (i = 0; i < n_dma_bufs; i++) { + cam->dma_bufs[i] = dma_alloc_coherent(&cam->pdev->dev, + cam->dma_buf_size, cam->dma_handles + i, + GFP_KERNEL); + if (cam->dma_bufs[i] == NULL) { + cam_warn(cam, "Failed to allocate DMA buffer\n"); + break; + } + /* For debug, remove eventually */ + memset(cam->dma_bufs[i], 0xcc, cam->dma_buf_size); + (cam->nbufs)++; + } + + switch (cam->nbufs) { + case 1: + dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size, + cam->dma_bufs[0], cam->dma_handles[0]); + cam->nbufs = 0; + case 0: + cam_err(cam, "Insufficient DMA buffers, cannot operate\n"); + return -ENOMEM; + + case 2: + if (n_dma_bufs > 2) + cam_warn(cam, "Will limp along with only 2 buffers\n"); + break; + } + return 0; +} + +static void cafe_free_dma_bufs(struct cafe_camera *cam) +{ + int i; + + for (i = 0; i < cam->nbufs; i++) { + dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size, + cam->dma_bufs[i], cam->dma_handles[i]); + cam->dma_bufs[i] = NULL; + } + cam->nbufs = 0; +} + + + + + +/* ----------------------------------------------------------------------- */ +/* + * Here starts the V4L2 interface code. + */ + +/* + * Read an image from the device. + */ +static ssize_t cafe_deliver_buffer(struct cafe_camera *cam, + char __user *buffer, size_t len, loff_t *pos) +{ + int bufno; + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + if (cam->next_buf < 0) { + cam_err(cam, "deliver_buffer: No next buffer\n"); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return -EIO; + } + bufno = cam->next_buf; + clear_bit(bufno, &cam->flags); + if (++(cam->next_buf) >= cam->nbufs) + cam->next_buf = 0; + if (! test_bit(cam->next_buf, &cam->flags)) + cam->next_buf = -1; + cam->specframes = 0; + spin_unlock_irqrestore(&cam->dev_lock, flags); + + if (len > cam->pix_format.sizeimage) + len = cam->pix_format.sizeimage; + if (copy_to_user(buffer, cam->dma_bufs[bufno], len)) + return -EFAULT; + (*pos) += len; + return len; +} + +/* + * Get everything ready, and start grabbing frames. + */ +static int cafe_read_setup(struct cafe_camera *cam, enum cafe_state state) +{ + int ret; + unsigned long flags; + + /* + * Configuration. If we still don't have DMA buffers, + * make one last, desperate attempt. + */ + if (cam->nbufs == 0) + if (cafe_alloc_dma_bufs(cam, 0)) + return -ENOMEM; + + if (cafe_needs_config(cam)) { + cafe_cam_configure(cam); + ret = cafe_ctlr_configure(cam); + if (ret) + return ret; + } + + /* + * Turn it loose. + */ + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_reset_buffers(cam); + cafe_ctlr_irq_enable(cam); + cam->state = state; + cafe_ctlr_start(cam); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return 0; +} + + +static ssize_t cafe_v4l_read(struct file *filp, + char __user *buffer, size_t len, loff_t *pos) +{ + struct cafe_camera *cam = filp->private_data; + int ret; + + /* + * Perhaps we're in speculative read mode and already + * have data? + */ + mutex_lock(&cam->s_mutex); + if (cam->state == S_SPECREAD) { + if (cam->next_buf >= 0) { + ret = cafe_deliver_buffer(cam, buffer, len, pos); + if (ret != 0) + goto out_unlock; + } + } else if (cam->state == S_FLAKED || cam->state == S_NOTREADY) { + ret = -EIO; + goto out_unlock; + } else if (cam->state != S_IDLE) { + ret = -EBUSY; + goto out_unlock; + } + + /* + * v4l2: multiple processes can open the device, but only + * one gets to grab data from it. + */ + if (cam->owner && cam->owner != filp) { + ret = -EBUSY; + goto out_unlock; + } + cam->owner = filp; + + /* + * Do setup if need be. + */ + if (cam->state != S_SPECREAD) { + ret = cafe_read_setup(cam, S_SINGLEREAD); + if (ret) + goto out_unlock; + } + /* + * Wait for something to happen. This should probably + * be interruptible (FIXME). + */ + wait_event_timeout(cam->iowait, cam->next_buf >= 0, HZ); + if (cam->next_buf < 0) { + cam_err(cam, "read() operation timed out\n"); + cafe_ctlr_stop_dma(cam); + ret = -EIO; + goto out_unlock; + } + /* + * Give them their data and we should be done. + */ + ret = cafe_deliver_buffer(cam, buffer, len, pos); + + out_unlock: + mutex_unlock(&cam->s_mutex); + return ret; +} + + + + + + + + +/* + * Streaming I/O support. + */ + + + +static int cafe_vidioc_streamon(struct file *filp, void *priv, + enum v4l2_buf_type type) +{ + struct cafe_camera *cam = filp->private_data; + int ret = -EINVAL; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + mutex_lock(&cam->s_mutex); + if (cam->state != S_IDLE || cam->n_sbufs == 0) + goto out_unlock; + + cam->sequence = 0; + ret = cafe_read_setup(cam, S_STREAMING); + + out_unlock: + mutex_unlock(&cam->s_mutex); + out: + return ret; +} + + +static int cafe_vidioc_streamoff(struct file *filp, void *priv, + enum v4l2_buf_type type) +{ + struct cafe_camera *cam = filp->private_data; + int ret = -EINVAL; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + mutex_lock(&cam->s_mutex); + if (cam->state != S_STREAMING) + goto out_unlock; + + cafe_ctlr_stop_dma(cam); + ret = 0; + + out_unlock: + mutex_unlock(&cam->s_mutex); + out: + return ret; +} + + + +static int cafe_setup_siobuf(struct cafe_camera *cam, int index) +{ + struct cafe_sio_buffer *buf = cam->sb_bufs + index; + + INIT_LIST_HEAD(&buf->list); + buf->v4lbuf.length = PAGE_ALIGN(cam->pix_format.sizeimage); + buf->buffer = vmalloc_user(buf->v4lbuf.length); + if (buf->buffer == NULL) + return -ENOMEM; + buf->mapcount = 0; + buf->cam = cam; + + buf->v4lbuf.index = index; + buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->v4lbuf.field = V4L2_FIELD_NONE; + buf->v4lbuf.memory = V4L2_MEMORY_MMAP; + /* + * Offset: must be 32-bit even on a 64-bit system. video-buf + * just uses the length times the index, but the spec warns + * against doing just that - vma merging problems. So we + * leave a gap between each pair of buffers. + */ + buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; + return 0; +} + +static int cafe_free_sio_buffers(struct cafe_camera *cam) +{ + int i; + + /* + * If any buffers are mapped, we cannot free them at all. + */ + for (i = 0; i < cam->n_sbufs; i++) + if (cam->sb_bufs[i].mapcount > 0) + return -EBUSY; + /* + * OK, let's do it. + */ + for (i = 0; i < cam->n_sbufs; i++) + vfree(cam->sb_bufs[i].buffer); + cam->n_sbufs = 0; + kfree(cam->sb_bufs); + cam->sb_bufs = NULL; + INIT_LIST_HEAD(&cam->sb_avail); + INIT_LIST_HEAD(&cam->sb_full); + return 0; +} + + + +static int cafe_vidioc_reqbufs(struct file *filp, void *priv, + struct v4l2_requestbuffers *req) +{ + struct cafe_camera *cam = filp->private_data; + int ret; + + /* + * Make sure it's something we can do. User pointers could be + * implemented without great pain, but that's not been done yet. + */ + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + /* + * If they ask for zero buffers, they really want us to stop streaming + * (if it's happening) and free everything. Should we check owner? + */ + mutex_lock(&cam->s_mutex); + if (req->count == 0) { + if (cam->state == S_STREAMING) + cafe_ctlr_stop_dma(cam); + ret = cafe_free_sio_buffers (cam); + goto out; + } + /* + * Device needs to be idle and working. We *could* try to do the + * right thing in S_SPECREAD by shutting things down, but it + * probably doesn't matter. + */ + if (cam->state != S_IDLE || (cam->owner && cam->owner != filp)) { + ret = -EBUSY; + goto out; + } + cam->owner = filp; + + if (req->count < min_buffers) + req->count = min_buffers; + else if (req->count > max_buffers) + req->count = max_buffers; + if (cam->n_sbufs > 0) { + ret = cafe_free_sio_buffers(cam); + if (ret) + goto out; + } + + cam->sb_bufs = kzalloc(req->count*sizeof(struct cafe_sio_buffer), + GFP_KERNEL); + if (cam->sb_bufs == NULL) { + ret = -ENOMEM; + goto out; + } + for (cam->n_sbufs = 0; cam->n_sbufs < req->count; (cam->n_sbufs++)) { + ret = cafe_setup_siobuf(cam, cam->n_sbufs); + if (ret) + break; + } + + if (cam->n_sbufs == 0) /* no luck at all - ret already set */ + kfree(cam->sb_bufs); + else + ret = 0; + req->count = cam->n_sbufs; /* In case of partial success */ + + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int cafe_vidioc_querybuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct cafe_camera *cam = filp->private_data; + int ret = -EINVAL; + + mutex_lock(&cam->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + if (buf->index < 0 || buf->index >= cam->n_sbufs) + goto out; + *buf = cam->sb_bufs[buf->index].v4lbuf; + ret = 0; + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +static int cafe_vidioc_qbuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct cafe_camera *cam = filp->private_data; + struct cafe_sio_buffer *sbuf; + int ret = -EINVAL; + unsigned long flags; + + mutex_lock(&cam->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + if (buf->index < 0 || buf->index >= cam->n_sbufs) + goto out; + sbuf = cam->sb_bufs + buf->index; + if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) { + ret = 0; /* Already queued?? */ + goto out; + } + if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_DONE) { + /* Spec doesn't say anything, seems appropriate tho */ + ret = -EBUSY; + goto out; + } + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; + spin_lock_irqsave(&cam->dev_lock, flags); + list_add(&sbuf->list, &cam->sb_avail); + spin_unlock_irqrestore(&cam->dev_lock, flags); + ret = 0; + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +static int cafe_vidioc_dqbuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct cafe_camera *cam = filp->private_data; + struct cafe_sio_buffer *sbuf; + int ret = -EINVAL; + unsigned long flags; + + mutex_lock(&cam->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out_unlock; + if (cam->state != S_STREAMING) + goto out_unlock; + if (list_empty(&cam->sb_full) && filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto out_unlock; + } + + while (list_empty(&cam->sb_full) && cam->state == S_STREAMING) { + mutex_unlock(&cam->s_mutex); + if (wait_event_interruptible(cam->iowait, + !list_empty(&cam->sb_full))) { + ret = -ERESTARTSYS; + goto out; + } + mutex_lock(&cam->s_mutex); + } + + if (cam->state != S_STREAMING) + ret = -EINTR; + else { + spin_lock_irqsave(&cam->dev_lock, flags); + /* Should probably recheck !list_empty() here */ + sbuf = list_entry(cam->sb_full.next, + struct cafe_sio_buffer, list); + list_del_init(&sbuf->list); + spin_unlock_irqrestore(&cam->dev_lock, flags); + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; + *buf = sbuf->v4lbuf; + ret = 0; + } + + out_unlock: + mutex_unlock(&cam->s_mutex); + out: + return ret; +} + + + +static void cafe_v4l_vm_open(struct vm_area_struct *vma) +{ + struct cafe_sio_buffer *sbuf = vma->vm_private_data; + /* + * Locking: done under mmap_sem, so we don't need to + * go back to the camera lock here. + */ + sbuf->mapcount++; +} + + +static void cafe_v4l_vm_close(struct vm_area_struct *vma) +{ + struct cafe_sio_buffer *sbuf = vma->vm_private_data; + + mutex_lock(&sbuf->cam->s_mutex); + sbuf->mapcount--; + /* Docs say we should stop I/O too... */ + if (sbuf->mapcount == 0) + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; + mutex_unlock(&sbuf->cam->s_mutex); +} + +static struct vm_operations_struct cafe_v4l_vm_ops = { + .open = cafe_v4l_vm_open, + .close = cafe_v4l_vm_close +}; + + +static int cafe_v4l_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct cafe_camera *cam = filp->private_data; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + int ret = -EINVAL; + int i; + struct cafe_sio_buffer *sbuf = NULL; + + if (! (vma->vm_flags & VM_WRITE) || ! (vma->vm_flags & VM_SHARED)) + return -EINVAL; + /* + * Find the buffer they are looking for. + */ + mutex_lock(&cam->s_mutex); + for (i = 0; i < cam->n_sbufs; i++) + if (cam->sb_bufs[i].v4lbuf.m.offset == offset) { + sbuf = cam->sb_bufs + i; + break; + } + if (sbuf == NULL) + goto out; + + ret = remap_vmalloc_range(vma, sbuf->buffer, 0); + if (ret) + goto out; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = sbuf; + vma->vm_ops = &cafe_v4l_vm_ops; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; + cafe_v4l_vm_open(vma); + ret = 0; + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + + + +static int cafe_v4l_open(struct inode *inode, struct file *filp) +{ + struct cafe_camera *cam; + + cam = cafe_find_dev(iminor(inode)); + if (cam == NULL) + return -ENODEV; + filp->private_data = cam; + + mutex_lock(&cam->s_mutex); + if (cam->users == 0) { + cafe_ctlr_power_up(cam); + __cafe_cam_reset(cam); + cafe_set_config_needed(cam, 1); + /* FIXME make sure this is complete */ + } + (cam->users)++; + mutex_unlock(&cam->s_mutex); + return 0; +} + + +static int cafe_v4l_release(struct inode *inode, struct file *filp) +{ + struct cafe_camera *cam = filp->private_data; + + mutex_lock(&cam->s_mutex); + (cam->users)--; + if (filp == cam->owner) { + cafe_ctlr_stop_dma(cam); + cafe_free_sio_buffers(cam); + cam->owner = NULL; + } + if (cam->users == 0) { + cafe_ctlr_power_down(cam); + if (! alloc_bufs_at_load) + cafe_free_dma_bufs(cam); + } + mutex_unlock(&cam->s_mutex); + return 0; +} + + + +static unsigned int cafe_v4l_poll(struct file *filp, + struct poll_table_struct *pt) +{ + struct cafe_camera *cam = filp->private_data; + + poll_wait(filp, &cam->iowait, pt); + if (cam->next_buf >= 0) + return POLLIN | POLLRDNORM; + return 0; +} + + + +static int cafe_vidioc_queryctrl(struct file *filp, void *priv, + struct v4l2_queryctrl *qc) +{ + struct cafe_camera *cam = filp->private_data; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_QUERYCTRL, qc); + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int cafe_vidioc_g_ctrl(struct file *filp, void *priv, + struct v4l2_control *ctrl) +{ + struct cafe_camera *cam = filp->private_data; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_G_CTRL, ctrl); + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int cafe_vidioc_s_ctrl(struct file *filp, void *priv, + struct v4l2_control *ctrl) +{ + struct cafe_camera *cam = filp->private_data; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_S_CTRL, ctrl); + mutex_unlock(&cam->s_mutex); + return ret; +} + + + + + +static int cafe_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strcpy(cap->driver, "cafe_ccic"); + strcpy(cap->card, "cafe_ccic"); + cap->version = CAFE_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + return 0; +} + + +/* + * The default format we use until somebody says otherwise. + */ +static struct v4l2_pix_format cafe_def_pix_format = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .pixelformat = V4L2_PIX_FMT_YUYV, + .field = V4L2_FIELD_NONE, + .bytesperline = VGA_WIDTH*2, + .sizeimage = VGA_WIDTH*VGA_HEIGHT*2, +}; + +static int cafe_vidioc_enum_fmt_cap(struct file *filp, + void *priv, struct v4l2_fmtdesc *fmt) +{ + struct cafe_camera *cam = priv; + int ret; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_ENUM_FMT, fmt); + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv, + struct v4l2_format *fmt) +{ + struct cafe_camera *cam = priv; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_TRY_FMT, fmt); + mutex_unlock(&cam->s_mutex); + return ret; +} + +static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv, + struct v4l2_format *fmt) +{ + struct cafe_camera *cam = priv; + int ret; + + /* + * Can't do anything if the device is not idle + * Also can't if there are streaming buffers in place. + */ + if (cam->state != S_IDLE || cam->n_sbufs > 0) + return -EBUSY; + /* + * See if the formatting works in principle. + */ + ret = cafe_vidioc_try_fmt_cap(filp, priv, fmt); + if (ret) + return ret; + /* + * Now we start to change things for real, so let's do it + * under lock. + */ + mutex_lock(&cam->s_mutex); + cam->pix_format = fmt->fmt.pix; + /* + * Make sure we have appropriate DMA buffers. + */ + ret = -ENOMEM; + if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage) + cafe_free_dma_bufs(cam); + if (cam->nbufs == 0) { + if (cafe_alloc_dma_bufs(cam, 0)) + goto out; + } + /* + * It looks like this might work, so let's program the sensor. + */ + ret = cafe_cam_configure(cam); + if (! ret) + ret = cafe_ctlr_configure(cam); + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +/* + * Return our stored notion of how the camera is/should be configured. + * The V4l2 spec wants us to be smarter, and actually get this from + * the camera (and not mess with it at open time). Someday. + */ +static int cafe_vidioc_g_fmt_cap(struct file *filp, void *priv, + struct v4l2_format *f) +{ + struct cafe_camera *cam = priv; + + f->fmt.pix = cam->pix_format; + return 0; +} + +/* + * We only have one input - the sensor - so minimize the nonsense here. + */ +static int cafe_vidioc_enum_input(struct file *filp, void *priv, + struct v4l2_input *input) +{ + if (input->index != 0) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + input->std = V4L2_STD_ALL; /* Not sure what should go here */ + strcpy(input->name, "Camera"); + return 0; +} + +static int cafe_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int cafe_vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +/* from vivi.c */ +static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) +{ + return 0; +} + +static void cafe_v4l_dev_release(struct video_device *vd) +{ + struct cafe_camera *cam = container_of(vd, struct cafe_camera, v4ldev); + + kfree(cam); +} + + +/* + * This template device holds all of those v4l2 methods; we + * clone it for specific real devices. + */ + +static struct file_operations cafe_v4l_fops = { + .owner = THIS_MODULE, + .open = cafe_v4l_open, + .release = cafe_v4l_release, + .read = cafe_v4l_read, + .poll = cafe_v4l_poll, + .mmap = cafe_v4l_mmap, + .ioctl = video_ioctl2, + .llseek = no_llseek, +}; + +static struct video_device cafe_v4l_template = { + .name = "cafe", + .type = VFL_TYPE_GRABBER, + .type2 = VID_TYPE_CAPTURE, + .minor = -1, /* Get one dynamically */ + .tvnorms = V4L2_STD_NTSC_M, + .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */ + + .fops = &cafe_v4l_fops, + .release = cafe_v4l_dev_release, + + .vidioc_querycap = cafe_vidioc_querycap, + .vidioc_enum_fmt_cap = cafe_vidioc_enum_fmt_cap, + .vidioc_try_fmt_cap = cafe_vidioc_try_fmt_cap, + .vidioc_s_fmt_cap = cafe_vidioc_s_fmt_cap, + .vidioc_g_fmt_cap = cafe_vidioc_g_fmt_cap, + .vidioc_enum_input = cafe_vidioc_enum_input, + .vidioc_g_input = cafe_vidioc_g_input, + .vidioc_s_input = cafe_vidioc_s_input, + .vidioc_s_std = cafe_vidioc_s_std, + .vidioc_reqbufs = cafe_vidioc_reqbufs, + .vidioc_querybuf = cafe_vidioc_querybuf, + .vidioc_qbuf = cafe_vidioc_qbuf, + .vidioc_dqbuf = cafe_vidioc_dqbuf, + .vidioc_streamon = cafe_vidioc_streamon, + .vidioc_streamoff = cafe_vidioc_streamoff, + .vidioc_queryctrl = cafe_vidioc_queryctrl, + .vidioc_g_ctrl = cafe_vidioc_g_ctrl, + .vidioc_s_ctrl = cafe_vidioc_s_ctrl, + /* Do cropping someday */ +}; + + + + + + + +/* ---------------------------------------------------------------------- */ +/* + * Interrupt handler stuff + */ + +/* + * Create RGB32 from RGB444 so it can be displayed before the applications + * know about the latter format. + */ +static void cafe_fake_rgb32(struct cafe_camera *cam, char *dest, char *src) +{ + int i; + u16 *ssrc = (u16 *) src; + + /* RGB444 version */ + for (i = 0; i < cam->pix_format.sizeimage; i += 4) { + // dest[0] = (*ssrc & 0xf000) >> 8; + dest[0] = (*ssrc & 0x000f) << 4; + dest[1] = (*ssrc & 0x00f0); + dest[2] = (*ssrc & 0x0f00) >> 4; + dest[3] = (*ssrc & 0xf000); /* Alpha */ + dest += 4; + ssrc++; + } +#if 0 + /* RGB565 version */ + for (i = 0; i < cam->pix_format.sizeimage; i += 4) { + dest[0] = (*ssrc & 0xf800) >> 8; + dest[1] = (*ssrc & 0x07e0) >> 3; + dest[2] = (*ssrc & 0x001f) << 3; + dest[3] = 0; + dest += 4; + ssrc++; + } +#endif +} + + +static void cafe_frame_tasklet(unsigned long data) +{ + struct cafe_camera *cam = (struct cafe_camera *) data; + int i; + unsigned long flags; + struct cafe_sio_buffer *sbuf; + + spin_lock_irqsave(&cam->dev_lock, flags); + for (i = 0; i < cam->nbufs; i++) { + int bufno = cam->next_buf; + if (bufno < 0) { /* "will never happen" */ + cam_err(cam, "No valid bufs in tasklet!\n"); + break; + } + if (++(cam->next_buf) >= cam->nbufs) + cam->next_buf = 0; + if (! test_bit(bufno, &cam->flags)) + continue; + if (list_empty(&cam->sb_avail)) + break; /* Leave it valid, hope for better later */ + clear_bit(bufno, &cam->flags); + /* + * We could perhaps drop the spinlock during this + * big copy. Something to consider. + */ + sbuf = list_entry(cam->sb_avail.next, + struct cafe_sio_buffer, list); + if (cam->pix_format.pixelformat == V4L2_PIX_FMT_RGB32) + cafe_fake_rgb32(cam, sbuf->buffer, cam->dma_bufs[bufno]); + else + memcpy(sbuf->buffer, cam->dma_bufs[bufno], + cam->pix_format.sizeimage); + sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage; + sbuf->v4lbuf.sequence = cam->buf_seq[bufno]; + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; + list_move_tail(&sbuf->list, &cam->sb_full); + } + if (! list_empty(&cam->sb_full)) + wake_up(&cam->iowait); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + + + +static void cafe_frame_complete(struct cafe_camera *cam, int frame) +{ + /* + * Basic frame housekeeping. + */ + if (test_bit(frame, &cam->flags) && printk_ratelimit()) + cam_err(cam, "Frame overrun on %d, frames lost\n", frame); + set_bit(frame, &cam->flags); + clear_bit(CF_DMA_ACTIVE, &cam->flags); + if (cam->next_buf < 0) + cam->next_buf = frame; + cam->buf_seq[frame] = ++(cam->sequence); + + switch (cam->state) { + /* + * If in single read mode, try going speculative. + */ + case S_SINGLEREAD: + cam->state = S_SPECREAD; + cam->specframes = 0; + wake_up(&cam->iowait); + break; + + /* + * If we are already doing speculative reads, and nobody is + * reading them, just stop. + */ + case S_SPECREAD: + if (++(cam->specframes) >= cam->nbufs) { + cafe_ctlr_stop(cam); + cafe_ctlr_irq_disable(cam); + cam->state = S_IDLE; + } + wake_up(&cam->iowait); + break; + /* + * For the streaming case, we defer the real work to the + * camera tasklet. + * + * FIXME: if the application is not consuming the buffers, + * we should eventually put things on hold and restart in + * vidioc_dqbuf(). + */ + case S_STREAMING: + tasklet_schedule(&cam->s_tasklet); + break; + + default: + cam_err(cam, "Frame interrupt in non-operational state\n"); + break; + } +} + + + + +static void cafe_frame_irq(struct cafe_camera *cam, unsigned int irqs) +{ + unsigned int frame; + + cafe_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); /* Clear'em all */ + /* + * Handle any frame completions. There really should + * not be more than one of these, or we have fallen + * far behind. + */ + for (frame = 0; frame < cam->nbufs; frame++) + if (irqs & (IRQ_EOF0 << frame)) + cafe_frame_complete(cam, frame); + /* + * If a frame starts, note that we have DMA active. This + * code assumes that we won't get multiple frame interrupts + * at once; may want to rethink that. + */ + if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) + set_bit(CF_DMA_ACTIVE, &cam->flags); +} + + + +static irqreturn_t cafe_irq(int irq, void *data) +{ + struct cafe_camera *cam = data; + unsigned int irqs; + + spin_lock(&cam->dev_lock); + irqs = cafe_reg_read(cam, REG_IRQSTAT); + if ((irqs & ALLIRQS) == 0) { + spin_unlock(&cam->dev_lock); + return IRQ_NONE; + } + if (irqs & FRAMEIRQS) + cafe_frame_irq(cam, irqs); + if (irqs & TWSIIRQS) { + cafe_reg_write(cam, REG_IRQSTAT, TWSIIRQS); + wake_up(&cam->smbus_wait); + } + spin_unlock(&cam->dev_lock); + return IRQ_HANDLED; +} + + +/* -------------------------------------------------------------------------- */ +#ifdef CONFIG_VIDEO_ADV_DEBUG +/* + * Debugfs stuff. + */ + +static char cafe_debug_buf[1024]; +static struct dentry *cafe_dfs_root; + +static void cafe_dfs_setup(void) +{ + cafe_dfs_root = debugfs_create_dir("cafe_ccic", NULL); + if (IS_ERR(cafe_dfs_root)) { + cafe_dfs_root = NULL; /* Never mind */ + printk(KERN_NOTICE "cafe_ccic unable to set up debugfs\n"); + } +} + +static void cafe_dfs_shutdown(void) +{ + if (cafe_dfs_root) + debugfs_remove(cafe_dfs_root); +} + +static int cafe_dfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t cafe_dfs_read_regs(struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + struct cafe_camera *cam = file->private_data; + char *s = cafe_debug_buf; + int offset; + + for (offset = 0; offset < 0x44; offset += 4) + s += sprintf(s, "%02x: %08x\n", offset, + cafe_reg_read(cam, offset)); + for (offset = 0x88; offset <= 0x90; offset += 4) + s += sprintf(s, "%02x: %08x\n", offset, + cafe_reg_read(cam, offset)); + for (offset = 0xb4; offset <= 0xbc; offset += 4) + s += sprintf(s, "%02x: %08x\n", offset, + cafe_reg_read(cam, offset)); + for (offset = 0x3000; offset <= 0x300c; offset += 4) + s += sprintf(s, "%04x: %08x\n", offset, + cafe_reg_read(cam, offset)); + return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf, + s - cafe_debug_buf); +} + +static struct file_operations cafe_dfs_reg_ops = { + .owner = THIS_MODULE, + .read = cafe_dfs_read_regs, + .open = cafe_dfs_open +}; + +static ssize_t cafe_dfs_read_cam(struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + struct cafe_camera *cam = file->private_data; + char *s = cafe_debug_buf; + int offset; + + if (! cam->sensor) + return -EINVAL; + for (offset = 0x0; offset < 0x8a; offset++) + { + u8 v; + + cafe_smbus_read_data(cam, cam->sensor->addr, offset, &v); + s += sprintf(s, "%02x: %02x\n", offset, v); + } + return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf, + s - cafe_debug_buf); +} + +static struct file_operations cafe_dfs_cam_ops = { + .owner = THIS_MODULE, + .read = cafe_dfs_read_cam, + .open = cafe_dfs_open +}; + + + +static void cafe_dfs_cam_setup(struct cafe_camera *cam) +{ + char fname[40]; + + if (!cafe_dfs_root) + return; + sprintf(fname, "regs-%d", cam->v4ldev.minor); + cam->dfs_regs = debugfs_create_file(fname, 0444, cafe_dfs_root, + cam, &cafe_dfs_reg_ops); + sprintf(fname, "cam-%d", cam->v4ldev.minor); + cam->dfs_cam_regs = debugfs_create_file(fname, 0444, cafe_dfs_root, + cam, &cafe_dfs_cam_ops); +} + + +static void cafe_dfs_cam_shutdown(struct cafe_camera *cam) +{ + if (! IS_ERR(cam->dfs_regs)) + debugfs_remove(cam->dfs_regs); + if (! IS_ERR(cam->dfs_cam_regs)) + debugfs_remove(cam->dfs_cam_regs); +} + +#else + +#define cafe_dfs_setup() +#define cafe_dfs_shutdown() +#define cafe_dfs_cam_setup(cam) +#define cafe_dfs_cam_shutdown(cam) +#endif /* CONFIG_VIDEO_ADV_DEBUG */ + + + + +/* ------------------------------------------------------------------------*/ +/* + * PCI interface stuff. + */ + +static int cafe_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int ret; + u16 classword; + struct cafe_camera *cam; + /* + * Make sure we have a camera here - we'll get calls for + * the other cafe devices as well. + */ + pci_read_config_word(pdev, PCI_CLASS_DEVICE, &classword); + if (classword != PCI_CLASS_MULTIMEDIA_VIDEO) + return -ENODEV; + /* + * Start putting together one of our big camera structures. + */ + ret = -ENOMEM; + cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL); + if (cam == NULL) + goto out; + mutex_init(&cam->s_mutex); + mutex_lock(&cam->s_mutex); + spin_lock_init(&cam->dev_lock); + cam->state = S_NOTREADY; + cafe_set_config_needed(cam, 1); + init_waitqueue_head(&cam->smbus_wait); + init_waitqueue_head(&cam->iowait); + cam->pdev = pdev; + cam->pix_format = cafe_def_pix_format; + INIT_LIST_HEAD(&cam->dev_list); + INIT_LIST_HEAD(&cam->sb_avail); + INIT_LIST_HEAD(&cam->sb_full); + tasklet_init(&cam->s_tasklet, cafe_frame_tasklet, (unsigned long) cam); + /* + * Get set up on the PCI bus. + */ + ret = pci_enable_device(pdev); + if (ret) + goto out_free; + pci_set_master(pdev); + + ret = -EIO; + cam->regs = pci_iomap(pdev, 0, 0); + if (! cam->regs) { + printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n"); + goto out_free; + } + ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam); + if (ret) + goto out_iounmap; + cafe_ctlr_init(cam); + cafe_ctlr_power_up(cam); + /* + * Set up I2C/SMBUS communications + */ + mutex_unlock(&cam->s_mutex); /* attach can deadlock */ + ret = cafe_smbus_setup(cam); + if (ret) + goto out_freeirq; + /* + * Get the v4l2 setup done. + */ + mutex_lock(&cam->s_mutex); + cam->v4ldev = cafe_v4l_template; + cam->v4ldev.debug = 0; +// cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG; + ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1); + if (ret) + goto out_smbus; + /* + * If so requested, try to get our DMA buffers now. + */ + if (alloc_bufs_at_load) { + if (cafe_alloc_dma_bufs(cam, 1)) + cam_warn(cam, "Unable to alloc DMA buffers at load" + " will try again later."); + } + + cafe_dfs_cam_setup(cam); + mutex_unlock(&cam->s_mutex); + cafe_add_dev(cam); + return 0; + + out_smbus: + cafe_smbus_shutdown(cam); + out_freeirq: + cafe_ctlr_power_down(cam); + free_irq(pdev->irq, cam); + out_iounmap: + pci_iounmap(pdev, cam->regs); + out_free: + kfree(cam); + out: + return ret; +} + + +/* + * Shut down an initialized device + */ +static void cafe_shutdown(struct cafe_camera *cam) +{ +/* FIXME: Make sure we take care of everything here */ + cafe_dfs_cam_shutdown(cam); + if (cam->n_sbufs > 0) + /* What if they are still mapped? Shouldn't be, but... */ + cafe_free_sio_buffers(cam); + cafe_remove_dev(cam); + cafe_ctlr_stop_dma(cam); + cafe_ctlr_power_down(cam); + cafe_smbus_shutdown(cam); + cafe_free_dma_bufs(cam); + free_irq(cam->pdev->irq, cam); + pci_iounmap(cam->pdev, cam->regs); + video_unregister_device(&cam->v4ldev); + /* kfree(cam); done in v4l_release () */ +} + + +static void cafe_pci_remove(struct pci_dev *pdev) +{ + struct cafe_camera *cam = cafe_find_by_pdev(pdev); + + if (cam == NULL) { + cam_warn(cam, "pci_remove on unknown pdev %p\n", pdev); + return; + } + mutex_lock(&cam->s_mutex); + if (cam->users > 0) + cam_warn(cam, "Removing a device with users!\n"); + cafe_shutdown(cam); +/* No unlock - it no longer exists */ +} + + + + +static struct pci_device_id cafe_ids[] = { + { PCI_DEVICE(0x1148, 0x4340) }, /* Temporary ID on devel board */ + { PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */ + { PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, cafe_ids); + +static struct pci_driver cafe_pci_driver = { + .name = "cafe1000-ccic", + .id_table = cafe_ids, + .probe = cafe_pci_probe, + .remove = cafe_pci_remove, +}; + + + + +static int __init cafe_init(void) +{ + int ret; + + printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n", + CAFE_VERSION); + cafe_dfs_setup(); + ret = pci_register_driver(&cafe_pci_driver); + if (ret) { + printk(KERN_ERR "Unable to register cafe_ccic driver\n"); + goto out; + } + request_module("ov7670"); /* FIXME want something more general */ + ret = 0; + + out: + return ret; +} + + +static void __exit cafe_exit(void) +{ + pci_unregister_driver(&cafe_pci_driver); + cafe_dfs_shutdown(); +} + +module_init(cafe_init); +module_exit(cafe_exit); diff --git a/linux/drivers/media/video/cx88/Kconfig b/linux/drivers/media/video/cx88/Kconfig index 0f9d96963..b2a66ba62 100644 --- a/linux/drivers/media/video/cx88/Kconfig +++ b/linux/drivers/media/video/cx88/Kconfig @@ -53,6 +53,7 @@ config VIDEO_CX88_DVB select DVB_OR51132 if !DVB_FE_CUSTOMISE select DVB_CX22702 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index dc27d74eb..896beb7ed 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -975,9 +975,10 @@ static int mpeg_open(struct inode *inode, struct file *file) struct cx8802_driver *drv = NULL; int err; + dev = cx8802_get_device(inode); + dprintk( 1, "%s\n", __FUNCTION__); - dev = cx8802_get_device(inode); if (dev == NULL) return -ENODEV; diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 184903779..e4f3a6a55 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1564,6 +1564,10 @@ struct cx88_subid cx88_subids[] = { },{ .subvendor = 0x17de, .subdevice = 0x0840, + .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, + },{ + .subvendor = 0x1421, + .subdevice = 0x0305, .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, },{ .subvendor = 0x18ac, diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 56ef3b413..105f60f4b 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -43,7 +43,7 @@ #include "cx22702.h" #include "or51132.h" #include "lgdt330x.h" -#include "lg_h06xf.h" +#include "lgh06xf.h" #include "nxt200x.h" #include "cx24123.h" #include "isl6421.h" @@ -316,10 +316,12 @@ static struct cx22702_config hauppauge_novat_config = { .demod_address = 0x43, .output_mode = CX22702_SERIAL_OUTPUT, }; + static struct cx22702_config hauppauge_hvr1100_config = { .demod_address = 0x63, .output_mode = CX22702_SERIAL_OUTPUT, }; + static struct cx22702_config hauppauge_hvr3000_config = { .demod_address = 0x63, .output_mode = CX22702_SERIAL_OUTPUT, @@ -393,18 +395,6 @@ static int lgdt3302_tuner_set_params(struct dvb_frontend* fe, return 0; } -static int lgdt3303_tuner_set_params(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) -{ - struct cx8802_dev *dev= fe->dvb->priv; - struct cx88_core *core = dev->core; - - /* Put the analog decoder in standby to keep it quiet */ - cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); - - return lg_h06xf_pll_set(fe, &core->i2c_adap, params); -} - static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index) { struct cx8802_dev *dev= fe->dvb->priv; @@ -720,7 +710,8 @@ static int dvb_register(struct cx8802_dev *dev) &fusionhdtv_5_gold, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params; + dvb_attach(lgh06xf_attach, dev->dvb.frontend, + &dev->core->i2c_adap); } } break; @@ -738,7 +729,8 @@ static int dvb_register(struct cx8802_dev *dev) &pchdtv_hd5500, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params; + dvb_attach(lgh06xf_attach, dev->dvb.frontend, + &dev->core->i2c_adap); } } break; diff --git a/linux/drivers/media/video/cx88/cx88-input.c b/linux/drivers/media/video/cx88/cx88-input.c index 2908916cb..a5e4b7b50 100644 --- a/linux/drivers/media/video/cx88/cx88-input.c +++ b/linux/drivers/media/video/cx88/cx88-input.c @@ -156,6 +156,35 @@ static void cx88_ir_work(void *data) mod_timer(&ir->timer, timeout); } +static void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) +{ + if (ir->polling) { + INIT_WORK(&ir->work, cx88_ir_work, ir); + init_timer(&ir->timer); + ir->timer.function = ir_timer; + ir->timer.data = (unsigned long)ir; + schedule_work(&ir->work); + } + if (ir->sampling) { + core->pci_irqmask |= (1 << 18); /* IR_SMP_INT */ + cx_write(MO_DDS_IO, 0xa80a80); /* 4 kHz sample rate */ + cx_write(MO_DDSCFG_IO, 0x5); /* enable */ + } +} + +static void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) +{ + if (ir->sampling) { + cx_write(MO_DDSCFG_IO, 0x0); + core->pci_irqmask &= ~(1 << 18); + } + + if (ir->polling) { + del_timer_sync(&ir->timer); + flush_scheduled_work(); + } +} + /* ---------------------------------------------------------------------- */ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) @@ -164,14 +193,12 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) struct input_dev *input_dev; IR_KEYTAB_TYPE *ir_codes = NULL; int ir_type = IR_TYPE_OTHER; + int err = -ENOMEM; ir = kzalloc(sizeof(*ir), GFP_KERNEL); input_dev = input_allocate_device(); - if (!ir || !input_dev) { - kfree(ir); - input_free_device(input_dev); - return -ENOMEM; - } + if (!ir || !input_dev) + goto err_out_free; ir->input = input_dev; @@ -203,13 +230,19 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->sampling = 1; break; case CX88_BOARD_WINFAST_DTV2000H: - case CX88_BOARD_WINFAST2000XP_EXPERT: ir_codes = ir_codes_winfast; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0x8f8; ir->mask_keyup = 0x100; ir->polling = 50; /* ms */ break; + case CX88_BOARD_WINFAST2000XP_EXPERT: + ir_codes = ir_codes_winfast; + ir->gpio_addr = MO_GP0_IO; + ir->mask_keycode = 0x8f8; + ir->mask_keyup = 0x100; + ir->polling = 1; /* ms */ + break; case CX88_BOARD_IODATA_GVBCTV7E: ir_codes = ir_codes_iodata_bctv7e; ir->gpio_addr = MO_GP0_IO; @@ -217,7 +250,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->mask_keydown = 0x02; ir->polling = 5; /* ms */ break; - case CX88_BOARD_PROLINK_PLAYTVPVR: + case CX88_BOARD_PROLINK_PLAYTVPVR: case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO: ir_codes = ir_codes_pixelview; ir->gpio_addr = MO_GP1_IO; @@ -275,9 +308,8 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) } if (NULL == ir_codes) { - kfree(ir); - input_free_device(input_dev); - return -ENODEV; + err = -ENODEV; + goto err_out_free; } /* init input device */ @@ -308,23 +340,22 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->core = core; core->ir = ir; - if (ir->polling) { - INIT_WORK(&ir->work, cx88_ir_work, ir); - init_timer(&ir->timer); - ir->timer.function = ir_timer; - ir->timer.data = (unsigned long)ir; - schedule_work(&ir->work); - } - if (ir->sampling) { - core->pci_irqmask |= (1 << 18); /* IR_SMP_INT */ - cx_write(MO_DDS_IO, 0xa80a80); /* 4 kHz sample rate */ - cx_write(MO_DDSCFG_IO, 0x5); /* enable */ - } + cx88_ir_start(core, ir); /* all done */ - input_register_device(ir->input); + err = input_register_device(ir->input); + if (err) + goto err_out_stop; return 0; + + err_out_stop: + cx88_ir_stop(core, ir); + core->ir = NULL; + err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; } int cx88_ir_fini(struct cx88_core *core) @@ -335,15 +366,7 @@ int cx88_ir_fini(struct cx88_core *core) if (NULL == ir) return 0; - if (ir->sampling) { - cx_write(MO_DDSCFG_IO, 0x0); - core->pci_irqmask &= ~(1 << 18); - } - if (ir->polling) { - del_timer(&ir->timer); - flush_scheduled_work(); - } - + cx88_ir_stop(core, ir); input_unregister_device(ir->input); kfree(ir); diff --git a/linux/drivers/media/video/et61x251/et61x251_core.c b/linux/drivers/media/video/et61x251/et61x251_core.c index d8961d82c..17577a9cc 100644 --- a/linux/drivers/media/video/et61x251/et61x251_core.c +++ b/linux/drivers/media/video/et61x251/et61x251_core.c @@ -1186,8 +1186,6 @@ static void et61x251_release_resources(struct et61x251_device* cam) video_set_drvdata(cam->v4ldev, NULL); video_unregister_device(cam->v4ldev); - usb_put_dev(cam->usbdev); - mutex_unlock(&et61x251_sysfs_lock); kfree(cam->control_buffer); @@ -1279,6 +1277,7 @@ static int et61x251_release(struct inode* inode, struct file* filp) if (cam->state & DEV_DISCONNECTED) { et61x251_release_resources(cam); + usb_put_dev(cam->usbdev); mutex_unlock(&cam->dev_mutex); kfree(cam); return 0; diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c index b826dcca2..9ac3e1d9d 100644 --- a/linux/drivers/media/video/ir-kbd-i2c.c +++ b/linux/drivers/media/video/ir-kbd-i2c.c @@ -314,15 +314,14 @@ static int ir_attach(struct i2c_adapter *adap, int addr, int ir_type; struct IR_i2c *ir; struct input_dev *input_dev; + int err; ir = kzalloc(sizeof(struct IR_i2c),GFP_KERNEL); input_dev = input_allocate_device(); if (!ir || !input_dev) { - input_free_device(input_dev); - kfree(ir); - return -ENOMEM; + err = -ENOMEM; + goto err_out_free; } - memset(ir,0,sizeof(*ir)); ir->c = client_template; ir->input = input_dev; @@ -370,26 +369,27 @@ static int ir_attach(struct i2c_adapter *adap, int addr, break; default: /* shouldn't happen */ - printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n",addr); - kfree(ir); - return -1; + printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n", addr); + err = -ENODEV; + goto err_out_free; } /* Sets name */ snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (%s)", name); - ir->ir_codes=ir_codes; + ir->ir_codes = ir_codes; /* register i2c device * At device register, IR codes may be changed to be * board dependent. */ - i2c_attach_client(&ir->c); + err = i2c_attach_client(&ir->c); + if (err) + goto err_out_free; /* If IR not supported or disabled, unregisters driver */ if (ir->get_key == NULL) { - i2c_detach_client(&ir->c); - kfree(ir); - return -1; + err = -ENODEV; + goto err_out_detach; } /* Phys addr can only be set after attaching (for ir->c.dev.bus_id) */ @@ -398,15 +398,17 @@ static int ir_attach(struct i2c_adapter *adap, int addr, ir->c.dev.bus_id); /* init + register input device */ - ir_input_init(input_dev,&ir->ir,ir_type,ir->ir_codes); + ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes); input_dev->id.bustype = BUS_I2C; input_dev->name = ir->c.name; input_dev->phys = ir->phys; - /* register event device */ - input_register_device(ir->input); + err = input_register_device(ir->input); + if (err) + goto err_out_detach; + printk(DEVNAME ": %s detected at %s [%s]\n", - ir->input->name,ir->input->phys,adap->name); + ir->input->name, ir->input->phys, adap->name); /* start polling via eventd */ INIT_WORK(&ir->work, ir_work, ir); @@ -416,6 +418,13 @@ static int ir_attach(struct i2c_adapter *adap, int addr, schedule_work(&ir->work); return 0; + + err_out_detach: + i2c_detach_client(&ir->c); + err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; } static int ir_detach(struct i2c_client *client) @@ -423,7 +432,7 @@ static int ir_detach(struct i2c_client *client) struct IR_i2c *ir = i2c_get_clientdata(client); /* kill outstanding polls */ - del_timer(&ir->timer); + del_timer_sync(&ir->timer); flush_scheduled_work(); /* unregister devices */ diff --git a/linux/drivers/media/video/ov7670.c b/linux/drivers/media/video/ov7670.c new file mode 100644 index 000000000..875d5f826 --- /dev/null +++ b/linux/drivers/media/video/ov7670.c @@ -0,0 +1,1330 @@ +/* + * A V4L2 driver for OmniVision OV7670 cameras. + * + * Copyright 2006 One Laptop Per Child Association, Inc. Written + * by Jonathan Corbet with substantial inspiration from Mark + * McClelland's ovcamchip code. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/videodev.h> +#include "compat.h" +#include <media/v4l2-common.h> +#include <linux/i2c.h> + + +MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net."); +MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); +MODULE_LICENSE("GPL"); + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 + +/* + * The 7670 sits on i2c with ID 0x42 + */ +#define OV7670_I2C_ADDR 0x42 + +/* Registers */ +#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define REG_COM1 0x04 /* Control 1 */ +#define COM1_CCIR656 0x40 /* CCIR656 enable */ +#define REG_BAVE 0x05 /* U/B Average level */ +#define REG_GbAVE 0x06 /* Y/Gb Average level */ +#define REG_AECHH 0x07 /* AEC MS 5 bits */ +#define REG_RAVE 0x08 /* V/R Average level */ +#define REG_COM2 0x09 /* Control 2 */ +#define COM2_SSLEEP 0x10 /* Soft sleep mode */ +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ +#define REG_COM3 0x0c /* Control 3 */ +#define COM3_SWAP 0x40 /* Byte swap */ +#define COM3_SCALEEN 0x08 /* Enable scaling */ +#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ +#define REG_COM4 0x0d /* Control 4 */ +#define REG_COM5 0x0e /* All "reserved" */ +#define REG_COM6 0x0f /* Control 6 */ +#define REG_AECH 0x10 /* More bits of AEC value */ +#define REG_CLKRC 0x11 /* Clocl control */ +#define CLK_EXT 0x40 /* Use external clock directly */ +#define CLK_SCALE 0x3f /* Mask for internal clock scale */ +#define REG_COM7 0x12 /* Control 7 */ +#define COM7_RESET 0x80 /* Register reset */ +#define COM7_FMT_MASK 0x38 +#define COM7_FMT_VGA 0x00 +#define COM7_FMT_CIF 0x20 /* CIF format */ +#define COM7_FMT_QVGA 0x10 /* QVGA format */ +#define COM7_FMT_QCIF 0x08 /* QCIF format */ +#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ +#define COM7_YUV 0x00 /* YUV */ +#define COM7_BAYER 0x01 /* Bayer format */ +#define COM7_PBAYER 0x05 /* "Processed bayer" */ +#define REG_COM8 0x13 /* Control 8 */ +#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define COM8_BFILT 0x20 /* Band filter enable */ +#define COM8_AGC 0x04 /* Auto gain enable */ +#define COM8_AWB 0x02 /* White balance enable */ +#define COM8_AEC 0x01 /* Auto exposure enable */ +#define REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define REG_COM10 0x15 /* Control 10 */ +#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ +#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ +#define COM10_HREF_REV 0x08 /* Reverse HREF */ +#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ +#define COM10_VS_NEG 0x02 /* VSYNC negative */ +#define COM10_HS_NEG 0x01 /* HSYNC negative */ +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ +#define REG_MVFP 0x1e /* Mirror / vflip */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ + +#define REG_AEW 0x24 /* AGC upper limit */ +#define REG_AEB 0x25 /* AGC lower limit */ +#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define REG_HSYST 0x30 /* HSYNC rising edge delay */ +#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_TSLB 0x3a /* lots of stuff */ +#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ +#define REG_COM11 0x3b /* Control 11 */ +#define COM11_NIGHT 0x80 /* NIght mode enable */ +#define COM11_NMFR 0x60 /* Two bit NM frame rate */ +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define COM11_50HZ 0x08 /* Manual 50Hz select */ +#define COM11_EXP 0x02 +#define REG_COM12 0x3c /* Control 12 */ +#define COM12_HREF 0x80 /* HREF always */ +#define REG_COM13 0x3d /* Control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ +#define REG_COM14 0x3e /* Control 14 */ +#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ +#define REG_EDGE 0x3f /* Edge enhancement factor */ +#define REG_COM15 0x40 /* Control 15 */ +#define COM15_R10F0 0x00 /* Data range 10 to F0 */ +#define COM15_R01FE 0x80 /* 01 to FE */ +#define COM15_R00FF 0xc0 /* 00 to FF */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_RGB555 0x30 /* RGB555 output */ +#define REG_COM16 0x41 /* Control 16 */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define REG_COM17 0x42 /* Control 17 */ +#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ +#define COM17_CBAR 0x08 /* DSP Color bar */ + +/* + * This matrix defines how the colors are generated, must be + * tweaked to adjust hue and saturation. + * + * Order: v-red, v-green, v-blue, u-red, u-green, u-blue + * + * They are nine-bit signed quantities, with the sign bit + * stored in 0x58. Sign for v-red is bit 0, and up from there. + */ +#define REG_CMATRIX_BASE 0x4f +#define CMATRIX_LEN 6 +#define REG_CMATRIX_SIGN 0x58 + + +#define REG_BRIGHT 0x55 /* Brightness */ +#define REG_CONTRAS 0x56 /* Contrast control */ + +#define REG_GFIX 0x69 /* Fix gain control */ + +#define REG_RGB444 0x8c /* RGB 444 control */ +#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ +#define R444_RGBX 0x01 /* Empty nibble at end */ + +#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ + +#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define REG_BD60MAX 0xab /* 60hz banding step limit */ + + +/* + * Information we maintain about a known sensor. + */ +struct ov7670_format_struct; /* coming later */ +struct ov7670_info { + struct ov7670_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ +}; + + + + +/* + * The default register settings, as obtained from OmniVision. There + * is really no making sense of most of these - lots of "reserved" values + * and such. + * + * These settings give VGA YUYV. + */ + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +static struct regval_list ov7670_default_regs[] = { + { REG_COM7, COM7_RESET }, +/* + * Clock scale: 3 = 15fps + * 2 = 20fps + * 1 = 30fps + */ + { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ + { REG_TSLB, 0x04 }, /* OV */ + { REG_COM7, 0 }, /* VGA */ + /* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, + { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, + { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, + + { REG_COM3, 0 }, { REG_COM14, 0 }, + /* Mystery scaling numbers */ + { 0x70, 0x3a }, { 0x71, 0x35 }, + { 0x72, 0x11 }, { 0x73, 0xf0 }, + { 0xa2, 0x02 }, { REG_COM10, 0x0 }, + + /* Gamma curve values */ + { 0x7a, 0x20 }, { 0x7b, 0x10 }, + { 0x7c, 0x1e }, { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, + + /* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, + { REG_GAIN, 0 }, { REG_AECH, 0 }, + { REG_COM4, 0x40 }, /* magic reserved bit */ + { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, + { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, + { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, + { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ + { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, + { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, + { REG_HAECC7, 0x94 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, + + /* Almost all of these are magic "reserved" values. */ + { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, + { 0x16, 0x02 }, { REG_MVFP, 0x07|MVFP_MIRROR }, + { 0x21, 0x02 }, { 0x22, 0x91 }, + { 0x29, 0x07 }, { 0x33, 0x0b }, + { 0x35, 0x0b }, { 0x37, 0x1d }, + { 0x38, 0x71 }, { 0x39, 0x2a }, + { REG_COM12, 0x78 }, { 0x4d, 0x40 }, + { 0x4e, 0x20 }, { REG_GFIX, 0 }, + { 0x6b, 0x4a }, { 0x74, 0x10 }, + { 0x8d, 0x4f }, { 0x8e, 0 }, + { 0x8f, 0 }, { 0x90, 0 }, + { 0x91, 0 }, { 0x96, 0 }, + { 0x9a, 0 }, { 0xb0, 0x84 }, + { 0xb1, 0x0c }, { 0xb2, 0x0e }, + { 0xb3, 0x82 }, { 0xb8, 0x0a }, + + /* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, { 0x44, 0xf0 }, + { 0x45, 0x34 }, { 0x46, 0x58 }, + { 0x47, 0x28 }, { 0x48, 0x3a }, + { 0x59, 0x88 }, { 0x5a, 0x88 }, + { 0x5b, 0x44 }, { 0x5c, 0x67 }, + { 0x5d, 0x49 }, { 0x5e, 0x0e }, + { 0x6c, 0x0a }, { 0x6d, 0x55 }, + { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ + { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, + { REG_RED, 0x60 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, + + /* Matrix coefficients */ + { 0x4f, 0x80 }, { 0x50, 0x80 }, + { 0x51, 0 }, { 0x52, 0x22 }, + { 0x53, 0x5e }, { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, + { 0x75, 0x05 }, { 0x76, 0xe1 }, + { 0x4c, 0 }, { 0x77, 0x01 }, + { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, + { 0xc9, 0x60 }, { REG_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, { REG_COM11, COM11_EXP }, + { 0xa4, 0x88 }, { 0x96, 0 }, + { 0x97, 0x30 }, { 0x98, 0x20 }, + { 0x99, 0x30 }, { 0x9a, 0x84 }, + { 0x9b, 0x29 }, { 0x9c, 0x03 }, + { 0x9d, 0x4c }, { 0x9e, 0x3f }, + { 0x78, 0x04 }, + + /* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, { 0xc8, 0xf0 }, + { 0x79, 0x0f }, { 0xc8, 0x00 }, + { 0x79, 0x10 }, { 0xc8, 0x7e }, + { 0x79, 0x0a }, { 0xc8, 0x80 }, + { 0x79, 0x0b }, { 0xc8, 0x01 }, + { 0x79, 0x0c }, { 0xc8, 0x0f }, + { 0x79, 0x0d }, { 0xc8, 0x20 }, + { 0x79, 0x09 }, { 0xc8, 0x80 }, + { 0x79, 0x02 }, { 0xc8, 0xc0 }, + { 0x79, 0x03 }, { 0xc8, 0x40 }, + { 0x79, 0x05 }, { 0xc8, 0x30 }, + { 0x79, 0x26 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + + +/* + * Here we'll try to encapsulate the changes for just the output + * video format. + * + * RGB656 and YUV422 come from OV; RGB444 is homebrewed. + * + * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why. + */ + + +static struct regval_list ov7670_fmt_yuv422[] = { + { REG_COM7, 0x0 }, /* Selects YUV mode */ + { REG_RGB444, 0 }, /* No RGB444 please */ + { REG_COM1, 0 }, + { REG_COM15, COM15_R00FF }, + { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_fmt_rgb565[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, 0 }, /* No RGB444 please */ + { REG_COM1, 0x0 }, + { REG_COM15, COM15_RGB565 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_fmt_rgb444[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ + { REG_COM1, 0x40 }, /* Magic reserved bit */ + { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ + { 0xff, 0xff }, +}; + + + + +/* + * Low-level register I/O. + */ + +static int ov7670_read(struct i2c_client *c, unsigned char reg, + unsigned char *value) +{ + int ret; + + ret = i2c_smbus_read_byte_data(c, reg); + if (ret >= 0) + *value = (unsigned char) ret; + return ret; +} + + +static int ov7670_write(struct i2c_client *c, unsigned char reg, + unsigned char value) +{ + return i2c_smbus_write_byte_data(c, reg, value); +} + +#if 0 /* Not currently used, but maybe should be */ + +static int ov7670_write_mask(struct i2c_client *c, unsigned char reg, + unsigned char value, unsigned char mask) +{ + unsigned char v; + int ret, tries = 0; + + ret = ov7670_read(c, reg, &v); + if (ret < 0) + return ret; + v &= ~mask; + v |= (value & mask); + msleep(10); /* FIXME experiment */ + do { + ret = ov7670_write(c, reg, v); + } while (ret < 0 && ++tries < 3); + return ret; +} + +#endif + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals) +{ + while (vals->reg_num != 0xff || vals->value != 0xff) { + int ret = ov7670_write(c, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + } + return 0; +} + + +/* + * Stuff that knows about the sensor. + */ +static void ov7670_reset(struct i2c_client *client) +{ + ov7670_write(client, REG_COM7, COM7_RESET); + msleep(1); +} + + +static int ov7670_init(struct i2c_client *client) +{ + return ov7670_write_array(client, ov7670_default_regs); +} + + + +static int ov7670_detect(struct i2c_client *client) +{ + unsigned char v; + int ret; + + ret = ov7670_init(client); + if (ret < 0) + return ret; + ret = ov7670_read(client, REG_MIDH, &v); + if (ret < 0) + return ret; + if (v != 0x7f) /* OV manuf. id. */ + return -ENODEV; + ret = ov7670_read(client, REG_MIDL, &v); + if (ret < 0) + return ret; + if (v != 0xa2) + return -ENODEV; + /* + * OK, we know we have an OmniVision chip...but which one? + */ + ret = ov7670_read(client, REG_PID, &v); + if (ret < 0) + return ret; + if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + ret = ov7670_read(client, REG_VER, &v); + if (ret < 0) + return ret; + if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + return 0; +} + + +/* + * Store information about the video data format. The color matrix + * is deeply tied into the format, so keep the relevant values here. + * The magic matrix nubmers come from OmniVision. + */ +static struct ov7670_format_struct { + __u8 *desc; + __u32 pixelformat; + struct regval_list *regs; + int cmatrix[CMATRIX_LEN]; +} ov7670_formats[] = { + { + .desc = "YUYV 4:2:2", + .pixelformat = V4L2_PIX_FMT_YUYV, + .regs = ov7670_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + }, + { + .desc = "RGB 444", + .pixelformat = V4L2_PIX_FMT_RGB444, + .regs = ov7670_fmt_rgb444, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + }, + { + .desc = "RGB 565", + .pixelformat = V4L2_PIX_FMT_RGB565, + .regs = ov7670_fmt_rgb565, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + }, + /* + * Pretend we do RGB32. This is here on the assumption that the + * upper layer will reformat RGB444 appropriately. + * + * The entire purpose for this thing's existence is to enable easy + * display of RGB444 for debugging purposes. It will come out soon. + */ + { + .desc = "RGB32 (faked)", + .pixelformat = V4L2_PIX_FMT_RGB32, + .regs = ov7670_fmt_rgb444, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + }, +}; +#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0])) + +/* + * All formats we support are 2 bytes/pixel. + */ +#define BYTES_PER_PIXEL 2 + +/* + * Then there is the issue of window sizes. Try to capture the info here. + */ + +/* + * QCIF mode is done (by OV) in a very strange way - it actually looks like + * VGA with weird scaling options - they do *not* use the canned QCIF mode + * which is allegedly provided by the sensor. So here's the weird register + * settings. + */ +static struct regval_list ov7670_qcif_regs[] = { + { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, + { REG_COM3, COM3_DCWEN }, + { REG_COM14, COM14_DCWEN | 0x01}, + { 0x73, 0xf1 }, + { 0xa2, 0x52 }, + { 0x7b, 0x1c }, + { 0x7c, 0x28 }, + { 0x7d, 0x3c }, + { 0x7f, 0x69 }, + { REG_COM9, 0x38 }, + { 0xa1, 0x0b }, + { 0x74, 0x19 }, + { 0x9a, 0x80 }, + { 0x43, 0x14 }, + { REG_COM13, 0xc0 }, + { 0xff, 0xff }, +}; + +static struct ov7670_win_size { + int width; + int height; + unsigned char com7_bit; + int hstart; /* Start/stop values for the camera. Note */ + int hstop; /* that they do not always make complete */ + int vstart; /* sense to humans, but evidently the sensor */ + int vstop; /* will do the right thing... */ + struct regval_list *regs; /* Regs to tweak */ +/* h/vref stuff */ +} ov7670_win_sizes[] = { + /* VGA */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .com7_bit = COM7_FMT_VGA, + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ + .vstart = 10, + .vstop = 490, + .regs = NULL, + }, + /* CIF */ + { + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .com7_bit = COM7_FMT_CIF, + .hstart = 170, /* Empirically determined */ + .hstop = 90, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, + /* QVGA */ + { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .com7_bit = COM7_FMT_QVGA, + .hstart = 164, /* Empirically determined */ + .hstop = 20, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, + /* QCIF */ + { + .width = QCIF_WIDTH, + .height = QCIF_HEIGHT, + .com7_bit = COM7_FMT_VGA, /* see comment above */ + .hstart = 456, /* Empirically determined */ + .hstop = 24, + .vstart = 14, + .vstop = 494, + .regs = ov7670_qcif_regs, + }, +}; + +#define N_WIN_SIZES (sizeof(ov7670_win_sizes)/sizeof(ov7670_win_sizes[0])) + + +/* + * Store a set of start/stop values into the camera. + */ +static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop, + int vstart, int vstop) +{ + int ret; + unsigned char v; +/* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = ov7670_write(client, REG_HSTART, (hstart >> 3) & 0xff); + ret += ov7670_write(client, REG_HSTOP, (hstop >> 3) & 0xff); + ret += ov7670_read(client, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += ov7670_write(client, REG_HREF, v); +/* + * Vertical: similar arrangement, but only 10 bits. + */ + ret += ov7670_write(client, REG_VSTART, (vstart >> 2) & 0xff); + ret += ov7670_write(client, REG_VSTOP, (vstop >> 2) & 0xff); + ret += ov7670_read(client, REG_VREF, &v); + v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); + msleep(10); + ret += ov7670_write(client, REG_VREF, v); + return ret; +} + + +static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt) +{ + struct ov7670_format_struct *ofmt; + + if (fmt->index >= N_OV7670_FMTS) + return -EINVAL; + + ofmt = ov7670_formats + fmt->index; + fmt->flags = 0; + strcpy(fmt->description, ofmt->desc); + fmt->pixelformat = ofmt->pixelformat; + return 0; +} + + +static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, + struct ov7670_format_struct **ret_fmt, + struct ov7670_win_size **ret_wsize) +{ + int index; + struct ov7670_win_size *wsize; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + for (index = 0; index < N_OV7670_FMTS; index++) + if (ov7670_formats[index].pixelformat == pix->pixelformat) + break; + if (index >= N_OV7670_FMTS) + return -EINVAL; + if (ret_fmt != NULL) + *ret_fmt = ov7670_formats + index; + /* + * Fields: the OV devices claim to be progressive. + */ + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + else if (pix->field != V4L2_FIELD_NONE) + return -EINVAL; + /* + * Round requested image size down to the nearest + * we support, but not below the smallest. + */ + for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; + wsize++) + if (pix->width >= wsize->width && pix->height >= wsize->height) + break; + if (wsize >= ov7670_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + pix->width = wsize->width; + pix->height = wsize->height; + pix->bytesperline = pix->width*BYTES_PER_PIXEL; + if (pix->pixelformat == V4L2_PIX_FMT_RGB32) + pix->bytesperline *= 2; + pix->sizeimage = pix->height*pix->bytesperline; + return 0; +} + +/* + * Set a format. + */ +static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) +{ + int ret; + struct ov7670_format_struct *ovfmt; + struct ov7670_win_size *wsize; + struct ov7670_info *info = i2c_get_clientdata(c); + unsigned char com7; + + ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize); + if (ret) + return ret; + /* + * COM7 is a pain in the ass, it doesn't like to be read then + * quickly written afterward. But we have everything we need + * to set it absolutely here, as long as the format-specific + * register sets list it first. + */ + com7 = ovfmt->regs[0].value; + com7 |= wsize->com7_bit; + ov7670_write(c, REG_COM7, com7); + /* + * Now write the rest of the array. Also store start/stops + */ + ov7670_write_array(c, ovfmt->regs + 1); + ov7670_set_hw(c, wsize->hstart, wsize->hstop, wsize->vstart, + wsize->vstop); + ret = 0; + if (wsize->regs) + ret = ov7670_write_array(c, wsize->regs); + info->fmt = ovfmt; + return 0; +} + +/* + * Code for dealing with controls. + */ + +#if 0 /* This seems unneeded after all, should probably come out */ +/* + * Fetch and store the color matrix. + */ +static int ov7670_get_cmatrix(struct i2c_client *client, + int matrix[CMATRIX_LEN]) +{ + int i, ret; + unsigned char signbits; + + ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits); + for (i = 0; i < CMATRIX_LEN; i++) { + unsigned char raw; + + ret += ov7670_read(client, REG_CMATRIX_BASE + i, &raw); + matrix[i] = (int) raw; + if (signbits & (1 << i)) + matrix[i] *= -1; + } + return ret; +} +#endif + + + + +static int ov7670_store_cmatrix(struct i2c_client *client, + int matrix[CMATRIX_LEN]) +{ + int i, ret; + unsigned char signbits; + + /* + * Weird crap seems to exist in the upper part of + * the sign bits register, so let's preserve it. + */ + ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits); + signbits &= 0xc0; + + for (i = 0; i < CMATRIX_LEN; i++) { + unsigned char raw; + + if (matrix[i] < 0) { + signbits |= (1 << i); + if (matrix[i] < -255) + raw = 0xff; + else + raw = (-1 * matrix[i]) & 0xff; + } + else { + if (matrix[i] > 255) + raw = 0xff; + else + raw = matrix[i] & 0xff; + } + ret += ov7670_write(client, REG_CMATRIX_BASE + i, raw); + } + ret += ov7670_write(client, REG_CMATRIX_SIGN, signbits); + return ret; +} + + +/* + * Hue also requires messing with the color matrix. It also requires + * trig functions, which tend not to be well supported in the kernel. + * So here is a simple table of sine values, 0-90 degrees, in steps + * of five degrees. Values are multiplied by 1000. + * + * The following naive approximate trig functions require an argument + * carefully limited to -180 <= theta <= 180. + */ +#define SIN_STEP 5 +static const int ov7670_sin_table[] = { + 0, 87, 173, 258, 342, 422, + 499, 573, 642, 707, 766, 819, + 866, 906, 939, 965, 984, 996, + 1000 +}; + +static int ov7670_sine(int theta) +{ + int chs = 1; + int sine; + + if (theta < 0) { + theta = -theta; + chs = -1; + } + if (theta <= 90) + sine = ov7670_sin_table[theta/SIN_STEP]; + else { + theta -= 90; + sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; + } + return sine*chs; +} + +static int ov7670_cosine(int theta) +{ + theta = 90 - theta; + if (theta > 180) + theta -= 360; + else if (theta < -180) + theta += 360; + return ov7670_sine(theta); +} + + + + +static void ov7670_calc_cmatrix(struct ov7670_info *info, + int matrix[CMATRIX_LEN]) +{ + int i; + /* + * Apply the current saturation setting first. + */ + for (i = 0; i < CMATRIX_LEN; i++) + matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; + /* + * Then, if need be, rotate the hue value. + */ + if (info->hue != 0) { + int sinth, costh, tmpmatrix[CMATRIX_LEN]; + + memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); + sinth = ov7670_sine(info->hue); + costh = ov7670_cosine(info->hue); + + matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; + matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; + matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; + matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; + matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; + matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; + } +} + + + +static int ov7670_t_sat(struct i2c_client *client, int value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + info->sat = value; + ov7670_calc_cmatrix(info, matrix); + ret = ov7670_store_cmatrix(client, matrix); + return ret; +} + +static int ov7670_q_sat(struct i2c_client *client, __s32 *value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + + *value = info->sat; + return 0; +} + +static int ov7670_t_hue(struct i2c_client *client, int value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + if (value < -180 || value > 180) + return -EINVAL; + info->hue = value; + ov7670_calc_cmatrix(info, matrix); + ret = ov7670_store_cmatrix(client, matrix); + return ret; +} + + +static int ov7670_q_hue(struct i2c_client *client, __s32 *value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + + *value = info->hue; + return 0; +} + + +/* + * Some weird registers seem to store values in a sign/magnitude format! + */ +static unsigned char ov7670_sm_to_abs(unsigned char v) +{ + if ((v & 0x80) == 0) + return v + 128; + else + return 128 - (v & 0x7f); +} + + +static unsigned char ov7670_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + else + return (128 - v) | 0x80; +} + +static int ov7670_t_brightness(struct i2c_client *client, int value) +{ + unsigned char com8, v; + int ret; + + ov7670_read(client, REG_COM8, &com8); + com8 &= ~COM8_AEC; + ov7670_write(client, REG_COM8, com8); + v = ov7670_abs_to_sm(value); + ret = ov7670_write(client, REG_BRIGHT, v); + return ret; +} + +static int ov7670_q_brightness(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = ov7670_read(client, REG_BRIGHT, &v); + + *value = ov7670_sm_to_abs(v); + return ret; +} + +static int ov7670_t_contrast(struct i2c_client *client, int value) +{ + return ov7670_write(client, REG_CONTRAS, (unsigned char) value); +} + +static int ov7670_q_contrast(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = ov7670_read(client, REG_CONTRAS, &v); + + *value = v; + return ret; +} + +static int ov7670_q_hflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov7670_read(client, REG_MVFP, &v); + *value = (v & MVFP_MIRROR) == MVFP_MIRROR; + return ret; +} + + +static int ov7670_t_hflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov7670_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_MIRROR; + else + v &= ~MVFP_MIRROR; + msleep(10); /* FIXME */ + ret += ov7670_write(client, REG_MVFP, v); + return ret; +} + + + +static int ov7670_q_vflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov7670_read(client, REG_MVFP, &v); + *value = (v & MVFP_FLIP) == MVFP_FLIP; + return ret; +} + + +static int ov7670_t_vflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov7670_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_FLIP; + else + v &= ~MVFP_FLIP; + msleep(10); /* FIXME */ + ret += ov7670_write(client, REG_MVFP, v); + return ret; +} + + +static struct ov7670_control { + struct v4l2_queryctrl qc; + int (*query)(struct i2c_client *c, __s32 *value); + int (*tweak)(struct i2c_client *c, int value); +} ov7670_controls[] = +{ + { + .qc = { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_brightness, + .query = ov7670_q_brightness, + }, + { + .qc = { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0x40, /* XXX ov7670 spec */ + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_contrast, + .query = ov7670_q_contrast, + }, + { + .qc = { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_sat, + .query = ov7670_q_sat, + }, + { + .qc = { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "HUE", + .minimum = -180, + .maximum = 180, + .step = 5, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_hue, + .query = ov7670_q_hue, + }, + { + .qc = { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov7670_t_vflip, + .query = ov7670_q_vflip, + }, + { + .qc = { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov7670_t_hflip, + .query = ov7670_q_hflip, + }, +}; +#define N_CONTROLS (sizeof(ov7670_controls)/sizeof(ov7670_controls[0])) + +static struct ov7670_control *ov7670_find_control(__u32 id) +{ + int i; + + for (i = 0; i < N_CONTROLS; i++) + if (ov7670_controls[i].qc.id == id) + return ov7670_controls + i; + return NULL; +} + + +static int ov7670_queryctrl(struct i2c_client *client, + struct v4l2_queryctrl *qc) +{ + struct ov7670_control *ctrl = ov7670_find_control(qc->id); + + if (ctrl == NULL) + return -EINVAL; + *qc = ctrl->qc; + return 0; +} + +static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov7670_control *octrl = ov7670_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->query(client, &ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + +static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov7670_control *octrl = ov7670_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->tweak(client, ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + + + + + + +/* + * Basic i2c stuff. + */ +static struct i2c_driver ov7670_driver; + +static int ov7670_attach(struct i2c_adapter *adapter) +{ + int ret; + struct i2c_client *client; + struct ov7670_info *info; + + /* + * For now: only deal with adapters we recognize. + */ + if (adapter->id != I2C_HW_SMBUS_CAFE) + return -ENODEV; + + client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL); + if (! client) + return -ENOMEM; + client->adapter = adapter; + client->addr = OV7670_I2C_ADDR; + client->driver = &ov7670_driver, + strcpy(client->name, "OV7670"); + /* + * Set up our info structure. + */ + info = kzalloc(sizeof (struct ov7670_info), GFP_KERNEL); + if (! info) { + ret = -ENOMEM; + goto out_free; + } + info->fmt = &ov7670_formats[0]; + info->sat = 128; /* Review this */ + i2c_set_clientdata(client, info); + + /* + * Make sure it's an ov7670 + */ + ret = ov7670_detect(client); + if (ret) + goto out_free_info; + i2c_attach_client(client); + return 0; + + out_free_info: + kfree(info); + out_free: + kfree(client); + return ret; +} + + +static int ov7670_detach(struct i2c_client *client) +{ + i2c_detach_client(client); + kfree(i2c_get_clientdata(client)); + kfree(client); + return 0; +} + + +static int ov7670_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + switch (cmd) { + case VIDIOC_INT_G_CHIP_IDENT: + * (enum v4l2_chip_ident *) arg = V4L2_IDENT_OV7670; + return 0; + + case VIDIOC_INT_RESET: + ov7670_reset(client); + return 0; + + case VIDIOC_INT_INIT: + return ov7670_init(client); + + case VIDIOC_ENUM_FMT: + return ov7670_enum_fmt(client, (struct v4l2_fmtdesc *) arg); + case VIDIOC_TRY_FMT: + return ov7670_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL); + case VIDIOC_S_FMT: + return ov7670_s_fmt(client, (struct v4l2_format *) arg); + case VIDIOC_QUERYCTRL: + return ov7670_queryctrl(client, (struct v4l2_queryctrl *) arg); + case VIDIOC_S_CTRL: + return ov7670_s_ctrl(client, (struct v4l2_control *) arg); + case VIDIOC_G_CTRL: + return ov7670_g_ctrl(client, (struct v4l2_control *) arg); + /* Todo: + g/s_parm + initialization + */ + } + return -EINVAL; +} + + + +static struct i2c_driver ov7670_driver = { + .driver = { + .name = "ov7670", + }, + .id = I2C_DRIVERID_OV7670, + .class = I2C_CLASS_CAM_DIGITAL, + .attach_adapter = ov7670_attach, + .detach_client = ov7670_detach, + .command = ov7670_command, +}; + + +/* + * Module initialization + */ +static int __init ov7670_mod_init(void) +{ + printk(KERN_NOTICE "OmniVision ov7670 sensor driver, at your service\n"); + return i2c_add_driver(&ov7670_driver); +} + +static void __exit ov7670_mod_exit(void) +{ + i2c_del_driver(&ov7670_driver); +} + +module_init(ov7670_mod_init); +module_exit(ov7670_mod_exit); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index c80c26be6..848fb233d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -260,6 +260,22 @@ int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw, sizeof(decoder_ops[0]))) - 1; hdw->decoder_ctrl = &ctxt->ctrl; cp->handler = &ctxt->handler; + { + /* + Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit + of nuttiness for cx25840 causes that module to + correctly set up its video scaling. This is really + a problem in the cx25840 module itself, but we work + around it here. The problem has not been seen in + ivtv because there VBI is supported and set up. We + don't do VBI here (at least not yet) and thus we + never attempted to even set it up. + */ + struct v4l2_format fmt; + memset(&fmt,0,sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_FMT,&fmt); + } pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up", cp->client->addr); return !0; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 17cb1d5c6..02f51eb1f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -357,28 +357,6 @@ static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) return 0; } -static int ctrl_hres_max_get(struct pvr2_ctrl *cptr,int *vp) -{ - /* If we're dealing with a 24xxx device, force the horizontal - maximum to be 720 no matter what, since we can't get the device - to work properly with any other value. Otherwise just return - the normal value. */ - *vp = cptr->info->def.type_int.max_value; - if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720; - return 0; -} - -static int ctrl_hres_min_get(struct pvr2_ctrl *cptr,int *vp) -{ - /* If we're dealing with a 24xxx device, force the horizontal - minimum to be 720 no matter what, since we can't get the device - to work properly with any other value. Otherwise just return - the normal value. */ - *vp = cptr->info->def.type_int.min_value; - if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720; - return 0; -} - static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp) { /* Actual maximum depends on the video standard in effect. */ @@ -759,10 +737,6 @@ static const struct pvr2_ctl_info control_defs[] = { .default_value = 720, DEFREF(res_hor), DEFINT(19,720), - /* Hook in check for clamp on horizontal resolution in - order to avoid unsolved problem involving cx25840. */ - .get_max_value = ctrl_hres_max_get, - .get_min_value = ctrl_hres_min_get, },{ .desc = "Vertical capture resolution", .name = "resolution_ver", diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c index 3c83629b1..3592202c1 100644 --- a/linux/drivers/media/video/pwc/pwc-if.c +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -903,11 +903,10 @@ int pwc_isoc_init(struct pwc_device *pdev) } if (ret) { /* De-allocate in reverse order */ - while (i >= 0) { + while (i--) { if (pdev->sbuf[i].urb != NULL) usb_free_urb(pdev->sbuf[i].urb); pdev->sbuf[i].urb = NULL; - i--; } return ret; } diff --git a/linux/drivers/media/video/saa6588.c b/linux/drivers/media/video/saa6588.c index 02709340b..4b2375c80 100644 --- a/linux/drivers/media/video/saa6588.c +++ b/linux/drivers/media/video/saa6588.c @@ -219,8 +219,10 @@ static void read_from_buf(struct saa6588 *s, struct rds_command *a) if (rd_blocks > s->block_count) rd_blocks = s->block_count; - if (!rd_blocks) + if (!rd_blocks) { + spin_unlock_irqrestore(&s->lock, flags); return; + } for (i = 0; i < rd_blocks; i++) { if (block_to_user_buf(s, buf_ptr)) { diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c index 8e661a352..6b99a4624 100644 --- a/linux/drivers/media/video/saa7115.c +++ b/linux/drivers/media/video/saa7115.c @@ -862,7 +862,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) /* On 60Hz, it is using a higher Vertical Output Size */ if (!is_50hz) - res+=(VRES_60HZ-480)>>1; + res += (VRES_60HZ - 480) >> 1; /* height */ saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, @@ -918,7 +918,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) /* Activates task "B" */ saa711x_write(client, R_80_GLOBAL_CNTL_1, - saa711x_read(client,R_80_GLOBAL_CNTL_1)|0x20); + saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20); return 0; } @@ -943,11 +943,11 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) if (std & V4L2_STD_525_60) { v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n"); saa711x_writeregs(client, saa7115_cfg_60hz_video); - saa711x_set_size(client,720,480); + saa711x_set_size(client, 720, 480); } else { v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n"); saa711x_writeregs(client, saa7115_cfg_50hz_video); - saa711x_set_size(client,720,576); + saa711x_set_size(client, 720, 576); } /* Register 0E - Bits D6-D4 on NO-AUTO mode @@ -1483,19 +1483,24 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind) #endif snprintf(client->name, sizeof(client->name) - 1, "saa7115"); - v4l_dbg(1, debug, client, "detecting saa7115 client on address 0x%x\n", address << 1); - - for (i=0;i<0x0f;i++) { + for (i = 0; i < 0x0f; i++) { saa711x_write(client, 0, i); - name[i] = (saa711x_read(client, 0) &0x0f) +'0'; - if (name[i]>'9') - name[i]+='a'-'9'-1; + name[i] = (saa711x_read(client, 0) & 0x0f) + '0'; + if (name[i] > '9') + name[i] += 'a' - '9' - 1; } - name[i]='\0'; + name[i] = '\0'; saa711x_write(client, 0, 5); chip_id = saa711x_read(client, 0) & 0x0f; + /* Check whether this chip is part of the saa711x series */ + if (memcmp(name, "1f711", 5)) { + v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n", + address << 1, name); + return 0; + } + snprintf(client->name, sizeof(client->name) - 1, "saa711%d",chip_id); v4l_info(client, "saa711%d found (%s) @ 0x%x (%s)\n", chip_id, name, address << 1, adapter->name); diff --git a/linux/drivers/media/video/saa7134/saa7134-alsa.c b/linux/drivers/media/video/saa7134/saa7134-alsa.c index 3dcd2203f..1ff1c706d 100644 --- a/linux/drivers/media/video/saa7134/saa7134-alsa.c +++ b/linux/drivers/media/video/saa7134/saa7134-alsa.c @@ -1,10 +1,6 @@ /* * SAA713x ALSA support for V4L * - * - * Caveats: - * - Volume doesn't work (it's always at max) - * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2 @@ -694,13 +690,18 @@ static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) snd_card_saa7134_pcm_t *pcm; snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); struct saa7134_dev *dev = saa7134->dev; - int err; + int amux, err; mutex_lock(&dev->dmasound.lock); dev->dmasound.read_count = 0; dev->dmasound.read_offset = 0; + amux = dev->input->amux; + if ((amux < 1) || (amux > 3)) + amux = 1; + dev->dmasound.input = amux - 1; + mutex_unlock(&dev->dmasound.lock); pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); @@ -813,6 +814,8 @@ static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol, #endif { snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); + struct saa7134_dev *dev = chip->dev; + int change, addr = kcontrol->private_value; int left, right; @@ -827,10 +830,52 @@ static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol, if (right > 20) right = 20; spin_lock_irq(&chip->mixer_lock); - change = chip->mixer_volume[addr][0] != left || - chip->mixer_volume[addr][1] != right; - chip->mixer_volume[addr][0] = left; - chip->mixer_volume[addr][1] = right; + change = 0; + if (chip->mixer_volume[addr][0] != left) { + change = 1; + right = left; + } + if (chip->mixer_volume[addr][1] != right) { + change = 1; + left = right; + } + if (change) { + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + switch (addr) { + case MIXER_ADDR_TVTUNER: + left = 20; + break; + case MIXER_ADDR_LINE1: + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10, + (left > 10) ? 0x00 : 0x10); + break; + case MIXER_ADDR_LINE2: + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20, + (left > 10) ? 0x00 : 0x20); + break; + } + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + switch (addr) { + case MIXER_ADDR_TVTUNER: + left = 20; + break; + case MIXER_ADDR_LINE1: + saa_andorb(0x0594, 0x10, + (left > 10) ? 0x00 : 0x10); + break; + case MIXER_ADDR_LINE2: + saa_andorb(0x0594, 0x20, + (left > 10) ? 0x00 : 0x20); + break; + } + break; + } + chip->mixer_volume[addr][0] = left; + chip->mixer_volume[addr][1] = right; + } spin_unlock_irq(&chip->mixer_lock); return change; } diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index 3cdcaa245..a5c92428b 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -3012,7 +3012,7 @@ struct saa7134_board saa7134_boards[] = { /* Petr Baudis <pasky@ucw.cz> */ .name = "AVerMedia TV Hybrid A16AR", .audio_clock = 0x187de7, - .tuner_type = TUNER_PHILIPS_TDA8290, /* untested */ + .tuner_type = TUNER_PHILIPS_TD1316, /* untested */ .radio_type = TUNER_TEA5767, /* untested */ .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -3141,6 +3141,76 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x00, }, }, + [SAA7134_BOARD_VIDEOMATE_DVBT_200A] = { + /* Francis Barber <fedora@barber-family.id.au> */ + .name = "Compro Videomate DVB-T200A", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_HAUPPAUGE_HVR1110] = { + /* Thomas Genty <tomlohave@gmail.com> */ + .name = "Hauppauge WinTV-HVR1110 DVB-T/Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x000200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_CINERGY_HT_PCMCIA] = { + .name = "Terratec Cinergy HT PCMCIA", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + }}, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3768,6 +3838,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x4876, .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6701, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x153b, + .subdevice = 0x1172, + .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -3893,12 +3975,14 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_307: case SAA7134_BOARD_AVERMEDIA_STUDIO_507: case SAA7134_BOARD_AVERMEDIA_GO_007_FM: + case SAA7134_BOARD_AVERMEDIA_777: /* case SAA7134_BOARD_SABRENT_SBTTVFM: */ /* not finished yet */ case SAA7134_BOARD_VIDEOMATE_TV_PVR: case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII: case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_VIDEOMATE_DVBT_200: + case SAA7134_BOARD_VIDEOMATE_DVBT_200A: case SAA7134_BOARD_MANLI_MTV001: case SAA7134_BOARD_MANLI_MTV002: case SAA7134_BOARD_BEHOLD_409FM: @@ -3909,6 +3993,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_PROTEUS_2309: + case SAA7134_BOARD_AVERMEDIA_A16AR: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -3947,7 +4032,6 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_writeb(SAA7134_GPIO_GPMODE3, 0x08); saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x00); break; - case SAA7134_BOARD_AVERMEDIA_A16AR: case SAA7134_BOARD_AVERMEDIA_CARDBUS: /* power-up tuner chip */ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0xffffffff); @@ -4103,6 +4187,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) case SAA7134_BOARD_TEVION_DVBT_220RF: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_MEDION_MD8800_QUADRO: + case SAA7134_BOARD_HAUPPAUGE_HVR1110: /* this is a hybrid board, initialize to analog mode * and configure firmware eeprom address */ @@ -4128,6 +4213,14 @@ int saa7134_board_init2(struct saa7134_dev *dev) i2c_transfer(&dev->i2c_adap, &msg, 1); } break; + case SAA7134_BOARD_CINERGY_HT_PCMCIA: + /* make the tda10046 find its eeprom */ + { + u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + i2c_transfer(&dev->i2c_adap, &msg, 1); + } + break; case SAA7134_BOARD_KWORLD_ATSC110: { /* enable tuner */ @@ -4140,6 +4233,29 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->name, i); } break; + case SAA7134_BOARD_VIDEOMATE_DVBT_200: + case SAA7134_BOARD_VIDEOMATE_DVBT_200A: + /* The T200 and the T200A share the same pci id. Consequently, + * we are going to query eeprom to try to find out which one we + * are actually looking at. */ + + /* Don't do this if the board was specifically selected with an + * insmod option or if we have the default configuration T200*/ + if(!dev->autodetected || (dev->eedata[0x41] == 0xd0)) + break; + if(dev->eedata[0x41] == 0x02) { + /* Reconfigure board as T200A */ + dev->board = SAA7134_BOARD_VIDEOMATE_DVBT_200A; + dev->tuner_type = saa7134_boards[dev->board].tuner_type; + dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; + printk(KERN_INFO "%s: Reconfigured board as %s\n", + dev->name, saa7134_boards[dev->board].name); + } else { + printk(KERN_WARNING "%s: Unexpected tuner type info: %x in eeprom\n", + dev->name, dev->eedata[0x41]); + break; + } + break; } return 0; } diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index a65da12e4..601c093eb 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -963,15 +963,16 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, must_configure_manually(); dev->board = SAA7134_BOARD_UNKNOWN; } + dev->autodetected = card[dev->nr] != dev->board; dev->tuner_type = saa7134_boards[dev->board].tuner_type; dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; if (UNSET != tuner[dev->nr]) dev->tuner_type = tuner[dev->nr]; - printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", - dev->name,pci_dev->subsystem_vendor, - pci_dev->subsystem_device,saa7134_boards[dev->board].name, - dev->board, card[dev->nr] == dev->board ? - "insmod option" : "autodetected"); + printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", + dev->name,pci_dev->subsystem_vendor, + pci_dev->subsystem_device,saa7134_boards[dev->board].name, + dev->board, dev->autodetected ? + "autodetected" : "insmod option"); /* get mmio */ if (!request_mem_region(pci_resource_start(pci_dev,0), diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 28d3e5bfd..c52c91e4f 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -51,6 +51,10 @@ static unsigned int antenna_pwr = 0; module_param(antenna_pwr, int, 0444); MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)"); +static int use_frontent = 0; +module_param(use_frontent, int, 0644); +MODULE_PARM_DESC(use_frontent,"for cards with multiple frontends (0: terrestrial, 1: satellite)"); + /* ------------------------------------------------------------------ */ static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on) { @@ -337,7 +341,7 @@ static struct tda1004x_config philips_tu1216_61_config = { /* ------------------------------------------------------------------ */ -static int philips_europa_tuner_init(struct dvb_frontend *fe) +static int philips_td1316_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab }; @@ -348,18 +352,8 @@ static int philips_europa_tuner_init(struct dvb_frontend *fe) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) return -EIO; - msleep(1); - - /* switch the board to dvb mode */ - init_msg.addr = 0x43; - init_msg.len = 0x02; - msg[0] = 0x00; - msg[1] = 0x40; if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) - return -EIO; - + fe->ops.i2c_gate_ctrl(fe, 0); return 0; } @@ -368,6 +362,22 @@ static int philips_td1316_tuner_set_params(struct dvb_frontend *fe, struct dvb_f return philips_tda6651_pll_set(0x61, fe, params); } +static int philips_europa_tuner_init(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + static u8 msg[] = { 0x00, 0x40}; + struct i2c_msg init_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + + if (philips_td1316_tuner_init(fe)) + return -EIO; + msleep(1); + if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) + return -EIO; + + return 0; +} + static int philips_europa_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; @@ -672,7 +682,7 @@ static struct tda1004x_config tda827x_lifeview_config = { .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X, + .agc_config = TDA10046_AGC_TDA827X_GP11, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; @@ -876,7 +886,40 @@ static struct tda1004x_config philips_tiger_config = { .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X, + .agc_config = TDA10046_AGC_TDA827X_GP11, + .if_freq = TDA10046_FREQ_045, + .request_firmware = NULL, +}; +/* ------------------------------------------------------------------ */ + +static int cinergy_ht_tuner_init(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + static u8 data[] = { 0x3c, 0x33, 0x62}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; +} + +static int cinergy_ht_tuner_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + static u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + + i2c_transfer(&dev->i2c_adap, &msg, 1); + philips_tda827xa_tuner_sleep( 0x61, fe); + return 0; +} + +static struct tda1004x_config cinergy_ht_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X_GP01, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; @@ -888,7 +931,19 @@ static struct tda1004x_config pinnacle_pctv_310i_config = { .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X, + .agc_config = TDA10046_AGC_TDA827X_GP11, + .if_freq = TDA10046_FREQ_045, + .request_firmware = philips_tda1004x_request_firmware, +}; + +/* ------------------------------------------------------------------ */ + +static struct tda1004x_config hauppauge_hvr_1110_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X_GP11, .if_freq = TDA10046_FREQ_045, .request_firmware = philips_tda1004x_request_firmware, }; @@ -900,7 +955,7 @@ static struct tda1004x_config asus_p7131_dual_config = { .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X, + .agc_config = TDA10046_AGC_TDA827X_GP11, .if_freq = TDA10046_FREQ_045, .request_firmware = philips_tda1004x_request_firmware, }; @@ -952,7 +1007,7 @@ static struct tda1004x_config lifeview_trio_config = { .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GPL, + .agc_config = TDA10046_AGC_TDA827X_GP00, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; @@ -989,7 +1044,7 @@ static struct tda1004x_config ads_tech_duo_config = { .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GPL, + .agc_config = TDA10046_AGC_TDA827X_GP00, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; @@ -1014,7 +1069,7 @@ static struct tda1004x_config tevion_dvbt220rf_config = { .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X, + .agc_config = TDA10046_AGC_TDA827X_GP11, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; @@ -1059,7 +1114,7 @@ static struct tda1004x_config md8800_dvbt_config = { .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X, + .agc_config = TDA10046_AGC_TDA827X_GP11, .if_freq = TDA10046_FREQ_045, .request_firmware = NULL, }; @@ -1216,6 +1271,17 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; } break; + case SAA7134_BOARD_HAUPPAUGE_HVR1110: + dev->dvb.frontend = dvb_attach(tda10046_attach, + &hauppauge_hvr_1110_config, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; + dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; + dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; + } + break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: dev->dvb.frontend = dvb_attach(tda10046_attach, &asus_p7131_dual_config, @@ -1238,12 +1304,27 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_FLYDVB_TRIO: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &lifeview_trio_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = lifeview_trio_tuner_set_params; + if(! use_frontent) { //terrestrial + dev->dvb.frontend = dvb_attach(tda10046_attach, + &lifeview_trio_config, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep; + dev->dvb.frontend->ops.tuner_ops.set_params = + lifeview_trio_tuner_set_params; + } + } else { //satellite + dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); + if (dev->dvb.frontend) { + if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x63, + &dev->i2c_adap, 0) == NULL) { + printk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__); + } + if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap, + 0x08, 0, 0) == NULL) { + printk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__); + } + } } break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: @@ -1325,7 +1406,27 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend->ops.tuner_ops.set_params = philips_fmd1216_tuner_set_params; } break; + case SAA7134_BOARD_VIDEOMATE_DVBT_200A: + dev->dvb.frontend = dvb_attach(tda10046_attach, + &philips_europa_config, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.tuner_ops.init = philips_td1316_tuner_init; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; + } + break; + case SAA7134_BOARD_CINERGY_HT_PCMCIA: + dev->dvb.frontend = dvb_attach(tda10046_attach, + &cinergy_ht_config, + &dev->i2c_adap); + if (dev->dvb.frontend) { + dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; + dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init; + dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; + } + break; default: printk("%s: Huh? unknown DVB card?\n",dev->name); break; diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c index 99462db8f..9b15d962c 100644 --- a/linux/drivers/media/video/saa7134/saa7134-input.c +++ b/linux/drivers/media/video/saa7134/saa7134-input.c @@ -132,6 +132,23 @@ static void saa7134_input_timer(unsigned long data) mod_timer(&ir->timer, timeout); } +static void saa7134_ir_start(struct saa7134_dev *dev, struct saa7134_ir *ir) +{ + if (ir->polling) { + init_timer(&ir->timer); + ir->timer.function = saa7134_input_timer; + ir->timer.data = (unsigned long)dev; + ir->timer.expires = jiffies + HZ; + add_timer(&ir->timer); + } +} + +static void saa7134_ir_stop(struct saa7134_dev *dev) +{ + if (dev->remote->polling) + del_timer_sync(&dev->remote->timer); +} + int saa7134_input_init1(struct saa7134_dev *dev) { struct saa7134_ir *ir; @@ -142,6 +159,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) u32 mask_keyup = 0; int polling = 0; int ir_type = IR_TYPE_OTHER; + int err; if (dev->has_remote != SAA7134_REMOTE_GPIO) return -ENODEV; @@ -187,7 +205,6 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_STUDIO_307: case SAA7134_BOARD_AVERMEDIA_STUDIO_507: case SAA7134_BOARD_AVERMEDIA_GO_007_FM: - case SAA7134_BOARD_AVERMEDIA_A16AR: ir_codes = ir_codes_avermedia; mask_keycode = 0x0007C8; mask_keydown = 0x000010; @@ -196,6 +213,16 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa_setb(SAA7134_GPIO_GPMODE0, 0x4); saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4); break; + case SAA7134_BOARD_AVERMEDIA_777: + case SAA7134_BOARD_AVERMEDIA_A16AR: + ir_codes = ir_codes_avermedia; + mask_keycode = 0x02F200; + mask_keydown = 0x000400; + polling = 50; // ms + /* Without this we won't receive key up events */ + saa_setb(SAA7134_GPIO_GPMODE1, 0x1); + saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); + break; case SAA7134_BOARD_KWORLD_TERMINATOR: ir_codes = ir_codes_pixelview; mask_keycode = 0x00001f; @@ -259,9 +286,8 @@ int saa7134_input_init1(struct saa7134_dev *dev) ir = kzalloc(sizeof(*ir), GFP_KERNEL); input_dev = input_allocate_device(); if (!ir || !input_dev) { - kfree(ir); - input_free_device(input_dev); - return -ENOMEM; + err = -ENOMEM; + goto err_out_free; } ir->dev = input_dev; @@ -298,18 +324,22 @@ int saa7134_input_init1(struct saa7134_dev *dev) #endif #endif - /* all done */ dev->remote = ir; - if (ir->polling) { - init_timer(&ir->timer); - ir->timer.function = saa7134_input_timer; - ir->timer.data = (unsigned long)dev; - ir->timer.expires = jiffies + HZ; - add_timer(&ir->timer); - } + saa7134_ir_start(dev, ir); + + err = input_register_device(ir->dev); + if (err) + goto err_out_stop; - input_register_device(ir->dev); return 0; + + err_out_stop: + saa7134_ir_stop(dev); + dev->remote = NULL; + err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; } void saa7134_input_fini(struct saa7134_dev *dev) @@ -317,8 +347,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) if (NULL == dev->remote) return; - if (dev->remote->polling) - del_timer_sync(&dev->remote->timer); + saa7134_ir_stop(dev); input_unregister_device(dev->remote->dev); kfree(dev->remote); dev->remote = NULL; diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index 2b3f39e53..0cf866108 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -238,6 +238,9 @@ struct saa7134_format { #define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100 #define SAA7134_BOARD_PINNACLE_PCTV_310i 101 #define SAA7134_BOARD_AVERMEDIA_STUDIO_507 102 +#define SAA7134_BOARD_VIDEOMATE_DVBT_200A 103 +#define SAA7134_BOARD_HAUPPAUGE_HVR1110 104 +#define SAA7134_BOARD_CINERGY_HT_PCMCIA 105 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 @@ -471,6 +474,9 @@ struct saa7134_dev { struct v4l2_prio_state prio; #endif + /* insmod option/autodetected */ + int autodetected; + /* various device info */ unsigned int resources; struct video_device *video_dev; diff --git a/linux/drivers/media/video/sn9c102/sn9c102_core.c b/linux/drivers/media/video/sn9c102/sn9c102_core.c index d9c512fef..793a249b8 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_core.c +++ b/linux/drivers/media/video/sn9c102/sn9c102_core.c @@ -1466,8 +1466,6 @@ static void sn9c102_release_resources(struct sn9c102_device* cam) video_set_drvdata(cam->v4ldev, NULL); video_unregister_device(cam->v4ldev); - usb_put_dev(cam->usbdev); - mutex_unlock(&sn9c102_sysfs_lock); kfree(cam->control_buffer); @@ -1559,6 +1557,7 @@ static int sn9c102_release(struct inode* inode, struct file* filp) if (cam->state & DEV_DISCONNECTED) { sn9c102_release_resources(cam); + usb_put_dev(cam->usbdev); mutex_unlock(&cam->dev_mutex); kfree(cam); return 0; diff --git a/linux/drivers/media/video/tveeprom.c b/linux/drivers/media/video/tveeprom.c index f963cee12..dd06107a3 100644 --- a/linux/drivers/media/video/tveeprom.c +++ b/linux/drivers/media/video/tveeprom.c @@ -434,7 +434,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, tvee->has_radio = eeprom_data[i+len-1]; /* old style tag, don't know how to detect IR presence, mark as unknown. */ - tvee->has_ir = 2; + tvee->has_ir = -1; tvee->model = eeprom_data[i+8] + (eeprom_data[i+9] << 8); @@ -472,7 +472,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, (eeprom_data[i+6] << 8) + (eeprom_data[i+7] << 16); - if ( (eeprom_data[i + 8] && 0xf0) && + if ( (eeprom_data[i + 8] & 0xf0) && (tvee->serial_number < 0xffffff) ) { tvee->MAC_address[0] = 0x00; tvee->MAC_address[1] = 0x0D; @@ -657,13 +657,14 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, STRM(decoderIC, tvee->decoder_processor), tvee->decoder_processor); } - if (tvee->has_ir == 2) + if (tvee->has_ir == -1) tveeprom_info("has %sradio\n", tvee->has_radio ? "" : "no "); else - tveeprom_info("has %sradio, has %sIR remote\n", + tveeprom_info("has %sradio, has %sIR receiver, has %sIR transmitter\n", tvee->has_radio ? "" : "no ", - tvee->has_ir ? "" : "no "); + (tvee->has_ir & 1) ? "" : "no ", + (tvee->has_ir & 2) ? "" : "no "); } EXPORT_SYMBOL(tveeprom_hauppauge_analog); diff --git a/linux/drivers/media/video/usbvideo/quickcam_messenger.c b/linux/drivers/media/video/usbvideo/quickcam_messenger.c index 95f6b86f0..8c7504bba 100644 --- a/linux/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/linux/drivers/media/video/usbvideo/quickcam_messenger.c @@ -91,6 +91,7 @@ MODULE_DEVICE_TABLE(usb, qcm_table); static void qcm_register_input(struct qcm *cam, struct usb_device *dev) { struct input_dev *input_dev; + int error; usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); @@ -111,7 +112,13 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev) input_dev->private = cam; - input_register_device(cam->input); + error = input_register_device(cam->input); + if (error) { + warn("Failed to register camera's input device, err: %d\n", + error); + input_free_device(cam->input); + cam->input = NULL; + } } static void qcm_unregister_input(struct qcm *cam) diff --git a/linux/drivers/media/video/v4l1-compat.c b/linux/drivers/media/video/v4l1-compat.c index bb8d55523..a8968cd79 100644 --- a/linux/drivers/media/video/v4l1-compat.c +++ b/linux/drivers/media/video/v4l1-compat.c @@ -362,6 +362,7 @@ v4l_compat_translate_ioctl(struct inode *inode, struct video_buffer *buffer = arg; memset(buffer, 0, sizeof(*buffer)); + memset(&fbuf2, 0, sizeof(fbuf2)); err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2); if (err < 0) { @@ -628,6 +629,7 @@ v4l_compat_translate_ioctl(struct inode *inode, case VIDIOCSPICT: /* set tone controls & partial capture format */ { struct video_picture *pict = arg; + memset(&fbuf2, 0, sizeof(fbuf2)); set_v4l_control(inode, file, V4L2_CID_BRIGHTNESS, pict->brightness, drv); @@ -720,18 +722,22 @@ v4l_compat_translate_ioctl(struct inode *inode, } case VIDIOCSTUNER: /* select a tuner input */ { -#if 0 /* FIXME */ - err = drv(inode, file, VIDIOC_S_INPUT, &i); + struct video_tuner *tun = arg; + struct v4l2_tuner t; + memset(&t,0,sizeof(t)); + + t.index=tun->tuner; + + err = drv(inode, file, VIDIOC_S_INPUT, &t); if (err < 0) dprintk("VIDIOCSTUNER / VIDIOC_S_INPUT: %d\n",err); -#else - err = 0; -#endif + break; } case VIDIOCGFREQ: /* get frequency */ { unsigned long *freq = arg; + memset(&freq2,0,sizeof(freq2)); freq2.tuner = 0; err = drv(inode, file, VIDIOC_G_FREQUENCY, &freq2); @@ -744,8 +750,8 @@ v4l_compat_translate_ioctl(struct inode *inode, case VIDIOCSFREQ: /* set frequency */ { unsigned long *freq = arg; + memset(&freq2,0,sizeof(freq2)); - freq2.tuner = 0; drv(inode, file, VIDIOC_G_FREQUENCY, &freq2); freq2.frequency = *freq; err = drv(inode, file, VIDIOC_S_FREQUENCY, &freq2); @@ -756,6 +762,7 @@ v4l_compat_translate_ioctl(struct inode *inode, case VIDIOCGAUDIO: /* get audio properties/controls */ { struct video_audio *aud = arg; + memset(&aud2,0,sizeof(aud2)); err = drv(inode, file, VIDIOC_G_AUDIO, &aud2); if (err < 0) { @@ -922,6 +929,7 @@ v4l_compat_translate_ioctl(struct inode *inode, { int *i = arg; + memset(&buf2,0,sizeof(buf2)); buf2.index = *i; buf2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; err = drv(inode, file, VIDIOC_QUERYBUF, &buf2); diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index e6c821745..18ac4b8d0 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -105,6 +105,78 @@ v4l2_video_std_fps(struct v4l2_standard *vs) EXPORT_SYMBOL(v4l2_video_std_fps); #endif +char *v4l2_norm_to_name(v4l2_std_id id) +{ + char *name; + + switch (id) { + case V4L2_STD_PAL: + name="PAL"; break; + case V4L2_STD_PAL_BG: + name="PAL-BG"; break; + case V4L2_STD_PAL_DK: + name="PAL-DK"; break; + case V4L2_STD_PAL_B: + name="PAL-B"; break; + case V4L2_STD_PAL_B1: + name="PAL-B1"; break; + case V4L2_STD_PAL_G: + name="PAL-G"; break; + case V4L2_STD_PAL_H: + name="PAL-H"; break; + case V4L2_STD_PAL_I: + name="PAL-I"; break; + case V4L2_STD_PAL_D: + name="PAL-D"; break; + case V4L2_STD_PAL_D1: + name="PAL-D1"; break; + case V4L2_STD_PAL_K: + name="PAL-K"; break; + case V4L2_STD_PAL_M: + name="PAL-M"; break; + case V4L2_STD_PAL_N: + name="PAL-N"; break; + case V4L2_STD_PAL_Nc: + name="PAL-Nc"; break; + case V4L2_STD_PAL_60: + name="PAL-60"; break; + case V4L2_STD_NTSC: + name="NTSC"; break; + case V4L2_STD_NTSC_M: + name="NTSC-M"; break; + case V4L2_STD_NTSC_M_JP: + name="NTSC-M-JP"; break; + case V4L2_STD_NTSC_443: + name="NTSC-443"; break; + case V4L2_STD_NTSC_M_KR: + name="NTSC-M-KR"; break; + case V4L2_STD_SECAM: + name="SECAM"; break; + case V4L2_STD_SECAM_DK: + name="SECAM-DK"; break; + case V4L2_STD_SECAM_B: + name="SECAM-B"; break; + case V4L2_STD_SECAM_D: + name="SECAM-D"; break; + case V4L2_STD_SECAM_G: + name="SECAM-G"; break; + case V4L2_STD_SECAM_H: + name="SECAM-H"; break; + case V4L2_STD_SECAM_K: + name="SECAM-K"; break; + case V4L2_STD_SECAM_K1: + name="SECAM-K1"; break; + case V4L2_STD_SECAM_L: + name="SECAM-L"; break; + case V4L2_STD_SECAM_LC: + name="SECAM-LC"; break; + default: + name="Unknown"; break; + } + + return name; +} + /* Fill in the fields of a v4l2_standard structure according to the 'id' and 'transmission' parameters. Returns negative on error. */ int v4l2_video_std_construct(struct v4l2_standard *vs, @@ -202,11 +274,13 @@ char *v4l2_field_names[] = { }; char *v4l2_type_names[] = { - [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "video-cap", - [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "video-over", - [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "video-out", - [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", - [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", + [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "video-cap", + [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "video-over", + [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "video-out", + [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", + [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", + [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap", + [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "slicec-vbi-out", }; static char *v4l2_memory_names[] = { @@ -1482,6 +1556,7 @@ u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id) /* ----------------------------------------------------------------- */ +EXPORT_SYMBOL(v4l2_norm_to_name); EXPORT_SYMBOL(v4l2_video_std_construct); EXPORT_SYMBOL(v4l2_prio_init); diff --git a/linux/drivers/media/video/videodev.c b/linux/drivers/media/video/videodev.c index bcaee23da..4436292e9 100644 --- a/linux/drivers/media/video/videodev.c +++ b/linux/drivers/media/video/videodev.c @@ -447,6 +447,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, v4l_print_ioctl(vfd->name, cmd); } + if (_IOC_TYPE(cmd)=='v') + return v4l_compat_translate_ioctl(inode,file,cmd,arg, + __video_do_ioctl); + switch(cmd) { /* --- capabilities ------------------------------------------ */ case VIDIOC_QUERYCAP: @@ -848,20 +852,85 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUMSTD: { struct v4l2_standard *p = arg; - unsigned int index = p->index; + v4l2_std_id id = vfd->tvnorms,curr_id=0; + unsigned int index = p->index,i; - if (!vfd->tvnormsize) { - printk (KERN_WARNING "%s: no TV norms defined!\n", - vfd->name); + if (index<0) { + ret=-EINVAL; break; } - if (index<0 || index >= vfd->tvnormsize) { - ret=-EINVAL; - break; + /* Return norm array on a canonical way */ + for (i=0;i<= index && id; i++) { + if ( (id & V4L2_STD_PAL) == V4L2_STD_PAL) { + curr_id = V4L2_STD_PAL; + } else if ( (id & V4L2_STD_PAL_BG) == V4L2_STD_PAL_BG) { + curr_id = V4L2_STD_PAL_BG; + } else if ( (id & V4L2_STD_PAL_DK) == V4L2_STD_PAL_DK) { + curr_id = V4L2_STD_PAL_DK; + } else if ( (id & V4L2_STD_PAL_B) == V4L2_STD_PAL_B) { + curr_id = V4L2_STD_PAL_B; + } else if ( (id & V4L2_STD_PAL_B1) == V4L2_STD_PAL_B1) { + curr_id = V4L2_STD_PAL_B1; + } else if ( (id & V4L2_STD_PAL_G) == V4L2_STD_PAL_G) { + curr_id = V4L2_STD_PAL_G; + } else if ( (id & V4L2_STD_PAL_H) == V4L2_STD_PAL_H) { + curr_id = V4L2_STD_PAL_H; + } else if ( (id & V4L2_STD_PAL_I) == V4L2_STD_PAL_I) { + curr_id = V4L2_STD_PAL_I; + } else if ( (id & V4L2_STD_PAL_D) == V4L2_STD_PAL_D) { + curr_id = V4L2_STD_PAL_D; + } else if ( (id & V4L2_STD_PAL_D1) == V4L2_STD_PAL_D1) { + curr_id = V4L2_STD_PAL_D1; + } else if ( (id & V4L2_STD_PAL_K) == V4L2_STD_PAL_K) { + curr_id = V4L2_STD_PAL_K; + } else if ( (id & V4L2_STD_PAL_M) == V4L2_STD_PAL_M) { + curr_id = V4L2_STD_PAL_M; + } else if ( (id & V4L2_STD_PAL_N) == V4L2_STD_PAL_N) { + curr_id = V4L2_STD_PAL_N; + } else if ( (id & V4L2_STD_PAL_Nc) == V4L2_STD_PAL_Nc) { + curr_id = V4L2_STD_PAL_Nc; + } else if ( (id & V4L2_STD_PAL_60) == V4L2_STD_PAL_60) { + curr_id = V4L2_STD_PAL_60; + } else if ( (id & V4L2_STD_NTSC) == V4L2_STD_NTSC) { + curr_id = V4L2_STD_NTSC; + } else if ( (id & V4L2_STD_NTSC_M) == V4L2_STD_NTSC_M) { + curr_id = V4L2_STD_NTSC_M; + } else if ( (id & V4L2_STD_NTSC_M_JP) == V4L2_STD_NTSC_M_JP) { + curr_id = V4L2_STD_NTSC_M_JP; + } else if ( (id & V4L2_STD_NTSC_443) == V4L2_STD_NTSC_443) { + curr_id = V4L2_STD_NTSC_443; + } else if ( (id & V4L2_STD_NTSC_M_KR) == V4L2_STD_NTSC_M_KR) { + curr_id = V4L2_STD_NTSC_M_KR; + } else if ( (id & V4L2_STD_SECAM) == V4L2_STD_SECAM) { + curr_id = V4L2_STD_SECAM; + } else if ( (id & V4L2_STD_SECAM_DK) == V4L2_STD_SECAM_DK) { + curr_id = V4L2_STD_SECAM_DK; + } else if ( (id & V4L2_STD_SECAM_B) == V4L2_STD_SECAM_B) { + curr_id = V4L2_STD_SECAM_B; + } else if ( (id & V4L2_STD_SECAM_D) == V4L2_STD_SECAM_D) { + curr_id = V4L2_STD_SECAM_D; + } else if ( (id & V4L2_STD_SECAM_G) == V4L2_STD_SECAM_G) { + curr_id = V4L2_STD_SECAM_G; + } else if ( (id & V4L2_STD_SECAM_H) == V4L2_STD_SECAM_H) { + curr_id = V4L2_STD_SECAM_H; + } else if ( (id & V4L2_STD_SECAM_K) == V4L2_STD_SECAM_K) { + curr_id = V4L2_STD_SECAM_K; + } else if ( (id & V4L2_STD_SECAM_K1) == V4L2_STD_SECAM_K1) { + curr_id = V4L2_STD_SECAM_K1; + } else if ( (id & V4L2_STD_SECAM_L) == V4L2_STD_SECAM_L) { + curr_id = V4L2_STD_SECAM_L; + } else if ( (id & V4L2_STD_SECAM_LC) == V4L2_STD_SECAM_LC) { + curr_id = V4L2_STD_SECAM_LC; + } else { + break; + } + id &= ~curr_id; } - v4l2_video_std_construct(p, vfd->tvnorms[p->index].id, - vfd->tvnorms[p->index].name); + if (i<=index) + return -EINVAL; + + v4l2_video_std_construct(p, curr_id,v4l2_norm_to_name(curr_id)); p->index = index; dbgarg (cmd, "index=%d, id=%Ld, name=%s, fps=%d/%d, " @@ -887,39 +956,23 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, } case VIDIOC_S_STD: { - v4l2_std_id *id = arg; - unsigned int i; - - if (!vfd->tvnormsize) { - printk (KERN_WARNING "%s: no TV norms defined!\n", - vfd->name); - break; - } + v4l2_std_id *id = arg,norm; dbgarg (cmd, "value=%Lu\n", (long long unsigned) *id); - /* First search for exact match */ - for (i = 0; i < vfd->tvnormsize; i++) - if (*id == vfd->tvnorms[i].id) - break; - /* Then for a generic video std that contains desired std */ - if (i == vfd->tvnormsize) - for (i = 0; i < vfd->tvnormsize; i++) - if (*id & vfd->tvnorms[i].id) - break; - if (i == vfd->tvnormsize) { + norm = (*id) & vfd->tvnorms; + if ( vfd->tvnorms && !norm) /* Check if std is supported */ break; - } /* Calls the specific handler */ if (vfd->vidioc_s_std) - ret=vfd->vidioc_s_std(file, fh, i); + ret=vfd->vidioc_s_std(file, fh, &norm); else ret=-EINVAL; /* Updates standard information */ - if (!ret) - vfd->current_norm=*id; + if (ret>=0) + vfd->current_norm=norm; break; } @@ -1107,9 +1160,13 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_AUDIO: { struct v4l2_audio *p=arg; + __u32 index=p->index; if (!vfd->vidioc_g_audio) break; + + memset(p,0,sizeof(*p)); + p->index=index; dbgarg(cmd, "Get for index=%d\n", p->index); ret=vfd->vidioc_g_audio(file, fh, p); if (!ret) @@ -1307,25 +1364,12 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_g_parm(file, fh, p); } else { struct v4l2_standard s; - int i; - - if (!vfd->tvnormsize) { - printk (KERN_WARNING "%s: no TV norms defined!\n", - vfd->name); - break; - } if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - for (i = 0; i < vfd->tvnormsize; i++) - if (vfd->tvnorms[i].id == vfd->current_norm) - break; - if (i >= vfd->tvnormsize) - return -EINVAL; - v4l2_video_std_construct(&s, vfd->current_norm, - vfd->tvnorms[i].name); + v4l2_norm_to_name(vfd->current_norm)); memset(p,0,sizeof(*p)); @@ -1348,8 +1392,14 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_TUNER: { struct v4l2_tuner *p=arg; + __u32 index=p->index; + if (!vfd->vidioc_g_tuner) break; + + memset(p,0,sizeof(*p)); + p->index=index; + ret=vfd->vidioc_g_tuner(file, fh, p); if (!ret) dbgarg (cmd, "index=%d, name=%s, type=%d, " @@ -1382,6 +1432,9 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, struct v4l2_frequency *p=arg; if (!vfd->vidioc_g_frequency) break; + + memset(p,0,sizeof(*p)); + ret=vfd->vidioc_g_frequency(file, fh, p); if (!ret) dbgarg (cmd, "tuner=%d, type=%d, frequency=%d\n", @@ -1415,12 +1468,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_log_status(file, fh); break; } - - /* --- Others --------------------------------------------- */ - - default: - ret=v4l_compat_translate_ioctl(inode,file,cmd,arg,__video_do_ioctl); - } + } /* switch */ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { if (ret<0) { diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index 61672a219..9b1407426 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -1119,16 +1119,8 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) return (0); } -static struct v4l2_tvnorm tvnorms[] = { - { - .name = "NTSC-M", - .id = V4L2_STD_NTSC_M, - } -}; - -static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id a) +static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *i) { - return 0; } @@ -1414,8 +1406,8 @@ static struct video_device vivi = { #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif - .tvnorms = tvnorms, - .tvnormsize = ARRAY_SIZE(tvnorms), + .tvnorms = V4L2_STD_NTSC_M, + .current_norm = V4L2_STD_NTSC_M, }; /* ----------------------------------------------------------------- Initialization and module stuff @@ -1442,8 +1434,6 @@ static int __init vivi_init(void) dev->vidq.timeout.data = (unsigned long)dev; init_timer(&dev->vidq.timeout); - vivi.current_norm = tvnorms[0].id; - ret = video_register_device(&vivi, VFL_TYPE_GRABBER, video_nr); printk(KERN_INFO "Video Technology Magazine Virtual Video Capture Board (Load status: %d)\n", ret); return ret; diff --git a/linux/drivers/media/video/zr36120.c b/linux/drivers/media/video/zr36120.c deleted file mode 100644 index 3d0b1fe18..000000000 --- a/linux/drivers/media/video/zr36120.c +++ /dev/null @@ -1,2087 +0,0 @@ -/* - zr36120.c - Zoran 36120/36125 based framegrabbers - - Copyright (C) 1998-1999 Pauline Middelink <middelin@polyware.nl> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> -#include <linux/mm.h> -#include <linux/pci.h> -#include <linux/signal.h> -#include <linux/wait.h> -#include <asm/io.h> -#include <asm/pgtable.h> -#include <asm/page.h> -#include <linux/sched.h> -#include <linux/video_decoder.h> - -#include <asm/uaccess.h> - -#include "tuner.h" -#include "zr36120.h" -#include "zr36120_mem.h" - -/* mark an required function argument unused - lintism */ -#define UNUSED(x) (void)(x) - -/* sensible default */ -#ifndef CARDTYPE -#define CARDTYPE 0 -#endif - -/* Anybody who uses more than four? */ -#define ZORAN_MAX 4 - -static unsigned int triton1=0; /* triton1 chipset? */ -static unsigned int cardtype[ZORAN_MAX]={ [ 0 ... ZORAN_MAX-1 ] = CARDTYPE }; -static int video_nr = -1; -static int vbi_nr = -1; - -static struct pci_device_id zr36120_pci_tbl[] = { - { PCI_VENDOR_ID_ZORAN,PCI_DEVICE_ID_ZORAN_36120, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, zr36120_pci_tbl); - -MODULE_AUTHOR("Pauline Middelink <middelin@polyware.nl>"); -MODULE_DESCRIPTION("Zoran ZR36120 based framegrabber"); -MODULE_LICENSE("GPL"); - -module_param(triton1, uint, 0); -module_param_array(cardtype, uint, NULL, 0); -module_param(video_nr, int, 0); -module_param(vbi_nr, int, 0); - -static int zoran_cards; -static struct zoran zorans[ZORAN_MAX]; - -/* - * the meaning of each element can be found in zr36120.h - * Determining the value of gpdir/gpval can be tricky. The - * best way is to run the card under the original software - * and read the values from the general purpose registers - * 0x28 and 0x2C. How you do that is left as an exercise - * to the impatient reader :) - */ -#define T 1 /* to separate the bools from the ints */ -#define F 0 -static struct tvcard tvcards[] = { - /* reported working by <middelin@polyware.nl> */ -/*0*/ { "Trust Victor II", - 2, 0, T, T, T, T, 0x7F, 0x80, { 1, SVHS(6) }, { 0 } }, - /* reported working by <Michael.Paxton@aihw.gov.au> */ -/*1*/ { "Aitech WaveWatcher TV-PCI", - 3, 0, T, F, T, T, 0x7F, 0x80, { 1, TUNER(3), SVHS(6) }, { 0 } }, - /* reported working by ? */ -/*2*/ { "Genius Video Wonder PCI Video Capture Card", - 2, 0, T, T, T, T, 0x7F, 0x80, { 1, SVHS(6) }, { 0 } }, - /* reported working by <Pascal.Gabriel@wanadoo.fr> */ -/*3*/ { "Guillemot Maxi-TV PCI", - 2, 0, T, T, T, T, 0x7F, 0x80, { 1, SVHS(6) }, { 0 } }, - /* reported working by "Craig Whitmore <lennon@igrin.co.nz> */ -/*4*/ { "Quadrant Buster", - 3, 3, T, F, T, T, 0x7F, 0x80, { SVHS(1), TUNER(2), 3 }, { 1, 2, 3 } }, - /* a debug entry which has all inputs mapped */ -/*5*/ { "ZR36120 based framegrabber (all inputs enabled)", - 6, 0, T, T, T, T, 0x7F, 0x80, { 1, 2, 3, 4, 5, 6 }, { 0 } } -}; -#undef T -#undef F -#define NRTVCARDS (sizeof(tvcards)/sizeof(tvcards[0])) - -#ifdef __sparc__ -#define ENDIANESS 0 -#else -#define ENDIANESS ZORAN_VFEC_LE -#endif - -static struct { const char name[8]; uint mode; uint bpp; } palette2fmt[] = { -/* n/a */ { "n/a", 0, 0 }, -/* GREY */ { "GRAY", 0, 0 }, -/* HI240 */ { "HI240", 0, 0 }, -/* RGB565 */ { "RGB565", ZORAN_VFEC_RGB_RGB565|ENDIANESS, 2 }, -/* RGB24 */ { "RGB24", ZORAN_VFEC_RGB_RGB888|ENDIANESS|ZORAN_VFEC_PACK24, 3 }, -/* RGB32 */ { "RGB32", ZORAN_VFEC_RGB_RGB888|ENDIANESS, 4 }, -/* RGB555 */ { "RGB555", ZORAN_VFEC_RGB_RGB555|ENDIANESS, 2 }, -/* YUV422 */ { "YUV422", ZORAN_VFEC_RGB_YUV422|ENDIANESS, 2 }, -/* YUYV */ { "YUYV", 0, 0 }, -/* UYVY */ { "UYVY", 0, 0 }, -/* YUV420 */ { "YUV420", 0, 0 }, -/* YUV411 */ { "YUV411", 0, 0 }, -/* RAW */ { "RAW", 0, 0 }, -/* YUV422P */ { "YUV422P", 0, 0 }, -/* YUV411P */ { "YUV411P", 0, 0 }}; -#define NRPALETTES (sizeof(palette2fmt)/sizeof(palette2fmt[0])) -#undef ENDIANESS - -/* ----------------------------------------------------------------------- */ -/* ZORAN chipset detector */ -/* shamelessly stolen from bttv.c */ -/* Reason for beeing here: we need to detect if we are running on a */ -/* Triton based chipset, and if so, enable a certain bit */ -/* ----------------------------------------------------------------------- */ -static -void __init handle_chipset(void) -{ - /* Just in case some nut set this to something dangerous */ - if (triton1) - triton1 = ZORAN_VDC_TRICOM; - - if (pci_pci_problems & PCIPCI_TRITON) { - printk(KERN_INFO "zoran: Host bridge 82437FX Triton PIIX\n"); - triton1 = ZORAN_VDC_TRICOM; - } -} - -/* ----------------------------------------------------------------------- */ -/* ZORAN functions */ -/* ----------------------------------------------------------------------- */ - -static void zoran_set_geo(struct zoran* ztv, struct vidinfo* i); - -#if 0 /* unused */ -static -void zoran_dump(struct zoran *ztv) -{ - char str[256]; - char *p=str; /* shut up, gcc! */ - int i; - - for (i=0; i<0x60; i+=4) { - if ((i % 16) == 0) { - if (i) printk("%s\n",str); - p = str; - p+= sprintf(str, KERN_DEBUG " %04x: ",i); - } - p += sprintf(p, "%08x ",zrread(i)); - } -} -#endif /* unused */ - -static -void reap_states(struct zoran* ztv) -{ - /* count frames */ - ztv->fieldnr++; - - /* - * Are we busy at all? - * This depends on if there is a workqueue AND the - * videotransfer is enabled on the chip... - */ - if (ztv->workqueue && (zrread(ZORAN_VDC) & ZORAN_VDC_VIDEN)) - { - struct vidinfo* newitem; - - /* did we get a complete frame? */ - if (zrread(ZORAN_VSTR) & ZORAN_VSTR_GRAB) - return; - -DEBUG(printk(CARD_DEBUG "completed %s at %p\n",CARD,ztv->workqueue->kindof==FBUFFER_GRAB?"grab":"read",ztv->workqueue)); - - /* we are done with this buffer, tell everyone */ - ztv->workqueue->status = FBUFFER_DONE; - ztv->workqueue->fieldnr = ztv->fieldnr; - /* not good, here for BTTV_FIELDNR reasons */ - ztv->lastfieldnr = ztv->fieldnr; - - switch (ztv->workqueue->kindof) { - case FBUFFER_GRAB: - wake_up_interruptible(&ztv->grabq); - break; - case FBUFFER_VBI: - wake_up_interruptible(&ztv->vbiq); - break; - default: - printk(CARD_INFO "somebody killed the workqueue (kindof=%d)!\n",CARD,ztv->workqueue->kindof); - } - - /* item completed, skip to next item in queue */ - write_lock(&ztv->lock); - newitem = ztv->workqueue->next; - ztv->workqueue->next = 0; /* mark completed */ - ztv->workqueue = newitem; - write_unlock(&ztv->lock); - } - - /* - * ok, so it seems we have nothing in progress right now. - * Lets see if we can find some work. - */ - if (ztv->workqueue) - { - struct vidinfo* newitem; -again: - -DEBUG(printk(CARD_DEBUG "starting %s at %p\n",CARD,ztv->workqueue->kindof==FBUFFER_GRAB?"grab":"read",ztv->workqueue)); - - /* loadup the frame settings */ - read_lock(&ztv->lock); - zoran_set_geo(ztv,ztv->workqueue); - read_unlock(&ztv->lock); - - switch (ztv->workqueue->kindof) { - case FBUFFER_GRAB: - case FBUFFER_VBI: - zrand(~ZORAN_OCR_OVLEN, ZORAN_OCR); - zror(ZORAN_VSTR_SNAPSHOT,ZORAN_VSTR); - zror(ZORAN_VDC_VIDEN,ZORAN_VDC); - - /* start single-shot grab */ - zror(ZORAN_VSTR_GRAB, ZORAN_VSTR); - break; - default: - printk(CARD_INFO "what is this doing on the queue? (kindof=%d)\n",CARD,ztv->workqueue->kindof); - write_lock(&ztv->lock); - newitem = ztv->workqueue->next; - ztv->workqueue->next = 0; - ztv->workqueue = newitem; - write_unlock(&ztv->lock); - if (newitem) - goto again; /* yeah, sure.. */ - } - /* bye for now */ - return; - } -DEBUG(printk(CARD_DEBUG "nothing in queue\n",CARD)); - - /* - * What? Even the workqueue is empty? Am i really here - * for nothing? Did i come all that way to... do nothing? - */ - - /* do we need to overlay? */ - if (test_bit(STATE_OVERLAY, &ztv->state)) - { - /* are we already overlaying? */ - if (!(zrread(ZORAN_OCR) & ZORAN_OCR_OVLEN) || - !(zrread(ZORAN_VDC) & ZORAN_VDC_VIDEN)) - { -DEBUG(printk(CARD_DEBUG "starting overlay\n",CARD)); - - read_lock(&ztv->lock); - zoran_set_geo(ztv,&ztv->overinfo); - read_unlock(&ztv->lock); - - zror(ZORAN_OCR_OVLEN, ZORAN_OCR); - zrand(~ZORAN_VSTR_SNAPSHOT,ZORAN_VSTR); - zror(ZORAN_VDC_VIDEN,ZORAN_VDC); - } - - /* - * leave overlaying on, but turn interrupts off. - */ - zrand(~ZORAN_ICR_EN,ZORAN_ICR); - return; - } - - /* do we have any VBI idle time processing? */ - if (test_bit(STATE_VBI, &ztv->state)) - { - struct vidinfo* item; - struct vidinfo* lastitem; - - /* protect the workqueue */ - write_lock(&ztv->lock); - lastitem = ztv->workqueue; - if (lastitem) - while (lastitem->next) lastitem = lastitem->next; - for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++) - if (item->next == 0 && item->status == FBUFFER_FREE) - { -DEBUG(printk(CARD_DEBUG "%p added to queue\n",CARD,item)); - item->status = FBUFFER_BUSY; - if (!lastitem) - ztv->workqueue = item; - else - lastitem->next = item; - lastitem = item; - } - write_unlock(&ztv->lock); - if (ztv->workqueue) - goto again; /* hey, _i_ graduated :) */ - } - - /* - * Then we must be realy IDLE - */ -DEBUG(printk(CARD_DEBUG "turning off\n",CARD)); - /* nothing further to do, disable DMA and further IRQs */ - zrand(~ZORAN_VDC_VIDEN,ZORAN_VDC); - zrand(~ZORAN_ICR_EN,ZORAN_ICR); -} - -static -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -void zoran_irq(int irq, void *dev_id, struct pt_regs * regs) -#else -void zoran_irq(int irq, void *dev_id) -#endif -{ - u32 stat,estat; - int count = 0; - struct zoran *ztv = dev_id; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) - UNUSED(irq); UNUSED(regs); -#else - UNUSED(irq); -#endif - for (;;) { - /* get/clear interrupt status bits */ - stat=zrread(ZORAN_ISR); - estat=stat & zrread(ZORAN_ICR); - if (!estat) - return; - zrwrite(estat,ZORAN_ISR); - IDEBUG(printk(CARD_DEBUG "estat %08x\n",CARD,estat)); - IDEBUG(printk(CARD_DEBUG " stat %08x\n",CARD,stat)); - - if (estat & ZORAN_ISR_CODE) - { - IDEBUG(printk(CARD_DEBUG "CodReplIRQ\n",CARD)); - } - if (estat & ZORAN_ISR_GIRQ0) - { - IDEBUG(printk(CARD_DEBUG "GIRQ0\n",CARD)); - if (!ztv->card->usegirq1) - reap_states(ztv); - } - if (estat & ZORAN_ISR_GIRQ1) - { - IDEBUG(printk(CARD_DEBUG "GIRQ1\n",CARD)); - if (ztv->card->usegirq1) - reap_states(ztv); - } - - count++; - if (count > 10) - printk(CARD_ERR "irq loop %d (%x)\n",CARD,count,estat); - if (count > 20) - { - zrwrite(0, ZORAN_ICR); - printk(CARD_ERR "IRQ lockup, cleared int mask\n",CARD); - } - } -} - -static -int zoran_muxsel(struct zoran* ztv, int channel, int norm) -{ - int rv; - - /* set the new video norm */ - rv = i2c_control_device(&(ztv->i2c), I2C_DRIVERID_VIDEODECODER, DECODER_SET_NORM, &norm); - if (rv) - return rv; - ztv->norm = norm; - - /* map the given channel to the cards decoder's channel */ - channel = ztv->card->video_mux[channel] & CHANNEL_MASK; - - /* set the new channel */ - rv = i2c_control_device(&(ztv->i2c), I2C_DRIVERID_VIDEODECODER, DECODER_SET_INPUT, &channel); - return rv; -} - -/* Tell the interrupt handler what to to. */ -static -void zoran_cap(struct zoran* ztv, int on) -{ -DEBUG(printk(CARD_DEBUG "zoran_cap(%d) state=%x\n",CARD,on,ztv->state)); - - if (on) { - ztv->running = 1; - - /* - * turn interrupts (back) on. The DMA will be enabled - * inside the irq handler when it detects a restart. - */ - zror(ZORAN_ICR_EN,ZORAN_ICR); - } - else { - /* - * turn both interrupts and DMA off - */ - zrand(~ZORAN_VDC_VIDEN,ZORAN_VDC); - zrand(~ZORAN_ICR_EN,ZORAN_ICR); - - ztv->running = 0; - } -} - -static ulong dmask[] = { - 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFC, 0xFFFFFFF8, - 0xFFFFFFF0, 0xFFFFFFE0, 0xFFFFFFC0, 0xFFFFFF80, - 0xFFFFFF00, 0xFFFFFE00, 0xFFFFFC00, 0xFFFFF800, - 0xFFFFF000, 0xFFFFE000, 0xFFFFC000, 0xFFFF8000, - 0xFFFF0000, 0xFFFE0000, 0xFFFC0000, 0xFFF80000, - 0xFFF00000, 0xFFE00000, 0xFFC00000, 0xFF800000, - 0xFF000000, 0xFE000000, 0xFC000000, 0xF8000000, - 0xF0000000, 0xE0000000, 0xC0000000, 0x80000000 -}; - -static -void zoran_built_overlay(struct zoran* ztv, int count, struct video_clip *vcp) -{ - ulong* mtop; - int ystep = (ztv->vidXshift + ztv->vidWidth+31)/32; /* next DWORD */ - int i; - -DEBUG(printk(KERN_DEBUG " overlay at %p, ystep=%d, clips=%d\n",ztv->overinfo.overlay,ystep,count)); - - for (i=0; i<count; i++) { - struct video_clip *vp = vcp+i; - UNUSED(vp); -DEBUG(printk(KERN_DEBUG " %d: clip(%d,%d,%d,%d)\n", i,vp->x,vp->y,vp->width,vp->height)); - } - - /* - * activate the visible portion of the screen - * Note we take some shortcuts here, because we - * know the width can never be < 32. (I.e. a DWORD) - * We also assume the overlay starts somewhere in - * the FIRST dword. - */ - { - int start = ztv->vidXshift; - ulong firstd = dmask[start]; - ulong lastd = ~dmask[(start + ztv->overinfo.w) & 31]; - mtop = ztv->overinfo.overlay; - for (i=0; i<ztv->overinfo.h; i++) { - int w = ztv->vidWidth; - ulong* line = mtop; - if (start & 31) { - *line++ = firstd; - w -= 32-(start&31); - } - memset(line, ~0, w/8); - if (w & 31) - line[w/32] = lastd; - mtop += ystep; - } - } - - /* process clipping regions */ - for (i=0; i<count; i++) { - int h; - if (vcp->x < 0 || (uint)vcp->x > ztv->overinfo.w || - vcp->y < 0 || vcp->y > ztv->overinfo.h || - vcp->width < 0 || (uint)(vcp->x+vcp->width) > ztv->overinfo.w || - vcp->height < 0 || (vcp->y+vcp->height) > ztv->overinfo.h) - { - DEBUG(printk(CARD_DEBUG "invalid clipzone (%d,%d,%d,%d) not in (0,0,%d,%d), adapting\n",CARD,vcp->x,vcp->y,vcp->width,vcp->height,ztv->overinfo.w,ztv->overinfo.h)); - if (vcp->x < 0) vcp->x = 0; - if ((uint)vcp->x > ztv->overinfo.w) vcp->x = ztv->overinfo.w; - if (vcp->y < 0) vcp->y = 0; - if (vcp->y > ztv->overinfo.h) vcp->y = ztv->overinfo.h; - if (vcp->width < 0) vcp->width = 0; - if ((uint)(vcp->x+vcp->width) > ztv->overinfo.w) vcp->width = ztv->overinfo.w - vcp->x; - if (vcp->height < 0) vcp->height = 0; - if (vcp->y+vcp->height > ztv->overinfo.h) vcp->height = ztv->overinfo.h - vcp->y; -// continue; - } - - mtop = &ztv->overinfo.overlay[vcp->y*ystep]; - for (h=0; h<=vcp->height; h++) { - int w; - int x = ztv->vidXshift + vcp->x; - for (w=0; w<=vcp->width; w++) { - clear_bit(x&31, &mtop[x/32]); - x++; - } - mtop += ystep; - } - ++vcp; - } - - mtop = ztv->overinfo.overlay; - zrwrite(virt_to_bus(mtop), ZORAN_MTOP); - zrwrite(virt_to_bus(mtop+ystep), ZORAN_MBOT); - zraor((ztv->vidInterlace*ystep)<<0,~ZORAN_OCR_MASKSTRIDE,ZORAN_OCR); -} - -struct tvnorm -{ - u16 Wt, Wa, Ht, Ha, HStart, VStart; -}; - -static struct tvnorm tvnorms[] = { - /* PAL-BDGHI */ -/* { 864, 720, 625, 576, 131, 21 },*/ -/*00*/ { 864, 768, 625, 576, 81, 17 }, - /* NTSC */ -/*01*/ { 858, 720, 525, 480, 121, 10 }, - /* SECAM */ -/*02*/ { 864, 720, 625, 576, 131, 21 }, - /* BW50 */ -/*03*/ { 864, 720, 625, 576, 131, 21 }, - /* BW60 */ -/*04*/ { 858, 720, 525, 480, 121, 10 } -}; -#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm)) - -/* - * Program the chip for a setup as described in the vidinfo struct. - * - * Side-effects: calculates vidXshift, vidInterlace, - * vidHeight, vidWidth which are used in a later stage - * to calculate the overlay mask - * - * This is an internal function, as such it does not check the - * validity of the struct members... Spectaculair crashes will - * follow /very/ quick when you're wrong and the chip right :) - */ -static -void zoran_set_geo(struct zoran* ztv, struct vidinfo* i) -{ - ulong top, bot; - int stride; - int winWidth, winHeight; - int maxWidth, maxHeight, maxXOffset, maxYOffset; - long vfec; - -DEBUG(printk(CARD_DEBUG "set_geo(rect=(%d,%d,%d,%d), norm=%d, format=%d, bpp=%d, bpl=%d, busadr=%lx, overlay=%p)\n",CARD,i->x,i->y,i->w,i->h,ztv->norm,i->format,i->bpp,i->bpl,i->busadr,i->overlay)); - - /* - * make sure the DMA transfers are inhibited during our - * reprogramming of the chip - */ - zrand(~ZORAN_VDC_VIDEN,ZORAN_VDC); - - maxWidth = tvnorms[ztv->norm].Wa; - maxHeight = tvnorms[ztv->norm].Ha/2; - maxXOffset = tvnorms[ztv->norm].HStart; - maxYOffset = tvnorms[ztv->norm].VStart; - - /* setup vfec register (keep ExtFl,TopField and VCLKPol settings) */ - vfec = (zrread(ZORAN_VFEC) & (ZORAN_VFEC_EXTFL|ZORAN_VFEC_TOPFIELD|ZORAN_VFEC_VCLKPOL)) | - (palette2fmt[i->format].mode & (ZORAN_VFEC_RGB|ZORAN_VFEC_ERRDIF|ZORAN_VFEC_LE|ZORAN_VFEC_PACK24)); - - /* - * Set top, bottom ptrs. Since these must be DWORD aligned, - * possible adjust the x and the width of the window. - * so the endposition stay the same. The vidXshift will make - * sure we are not writing pixels before the requested x. - */ - ztv->vidXshift = 0; - winWidth = i->w; - if (winWidth < 0) - winWidth = -winWidth; - top = i->busadr + i->x*i->bpp + i->y*i->bpl; - if (top & 3) { - ztv->vidXshift = (top & 3) / i->bpp; - winWidth += ztv->vidXshift; - DEBUG(printk(KERN_DEBUG " window-x shifted %d pixels left\n",ztv->vidXshift)); - top &= ~3; - } - - /* - * bottom points to next frame but in interleaved mode we want - * to 'mix' the 2 frames to one capture, so 'bot' points to one - * (physical) line below the top line. - */ - bot = top + i->bpl; - zrwrite(top,ZORAN_VTOP); - zrwrite(bot,ZORAN_VBOT); - - /* - * Make sure the winWidth is DWORD aligned too, - * thereby automaticly making sure the stride to the - * next line is DWORD aligned too (as required by spec). - */ - if ((winWidth*i->bpp) & 3) { -DEBUG(printk(KERN_DEBUG " window-width enlarged by %d pixels\n",(winWidth*i->bpp) & 3)); - winWidth += (winWidth*i->bpp) & 3; - } - - /* determine the DispMode and stride */ - if (i->h >= 0 && i->h <= maxHeight) { - /* single frame grab suffices for this height. */ - vfec |= ZORAN_VFEC_DISPMOD; - ztv->vidInterlace = 0; - stride = i->bpl - (winWidth*i->bpp); - winHeight = i->h; - } - else { - /* interleaving needed for this height */ - ztv->vidInterlace = 1; - stride = i->bpl*2 - (winWidth*i->bpp); - winHeight = i->h/2; - } - if (winHeight < 0) /* can happen for VBI! */ - winHeight = -winHeight; - - /* safety net, sometimes bpl is too short??? */ - if (stride<0) { -DEBUG(printk(CARD_DEBUG "WARNING stride = %d\n",CARD,stride)); - stride = 0; - } - - zraor((winHeight<<12)|(winWidth<<0),~(ZORAN_VDC_VIDWINHT|ZORAN_VDC_VIDWINWID), ZORAN_VDC); - zraor(stride<<16,~ZORAN_VSTR_DISPSTRIDE,ZORAN_VSTR); - - /* remember vidWidth, vidHeight for overlay calculations */ - ztv->vidWidth = winWidth; - ztv->vidHeight = winHeight; -DEBUG(printk(KERN_DEBUG " top=%08lx, bottom=%08lx\n",top,bot)); -DEBUG(printk(KERN_DEBUG " winWidth=%d, winHeight=%d\n",winWidth,winHeight)); -DEBUG(printk(KERN_DEBUG " maxWidth=%d, maxHeight=%d\n",maxWidth,maxHeight)); -DEBUG(printk(KERN_DEBUG " stride=%d\n",stride)); - - /* - * determine horizontal scales and crops - */ - if (i->w < 0) { - int Hstart = 1; - int Hend = Hstart + winWidth; -DEBUG(printk(KERN_DEBUG " Y: scale=0, start=%d, end=%d\n", Hstart, Hend)); - zraor((Hstart<<10)|(Hend<<0),~(ZORAN_VFEH_HSTART|ZORAN_VFEH_HEND),ZORAN_VFEH); - } - else { - int Wa = maxWidth; - int X = (winWidth*64+Wa-1)/Wa; - int We = winWidth*64/X; - int HorDcm = 64-X; - int hcrop1 = 2*(Wa-We)/4; - /* - * BUGFIX: Juha Nurmela <junki@qn-lpr2-165.quicknet.inet.fi> - * found the solution to the color phase shift. - * See ChangeLog for the full explanation) - */ - int Hstart = (maxXOffset + hcrop1) | 1; - int Hend = Hstart + We - 1; - -DEBUG(printk(KERN_DEBUG " X: scale=%d, start=%d, end=%d\n", HorDcm, Hstart, Hend)); - - zraor((Hstart<<10)|(Hend<<0),~(ZORAN_VFEH_HSTART|ZORAN_VFEH_HEND),ZORAN_VFEH); - vfec |= HorDcm<<14; - - if (HorDcm<16) - vfec |= ZORAN_VFEC_HFILTER_1; /* no filter */ - else if (HorDcm<32) - vfec |= ZORAN_VFEC_HFILTER_3; /* 3 tap filter */ - else if (HorDcm<48) - vfec |= ZORAN_VFEC_HFILTER_4; /* 4 tap filter */ - else vfec |= ZORAN_VFEC_HFILTER_5; /* 5 tap filter */ - } - - /* - * Determine vertical scales and crops - * - * when height is negative, we want to read starting at line 0 - * One day someone might need access to these lines... - */ - if (i->h < 0) { - int Vstart = 0; - int Vend = Vstart + winHeight; -DEBUG(printk(KERN_DEBUG " Y: scale=0, start=%d, end=%d\n", Vstart, Vend)); - zraor((Vstart<<10)|(Vend<<0),~(ZORAN_VFEV_VSTART|ZORAN_VFEV_VEND),ZORAN_VFEV); - } - else { - int Ha = maxHeight; - int Y = (winHeight*64+Ha-1)/Ha; - int He = winHeight*64/Y; - int VerDcm = 64-Y; - int vcrop1 = 2*(Ha-He)/4; - int Vstart = maxYOffset + vcrop1; - int Vend = Vstart + He - 1; - -DEBUG(printk(KERN_DEBUG " Y: scale=%d, start=%d, end=%d\n", VerDcm, Vstart, Vend)); - zraor((Vstart<<10)|(Vend<<0),~(ZORAN_VFEV_VSTART|ZORAN_VFEV_VEND),ZORAN_VFEV); - vfec |= VerDcm<<8; - } - -DEBUG(printk(KERN_DEBUG " F: format=%d(=%s)\n",i->format,palette2fmt[i->format].name)); - - /* setup the requested format */ - zrwrite(vfec, ZORAN_VFEC); -} - -static -void zoran_common_open(struct zoran* ztv, int flags) -{ - UNUSED(flags); - - /* already opened? */ - if (ztv->users++ != 0) - return; - - /* unmute audio */ - /* /what/ audio? */ - - ztv->state = 0; - - /* setup the encoder to the initial values */ - ztv->picture.colour=254<<7; - ztv->picture.brightness=128<<8; - ztv->picture.hue=128<<8; - ztv->picture.contrast=216<<7; - i2c_control_device(&ztv->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_PICTURE, &ztv->picture); - - /* default to the composite input since my camera is there */ - zoran_muxsel(ztv, 0, VIDEO_MODE_PAL); -} - -static -void zoran_common_close(struct zoran* ztv) -{ - if (--ztv->users != 0) - return; - - /* mute audio */ - /* /what/ audio? */ - - /* stop the chip */ - zoran_cap(ztv, 0); -} - -/* - * Open a zoran card. Right now the flags are just a hack - */ -static int zoran_open(struct video_device *dev, int flags) -{ - struct zoran *ztv = (struct zoran*)dev; - struct vidinfo* item; - char* pos; - - DEBUG(printk(CARD_DEBUG "open(dev,%d)\n",CARD,flags)); - - /********************************************* - * We really should be doing lazy allocing... - *********************************************/ - /* allocate a frame buffer */ - if (!ztv->fbuffer) - ztv->fbuffer = bmalloc(ZORAN_MAX_FBUFSIZE); - if (!ztv->fbuffer) { - /* could not get a buffer, bail out */ - return -ENOBUFS; - } - /* at this time we _always_ have a framebuffer */ - memset(ztv->fbuffer,0,ZORAN_MAX_FBUFSIZE); - - if (!ztv->overinfo.overlay) - ztv->overinfo.overlay = kmalloc(1024*1024/8, GFP_KERNEL); - if (!ztv->overinfo.overlay) { - /* could not get an overlay buffer, bail out */ - bfree(ztv->fbuffer, ZORAN_MAX_FBUFSIZE); - return -ENOBUFS; - } - /* at this time we _always_ have a overlay */ - - /* clear buffer status, and give them a DMAable address */ - pos = ztv->fbuffer; - for (item=ztv->grabinfo; item!=ztv->grabinfo+ZORAN_MAX_FBUFFERS; item++) - { - item->status = FBUFFER_FREE; - item->memadr = pos; - item->busadr = virt_to_bus(pos); - pos += ZORAN_MAX_FBUFFER; - } - - /* do the common part of all open's */ - zoran_common_open(ztv, flags); - - return 0; -} - -static -void zoran_close(struct video_device* dev) -{ - struct zoran *ztv = (struct zoran*)dev; - - DEBUG(printk(CARD_DEBUG "close(dev)\n",CARD)); - - /* driver specific closure */ - clear_bit(STATE_OVERLAY, &ztv->state); - - zoran_common_close(ztv); - - /* - * This is sucky but right now I can't find a good way to - * be sure its safe to free the buffer. We wait 5-6 fields - * which is more than sufficient to be sure. - */ - msleep(100); /* Wait 1/10th of a second */ - - /* free the allocated framebuffer */ - bfree(ztv->fbuffer, ZORAN_MAX_FBUFSIZE); - ztv->fbuffer = 0; - kfree(ztv->overinfo.overlay); - ztv->overinfo.overlay = 0; - -} - -/* - * This read function could be used reentrant in a SMP situation. - * - * This is made possible by the spinlock which is kept till we - * found and marked a buffer for our own use. The lock must - * be released as soon as possible to prevent lock contention. - */ -static -long zoran_read(struct video_device* dev, char* buf, unsigned long count, int nonblock) -{ - struct zoran *ztv = (struct zoran*)dev; - unsigned long max; - struct vidinfo* unused = 0; - struct vidinfo* done = 0; - - DEBUG(printk(CARD_DEBUG "zoran_read(%p,%ld,%d)\n",CARD,buf,count,nonblock)); - - /* find ourself a free or completed buffer */ - for (;;) { - struct vidinfo* item; - - write_lock_irq(&ztv->lock); - for (item=ztv->grabinfo; item!=ztv->grabinfo+ZORAN_MAX_FBUFFERS; item++) - { - if (!unused && item->status == FBUFFER_FREE) - unused = item; - if (!done && item->status == FBUFFER_DONE) - done = item; - } - if (done || unused) - break; - - /* no more free buffers, wait for them. */ - write_unlock_irq(&ztv->lock); - if (nonblock) - return -EWOULDBLOCK; - interruptible_sleep_on(&ztv->grabq); - if (signal_pending(current)) - return -EINTR; - } - - /* Do we have 'ready' data? */ - if (!done) { - /* no? than this will take a while... */ - if (nonblock) { - write_unlock_irq(&ztv->lock); - return -EWOULDBLOCK; - } - - /* mark the unused buffer as wanted */ - unused->status = FBUFFER_BUSY; - unused->w = 320; - unused->h = 240; - unused->format = VIDEO_PALETTE_RGB24; - unused->bpp = palette2fmt[unused->format].bpp; - unused->bpl = unused->w * unused->bpp; - unused->next = 0; - { /* add to tail of queue */ - struct vidinfo* oldframe = ztv->workqueue; - if (!oldframe) ztv->workqueue = unused; - else { - while (oldframe->next) oldframe = oldframe->next; - oldframe->next = unused; - } - } - write_unlock_irq(&ztv->lock); - - /* tell the state machine we want it filled /NOW/ */ - zoran_cap(ztv, 1); - - /* wait till this buffer gets grabbed */ - wait_event_interruptible(ztv->grabq, - (unused->status != FBUFFER_BUSY)); - /* see if a signal did it */ - if (signal_pending(current)) - return -EINTR; - done = unused; - } - else - write_unlock_irq(&ztv->lock); - - /* Yes! we got data! */ - max = done->bpl * done->h; - if (count > max) - count = max; - if (copy_to_user((void*)buf, done->memadr, count)) - count = -EFAULT; - - /* keep the engine running */ - done->status = FBUFFER_FREE; -// zoran_cap(ztv,1); - - /* tell listeners this buffer became free */ - wake_up_interruptible(&ztv->grabq); - - /* goodbye */ - DEBUG(printk(CARD_DEBUG "zoran_read() returns %lu\n",CARD,count)); - return count; -} - -static -long zoran_write(struct video_device* dev, const char* buf, unsigned long count, int nonblock) -{ - struct zoran *ztv = (struct zoran *)dev; - UNUSED(ztv); UNUSED(dev); UNUSED(buf); UNUSED(count); UNUSED(nonblock); - DEBUG(printk(CARD_DEBUG "zoran_write\n",CARD)); - return -EINVAL; -} - -static -unsigned int zoran_poll(struct video_device *dev, struct file *file, poll_table *wait) -{ - struct zoran *ztv = (struct zoran *)dev; - struct vidinfo* item; - unsigned int mask = 0; - - poll_wait(file, &ztv->grabq, wait); - - for (item=ztv->grabinfo; item!=ztv->grabinfo+ZORAN_MAX_FBUFFERS; item++) - if (item->status == FBUFFER_DONE) - { - mask |= (POLLIN | POLLRDNORM); - break; - } - - DEBUG(printk(CARD_DEBUG "zoran_poll()=%x\n",CARD,mask)); - - return mask; -} - -/* append a new clipregion to the vector of video_clips */ -static -void new_clip(struct video_window* vw, struct video_clip* vcp, int x, int y, int w, int h) -{ - vcp[vw->clipcount].x = x; - vcp[vw->clipcount].y = y; - vcp[vw->clipcount].width = w; - vcp[vw->clipcount].height = h; - vw->clipcount++; -} - -static -int zoran_ioctl(struct video_device* dev, unsigned int cmd, void *arg) -{ - struct zoran* ztv = (struct zoran*)dev; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability c; - DEBUG(printk(CARD_DEBUG "VIDIOCGCAP\n",CARD)); - - strcpy(c.name,ztv->video_dev.name); - c.type = VID_TYPE_CAPTURE| - VID_TYPE_OVERLAY| - VID_TYPE_CLIPPING| - VID_TYPE_FRAMERAM| - VID_TYPE_SCALES; - if (ztv->have_tuner) - c.type |= VID_TYPE_TUNER; - if (pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) - c.type &= ~VID_TYPE_OVERLAY; - if (ztv->have_decoder) { - c.channels = ztv->card->video_inputs; - c.audios = ztv->card->audio_inputs; - } else - /* no decoder -> no channels */ - c.channels = c.audios = 0; - c.maxwidth = 768; - c.maxheight = 576; - c.minwidth = 32; - c.minheight = 32; - if (copy_to_user(arg,&c,sizeof(c))) - return -EFAULT; - break; - } - - case VIDIOCGCHAN: - { - struct video_channel v; - int mux; - if (copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCGCHAN(%d)\n",CARD,v.channel)); - v.flags=VIDEO_VC_AUDIO -#ifdef VIDEO_VC_NORM - |VIDEO_VC_NORM -#endif - ; - v.tuners=0; - v.type=VIDEO_TYPE_CAMERA; -#ifdef I_EXPECT_POSSIBLE_NORMS_IN_THE_API - v.norm=VIDEO_MODE_PAL| - VIDEO_MODE_NTSC| - VIDEO_MODE_SECAM; -#else - v.norm=VIDEO_MODE_PAL; -#endif - /* too many inputs? no decoder -> no channels */ - if (!ztv->have_decoder || v.channel < 0 || v.channel >= ztv->card->video_inputs) - return -EINVAL; - - /* now determine the name of the channel */ - mux = ztv->card->video_mux[v.channel]; - if (mux & IS_TUNER) { - /* lets assume only one tuner, yes? */ - strcpy(v.name,"Television"); - v.type = VIDEO_TYPE_TV; - if (ztv->have_tuner) { - v.flags |= VIDEO_VC_TUNER; - v.tuners = 1; - } - } - else if (mux & IS_SVHS) - sprintf(v.name,"S-Video-%d",v.channel); - else - sprintf(v.name,"CVBS-%d",v.channel); - - if (copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - break; - } - case VIDIOCSCHAN: - { /* set video channel */ - struct video_channel v; - if (copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCSCHAN(%d,%d)\n",CARD,v.channel,v.norm)); - - /* too many inputs? no decoder -> no channels */ - if (!ztv->have_decoder || v.channel >= ztv->card->video_inputs || v.channel < 0) - return -EINVAL; - - if (v.norm != VIDEO_MODE_PAL && - v.norm != VIDEO_MODE_NTSC && - v.norm != VIDEO_MODE_SECAM && - v.norm != VIDEO_MODE_AUTO) - return -EOPNOTSUPP; - - /* make it happen, nr1! */ - return zoran_muxsel(ztv,v.channel,v.norm); - } - - case VIDIOCGTUNER: - { - struct video_tuner v; - if (copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCGTUNER(%d)\n",CARD,v.tuner)); - - /* Only no or one tuner for now */ - if (!ztv->have_tuner || v.tuner) - return -EINVAL; - - strcpy(v.name,"Television"); - v.rangelow = 0; - v.rangehigh = ~0; - v.flags = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; - v.mode = ztv->norm; - v.signal = 0xFFFF; /* unknown */ - - if (copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - break; - } - case VIDIOCSTUNER: - { - struct video_tuner v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCSTUNER(%d,%d)\n",CARD,v.tuner,v.mode)); - - /* Only no or one tuner for now */ - if (!ztv->have_tuner || v.tuner) - return -EINVAL; - - /* and it only has certain valid modes */ - if( v.mode != VIDEO_MODE_PAL && - v.mode != VIDEO_MODE_NTSC && - v.mode != VIDEO_MODE_SECAM) - return -EOPNOTSUPP; - - /* engage! */ - return zoran_muxsel(ztv,v.tuner,v.mode); - } - - case VIDIOCGPICT: - { - struct video_picture p = ztv->picture; - DEBUG(printk(CARD_DEBUG "VIDIOCGPICT\n",CARD)); - p.depth = ztv->depth; - switch (p.depth) { - case 8: p.palette=VIDEO_PALETTE_YUV422; - break; - case 15: p.palette=VIDEO_PALETTE_RGB555; - break; - case 16: p.palette=VIDEO_PALETTE_RGB565; - break; - case 24: p.palette=VIDEO_PALETTE_RGB24; - break; - case 32: p.palette=VIDEO_PALETTE_RGB32; - break; - } - if (copy_to_user(arg, &p, sizeof(p))) - return -EFAULT; - break; - } - case VIDIOCSPICT: - { - struct video_picture p; - if (copy_from_user(&p, arg,sizeof(p))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCSPICT(%d,%d,%d,%d,%d,%d,%d)\n",CARD,p.brightness,p.hue,p.colour,p.contrast,p.whiteness,p.depth,p.palette)); - - /* depth must match with framebuffer */ - if (p.depth != ztv->depth) - return -EINVAL; - - /* check if palette matches this bpp */ - if (p.palette>NRPALETTES || - palette2fmt[p.palette].bpp != ztv->overinfo.bpp) - return -EINVAL; - - write_lock_irq(&ztv->lock); - ztv->overinfo.format = p.palette; - ztv->picture = p; - write_unlock_irq(&ztv->lock); - - /* tell the decoder */ - i2c_control_device(&ztv->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_SET_PICTURE, &p); - break; - } - - case VIDIOCGWIN: - { - struct video_window vw; - DEBUG(printk(CARD_DEBUG "VIDIOCGWIN\n",CARD)); - read_lock(&ztv->lock); - vw.x = ztv->overinfo.x; - vw.y = ztv->overinfo.y; - vw.width = ztv->overinfo.w; - vw.height = ztv->overinfo.h; - vw.chromakey= 0; - vw.flags = 0; - if (ztv->vidInterlace) - vw.flags|=VIDEO_WINDOW_INTERLACE; - read_unlock(&ztv->lock); - if (copy_to_user(arg,&vw,sizeof(vw))) - return -EFAULT; - break; - } - case VIDIOCSWIN: - { - struct video_window vw; - struct video_clip *vcp; - int on; - if (copy_from_user(&vw,arg,sizeof(vw))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCSWIN(%d,%d,%d,%d,%x,%d)\n",CARD,vw.x,vw.y,vw.width,vw.height,vw.flags,vw.clipcount)); - - if (vw.flags) - return -EINVAL; - - if (vw.clipcount <0 || vw.clipcount>256) - return -EDOM; /* Too many! */ - - /* - * Do any clips. - */ - vcp = vmalloc(sizeof(struct video_clip)*(vw.clipcount+4)); - if (vcp==NULL) - return -ENOMEM; - if (vw.clipcount && copy_from_user(vcp,vw.clips,sizeof(struct video_clip)*vw.clipcount)) { - vfree(vcp); - return -EFAULT; - } - - on = ztv->running; - if (on) - zoran_cap(ztv, 0); - - /* - * strange, it seems xawtv sometimes calls us with 0 - * width and/or height. Ignore these values - */ - if (vw.x == 0) - vw.x = ztv->overinfo.x; - if (vw.y == 0) - vw.y = ztv->overinfo.y; - - /* by now we are committed to the new data... */ - write_lock_irq(&ztv->lock); - ztv->overinfo.x = vw.x; - ztv->overinfo.y = vw.y; - ztv->overinfo.w = vw.width; - ztv->overinfo.h = vw.height; - write_unlock_irq(&ztv->lock); - - /* - * Impose display clips - */ - if (vw.x+vw.width > ztv->swidth) - new_clip(&vw, vcp, ztv->swidth-vw.x, 0, vw.width-1, vw.height-1); - if (vw.y+vw.height > ztv->sheight) - new_clip(&vw, vcp, 0, ztv->sheight-vw.y, vw.width-1, vw.height-1); - - /* built the requested clipping zones */ - zoran_set_geo(ztv, &ztv->overinfo); - zoran_built_overlay(ztv, vw.clipcount, vcp); - vfree(vcp); - - /* if we were on, restart the video engine */ - if (on) - zoran_cap(ztv, 1); - break; - } - - case VIDIOCCAPTURE: - { - int v; - if (get_user(v, (int *)arg)) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCCAPTURE(%d)\n",CARD,v)); - - if (v==0) { - clear_bit(STATE_OVERLAY, &ztv->state); - zoran_cap(ztv, 1); - } - else { - /* is VIDIOCSFBUF, VIDIOCSWIN done? */ - if (ztv->overinfo.busadr==0 || ztv->overinfo.w==0 || ztv->overinfo.h==0) - return -EINVAL; - - set_bit(STATE_OVERLAY, &ztv->state); - zoran_cap(ztv, 1); - } - break; - } - - case VIDIOCGFBUF: - { - struct video_buffer v; - DEBUG(printk(CARD_DEBUG "VIDIOCGFBUF\n",CARD)); - read_lock(&ztv->lock); - v.base = (void *)ztv->overinfo.busadr; - v.height = ztv->sheight; - v.width = ztv->swidth; - v.depth = ztv->depth; - v.bytesperline = ztv->overinfo.bpl; - read_unlock(&ztv->lock); - if(copy_to_user(arg, &v,sizeof(v))) - return -EFAULT; - break; - } - case VIDIOCSFBUF: - { - struct video_buffer v; - if(!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (pcipci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) - return -ENXIO; - if (copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCSFBUF(%p,%d,%d,%d,%d)\n",CARD,v.base, v.width,v.height,v.depth,v.bytesperline)); - - if (v.depth!=15 && v.depth!=16 && v.depth!=24 && v.depth!=32) - return -EINVAL; - if (v.bytesperline<1) - return -EINVAL; - if (ztv->running) - return -EBUSY; - write_lock_irq(&ztv->lock); - ztv->overinfo.busadr = (ulong)v.base; - ztv->sheight = v.height; - ztv->swidth = v.width; - ztv->depth = v.depth; /* bits per pixel */ - ztv->overinfo.bpp = ((v.depth+1)&0x38)/8;/* bytes per pixel */ - ztv->overinfo.bpl = v.bytesperline; /* bytes per line */ - write_unlock_irq(&ztv->lock); - break; - } - - case VIDIOCKEY: - { - /* Will be handled higher up .. */ - break; - } - - case VIDIOCSYNC: - { - int i; - if (get_user(i, (int *) arg)) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDEOCSYNC(%d)\n",CARD,i)); - if (i<0 || i>ZORAN_MAX_FBUFFERS) - return -EINVAL; - switch (ztv->grabinfo[i].status) { - case FBUFFER_FREE: - return -EINVAL; - case FBUFFER_BUSY: - /* wait till this buffer gets grabbed */ - wait_event_interruptible(ztv->grabq, - (ztv->grabinfo[i].status != FBUFFER_BUSY)); - /* see if a signal did it */ - if (signal_pending(current)) - return -EINTR; - /* don't fall through; a DONE buffer is not UNUSED */ - break; - case FBUFFER_DONE: - ztv->grabinfo[i].status = FBUFFER_FREE; - /* tell ppl we have a spare buffer */ - wake_up_interruptible(&ztv->grabq); - break; - } - DEBUG(printk(CARD_DEBUG "VIDEOCSYNC(%d) returns\n",CARD,i)); - break; - } - - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - struct vidinfo* frame; - if (copy_from_user(&vm,arg,sizeof(vm))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCMCAPTURE(%d,(%d,%d),%d)\n",CARD,vm.frame,vm.width,vm.height,vm.format)); - if (vm.frame<0 || vm.frame>ZORAN_MAX_FBUFFERS || - vm.width<32 || vm.width>768 || - vm.height<32 || vm.height>576 || - vm.format>NRPALETTES || - palette2fmt[vm.format].mode == 0) - return -EINVAL; - - /* we are allowed to take over UNUSED and DONE buffers */ - frame = &ztv->grabinfo[vm.frame]; - if (frame->status == FBUFFER_BUSY) - return -EBUSY; - - /* setup the other parameters if they are given */ - write_lock_irq(&ztv->lock); - frame->w = vm.width; - frame->h = vm.height; - frame->format = vm.format; - frame->bpp = palette2fmt[frame->format].bpp; - frame->bpl = frame->w*frame->bpp; - frame->status = FBUFFER_BUSY; - frame->next = 0; - { /* add to tail of queue */ - struct vidinfo* oldframe = ztv->workqueue; - if (!oldframe) ztv->workqueue = frame; - else { - while (oldframe->next) oldframe = oldframe->next; - oldframe->next = frame; - } - } - write_unlock_irq(&ztv->lock); - zoran_cap(ztv, 1); - break; - } - - case VIDIOCGMBUF: - { - struct video_mbuf mb; - int i; - DEBUG(printk(CARD_DEBUG "VIDIOCGMBUF\n",CARD)); - mb.size = ZORAN_MAX_FBUFSIZE; - mb.frames = ZORAN_MAX_FBUFFERS; - for (i=0; i<ZORAN_MAX_FBUFFERS; i++) - mb.offsets[i] = i*ZORAN_MAX_FBUFFER; - if(copy_to_user(arg, &mb,sizeof(mb))) - return -EFAULT; - break; - } - - case VIDIOCGUNIT: - { - struct video_unit vu; - DEBUG(printk(CARD_DEBUG "VIDIOCGUNIT\n",CARD)); - vu.video = ztv->video_dev.minor; - vu.vbi = ztv->vbi_dev.minor; - vu.radio = VIDEO_NO_UNIT; - vu.audio = VIDEO_NO_UNIT; - vu.teletext = VIDEO_NO_UNIT; - if(copy_to_user(arg, &vu,sizeof(vu))) - return -EFAULT; - break; - } - - case VIDIOCGFREQ: - { - unsigned long v = ztv->tuner_freq; - if (copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCGFREQ\n",CARD)); - break; - } - case VIDIOCSFREQ: - { - unsigned long v; - if (copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCSFREQ\n",CARD)); - - if (ztv->have_tuner) { - int fixme = v; - if (i2c_control_device(&(ztv->i2c), I2C_DRIVERID_TUNER, TUNER_SET_TVFREQ, &fixme) < 0) - return -EAGAIN; - } - ztv->tuner_freq = v; - break; - } - - /* Why isn't this in the API? - * And why doesn't it take a buffer number? - case BTTV_FIELDNR: - { - unsigned long v = ztv->lastfieldnr; - if (copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "BTTV_FIELDNR\n",CARD)); - break; - } - */ - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static -int zoran_mmap(struct vm_area_struct *vma, struct video_device* dev, const char* adr, unsigned long size) -{ - struct zoran* ztv = (struct zoran*)dev; - unsigned long start = (unsigned long)adr; - unsigned long pos; - - DEBUG(printk(CARD_DEBUG "zoran_mmap(0x%p,%ld)\n",CARD,adr,size)); - - /* sanity checks */ - if (size > ZORAN_MAX_FBUFSIZE || !ztv->fbuffer) - return -EINVAL; - - /* start mapping the whole shabang to user memory */ - pos = (unsigned long)ztv->fbuffer; - while (size>0) { - unsigned long pfn = virt_to_phys((void*)pos) >> PAGE_SHIFT; - if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } - return 0; -} - -static struct video_device zr36120_template= -{ - .owner = THIS_MODULE, - .name = "UNSET", - .type = VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY, - .hardware = VID_HARDWARE_ZR36120, - .open = zoran_open, - .close = zoran_close, - .read = zoran_read, - .write = zoran_write, - .poll = zoran_poll, - .ioctl = zoran_ioctl, - .compat_ioctl = v4l_compat_ioctl32, - .mmap = zoran_mmap, - .minor = -1, -}; - -static -int vbi_open(struct video_device *dev, int flags) -{ - struct zoran *ztv = dev->priv; - struct vidinfo* item; - - DEBUG(printk(CARD_DEBUG "vbi_open(dev,%d)\n",CARD,flags)); - - /* - * During VBI device open, we continiously grab VBI-like - * data in the vbi buffer when we have nothing to do. - * Only when there is an explicit request for VBI data - * (read call) we /force/ a read. - */ - - /* allocate buffers */ - for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++) - { - item->status = FBUFFER_FREE; - - /* alloc */ - if (!item->memadr) { - item->memadr = bmalloc(ZORAN_VBI_BUFSIZE); - if (!item->memadr) { - /* could not get a buffer, bail out */ - while (item != ztv->readinfo) { - item--; - bfree(item->memadr, ZORAN_VBI_BUFSIZE); - item->memadr = 0; - item->busadr = 0; - } - return -ENOBUFS; - } - } - - /* determine the DMAable address */ - item->busadr = virt_to_bus(item->memadr); - } - - /* do the common part of all open's */ - zoran_common_open(ztv, flags); - - set_bit(STATE_VBI, &ztv->state); - /* start read-ahead */ - zoran_cap(ztv, 1); - - return 0; -} - -static -void vbi_close(struct video_device *dev) -{ - struct zoran *ztv = dev->priv; - struct vidinfo* item; - - DEBUG(printk(CARD_DEBUG "vbi_close(dev)\n",CARD)); - - /* driver specific closure */ - clear_bit(STATE_VBI, &ztv->state); - - zoran_common_close(ztv); - - /* - * This is sucky but right now I can't find a good way to - * be sure its safe to free the buffer. We wait 5-6 fields - * which is more than sufficient to be sure. - */ - msleep(100); /* Wait 1/10th of a second */ - - for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++) - { - if (item->memadr) - bfree(item->memadr, ZORAN_VBI_BUFSIZE); - item->memadr = 0; - } - -} - -/* - * This read function could be used reentrant in a SMP situation. - * - * This is made possible by the spinlock which is kept till we - * found and marked a buffer for our own use. The lock must - * be released as soon as possible to prevent lock contention. - */ -static -long vbi_read(struct video_device* dev, char* buf, unsigned long count, int nonblock) -{ - struct zoran *ztv = dev->priv; - unsigned long max; - struct vidinfo* unused = 0; - struct vidinfo* done = 0; - - DEBUG(printk(CARD_DEBUG "vbi_read(0x%p,%ld,%d)\n",CARD,buf,count,nonblock)); - - /* find ourself a free or completed buffer */ - for (;;) { - struct vidinfo* item; - - write_lock_irq(&ztv->lock); - for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++) { - if (!unused && item->status == FBUFFER_FREE) - unused = item; - if (!done && item->status == FBUFFER_DONE) - done = item; - } - if (done || unused) - break; - - /* no more free buffers, wait for them. */ - write_unlock_irq(&ztv->lock); - if (nonblock) - return -EWOULDBLOCK; - interruptible_sleep_on(&ztv->vbiq); - if (signal_pending(current)) - return -EINTR; - } - - /* Do we have 'ready' data? */ - if (!done) { - /* no? than this will take a while... */ - if (nonblock) { - write_unlock_irq(&ztv->lock); - return -EWOULDBLOCK; - } - - /* mark the unused buffer as wanted */ - unused->status = FBUFFER_BUSY; - unused->next = 0; - { /* add to tail of queue */ - struct vidinfo* oldframe = ztv->workqueue; - if (!oldframe) ztv->workqueue = unused; - else { - while (oldframe->next) oldframe = oldframe->next; - oldframe->next = unused; - } - } - write_unlock_irq(&ztv->lock); - - /* tell the state machine we want it filled /NOW/ */ - zoran_cap(ztv, 1); - - /* wait till this buffer gets grabbed */ - wait_event_interruptible(ztv->vbiq, - (unused->status != FBUFFER_BUSY)); - /* see if a signal did it */ - if (signal_pending(current)) - return -EINTR; - done = unused; - } - else - write_unlock_irq(&ztv->lock); - - /* Yes! we got data! */ - max = done->bpl * -done->h; - if (count > max) - count = max; - - /* check if the user gave us enough room to write the data */ - if (!access_ok(VERIFY_WRITE, buf, count)) { - count = -EFAULT; - goto out; - } - - /* - * Now transform/strip the data from YUV to Y-only - * NB. Assume the Y is in the LSB of the YUV data. - */ - { - unsigned char* optr = buf; - unsigned char* eptr = buf+count; - - /* are we beeing accessed from an old driver? */ - if (count == 2*19*2048) { - /* - * Extreme HACK, old VBI programs expect 2048 points - * of data, and we only got 864 orso. Double each - * datapoint and clear the rest of the line. - * This way we have appear to have a - * sample_frequency of 29.5 Mc. - */ - int x,y; - unsigned char* iptr = done->memadr+1; - for (y=done->h; optr<eptr && y<0; y++) - { - /* copy to doubled data to userland */ - for (x=0; optr+1<eptr && x<-done->w; x++) - { - unsigned char a = iptr[x*2]; - __put_user(a, optr++); - __put_user(a, optr++); - } - /* and clear the rest of the line */ - for (x*=2; optr<eptr && x<done->bpl; x++) - __put_user(0, optr++); - /* next line */ - iptr += done->bpl; - } - } - else { - /* - * Other (probably newer) programs asked - * us what geometry we are using, and are - * reading the correct size. - */ - int x,y; - unsigned char* iptr = done->memadr+1; - for (y=done->h; optr<eptr && y<0; y++) - { - /* copy to doubled data to userland */ - for (x=0; optr<eptr && x<-done->w; x++) - __put_user(iptr[x*2], optr++); - /* and clear the rest of the line */ - for (;optr<eptr && x<done->bpl; x++) - __put_user(0, optr++); - /* next line */ - iptr += done->bpl; - } - } - - /* API compliance: - * place the framenumber (half fieldnr) in the last long - */ - __put_user(done->fieldnr/2, ((ulong*)eptr)[-1]); - } - - /* keep the engine running */ - done->status = FBUFFER_FREE; - zoran_cap(ztv, 1); - - /* tell listeners this buffer just became free */ - wake_up_interruptible(&ztv->vbiq); - - /* goodbye */ -out: - DEBUG(printk(CARD_DEBUG "vbi_read() returns %lu\n",CARD,count)); - return count; -} - -static -unsigned int vbi_poll(struct video_device *dev, struct file *file, poll_table *wait) -{ - struct zoran *ztv = dev->priv; - struct vidinfo* item; - unsigned int mask = 0; - - poll_wait(file, &ztv->vbiq, wait); - - for (item=ztv->readinfo; item!=ztv->readinfo+ZORAN_VBI_BUFFERS; item++) - if (item->status == FBUFFER_DONE) - { - mask |= (POLLIN | POLLRDNORM); - break; - } - - DEBUG(printk(CARD_DEBUG "vbi_poll()=%x\n",CARD,mask)); - - return mask; -} - -static -int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) -{ - struct zoran* ztv = dev->priv; - - switch (cmd) { - case VIDIOCGVBIFMT: - { - struct vbi_format f; - DEBUG(printk(CARD_DEBUG "VIDIOCGVBIINFO\n",CARD)); - f.sampling_rate = 14750000UL; - f.samples_per_line = -ztv->readinfo[0].w; - f.sample_format = VIDEO_PALETTE_RAW; - f.start[0] = f.start[1] = ztv->readinfo[0].y; - f.start[1] += 312; - f.count[0] = f.count[1] = -ztv->readinfo[0].h; - f.flags = VBI_INTERLACED; - if (copy_to_user(arg,&f,sizeof(f))) - return -EFAULT; - break; - } - case VIDIOCSVBIFMT: - { - struct vbi_format f; - int i; - if (copy_from_user(&f, arg,sizeof(f))) - return -EFAULT; - DEBUG(printk(CARD_DEBUG "VIDIOCSVBIINFO(%d,%d,%d,%d,%d,%d,%d,%x)\n",CARD,f.sampling_rate,f.samples_per_line,f.sample_format,f.start[0],f.start[1],f.count[0],f.count[1],f.flags)); - - /* lots of parameters are fixed... (PAL) */ - if (f.sampling_rate != 14750000UL || - f.samples_per_line > 864 || - f.sample_format != VIDEO_PALETTE_RAW || - f.start[0] < 0 || - f.start[0] != f.start[1]-312 || - f.count[0] != f.count[1] || - f.start[0]+f.count[0] >= 288 || - f.flags != VBI_INTERLACED) - return -EINVAL; - - write_lock_irq(&ztv->lock); - ztv->readinfo[0].y = f.start[0]; - ztv->readinfo[0].w = -f.samples_per_line; - ztv->readinfo[0].h = -f.count[0]; - ztv->readinfo[0].bpl = f.samples_per_line*ztv->readinfo[0].bpp; - for (i=1; i<ZORAN_VBI_BUFFERS; i++) - ztv->readinfo[i] = ztv->readinfo[i]; - write_unlock_irq(&ztv->lock); - break; - } - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static struct video_device vbi_template= -{ - .owner = THIS_MODULE, - .name = "UNSET", - .type = VID_TYPE_CAPTURE|VID_TYPE_TELETEXT, - .hardware = VID_HARDWARE_ZR36120, - .open = vbi_open, - .close = vbi_close, - .read = vbi_read, - .write = zoran_write, - .poll = vbi_poll, - .ioctl = vbi_ioctl, - .minor = -1, -}; - -/* - * Scan for a Zoran chip, request the irq and map the io memory - */ -static -int __init find_zoran(void) -{ - int result; - struct zoran *ztv; - struct pci_dev *dev = NULL; - unsigned char revision; - int zoran_num = 0; - - while ((dev = pci_get_device(PCI_VENDOR_ID_ZORAN,PCI_DEVICE_ID_ZORAN_36120, dev))) - { - /* Ok, a ZR36120/ZR36125 found! */ - ztv = &zorans[zoran_num]; - ztv->dev = dev; - - if (pci_enable_device(dev)) - continue; - - pci_read_config_byte(dev, PCI_CLASS_REVISION, &revision); - printk(KERN_INFO "zoran: Zoran %x (rev %d) ", - dev->device, revision); - printk("bus: %d, devfn: %d, irq: %d, ", - dev->bus->number, dev->devfn, dev->irq); - printk("memory: 0x%08lx.\n", ztv->zoran_adr); - - ztv->zoran_mem = ioremap(ztv->zoran_adr, 0x1000); - DEBUG(printk(KERN_DEBUG "zoran: mapped-memory at 0x%p\n",ztv->zoran_mem)); - - result = request_irq(dev->irq, zoran_irq, - IRQF_SHARED|IRQF_DISABLED,"zoran", ztv); - if (result==-EINVAL) - { - iounmap(ztv->zoran_mem); - printk(KERN_ERR "zoran: Bad irq number or handler\n"); - continue; - } - if (result==-EBUSY) - printk(KERN_ERR "zoran: IRQ %d busy, change your PnP config in BIOS\n",dev->irq); - if (result < 0) { - iounmap(ztv->zoran_mem); - continue; - } - /* Enable bus-mastering */ - pci_set_master(dev); - /* Keep a reference */ - pci_dev_get(dev); - zoran_num++; - } - if(zoran_num) - printk(KERN_INFO "zoran: %d Zoran card(s) found.\n",zoran_num); - return zoran_num; -} - -static -int __init init_zoran(int card) -{ - struct zoran *ztv = &zorans[card]; - int i; - - /* if the given cardtype valid? */ - if (cardtype[card]>=NRTVCARDS) { - printk(KERN_INFO "invalid cardtype(%d) detected\n",cardtype[card]); - return -1; - } - - /* reset the zoran */ - zrand(~ZORAN_PCI_SOFTRESET,ZORAN_PCI); - udelay(10); - zror(ZORAN_PCI_SOFTRESET,ZORAN_PCI); - udelay(10); - - /* zoran chip specific details */ - ztv->card = tvcards+cardtype[card]; /* point to the selected card */ - ztv->norm = 0; /* PAL */ - ztv->tuner_freq = 0; - - /* videocard details */ - ztv->swidth = 800; - ztv->sheight = 600; - ztv->depth = 16; - - /* State details */ - ztv->fbuffer = 0; - ztv->overinfo.kindof = FBUFFER_OVERLAY; - ztv->overinfo.status = FBUFFER_FREE; - ztv->overinfo.x = 0; - ztv->overinfo.y = 0; - ztv->overinfo.w = 768; /* 640 */ - ztv->overinfo.h = 576; /* 480 */ - ztv->overinfo.format = VIDEO_PALETTE_RGB565; - ztv->overinfo.bpp = palette2fmt[ztv->overinfo.format].bpp; - ztv->overinfo.bpl = ztv->overinfo.bpp*ztv->swidth; - ztv->overinfo.busadr = 0; - ztv->overinfo.memadr = 0; - ztv->overinfo.overlay = 0; - for (i=0; i<ZORAN_MAX_FBUFFERS; i++) { - ztv->grabinfo[i] = ztv->overinfo; - ztv->grabinfo[i].kindof = FBUFFER_GRAB; - } - init_waitqueue_head(&ztv->grabq); - - /* VBI details */ - ztv->readinfo[0] = ztv->overinfo; - ztv->readinfo[0].kindof = FBUFFER_VBI; - ztv->readinfo[0].w = -864; - ztv->readinfo[0].h = -38; - ztv->readinfo[0].format = VIDEO_PALETTE_YUV422; - ztv->readinfo[0].bpp = palette2fmt[ztv->readinfo[0].format].bpp; - ztv->readinfo[0].bpl = 1024*ztv->readinfo[0].bpp; - for (i=1; i<ZORAN_VBI_BUFFERS; i++) - ztv->readinfo[i] = ztv->readinfo[0]; - init_waitqueue_head(&ztv->vbiq); - - /* maintenance data */ - ztv->have_decoder = 0; - ztv->have_tuner = 0; - ztv->tuner_type = 0; - ztv->running = 0; - ztv->users = 0; - rwlock_init(&ztv->lock); - ztv->workqueue = 0; - ztv->fieldnr = 0; - ztv->lastfieldnr = 0; - - if (triton1) - zrand(~ZORAN_VDC_TRICOM, ZORAN_VDC); - - /* external FL determines TOP frame */ - zror(ZORAN_VFEC_EXTFL, ZORAN_VFEC); - - /* set HSpol */ - if (ztv->card->hsync_pos) - zrwrite(ZORAN_VFEH_HSPOL, ZORAN_VFEH); - /* set VSpol */ - if (ztv->card->vsync_pos) - zrwrite(ZORAN_VFEV_VSPOL, ZORAN_VFEV); - - /* Set the proper General Purpuse register bits */ - /* implicit: no softreset, 0 waitstates */ - zrwrite(ZORAN_PCI_SOFTRESET|(ztv->card->gpdir<<0),ZORAN_PCI); - /* implicit: 3 duration and recovery PCI clocks on guest 0-3 */ - zrwrite(ztv->card->gpval<<24,ZORAN_GUEST); - - /* clear interrupt status */ - zrwrite(~0, ZORAN_ISR); - - /* - * i2c template - */ - ztv->i2c = zoran_i2c_bus_template; - sprintf(ztv->i2c.name,"zoran-%d",card); - ztv->i2c.data = ztv; - - /* - * Now add the template and register the device unit - */ - ztv->video_dev = zr36120_template; - strcpy(ztv->video_dev.name, ztv->i2c.name); - ztv->video_dev.priv = ztv; - if (video_register_device(&ztv->video_dev, VFL_TYPE_GRABBER, video_nr) < 0) - return -1; - - ztv->vbi_dev = vbi_template; - strcpy(ztv->vbi_dev.name, ztv->i2c.name); - ztv->vbi_dev.priv = ztv; - if (video_register_device(&ztv->vbi_dev, VFL_TYPE_VBI, vbi_nr) < 0) { - video_unregister_device(&ztv->video_dev); - return -1; - } - i2c_register_bus(&ztv->i2c); - - /* set interrupt mask - the PIN enable will be set later */ - zrwrite(ZORAN_ICR_GIRQ0|ZORAN_ICR_GIRQ1|ZORAN_ICR_CODE, ZORAN_ICR); - - printk(KERN_INFO "%s: installed %s\n",ztv->i2c.name,ztv->card->name); - return 0; -} - -static -void release_zoran(int max) -{ - struct zoran *ztv; - int i; - - for (i=0;i<max; i++) - { - ztv = &zorans[i]; - - /* turn off all capturing, DMA and IRQs */ - /* reset the zoran */ - zrand(~ZORAN_PCI_SOFTRESET,ZORAN_PCI); - udelay(10); - zror(ZORAN_PCI_SOFTRESET,ZORAN_PCI); - udelay(10); - - /* first disable interrupts before unmapping the memory! */ - zrwrite(0, ZORAN_ICR); - zrwrite(0xffffffffUL,ZORAN_ISR); - - /* free it */ - free_irq(ztv->dev->irq,ztv); - - /* unregister i2c_bus */ - i2c_unregister_bus((&ztv->i2c)); - - /* unmap and free memory */ - if (ztv->zoran_mem) - iounmap(ztv->zoran_mem); - - /* Drop PCI device */ - pci_dev_put(ztv->dev); - - video_unregister_device(&ztv->video_dev); - video_unregister_device(&ztv->vbi_dev); - } -} - -void __exit zr36120_exit(void) -{ - release_zoran(zoran_cards); -} - -int __init zr36120_init(void) -{ - int card; - - handle_chipset(); - zoran_cards = find_zoran(); - if (zoran_cards <= 0) - return -EIO; - - /* initialize Zorans */ - for (card=0; card<zoran_cards; card++) { - if (init_zoran(card) < 0) { - /* only release the zorans we have registered */ - release_zoran(card); - return -EIO; - } - } - return 0; -} - -module_init(zr36120_init); -module_exit(zr36120_exit); diff --git a/linux/drivers/media/video/zr36120.h b/linux/drivers/media/video/zr36120.h deleted file mode 100644 index a71e485b0..000000000 --- a/linux/drivers/media/video/zr36120.h +++ /dev/null @@ -1,279 +0,0 @@ -/* - zr36120.h - Zoran 36120/36125 based framegrabbers - - Copyright (C) 1998-1999 Pauline Middelink (middelin@polyware.nl) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#ifndef _ZR36120_H -#define _ZR36120_H - -#ifdef __KERNEL__ - -#include <linux/types.h> -#include <linux/wait.h> - -#include <linux/i2c-old.h> -#include <linux/videodev.h> - -#include <asm/io.h> - -/* - * Debug macro's, place an x behind the ) for actual debug-compilation - * E.g. #define DEBUG(x...) x - */ -#define DEBUG(x...) /* Debug driver */ -#define IDEBUG(x...) /* Debug interrupt handler */ -#define PDEBUG 0 /* Debug PCI writes */ - -/* defined in zr36120_i2c */ -extern struct i2c_bus zoran_i2c_bus_template; - -#define ZORAN_MAX_FBUFFERS 2 -#define ZORAN_MAX_FBUFFER (768*576*2) -#define ZORAN_MAX_FBUFSIZE (ZORAN_MAX_FBUFFERS*ZORAN_MAX_FBUFFER) - -#define ZORAN_VBI_BUFFERS 2 -#define ZORAN_VBI_BUFSIZE (22*1024*2) - -struct tvcard { - char* name; /* name of the cardtype */ - int video_inputs; /* number of channels defined in video_mux */ - int audio_inputs; /* number of channels defined in audio_mux */ - __u32 swapi2c:1, /* need to swap i2c wires SDA/SCL? */ - usegirq1:1, /* VSYNC at GIRQ1 instead of GIRQ0? */ - vsync_pos:1, /* positive VSYNC signal? */ - hsync_pos:1, /* positive HSYNC signal? */ - gpdir:8, /* General Purpose Direction register */ - gpval:8; /* General Purpose Value register */ - int video_mux[6]; /* mapping channel number to physical input */ -#define IS_TUNER 0x80 -#define IS_SVHS 0x40 -#define CHANNEL_MASK 0x3F - int audio_mux[6]; /* mapping channel number to physical input */ -}; -#define TUNER(x) ((x)|IS_TUNER) -#define SVHS(x) ((x)|IS_SVHS) - -struct vidinfo { - struct vidinfo* next; /* next active buffer */ - uint kindof; -#define FBUFFER_OVERLAY 0 -#define FBUFFER_GRAB 1 -#define FBUFFER_VBI 2 - uint status; -#define FBUFFER_FREE 0 -#define FBUFFER_BUSY 1 -#define FBUFFER_DONE 2 - ulong fieldnr; /* # of field, not framer! */ - uint x,y; - int w,h; /* w,h can be negative! */ - uint format; /* index in palette2fmt[] */ - uint bpp; /* lookup from palette2fmt[] */ - uint bpl; /* calc: width * bpp */ - ulong busadr; /* bus addr for DMA engine */ - char* memadr; /* kernel addr for making copies */ - ulong* overlay; /* kernel addr of overlay mask */ -}; - -struct zoran -{ - struct video_device video_dev; -#define CARD_DEBUG KERN_DEBUG "%s(%lu): " -#define CARD_INFO KERN_INFO "%s(%lu): " -#define CARD_ERR KERN_ERR "%s(%lu): " -#define CARD ztv->video_dev.name,ztv->fieldnr - - /* zoran chip specific details */ - struct i2c_bus i2c; /* i2c registration data */ - struct pci_dev* dev; /* ptr to PCI device */ - ulong zoran_adr; /* bus address of IO memory */ - char* zoran_mem; /* kernel address of IO memory */ - struct tvcard* card; /* the cardtype */ - uint norm; /* 0=PAL, 1=NTSC, 2=SECAM */ - uint tuner_freq; /* Current freq in kHz */ - struct video_picture picture; /* Current picture params */ - - /* videocard details */ - uint swidth; /* screen width */ - uint sheight; /* screen height */ - uint depth; /* depth in bits */ - - /* State details */ - char* fbuffer; /* framebuffers for mmap */ - struct vidinfo overinfo; /* overlay data */ - struct vidinfo grabinfo[ZORAN_MAX_FBUFFERS]; /* grabbing data*/ - wait_queue_head_t grabq; /* grabbers queue */ - - /* VBI details */ - struct video_device vbi_dev; - struct vidinfo readinfo[2]; /* VBI data - flip buffers */ - wait_queue_head_t vbiq; /* vbi queue */ - - /* maintenance data */ - int have_decoder; /* did we detect a mux? */ - int have_tuner; /* did we detect a tuner? */ - int users; /* howmany video/vbi open? */ - int tuner_type; /* tuner type, when found */ - int running; /* are we rolling? */ - rwlock_t lock; - long state; /* what is requested of us? */ -#define STATE_OVERLAY 0 -#define STATE_VBI 1 - struct vidinfo* workqueue; /* buffers to grab, head is active */ - ulong fieldnr; /* #field, ticked every VSYNC */ - ulong lastfieldnr; /* #field, ticked every GRAB */ - - int vidInterlace; /* calculated */ - int vidXshift; /* calculated */ - uint vidWidth; /* calculated */ - uint vidHeight; /* calculated */ -}; - -#define zrwrite(dat,adr) writel((dat),(char *) (ztv->zoran_mem+(adr))) -#define zrread(adr) readl(ztv->zoran_mem+(adr)) - -#if PDEBUG == 0 -#define zrand(dat,adr) zrwrite((dat) & zrread(adr), adr) -#define zror(dat,adr) zrwrite((dat) | zrread(adr), adr) -#define zraor(dat,mask,adr) zrwrite( ((dat)&~(mask)) | ((mask)&zrread(adr)), adr) -#else -#define zrand(dat, adr) \ -do { \ - ulong data = (dat) & zrread((adr)); \ - zrwrite(data, (adr)); \ - if (0 != (~(dat) & zrread((adr)))) \ - printk(KERN_DEBUG "zoran: zrand at %d(%d) detected set bits(%x)\n", __LINE__, (adr), (dat)); \ -} while(0) - -#define zror(dat, adr) \ -do { \ - ulong data = (dat) | zrread((adr)); \ - zrwrite(data, (adr)); \ - if ((dat) != ((dat) & zrread(adr))) \ - printk(KERN_DEBUG "zoran: zror at %d(%d) detected unset bits(%x)\n", __LINE__, (adr), (dat)); \ -} while(0) - -#define zraor(dat, mask, adr) \ -do { \ - ulong data; \ - if ((dat) & (mask)) \ - printk(KERN_DEBUG "zoran: zraor at %d(%d) detected bits(%x:%x)\n", __LINE__, (adr), (dat), (mask)); \ - data = ((dat)&~(mask)) | ((mask) & zrread((adr))); \ - zrwrite(data,(adr)); \ - if ( (dat) != (~(mask) & zrread((adr))) ) \ - printk(KERN_DEBUG "zoran: zraor at %d(%d) could not set all bits(%x:%x)\n", __LINE__, (adr), (dat), (mask)); \ -} while(0) -#endif - -#endif - -/* zoran PCI address space */ -#define ZORAN_VFEH 0x000 /* Video Front End Horizontal Conf. */ -#define ZORAN_VFEH_HSPOL (1<<30) -#define ZORAN_VFEH_HSTART (0x3FF<<10) -#define ZORAN_VFEH_HEND (0x3FF<<0) - -#define ZORAN_VFEV 0x004 /* Video Front End Vertical Conf. */ -#define ZORAN_VFEV_VSPOL (1<<30) -#define ZORAN_VFEV_VSTART (0x3FF<<10) -#define ZORAN_VFEV_VEND (0x3FF<<0) - -#define ZORAN_VFEC 0x008 /* Video Front End Scaler and Pixel */ -#define ZORAN_VFEC_EXTFL (1<<26) -#define ZORAN_VFEC_TOPFIELD (1<<25) -#define ZORAN_VFEC_VCLKPOL (1<<24) -#define ZORAN_VFEC_HFILTER (7<<21) -#define ZORAN_VFEC_HFILTER_1 (0<<21) /* no lumi, 3-tap chromo */ -#define ZORAN_VFEC_HFILTER_2 (1<<21) /* 3-tap lumi, 3-tap chromo */ -#define ZORAN_VFEC_HFILTER_3 (2<<21) /* 4-tap lumi, 4-tap chromo */ -#define ZORAN_VFEC_HFILTER_4 (3<<21) /* 5-tap lumi, 4-tap chromo */ -#define ZORAN_VFEC_HFILTER_5 (4<<21) /* 4-tap lumi, 4-tap chromo */ -#define ZORAN_VFEC_DUPFLD (1<<20) -#define ZORAN_VFEC_HORDCM (63<<14) -#define ZORAN_VFEC_VERDCM (63<<8) -#define ZORAN_VFEC_DISPMOD (1<<6) -#define ZORAN_VFEC_RGB (3<<3) -#define ZORAN_VFEC_RGB_YUV422 (0<<3) -#define ZORAN_VFEC_RGB_RGB888 (1<<3) -#define ZORAN_VFEC_RGB_RGB565 (2<<3) -#define ZORAN_VFEC_RGB_RGB555 (3<<3) -#define ZORAN_VFEC_ERRDIF (1<<2) -#define ZORAN_VFEC_PACK24 (1<<1) -#define ZORAN_VFEC_LE (1<<0) - -#define ZORAN_VTOP 0x00C /* Video Display "Top" */ - -#define ZORAN_VBOT 0x010 /* Video Display "Bottom" */ - -#define ZORAN_VSTR 0x014 /* Video Display Stride */ -#define ZORAN_VSTR_DISPSTRIDE (0xFFFF<<16) -#define ZORAN_VSTR_VIDOVF (1<<8) -#define ZORAN_VSTR_SNAPSHOT (1<<1) -#define ZORAN_VSTR_GRAB (1<<0) - -#define ZORAN_VDC 0x018 /* Video Display Conf. */ -#define ZORAN_VDC_VIDEN (1<<31) -#define ZORAN_VDC_MINPIX (0x1F<<25) -#define ZORAN_VDC_TRICOM (1<<24) -#define ZORAN_VDC_VIDWINHT (0x3FF<<12) -#define ZORAN_VDC_VIDWINWID (0x3FF<<0) - -#define ZORAN_MTOP 0x01C /* Masking Map "Top" */ - -#define ZORAN_MBOT 0x020 /* Masking Map "Bottom" */ - -#define ZORAN_OCR 0x024 /* Overlay Control */ -#define ZORAN_OCR_OVLEN (1<<15) -#define ZORAN_OCR_MASKSTRIDE (0xFF<<0) - -#define ZORAN_PCI 0x028 /* System, PCI and GPP Control */ -#define ZORAN_PCI_SOFTRESET (1<<24) -#define ZORAN_PCI_WAITSTATE (3<<16) -#define ZORAN_PCI_GENPURDIR (0xFF<<0) - -#define ZORAN_GUEST 0x02C /* GuestBus Control */ - -#define ZORAN_CSOURCE 0x030 /* Code Source Address */ - -#define ZORAN_CTRANS 0x034 /* Code Transfer Control */ - -#define ZORAN_CMEM 0x038 /* Code Memory Pointer */ - -#define ZORAN_ISR 0x03C /* Interrupt Status Register */ -#define ZORAN_ISR_CODE (1<<28) -#define ZORAN_ISR_GIRQ0 (1<<29) -#define ZORAN_ISR_GIRQ1 (1<<30) - -#define ZORAN_ICR 0x040 /* Interrupt Control Register */ -#define ZORAN_ICR_EN (1<<24) -#define ZORAN_ICR_CODE (1<<28) -#define ZORAN_ICR_GIRQ0 (1<<29) -#define ZORAN_ICR_GIRQ1 (1<<30) - -#define ZORAN_I2C 0x044 /* I2C-Bus */ -#define ZORAN_I2C_SCL (1<<1) -#define ZORAN_I2C_SDA (1<<0) - -#define ZORAN_POST 0x48 /* PostOffice */ -#define ZORAN_POST_PEN (1<<25) -#define ZORAN_POST_TIME (1<<24) -#define ZORAN_POST_DIR (1<<23) -#define ZORAN_POST_GUESTID (3<<20) -#define ZORAN_POST_GUEST (7<<16) -#define ZORAN_POST_DATA (0xFF<<0) - -#endif |