From 45a9b83fe4cf91b13900dc665f526f7fd94d484c Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Thu, 19 Oct 2006 08:15:40 -0300 Subject: V4L/DVB (4777): Correct AVerMedia Volar USB ID correct AVerMedia Volar USB ID Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 4d6b069..967d102 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -97,7 +97,7 @@ #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_AVERMEDIA_VOLAR 0x1234 +#define USB_PID_AVERMEDIA_VOLAR 0xa807 #define USB_PID_NEBULA_DIGITV 0x0201 #define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820 #define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500 -- cgit v0.10.2 From 902fc997adb7b917e1bd06ad6f2f0d38aa482578 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Wed, 4 Oct 2006 20:33:51 -0300 Subject: V4L/DVB (4722): Cx88: Add support for VIDIOC_INT_[SR]_REGISTER ioctls Add support for the advanced debugging ioctls, to allow access to the cx88 registers from userspace. Only i2c_id == 0 is supported, for access to the cx88 adapter itself. There isn't any support for access to I2C clients of the adapter. Most of them don't have R/W registers anyway, and its necessary to use i2c-dev to talk to them from userspace. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 90e298d..f273121 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1490,6 +1490,30 @@ int cx88_do_ioctl(struct inode *inode, struct file *file, int radio, mutex_unlock(&core->lock); return 0; } +#ifdef CONFIG_VIDEO_ADV_DEBUG + /* ioctls to allow direct acces to the cx2388x registers */ + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != 0) + return -EINVAL; + /* cx2388x has a 24-bit register space */ + reg->val = cx_read(reg->reg&0xffffff); + return 0; + } + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != 0) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx_write(reg->reg&0xffffff, reg->val); + return 0; + } +#endif default: return v4l_compat_translate_ioctl(inode,file,cmd,arg, -- cgit v0.10.2 From 91bb9be6ff4101652bb104f9f083f340e73ba6dd Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sat, 2 Dec 2006 21:15:51 -0200 Subject: V4L/DVB (4524): Initial commit for the DiB7000M-demod Initial commit for the driver for the DiB7000M COFDM demodulator. Signed-off-by: Francois KANOUNNIKOFF Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index a263b3f..a0aeaf4 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -69,6 +69,7 @@ config DVB_USB_DIBUSB_MC config DVB_USB_DIB0700 tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)" depends on DVB_USB + select DVB_DIB7000M select DVB_DIB3000MC select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE help diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index e473bfe..b4e339d 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -9,6 +9,7 @@ #include "dib0700.h" #include "dib3000mc.h" +#include "dib7000m.h" #include "mt2060.h" static int force_lna_activation; diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c new file mode 100644 index 0000000..c9a5767 --- /dev/null +++ b/drivers/media/dvb/frontends/dib7000m.c @@ -0,0 +1,242 @@ +/* + * Linux-DVB Driver for DiBcom's DiB7000M and + * first generation DiB7000P-demodulator-family. + * + * Copyright (C) 2005-6 DiBcom (http://www.dibcom.fr/) + * + * 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. + */ +#include +#include + +#include "dvb_frontend.h" + +#include "dib7000m.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); } } while (0) + +struct dib7000m_state { + struct dvb_frontend demod; + struct dib7000m_config cfg; + + u8 i2c_addr; + struct i2c_adapter *i2c_adap; + + struct dibx000_i2c_master i2c_master; + +/* offset is 1 in case of the 7000MC */ + u8 reg_offs; + + u16 wbd_ref; + + u8 current_band; + fe_bandwidth_t current_bandwidth; + struct dibx000_agc_config *current_agc; + u32 timf[9]; + + u16 revision; +}; + +static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg) +{ + u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff }; + u8 rb[2]; + struct i2c_msg msg[2] = { + { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 }, + { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 }, + }; + + if (i2c_transfer(state->i2c_adap, msg, 2) != 2) + dprintk("i2c read error on %d\n",reg); + + return (rb[0] << 8) | rb[1]; +} + +/* +static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val) +{ + u8 b[4] = { + (reg >> 8) & 0xff, reg & 0xff, + (val >> 8) & 0xff, val & 0xff, + }; + struct i2c_msg msg = { + .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4 + }; + return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +} +*/ + +static int dib7000m_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dib7000m_state *state = fe->demodulator_priv; + u16 tps = dib7000m_read_word(state,480); + + fep->inversion = INVERSION_AUTO; + + fep->u.ofdm.bandwidth = state->current_bandwidth; + + switch ((tps >> 8) & 0x2) { + case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break; + case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break; + /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */ + } + + switch (tps & 0x3) { + case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break; + case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break; + case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break; + case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break; + } + + switch ((tps >> 14) & 0x3) { + case 0: fep->u.ofdm.constellation = QPSK; break; + case 1: fep->u.ofdm.constellation = QAM_16; break; + case 2: + default: fep->u.ofdm.constellation = QAM_64; break; + } + + /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ + /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */ + + fep->u.ofdm.hierarchy_information = HIERARCHY_NONE; + switch ((tps >> 5) & 0x7) { + case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break; + case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break; + case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break; + case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break; + case 7: + default: fep->u.ofdm.code_rate_HP = FEC_7_8; break; + + } + + switch ((tps >> 2) & 0x7) { + case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break; + case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break; + case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break; + case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break; + case 7: + default: fep->u.ofdm.code_rate_LP = FEC_7_8; break; + } + + /* native interleaver: (dib7000m_read_word(state, 481) >> 5) & 0x1 */ + + return 0; +} + +static int dib7000m_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + return 0; +} + +static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat) +{ + struct dib7000m_state *state = fe->demodulator_priv; + u16 lock = dib7000m_read_word(state, 509); + + *stat = 0; + + if (lock & 0x8000) + *stat |= FE_HAS_SIGNAL; + if (lock & 0x3000) + *stat |= FE_HAS_CARRIER; + if (lock & 0x0100) + *stat |= FE_HAS_VITERBI; + if (lock & 0x0010) + *stat |= FE_HAS_SYNC; + if (lock & 0x0008) + *stat |= FE_HAS_LOCK; + + return 0; +} + +static int dib7000m_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct dib7000m_state *state = fe->demodulator_priv; + *ber = (dib7000m_read_word(state, 526) << 16) | dib7000m_read_word(state, 527); + return 0; +} + +static int dib7000m_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +{ + struct dib7000m_state *state = fe->demodulator_priv; + *unc = dib7000m_read_word(state, 534); + return 0; +} + +static int dib7000m_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dib7000m_state *state = fe->demodulator_priv; + u16 val = dib7000m_read_word(state, 390); + *strength = 65535 - val; + return 0; +} + +static int dib7000m_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + *snr = 0x0000; + return 0; +} + +static int dib7000m_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static int dib7000m_init(struct dvb_frontend *fe) +{ + return 0; +} + +static int dib7000m_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static void dib7000m_release(struct dvb_frontend *fe) +{ } + +static struct dvb_frontend_ops dib7000m_ops = { + .info = { + .name = "DiBcom 7000MA/MB/PA/PB/MC", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib7000m_release, + + .init = dib7000m_init, + .sleep = dib7000m_sleep, + + .set_frontend = dib7000m_set_frontend, + .get_tune_settings = dib7000m_fe_get_tune_settings, + .get_frontend = dib7000m_get_frontend, + + .read_status = dib7000m_read_status, + .read_ber = dib7000m_read_ber, + .read_signal_strength = dib7000m_read_signal_strength, + .read_snr = dib7000m_read_snr, + .read_ucblocks = dib7000m_read_unc_blocks, +}; + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for the DiBcom 7000MA/MB/PA/PB/MC COFDM demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/dib7000m.h b/drivers/media/dvb/frontends/dib7000m.h new file mode 100644 index 0000000..45990e3 --- /dev/null +++ b/drivers/media/dvb/frontends/dib7000m.h @@ -0,0 +1,50 @@ +#ifndef DIB7000M_H +#define DIB7000M_H + +#include "dibx000_common.h" + +struct dib7000m_config { + u8 dvbt_mode; + u8 output_mpeg2_in_188_bytes; + u8 hostbus_diversity; + u8 tuner_is_baseband; + u8 mobile_mode; + int (*update_lna) (struct dvb_frontend *, u16 agc_global); + + u8 agc_config_count; + struct dibx000_agc_config *agc; + + struct dibx000_bandwidth_config *bw; + +#define DIB7000M_GPIO_DEFAULT_DIRECTIONS 0xffff + u16 gpio_dir; +#define DIB7000M_GPIO_DEFAULT_VALUES 0x0000 + u16 gpio_val; +#define DIB7000M_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +#define DIB7000M_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +#define DIB7000M_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +#define DIB7000M_GPIO_PWM_POS3(v) (v & 0xf) +#define DIB7000M_GPIO_DEFAULT_PWM_POS 0xffff + u16 gpio_pwm_pos; + + u16 pwm_freq_div; + + u8 quartz_direct; + + u8 input_clk_is_div_2; +}; + +#define DEFAULT_DIB7000M_I2C_ADDRESS 18 + +extern int dib7000m_attach(struct i2c_adapter *i2c_adap, int no_of_demods, u8 default_addr , u8 do_i2c_enum, struct dib7000m_config[], struct dvb_frontend*[]); + +extern struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); + +/* TODO +extern INT dib7000m_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val); +extern INT dib7000m_enable_vbg_voltage(struct dibDemod *demod); +extern void dib7000m_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff); +extern USHORT dib7000m_get_current_agc_global(struct dibDemod *demod); +*/ + +#endif -- cgit v0.10.2 From 6c5be74c86f102c2d4e123bc51d2fa93155fd794 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Sat, 2 Dec 2006 21:15:51 -0200 Subject: V4L/DVB (4676): Dynamic cx88 mpeg port management for HVR1300 MPEG2/DVB-T support. A series of patches to change the cx88 framework to allow the PCI mpeg port to be shared dynamically between different types of drivers or applications. This patch changes the cx88-dvb and cx88-blackbird drivers to become 'sub drivers' of a higher single cx88-mpeg driver. The cx88-mpeg driver is a superset of the previous cx88-mpeg/blackbird drivers and now owns the IRQ. cx88-dvb/blackbird now become mini drivers, registering themselves with cx88-mpeg through a standard interface with callbacks. Sub drivers request access to hardware via the cx88-mpeg driver. In turn the cx88-mpeg driver determines whether the hardware is busy and accepts or refuses the request, grant access using callbacks into the sub drivers. The net effect is that you are no longer able to tamper with the mpeg port from multiple different applications at the same time, potentially breaking a live mpeg2 hardware encoding or dvb stream. The mechanism extends to enable multiple dvb frontends to be registered and share the single resource. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 4673832..0037188 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -50,7 +50,6 @@ MODULE_PARM_DESC(debug,"enable debug messages [blackbird]"); #define dprintk(level,fmt, arg...) if (debug >= level) \ printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg) -static LIST_HEAD(cx8802_devlist); /* ------------------------------------------------------------------ */ @@ -882,7 +881,7 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file, BLACKBIRD_MPEG_CAPTURE, BLACKBIRD_RAW_BITS_NONE); - cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl); + cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook); blackbird_initialize_codec(dev); cx88_set_scale(dev->core, dev->width, dev->height, @@ -914,11 +913,15 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file, } default: - return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl); + return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook); } return 0; } +int (*cx88_ioctl_hook)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg); +unsigned int (*cx88_ioctl_translator)(unsigned int cmd); + static unsigned int mpeg_translate_ioctl(unsigned int cmd) { return cmd; @@ -927,33 +930,48 @@ static unsigned int mpeg_translate_ioctl(unsigned int cmd) static int mpeg_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - cmd = mpeg_translate_ioctl( cmd ); - return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl); + cmd = cx88_ioctl_translator( cmd ); + return video_usercopy(inode, file, cmd, arg, cx88_ioctl_hook); } static int mpeg_open(struct inode *inode, struct file *file) { int minor = iminor(inode); - struct cx8802_dev *h,*dev = NULL; + struct cx8802_dev *dev = NULL; struct cx8802_fh *fh; - struct list_head *list; + struct cx8802_driver *drv = NULL; + int err; - list_for_each(list,&cx8802_devlist) { - h = list_entry(list, struct cx8802_dev, devlist); - if (h->mpeg_dev->minor == minor) - dev = h; - } - if (NULL == dev) + dprintk( 1, "%s\n", __FUNCTION__); + + dev = cx8802_get_device(inode); + if (dev == NULL) return -ENODEV; - if (blackbird_initialize_codec(dev) < 0) + /* Make sure we can acquire the hardware */ + drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); + if (drv) { + err = drv->request_acquire(drv); + if(err != 0) { + dprintk(1,"%s: Unable to acquire hardware, %d\n", __FUNCTION__, err); + return err; + } + } + + if (blackbird_initialize_codec(dev) < 0) { + if (drv) + drv->request_release(drv); return -EINVAL; + } dprintk(1,"open minor=%d\n",minor); /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh),GFP_KERNEL); - if (NULL == fh) + if (NULL == fh) { + if (drv) + drv->request_release(drv); return -ENOMEM; + } file->private_data = fh; fh->dev = dev; @@ -974,6 +992,8 @@ static int mpeg_open(struct inode *inode, struct file *file) static int mpeg_release(struct inode *inode, struct file *file) { struct cx8802_fh *fh = file->private_data; + struct cx8802_dev *dev = NULL; + struct cx8802_driver *drv = NULL; /* blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, BLACKBIRD_END_NOW, 0, 0x13); */ blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, @@ -992,6 +1012,16 @@ static int mpeg_release(struct inode *inode, struct file *file) videobuf_mmap_free(&fh->mpegq); file->private_data = NULL; kfree(fh); + + /* Make sure we release the hardware */ + dev = cx8802_get_device(inode); + if (dev == NULL) + return -ENODEV; + + drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); + if (drv) + drv->request_release(drv); + return 0; } @@ -1043,6 +1073,44 @@ static struct video_device cx8802_mpeg_template = /* ------------------------------------------------------------------ */ +/* The CX8802 MPEG API will call this when we can use the hardware */ +static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + int err = 0; + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* By default, core setup will leave the cx22702 out of reset, on the bus. + * We left the hardware on power up with the cx22702 active. + * We're being given access to re-arrange the GPIOs. + * Take the bus off the cx22702 and put the cx23416 on it. + */ + cx_clear(MO_GP0_IO, 0x00000080); /* cx22702 in reset */ + cx_set(MO_GP0_IO, 0x00000004); /* Disable the cx22702 */ + break; + default: + err = -ENODEV; + } + return err; +} + +/* The CX8802 MPEG API will call this when we need to release the hardware */ +static int cx8802_blackbird_advise_release(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + int err = 0; + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* Exit leaving the cx23416 on the bus */ + break; + default: + err = -ENODEV; + } + return err; +} + static void blackbird_unregister_video(struct cx8802_dev *dev) { if (dev->mpeg_dev) { @@ -1073,28 +1141,23 @@ static int blackbird_register_video(struct cx8802_dev *dev) /* ----------------------------------------------------------- */ -static int __devinit blackbird_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pci_id) +static int cx8802_blackbird_probe(struct cx8802_driver *drv) { - struct cx8802_dev *dev; - struct cx88_core *core; + struct cx88_core *core = drv->core; + struct cx8802_dev *dev = core->dvbdev; int err; - /* general setup */ - core = cx88_core_get(pci_dev); - if (NULL == core) - return -EINVAL; + dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", + core->board, + core->name, + core->pci_bus, + core->pci_slot); err = -ENODEV; if (!(cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD)) goto fail_core; - err = -ENOMEM; - dev = kzalloc(sizeof(*dev),GFP_KERNEL); - if (NULL == dev) - goto fail_core; - dev->pci = pci_dev; - dev->core = core; dev->width = 720; dev->height = 576; cx2341x_fill_defaults(&dev->params); @@ -1106,64 +1169,36 @@ static int __devinit blackbird_probe(struct pci_dev *pci_dev, dev->height = 576; } - err = cx8802_init_common(dev); - if (0 != err) - goto fail_free; - /* blackbird stuff */ printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n", core->name); host_setup(dev->core); - list_add_tail(&dev->devlist,&cx8802_devlist); blackbird_register_video(dev); /* initial device configuration: needed ? */ return 0; - fail_free: - kfree(dev); fail_core: - cx88_core_put(core,pci_dev); return err; } -static void __devexit blackbird_remove(struct pci_dev *pci_dev) +static int cx8802_blackbird_remove(struct cx8802_driver *drv) { - struct cx8802_dev *dev = pci_get_drvdata(pci_dev); - /* blackbird */ - blackbird_unregister_video(dev); - list_del(&dev->devlist); + blackbird_unregister_video(drv->core->dvbdev); - /* common */ - cx8802_fini_common(dev); - cx88_core_put(dev->core,dev->pci); - kfree(dev); + return 0; } -static struct pci_device_id cx8802_pci_tbl[] = { - { - .vendor = 0x14f1, - .device = 0x8802, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - },{ - /* --- end of list --- */ - } -}; -MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); - -static struct pci_driver blackbird_pci_driver = { - .name = "cx88-blackbird", - .id_table = cx8802_pci_tbl, - .probe = blackbird_probe, - .remove = __devexit_p(blackbird_remove), -#ifdef CONFIG_PM - .suspend = cx8802_suspend_common, - .resume = cx8802_resume_common, -#endif +static struct cx8802_driver cx8802_blackbird_driver = { + .type_id = CX88_MPEG_BLACKBIRD, + .hw_access = CX8802_DRVCTL_SHARED, + .probe = cx8802_blackbird_probe, + .remove = cx8802_blackbird_remove, + .advise_acquire = cx8802_blackbird_advise_acquire, + .advise_release = cx8802_blackbird_advise_release, }; static int blackbird_init(void) @@ -1176,17 +1211,22 @@ static int blackbird_init(void) printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); #endif - return pci_register_driver(&blackbird_pci_driver); + cx88_ioctl_hook = mpeg_do_ioctl; + cx88_ioctl_translator = mpeg_translate_ioctl; + return cx8802_register_driver(&cx8802_blackbird_driver); } static void blackbird_fini(void) { - pci_unregister_driver(&blackbird_pci_driver); + cx8802_unregister_driver(&cx8802_blackbird_driver); } module_init(blackbird_init); module_exit(blackbird_fini); +EXPORT_SYMBOL(cx88_ioctl_hook); +EXPORT_SYMBOL(cx88_ioctl_translator); + /* ----------------------------------------------------------- */ /* * Local variables: diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index f764a57..1c5db20 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1303,7 +1303,7 @@ struct cx88_board cx88_boards[] = { .gpio0 = 0xe780, }}, /* fixme: Add radio support */ - .mpeg = CX88_MPEG_DVB, + .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, }, }; const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 0ef13e7..8150c09 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -57,7 +57,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"enable debug messages [dvb]"); #define dprintk(level,fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->core->name , ## arg) + printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg) /* ------------------------------------------------------------------ */ @@ -315,20 +315,36 @@ 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_hvr1300_config = { +static struct cx22702_config hauppauge_hvr3000_config = { .demod_address = 0x63, .output_mode = CX22702_SERIAL_OUTPUT, }; -static struct cx22702_config hauppauge_hvr3000_config = { +static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, + int acquire) +{ + struct cx8802_dev *dev= fe->dvb->priv; + struct cx8802_driver *drv = NULL; + int ret = 0; + + drv = cx8802_get_driver(dev, CX88_MPEG_DVB); + if (drv) { + if(acquire) + ret = drv->request_acquire(drv); + else + ret = drv->request_release(drv); + } + + return ret; +} + +static struct cx22702_config hauppauge_hvr1300_config = { .demod_address = 0x63, - .output_mode = CX22702_SERIAL_OUTPUT, + .output_mode = CX22702_SERIAL_OUTPUT, }; static int or51132_set_ts_param(struct dvb_frontend* fe, @@ -555,26 +571,6 @@ static int dvb_register(struct cx8802_dev *dev) &dvb_pll_fmd1216me); } break; - case CX88_BOARD_HAUPPAUGE_HVR1300: - dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr1300_config, - &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_fmd1216me); - } - break; - case CX88_BOARD_HAUPPAUGE_HVR3000: - dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr3000_config, - &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_fmd1216me); - } - break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: dev->dvb.frontend = dvb_attach(mt352_attach, &dvico_fusionhdtv, @@ -782,6 +778,26 @@ static int dvb_register(struct cx8802_dev *dev) dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage; } break; + case CX88_BOARD_HAUPPAUGE_HVR1300: + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr1300_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_fmd1216me); + } + break; + case CX88_BOARD_HAUPPAUGE_HVR3000: + dev->dvb.frontend = dvb_attach(cx22702_attach, + &hauppauge_hvr3000_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_fmd1216me); + } + break; default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->core->name); @@ -796,6 +812,8 @@ static int dvb_register(struct cx8802_dev *dev) dev->dvb.frontend->ops.info.frequency_min = dev->core->pll_desc->min; dev->dvb.frontend->ops.info.frequency_max = dev->core->pll_desc->max; } + /* Ensure all frontends negotiate bus access */ + dev->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; /* Put the analog decoder in standby to keep it quiet */ cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); @@ -806,37 +824,67 @@ static int dvb_register(struct cx8802_dev *dev) /* ----------------------------------------------------------- */ -static int __devinit dvb_probe(struct pci_dev *pci_dev, - const struct pci_device_id *pci_id) +/* CX8802 MPEG -> mini driver - We have been given the hardware */ +static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv) { - struct cx8802_dev *dev; - struct cx88_core *core; + struct cx88_core *core = drv->core; + int err = 0; + dprintk( 1, "%s\n", __FUNCTION__); + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* We arrive here with either the cx23416 or the cx22702 + * on the bus. Take the bus from the cx23416 and enable the + * cx22702 demod + */ + cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset and enable */ + cx_clear(MO_GP0_IO, 0x00000004); + udelay(1000); + break; + default: + err = -ENODEV; + } + return err; +} + +/* CX8802 MPEG -> mini driver - We no longer have the hardware */ +static int cx8802_dvb_advise_release(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + int err = 0; + dprintk( 1, "%s\n", __FUNCTION__); + + switch (core->board) { + case CX88_BOARD_HAUPPAUGE_HVR1300: + /* Do Nothing, leave the cx22702 on the bus. */ + break; + default: + err = -ENODEV; + } + return err; +} + +static int cx8802_dvb_probe(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + struct cx8802_dev *dev = drv->core->dvbdev; int err; - /* general setup */ - core = cx88_core_get(pci_dev); - if (NULL == core) - return -EINVAL; + dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", + core->board, + core->name, + core->pci_bus, + core->pci_slot); err = -ENODEV; if (!(cx88_boards[core->board].mpeg & CX88_MPEG_DVB)) goto fail_core; - err = -ENOMEM; - dev = kzalloc(sizeof(*dev),GFP_KERNEL); - if (NULL == dev) - goto fail_core; - dev->pci = pci_dev; - dev->core = core; - - err = cx8802_init_common(dev); - if (0 != err) - goto fail_free; - #ifdef HAVE_VP3054_I2C err = vp3054_i2c_probe(dev); if (0 != err) - goto fail_free; + goto fail_core; #endif /* dvb stuff */ @@ -848,28 +896,16 @@ static int __devinit dvb_probe(struct pci_dev *pci_dev, sizeof(struct cx88_buffer), dev); err = dvb_register(dev); - if (0 != err) - goto fail_fini; - - /* Maintain a reference to cx88-video can query the 8802 device. */ - core->dvbdev = dev; - return 0; + if (err != 0) + printk("%s dvb_register failed err = %d\n", __FUNCTION__, err); - fail_fini: - cx8802_fini_common(dev); - fail_free: - kfree(dev); fail_core: - cx88_core_put(core,pci_dev); return err; } -static void __devexit dvb_remove(struct pci_dev *pci_dev) +static int cx8802_dvb_remove(struct cx8802_driver *drv) { - struct cx8802_dev *dev = pci_get_drvdata(pci_dev); - - /* Destroy any 8802 reference. */ - dev->core->dvbdev = NULL; + struct cx8802_dev *dev = drv->core->dvbdev; /* dvb */ videobuf_dvb_unregister(&dev->dvb); @@ -878,33 +914,16 @@ static void __devexit dvb_remove(struct pci_dev *pci_dev) vp3054_i2c_remove(dev); #endif - /* common */ - cx8802_fini_common(dev); - cx88_core_put(dev->core,dev->pci); - kfree(dev); + return 0; } -static struct pci_device_id cx8802_pci_tbl[] = { - { - .vendor = 0x14f1, - .device = 0x8802, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - },{ - /* --- end of list --- */ - } -}; -MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); - -static struct pci_driver dvb_pci_driver = { - .name = "cx88-dvb", - .id_table = cx8802_pci_tbl, - .probe = dvb_probe, - .remove = __devexit_p(dvb_remove), -#ifdef CONFIG_PM - .suspend = cx8802_suspend_common, - .resume = cx8802_resume_common, -#endif +static struct cx8802_driver cx8802_dvb_driver = { + .type_id = CX88_MPEG_DVB, + .hw_access = CX8802_DRVCTL_SHARED, + .probe = cx8802_dvb_probe, + .remove = cx8802_dvb_remove, + .advise_acquire = cx8802_dvb_advise_acquire, + .advise_release = cx8802_dvb_advise_release, }; static int dvb_init(void) @@ -917,12 +936,12 @@ static int dvb_init(void) printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); #endif - return pci_register_driver(&dvb_pci_driver); + return cx8802_register_driver(&cx8802_dvb_driver); } static void dvb_fini(void) { - pci_unregister_driver(&dvb_pci_driver); + cx8802_unregister_driver(&cx8802_dvb_driver); } module_init(dvb_init); diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 6b23a4e..6f15571 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -44,8 +44,12 @@ module_param(debug,int,0644); MODULE_PARM_DESC(debug,"enable debug messages [mpeg]"); #define dprintk(level,fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG "%s/2: " fmt, dev->core->name , ## arg) + printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg) +#define mpeg_dbg(level,fmt, arg...) if (debug >= level) \ + printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg) + +static LIST_HEAD(cx8802_devlist); /* ------------------------------------------------------------------ */ static int cx8802_start_dma(struct cx8802_dev *dev, @@ -65,17 +69,13 @@ static int cx8802_start_dma(struct cx8802_dev *dev, /* FIXME: this needs a review. * also: move to cx88-blackbird + cx88-dvb source files? */ - if (cx88_boards[core->board].mpeg == (CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD) ) { - /* Report a warning until the mini driver patch is applied, - * else the following conditions will set the dma registers incorrectly. - * This will be removed in the next major patch and changes to the conditions - * will be made. - */ - printk(KERN_INFO "%s() board->(CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD) is invalid\n", __FUNCTION__); - return -EINVAL; - } - if (cx88_boards[core->board].mpeg & CX88_MPEG_DVB) { + dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id); + + if ( (core->active_type_id == CX88_MPEG_DVB) && + (cx88_boards[core->board].mpeg & CX88_MPEG_DVB) ) { + + dprintk( 1, "cx8802_start_dma doing .dvb\n"); /* negedge driven & software reset */ cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl); udelay(100); @@ -93,15 +93,17 @@ static int cx8802_start_dma(struct cx8802_dev *dev, cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */ udelay(100); break; + case CX88_BOARD_HAUPPAUGE_HVR1300: + break; default: cx_write(TS_SOP_STAT, 0x00); break; } cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl); udelay(100); - } - - if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) { + } else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) && + (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) ) { + dprintk( 1, "cx8802_start_dma doing .blackbird\n"); cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */ cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */ @@ -112,6 +114,10 @@ static int cx8802_start_dma(struct cx8802_dev *dev, cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */ udelay(100); + } else { + printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __FUNCTION__, + cx88_boards[core->board].mpeg ); + return -EINVAL; } /* reset counter */ @@ -542,8 +548,311 @@ int cx8802_resume_common(struct pci_dev *pci_dev) return 0; } +struct cx8802_dev * cx8802_get_device(struct inode *inode) +{ + int minor = iminor(inode); + struct cx8802_dev *h = NULL; + struct list_head *list; + + list_for_each(list,&cx8802_devlist) { + h = list_entry(list, struct cx8802_dev, devlist); + if (h->mpeg_dev->minor == minor) + return h; + } + + return NULL; +} + +struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype) +{ + struct cx8802_dev *h = NULL; + struct cx8802_driver *d = NULL; + struct list_head *list; + struct list_head *list2; + + list_for_each(list,&cx8802_devlist) { + h = list_entry(list, struct cx8802_dev, devlist); + + list_for_each(list2, &h->drvlist.devlist) { + d = list_entry(list2, struct cx8802_driver, devlist); + + /* only unregister the correct driver type */ + if (d->type_id == btype) { + return d; + } + } + } + + return NULL; +} + +/* Driver asked for hardware access. */ +int cx8802_request_acquire(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + + /* Fail a request for hardware if the device is busy. */ + if (core->active_type_id != CX88_BOARD_NONE) + return -EBUSY; + + if (drv->advise_acquire) + { + core->active_type_id = drv->type_id; + drv->advise_acquire(drv); + + mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO)); + } + + return 0; +} + +/* Driver asked to release hardware. */ +int cx8802_request_release(struct cx8802_driver *drv) +{ + struct cx88_core *core = drv->core; + + if (drv->advise_release) + { + drv->advise_release(drv); + core->active_type_id = CX88_BOARD_NONE; + mpeg_dbg(1,"%s() Post release GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO)); + } + + return 0; +} + +static int cx8802_check_driver(struct cx8802_driver *drv) +{ + if (drv == NULL) + return -ENODEV; + + if ((drv->type_id != CX88_MPEG_DVB) && + (drv->type_id != CX88_MPEG_BLACKBIRD)) + return -EINVAL; + + if ((drv->hw_access != CX8802_DRVCTL_SHARED) && + (drv->hw_access != CX8802_DRVCTL_EXCLUSIVE)) + return -EINVAL; + + if ((drv->probe == NULL) || + (drv->remove == NULL) || + (drv->advise_acquire == NULL) || + (drv->advise_release == NULL)) + return -EINVAL; + + return 0; +} + +int cx8802_register_driver(struct cx8802_driver *drv) +{ + struct cx8802_dev *h; + struct cx8802_driver *driver; + struct list_head *list; + int err = 0, i = 0; + + printk(KERN_INFO "%s() ->registering driver type=%s access=%s\n", __FUNCTION__ , + drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", + drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive"); + + if ((err = cx8802_check_driver(drv)) != 0) { + printk(KERN_INFO "%s() cx8802_driver is invalid\n", __FUNCTION__ ); + return err; + } + + list_for_each(list,&cx8802_devlist) { + i++; + h = list_entry(list, struct cx8802_dev, devlist); + + printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d]\n", + h->core->name,h->pci->subsystem_vendor, + h->pci->subsystem_device,cx88_boards[h->core->board].name, + h->core->board); + + /* Bring up a new struct for each driver instance */ + driver = kzalloc(sizeof(*drv),GFP_KERNEL); + if (driver == NULL) + return -ENOMEM; + + /* Snapshot of the driver registration data */ + drv->core = h->core; + drv->suspend = cx8802_suspend_common; + drv->resume = cx8802_resume_common; + drv->request_acquire = cx8802_request_acquire; + drv->request_release = cx8802_request_release; + memcpy(driver, drv, sizeof(*driver)); + + err = drv->probe(driver); + if (err == 0) { + mutex_lock(&drv->core->lock); + list_add_tail(&driver->devlist,&h->drvlist.devlist); + mutex_unlock(&drv->core->lock); + } else { + printk(KERN_ERR "%s() ->probe failed err = %d\n", __FUNCTION__, err); + } + + } + if (i == 0) + err = -ENODEV; + + return err; +} + +int cx8802_unregister_driver(struct cx8802_driver *drv) +{ + struct cx8802_dev *h; + struct cx8802_driver *d; + struct list_head *list; + struct list_head *list2, *q; + int err = 0, i = 0; + + printk(KERN_INFO "%s() ->unregistering driver type=%s\n", __FUNCTION__ , + drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird"); + + list_for_each(list,&cx8802_devlist) { + i++; + h = list_entry(list, struct cx8802_dev, devlist); + + printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d]\n", + h->core->name,h->pci->subsystem_vendor, + h->pci->subsystem_device,cx88_boards[h->core->board].name, + h->core->board); + + list_for_each_safe(list2, q, &h->drvlist.devlist) { + d = list_entry(list2, struct cx8802_driver, devlist); + + /* only unregister the correct driver type */ + if (d->type_id != drv->type_id) + continue; + + err = d->remove(d); + if (err == 0) { + mutex_lock(&drv->core->lock); + list_del(list2); + mutex_unlock(&drv->core->lock); + } else + printk(KERN_ERR "%s() ->remove failed err = %d\n", __FUNCTION__, err); + + } + + } + + return err; +} + /* ----------------------------------------------------------- */ +static int __devinit cx8802_probe(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct cx8802_dev *dev; + struct cx88_core *core; + int err; + + /* general setup */ + core = cx88_core_get(pci_dev); + if (NULL == core) + return -EINVAL; + + printk("%s/2: cx2388x 8802 Driver Manager\n", core->name); + + err = -ENODEV; + if (!cx88_boards[core->board].mpeg) + goto fail_core; + + err = -ENOMEM; + dev = kzalloc(sizeof(*dev),GFP_KERNEL); + if (NULL == dev) + goto fail_core; + dev->pci = pci_dev; + dev->core = core; + + err = cx8802_init_common(dev); + if (err != 0) + goto fail_free; + + INIT_LIST_HEAD(&dev->drvlist.devlist); + list_add_tail(&dev->devlist,&cx8802_devlist); + /* Maintain a reference so cx88-video can query the 8802 device. */ + core->dvbdev = dev; + return 0; + + fail_free: + kfree(dev); + fail_core: + cx88_core_put(core,pci_dev); + return err; +} + +static void __devexit cx8802_remove(struct pci_dev *pci_dev) +{ + struct cx8802_dev *dev; + struct cx8802_driver *h; + struct list_head *list; + + dev = pci_get_drvdata(pci_dev); + + dprintk( 1, "%s\n", __FUNCTION__); + + list_for_each(list,&dev->drvlist.devlist) { + h = list_entry(list, struct cx8802_driver, devlist); + dprintk( 1, " ->driver\n"); + if (h->remove == NULL) { + printk(KERN_ERR "%s .. skipping driver, no probe function\n", __FUNCTION__); + continue; + } + printk(KERN_INFO "%s .. Removing driver type %d\n", __FUNCTION__, h->type_id); + cx8802_unregister_driver(h); + list_del(&dev->drvlist.devlist); + } + + /* Destroy any 8802 reference. */ + dev->core->dvbdev = NULL; + + /* common */ + cx8802_fini_common(dev); + cx88_core_put(dev->core,dev->pci); + kfree(dev); +} + +static struct pci_device_id cx8802_pci_tbl[] = { + { + .vendor = 0x14f1, + .device = 0x8802, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + },{ + /* --- end of list --- */ + } +}; +MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); + +static struct pci_driver cx8802_pci_driver = { + .name = "cx88-mpeg driver manager", + .id_table = cx8802_pci_tbl, + .probe = cx8802_probe, + .remove = __devexit_p(cx8802_remove), +}; + +static int cx8802_init(void) +{ + printk(KERN_INFO "cx2388x cx88-mpeg Driver Manager version %d.%d.%d loaded\n", + (CX88_VERSION_CODE >> 16) & 0xff, + (CX88_VERSION_CODE >> 8) & 0xff, + CX88_VERSION_CODE & 0xff); +#ifdef SNAPSHOT + printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", + SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); +#endif + return pci_register_driver(&cx8802_pci_driver); +} + +static void cx8802_fini(void) +{ + pci_unregister_driver(&cx8802_pci_driver); +} + +module_init(cx8802_init); +module_exit(cx8802_fini); EXPORT_SYMBOL(cx8802_buf_prepare); EXPORT_SYMBOL(cx8802_buf_queue); EXPORT_SYMBOL(cx8802_cancel_buffers); @@ -551,9 +860,10 @@ EXPORT_SYMBOL(cx8802_cancel_buffers); EXPORT_SYMBOL(cx8802_init_common); EXPORT_SYMBOL(cx8802_fini_common); -EXPORT_SYMBOL(cx8802_suspend_common); -EXPORT_SYMBOL(cx8802_resume_common); - +EXPORT_SYMBOL(cx8802_register_driver); +EXPORT_SYMBOL(cx8802_unregister_driver); +EXPORT_SYMBOL(cx8802_get_device); +EXPORT_SYMBOL(cx8802_get_driver); /* ----------------------------------------------------------- */ /* * Local variables: diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 3bc91aa..5980e47 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -74,6 +74,11 @@ enum cx88_board_type { CX88_MPEG_BLACKBIRD }; +enum cx8802_board_access { + CX8802_DRVCTL_SHARED = 1, + CX8802_DRVCTL_EXCLUSIVE = 2, +}; + /* ----------------------------------------------------------- */ /* tv norms */ @@ -330,6 +335,7 @@ struct cx88_core { /* cx88-video needs to access cx8802 for hybrid tuner pll access. */ struct cx8802_dev *dvbdev; + enum cx88_board_type active_type_id; }; struct cx8800_dev; @@ -405,6 +411,31 @@ struct cx8802_suspend_state { int disabled; }; +struct cx8802_driver { + struct cx88_core *core; + struct list_head devlist; + + /* Type of driver and access required */ + enum cx88_board_type type_id; + enum cx8802_board_access hw_access; + + /* MPEG 8802 internal only */ + int (*suspend)(struct pci_dev *pci_dev, pm_message_t state); + int (*resume)(struct pci_dev *pci_dev); + + /* MPEG 8802 -> mini driver - Driver probe and configuration */ + int (*probe)(struct cx8802_driver *drv); + int (*remove)(struct cx8802_driver *drv); + + /* MPEG 8802 -> mini driver - Access for hardware control */ + int (*advise_acquire)(struct cx8802_driver *drv); + int (*advise_release)(struct cx8802_driver *drv); + + /* MPEG 8802 <- mini driver - Access for hardware control */ + int (*request_acquire)(struct cx8802_driver *drv); + int (*request_release)(struct cx8802_driver *drv); +}; + struct cx8802_dev { struct cx88_core *core; spinlock_t slock; @@ -439,6 +470,9 @@ struct cx8802_dev { /* mpeg params */ struct cx2341x_mpeg_params params; + + /* List of attached drivers */ + struct cx8802_driver drvlist; }; /* ----------------------------------------------------------- */ @@ -571,6 +605,11 @@ void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t); void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual); int cx88_audio_thread(void *data); +int cx8802_register_driver(struct cx8802_driver *drv); +int cx8802_unregister_driver(struct cx8802_driver *drv); +struct cx8802_dev * cx8802_get_device(struct inode *inode); +struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype); + /* ----------------------------------------------------------- */ /* cx88-input.c */ @@ -600,6 +639,13 @@ extern int cx88_do_ioctl(struct inode *inode, struct file *file, int radio, extern const u32 cx88_user_ctrls[]; extern int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl); +/* ----------------------------------------------------------- */ +/* cx88-blackbird.c */ +/* used by cx88-ivtv ioctl emulation layer */ +extern int (*cx88_ioctl_hook)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg); +extern unsigned int (*cx88_ioctl_translator)(unsigned int cmd); + /* * Local variables: * c-basic-offset: 8 -- cgit v0.10.2 From 7343826370dd5fe14a2dcec20968f2d3a4431ce6 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Thu, 5 Oct 2006 21:28:24 -0300 Subject: V4L/DVB (4723): Bugfix: Select the correct cx8802_dev when enumerating by CX88_MPEG_type A bug in cx8802_get_driver() meant that in multiboard environments, when testing frontends on the non primary board, the incorrect device was returned resulting in "Unsupported value in .mpeg.." messages. Depending on the electrical design of the hardware (serial, parallel, rising/falling edge detect), transport would still be delivered and the problem went unnoticed. This patch ensures the correct instance of cx8802_dev is returned. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 6f15571..3d8e6d7 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -572,6 +572,8 @@ struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board list_for_each(list,&cx8802_devlist) { h = list_entry(list, struct cx8802_dev, devlist); + if (h != dev) + continue; list_for_each(list2, &h->drvlist.devlist) { d = list_entry(list2, struct cx8802_driver, devlist); -- cgit v0.10.2 From 587d2fd7f296dfb5ccf348e48d6d751bcc6a423a Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Fri, 6 Oct 2006 19:13:50 -0300 Subject: V4L/DVB (4726): Add support for Pinnacle 310i The driver supports analog TV, radio and DVB-T. It is based on the preliminary patch by Pierluigi Rolando. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 53ce6a3..74536df 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -99,3 +99,4 @@ 98 -> Proteus Pro 2309 [0919:2003] 99 -> AVerMedia TV Hybrid A16AR [1461:2c00] 100 -> Asus Europa2 OEM [1043:4860] +101 -> Pinnacle PCTV 310i [11bd:002f] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 51f0cfd..100cfc5 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3022,6 +3022,39 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE1, }, }, + [SAA7134_BOARD_PINNACLE_PCTV_310i] = { + .name = "Pinnacle PCTV 310i", + .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 = 4, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3631,6 +3664,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x4860, .driver_data = SAA7134_BOARD_ASUS_EUROPA2_HYBRID, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x11bd, + .subdevice = 0x002f, + .driver_data = SAA7134_BOARD_PINNACLE_PCTV_310i, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -3793,6 +3832,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) break; /* i2c remotes */ case SAA7134_BOARD_PINNACLE_PCTV_110i: + case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_UPMOST_PURPLE_TV: dev->has_remote = SAA7134_REMOTE_I2C; break; @@ -3924,6 +3964,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) } break; case SAA7134_BOARD_PHILIPS_TIGER: + case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_TEVION_DVBT_220RF: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_MEDION_MD8800_QUADRO: diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 6b61d9b..88e4b0f 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -293,7 +293,7 @@ static int philips_tu1216_tuner_60_set_params(struct dvb_frontend *fe, struct dv return philips_tda6651_pll_set(0x60, fe, params); } -static int philips_tu1216_request_firmware(struct dvb_frontend *fe, +static int philips_tda1004x_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name) { struct saa7134_dev *dev = fe->dvb->priv; @@ -308,7 +308,7 @@ static struct tda1004x_config philips_tu1216_60_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tu1216_request_firmware, + .request_firmware = philips_tda1004x_request_firmware, }; /* ------------------------------------------------------------------ */ @@ -331,7 +331,7 @@ static struct tda1004x_config philips_tu1216_61_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tu1216_request_firmware, + .request_firmware = philips_tda1004x_request_firmware, }; /* ------------------------------------------------------------------ */ @@ -812,32 +812,40 @@ static int philips_tda827xa_tuner_sleep(u8 addr, struct dvb_frontend *fe) if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); return 0; } /* ------------------------------------------------------------------ */ -static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int tda8290_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) { - int ret; struct saa7134_dev *dev = fe->dvb->priv; static u8 tda8290_close[] = { 0x21, 0xc0}; static u8 tda8290_open[] = { 0x21, 0x80}; struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2}; - - /* close tda8290 i2c bridge */ - tda8290_msg.buf = tda8290_close; - ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); - if (ret != 1) + if (enable) { + tda8290_msg.buf = tda8290_close; + } else { + tda8290_msg.buf = tda8290_open; + } + if (i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1) != 1) return -EIO; msleep(20); + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + int ret; + ret = philips_tda827xa_pll_set(0x61, fe, params); if (ret != 0) return ret; - /* open tda8290 i2c bridge */ - tda8290_msg.buf = tda8290_open; - i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); - return ret; + return 0; } static int philips_tiger_tuner_init(struct dvb_frontend *fe) @@ -874,6 +882,18 @@ static struct tda1004x_config philips_tiger_config = { /* ------------------------------------------------------------------ */ +static struct tda1004x_config pinnacle_pctv_310i_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .if_freq = TDA10046_FREQ_045, + .request_firmware = philips_tda1004x_request_firmware, +}; + +/* ------------------------------------------------------------------ */ + static int asus_p7131_dual_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; @@ -1168,6 +1188,18 @@ static int dvb_init(struct saa7134_dev *dev) &philips_tiger_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_PINNACLE_PCTV_310i: + dev->dvb.frontend = dvb_attach(tda10046_attach, + &pinnacle_pctv_310i_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; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 7cf96b4..2de92ce 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -227,6 +227,7 @@ struct saa7134_format { #define SAA7134_BOARD_PROTEUS_2309 98 #define SAA7134_BOARD_AVERMEDIA_A16AR 99 #define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100 +#define SAA7134_BOARD_PINNACLE_PCTV_310i 101 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 -- cgit v0.10.2 From 3ac706d2f77fd7bbef037a6137d08a72d7dc8334 Mon Sep 17 00:00:00 2001 From: Mikhail Fedotov Date: Fri, 6 Oct 2006 20:23:47 -0300 Subject: V4L/DVB (4728): Add support for AverMedia AverTV Studio 507 This is just an additional analog board configuration. Signed-off-by: Mikhail Fedotov Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 74536df..8be8f97 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -100,3 +100,4 @@ 99 -> AVerMedia TV Hybrid A16AR [1461:2c00] 100 -> Asus Europa2 OEM [1043:4860] 101 -> Pinnacle PCTV 310i [11bd:002f] +102 -> Avermedia AVerTV Studio 507 [1461:9715] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 100cfc5..2a00cb4 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3055,6 +3055,49 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200000, }, }, + [SAA7134_BOARD_AVERMEDIA_STUDIO_507] = { + /* Mikhail Fedotov */ + .name = "Avermedia AVerTV Studio 507", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1256_IH3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x03, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x00, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x00, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + .gpio = 0x00, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x00, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x01, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + .gpio = 0x00, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3670,6 +3713,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x002f, .driver_data = SAA7134_BOARD_PINNACLE_PCTV_310i, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0x9715, + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -3756,6 +3805,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_305: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: 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 */ diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index dee8355..0548bd3 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -184,6 +184,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_307: case SAA7134_BOARD_AVERMEDIA_STUDIO_305: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: + case SAA7134_BOARD_AVERMEDIA_STUDIO_507: case SAA7134_BOARD_AVERMEDIA_GO_007_FM: ir_codes = ir_codes_avermedia; mask_keycode = 0x0007C8; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 2de92ce..799a714 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -228,6 +228,7 @@ struct saa7134_format { #define SAA7134_BOARD_AVERMEDIA_A16AR 99 #define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100 #define SAA7134_BOARD_PINNACLE_PCTV_310i 101 +#define SAA7134_BOARD_AVERMEDIA_STUDIO_507 102 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 -- cgit v0.10.2 From 019391e426266a346ac2f1b3d3c70c26b482ff31 Mon Sep 17 00:00:00 2001 From: Steven Toth Date: Fri, 6 Oct 2006 21:29:25 -0300 Subject: V4L/DVB (4736): Cx88-blackbird module is rejected during probe. If the last cx88 board probed is not backbird based, and a previous board was, the entire module is unloaded leading to an oops during mpeg_open on the first /dev/videoN device. Signed-off-by: Steven Toth Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 3d8e6d7..1fe1a83 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -662,7 +662,6 @@ int cx8802_register_driver(struct cx8802_driver *drv) } list_for_each(list,&cx8802_devlist) { - i++; h = list_entry(list, struct cx8802_dev, devlist); printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d]\n", @@ -685,6 +684,7 @@ int cx8802_register_driver(struct cx8802_driver *drv) err = drv->probe(driver); if (err == 0) { + i++; mutex_lock(&drv->core->lock); list_add_tail(&driver->devlist,&h->drvlist.devlist); mutex_unlock(&drv->core->lock); @@ -695,6 +695,8 @@ int cx8802_register_driver(struct cx8802_driver *drv) } if (i == 0) err = -ENODEV; + else + err = 0; return err; } -- cgit v0.10.2 From 01451e722793f191f6e13c5150dd8664f4439d17 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Fri, 13 Oct 2006 11:34:46 -0300 Subject: V4L/DVB (4749): Fixed DVB-USB-Adapter indention While converting everything to DVB-USB-Adapter, there was a wrong indention. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c index 2ed3eb6..a6c5f19 100644 --- a/drivers/media/dvb/dvb-usb/a800.c +++ b/drivers/media/dvb/dvb-usb/a800.c @@ -116,24 +116,24 @@ static struct dvb_usb_device_properties a800_properties = { { .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - - .frontend_attach = dibusb_dib3000mc_frontend_attach, - .tuner_attach = dibusb_dib3000mc_tuner_attach, - - /* parameter for the MPEG2-data transfer */ - .stream = { - .type = USB_BULK, - .count = 7, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), }, diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 43f3906..e05200b 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -529,20 +529,20 @@ static struct dvb_usb_device_properties cxusb_medion_properties = { .num_adapters = 1, .adapter = { { - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_cx22702_frontend_attach, - .tuner_attach = cxusb_fmd1216me_tuner_attach, - /* parameter for the MPEG2-data transfer */ - .stream = { - .type = USB_BULK, - .count = 5, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_cx22702_frontend_attach, + .tuner_attach = cxusb_fmd1216me_tuner_attach, + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, }, }, @@ -575,21 +575,21 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { .num_adapters = 1, .adapter = { { - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_lgdt3303_frontend_attach, - .tuner_attach = cxusb_lgdt3303_tuner_attach, - - /* parameter for the MPEG2-data transfer */ - .stream = { - .type = USB_BULK, - .count = 5, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_lgdt3303_frontend_attach, + .tuner_attach = cxusb_lgdt3303_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, }, }, @@ -627,20 +627,20 @@ static struct dvb_usb_device_properties cxusb_bluebird_dee1601_properties = { .num_adapters = 1, .adapter = { { - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_dee1601_frontend_attach, - .tuner_attach = cxusb_dee1601_tuner_attach, - /* parameter for the MPEG2-data transfer */ + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_dee1601_frontend_attach, + .tuner_attach = cxusb_dee1601_tuner_attach, + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 5, - .endpoint = 0x04, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .count = 5, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, }, }, @@ -686,21 +686,21 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties = { .num_adapters = 2, .adapter = { { - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_mt352_frontend_attach, - .tuner_attach = cxusb_lgz201_tuner_attach, + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_mt352_frontend_attach, + .tuner_attach = cxusb_lgz201_tuner_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 5, - .endpoint = 0x04, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .count = 5, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, @@ -736,21 +736,21 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { .num_adapters = 1, .adapter = { { - .streaming_ctrl = cxusb_streaming_ctrl, - .frontend_attach = cxusb_mt352_frontend_attach, - .tuner_attach = cxusb_dtt7579_tuner_attach, + .streaming_ctrl = cxusb_streaming_ctrl, + .frontend_attach = cxusb_mt352_frontend_attach, + .tuner_attach = cxusb_dtt7579_tuner_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 5, - .endpoint = 0x04, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .count = 5, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, }, }, .power_ctrl = cxusb_bluebird_power_ctrl, diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c index 4fe363e..7a6ae8f 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mb.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -163,23 +163,23 @@ static struct dvb_usb_device_properties dibusb1_1_properties = { .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 16, - .streaming_ctrl = dibusb_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mb_frontend_attach, - .tuner_attach = dibusb_tuner_probe_and_attach, + .streaming_ctrl = dibusb_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_tuner_probe_and_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } }, @@ -248,23 +248,23 @@ static struct dvb_usb_device_properties dibusb1_1_an2235_properties = { .caps = DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF | DVB_USB_ADAP_HAS_PID_FILTER, .pid_filter_count = 16, - .streaming_ctrl = dibusb_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mb_frontend_attach, - .tuner_attach = dibusb_tuner_probe_and_attach, + .streaming_ctrl = dibusb_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_tuner_probe_and_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), }, }, @@ -312,22 +312,23 @@ static struct dvb_usb_device_properties dibusb2_0b_properties = { .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 16, - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mb_frontend_attach, - .tuner_attach = dibusb_thomson_tuner_attach, - /* parameter for the MPEG2-data transfer */ + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_thomson_tuner_attach, + + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } }, @@ -369,22 +370,22 @@ static struct dvb_usb_device_properties artec_t1_usb2_properties = { .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 16, - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mb_frontend_attach, - .tuner_attach = dibusb_tuner_probe_and_attach, - /* parameter for the MPEG2-data transfer */ + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mb_frontend_attach, + .tuner_attach = dibusb_tuner_probe_and_attach, + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } }, diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c index a0fd37e..e7ea3e7 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mc.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c @@ -54,23 +54,23 @@ static struct dvb_usb_device_properties dibusb_mc_properties = { { .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mc_frontend_attach, - .tuner_attach = dibusb_dib3000mc_tuner_attach, + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } }, diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c index 8fb3437..4a198d4 100644 --- a/drivers/media/dvb/dvb-usb/digitv.c +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -274,20 +274,20 @@ static struct dvb_usb_device_properties digitv_properties = { .num_adapters = 1, .adapter = { { - .frontend_attach = digitv_frontend_attach, - .tuner_attach = digitv_tuner_attach, + .frontend_attach = digitv_frontend_attach, + .tuner_attach = digitv_tuner_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, } }, .identify_state = digitv_identify_state, diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c index fa43a41..7dbe143 100644 --- a/drivers/media/dvb/dvb-usb/dtt200u.c +++ b/drivers/media/dvb/dvb-usb/dtt200u.c @@ -268,20 +268,20 @@ static struct dvb_usb_device_properties wt220u_zl0353_properties = { .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, .pid_filter_count = 15, - .streaming_ctrl = dtt200u_streaming_ctrl, - .pid_filter = dtt200u_pid_filter, - .frontend_attach = dtt200u_frontend_attach, - /* parameter for the MPEG2-data transfer */ + .streaming_ctrl = dtt200u_streaming_ctrl, + .pid_filter = dtt200u_pid_filter, + .frontend_attach = dtt200u_frontend_attach, + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, } }, .power_ctrl = dtt200u_power_ctrl, diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c index 7375eb2..518d67f 100644 --- a/drivers/media/dvb/dvb-usb/gp8psk.c +++ b/drivers/media/dvb/dvb-usb/gp8psk.c @@ -194,19 +194,19 @@ static struct dvb_usb_device_properties gp8psk_properties = { .num_adapters = 1, .adapter = { { - .streaming_ctrl = gp8psk_streaming_ctrl, - .frontend_attach = gp8psk_frontend_attach, - /* parameter for the MPEG2-data transfer */ + .streaming_ctrl = gp8psk_streaming_ctrl, + .frontend_attach = gp8psk_frontend_attach, + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x82, - .u = { - .bulk = { - .buffersize = 8192, - } - } - }, + .count = 7, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, } }, .power_ctrl = gp8psk_power_ctrl, diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c index a58874c..d48622e 100644 --- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c +++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c @@ -163,23 +163,23 @@ static struct dvb_usb_device_properties nova_t_properties = { .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, .pid_filter_count = 32, - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .pid_filter = dibusb_pid_filter, - .pid_filter_ctrl = dibusb_pid_filter_ctrl, - .frontend_attach = dibusb_dib3000mc_frontend_attach, - .tuner_attach = dibusb_dib3000mc_tuner_attach, - - /* parameter for the MPEG2-data transfer */ - .stream = { - .type = USB_BULK, - .count = 7, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = dibusb_dib3000mc_frontend_attach, + .tuner_attach = dibusb_dib3000mc_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c index f9941ea..f77b48f 100644 --- a/drivers/media/dvb/dvb-usb/umt-010.c +++ b/drivers/media/dvb/dvb-usb/umt-010.c @@ -99,21 +99,21 @@ static struct dvb_usb_device_properties umt_properties = { .num_adapters = 1, .adapter = { { - .streaming_ctrl = dibusb2_0_streaming_ctrl, - .frontend_attach = umt_mt352_frontend_attach, - .tuner_attach = umt_tuner_attach, + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .frontend_attach = umt_mt352_frontend_attach, + .tuner_attach = umt_tuner_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 20, - .endpoint = 0x06, - .u = { - .bulk = { - .buffersize = 512, - } - } - }, + .count = 20, + .endpoint = 0x06, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, .size_of_priv = sizeof(struct dibusb_state), } diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c index 02bd61a..16533b3 100644 --- a/drivers/media/dvb/dvb-usb/vp702x.c +++ b/drivers/media/dvb/dvb-usb/vp702x.c @@ -275,22 +275,22 @@ static struct dvb_usb_device_properties vp702x_properties = { .caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS, .streaming_ctrl = vp702x_streaming_ctrl, - .frontend_attach = vp702x_frontend_attach, + .frontend_attach = vp702x_frontend_attach, - /* parameter for the MPEG2-data transfer */ + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, .count = 10, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, .size_of_priv = sizeof(struct vp702x_state), } - }, + }, .read_mac_address = vp702x_read_mac_addr, .rc_key_map = vp702x_rc_keys, diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c index b4cf002..2978062 100644 --- a/drivers/media/dvb/dvb-usb/vp7045.c +++ b/drivers/media/dvb/dvb-usb/vp7045.c @@ -217,18 +217,18 @@ static struct dvb_usb_device_properties vp7045_properties = { .num_adapters = 1, .adapter = { { - .frontend_attach = vp7045_frontend_attach, - /* parameter for the MPEG2-data transfer */ + .frontend_attach = vp7045_frontend_attach, + /* parameter for the MPEG2-data transfer */ .stream = { .type = USB_BULK, - .count = 7, - .endpoint = 0x02, - .u = { - .bulk = { - .buffersize = 4096, - } - } - }, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, } }, .power_ctrl = vp7045_power_ctrl, -- cgit v0.10.2 From f71a56c17225392c873225f2d567f5caddc6b963 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 13 Oct 2006 21:55:57 -0300 Subject: V4L/DVB (4753): Cxusb: rename cxusb_lgdt3303_tuner_attach cxusb_lgdt3303_tuner_attach were renamed to cxusb_lgh064f_tuner_attach Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index e05200b..ef542b6 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -396,7 +396,7 @@ static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap) return 0; } -static int cxusb_lgdt3303_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; return 0; @@ -577,7 +577,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgh064f_properties = { { .streaming_ctrl = cxusb_streaming_ctrl, .frontend_attach = cxusb_lgdt3303_frontend_attach, - .tuner_attach = cxusb_lgdt3303_tuner_attach, + .tuner_attach = cxusb_lgh064f_tuner_attach, /* parameter for the MPEG2-data transfer */ .stream = { -- cgit v0.10.2 From 5b9ed286759eb1c805f344398ee2c57191d7e2bd Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 15 Oct 2006 14:51:08 -0300 Subject: V4L/DVB (4754): Cxusb: update copyright and author email address Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index ef542b6..4e2eef8 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -14,7 +14,7 @@ * TODO: Use the cx25840-driver for the analogue part * * Copyright (C) 2005 Patrick Boettcher (patrick.boettcher@desy.de) - * Copyright (C) 2005 Michael Krufky (mkrufky@m1k.net) + * Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org) * Copyright (C) 2006 Chris Pascoe (c.pascoe@itee.uq.edu.au) * * This program is free software; you can redistribute it and/or modify it @@ -802,7 +802,7 @@ module_init (cxusb_module_init); module_exit (cxusb_module_exit); MODULE_AUTHOR("Patrick Boettcher "); -MODULE_AUTHOR("Michael Krufky "); +MODULE_AUTHOR("Michael Krufky "); MODULE_AUTHOR("Chris Pascoe "); MODULE_DESCRIPTION("Driver for Conexant USB2.0 hybrid reference design"); MODULE_VERSION("1.0-alpha"); -- cgit v0.10.2 From 5b26c82f959e72cbd642ef4039a356ff8385cd58 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 16 Oct 2006 16:07:51 -0300 Subject: V4L/DVB (4756): Cx88: cleanups - fixed whitespace, replaced leading spaces with tabs - moved .mpeg descriptor below input settings Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 1c5db20..3597993 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -786,7 +786,6 @@ struct cx88_board cx88_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .mpeg = CX88_MPEG_BLACKBIRD, .input = {{ .type = CX88_VMUX_COMPOSITE1, .vmux = 0, @@ -805,6 +804,7 @@ struct cx88_board cx88_boards[] = { .vmux = 2, .gpio0 = 0x0000cdf3, }, + .mpeg = CX88_MPEG_BLACKBIRD, }, [CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = { /* Alexander Wold */ @@ -1252,35 +1252,35 @@ struct cx88_board cx88_boards[] = { .gpio0 = 0x070b, }}, }, - [CX88_BOARD_TE_DTV_250_OEM_SWANN] = { - .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM", - .tuner_type = TUNER_LG_PAL_NEW_TAPC, - .radio_type = UNSET, - .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .input = {{ - .type = CX88_VMUX_TELEVISION, - .vmux = 0, - .gpio0 = 0x003fffff, - .gpio1 = 0x00e00000, - .gpio2 = 0x003fffff, - .gpio3 = 0x02000000, - },{ - .type = CX88_VMUX_COMPOSITE1, - .vmux = 1, - .gpio0 = 0x003fffff, - .gpio1 = 0x00e00000, - .gpio2 = 0x003fffff, - .gpio3 = 0x02000000, - },{ - .type = CX88_VMUX_SVIDEO, - .vmux = 2, - .gpio0 = 0x003fffff, - .gpio1 = 0x00e00000, - .gpio2 = 0x003fffff, - .gpio3 = 0x02000000, - }}, - }, + [CX88_BOARD_TE_DTV_250_OEM_SWANN] = { + .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM", + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x003fffff, + .gpio1 = 0x00e00000, + .gpio2 = 0x003fffff, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x003fffff, + .gpio1 = 0x00e00000, + .gpio2 = 0x003fffff, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x003fffff, + .gpio1 = 0x00e00000, + .gpio2 = 0x003fffff, + .gpio3 = 0x02000000, + }}, + }, [CX88_BOARD_HAUPPAUGE_HVR1300] = { .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder", .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, -- cgit v0.10.2 From f24546a95ade39b9cd292f06f92232becbbf35ae Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 16 Oct 2006 16:51:11 -0300 Subject: V4L/DVB (4757): Cx88: determine whether or not to use external adc Some cx88-blackbird boards use an external adc, but not necessarily for all inputs. Thus, this needs to be configurable on the card level for each input. This patch allows for the usage of the external adc to be determined by a bit setting in the cx88_input struct for cards based on the cx88 blackbird design. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 3597993..d7cf54b 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -281,18 +281,22 @@ struct cx88_board cx88_boards[] = { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x0000bde2, + .extadc = 1, },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x0000bde6, + .extadc = 1, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0000bde6, + .extadc = 1, }}, .radio = { .type = CX88_RADIO, .gpio0 = 0x0000bd62, + .extadc = 1, }, .mpeg = CX88_MPEG_BLACKBIRD, }, @@ -523,6 +527,7 @@ struct cx88_board cx88_boards[] = { .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, + .extadc = 1, }}, .mpeg = CX88_MPEG_BLACKBIRD, }, @@ -646,18 +651,22 @@ struct cx88_board cx88_boards[] = { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0x00009d80, + .extadc = 1, },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x00009d76, + .extadc = 1, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x00009d76, + .extadc = 1, }}, .radio = { .type = CX88_RADIO, .gpio0 = 0x00009d00, + .extadc = 1, }, .mpeg = CX88_MPEG_BLACKBIRD, }, @@ -790,19 +799,23 @@ struct cx88_board cx88_boards[] = { .type = CX88_VMUX_COMPOSITE1, .vmux = 0, .gpio0 = 0x0000cd73, + .extadc = 1, },{ .type = CX88_VMUX_SVIDEO, .vmux = 1, .gpio0 = 0x0000cd73, + .extadc = 1, },{ .type = CX88_VMUX_TELEVISION, .vmux = 3, .gpio0 = 0x0000cdb3, + .extadc = 1, }}, .radio = { .type = CX88_RADIO, .vmux = 2, .gpio0 = 0x0000cdf3, + .extadc = 1, }, .mpeg = CX88_MPEG_BLACKBIRD, }, @@ -1293,14 +1306,17 @@ struct cx88_board cx88_boards[] = { .type = CX88_VMUX_TELEVISION, .vmux = 0, .gpio0 = 0xe780, + .extadc = 1, },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0xe780, + .extadc = 1, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0xe780, + .extadc = 1, }}, /* fixme: Add radio support */ .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c index 58ba9f7..3482e01 100644 --- a/drivers/media/video/cx88/cx88-tvaudio.c +++ b/drivers/media/video/cx88/cx88-tvaudio.c @@ -143,19 +143,6 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl) cx88_start_audio_dma(core); if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) { - /* sets sound input from external adc */ - switch (core->board) { - case CX88_BOARD_HAUPPAUGE_ROSLYN: - case CX88_BOARD_KWORLD_MCE200_DELUXE: - case CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT: - case CX88_BOARD_PIXELVIEW_PLAYTV_P7000: - case CX88_BOARD_ASUS_PVR_416: - cx_clear(AUD_CTL, EN_I2SIN_ENABLE); - break; - default: - cx_set(AUD_CTL, EN_I2SIN_ENABLE); - } - cx_write(AUD_I2SINPUTCNTL, 4); cx_write(AUD_BAUDRATE, 1); /* 'pass-thru mode': this enables the i2s output to the mpeg encoder */ diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index f273121..8613378 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -454,6 +454,14 @@ static int video_mux(struct cx88_core *core, unsigned int input) cx_clear(MO_FILTER_ODD, 0x00002020); break; } + + if (cx88_boards[core->board].mpeg & CX88_MPEG_BLACKBIRD) { + /* sets sound input from external adc */ + if (INPUT(input)->extadc) + cx_set(AUD_CTL, EN_I2SIN_ENABLE); + else + cx_clear(AUD_CTL, EN_I2SIN_ENABLE); + } return 0; } diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 5980e47..7054e94 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -225,6 +225,7 @@ struct cx88_input { enum cx88_itype type; unsigned int vmux; u32 gpio0, gpio1, gpio2, gpio3; + unsigned int extadc:1; }; struct cx88_board { -- cgit v0.10.2 From cc0f3f51357f46c08e2f34a7a22df1100f082de5 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 16 Oct 2006 16:53:01 -0300 Subject: V4L/DVB (4758): Cx88: use external adc for svideo/composite For the KWorld HardwareMpegTV XPert, the external adc must be used for svideo / composite inputs, but television / radio inputs use the internal adc. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index d7cf54b..43e5af5 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1063,7 +1063,6 @@ struct cx88_board cx88_boards[] = { .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = { - /* FIXME: Audio not working for s-video / composite inputs. */ .name = "KWorld HardwareMpegTV XPert", .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -1078,10 +1077,12 @@ struct cx88_board cx88_boards[] = { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, .gpio0 = 0x3de6, + .extadc = 1, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x3de6, + .extadc = 1, }}, .radio = { .type = CX88_RADIO, -- cgit v0.10.2 From fe16af26c849eb75134855c7be22352f1a15844c Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 16 Oct 2006 20:47:14 -0300 Subject: V4L/DVB (4759): Cx88: use external adc for rca audio inputs on the ASUS PVR-416 For the ASUS PVR-416, the external adc must be used for the rca audio inputs, but television / radio inputs use the internal adc. Thanks to Alex Deucher for lending his card to me. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 43e5af5..f30f889 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -357,6 +357,7 @@ struct cx88_board cx88_boards[] = { .type = CX88_VMUX_SVIDEO, .vmux = 2, .gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in? + .extadc = 1, }}, .radio = { .type = CX88_RADIO, -- cgit v0.10.2 From c85f49d49f437605550322bd2739af18230cf85b Mon Sep 17 00:00:00 2001 From: Amit Choudhary Date: Tue, 17 Oct 2006 11:39:06 -0300 Subject: V4L/DVB (4761): Stv680.c: check kmalloc() return value. Signed-off-by: Amit Choudhary Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c index 6d1ef1e..a1ec3ac 100644 --- a/drivers/media/video/stv680.c +++ b/drivers/media/video/stv680.c @@ -687,7 +687,7 @@ static int stv680_start_stream (struct usb_stv *stv680) stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL); if (stv680->sbuf[i].data == NULL) { PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i); - return -1; + goto nomem_err; } } @@ -698,7 +698,7 @@ static int stv680_start_stream (struct usb_stv *stv680) stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL); if (stv680->scratch[i].data == NULL) { PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i); - return -1; + goto nomem_err; } stv680->scratch[i].state = BUFFER_UNUSED; } @@ -706,7 +706,7 @@ static int stv680_start_stream (struct usb_stv *stv680) for (i = 0; i < STV680_NUMSBUF; i++) { urb = usb_alloc_urb (0, GFP_KERNEL); if (!urb) - return -ENOMEM; + goto nomem_err; /* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */ usb_fill_bulk_urb (urb, stv680->udev, @@ -721,6 +721,21 @@ static int stv680_start_stream (struct usb_stv *stv680) stv680->framecount = 0; return 0; + + nomem_err: + for (i = 0; i < STV680_NUMSCRATCH; i++) { + kfree(stv680->scratch[i].data); + stv680->scratch[i].data = NULL; + } + for (i = 0; i < STV680_NUMSBUF; i++) { + usb_kill_urb(stv680->urb[i]); + usb_free_urb(stv680->urb[i]); + stv680->urb[i] = NULL; + kfree(stv680->sbuf[i].data); + stv680->sbuf[i].data = NULL; + } + return -ENOMEM; + } static int stv680_stop_stream (struct usb_stv *stv680) -- cgit v0.10.2 From cc75aede1b1ce29a478911ad7dadee94ecd45bbf Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 15 Oct 2006 21:35:14 -0300 Subject: V4L/DVB (4763): Pvrusb2: Implement IR reception for 24xxx devices Unlike 29xxx devices, the 24xxx model series does not have a dedicated I2C device for reception of IR codes. Instead IR is handled directly by the FX2 microcontroller and the results are communicated via commands to the FX2. Rather than implement a whole new IR reception pathway for 24xxx devices, this changeset instead emulates the presence of the 29xxx device's I2C based IR receiver by intercepting commands to that chip and issuing appropriate FX2 commands to do the needed action. This has the result of allowing all the usual IR frameworks (ir-kbd-i2c or lirc) to continue working unmodified for 24xxx devices. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 3b9012f8e3..f9bb41d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -185,6 +185,79 @@ static int pvr2_i2c_basic_op(struct pvr2_hdw *hdw, } } + +/* This is a special entry point for cases of I2C transaction attempts to + the IR receiver. The implementation here simulates the IR receiver by + issuing a command to the FX2 firmware and using that response to return + what the real I2C receiver would have returned. We use this for 24xxx + devices, where the IR receiver chip has been removed and replaced with + FX2 related logic. */ +static int i2c_24xxx_ir(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + u8 dat[4]; + unsigned int stat; + + if (!(rlen || wlen)) { + /* This is a probe attempt. Just let it succeed. */ + return 0; + } + + /* We don't understand this kind of transaction */ + if ((wlen != 0) || (rlen == 0)) return -EIO; + + if (rlen < 3) { + /* Mike Isely Appears to be a probe + attempt from lirc. Just fill in zeroes and return. If + we try instead to do the full transaction here, then bad + things seem to happen within the lirc driver module + (version 0.8.0-7 sources from Debian, when run under + vanilla 2.6.17.6 kernel) - and I don't have the patience + to chase it down. */ + if (rlen > 0) rdata[0] = 0; + if (rlen > 1) rdata[1] = 0; + return 0; + } + + /* Issue a command to the FX2 to read the IR receiver. */ + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = 0xec; + stat = pvr2_send_request(hdw, + hdw->cmd_buffer,1, + hdw->cmd_buffer,4); + dat[0] = hdw->cmd_buffer[0]; + dat[1] = hdw->cmd_buffer[1]; + dat[2] = hdw->cmd_buffer[2]; + dat[3] = hdw->cmd_buffer[3]; + } while (0); LOCK_GIVE(hdw->ctl_lock); + + /* Give up if that operation failed. */ + if (stat != 0) return stat; + + /* Mangle the results into something that looks like the real IR + receiver. */ + rdata[2] = 0xc1; + if (dat[0] != 1) { + /* No code received. */ + rdata[0] = 0; + rdata[1] = 0; + } else { + u16 val; + /* Mash the FX2 firmware-provided IR code into something + that the normal i2c chip-level driver expects. */ + val = dat[1]; + val <<= 8; + val |= dat[2]; + val >>= 1; + val &= ~0x0003; + val |= 0x8000; + rdata[0] = (val >> 8) & 0xffu; + rdata[1] = val & 0xffu; + } + + return 0; +} + /* This is a special entry point that is entered if an I2C operation is attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this part doesn't work, but we know it is really there. So let's look for @@ -887,17 +960,17 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) { unsigned int idx; - // The default action for all possible I2C addresses is just to do - // the transfer normally. + /* The default action for all possible I2C addresses is just to do + the transfer normally. */ for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) { hdw->i2c_func[idx] = pvr2_i2c_basic_op; } - // If however we're dealing with new hardware, insert some hacks in - // the I2C transfer stack to let things work better. + /* However, deal with various special cases for 24xxx hardware. */ if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { hdw->i2c_func[0x1b] = i2c_hack_wm8775; hdw->i2c_func[0x44] = i2c_hack_cx25840; + hdw->i2c_func[0x18] = i2c_24xxx_ir; } // Configure the adapter and set up everything else related to it. -- cgit v0.10.2 From 3cc2e4c3a137075ee66e2d4ce95a95ba153bd7b9 Mon Sep 17 00:00:00 2001 From: Luke Deller Date: Tue, 17 Oct 2006 18:28:10 -0300 Subject: V4L/DVB (4765): Dvb-usb/vp7045.c patch for extra key Add support for more keys on the remote control included with the DigitalNow tinyUSB2 DVB-T Receiver. Signed-off-by: Luke Deller Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c index 2978062..69a46b3 100644 --- a/drivers/media/dvb/dvb-usb/vp7045.c +++ b/drivers/media/dvb/dvb-usb/vp7045.c @@ -125,7 +125,25 @@ static struct dvb_usb_rc_key vp7045_rc_keys[] = { { 0x00, 0x00, KEY_TAB }, /* Tab */ { 0x00, 0x48, KEY_INFO }, /* Preview */ { 0x00, 0x04, KEY_LIST }, /* RecordList */ - { 0x00, 0x0f, KEY_TEXT } /* Teletext */ + { 0x00, 0x0f, KEY_TEXT }, /* Teletext */ + { 0x00, 0x41, KEY_PREVIOUSSONG }, + { 0x00, 0x42, KEY_NEXTSONG }, + { 0x00, 0x4b, KEY_UP }, + { 0x00, 0x51, KEY_DOWN }, + { 0x00, 0x4e, KEY_LEFT }, + { 0x00, 0x52, KEY_RIGHT }, + { 0x00, 0x4f, KEY_ENTER }, + { 0x00, 0x13, KEY_CANCEL }, + { 0x00, 0x4a, KEY_CLEAR }, + { 0x00, 0x54, KEY_PRINT }, /* Capture */ + { 0x00, 0x43, KEY_SUBTITLE }, /* Subtitle/CC */ + { 0x00, 0x08, KEY_VIDEO }, /* A/V */ + { 0x00, 0x07, KEY_SLEEP }, /* Hibernate */ + { 0x00, 0x45, KEY_ZOOM }, /* Zoom+ */ + { 0x00, 0x18, KEY_RED}, + { 0x00, 0x53, KEY_GREEN}, + { 0x00, 0x5e, KEY_YELLOW}, + { 0x00, 0x5f, KEY_BLUE} }; static int vp7045_rc_query(struct dvb_usb_device *d, u32 *event, int *state) -- cgit v0.10.2 From 69ea31e7debdefcf1412e8d590ae9bd90cf9253f Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Tue, 17 Oct 2006 18:28:14 -0300 Subject: V4L/DVB (4766): Add working dib7000m-module First working version of the dib7000m-driver. This commit also makes the Hauppauge NOVA-T Stick working. Signed-off-by: Francois Kanounnikoff Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index b4e339d..305dd35 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -96,29 +96,97 @@ static int bristol_tuner_attach(struct dvb_usb_adapter *adap) } /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */ -/* -static struct mt2060_config stk7000p_mt2060_config = { - 0x60 +static struct dibx000_agc_config stk7700p_dib7000m_agc_config = { + BAND_UHF | BAND_VHF, // band_caps + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup + + 712, // inv_gain + 41, // time_stabiliz + + 0, // alpha_level + 118, // thlock + + 0, // wbd_inv + 4095, // wbd_ref + 0, // wbd_sel + 0, // wbd_alpha + + 42598, // agc1_max + 17694, // agc1_min + 45875, // agc2_max + 2621, // agc2_min + 0, // agc1_pt1 + 76, // agc1_pt2 + 139, // agc1_pt3 + 52, // agc1_slope1 + 59, // agc1_slope2 + 107, // agc2_pt1 + 172, // agc2_pt2 + 57, // agc2_slope1 + 70, // agc2_slope2 + + 21, // alpha_mant + 25, // alpha_exp + 28, // beta_mant + 48, // beta_exp + + 1, // perform_agc_softsplit + { 0, // split_min + 107, // split_max + 51800, // global_split_min + 24700 // global_split_max + }, +}; + +static struct dibx000_bandwidth_config stk7700p_dib7000m_mt2060_config = { + 60000, 30000, // internal, sampling + 1, 8, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass + 0, 0, 1, 1, 0, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo + (3 << 14) | (1 << 12) | (524 << 0), // sad_cfg: refsel, sel, freq_15k + 60258167, // ifreq + 20452225, // timf +}; + +static struct dib7000m_config stk7700p_dib7000m_config = { + .dvbt_mode = 1, + .output_mpeg2_in_188_bytes = 1, + .quartz_direct = 1, + + .agc_config_count = 1, + .agc = &stk7700p_dib7000m_agc_config, + .bw = &stk7700p_dib7000m_mt2060_config, + + .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, }; -*/ static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap) { + struct dib0700_state *st = adap->dev->priv; /* unless there is no real power management in DVB - we leave the device on GPIO6 */ - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10); - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); - dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10); -// adap->fe = dib7000m_attach(&adap->dev->i2c_adap, &stk7700p_dib7000m_config, 18); - return 0; + st->mt2060_if1[0] = 1220; + return (adap->fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config)) == NULL ? -ENODEV : 0; } +static struct mt2060_config stk7700p_mt2060_config = { + 0x60 +}; + static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap) { -// tun_i2c = dib7000m_get_tuner_i2c_master(adap->fe, 1); -// return mt2060_attach(adap->fe, tun_i2c, &stk3000p_mt2060_config, if1); - return 0; + struct dib0700_state *st = adap->dev->priv; + struct i2c_adapter *tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + return dvb_attach(mt2060_attach,adap->fe, tun_i2c, &stk7700p_mt2060_config, + st->mt2060_if1[0]) == NULL ? -ENODEV : 0; } struct usb_device_id dib0700_usb_id_table[] = { @@ -127,6 +195,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_2) }, { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK) }, { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P_PC) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -171,7 +240,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_device_descs = 3, .devices = { { "DiBcom STK7700P reference design", - { &dib0700_usb_id_table[0], NULL }, + { &dib0700_usb_id_table[0], &dib0700_usb_id_table[5] }, { NULL }, }, { "Hauppauge Nova-T Stick", diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 967d102..fd8ba25 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -53,6 +53,7 @@ #define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6 #define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7 #define USB_PID_DIBCOM_STK7700P 0x1e14 +#define USB_PID_DIBCOM_STK7700P_PC 0x1e78 #define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 #define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index aebb8d6f..093c769 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -172,6 +172,14 @@ config DVB_DIB3000MC A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. +config DVB_DIB7000M + tristate "DiBcom 7000MA/MB/PA/PB/MC" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. + comment "DVB-C (cable) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index dce9cf0..244e874 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_DVB_TDA8083) += tda8083.o obj-$(CONFIG_DVB_L64781) += l64781.o obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o +obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o obj-$(CONFIG_DVB_MT312) += mt312.o obj-$(CONFIG_DVB_VES1820) += ves1820.o obj-$(CONFIG_DVB_VES1X93) += ves1x93.o diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c index c9a5767..a851903 100644 --- a/drivers/media/dvb/frontends/dib7000m.c +++ b/drivers/media/dvb/frontends/dib7000m.c @@ -19,7 +19,7 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); -#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); } } while (0) +#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000M:"); printk(args); } } while (0) struct dib7000m_state { struct dvb_frontend demod; @@ -38,11 +38,21 @@ struct dib7000m_state { u8 current_band; fe_bandwidth_t current_bandwidth; struct dibx000_agc_config *current_agc; - u32 timf[9]; + u32 timf; u16 revision; }; +enum dib7000m_power_mode { + DIB7000M_POWER_ALL = 0, + + DIB7000M_POWER_NO, + DIB7000M_POWER_INTERF_ANALOG_AGC, + DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD, + DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD, + DIB7000M_POWER_INTERFACE_ONLY, +}; + static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg) { u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff }; @@ -58,7 +68,6 @@ static u16 dib7000m_read_word(struct dib7000m_state *state, u16 reg) return (rb[0] << 8) | rb[1]; } -/* static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val) { u8 b[4] = { @@ -70,7 +79,835 @@ static int dib7000m_write_word(struct dib7000m_state *state, u16 reg, u16 val) }; return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; } -*/ +static int dib7000m_set_output_mode(struct dib7000m_state *state, int mode) +{ + int ret = 0; + u16 outreg, fifo_threshold, smo_mode, + sram = 0x0005; /* by default SRAM output is disabled */ + + outreg = 0; + fifo_threshold = 1792; + smo_mode = (dib7000m_read_word(state, 294 + state->reg_offs) & 0x0010) | (1 << 1); + + dprintk("-I- Setting output mode for demod %p to %d\n", + &state->demod, mode); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0482 */ + break; + case OUTMODE_DIVERSITY: + if (state->cfg.hostbus_diversity) + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + else + sram |= 0x0c00; + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + smo_mode |= (3 << 1); + fifo_threshold = 512; + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_HIGH_Z: // disable + outreg = 0; + break; + default: + dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod); + break; + } + + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5) ; + + ret |= dib7000m_write_word(state, 294 + state->reg_offs, smo_mode); + ret |= dib7000m_write_word(state, 295 + state->reg_offs, fifo_threshold); /* synchronous fread */ + ret |= dib7000m_write_word(state, 1795, outreg); + ret |= dib7000m_write_word(state, 1805, sram); + + return ret; +} + +static int dib7000m_set_power_mode(struct dib7000m_state *state, enum dib7000m_power_mode mode) +{ + /* by default everything is going to be powered off */ + u16 reg_903 = 0xffff, reg_904 = 0xffff, reg_905 = 0xffff, reg_906 = 0x3fff; + + /* now, depending on the requested mode, we power on */ + switch (mode) { + /* power up everything in the demod */ + case DIB7000M_POWER_ALL: + reg_903 = 0x0000; reg_904 = 0x0000; reg_905 = 0x0000; reg_906 = 0x0000; + break; + + /* just leave power on the control-interfaces: GPIO and (I2C or SDIO or SRAM) */ + case DIB7000M_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C or SRAM */ + reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 2)); + break; + + case DIB7000M_POWER_INTERF_ANALOG_AGC: + reg_903 &= ~((1 << 15) | (1 << 14) | (1 << 11) | (1 << 10)); + reg_905 &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2)); + reg_906 &= ~((1 << 0)); + break; + + case DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD: + reg_903 = 0x0000; reg_904 = 0x801f; reg_905 = 0x0000; reg_906 = 0x0000; + break; + + case DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD: + reg_903 = 0x0000; reg_904 = 0x8000; reg_905 = 0x010b; reg_906 = 0x0000; + break; + case DIB7000M_POWER_NO: + break; + } + + /* always power down unused parts */ + if (!state->cfg.mobile_mode) + reg_904 |= (1 << 7) | (1 << 6) | (1 << 4) | (1 << 2) | (1 << 1); + + /* P_sdio_select_clk = 0 on MC */ + if (state->revision != 0x4000) + reg_906 <<= 1; + + dib7000m_write_word(state, 903, reg_903); + dib7000m_write_word(state, 904, reg_904); + dib7000m_write_word(state, 905, reg_905); + dib7000m_write_word(state, 906, reg_906); + + return 0; +} + +static int dib7000m_set_adc_state(struct dib7000m_state *state, enum dibx000_adc_states no) +{ + int ret = 0; + u16 reg_913 = dib7000m_read_word(state, 913), + reg_914 = dib7000m_read_word(state, 914); + + switch (no) { + case DIBX000_SLOW_ADC_ON: + reg_914 |= (1 << 1) | (1 << 0); + ret |= dib7000m_write_word(state, 914, reg_914); + reg_914 &= ~(1 << 1); + break; + + case DIBX000_SLOW_ADC_OFF: + reg_914 |= (1 << 1) | (1 << 0); + break; + + case DIBX000_ADC_ON: + if (state->revision == 0x4000) { // workaround for PA/MA + // power-up ADC + dib7000m_write_word(state, 913, 0); + dib7000m_write_word(state, 914, reg_914 & 0x3); + // power-down bandgag + dib7000m_write_word(state, 913, (1 << 15)); + dib7000m_write_word(state, 914, reg_914 & 0x3); + } + + reg_913 &= 0x0fff; + reg_914 &= 0x0003; + break; + + case DIBX000_ADC_OFF: // leave the VBG voltage on + reg_913 |= (1 << 14) | (1 << 13) | (1 << 12); + reg_914 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); + break; + + case DIBX000_VBG_ENABLE: + reg_913 &= ~(1 << 15); + break; + + case DIBX000_VBG_DISABLE: + reg_913 |= (1 << 15); + break; + + default: + break; + } + +// dprintk("-D- 913: %x, 914: %x\n", reg_913, reg_914); + + ret |= dib7000m_write_word(state, 913, reg_913); + ret |= dib7000m_write_word(state, 914, reg_914); + + return ret; +} + +static int dib7000m_set_bandwidth(struct dvb_frontend *demod, u8 bw_idx) +{ + struct dib7000m_state *state = demod->demodulator_priv; + u32 timf; + + // store the current bandwidth for later use + state->current_bandwidth = bw_idx; + + if (state->timf == 0) { + dprintk("-D- Using default timf\n"); + timf = state->cfg.bw->timf; + } else { + dprintk("-D- Using updated timf\n"); + timf = state->timf; + } + + timf = timf * (BW_INDEX_TO_KHZ(bw_idx) / 100) / 80; + + dib7000m_write_word(state, 23, (timf >> 16) & 0xffff); + dib7000m_write_word(state, 24, (timf ) & 0xffff); + + return 0; +} + +static int dib7000m_sad_calib(struct dib7000m_state *state) +{ + +/* internal */ +// dib7000m_write_word(state, 928, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth + dib7000m_write_word(state, 929, (0 << 1) | (0 << 0)); + dib7000m_write_word(state, 930, 776); // 0.625*3.3 / 4096 + + /* do the calibration */ + dib7000m_write_word(state, 929, (1 << 0)); + dib7000m_write_word(state, 929, (0 << 0)); + + msleep(1); + + return 0; +} + +static void dib7000m_reset_pll_common(struct dib7000m_state *state, const struct dibx000_bandwidth_config *bw) +{ + dib7000m_write_word(state, 18, ((bw->internal*1000) >> 16) & 0xffff); + dib7000m_write_word(state, 19, (bw->internal*1000) & 0xffff); + dib7000m_write_word(state, 21, (bw->ifreq >> 16) & 0xffff); + dib7000m_write_word(state, 22, bw->ifreq & 0xffff); + + dib7000m_write_word(state, 928, bw->sad_cfg); +} + +static void dib7000m_reset_pll(struct dib7000m_state *state) +{ + const struct dibx000_bandwidth_config *bw = state->cfg.bw; + u16 reg_907,reg_910; + + /* default */ + reg_907 = (bw->pll_bypass << 15) | (bw->modulo << 7) | + (bw->ADClkSrc << 6) | (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | + (bw->enable_refdiv << 1) | (0 << 0); + reg_910 = (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset; + + // for this oscillator frequency should be 30 MHz for the Master (default values in the board_parameters give that value) + // this is only working only for 30 MHz crystals + if (!state->cfg.quartz_direct) { + reg_910 |= (1 << 5); // forcing the predivider to 1 + + // if the previous front-end is baseband, its output frequency is 15 MHz (prev freq divided by 2) + if(state->cfg.input_clk_is_div_2) + reg_907 |= (16 << 9); + else // otherwise the previous front-end puts out its input (default 30MHz) - no extra division necessary + reg_907 |= (8 << 9); + } else { + reg_907 |= (bw->pll_ratio & 0x3f) << 9; + reg_910 |= (bw->pll_prediv << 5); + } + + dib7000m_write_word(state, 910, reg_910); // pll cfg + dib7000m_write_word(state, 907, reg_907); // clk cfg0 + dib7000m_write_word(state, 908, 0x0006); // clk_cfg1 + + dib7000m_reset_pll_common(state, bw); +} + +static void dib7000mc_reset_pll(struct dib7000m_state *state) +{ + const struct dibx000_bandwidth_config *bw = state->cfg.bw; + + // clk_cfg0 + dib7000m_write_word(state, 907, (bw->pll_prediv << 8) | (bw->pll_ratio << 0)); + + // clk_cfg1 + //dib7000m_write_word(state, 908, (1 << 14) | (3 << 12) |(0 << 11) | + dib7000m_write_word(state, 908, (0 << 14) | (3 << 12) |(0 << 11) | + (bw->IO_CLK_en_core << 10) | (bw->bypclk_div << 5) | (bw->enable_refdiv << 4) | + (bw->pll_bypass << 3) | (bw->pll_range << 1) | (bw->pll_reset << 0)); + + // smpl_cfg + dib7000m_write_word(state, 910, (1 << 12) | (2 << 10) | (bw->modulo << 8) | (bw->ADClkSrc << 7)); + + dib7000m_reset_pll_common(state, bw); +} + +static int dib7000m_reset_gpio(struct dib7000m_state *st) +{ + /* reset the GPIOs */ + dprintk("-D- gpio dir: %x: gpio val: %x, gpio pwm pos: %x\n", + st->cfg.gpio_dir, st->cfg.gpio_val,st->cfg.gpio_pwm_pos); + + dib7000m_write_word(st, 773, st->cfg.gpio_dir); + dib7000m_write_word(st, 774, st->cfg.gpio_val); + + /* TODO 782 is P_gpio_od */ + + dib7000m_write_word(st, 775, st->cfg.gpio_pwm_pos); + + dib7000m_write_word(st, 780, st->cfg.pwm_freq_div); + return 0; +} + +static int dib7000m_demod_reset(struct dib7000m_state *state) +{ + dib7000m_set_power_mode(state, DIB7000M_POWER_ALL); + + /* always leave the VBG voltage on - it consumes almost nothing but takes a long time to start */ + dib7000m_set_adc_state(state, DIBX000_VBG_ENABLE); + + /* restart all parts */ + dib7000m_write_word(state, 898, 0xffff); + dib7000m_write_word(state, 899, 0xffff); + dib7000m_write_word(state, 900, 0xff0f); + dib7000m_write_word(state, 901, 0xfffc); + + dib7000m_write_word(state, 898, 0); + dib7000m_write_word(state, 899, 0); + dib7000m_write_word(state, 900, 0); + dib7000m_write_word(state, 901, 0); + + if (state->revision == 0x4000) + dib7000m_reset_pll(state); + else + dib7000mc_reset_pll(state); + + if (dib7000m_reset_gpio(state) != 0) + dprintk("-E- GPIO reset was not successful.\n"); + + if (dib7000m_set_output_mode(state, OUTMODE_HIGH_Z) != 0) + dprintk("-E- OUTPUT_MODE could not be resetted.\n"); + + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib7000m_write_word(state, 1794, dib7000m_read_word(state, 1794) & ~(1 << 1) ); + + dib7000m_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ); + + dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON); + dib7000m_sad_calib(state); + dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_OFF); + + dib7000m_set_power_mode(state, DIB7000M_POWER_INTERFACE_ONLY); + + return 0; +} + +static void dib7000m_restart_agc(struct dib7000m_state *state) +{ + // P_restart_iqc & P_restart_agc + dib7000m_write_word(state, 898, 0x0c00); + dib7000m_write_word(state, 898, 0x0000); +} + +static int dib7000m_agc_soft_split(struct dib7000m_state *state) +{ + u16 agc,split_offset; + + if(!state->current_agc || !state->current_agc->perform_agc_softsplit || state->current_agc->split.max == 0) + return 0; + + // n_agc_global + agc = dib7000m_read_word(state, 390); + + if (agc > state->current_agc->split.min_thres) + split_offset = state->current_agc->split.min; + else if (agc < state->current_agc->split.max_thres) + split_offset = state->current_agc->split.max; + else + split_offset = state->current_agc->split.max * + (agc - state->current_agc->split.min_thres) / + (state->current_agc->split.max_thres - state->current_agc->split.min_thres); + + dprintk("AGC split_offset: %d\n",split_offset); + + // P_agc_force_split and P_agc_split_offset + return dib7000m_write_word(state, 103, (dib7000m_read_word(state, 103) & 0xff00) | split_offset); +} + +static int dib7000m_update_lna(struct dib7000m_state *state) +{ + int i; + u16 dyn_gain; + + // when there is no LNA to program return immediatly + if (state->cfg.update_lna == NULL) + return 0; + + msleep(60); + for (i = 0; i < 20; i++) { + // read dyn_gain here (because it is demod-dependent and not tuner) + dyn_gain = dib7000m_read_word(state, 390); + + dprintk("agc global: %d\n", dyn_gain); + + if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed + dib7000m_restart_agc(state); + msleep(60); + } else + break; + } + return 0; +} + +static void dib7000m_set_agc_config(struct dib7000m_state *state, u8 band) +{ + struct dibx000_agc_config *agc = NULL; + int i; + if (state->current_band == band) + return; + state->current_band = band; + + for (i = 0; i < state->cfg.agc_config_count; i++) + if (state->cfg.agc[i].band_caps & band) { + agc = &state->cfg.agc[i]; + break; + } + + if (agc == NULL) { + dprintk("-E- No valid AGC configuration found for band 0x%02x\n",band); + return; + } + + state->current_agc = agc; + + /* AGC */ + dib7000m_write_word(state, 72 , agc->setup); + dib7000m_write_word(state, 73 , agc->inv_gain); + dib7000m_write_word(state, 74 , agc->time_stabiliz); + dib7000m_write_word(state, 97 , (agc->alpha_level << 12) | agc->thlock); + + // Demod AGC loop configuration + dib7000m_write_word(state, 98, (agc->alpha_mant << 5) | agc->alpha_exp); + dib7000m_write_word(state, 99, (agc->beta_mant << 6) | agc->beta_exp); + + dprintk("-D- WBD: ref: %d, sel: %d, active: %d, alpha: %d\n", + state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); + + /* AGC continued */ + if (state->wbd_ref != 0) + dib7000m_write_word(state, 102, state->wbd_ref); + else // use default + dib7000m_write_word(state, 102, agc->wbd_ref); + + dib7000m_write_word(state, 103, (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) ); + dib7000m_write_word(state, 104, agc->agc1_max); + dib7000m_write_word(state, 105, agc->agc1_min); + dib7000m_write_word(state, 106, agc->agc2_max); + dib7000m_write_word(state, 107, agc->agc2_min); + dib7000m_write_word(state, 108, (agc->agc1_pt1 << 8) | agc->agc1_pt2 ); + dib7000m_write_word(state, 109, (agc->agc1_slope1 << 8) | agc->agc1_slope2); + dib7000m_write_word(state, 110, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + dib7000m_write_word(state, 111, (agc->agc2_slope1 << 8) | agc->agc2_slope2); + + if (state->revision > 0x4000) { // settings for the MC + dib7000m_write_word(state, 71, agc->agc1_pt3); +// dprintk("-D- 929: %x %d %d\n", +// (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2), agc->wbd_inv, agc->wbd_sel); + dib7000m_write_word(state, 929, (dib7000m_read_word(state, 929) & 0xffe3) | (agc->wbd_inv << 4) | (agc->wbd_sel << 2)); + } else { + // wrong default values + u16 b[9] = { 676, 696, 717, 737, 758, 778, 799, 819, 840 }; + for (i = 0; i < 9; i++) + dib7000m_write_word(state, 88 + i, b[i]); + } +} + +static void dib7000m_update_timf_freq(struct dib7000m_state *state) +{ + u32 timf = (dib7000m_read_word(state, 436) << 16) | dib7000m_read_word(state, 437); + state->timf = timf * 80 / (BW_INDEX_TO_KHZ(state->current_bandwidth) / 100); + dib7000m_write_word(state, 23, (u16) (timf >> 16)); + dib7000m_write_word(state, 24, (u16) (timf & 0xffff)); + dprintk("-D- Updated timf_frequency: %d (default: %d)\n",state->timf, state->cfg.bw->timf); +} + +static void dib7000m_set_channel(struct dib7000m_state *state, struct dibx000_ofdm_channel *ch, u8 seq) +{ + u16 value, est[4]; + + dib7000m_set_agc_config(state, BAND_OF_FREQUENCY(ch->RF_kHz)); + + /* nfft, guard, qam, alpha */ + dib7000m_write_word(state, 0, (ch->nfft << 7) | (ch->guard << 5) | (ch->nqam << 3) | (ch->vit_alpha)); + dib7000m_write_word(state, 5, (seq << 4)); + + /* P_dintl_native, P_dintlv_inv, P_vit_hrch, P_vit_code_rate, P_vit_select_hp */ + value = (ch->intlv_native << 6) | (ch->vit_hrch << 4) | (ch->vit_select_hp & 0x1); + if (ch->vit_hrch == 0 || ch->vit_select_hp == 1) + value |= (ch->vit_code_rate_hp << 1); + else + value |= (ch->vit_code_rate_lp << 1); + dib7000m_write_word(state, 267 + state->reg_offs, value); + + /* offset loop parameters */ + + /* P_timf_alpha = 6, P_corm_alpha=6, P_corm_thres=0x80 */ + dib7000m_write_word(state, 26, (6 << 12) | (6 << 8) | 0x80); + + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=1, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ + dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (1 << 9) | (3 << 5) | (1 << 4) | (0x3)); + + /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max=3 */ + dib7000m_write_word(state, 32, (0 << 4) | 0x3); + + /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step=5 */ + dib7000m_write_word(state, 33, (0 << 4) | 0x5); + + /* P_dvsy_sync_wait */ + switch (ch->nfft) { + case 1: value = 256; break; + case 2: value = 128; break; + case 0: + default: value = 64; break; + } + value *= ((1 << (ch->guard)) * 3 / 2); // add 50% SFN margin + value <<= 4; + + /* deactive the possibility of diversity reception if extended interleave - not for 7000MC */ + /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ + if (ch->intlv_native || state->revision > 0x4000) + value |= (1 << 2) | (2 << 0); + else + value |= 0; + dib7000m_write_word(state, 266 + state->reg_offs, value); + + /* channel estimation fine configuration */ + switch (ch->nqam) { + case 2: + est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ + est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ + break; + case 1: + est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ + est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ + break; + default: + est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ + est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ + est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ + break; + } + for (value = 0; value < 4; value++) + dib7000m_write_word(state, 214 + value + state->reg_offs, est[value]); + + // set power-up level: interf+analog+AGC + dib7000m_set_power_mode(state, DIB7000M_POWER_INTERF_ANALOG_AGC); + dib7000m_set_adc_state(state, DIBX000_ADC_ON); + + msleep(7); + + //AGC initialization + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 1); + + dib7000m_restart_agc(state); + + // wait AGC rough lock time + msleep(5); + + dib7000m_update_lna(state); + dib7000m_agc_soft_split(state); + + // wait AGC accurate lock time + msleep(7); + + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 0); + + // set power-up level: autosearch + dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_DINTLV_ICIRM_EQUAL_CFROD); +} + +static int dib7000m_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch) +{ + struct dib7000m_state *state = demod->demodulator_priv; + struct dibx000_ofdm_channel auto_ch; + int ret = 0; + u8 seq; + u32 value; + + INIT_OFDM_CHANNEL(&auto_ch); + auto_ch.RF_kHz = ch->RF_kHz; + auto_ch.Bw = ch->Bw; + auto_ch.nqam = 2; + auto_ch.guard = ch->guard == GUARD_INTERVAL_AUTO ? 0 : ch->guard; + auto_ch.nfft = ch->nfft == -1 ? 1 : ch->nfft; + auto_ch.vit_alpha = 1; + auto_ch.vit_select_hp = 1; + auto_ch.vit_code_rate_hp = 2; + auto_ch.vit_code_rate_lp = 3; + auto_ch.vit_hrch = 0; + auto_ch.intlv_native = 1; + + seq = 0; + if (ch->nfft == -1 && ch->guard == GUARD_INTERVAL_AUTO) seq = 7; + if (ch->nfft == -1 && ch->guard != GUARD_INTERVAL_AUTO) seq = 2; + if (ch->nfft != -1 && ch->guard == GUARD_INTERVAL_AUTO) seq = 3; + dib7000m_set_channel(state, &auto_ch, seq); + + // always use the setting for 8MHz here lock_time for 7,6 MHz are longer + value = 30 * state->cfg.bw[BANDWIDTH_8_MHZ].internal; + ret |= dib7000m_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time + ret |= dib7000m_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time + value = 100 * state->cfg.bw[BANDWIDTH_8_MHZ].internal; + ret |= dib7000m_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time + ret |= dib7000m_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time + value = 500 * state->cfg.bw[BANDWIDTH_8_MHZ].internal; + ret |= dib7000m_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time + ret |= dib7000m_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time + + // start search + value = dib7000m_read_word(state, 0); + ret |= dib7000m_write_word(state, 0, value | (1 << 9)); + + /* clear n_irq_pending */ + if (state->revision == 0x4000) + dib7000m_write_word(state, 1793, 0); + else + dib7000m_read_word(state, 537); + + ret |= dib7000m_write_word(state, 0, (u16) value); + + return ret; +} + +static int dib7000m_autosearch_irq(struct dib7000m_state *state, u16 reg) +{ + u16 irq_pending = dib7000m_read_word(state, reg); + + if (irq_pending & 0x1) { // failed + dprintk("#\n"); + return 1; + } + + if (irq_pending & 0x2) { // succeeded + dprintk("!\n"); + return 2; + } + return 0; // still pending +} + +static int dib7000m_autosearch_is_irq(struct dvb_frontend *demod) +{ + struct dib7000m_state *state = demod->demodulator_priv; + if (state->revision == 0x4000) + return dib7000m_autosearch_irq(state, 1793); + else + return dib7000m_autosearch_irq(state, 537); +} + +static int dib7000m_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch) +{ + struct dib7000m_state *state = demod->demodulator_priv; + int ret = 0; + u16 value; + + // we are already tuned - just resuming from suspend + if (ch != NULL) + dib7000m_set_channel(state, ch, 0); + else + return -EINVAL; + + // restart demod + ret |= dib7000m_write_word(state, 898, 0x4000); + ret |= dib7000m_write_word(state, 898, 0x0000); + msleep(45); + + ret |= dib7000m_set_power_mode(state, DIB7000M_POWER_COR4_CRY_ESRAM_MOUT_NUD); + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ + ret |= dib7000m_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3)); + + // never achieved a lock with that bandwidth so far - wait for timfreq to update + if (state->timf == 0) + msleep(200); + + //dump_reg(state); + /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */ + value = (6 << 8) | 0x80; + switch (ch->nfft) { + case 0: value |= (7 << 12); break; + case 1: value |= (9 << 12); break; + case 2: value |= (8 << 12); break; + } + ret |= dib7000m_write_word(state, 26, value); + + /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */ + value = (0 << 4); + switch (ch->nfft) { + case 0: value |= 0x6; break; + case 1: value |= 0x8; break; + case 2: value |= 0x7; break; + } + ret |= dib7000m_write_word(state, 32, value); + + /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */ + value = (0 << 4); + switch (ch->nfft) { + case 0: value |= 0x6; break; + case 1: value |= 0x8; break; + case 2: value |= 0x7; break; + } + ret |= dib7000m_write_word(state, 33, value); + + // we achieved a lock - it's time to update the osc freq + if ((dib7000m_read_word(state, 535) >> 6) & 0x1) + dib7000m_update_timf_freq(state); + + return ret; +} + +static int dib7000m_init(struct dvb_frontend *demod) +{ + struct dib7000m_state *state = demod->demodulator_priv; + int ret = 0; + u8 o = state->reg_offs; + + dib7000m_set_power_mode(state, DIB7000M_POWER_ALL); + + if (dib7000m_set_adc_state(state, DIBX000_SLOW_ADC_ON) != 0) + dprintk("-E- could not start Slow ADC\n"); + + if (state->cfg.dvbt_mode) + dib7000m_write_word(state, 1796, 0x0); // select DVB-T output + + if (state->cfg.mobile_mode) + ret |= dib7000m_write_word(state, 261 + o, 2); + else + ret |= dib7000m_write_word(state, 224 + o, 1); + + ret |= dib7000m_write_word(state, 173 + o, 0); + ret |= dib7000m_write_word(state, 174 + o, 0); + ret |= dib7000m_write_word(state, 175 + o, 0); + ret |= dib7000m_write_word(state, 176 + o, 0); + ret |= dib7000m_write_word(state, 177 + o, 0); + ret |= dib7000m_write_word(state, 178 + o, 0); + ret |= dib7000m_write_word(state, 179 + o, 0); + ret |= dib7000m_write_word(state, 180 + o, 0); + + // P_corm_thres Lock algorithms configuration + ret |= dib7000m_write_word(state, 26, 0x6680); + + // P_palf_alpha_regul, P_palf_filter_freeze, P_palf_filter_on + ret |= dib7000m_write_word(state, 170 + o, 0x0410); + // P_fft_nb_to_cut + ret |= dib7000m_write_word(state, 182 + o, 8192); + // P_pha3_thres + ret |= dib7000m_write_word(state, 195 + o, 0x0ccd); + // P_cti_use_cpe, P_cti_use_prog + ret |= dib7000m_write_word(state, 196 + o, 0); + // P_cspu_regul, P_cspu_win_cut + ret |= dib7000m_write_word(state, 205 + o, 0x200f); + // P_adp_regul_cnt + ret |= dib7000m_write_word(state, 214 + o, 0x023d); + // P_adp_noise_cnt + ret |= dib7000m_write_word(state, 215 + o, 0x00a4); + // P_adp_regul_ext + ret |= dib7000m_write_word(state, 216 + o, 0x00a4); + // P_adp_noise_ext + ret |= dib7000m_write_word(state, 217 + o, 0x7ff0); + // P_adp_fil + ret |= dib7000m_write_word(state, 218 + o, 0x3ccc); + + // P_2d_byp_ti_num + ret |= dib7000m_write_word(state, 226 + o, 0); + + // P_fec_* + ret |= dib7000m_write_word(state, 281 + o, 0x0010); + // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard + ret |= dib7000m_write_word(state, 294 + o,0x0062); + + // P_iqc_alpha_pha, P_iqc_alpha_amp, P_iqc_dcc_alpha, ... + if(state->cfg.tuner_is_baseband) + ret |= dib7000m_write_word(state, 36, 0x0755); + else + ret |= dib7000m_write_word(state, 36, 0x1f55); + + // auto search configuration + ret |= dib7000m_write_word(state, 2, 0x0004); + ret |= dib7000m_write_word(state, 3, 0x1000); + ret |= dib7000m_write_word(state, 4, 0x0814); + ret |= dib7000m_write_word(state, 6, 0x001b); + ret |= dib7000m_write_word(state, 7, 0x7740); + ret |= dib7000m_write_word(state, 8, 0x005b); + ret |= dib7000m_write_word(state, 9, 0x8d80); + ret |= dib7000m_write_word(state, 10, 0x01c9); + ret |= dib7000m_write_word(state, 11, 0xc380); + ret |= dib7000m_write_word(state, 12, 0x0000); + ret |= dib7000m_write_word(state, 13, 0x0080); + ret |= dib7000m_write_word(state, 14, 0x0000); + ret |= dib7000m_write_word(state, 15, 0x0090); + ret |= dib7000m_write_word(state, 16, 0x0001); + ret |= dib7000m_write_word(state, 17, 0xd4c0); + ret |= dib7000m_write_word(state, 263 + o,0x0001); + + // P_divclksel=3 P_divbitsel=1 + if (state->revision == 0x4000) + dib7000m_write_word(state, 909, (3 << 10) | (1 << 6)); + else + dib7000m_write_word(state, 909, (3 << 4) | 1); + + // Tuner IO bank: max drive (14mA) + ret |= dib7000m_write_word(state, 912 ,0x2c8a); + + ret |= dib7000m_write_word(state, 1817, 1); + + return ret; +} + +static int dib7000m_sleep(struct dvb_frontend *demod) +{ + struct dib7000m_state *st = demod->demodulator_priv; + dib7000m_set_output_mode(st, OUTMODE_HIGH_Z); + return dib7000m_set_power_mode(st, DIB7000M_POWER_INTERFACE_ONLY) | + dib7000m_set_adc_state(st, DIBX000_SLOW_ADC_OFF) | + dib7000m_set_adc_state(st, DIBX000_ADC_OFF); +} + +static int dib7000m_identify(struct dib7000m_state *state) +{ + u16 value; + if ((value = dib7000m_read_word(state, 896)) != 0x01b3) { + dprintk("-E- DiB7000M: wrong Vendor ID (read=0x%x)\n",value); + return -EREMOTEIO; + } + + state->revision = dib7000m_read_word(state, 897); + if (state->revision != 0x4000 && + state->revision != 0x4001 && + state->revision != 0x4002) { + dprintk("-E- DiB7000M: wrong Device ID (%x)\n",value); + return -EREMOTEIO; + } + + /* protect this driver to be used with 7000PC */ + if (state->revision == 0x4000 && dib7000m_read_word(state, 769) == 0x4000) { + dprintk("-E- DiB7000M: this driver does not work with DiB7000PC\n"); + return -EREMOTEIO; + } + + switch (state->revision) { + case 0x4000: dprintk("-I- found DiB7000MA/PA/MB/PB\n"); break; + case 0x4001: state->reg_offs = 1; dprintk("-I- found DiB7000HC\n"); break; + case 0x4002: state->reg_offs = 1; dprintk("-I- found DiB7000MC\n"); break; + } + + return 0; +} + static int dib7000m_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) @@ -133,13 +970,48 @@ static int dib7000m_get_frontend(struct dvb_frontend* fe, static int dib7000m_set_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep) { - return 0; + struct dib7000m_state *state = fe->demodulator_priv; + struct dibx000_ofdm_channel ch; + + INIT_OFDM_CHANNEL(&ch); + FEP2DIB(fep,&ch); + + state->current_bandwidth = fep->u.ofdm.bandwidth; + dib7000m_set_bandwidth(fe, fep->u.ofdm.bandwidth); + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, fep); + + if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO || + fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || + fep->u.ofdm.constellation == QAM_AUTO || + fep->u.ofdm.code_rate_HP == FEC_AUTO) { + int i = 800, found; + + dib7000m_autosearch_start(fe, &ch); + do { + msleep(1); + found = dib7000m_autosearch_is_irq(fe); + } while (found == 0 && i--); + + dprintk("autosearch returns: %d\n",found); + if (found == 0 || found == 1) + return 0; // no channel found + + dib7000m_get_frontend(fe, fep); + FEP2DIB(fep, &ch); + } + + /* make this a config parameter */ + dib7000m_set_output_mode(state, OUTMODE_MPEG2_FIFO); + + return dib7000m_tune(fe, &ch); } static int dib7000m_read_status(struct dvb_frontend *fe, fe_status_t *stat) { struct dib7000m_state *state = fe->demodulator_priv; - u16 lock = dib7000m_read_word(state, 509); + u16 lock = dib7000m_read_word(state, 535); *stat = 0; @@ -191,18 +1063,100 @@ static int dib7000m_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_fro return 0; } -static int dib7000m_init(struct dvb_frontend *fe) +static void dib7000m_release(struct dvb_frontend *demod) { - return 0; + struct dib7000m_state *st = demod->demodulator_priv; + dibx000_exit_i2c_master(&st->i2c_master); + kfree(st); +} + +struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) +{ + struct dib7000m_state *st = demod->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); } +EXPORT_SYMBOL(dib7000m_get_i2c_master); -static int dib7000m_sleep(struct dvb_frontend *fe) +int dib7000m_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000m_config cfg[]) { + struct dib7000m_state st = { .i2c_adap = i2c }; + int k = 0; + u8 new_addr = 0; + + for (k = no_of_demods-1; k >= 0; k--) { + st.cfg = cfg[k]; + + /* designated i2c address */ + new_addr = (0x40 + k) << 1; + st.i2c_addr = new_addr; + if (dib7000m_identify(&st) != 0) { + st.i2c_addr = default_addr; + if (dib7000m_identify(&st) != 0) { + dprintk("DiB7000M #%d: not identified\n", k); + return -EIO; + } + } + + /* start diversity to pull_down div_str - just for i2c-enumeration */ + dib7000m_set_output_mode(&st, OUTMODE_DIVERSITY); + + dib7000m_write_word(&st, 1796, 0x0); // select DVB-T output + + /* set new i2c address and force divstart */ + dib7000m_write_word(&st, 1794, (new_addr << 2) | 0x2); + + dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr); + } + + for (k = 0; k < no_of_demods; k++) { + st.cfg = cfg[k]; + st.i2c_addr = (0x40 + k) << 1; + + // unforce divstr + dib7000m_write_word(&st,1794, st.i2c_addr << 2); + + /* deactivate div - it was just for i2c-enumeration */ + dib7000m_set_output_mode(&st, OUTMODE_HIGH_Z); + } + return 0; } +EXPORT_SYMBOL(dib7000m_i2c_enumeration); -static void dib7000m_release(struct dvb_frontend *fe) -{ } +static struct dvb_frontend_ops dib7000m_ops; +struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg) +{ + struct dvb_frontend *demod; + struct dib7000m_state *st; + st = kzalloc(sizeof(struct dib7000m_state), GFP_KERNEL); + if (st == NULL) + return NULL; + + memcpy(&st->cfg, cfg, sizeof(struct dib7000m_config)); + st->i2c_adap = i2c_adap; + st->i2c_addr = i2c_addr; + + demod = &st->demod; + demod->demodulator_priv = st; + memcpy(&st->demod.ops, &dib7000m_ops, sizeof(struct dvb_frontend_ops)); + + if (dib7000m_identify(st) != 0) + goto error; + + if (st->revision == 0x4000) + dibx000_init_i2c_master(&st->i2c_master, DIB7000, st->i2c_adap, st->i2c_addr); + else + dibx000_init_i2c_master(&st->i2c_master, DIB7000MC, st->i2c_adap, st->i2c_addr); + + dib7000m_demod_reset(st); + + return demod; + +error: + kfree(st); + return NULL; +} +EXPORT_SYMBOL(dib7000m_attach); static struct dvb_frontend_ops dib7000m_ops = { .info = { diff --git a/drivers/media/dvb/frontends/dib7000m.h b/drivers/media/dvb/frontends/dib7000m.h index 45990e3..597e9cc 100644 --- a/drivers/media/dvb/frontends/dib7000m.h +++ b/drivers/media/dvb/frontends/dib7000m.h @@ -32,12 +32,13 @@ struct dib7000m_config { u8 quartz_direct; u8 input_clk_is_div_2; + + int (*agc_control) (struct dvb_frontend *, u8 before); }; #define DEFAULT_DIB7000M_I2C_ADDRESS 18 -extern int dib7000m_attach(struct i2c_adapter *i2c_adap, int no_of_demods, u8 default_addr , u8 do_i2c_enum, struct dib7000m_config[], struct dvb_frontend*[]); - +extern struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg); extern struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); /* TODO diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c index a18c8f4..61d28de 100644 --- a/drivers/media/dvb/frontends/dibx000_common.c +++ b/drivers/media/dvb/frontends/dibx000_common.c @@ -63,7 +63,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/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h index bb0c65f..a1df604 100644 --- a/drivers/media/dvb/frontends/dibx000_common.h +++ b/drivers/media/dvb/frontends/dibx000_common.h @@ -32,6 +32,13 @@ extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst); #define BAND_LBAND 0x01 #define BAND_UHF 0x02 #define BAND_VHF 0x04 +#define BAND_SBAND 0x08 +#define BAND_FM 0x10 + +#define BAND_OF_FREQUENCY(freq_kHz) ( (freq_kHz) <= 115000 ? BAND_FM : \ + (freq_kHz) <= 250000 ? BAND_VHF : \ + (freq_kHz) <= 863000 ? BAND_UHF : \ + (freq_kHz) <= 2000000 ? BAND_LBAND : BAND_SBAND ) struct dibx000_agc_config { /* defines the capabilities of this AGC-setting - using the BAND_-defines*/ @@ -129,6 +136,7 @@ enum dibx000_adc_states { /* I hope I can get rid of the following kludge in the near future */ struct dibx000_ofdm_channel { + u32 RF_kHz; u8 Bw; s16 nfft; s16 guard; @@ -138,9 +146,11 @@ struct dibx000_ofdm_channel { s16 vit_alpha; s16 vit_code_rate_hp; s16 vit_code_rate_lp; + u8 intlv_native; }; #define FEP2DIB(fep,ch) \ + (ch)->RF_kHz = (fep)->frequency / 1000; \ (ch)->Bw = (fep)->u.ofdm.bandwidth; \ (ch)->nfft = (fep)->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ? -1 : (fep)->u.ofdm.transmission_mode; \ (ch)->guard = (fep)->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ? -1 : (fep)->u.ofdm.guard_interval; \ @@ -149,7 +159,8 @@ struct dibx000_ofdm_channel { (ch)->vit_select_hp = 1; \ (ch)->vit_alpha = 1; \ (ch)->vit_code_rate_hp = (fep)->u.ofdm.code_rate_HP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_HP; \ - (ch)->vit_code_rate_lp = (fep)->u.ofdm.code_rate_LP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_LP; + (ch)->vit_code_rate_lp = (fep)->u.ofdm.code_rate_LP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_LP; \ + (ch)->intlv_native = 1; #define INIT_OFDM_CHANNEL(ch) do {\ (ch)->Bw = 0; \ -- cgit v0.10.2 From eb591af3a3703cb79e3efb5ad87b36dcd32b0712 Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Thu, 12 Oct 2006 19:46:16 -0300 Subject: V4L/DVB (4767): Make Remote control of the Pinnacle PCTV 310i work The remote control works, but we still need a better keymap. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 0548bd3..83887d1 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -336,6 +336,7 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir) switch (dev->board) { case SAA7134_BOARD_PINNACLE_PCTV_110i: + case SAA7134_BOARD_PINNACLE_PCTV_310i: snprintf(ir->c.name, sizeof(ir->c.name), "Pinnacle PCTV"); if (pinnacle_remote == 0) { ir->get_key = get_key_pinnacle_color; -- cgit v0.10.2 From 7da6894a96ebbc1505763213c42b8fcf66aa3d90 Mon Sep 17 00:00:00 2001 From: Hermann Pitton Date: Thu, 12 Oct 2006 20:00:56 -0300 Subject: V4L/DVB (4768): Add the Asus P7131 Dual hybrid to the new tda8290_i2c_gate_ctrl The gate control was moved to a separate function. Signed-off-by: Hermann Pitton Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 88e4b0f..69092f0 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -1210,6 +1210,7 @@ static int dvb_init(struct saa7134_dev *dev) &philips_tiger_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 = asus_p7131_dual_tuner_init; dev->dvb.frontend->ops.tuner_ops.sleep = asus_p7131_dual_tuner_sleep; dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; -- cgit v0.10.2 From 8364681766cc18b948cf9d7bd46d1b92f343743b Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Thu, 12 Oct 2006 20:38:51 -0300 Subject: V4L/DVB (4769): Added support for a ASUSTEK P7131 Dual DVB-T variant This card has no firmware eeprom. The old version still should not need a firmware file due to an undocumented feature of the TDA10046. The patch also includes Hermann Pitton's proposal for improved antenna switch handling Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 8be8f97..a9bffd9 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -76,7 +76,7 @@ 75 -> AVerMedia AVerTVHD MCE A180 [1461:1044] 76 -> SKNet MonsterTV Mobile [1131:4ee9] 77 -> Pinnacle PCTV 40i/50i/110i (saa7133) [11bd:002e] - 78 -> ASUSTeK P7131 Dual [1043:4862] + 78 -> ASUSTeK P7131 Dual [1043:4862,1043:4876] 79 -> Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B) 80 -> ASUS Digimatrix TV [1043:0210] 81 -> Philips Tiger reference design [1131:2018] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 2a00cb4..8109c8e 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -2462,14 +2462,17 @@ struct saa7134_board saa7134_boards[] = { .vmux = 1, .amux = TV, .tv = 1, + .gpio = 0x0000000, },{ .name = name_comp1, .vmux = 3, .amux = LINE2, + .gpio = 0x0200000, },{ .name = name_svideo, .vmux = 8, .amux = LINE2, + .gpio = 0x0200000, }}, .radio = { .name = name_radio, @@ -3719,6 +3722,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x9715, .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4876, + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 69092f0..706450c 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -894,6 +894,16 @@ static struct tda1004x_config pinnacle_pctv_310i_config = { /* ------------------------------------------------------------------ */ +static struct tda1004x_config asus_p7131_dual_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .if_freq = TDA10046_FREQ_045, + .request_firmware = philips_tda1004x_request_firmware, +}; + static int asus_p7131_dual_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; @@ -1207,7 +1217,7 @@ static int dvb_init(struct saa7134_dev *dev) break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: dev->dvb.frontend = dvb_attach(tda10046_attach, - &philips_tiger_config, + &asus_p7131_dual_config, &dev->i2c_adap); if (dev->dvb.frontend) { dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; -- cgit v0.10.2 From a75763ffff4175bd8c115c217a39fbf445612aae Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Wed, 18 Oct 2006 08:34:16 -0300 Subject: V4L/DVB (4772): Add support for DiBcom DiB7000PC This patch contains support for the DiB7000PC-driver. Signed-off-by: Francois KANOUNNIKOFF Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index a0aeaf4..dfd53d5 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -69,6 +69,7 @@ config DVB_USB_DIBUSB_MC config DVB_USB_DIB0700 tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)" depends on DVB_USB + select DVB_DIB7000P select DVB_DIB7000M select DVB_DIB3000MC select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h index ac84347..cda3ade 100644 --- a/drivers/media/dvb/dvb-usb/dib0700.h +++ b/drivers/media/dvb/dvb-usb/dib0700.h @@ -24,18 +24,23 @@ extern int dvb_usb_dib0700_debug; #define REQUEST_I2C_WRITE 0x3 #define REQUEST_POLL_RC 0x4 #define REQUEST_JUMPRAM 0x8 +#define REQUEST_SET_CLOCK 0xB #define REQUEST_SET_GPIO 0xC #define REQUEST_ENABLE_VIDEO 0xF // 1 Byte: 4MSB(1 = enable streaming, 0 = disable streaming) 4LSB(Video Mode: 0 = MPEG2 188Bytes, 1 = Analog) // 2 Byte: MPEG2 mode: 4MSB(1 = Master Mode, 0 = Slave Mode) 4LSB(Channel 1 = bit0, Channel 2 = bit1) // 2 Byte: Analog mode: 4MSB(0 = 625 lines, 1 = 525 lines) 4LSB( " " ) +#define REQUEST_GET_VERSION 0x15 struct dib0700_state { u8 channel_state; u16 mt2060_if1[2]; + + u8 is_dib7000pc; }; extern int dib0700_set_gpio(struct dvb_usb_device *, enum dib07x0_gpios gpio, u8 gpio_dir, u8 gpio_val); +extern int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3); extern int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw); extern int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff); extern struct i2c_algorithm dib0700_i2c_algo; diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index dca6c69..6a4d150 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -135,14 +135,46 @@ struct i2c_algorithm dib0700_i2c_algo = { int dib0700_identify_state(struct usb_device *udev, struct dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, int *cold) { - u8 buf[3] = { REQUEST_SET_GPIO, 4, (GPIO_IN << 7) | (0 << 6) }; // GPIO4 is save - used for I2C - *cold = usb_control_msg(udev, usb_sndctrlpipe(udev,0), - buf[0], USB_TYPE_VENDOR | USB_DIR_OUT, 0, 0, buf, 3, USB_CTRL_GET_TIMEOUT) != 3; + u8 b[16]; + s16 ret = usb_control_msg(udev, usb_rcvctrlpipe(udev,0), + REQUEST_GET_VERSION, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, b, 16, USB_CTRL_GET_TIMEOUT); + + deb_info("FW GET_VERSION length: %d\n",ret); + + *cold = ret <= 0; deb_info("cold: %d\n", *cold); return 0; } +static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll, + u8 pll_src, u8 pll_range, u8 clock_gpio3, u16 pll_prediv, + u16 pll_loopdiv, u16 free_div, u16 dsuScaler) +{ + u8 b[10]; + b[0] = REQUEST_SET_CLOCK; + b[1] = (en_pll << 7) | (pll_src << 6) | (pll_range << 5) | (clock_gpio3 << 4); + b[2] = (pll_prediv >> 8) & 0xff; // MSB + b[3] = pll_prediv & 0xff; // LSB + b[4] = (pll_loopdiv >> 8) & 0xff; // MSB + b[5] = pll_loopdiv & 0xff; // LSB + b[6] = (free_div >> 8) & 0xff; // MSB + b[7] = free_div & 0xff; // LSB + b[8] = (dsuScaler >> 8) & 0xff; // MSB + b[9] = dsuScaler & 0xff; // LSB + + return dib0700_ctrl_wr(d, b, 10); +} + +int dib0700_ctrl_clock(struct dvb_usb_device *d, u32 clk_MHz, u8 clock_out_gp3) +{ + switch (clk_MHz) { + case 72: dib0700_set_clock(d, 1, 0, 1, clock_out_gp3, 2, 24, 0, 0x4c); break; + default: return -EINVAL; + } + return 0; +} + static int dib0700_jumpram(struct usb_device *udev, u32 address) { int ret, actlen; @@ -197,7 +229,7 @@ int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw /* start the firmware */ if ((ret = dib0700_jumpram(udev, 0x70000000)) == 0) { info("firmware started successfully."); - msleep(100); + msleep(500); } } else ret = -EIO; diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 305dd35..c231a1e 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -10,6 +10,7 @@ #include "dib3000mc.h" #include "dib7000m.h" +#include "dib7000p.h" #include "mt2060.h" static int force_lna_activation; @@ -96,7 +97,7 @@ static int bristol_tuner_attach(struct dvb_usb_adapter *adap) } /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */ -static struct dibx000_agc_config stk7700p_dib7000m_agc_config = { +static struct dibx000_agc_config stk7700p_7000m_mt2060_agc_config = { BAND_UHF | BAND_VHF, // band_caps /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, @@ -141,7 +142,52 @@ static struct dibx000_agc_config stk7700p_dib7000m_agc_config = { }, }; -static struct dibx000_bandwidth_config stk7700p_dib7000m_mt2060_config = { +static struct dibx000_agc_config stk7700p_7000p_mt2060_agc_config = { + BAND_UHF | BAND_VHF, + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=5, P_agc_inv_pwm1=0, P_agc_inv_pwm2=0, + * P_agc_inh_dc_rv_est=0, P_agc_time_est=3, P_agc_freeze=0, P_agc_nb_est=2, P_agc_write=0 */ + (0 << 15) | (0 << 14) | (5 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), // setup + + 712, // inv_gain + 41, // time_stabiliz + + 0, // alpha_level + 118, // thlock + + 0, // wbd_inv + 4095, // wbd_ref + 0, // wbd_sel + 0, // wbd_alpha + + 42598, // agc1_max + 16384, // agc1_min + 42598, // agc2_max + 0, // agc2_min + + 0, // agc1_pt1 + 137, // agc1_pt2 + 255, // agc1_pt3 + + 0, // agc1_slope1 + 255, // agc1_slope2 + + 0, // agc2_pt1 + 0, // agc2_pt2 + + 0, // agc2_slope1 + 41, // agc2_slope2 + + 15, // alpha_mant + 25, // alpha_exp + + 28, // beta_mant + 48, // beta_exp + + 0, // perform_agc_softsplit +}; + +static struct dibx000_bandwidth_config stk7700p_pll_config = { 60000, 30000, // internal, sampling 1, 8, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass 0, 0, 1, 1, 0, // misc: refdiv, bypclk_div, IO_CLK_en_core, ADClkSrc, modulo @@ -156,8 +202,19 @@ static struct dib7000m_config stk7700p_dib7000m_config = { .quartz_direct = 1, .agc_config_count = 1, - .agc = &stk7700p_dib7000m_agc_config, - .bw = &stk7700p_dib7000m_mt2060_config, + .agc = &stk7700p_7000m_mt2060_agc_config, + .bw = &stk7700p_pll_config, + + .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000M_GPIO_DEFAULT_PWM_POS, +}; + +static struct dib7000p_config stk7700p_dib7000p_config = { + .output_mpeg2_in_188_bytes = 1, + + .agc = &stk7700p_7000p_mt2060_agc_config, + .bw = &stk7700p_pll_config, .gpio_dir = DIB7000M_GPIO_DEFAULT_DIRECTIONS, .gpio_val = DIB7000M_GPIO_DEFAULT_VALUES, @@ -168,13 +225,28 @@ static int stk7700p_frontend_attach(struct dvb_usb_adapter *adap) { struct dib0700_state *st = adap->dev->priv; /* unless there is no real power management in DVB - we leave the device on GPIO6 */ - dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(10); + + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0); msleep(50); + dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1); msleep(10); + dib0700_set_gpio(adap->dev, GPIO9, GPIO_OUT, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0); msleep(10); - dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(10); + dib0700_ctrl_clock(adap->dev, 72, 1); + dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1); msleep(100); + + dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1); st->mt2060_if1[0] = 1220; - return (adap->fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config)) == NULL ? -ENODEV : 0; + + if (dib7000pc_detection(&adap->dev->i2c_adap)) { + adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000p_config); + st->is_dib7000pc = 1; + } else + adap->fe = dvb_attach(dib7000m_attach, &adap->dev->i2c_adap, 18, &stk7700p_dib7000m_config); + + return adap->fe == NULL ? -ENODEV : 0; } static struct mt2060_config stk7700p_mt2060_config = { @@ -184,8 +256,14 @@ static struct mt2060_config stk7700p_mt2060_config = { static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap) { struct dib0700_state *st = adap->dev->priv; - struct i2c_adapter *tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); - return dvb_attach(mt2060_attach,adap->fe, tun_i2c, &stk7700p_mt2060_config, + struct i2c_adapter *tun_i2c; + + if (st->is_dib7000pc) + tun_i2c = dib7000p_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + else + tun_i2c = dib7000m_get_i2c_master(adap->fe, DIBX000_I2C_INTERFACE_TUNER, 1); + + return dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk7700p_mt2060_config, st->mt2060_if1[0]) == NULL ? -ENODEV : 0; } diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 093c769..7279e58 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -180,6 +180,14 @@ config DVB_DIB7000M A DVB-T tuner module. Designed for mobile usage. Say Y when you want to support this frontend. +config DVB_DIB7000P + tristate "DiBcom 7000PC" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-T tuner module. Designed for mobile usage. Say Y when you want + to support this frontend. + comment "DVB-C (cable) frontends" depends on DVB_CORE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 244e874..593152e 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_DVB_L64781) += l64781.o obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o obj-$(CONFIG_DVB_DIB7000M) += dib7000m.o dibx000_common.o +obj-$(CONFIG_DVB_DIB7000P) += dib7000p.o dibx000_common.o obj-$(CONFIG_DVB_MT312) += mt312.o obj-$(CONFIG_DVB_VES1820) += ves1820.o obj-$(CONFIG_DVB_VES1X93) += ves1x93.o diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c new file mode 100644 index 0000000..0349a4b --- /dev/null +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -0,0 +1,1019 @@ +/* + * Linux-DVB Driver for DiBcom's second generation DiB7000P (PC). + * + * Copyright (C) 2005-6 DiBcom (http://www.dibcom.fr/) + * + * 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. + */ +#include +#include + +#include "dvb_frontend.h" + +#include "dib7000p.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "turn on debugging (default: 0)"); + +#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB7000P:"); printk(args); } } while (0) + +struct dib7000p_state { + struct dvb_frontend demod; + struct dib7000p_config cfg; + + u8 i2c_addr; + struct i2c_adapter *i2c_adap; + + struct dibx000_i2c_master i2c_master; + + u16 wbd_ref; + + u8 current_band; + fe_bandwidth_t current_bandwidth; + struct dibx000_agc_config *current_agc; + u32 timf; + + u16 gpio_dir; + u16 gpio_val; +}; + +enum dib7000p_power_mode { + DIB7000P_POWER_ALL = 0, + DIB7000P_POWER_INTERFACE_ONLY, +}; + +static u16 dib7000p_read_word(struct dib7000p_state *state, u16 reg) +{ + u8 wb[2] = { reg >> 8, reg & 0xff }; + u8 rb[2]; + struct i2c_msg msg[2] = { + { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 }, + { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 }, + }; + + if (i2c_transfer(state->i2c_adap, msg, 2) != 2) + dprintk("i2c read error on %d\n",reg); + + return (rb[0] << 8) | rb[1]; +} + +static int dib7000p_write_word(struct dib7000p_state *state, u16 reg, u16 val) +{ + u8 b[4] = { + (reg >> 8) & 0xff, reg & 0xff, + (val >> 8) & 0xff, val & 0xff, + }; + struct i2c_msg msg = { + .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4 + }; + return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0; +} +static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode) +{ + int ret = 0; + u16 outreg, fifo_threshold, smo_mode; + + outreg = 0; + fifo_threshold = 1792; + smo_mode = (dib7000p_read_word(state, 235) & 0x0010) | (1 << 1); + + dprintk("-I- Setting output mode for demod %p to %d\n", + &state->demod, mode); + + switch (mode) { + case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock + outreg = (1 << 10); /* 0x0400 */ + break; + case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock + outreg = (1 << 10) | (1 << 6); /* 0x0440 */ + break; + case OUTMODE_MPEG2_SERIAL: // STBs with serial input + outreg = (1 << 10) | (2 << 6) | (0 << 1); /* 0x0480 */ + break; + case OUTMODE_DIVERSITY: + if (state->cfg.hostbus_diversity) + outreg = (1 << 10) | (4 << 6); /* 0x0500 */ + else + outreg = (1 << 11); + break; + case OUTMODE_MPEG2_FIFO: // e.g. USB feeding + smo_mode |= (3 << 1); + fifo_threshold = 512; + outreg = (1 << 10) | (5 << 6); + break; + case OUTMODE_HIGH_Z: // disable + outreg = 0; + break; + default: + dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod); + break; + } + + if (state->cfg.output_mpeg2_in_188_bytes) + smo_mode |= (1 << 5) ; + + ret |= dib7000p_write_word(state, 235, smo_mode); + ret |= dib7000p_write_word(state, 236, fifo_threshold); /* synchronous fread */ + ret |= dib7000p_write_word(state, 1286, outreg); /* P_Div_active */ + + return ret; +} + +static int dib7000p_set_power_mode(struct dib7000p_state *state, enum dib7000p_power_mode mode) +{ + /* by default everything is powered off */ + u16 reg_774 = 0xffff, reg_775 = 0xffff, reg_776 = 0x0007, reg_899 = 0x0003, + reg_1280 = (0xfe00) | (dib7000p_read_word(state, 1280) & 0x01ff); + + /* now, depending on the requested mode, we power on */ + switch (mode) { + /* power up everything in the demod */ + case DIB7000P_POWER_ALL: + reg_774 = 0x0000; reg_775 = 0x0000; reg_776 = 0x0; reg_899 = 0x0; reg_1280 &= 0x01ff; + break; + /* just leave power on the control-interfaces: GPIO and (I2C or SDIO) */ + case DIB7000P_POWER_INTERFACE_ONLY: /* TODO power up either SDIO or I2C */ + reg_1280 &= ~((1 << 14) | (1 << 13) | (1 << 12) | (1 << 10)); + break; +/* TODO following stuff is just converted from the dib7000-driver - check when is used what */ + } + + dib7000p_write_word(state, 774, reg_774); + dib7000p_write_word(state, 775, reg_775); + dib7000p_write_word(state, 776, reg_776); + dib7000p_write_word(state, 899, reg_899); + dib7000p_write_word(state, 1280, reg_1280); + + return 0; +} + +static void dib7000p_set_adc_state(struct dib7000p_state *state, enum dibx000_adc_states no) +{ + u16 reg_908 = dib7000p_read_word(state, 908), + reg_909 = dib7000p_read_word(state, 909); + + switch (no) { + case DIBX000_SLOW_ADC_ON: + reg_909 |= (1 << 1) | (1 << 0); + dib7000p_write_word(state, 909, reg_909); + reg_909 &= ~(1 << 1); + break; + + case DIBX000_SLOW_ADC_OFF: + reg_909 |= (1 << 1) | (1 << 0); + break; + + case DIBX000_ADC_ON: + reg_908 &= 0x0fff; + reg_909 &= 0x0003; + break; + + case DIBX000_ADC_OFF: // leave the VBG voltage on + reg_908 |= (1 << 14) | (1 << 13) | (1 << 12); + reg_909 |= (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2); + break; + + case DIBX000_VBG_ENABLE: + reg_908 &= ~(1 << 15); + break; + + case DIBX000_VBG_DISABLE: + reg_908 |= (1 << 15); + break; + + default: + break; + } + +// dprintk("908: %x, 909: %x\n", reg_908, reg_909); + + dib7000p_write_word(state, 908, reg_908); + dib7000p_write_word(state, 909, reg_909); +} + +static int dib7000p_set_bandwidth(struct dvb_frontend *demod, u8 BW_Idx) +{ + struct dib7000p_state *state = demod->demodulator_priv; + u32 timf; + + // store the current bandwidth for later use + state->current_bandwidth = BW_Idx; + + if (state->timf == 0) { + dprintk("-D- Using default timf\n"); + timf = state->cfg.bw->timf; + } else { + dprintk("-D- Using updated timf\n"); + timf = state->timf; + } + + timf = timf * (BW_INDEX_TO_KHZ(BW_Idx) / 100) / 80; + + dprintk("timf: %d\n",timf); + + dib7000p_write_word(state, 23, (timf >> 16) & 0xffff); + dib7000p_write_word(state, 24, (timf ) & 0xffff); + + return 0; +} + +static int dib7000p_sad_calib(struct dib7000p_state *state) +{ +/* internal */ +// dib7000p_write_word(state, 72, (3 << 14) | (1 << 12) | (524 << 0)); // sampling clock of the SAD is writting in set_bandwidth + dib7000p_write_word(state, 73, (0 << 1) | (0 << 0)); + dib7000p_write_word(state, 74, 776); // 0.625*3.3 / 4096 + + /* do the calibration */ + dib7000p_write_word(state, 73, (1 << 0)); + dib7000p_write_word(state, 73, (0 << 0)); + + msleep(1); + + return 0; +} + +static void dib7000p_reset_pll(struct dib7000p_state *state) +{ + struct dibx000_bandwidth_config *bw = &state->cfg.bw[0]; + + dib7000p_write_word(state, 903, (bw->pll_prediv << 5) | (((bw->pll_ratio >> 6) & 0x3) << 3) | (bw->pll_range << 1) | bw->pll_reset); + dib7000p_write_word(state, 900, ((bw->pll_ratio & 0x3f) << 9) | (bw->pll_bypass << 15) | (bw->modulo << 7) | (bw->ADClkSrc << 6) | + (bw->IO_CLK_en_core << 5) | (bw->bypclk_div << 2) | (bw->enable_refdiv << 1) | (0 << 0)); + + dib7000p_write_word(state, 18, ((bw->internal*1000) >> 16) & 0xffff); + dib7000p_write_word(state, 19, (bw->internal*1000 ) & 0xffff); + dib7000p_write_word(state, 21, (bw->ifreq >> 16) & 0xffff); + dib7000p_write_word(state, 22, (bw->ifreq ) & 0xffff); + + dib7000p_write_word(state, 72, bw->sad_cfg); +} + +static int dib7000p_reset_gpio(struct dib7000p_state *st) +{ + /* reset the GPIOs */ + dprintk("-D- gpio dir: %x: gpio val: %x, gpio pwm pos: %x\n",st->gpio_dir, st->gpio_val,st->cfg.gpio_pwm_pos); + + dib7000p_write_word(st, 1029, st->gpio_dir); + dib7000p_write_word(st, 1030, st->gpio_val); + + /* TODO 1031 is P_gpio_od */ + + dib7000p_write_word(st, 1032, st->cfg.gpio_pwm_pos); + + dib7000p_write_word(st, 1037, st->cfg.pwm_freq_div); + return 0; +} + +static int dib7000p_demod_reset(struct dib7000p_state *state) +{ + dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + + dib7000p_set_adc_state(state, DIBX000_VBG_ENABLE); + + /* restart all parts */ + dib7000p_write_word(state, 770, 0xffff); + dib7000p_write_word(state, 771, 0xffff); + dib7000p_write_word(state, 772, 0x001f); + dib7000p_write_word(state, 898, 0x0003); + /* except i2c, sdio, gpio - control interfaces */ + dib7000p_write_word(state, 1280, 0x01fc - ((1 << 7) | (1 << 6) | (1 << 5)) ); + + dib7000p_write_word(state, 770, 0); + dib7000p_write_word(state, 771, 0); + dib7000p_write_word(state, 772, 0); + dib7000p_write_word(state, 898, 0); + dib7000p_write_word(state, 1280, 0); + + /* default */ + dib7000p_reset_pll(state); + + if (dib7000p_reset_gpio(state) != 0) + dprintk("-E- GPIO reset was not successful.\n"); + + if (dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) != 0) + dprintk("-E- OUTPUT_MODE could not be resetted.\n"); + + /* unforce divstr regardless whether i2c enumeration was done or not */ + dib7000p_write_word(state, 1285, dib7000p_read_word(state, 1285) & ~(1 << 1) ); + + dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); + + return 0; +} + +static void dib7000p_restart_agc(struct dib7000p_state *state) +{ + // P_restart_iqc & P_restart_agc + dib7000p_write_word(state, 770, 0x0c00); + dib7000p_write_word(state, 770, 0x0000); +} + +static void dib7000p_update_lna(struct dib7000p_state *state) +{ + int i; + u16 dyn_gain; + + // when there is no LNA to program return immediatly + if (state->cfg.update_lna == NULL) + return; + + for (i = 0; i < 5; i++) { + // read dyn_gain here (because it is demod-dependent and not tuner) + dyn_gain = dib7000p_read_word(state, 394); + + if (state->cfg.update_lna(&state->demod,dyn_gain)) { // LNA has changed + dib7000p_restart_agc(state); + msleep(5); + } else + break; + } +} + +static void dib7000p_pll_clk_cfg(struct dib7000p_state *state) +{ + u16 tmp = 0; + tmp = dib7000p_read_word(state, 903); + dib7000p_write_word(state, 903, (tmp | 0x1)); //pwr-up pll + tmp = dib7000p_read_word(state, 900); + dib7000p_write_word(state, 900, (tmp & 0x7fff) | (1 << 6)); //use High freq clock +} + +static void dib7000p_update_timf_freq(struct dib7000p_state *state) +{ + u32 timf = (dib7000p_read_word(state, 427) << 16) | dib7000p_read_word(state, 428); + state->timf = timf * 80 / (BW_INDEX_TO_KHZ(state->current_bandwidth) / 100); + dib7000p_write_word(state, 23, (u16) (timf >> 16)); + dib7000p_write_word(state, 24, (u16) (timf & 0xffff)); + dprintk("-D- Updated timf_frequency: %d (default: %d)\n",state->timf, state->cfg.bw->timf); +} + +static void dib7000p_set_channel(struct dib7000p_state *state, struct dibx000_ofdm_channel *ch, u8 seq) +{ + u16 tmp, est[4]; // reg_26, reg_32, reg_33, reg_187, reg_188, reg_189, reg_190, reg_207, reg_208; + + /* nfft, guard, qam, alpha */ + dib7000p_write_word(state, 0, (ch->nfft << 7) | (ch->guard << 5) | (ch->nqam << 3) | (ch->vit_alpha)); + dib7000p_write_word(state, 5, (seq << 4) | 1); /* do not force tps, search list 0 */ + + /* P_dintl_native, P_dintlv_inv, P_vit_hrch, P_vit_code_rate, P_vit_select_hp */ + tmp = (ch->intlv_native << 6) | (ch->vit_hrch << 4) | (ch->vit_select_hp & 0x1); + if (ch->vit_hrch == 0 || ch->vit_select_hp == 1) + tmp |= (ch->vit_code_rate_hp << 1); + else + tmp |= (ch->vit_code_rate_lp << 1); + dib7000p_write_word(state, 208, tmp); + + /* P_dvsy_sync_wait */ + switch (ch->nfft) { + case 1: tmp = 256; break; + case 2: tmp = 128; break; + case 0: + default: tmp = 64; break; + } + tmp *= ((1 << (ch->guard)) * 3 / 2); // add 50% SFN margin + tmp <<= 4; + + /* deactive the possibility of diversity reception if extended interleave */ + /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ + if (ch->intlv_native || ch->nfft == 1) + tmp |= (1 << 2) | (2 << 0); + dib7000p_write_word(state, 207, tmp); + + dib7000p_write_word(state, 26, 0x6680); // timf(6xxx) + dib7000p_write_word(state, 29, 0x1273); // isi inh1273 on1073 + dib7000p_write_word(state, 32, 0x0003); // pha_off_max(xxx3) + dib7000p_write_word(state, 33, 0x0005); // sfreq(xxx5) + + /* channel estimation fine configuration */ + switch (ch->nqam) { + case 2: + est[0] = 0x0148; /* P_adp_regul_cnt 0.04 */ + est[1] = 0xfff0; /* P_adp_noise_cnt -0.002 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.001 */ + break; + case 1: + est[0] = 0x023d; /* P_adp_regul_cnt 0.07 */ + est[1] = 0xffdf; /* P_adp_noise_cnt -0.004 */ + est[2] = 0x00a4; /* P_adp_regul_ext 0.02 */ + est[3] = 0xfff0; /* P_adp_noise_ext -0.002 */ + break; + default: + est[0] = 0x099a; /* P_adp_regul_cnt 0.3 */ + est[1] = 0xffae; /* P_adp_noise_cnt -0.01 */ + est[2] = 0x0333; /* P_adp_regul_ext 0.1 */ + est[3] = 0xfff8; /* P_adp_noise_ext -0.002 */ + break; + } + for (tmp = 0; tmp < 4; tmp++) + dib7000p_write_word(state, 187 + tmp, est[tmp]); + + // set power-up level: interf+analog+AGC + dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + dib7000p_set_adc_state(state, DIBX000_ADC_ON); + dib7000p_pll_clk_cfg(state); + msleep(7); + + // AGC initialization + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 1); + + dib7000p_restart_agc(state); + + // wait AGC rough lock time + msleep(5); + + dib7000p_update_lna(state); + + // wait AGC accurate lock time + msleep(7); + if (state->cfg.agc_control) + state->cfg.agc_control(&state->demod, 0); +} + +static int dib7000p_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch) +{ + struct dib7000p_state *state = demod->demodulator_priv; + struct dibx000_ofdm_channel auto_ch; + u32 value; + + INIT_OFDM_CHANNEL(&auto_ch); + auto_ch.RF_kHz = ch->RF_kHz; + auto_ch.Bw = ch->Bw; + auto_ch.nqam = 2; + auto_ch.guard = 0; + auto_ch.nfft = 1; + auto_ch.vit_alpha = 1; + auto_ch.vit_select_hp = 1; + auto_ch.vit_code_rate_hp = 2; + auto_ch.vit_code_rate_lp = 3; + auto_ch.vit_hrch = 0; + auto_ch.intlv_native = 1; + + dib7000p_set_channel(state, &auto_ch, 7); + + // always use the setting for 8MHz here lock_time for 7,6 MHz are longer + value = 30 * state->cfg.bw->internal; + dib7000p_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time + dib7000p_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time + value = 100 * state->cfg.bw->internal; + dib7000p_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time + dib7000p_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time + value = 500 * state->cfg.bw->internal; + dib7000p_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time + dib7000p_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time + + value = dib7000p_read_word(state, 0); + dib7000p_write_word(state, 0, (1 << 9) | value); + dib7000p_read_word(state, 1284); + dib7000p_write_word(state, 0, (u16) value); + + return 0; +} + +static int dib7000p_autosearch_is_irq(struct dvb_frontend *demod) +{ + struct dib7000p_state *state = demod->demodulator_priv; + u16 irq_pending = dib7000p_read_word(state, 1284); + + if (irq_pending & 0x1) // failed + return 1; + + if (irq_pending & 0x2) // succeeded + return 2; + + return 0; // still pending +} + +static int dib7000p_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch) +{ + struct dib7000p_state *state = demod->demodulator_priv; + u16 tmp = 0; + + if (ch != NULL) + dib7000p_set_channel(state, ch, 0); + else + return -EINVAL; + + // restart demod + dib7000p_write_word(state, 770, 0x4000); + dib7000p_write_word(state, 770, 0x0000); + msleep(45); + + /* P_ctrl_inh_cor=0, P_ctrl_alpha_cor=4, P_ctrl_inh_isi=0, P_ctrl_alpha_isi=3, P_ctrl_inh_cor4=1, P_ctrl_alpha_cor4=3 */ + dib7000p_write_word(state, 29, (0 << 14) | (4 << 10) | (0 << 9) | (3 << 5) | (1 << 4) | (0x3)); + + // never achieved a lock with that bandwidth so far - wait for osc-freq to update + if (state->timf == 0) + msleep(200); + + /* offset loop parameters */ + + /* P_timf_alpha, P_corm_alpha=6, P_corm_thres=0x80 */ + tmp = (6 << 8) | 0x80; + switch (ch->nfft) { + case 0: tmp |= (7 << 12); break; + case 1: tmp |= (9 << 12); break; + case 2: tmp |= (8 << 12); break; + } + dib7000p_write_word(state, 26, tmp); /* timf_a(6xxx) */ + + /* P_ctrl_freeze_pha_shift=0, P_ctrl_pha_off_max */ + tmp = (0 << 4); + switch (ch->nfft) { + case 0: tmp |= 0x6; break; + case 1: tmp |= 0x8; break; + case 2: tmp |= 0x7; break; + } + dib7000p_write_word(state, 32, tmp); + + /* P_ctrl_sfreq_inh=0, P_ctrl_sfreq_step */ + tmp = (0 << 4); + switch (ch->nfft) { + case 0: tmp |= 0x6; break; + case 1: tmp |= 0x8; break; + case 2: tmp |= 0x7; break; + } + dib7000p_write_word(state, 33, tmp); + + tmp = dib7000p_read_word(state,509); + if (!((tmp >> 6) & 0x1)) { + /* restart the fec */ + tmp = dib7000p_read_word(state,771); + dib7000p_write_word(state, 771, tmp | (1 << 1)); + dib7000p_write_word(state, 771, tmp); + msleep(10); + tmp = dib7000p_read_word(state,509); + } + + // we achieved a lock - it's time to update the osc freq + if ((tmp >> 6) & 0x1) + dib7000p_update_timf_freq(state); + + return 0; +} + +static int dib7000p_init(struct dvb_frontend *demod) +{ + struct dibx000_agc_config *agc; + struct dib7000p_state *state = demod->demodulator_priv; + int ret = 0; + + // Demodulator default configuration + agc = state->cfg.agc; + + dib7000p_set_power_mode(state, DIB7000P_POWER_ALL); + dib7000p_set_adc_state(state, DIBX000_SLOW_ADC_ON); + + /* AGC */ + ret |= dib7000p_write_word(state, 75 , agc->setup ); + ret |= dib7000p_write_word(state, 76 , agc->inv_gain ); + ret |= dib7000p_write_word(state, 77 , agc->time_stabiliz ); + ret |= dib7000p_write_word(state, 100, (agc->alpha_level << 12) | agc->thlock); + + // Demod AGC loop configuration + ret |= dib7000p_write_word(state, 101, (agc->alpha_mant << 5) | agc->alpha_exp); + ret |= dib7000p_write_word(state, 102, (agc->beta_mant << 6) | agc->beta_exp); + + /* AGC continued */ + dprintk("-D- WBD: ref: %d, sel: %d, active: %d, alpha: %d\n", + state->wbd_ref != 0 ? state->wbd_ref : agc->wbd_ref, agc->wbd_sel, !agc->perform_agc_softsplit, agc->wbd_sel); + + if (state->wbd_ref != 0) + ret |= dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | state->wbd_ref); + else + ret |= dib7000p_write_word(state, 105, (agc->wbd_inv << 12) | agc->wbd_ref); + + ret |= dib7000p_write_word(state, 106, (agc->wbd_sel << 13) | (agc->wbd_alpha << 9) | (agc->perform_agc_softsplit << 8) ); + + ret |= dib7000p_write_word(state, 107, agc->agc1_max); + ret |= dib7000p_write_word(state, 108, agc->agc1_min); + ret |= dib7000p_write_word(state, 109, agc->agc2_max); + ret |= dib7000p_write_word(state, 110, agc->agc2_min); + ret |= dib7000p_write_word(state, 111, (agc->agc1_pt1 << 8) | agc->agc1_pt2 ); + ret |= dib7000p_write_word(state, 112, agc->agc1_pt3); + ret |= dib7000p_write_word(state, 113, (agc->agc1_slope1 << 8) | agc->agc1_slope2); + ret |= dib7000p_write_word(state, 114, (agc->agc2_pt1 << 8) | agc->agc2_pt2); + ret |= dib7000p_write_word(state, 115, (agc->agc2_slope1 << 8) | agc->agc2_slope2); + + /* disable power smoothing */ + ret |= dib7000p_write_word(state, 145, 0); + ret |= dib7000p_write_word(state, 146, 0); + ret |= dib7000p_write_word(state, 147, 0); + ret |= dib7000p_write_word(state, 148, 0); + ret |= dib7000p_write_word(state, 149, 0); + ret |= dib7000p_write_word(state, 150, 0); + ret |= dib7000p_write_word(state, 151, 0); + ret |= dib7000p_write_word(state, 152, 0); + + // P_timf_alpha=6, P_corm_alpha=6, P_corm_thres=128 default: 6,4,26 + ret |= dib7000p_write_word(state, 26 ,0x6680); + + // P_palf_filter_on=1, P_palf_filter_freeze=0, P_palf_alpha_regul=16 + ret |= dib7000p_write_word(state, 142,0x0410); + // P_fft_freq_dir=1, P_fft_nb_to_cut=0 + ret |= dib7000p_write_word(state, 154,1 << 13); + // P_pha3_thres, default 0x3000 + ret |= dib7000p_write_word(state, 168,0x0ccd); + // P_cti_use_cpe=0, P_cti_use_prog=0, P_cti_win_len=16, default: 0x0010 + //ret |= dib7000p_write_word(state, 169,0x0010); + // P_cspu_regul=512, P_cspu_win_cut=15, default: 0x2005 + ret |= dib7000p_write_word(state, 183,0x200f); + // P_adp_regul_cnt=573, default: 410 + ret |= dib7000p_write_word(state, 187,0x023d); + // P_adp_noise_cnt= + ret |= dib7000p_write_word(state, 188,0x00a4); + // P_adp_regul_ext + ret |= dib7000p_write_word(state, 189,0x00a4); + // P_adp_noise_ext + ret |= dib7000p_write_word(state, 190,0x7ff0); + // P_adp_fil + ret |= dib7000p_write_word(state, 191,0x3ccc); + + ret |= dib7000p_write_word(state, 222,0x0010); + // P_smo_mode, P_smo_rs_discard, P_smo_fifo_flush, P_smo_pid_parse, P_smo_error_discard + ret |= dib7000p_write_word(state, 235,0x0062); + + // P_iqc_alpha_pha, P_iqc_alpha_amp_dcc_alpha, ... + if(state->cfg.tuner_is_baseband) + ret |= dib7000p_write_word(state, 36,0x0755); + else + ret |= dib7000p_write_word(state, 36,0x1f55); + + // auto search configuration + ret |= dib7000p_write_word(state, 2 ,0x0004); + ret |= dib7000p_write_word(state, 3 ,0x1000); + + /* Equal Lock */ + ret |= dib7000p_write_word(state, 4 ,0x0814); + + ret |= dib7000p_write_word(state, 6 ,0x001b); + ret |= dib7000p_write_word(state, 7 ,0x7740); + ret |= dib7000p_write_word(state, 8 ,0x005b); + ret |= dib7000p_write_word(state, 9 ,0x8d80); + ret |= dib7000p_write_word(state, 10 ,0x01c9); + ret |= dib7000p_write_word(state, 11 ,0xc380); + ret |= dib7000p_write_word(state, 12 ,0x0000); + ret |= dib7000p_write_word(state, 13 ,0x0080); + ret |= dib7000p_write_word(state, 14 ,0x0000); + ret |= dib7000p_write_word(state, 15 ,0x0090); + ret |= dib7000p_write_word(state, 16 ,0x0001); + ret |= dib7000p_write_word(state, 17 ,0xd4c0); + + // P_clk_cfg1 + ret |= dib7000p_write_word(state, 901, 0x0006); + + // P_divclksel=3 P_divbitsel=1 + ret |= dib7000p_write_word(state, 902, (3 << 10) | (1 << 6)); + + // Tuner IO bank: max drive (14mA) + divout pads max drive + ret |= dib7000p_write_word(state, 905, 0x2c8e); + + ret |= dib7000p_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ); + dib7000p_sad_calib(state); + + return ret; +} + +static int dib7000p_sleep(struct dvb_frontend *demod) +{ + struct dib7000p_state *state = demod->demodulator_priv; + return dib7000p_set_output_mode(state, OUTMODE_HIGH_Z) | dib7000p_set_power_mode(state, DIB7000P_POWER_INTERFACE_ONLY); +} + +static int dib7000p_identify(struct dib7000p_state *st) +{ + u16 value; + dprintk("-I- DiB7000PC: checking demod on I2C address: %d (%x)\n", + st->i2c_addr, st->i2c_addr); + + if ((value = dib7000p_read_word(st, 768)) != 0x01b3) { + dprintk("-E- DiB7000PC: wrong Vendor ID (read=0x%x)\n",value); + return -EREMOTEIO; + } + + if ((value = dib7000p_read_word(st, 769)) != 0x4000) { + dprintk("-E- DiB7000PC: wrong Device ID (%x)\n",value); + return -EREMOTEIO; + } + + return 0; +} + + +static int dib7000p_get_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 tps = dib7000p_read_word(state,463); + + fep->inversion = INVERSION_AUTO; + + fep->u.ofdm.bandwidth = state->current_bandwidth; + + switch ((tps >> 8) & 0x3) { + case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break; + case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break; + /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */ + } + + switch (tps & 0x3) { + case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break; + case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break; + case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break; + case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break; + } + + switch ((tps >> 14) & 0x3) { + case 0: fep->u.ofdm.constellation = QPSK; break; + case 1: fep->u.ofdm.constellation = QAM_16; break; + case 2: + default: fep->u.ofdm.constellation = QAM_64; break; + } + + /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */ + /* (tps >> 13) & 0x1 == hrch is used, (tps >> 10) & 0x7 == alpha */ + + fep->u.ofdm.hierarchy_information = HIERARCHY_NONE; + switch ((tps >> 5) & 0x7) { + case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break; + case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break; + case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break; + case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break; + case 7: + default: fep->u.ofdm.code_rate_HP = FEC_7_8; break; + + } + + switch ((tps >> 2) & 0x7) { + case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break; + case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break; + case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break; + case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break; + case 7: + default: fep->u.ofdm.code_rate_LP = FEC_7_8; break; + } + + /* native interleaver: (dib7000p_read_word(state, 464) >> 5) & 0x1 */ + + return 0; +} + +static int dib7000p_set_frontend(struct dvb_frontend* fe, + struct dvb_frontend_parameters *fep) +{ + struct dib7000p_state *state = fe->demodulator_priv; + struct dibx000_ofdm_channel ch; + + INIT_OFDM_CHANNEL(&ch); + FEP2DIB(fep,&ch); + + state->current_bandwidth = fep->u.ofdm.bandwidth; + dib7000p_set_bandwidth(fe, fep->u.ofdm.bandwidth); + + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, fep); + + if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO || + fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO || + fep->u.ofdm.constellation == QAM_AUTO || + fep->u.ofdm.code_rate_HP == FEC_AUTO) { + int i = 800, found; + + dib7000p_autosearch_start(fe, &ch); + do { + msleep(1); + found = dib7000p_autosearch_is_irq(fe); + } while (found == 0 && i--); + + dprintk("autosearch returns: %d\n",found); + if (found == 0 || found == 1) + return 0; // no channel found + + dib7000p_get_frontend(fe, fep); + FEP2DIB(fep, &ch); + } + + /* make this a config parameter */ + dib7000p_set_output_mode(state, OUTMODE_MPEG2_FIFO); + + return dib7000p_tune(fe, &ch); +} + +static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 lock = dib7000p_read_word(state, 509); + + *stat = 0; + + if (lock & 0x8000) + *stat |= FE_HAS_SIGNAL; + if (lock & 0x3000) + *stat |= FE_HAS_CARRIER; + if (lock & 0x0100) + *stat |= FE_HAS_VITERBI; + if (lock & 0x0010) + *stat |= FE_HAS_SYNC; + if (lock & 0x0008) + *stat |= FE_HAS_LOCK; + + return 0; +} + +static int dib7000p_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct dib7000p_state *state = fe->demodulator_priv; + *ber = (dib7000p_read_word(state, 500) << 16) | dib7000p_read_word(state, 501); + return 0; +} + +static int dib7000p_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +{ + struct dib7000p_state *state = fe->demodulator_priv; + *unc = dib7000p_read_word(state, 506); + return 0; +} + +static int dib7000p_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 val = dib7000p_read_word(state, 394); + *strength = 65535 - val; + return 0; +} + +static int dib7000p_read_snr(struct dvb_frontend* fe, u16 *snr) +{ + *snr = 0x0000; + return 0; +} + +static int dib7000p_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void dib7000p_release(struct dvb_frontend *demod) +{ + struct dib7000p_state *st = demod->demodulator_priv; + dibx000_exit_i2c_master(&st->i2c_master); + kfree(st); +} + +int dib7000pc_detection(struct i2c_adapter *i2c_adap) +{ + u8 tx[2], rx[2]; + struct i2c_msg msg[2] = { + { .addr = 18 >> 1, .flags = 0, .buf = tx, .len = 2 }, + { .addr = 18 >> 1, .flags = I2C_M_RD, .buf = rx, .len = 2 }, + }; + + tx[0] = 0x03; + tx[1] = 0x00; + + if (i2c_transfer(i2c_adap, msg, 2) == 2) + if (rx[0] == 0x01 && rx[1] == 0xb3) { + dprintk("-D- DiB7000PC detected\n"); + return 1; + } + + msg[0].addr = msg[1].addr = 0x40; + + if (i2c_transfer(i2c_adap, msg, 2) == 2) + if (rx[0] == 0x01 && rx[1] == 0xb3) { + dprintk("-D- DiB7000PC detected\n"); + return 1; + } + + dprintk("-D- DiB7000PC not detected\n"); + return 0; +} +EXPORT_SYMBOL(dib7000pc_detection); + +struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *demod, enum dibx000_i2c_interface intf, int gating) +{ + struct dib7000p_state *st = demod->demodulator_priv; + return dibx000_get_i2c_adapter(&st->i2c_master, intf, gating); +} +EXPORT_SYMBOL(dib7000p_get_i2c_master); + +int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) +{ + struct dib7000p_state st = { .i2c_adap = i2c }; + int k = 0; + u8 new_addr = 0; + + for (k = no_of_demods-1; k >= 0; k--) { + st.cfg = cfg[k]; + + /* designated i2c address */ + new_addr = (0x40 + k) << 1; + st.i2c_addr = new_addr; + if (dib7000p_identify(&st) != 0) { + st.i2c_addr = default_addr; + if (dib7000p_identify(&st) != 0) { + dprintk("DiB7000P #%d: not identified\n", k); + return -EIO; + } + } + + /* start diversity to pull_down div_str - just for i2c-enumeration */ + dib7000p_set_output_mode(&st, OUTMODE_DIVERSITY); + + /* set new i2c address and force divstart */ + dib7000p_write_word(&st, 1285, (new_addr << 2) | 0x2); + + dprintk("IC %d initialized (to i2c_address 0x%x)\n", k, new_addr); + } + + for (k = 0; k < no_of_demods; k++) { + st.cfg = cfg[k]; + st.i2c_addr = (0x40 + k) << 1; + + // unforce divstr + dib7000p_write_word(&st, 1285, st.i2c_addr << 2); + + /* deactivate div - it was just for i2c-enumeration */ + dib7000p_set_output_mode(&st, OUTMODE_HIGH_Z); + } + + return 0; +} +EXPORT_SYMBOL(dib7000p_i2c_enumeration); + +static struct dvb_frontend_ops dib7000p_ops; +struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg) +{ + struct dvb_frontend *demod; + struct dib7000p_state *st; + st = kzalloc(sizeof(struct dib7000p_state), GFP_KERNEL); + if (st == NULL) + return NULL; + + memcpy(&st->cfg, cfg, sizeof(struct dib7000p_config)); + st->i2c_adap = i2c_adap; + st->i2c_addr = i2c_addr; + st->gpio_val = cfg->gpio_val; + st->gpio_dir = cfg->gpio_dir; + + demod = &st->demod; + demod->demodulator_priv = st; + memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops)); + + if (dib7000p_identify(st) != 0) + goto error; + + dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr); + + dib7000p_demod_reset(st); + + return demod; + +error: + kfree(st); + return NULL; +} +EXPORT_SYMBOL(dib7000p_attach); + +static struct dvb_frontend_ops dib7000p_ops = { + .info = { + .name = "DiBcom 7000PC", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 62500, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = dib7000p_release, + + .init = dib7000p_init, + .sleep = dib7000p_sleep, + + .set_frontend = dib7000p_set_frontend, + .get_tune_settings = dib7000p_fe_get_tune_settings, + .get_frontend = dib7000p_get_frontend, + + .read_status = dib7000p_read_status, + .read_ber = dib7000p_read_ber, + .read_signal_strength = dib7000p_read_signal_strength, + .read_snr = dib7000p_read_snr, + .read_ucblocks = dib7000p_read_unc_blocks, +}; + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for the DiBcom 7000PC COFDM demodulator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h new file mode 100644 index 0000000..79465cf --- /dev/null +++ b/drivers/media/dvb/frontends/dib7000p.h @@ -0,0 +1,46 @@ +#ifndef DIB7000P_H +#define DIB7000P_H + +#include "dibx000_common.h" + +struct dib7000p_config { + u8 output_mpeg2_in_188_bytes; + u8 hostbus_diversity; + u8 tuner_is_baseband; + int (*update_lna) (struct dvb_frontend *, u16 agc_global); + + struct dibx000_agc_config *agc; + struct dibx000_bandwidth_config *bw; + +#define DIB7000P_GPIO_DEFAULT_DIRECTIONS 0xffff + u16 gpio_dir; +#define DIB7000P_GPIO_DEFAULT_VALUES 0x0000 + u16 gpio_val; +#define DIB7000P_GPIO_PWM_POS0(v) ((v & 0xf) << 12) +#define DIB7000P_GPIO_PWM_POS1(v) ((v & 0xf) << 8 ) +#define DIB7000P_GPIO_PWM_POS2(v) ((v & 0xf) << 4 ) +#define DIB7000P_GPIO_PWM_POS3(v) (v & 0xf) +#define DIB7000P_GPIO_DEFAULT_PWM_POS 0xffff + u16 gpio_pwm_pos; + + u16 pwm_freq_div; + + u8 quartz_direct; + + int (*agc_control) (struct dvb_frontend *, u8 before); +}; + +#define DEFAULT_DIB7000P_I2C_ADDRESS 18 + +extern struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000p_config *cfg); +extern struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int); +extern int dib7000pc_detection(struct i2c_adapter *i2c_adap); + +/* TODO +extern INT dib7000p_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val); +extern INT dib7000p_enable_vbg_voltage(struct dibDemod *demod); +extern void dib7000p_set_hostbus_diversity(struct dibDemod *demod, UCHAR onoff); +extern USHORT dib7000p_get_current_agc_global(struct dibDemod *demod); +*/ + +#endif -- cgit v0.10.2 From d92532d9860663cb39828e18a3e6aed27821f2bf Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Wed, 18 Oct 2006 08:35:16 -0300 Subject: V4L/DVB (4773): Misc fixes for DiB3000 and DiB7000 This patch contains several fixes for the autosearch algorithm and other small ones. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c index 3561a77..5da6617 100644 --- a/drivers/media/dvb/frontends/dib3000mc.c +++ b/drivers/media/dvb/frontends/dib3000mc.c @@ -511,16 +511,11 @@ static int dib3000mc_autosearch_start(struct dvb_frontend *demod, struct dibx000 /* a channel for autosearch */ - reg = 0; - if (chan->nfft == -1 && chan->guard == -1) reg = 7; - if (chan->nfft == -1 && chan->guard != -1) reg = 2; - if (chan->nfft != -1 && chan->guard == -1) reg = 3; - fchan.nfft = 1; fchan.guard = 0; fchan.nqam = 2; fchan.vit_alpha = 1; fchan.vit_code_rate_hp = 2; fchan.vit_code_rate_lp = 2; fchan.vit_hrch = 0; fchan.vit_select_hp = 1; - dib3000mc_set_channel_cfg(state, &fchan, reg); + dib3000mc_set_channel_cfg(state, &fchan, 7); reg = dib3000mc_read_word(state, 0); dib3000mc_write_word(state, 0, reg | (1 << 8)); diff --git a/drivers/media/dvb/frontends/dib7000m.c b/drivers/media/dvb/frontends/dib7000m.c index a851903..f5d40aa 100644 --- a/drivers/media/dvb/frontends/dib7000m.c +++ b/drivers/media/dvb/frontends/dib7000m.c @@ -637,15 +637,14 @@ static int dib7000m_autosearch_start(struct dvb_frontend *demod, struct dibx000_ struct dib7000m_state *state = demod->demodulator_priv; struct dibx000_ofdm_channel auto_ch; int ret = 0; - u8 seq; u32 value; INIT_OFDM_CHANNEL(&auto_ch); auto_ch.RF_kHz = ch->RF_kHz; auto_ch.Bw = ch->Bw; auto_ch.nqam = 2; - auto_ch.guard = ch->guard == GUARD_INTERVAL_AUTO ? 0 : ch->guard; - auto_ch.nfft = ch->nfft == -1 ? 1 : ch->nfft; + auto_ch.guard = 0; + auto_ch.nfft = 1; auto_ch.vit_alpha = 1; auto_ch.vit_select_hp = 1; auto_ch.vit_code_rate_hp = 2; @@ -653,20 +652,16 @@ static int dib7000m_autosearch_start(struct dvb_frontend *demod, struct dibx000_ auto_ch.vit_hrch = 0; auto_ch.intlv_native = 1; - seq = 0; - if (ch->nfft == -1 && ch->guard == GUARD_INTERVAL_AUTO) seq = 7; - if (ch->nfft == -1 && ch->guard != GUARD_INTERVAL_AUTO) seq = 2; - if (ch->nfft != -1 && ch->guard == GUARD_INTERVAL_AUTO) seq = 3; - dib7000m_set_channel(state, &auto_ch, seq); + dib7000m_set_channel(state, &auto_ch, 7); // always use the setting for 8MHz here lock_time for 7,6 MHz are longer - value = 30 * state->cfg.bw[BANDWIDTH_8_MHZ].internal; + value = 30 * state->cfg.bw->internal; ret |= dib7000m_write_word(state, 6, (u16) ((value >> 16) & 0xffff)); // lock0 wait time ret |= dib7000m_write_word(state, 7, (u16) (value & 0xffff)); // lock0 wait time - value = 100 * state->cfg.bw[BANDWIDTH_8_MHZ].internal; + value = 100 * state->cfg.bw->internal; ret |= dib7000m_write_word(state, 8, (u16) ((value >> 16) & 0xffff)); // lock1 wait time ret |= dib7000m_write_word(state, 9, (u16) (value & 0xffff)); // lock1 wait time - value = 500 * state->cfg.bw[BANDWIDTH_8_MHZ].internal; + value = 500 * state->cfg.bw->internal; ret |= dib7000m_write_word(state, 10, (u16) ((value >> 16) & 0xffff)); // lock2 wait time ret |= dib7000m_write_word(state, 11, (u16) (value & 0xffff)); // lock2 wait time @@ -919,7 +914,7 @@ static int dib7000m_get_frontend(struct dvb_frontend* fe, fep->u.ofdm.bandwidth = state->current_bandwidth; - switch ((tps >> 8) & 0x2) { + switch ((tps >> 8) & 0x3) { case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break; case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break; /* case 2: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_4K; break; */ -- cgit v0.10.2 From 49a1376cf47b644f31d0fb2532054183fcec6fc8 Mon Sep 17 00:00:00 2001 From: Tomi Koivulahti Date: Thu, 19 Oct 2006 07:27:19 -0300 Subject: V4L/DVB (4775): [patch] Add Compro USB IDs This patch adds Compro Videomate U500 to supported devices. Signed-off-by: Tomi Koivulahti Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index c231a1e..fe305f4 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -269,11 +269,13 @@ static int stk7700p_tuner_attach(struct dvb_usb_adapter *adap) struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P) }, + { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P_PC) }, + { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500) }, { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_500_2) }, { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK) }, { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR) }, - { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P_PC) }, + { USB_DEVICE(USB_VID_COMPRO, USB_PID_COMPRO_VIDEOMATE_U500) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -315,20 +317,24 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 3, + .num_device_descs = 4, .devices = { { "DiBcom STK7700P reference design", - { &dib0700_usb_id_table[0], &dib0700_usb_id_table[5] }, + { &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] }, { NULL }, }, { "Hauppauge Nova-T Stick", - { &dib0700_usb_id_table[3], NULL }, + { &dib0700_usb_id_table[4], NULL }, { NULL }, }, { "AVerMedia AVerTV DVB-T Volar", - { &dib0700_usb_id_table[4], NULL }, + { &dib0700_usb_id_table[5], NULL }, { NULL }, }, + { "Compro Videomate U500", + { &dib0700_usb_id_table[6], NULL }, + { NULL }, + } } }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, @@ -350,7 +356,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_device_descs = 1, .devices = { { "Hauppauge Nova-T 500 Dual DVB-T", - { &dib0700_usb_id_table[1], &dib0700_usb_id_table[2], NULL }, + { &dib0700_usb_id_table[2], &dib0700_usb_id_table[3], NULL }, { NULL }, }, } diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index fd8ba25..036fb14 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -46,6 +46,7 @@ #define USB_PID_COMPRO_DVBU2000_WARM 0xd001 #define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c #define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d +#define USB_PID_COMPRO_VIDEOMATE_U500 0x1e78 #define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064 #define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065 #define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8 @@ -111,8 +112,8 @@ #define USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM 0xdb51 #define USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD 0xdb58 #define USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM 0xdb59 -#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54 -#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55 +#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54 +#define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55 #define USB_PID_MEDION_MD95700 0x0932 #define USB_PID_KYE_DVB_T_COLD 0x701e #define USB_PID_KYE_DVB_T_WARM 0x701f -- cgit v0.10.2 From 0ce215e1f0d40702d6233029d234293754610fd7 Mon Sep 17 00:00:00 2001 From: Henning Schroeer Date: Thu, 19 Oct 2006 07:58:22 -0300 Subject: V4L/DVB (4776): Adding USB IDs for Uniwill STK7700P-ref-design Adding USB IDs for Uniwill STK7700P-ref-design Signed-off-by: Henning Schroeer Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index fe305f4..92cf071 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -276,6 +276,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_NOVA_T_STICK) }, { 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) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -334,6 +335,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { "Compro Videomate U500", { &dib0700_usb_id_table[6], NULL }, { NULL }, + }, + { "Uniwill STK7700P based (Hama and others)", + { &dib0700_usb_id_table[7], NULL }, + { NULL }, } } }, { DIB0700_DEFAULT_DEVICE_PROPERTIES, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 036fb14..58e1a44 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -32,6 +32,7 @@ #define USB_VID_PINNACLE 0x2304 #define USB_VID_VISIONPLUS 0x13d3 #define USB_VID_TWINHAN 0x1822 +#define USB_VID_UNIWILL 0x1584 #define USB_VID_ULTIMA_ELECTRONIC 0x05d8 #define USB_VID_WIDEVIEW 0x14aa @@ -56,6 +57,7 @@ #define USB_PID_DIBCOM_STK7700P 0x1e14 #define USB_PID_DIBCOM_STK7700P_PC 0x1e78 #define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131 +#define USB_PID_UNIWILL_STK7700P 0x6003 #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 #define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 #define USB_PID_KWORLD_VSTREAM_COLD 0x17de @@ -130,4 +132,5 @@ #define USB_PID_GENPIX_8PSK_COLD 0x0200 #define USB_PID_GENPIX_8PSK_WARM 0x0201 + #endif -- cgit v0.10.2 From bf11f5e944b3bb7d310aff041a1664fb80a82078 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Thu, 19 Oct 2006 08:17:43 -0300 Subject: V4L/DVB (4778): Fixed device count Forgot to increase the device count for the STK7700P-devices Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 92cf071..1b1598d 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -318,7 +318,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 4, + .num_device_descs = 5, .devices = { { "DiBcom STK7700P reference design", { &dib0700_usb_id_table[0], &dib0700_usb_id_table[1] }, -- cgit v0.10.2 From dbb2e6392b4fe350e549cdc2d42cebf3b3574a6a Mon Sep 17 00:00:00 2001 From: Christoph Haubrich Date: Tue, 31 Oct 2006 00:29:30 -0300 Subject: V4L/DVB (4788): Tda8083: support for uncorrectable blocks and bit error rate Copied routines for uc blocks and BER from the removed tda80xx.c into tda8083.c. Signed-off-by: Christoph Haubrich Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c index 3aa45eb..67415c9 100644 --- a/drivers/media/dvb/frontends/tda8083.c +++ b/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, -- cgit v0.10.2 From 19be685a4a5475ecdbbbb579458eeda890d27a96 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Sun, 29 Oct 2006 13:35:39 -0300 Subject: V4L/DVB (4789): Lgdt330x: SNR and signal strength reporting Update the SNR calculations to use the new dvb_math log function, and add SNR calculations for all supported modulations for both lg dt3302 and dt3303. The QAM equations don't appear in the dt3302 datasheet, so the ones from the dt3303 datasheet were used. SNR returned is the actual value in dB as 8.8 fixed point. Reporting of real signal strength isn't supported, so rather than return 0, which confuses some software and users, a re-scaled SNR value is returned. Code originally by Rusty Scott. Signed-off-by: Trent Piepho Signed-off-by: Rusty Scott Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c index 9a35470..db3ef58 100644 --- a/drivers/media/dvb/frontends/lgdt330x.c +++ b/drivers/media/dvb/frontends/lgdt330x.c @@ -31,9 +31,6 @@ * Air2PC/AirStar 2 ATSC 3rd generation (HD5000) * pcHDTV HD5500 * - * TODO: - * signal strength always returns 0. - * */ #include @@ -46,9 +43,13 @@ #include #include "dvb_frontend.h" +#include "dvb_math.h" #include "lgdt330x_priv.h" #include "lgdt330x.h" +/* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */ +/* #define USE_EQMSE */ + static int debug = 0; module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off)."); @@ -68,6 +69,7 @@ struct lgdt330x_state /* Demodulator private data */ fe_modulation_t current_modulation; + u32 snr; /* Result of last SNR calculation */ /* Tuner private data */ u32 current_frequency; @@ -543,151 +545,150 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status) return 0; } -static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength) +/* Calculate SNR estimation (scaled by 2^24) + + 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM + equations from LGDT3303 datasheet. VSB is the same between the '02 + and '03, so maybe QAM is too? Perhaps someone with a newer datasheet + that has QAM information could verify? + + For 8-VSB: (two ways, take your pick) + LGDT3302: + SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE) + LGDT3303: + SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE) + LGDT3302 & LGDT3303: + SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one) + For 64-QAM: + SNR = 10 * log10( 688128 / MSEQAM) + For 256-QAM: + SNR = 10 * log10( 696320 / MSEQAM) + + We re-write the snr equation as: + SNR * 2^24 = 10*(c - intlog10(MSE)) + Where for 256-QAM, c = log10(696320) * 2^24, and so on. */ + +static u32 calculate_snr(u32 mse, u32 c) { - /* not directly available. */ - *strength = 0; - return 0; + if (mse == 0) /* No signal */ + return 0; + + mse = intlog10(mse); + if (mse > c) { + /* Negative SNR, which is possible, but realisticly the + demod will lose lock before the signal gets this bad. The + API only allows for unsigned values, so just return 0 */ + return 0; + } + return 10*(c - mse); } static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr) { -#ifdef SNR_IN_DB - /* - * Spec sheet shows formula for SNR_EQ = 10 log10(25 * 24**2 / noise) - * and SNR_PH = 10 log10(25 * 32**2 / noise) for equalizer and phase tracker - * respectively. The following tables are built on these formulas. - * The usual definition is SNR = 20 log10(signal/noise) - * If the specification is wrong the value retuned is 1/2 the actual SNR in db. - * - * This table is a an ordered list of noise values computed by the - * formula from the spec sheet such that the index into the table - * starting at 43 or 45 is the SNR value in db. There are duplicate noise - * value entries at the beginning because the SNR varies more than - * 1 db for a change of 1 digit in noise at very small values of noise. - * - * Examples from SNR_EQ table: - * noise SNR - * 0 43 - * 1 42 - * 2 39 - * 3 37 - * 4 36 - * 5 35 - * 6 34 - * 7 33 - * 8 33 - * 9 32 - * 10 32 - * 11 31 - * 12 31 - * 13 30 - */ - - static const u32 SNR_EQ[] = - { 1, 2, 2, 2, 3, 3, 4, 4, 5, 7, - 9, 11, 13, 17, 21, 26, 33, 41, 52, 65, - 81, 102, 129, 162, 204, 257, 323, 406, 511, 644, - 810, 1020, 1284, 1616, 2035, 2561, 3224, 4059, 5110, 6433, - 8098, 10195, 12835, 16158, 20341, 25608, 32238, 40585, 51094, 64323, - 80978, 101945, 128341, 161571, 203406, 256073, 0x40000 - }; - - static const u32 SNR_PH[] = - { 1, 2, 2, 2, 3, 3, 4, 5, 6, 8, - 10, 12, 15, 19, 23, 29, 37, 46, 58, 73, - 91, 115, 144, 182, 229, 288, 362, 456, 574, 722, - 909, 1144, 1440, 1813, 2282, 2873, 3617, 4553, 5732, 7216, - 9084, 11436, 14396, 18124, 22817, 28724, 36161, 45524, 57312, 72151, - 90833, 114351, 143960, 181235, 228161, 0x080000 - }; - - static u8 buf[5];/* read data buffer */ - static u32 noise; /* noise value */ - static u32 snr_db; /* index into SNR_EQ[] */ struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; + u8 buf[5]; /* read data buffer */ + u32 noise; /* noise value */ + u32 c; /* per-modulation SNR calculation constant */ - /* read both equalizer and phase tracker noise data */ - i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf)); - - if (state->current_modulation == VSB_8) { - /* Equalizer Mean-Square Error Register for VSB */ + switch(state->current_modulation) { + case VSB_8: + i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5); +#ifdef USE_EQMSE + /* Use Equalizer Mean-Square Error Register */ + /* SNR for ranges from -15.61 to +41.58 */ noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2]; - - /* - * Look up noise value in table. - * A better search algorithm could be used... - * watch out there are duplicate entries. - */ - for (snr_db = 0; snr_db < sizeof(SNR_EQ); snr_db++) { - if (noise < SNR_EQ[snr_db]) { - *snr = 43 - snr_db; - break; - } - } - } else { - /* Phase Tracker Mean-Square Error Register for QAM */ - noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4]; - - /* Look up noise value in table. */ - for (snr_db = 0; snr_db < sizeof(SNR_PH); snr_db++) { - if (noise < SNR_PH[snr_db]) { - *snr = 45 - snr_db; - break; - } - } - } + c = 69765745; /* log10(25*24^2)*2^24 */ #else - /* Return the raw noise value */ - static u8 buf[5];/* read data buffer */ - static u32 noise; /* noise value */ - struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; - - /* read both equalizer and pase tracker noise data */ - i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf)); - - if (state->current_modulation == VSB_8) { - /* Phase Tracker Mean-Square Error Register for VSB */ + /* Use Phase Tracker Mean-Square Error Register */ + /* SNR for ranges from -13.11 to +44.08 */ noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4]; - } else { - - /* Carrier Recovery Mean-Square Error for QAM */ - i2c_read_demod_bytes(state, 0x1a, buf, 2); + c = 73957994; /* log10(25*32^2)*2^24 */ +#endif + break; + case QAM_64: + case QAM_256: + i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); noise = ((buf[0] & 3) << 8) | buf[1]; + c = state->current_modulation == QAM_64 ? 97939837 : 98026066; + /* log10(688128)*2^24 and log10(696320)*2^24 */ + break; + default: + printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", + __FUNCTION__); + return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ } - /* Small values for noise mean signal is better so invert noise */ - *snr = ~noise; -#endif + state->snr = calculate_snr(noise, c); + *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ - dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr); + dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise, + state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); return 0; } static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr) { - /* Return the raw noise value */ - static u8 buf[5];/* read data buffer */ - static u32 noise; /* noise value */ struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; + u8 buf[5]; /* read data buffer */ + u32 noise; /* noise value */ + u32 c; /* per-modulation SNR calculation constant */ - if (state->current_modulation == VSB_8) { - - i2c_read_demod_bytes(state, 0x6e, buf, 5); - /* Phase Tracker Mean-Square Error Register for VSB */ + switch(state->current_modulation) { + case VSB_8: + i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5); +#ifdef USE_EQMSE + /* Use Equalizer Mean-Square Error Register */ + /* SNR for ranges from -16.12 to +44.08 */ + noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2]; + c = 73957994; /* log10(25*32^2)*2^24 */ +#else + /* Use Phase Tracker Mean-Square Error Register */ + /* SNR for ranges from -13.11 to +44.08 */ noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4]; - } else { - - /* Carrier Recovery Mean-Square Error for QAM */ - i2c_read_demod_bytes(state, 0x1a, buf, 2); + c = 73957994; /* log10(25*32^2)*2^24 */ +#endif + break; + case QAM_64: + case QAM_256: + i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); noise = (buf[0] << 8) | buf[1]; + c = state->current_modulation == QAM_64 ? 97939837 : 98026066; + /* log10(688128)*2^24 and log10(696320)*2^24 */ + break; + default: + printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", + __FUNCTION__); + return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ } - /* Small values for noise mean signal is better so invert noise */ - *snr = ~noise; + state->snr = calculate_snr(noise, c); + *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ + + dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise, + state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); + + return 0; +} + +static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + /* Calculate Strength from SNR up to 35dB */ + /* Even though the SNR can go higher than 35dB, there is some comfort */ + /* factor in having a range of strong signals that can show at 100% */ + struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; + u16 snr; + int ret; - dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr); + ret = fe->ops.read_snr(fe, &snr); + if (ret != 0) + return ret; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; return 0; } diff --git a/drivers/media/dvb/frontends/lgdt330x_priv.h b/drivers/media/dvb/frontends/lgdt330x_priv.h index 59b7c5b..38c7669 100644 --- a/drivers/media/dvb/frontends/lgdt330x_priv.h +++ b/drivers/media/dvb/frontends/lgdt330x_priv.h @@ -51,14 +51,19 @@ enum I2C_REG { AGC_RFIF_ACC2= 0x3b, AGC_STATUS= 0x3f, SYNC_STATUS_VSB= 0x43, - EQPH_ERR0= 0x47, - EQ_ERR1= 0x48, - EQ_ERR2= 0x49, - PH_ERR1= 0x4a, - PH_ERR2= 0x4b, DEMUX_CONTROL= 0x66, + LGDT3302_EQPH_ERR0= 0x47, + LGDT3302_EQ_ERR1= 0x48, + LGDT3302_EQ_ERR2= 0x49, + LGDT3302_PH_ERR1= 0x4a, + LGDT3302_PH_ERR2= 0x4b, LGDT3302_PACKET_ERR_COUNTER1= 0x6a, LGDT3302_PACKET_ERR_COUNTER2= 0x6b, + LGDT3303_EQPH_ERR0= 0x6e, + LGDT3303_EQ_ERR1= 0x6f, + LGDT3303_EQ_ERR2= 0x70, + LGDT3303_PH_ERR1= 0x71, + LGDT3303_PH_ERR2= 0x72, LGDT3303_PACKET_ERR_COUNTER1= 0x8b, LGDT3303_PACKET_ERR_COUNTER2= 0x8c, }; -- cgit v0.10.2 From a75ec3f033625fb02ccc855fcd2e322275633fc4 Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Mon, 30 Oct 2006 19:56:59 -0300 Subject: V4L/DVB (4791): Added autodetected flag to the saa7134_dev structure In case the exact board type needs to be determined by probing or evaluating the eeprom, this flag allows to still set the board type via the card=xx insmod option. This is an extract of a patch by Francis Barber. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 5c9e63d..ed038ff 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -889,15 +889,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/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 799a714..40603f3 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -448,6 +448,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; -- cgit v0.10.2 From cbb94521cff24c98c2dbee28fb95e007c6bcaa43 Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Mon, 30 Oct 2006 20:00:16 -0300 Subject: V4L/DVB (4792): Add support for the Compro Videomate DVB-T200A This board has the same PCI ID as the T200, so the exact board type is determined from the eeprom. The original patch was provided by Francis Barber Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index a9bffd9..92ce6e2 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -101,3 +101,4 @@ 100 -> Asus Europa2 OEM [1043:4860] 101 -> Pinnacle PCTV 310i [11bd:002f] 102 -> Avermedia AVerTV Studio 507 [1461:9715] +103 -> Compro Videomate DVB-T200A diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 8109c8e..f5ad450 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3101,6 +3101,31 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x00, }, }, + [SAA7134_BOARD_VIDEOMATE_DVBT_200A] = { + /* Francis Barber */ + .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, + }}, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3823,6 +3848,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) 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: @@ -4064,6 +4090,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/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 706450c..098d8a6 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -336,7 +336,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 }; @@ -347,18 +347,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; } @@ -367,6 +357,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; @@ -1324,7 +1330,15 @@ 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; default: printk("%s: Huh? unknown DVB card?\n",dev->name); break; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 40603f3..0c0f465 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -229,6 +229,7 @@ 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_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 -- cgit v0.10.2 From 589d069cfe7f2bb37c4836641ddffe9e2b51e74e Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 2 Nov 2006 19:45:13 -0300 Subject: V4L/DVB (4794): Dvb: dibx000_common-fix Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c index 61d28de..a18c8f4 100644 --- a/drivers/media/dvb/frontends/dibx000_common.c +++ b/drivers/media/dvb/frontends/dibx000_common.c @@ -63,7 +63,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); -- cgit v0.10.2 From 9c4dfadbde3cfa78b92c28597125b8c41d36ffd0 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 4 Nov 2006 09:22:27 -0300 Subject: V4L/DVB (4796): A couple of V4L2 defines needed by Cafe Camara driver Two defines for V4L2, needed by the Cafe camera driver: 1) Add the RGB444 image format 2) Add the "init" internal command which is separate from "reset". Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index df5c465..5cb380a 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -244,6 +244,7 @@ struct v4l2_pix_format #define V4L2_PIX_FMT_YYUV v4l2_fourcc('Y','Y','U','V') /* 16 YUV 4:2:2 */ #define V4L2_PIX_FMT_HI240 v4l2_fourcc('H','I','2','4') /* 8 8-bit color */ #define V4L2_PIX_FMT_HM12 v4l2_fourcc('H','M','1','2') /* 8 YUV 4:2:0 16x16 macroblocks */ +#define V4L2_PIX_FMT_RGB444 v4l2_fourcc('R','4','4','4') /* 16 xxxxrrrr ggggbbbb */ /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index aecc946..8263ea0 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -251,4 +251,8 @@ struct v4l2_crystal_freq { If the frequency is not supported, then -EINVAL is returned. */ #define VIDIOC_INT_S_CRYSTAL_FREQ _IOW ('d', 113, struct v4l2_crystal_freq) +/* Initialize the sensor registors to some sort of reasonable + default values. */ +#define VIDIOC_INT_INIT _IOW ('d', 114, u32) + #endif /* V4L2_COMMON_H_ */ -- cgit v0.10.2 From d905b382d797a213e15868cbf3204f50ed52e30b Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 4 Nov 2006 09:25:53 -0300 Subject: V4L/DVB (4797): Marvell 88ALP01 "cafe" driver A driver for the Marvell M88ALP01 "CAFE" CMOS integrated camera controller. This driver has been renamed "cafe_ccic" since my previous patch set. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/cafe_ccic b/Documentation/video4linux/cafe_ccic new file mode 100644 index 0000000..8882102 --- /dev/null +++ b/Documentation/video4linux/cafe_ccic @@ -0,0 +1,54 @@ +"cafe_ccic" is a driver for the Marvell 88ALP01 "cafe" CMOS camera +controller. This is the controller found in first-generation OLPC systems, +and this driver was written with support from the OLPC project. + +Current status: the core driver works. It can generate data in YUV422, +RGB565, and RGB444 formats. (Anybody looking at the code will see RGB32 as +well, but that is a debugging aid which will be removed shortly). VGA and +QVGA modes work; CIF is there but the colors remain funky. Only the OV7670 +sensor is known to work with this controller at this time. + +To try it out: either of these commands will work: + + mplayer tv:// -tv driver=v4l2:width=640:height=480 -nosound + mplayer tv:// -tv driver=v4l2:width=640:height=480:outfmt=bgr16 -nosound + +The "xawtv" utility also works; gqcam does not, for unknown reasons. + +There are a few load-time options, most of which can be changed after +loading via sysfs as well: + + - alloc_bufs_at_load: Normally, the driver will not allocate any DMA + buffers until the time comes to transfer data. If this option is set, + then worst-case-sized buffers will be allocated at module load time. + This option nails down the memory for the life of the module, but + perhaps decreases the chances of an allocation failure later on. + + - dma_buf_size: The size of DMA buffers to allocate. Note that this + option is only consulted for load-time allocation; when buffers are + allocated at run time, they will be sized appropriately for the current + camera settings. + + - n_dma_bufs: The controller can cycle through either two or three DMA + buffers. Normally, the driver tries to use three buffers; on faster + systems, however, it will work well with only two. + + - min_buffers: The minimum number of streaming I/O buffers that the driver + will consent to work with. Default is one, but, on slower systems, + better behavior with mplayer can be achieved by setting to a higher + value (like six). + + - max_buffers: The maximum number of streaming I/O buffers; default is + ten. That number was carefully picked out of a hat and should not be + assumed to actually mean much of anything. + + - flip: If this boolean parameter is set, the sensor will be instructed to + invert the video image. Whether it makes sense is determined by how + your particular camera is mounted. + +Work is ongoing with this driver, stay tuned. + +jon + +Jonathan Corbet +corbet@lwn.net diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index b8fde5c..4ea1d0e 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -670,6 +670,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/drivers/media/video/Makefile b/drivers/media/video/Makefile index af57abc..8ff787a 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -92,6 +92,8 @@ 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_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o diff --git a/drivers/media/video/cafe_ccic-regs.h b/drivers/media/video/cafe_ccic-regs.h new file mode 100644 index 0000000..b2c22a0 --- /dev/null +++ b/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/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c new file mode 100644 index 0000000..2026c49 --- /dev/null +++ b/drivers/media/video/cafe_ccic.c @@ -0,0 +1,2237 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cafe_ccic-regs.h" + +#define CAFE_VERSION 0x000001 + + +/* + * Parameters. + */ +MODULE_AUTHOR("Jonathan Corbet "); +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 (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 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. + */ + cam_err(cam, "smbus_attach id = %d\n", client->driver->id); + 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) + cam->sensor = NULL; /* Bummer, no camera */ + 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|GPR_C1); + 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); + 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; +} + + +/* + * The TV Norm stuff is weird - we're a camera with little to do with TV, + * really. The following is what vivi does. + */ +static struct v4l2_tvnorm cafe_tvnorm[] = { + { + .name = "NTSC-M", + .id = V4L2_STD_NTSC_M, + } +}; + + +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 = cafe_tvnorm, + .tvnormsize = 1, + .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++; + } +} + + +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/include/linux/i2c-id.h b/include/linux/i2c-id.h index 0a8f750c..a32db16 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -250,6 +250,7 @@ #define I2C_HW_SMBUS_OV518 0x04000f /* OV518(+) USB 1.1 webcam ICs */ #define I2C_HW_SMBUS_OV519 0x040010 /* OV519 USB 1.1 webcam IC */ #define I2C_HW_SMBUS_OVFX2 0x040011 /* Cypress/OmniVision FX2 webcam */ +#define I2C_HW_SMBUS_CAFE 0x040012 /* Marvell 88ALP01 "CAFE" cam */ /* --- ISA pseudo-adapter */ #define I2C_HW_ISA 0x050000 -- cgit v0.10.2 From 111f33564e19b2b5f70e3df9a8f92c08c1c91fd9 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 4 Nov 2006 09:26:00 -0300 Subject: V4L/DVB (4798): OmniVision OV7670 driver This patch adds a V4L2 driver for the OmniVision OV7670 camera. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 4ea1d0e..c95735c 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -184,6 +184,14 @@ 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 && I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 8ff787a..2adb56d 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -93,6 +93,7 @@ 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 diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c new file mode 100644 index 0000000..382aa83 --- /dev/null +++ b/drivers/media/video/ov7670.c @@ -0,0 +1,1002 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + + +MODULE_AUTHOR("Jonathan Corbet 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); +} + + +/* + * 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; +} + + + + + +static struct ov7670_format_struct { + __u8 *desc; + __u32 pixelformat; + struct regval_list *regs; +} ov7670_formats[] = { + { + .desc = "YUYV 4:2:2", + .pixelformat = V4L2_PIX_FMT_YUYV, + .regs = ov7670_fmt_yuv422, + }, + { + .desc = "RGB 444", + .pixelformat = V4L2_PIX_FMT_RGB444, + .regs = ov7670_fmt_rgb444, + }, + { + .desc = "RGB 565", + .pixelformat = V4L2_PIX_FMT_RGB565, + .regs = ov7670_fmt_rgb565, + }, + /* + * 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, + }, +}; +#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. + */ +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... */ +/* 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, + }, + /* CIF */ + { + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .com7_bit = COM7_FMT_CIF, + .hstart = 170, /* Empirically determined */ + .hstop = 90, + .vstart = 14, + .vstop = 494, + }, + /* QVGA */ + { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .com7_bit = COM7_FMT_QVGA, + .hstart = 164, /* Empirically determined */ + .hstop = 20, + .vstart = 14, + .vstop = 494, + }, +}; + +#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; + 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); + return 0; +} + +/* + * Code for dealing with controls. + */ + +/* + * 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, unsigned char value) +{ + unsigned char com8; + int ret; + + ov7670_read(client, REG_COM8, &com8); + com8 &= ~COM8_AEC; + ov7670_write(client, REG_COM8, com8); + value = ov7670_abs_to_sm(value); + ret = ov7670_write(client, REG_BRIGHT, value); + return ret; +} + +static int ov7670_q_brightness(struct i2c_client *client, unsigned char *value) +{ + int ret; + ret = ov7670_read(client, REG_BRIGHT, value); + *value = ov7670_sm_to_abs(*value); + return ret; +} + +static int ov7670_t_contrast(struct i2c_client *client, unsigned char value) +{ + return ov7670_write(client, REG_CONTRAS, value); +} + +static int ov7670_q_contrast(struct i2c_client *client, unsigned char *value) +{ + return ov7670_read(client, REG_CONTRAS, value); +} + +static int ov7670_q_hflip(struct i2c_client *client, unsigned char *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, unsigned char 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, unsigned char *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, unsigned char 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, unsigned char *value); + int (*tweak)(struct i2c_client *c, unsigned char 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_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; + unsigned char v; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->query(client, &v); + if (ret >= 0) { + ctrl->value = v; + 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); + + if (octrl == NULL) + return -EINVAL; + return octrl->tweak(client, ctrl->value); +} + + + + + + + +/* + * Basic i2c stuff. + */ +static struct i2c_driver ov7670_driver; + +static int ov7670_attach(struct i2c_adapter *adapter) +{ + int ret; + struct i2c_client *client; + + printk(KERN_ERR "ov7670 attach, id = %d\n", adapter->id); + /* + * For now: only deal with adapters we recognize. + */ + if (adapter->id != I2C_HW_SMBUS_CAFE) + return -ENODEV; + + printk(KERN_ERR "ov7670 accepting\n"); + 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"); + /* Do we need clientdata? */ + + /* + * Make sure it's an ov7670 + */ + ret = ov7670_detect(client); + printk(KERN_ERR "detect result is %d\n", ret); + if (ret) + goto out_free; + i2c_attach_client(client); + return 0; + + out_free: + kfree(client); + return ret; +} + + +static int ov7670_detach(struct i2c_client *client) +{ + i2c_detach_client(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/include/linux/i2c-id.h b/include/linux/i2c-id.h index a32db16..0f4cf34 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -159,6 +159,7 @@ #define I2C_DRIVERID_ASB100 1043 #define I2C_DRIVERID_FSCHER 1046 #define I2C_DRIVERID_W83L785TS 1047 +#define I2C_DRIVERID_OV7670 1048 /* Omnivision 7670 camera */ /* * ---- Adapter types ---------------------------------------------------- diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 8263ea0..91b1992 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -144,6 +144,9 @@ enum v4l2_chip_ident { V4L2_IDENT_CX25841 = 241, V4L2_IDENT_CX25842 = 242, V4L2_IDENT_CX25843 = 243, + + /* OmniVision sensors - range 250-299 */ + V4L2_IDENT_OV7670 = 250, }; /* audio ioctls */ -- cgit v0.10.2 From 8637a8759585b97ec1d54ff4a4f33f34be4f5b1c Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Wed, 8 Nov 2006 16:47:32 -0300 Subject: V4L/DVB (4805): Dib0700: Add support for Leadtek Winfast DTV Dongle (STK7700P based) This patch adds support for the new, STK7700-based revision of the Leadtek Winfast DTV Dongle. Signed-off-by: Michal CIJOML Semler Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 1b1598d..1c3546d 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -277,6 +277,7 @@ 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) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -318,7 +319,7 @@ 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] }, @@ -339,6 +340,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/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 58e1a44..727d500 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -129,6 +129,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 -- cgit v0.10.2 From c6e53daffc2c6e66069304b3970256744074abec Mon Sep 17 00:00:00 2001 From: Thomas Genty Date: Sun, 5 Nov 2006 14:17:30 -0300 Subject: V4L/DVB (4806): Saa7134: add support for Hauppauge WinTV-HVR1110 DVB-T/Hybrid This patch adds support for the Hauppauge WinTV-HVR1110 DVB-T/Hybrid Signed-off-by: Thomas Genty Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 92ce6e2..ef67ace 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -102,3 +102,4 @@ 101 -> Pinnacle PCTV 310i [11bd:002f] 102 -> Avermedia AVerTV Studio 507 [1461:9715] 103 -> Compro Videomate DVB-T200A +104 -> Hauppauge WinTV-HVR1110 DVB-T/Hybrid [0070:6701] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index f5ad450..d8d8142 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3126,6 +3126,28 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE2, }}, }, + [SAA7134_BOARD_HAUPPAUGE_HVR1110] = { + /* Thomas Genty */ + .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, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3753,6 +3775,12 @@ 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, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -4053,6 +4081,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 */ diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 098d8a6..a51264f 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -900,6 +900,18 @@ static struct tda1004x_config pinnacle_pctv_310i_config = { /* ------------------------------------------------------------------ */ +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, + .if_freq = TDA10046_FREQ_045, + .request_firmware = philips_tda1004x_request_firmware, +}; + +/* ------------------------------------------------------------------ */ + static struct tda1004x_config asus_p7131_dual_config = { .demod_address = 0x08, .invert = 1, @@ -1221,6 +1233,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, diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 0c0f465..fee7512 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -230,6 +230,7 @@ struct saa7134_format { #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_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 -- cgit v0.10.2 From f2520106e78b250c2c4662608cb8db1169932a2d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 12 Nov 2006 09:28:46 -0300 Subject: V4L/DVB (4812): Detect presence of IR receiver/IR transmitter in tveeprom Thanks to input from Steven Toth from Hauppauge the tveeprom module has been extended to detect the presence of an IR transmitter (aka IR-blaster). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 6b9ef73..2624e3f 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -430,7 +430,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); @@ -653,13 +653,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/include/media/tveeprom.h b/include/media/tveeprom.h index e9fc1a7..5660ea2 100644 --- a/include/media/tveeprom.h +++ b/include/media/tveeprom.h @@ -3,7 +3,7 @@ struct tveeprom { u32 has_radio; - u32 has_ir; /* 0: no IR, 1: IR present, 2: unknown */ + u32 has_ir; /* bit 0: IR receiver present, bit 1: IR transmitter (blaster) present. -1 == unknown */ u32 has_MAC_address; /* 0: no MAC, 1: MAC present, 2: unknown */ u32 tuner_type; -- cgit v0.10.2 From 9292e91b51dfd6238704519b2dc8097c7232941f Mon Sep 17 00:00:00 2001 From: Paolo Ciarrocchi Date: Sun, 12 Nov 2006 14:19:15 -0300 Subject: V4L/DVB (4813): Added information about Technisat Sky2Pc cards Acked-by: Daniele Vallini Signed-off-by: Paolo Ciarrocchi Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/dvb/cards.txt b/Documentation/dvb/cards.txt index ca58e33..cc09187 100644 --- a/Documentation/dvb/cards.txt +++ b/Documentation/dvb/cards.txt @@ -22,10 +22,10 @@ o Frontends drivers: - ves1x93 : Alps BSRV2 (ves1893 demodulator) and dbox2 (ves1993) - cx24110 : Conexant HM1221/HM1811 (cx24110 or cx24106 demod, cx24108 PLL) - grundig_29504-491 : Grundig 29504-491 (Philips TDA8083 demodulator), tsa5522 PLL - - mt312 : Zarlink mt312 or Mitel vp310 demodulator, sl1935 or tsa5059 PLL + - mt312 : Zarlink mt312 or Mitel vp310 demodulator, sl1935 or tsa5059 PLLi, Technisat Sky2Pc with bios Rev. 2.3 - stv0299 : Alps BSRU6 (tsa5059 PLL), LG TDQB-S00x (tsa5059 PLL), LG TDQF-S001F (sl1935 PLL), Philips SU1278 (tua6100 PLL), - Philips SU1278SH (tsa5059 PLL), Samsung TBMU24112IMB + Philips SU1278SH (tsa5059 PLL), Samsung TBMU24112IMB, Technisat Sky2Pc with bios Rev. 2.6 DVB-C: - ves1820 : various (ves1820 demodulator, sp5659c or spXXXX PLL) - at76c651 : Atmel AT76c651(B) with DAT7021 PLL -- cgit v0.10.2 From f9aeba45000423cdf1211736592cb02c25f28437 Mon Sep 17 00:00:00 2001 From: Stefan Traby Date: Sun, 12 Nov 2006 13:02:51 -0300 Subject: V4L/DVB (4819): Dib0700: Add support for new revision of Nova-T Stick Added support for Nova-T Stick with USB-pid: 0x7060 Signed-off-by: Stefan Traby Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 1c3546d..4428ab3 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -278,6 +278,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { 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) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -326,7 +327,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { { 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", diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 727d500..ec1f3b3 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -101,6 +101,7 @@ #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_NEBULA_DIGITV 0x0201 #define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820 -- cgit v0.10.2 From ced8fecaecba6aa6b092abeb0c9e51deeeac9247 Mon Sep 17 00:00:00 2001 From: Jose Carlos Garcia Sogo Date: Tue, 14 Nov 2006 05:01:47 -0300 Subject: V4L/DVB (4823): Add alternative device ID (0xb808) for AverMedia AverTV Volar dongles. Add alternative device ID (0xb808) for AverMedia AverTV Volar dongles. Signed-off-by: Jose Carlos Garcia Sogo Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 4428ab3..2208757 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -279,6 +279,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { 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); @@ -331,7 +332,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { { 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", diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index ec1f3b3..6a55ea2 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -103,6 +103,7 @@ #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 -- cgit v0.10.2 From 5d9d171aaed2e0b1c5caac96efd4d4150eec97e1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Nov 2006 12:40:07 -0300 Subject: V4L/DVB (4825): FIX bug 5760: audio were not working on some bttv drivers This fixes a bug introduced by the -git commit: bbf7871e1cd58b89f77b1152f457250c6e94b614 It seems that some bttv apps can't work fine when audioset=0. Thanks to Christian Casteyde for pointing this. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 6e1ddad..3c8e474 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -1793,7 +1793,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; -- cgit v0.10.2 From ed5801477addfbf5a40408866f5b2837e100451f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 16 Nov 2006 11:02:28 -0300 Subject: V4L/DVB (4829): v4l2_type_names table is incomplete Sliced VBI types are missing at the tables. Fixing it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 78d28b0..b0b4abe 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -184,11 +184,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[] = { -- cgit v0.10.2 From c01d1e486a0fc489a495ea304616484bcbcf0018 Mon Sep 17 00:00:00 2001 From: Thomas Hamm Date: Fri, 17 Nov 2006 07:12:58 -0300 Subject: V4L/DVB (4833): Patch for SATELCO EasyWatch PCI (DVB-C) Add support for Satelco EasyWatch PCI DVBC cards Signed-off-by: Thomas Hamm Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 2235ff8..c731242 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -914,6 +914,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 +953,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,6 +1008,7 @@ 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, @@ -1242,6 +1245,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 +1264,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), -- cgit v0.10.2 From ab33668f8e1bd686ebc87d51be3a0cf99a267a9f Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 17 Nov 2006 11:59:22 -0300 Subject: V4L/DVB (4834): Cafe_ccic.c: make a function static This patch makes the needlessly global cafe_v4l_dev_release() static. Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 2026c49..684d817 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -1688,7 +1688,7 @@ static struct v4l2_tvnorm cafe_tvnorm[] = { }; -void cafe_v4l_dev_release(struct video_device *vd) +static void cafe_v4l_dev_release(struct video_device *vd) { struct cafe_camera *cam = container_of(vd, struct cafe_camera, v4ldev); -- cgit v0.10.2 From 550a9a5e5f8086ae410832f134a5d80b9bd7fdb6 Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Wed, 15 Nov 2006 21:31:54 -0300 Subject: V4L/DVB (4835): Added support for the Terratec Cinergy HT PCMCIA module This is a hybrid cardbus module. Besides the card support, i modified the definition names for AGC and GPIO of the tda10046. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index ef67ace..f6201cc 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -103,3 +103,4 @@ 102 -> Avermedia AVerTV Studio 507 [1461:9715] 103 -> Compro Videomate DVB-T200A 104 -> Hauppauge WinTV-HVR1110 DVB-T/Hybrid [0070:6701] +105 -> Terratec Cinergy HT PCMCIA [153b:1172] diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 11e0dca..00e4bcd 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/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/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h index 605ad2d..ec502d7 100644 --- a/drivers/media/dvb/frontends/tda1004x.h +++ b/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/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index d8d8142..d0ddc65 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3148,6 +3148,29 @@ struct saa7134_board saa7134_boards[] = { .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); @@ -3781,6 +3804,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .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, @@ -4107,6 +4136,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 */ diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index a51264f..ce888f7 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -677,7 +677,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, }; @@ -881,7 +881,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, }; @@ -893,7 +926,7 @@ 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, }; @@ -905,7 +938,7 @@ static struct tda1004x_config hauppauge_hvr_1110_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, }; @@ -917,7 +950,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, }; @@ -969,7 +1002,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, }; @@ -1006,7 +1039,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, }; @@ -1031,7 +1064,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, }; @@ -1076,7 +1109,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, }; @@ -1362,6 +1395,18 @@ static int dvb_init(struct saa7134_dev *dev) 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/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index fee7512..e88ad7b 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -230,7 +230,8 @@ struct saa7134_format { #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_HAUPPAUGE_HVR1110 104 +#define SAA7134_BOARD_CINERGY_HT_PCMCIA 105 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 -- cgit v0.10.2 From 1f683cd8bc8512c02a7d1b8358d71937d4c5134b Mon Sep 17 00:00:00 2001 From: Nico Sabbi Date: Wed, 15 Nov 2006 22:06:56 -0300 Subject: V4L/DVB (4836): Added support for both DVB frontends of the Lifeview Trio This card (like some others) supports both, DVB-T and a DVB-S. The patch adds an insmod option to select the frontend: use_frontend=0 -> DVB-T use_frontend=1 -> DVB-S Signed-off-by: Nico Sabbi Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index ce888f7..fa83398 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -50,6 +50,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) { @@ -1299,12 +1303,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: -- cgit v0.10.2 From 49c6b46a05ade5e1c572431844b77755527a152c Mon Sep 17 00:00:00 2001 From: Jelle Foks Date: Sat, 18 Nov 2006 15:47:11 -0300 Subject: V4L/DVB (4838): Fix cx88-blackbird null pointer Allows 'debug=1' for cx88-blackbird module (dev needs to be valid for dprintk). Fixes a null-pointer dereference when using debug=1. Signed-off-by: Jelle Foks Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 0037188..0cf0360 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -942,9 +942,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; -- cgit v0.10.2 From 2d90fa4412549faa3830789ca606b77827cde827 Mon Sep 17 00:00:00 2001 From: Jelle Foks Date: Sat, 18 Nov 2006 15:47:15 -0300 Subject: V4L/DVB (4839): Add support ptv-305 Add subvendor/device of the ADSTech Instant TV Deluxe PTV-305 to the card list as card=45 (KWORLD_HARDWARE_MPEG_TV_XPERT). Signed-off-by: Jelle Foks Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index f30f889..c791708 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1531,6 +1531,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, -- cgit v0.10.2 From 96389bf5153d19151edc565b397363e122cab4a7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 19 Nov 2006 05:45:52 -0300 Subject: V4L/DVB (4841): Added a newer PCI ID to CARDLIST.cx88 Signed-off-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88 index 8755b3e..62e32b4 100644 --- a/Documentation/video4linux/CARDLIST.cx88 +++ b/Documentation/video4linux/CARDLIST.cx88 @@ -43,7 +43,7 @@ 42 -> digitalnow DNTV Live! DVB-T Pro [1822:0025,1822:0019] 43 -> KWorld/VStream XPert DVB-T with cx22702 [17de:08a1,12ab:2300] 44 -> DViCO FusionHDTV DVB-T Dual Digital [18ac:db50,18ac:db54] - 45 -> KWorld HardwareMpegTV XPert [17de:0840] + 45 -> KWorld HardwareMpegTV XPert [17de:0840,1421:0305] 46 -> DViCO FusionHDTV DVB-T Hybrid [18ac:db40,18ac:db44] 47 -> pcHDTV HD5500 HDTV [7063:5500] 48 -> Kworld MCE 200 Deluxe [17de:0841] -- cgit v0.10.2 From f9a7615686a854cb94b5252e66b836a0a539ad9e Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sun, 19 Nov 2006 19:04:55 -0300 Subject: V4L/DVB (4842): Updated camera driver A couple of Cafe driver fixes, and support for the hue and saturation controls. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 684d817..dc6e16c 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -505,6 +505,8 @@ static struct i2c_algorithm cafe_smbus_algo = { /* 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) { @@ -513,7 +515,6 @@ static int cafe_smbus_attach(struct i2c_client *client) /* * Don't talk to chips we don't recognize. */ - cam_err(cam, "smbus_attach id = %d\n", client->driver->id); if (client->driver->id == I2C_DRIVERID_OV7670) { cam->sensor = client; return cafe_cam_init(cam); @@ -525,8 +526,13 @@ static int cafe_smbus_detach(struct i2c_client *client) { struct cafe_camera *cam = i2c_get_adapdata(client->adapter); - if (cam->sensor == client) + 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; } @@ -774,7 +780,7 @@ static void cafe_ctlr_power_up(struct cafe_camera *cam) * 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|GPR_C1); + 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? */ @@ -1468,8 +1474,11 @@ static int cafe_v4l_release(struct inode *inode, struct file *filp) cafe_free_sio_buffers(cam); cam->owner = NULL; } - if (cam->users == 0) + 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; } diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 382aa83..b7d824e 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -138,6 +138,20 @@ MODULE_LICENSE("GPL"); #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 */ @@ -160,6 +174,19 @@ MODULE_LICENSE("GPL"); /* + * 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. @@ -179,7 +206,7 @@ static struct regval_list ov7670_default_regs[] = { * 2 = 20fps * 1 = 30fps */ - { REG_CLKRC, 0x1 }, /* OV: clock scale (15 fps) */ + { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ { REG_TSLB, 0x04 }, /* OV */ { REG_COM7, 0 }, /* VGA */ /* @@ -286,10 +313,6 @@ static struct regval_list ov7670_default_regs[] = { { 0x79, 0x05 }, { 0xc8, 0x30 }, { 0x79, 0x26 }, - /* Not sure if these should be here */ - { 0xf1, 0x10 }, { 0x0f, 0x1d }, - { 0x0f, 0x1f }, - { 0xff, 0xff }, /* END MARKER */ }; @@ -312,6 +335,7 @@ static struct regval_list ov7670_fmt_yuv422[] = { { 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" */ @@ -327,6 +351,7 @@ static struct regval_list ov7670_fmt_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" */ @@ -342,6 +367,7 @@ static struct regval_list ov7670_fmt_rgb444[] = { { 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" */ @@ -362,7 +388,7 @@ static int ov7670_read(struct i2c_client *c, unsigned char reg, int ret; ret = i2c_smbus_read_byte_data(c, reg); - if (ret > 0) + if (ret >= 0) *value = (unsigned char) ret; return ret; } @@ -442,28 +468,34 @@ static int ov7670_detect(struct i2c_client *client) } - - - +/* + * 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 @@ -476,6 +508,7 @@ static struct ov7670_format_struct { .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])) @@ -488,6 +521,32 @@ static struct ov7670_format_struct { /* * 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; @@ -496,6 +555,7 @@ static struct ov7670_win_size { 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 */ @@ -507,6 +567,7 @@ static struct ov7670_win_size { .hstop = 14, /* Omnivision */ .vstart = 10, .vstop = 490, + .regs = NULL, }, /* CIF */ { @@ -517,6 +578,7 @@ static struct ov7670_win_size { .hstop = 90, .vstart = 14, .vstop = 494, + .regs = NULL, }, /* QVGA */ { @@ -527,6 +589,18 @@ static struct ov7670_win_size { .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, }, }; @@ -610,7 +684,7 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, wsize++) if (pix->width >= wsize->width && pix->height >= wsize->height) break; - if (wsize > ov7670_win_sizes + N_WIN_SIZES) + if (wsize >= ov7670_win_sizes + N_WIN_SIZES) wsize--; /* Take the smallest one */ if (ret_wsize != NULL) *ret_wsize = wsize; @@ -624,7 +698,6 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, pix->bytesperline *= 2; pix->sizeimage = pix->height*pix->bytesperline; return 0; - } /* @@ -635,6 +708,7 @@ 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); @@ -655,6 +729,10 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) 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; } @@ -662,6 +740,168 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) * Code for dealing with controls. */ + + + + +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! */ @@ -682,38 +922,43 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) return (128 - v) | 0x80; } -static int ov7670_t_brightness(struct i2c_client *client, unsigned char value) +static int ov7670_t_brightness(struct i2c_client *client, int value) { - unsigned char com8; + unsigned char com8, v; int ret; ov7670_read(client, REG_COM8, &com8); com8 &= ~COM8_AEC; ov7670_write(client, REG_COM8, com8); - value = ov7670_abs_to_sm(value); - ret = ov7670_write(client, REG_BRIGHT, value); + v = ov7670_abs_to_sm(value); + ret = ov7670_write(client, REG_BRIGHT, v); return ret; } -static int ov7670_q_brightness(struct i2c_client *client, unsigned char *value) +static int ov7670_q_brightness(struct i2c_client *client, __s32 *value) { - int ret; - ret = ov7670_read(client, REG_BRIGHT, value); - *value = ov7670_sm_to_abs(*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, unsigned char value) +static int ov7670_t_contrast(struct i2c_client *client, int value) { - return ov7670_write(client, REG_CONTRAS, value); + return ov7670_write(client, REG_CONTRAS, (unsigned char) value); } -static int ov7670_q_contrast(struct i2c_client *client, unsigned char *value) +static int ov7670_q_contrast(struct i2c_client *client, __s32 *value) { - return ov7670_read(client, REG_CONTRAS, 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, unsigned char *value) +static int ov7670_q_hflip(struct i2c_client *client, __s32 *value) { int ret; unsigned char v; @@ -724,7 +969,7 @@ static int ov7670_q_hflip(struct i2c_client *client, unsigned char *value) } -static int ov7670_t_hflip(struct i2c_client *client, unsigned char value) +static int ov7670_t_hflip(struct i2c_client *client, int value) { unsigned char v; int ret; @@ -741,7 +986,7 @@ static int ov7670_t_hflip(struct i2c_client *client, unsigned char value) -static int ov7670_q_vflip(struct i2c_client *client, unsigned char *value) +static int ov7670_q_vflip(struct i2c_client *client, __s32 *value) { int ret; unsigned char v; @@ -752,7 +997,7 @@ static int ov7670_q_vflip(struct i2c_client *client, unsigned char *value) } -static int ov7670_t_vflip(struct i2c_client *client, unsigned char value) +static int ov7670_t_vflip(struct i2c_client *client, int value) { unsigned char v; int ret; @@ -770,8 +1015,8 @@ static int ov7670_t_vflip(struct i2c_client *client, unsigned char value) static struct ov7670_control { struct v4l2_queryctrl qc; - int (*query)(struct i2c_client *c, unsigned char *value); - int (*tweak)(struct i2c_client *c, unsigned char value); + int (*query)(struct i2c_client *c, __s32 *value); + int (*tweak)(struct i2c_client *c, int value); } ov7670_controls[] = { { @@ -804,6 +1049,34 @@ static struct ov7670_control { }, { .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", @@ -857,25 +1130,26 @@ static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) { struct ov7670_control *octrl = ov7670_find_control(ctrl->id); int ret; - unsigned char v; if (octrl == NULL) return -EINVAL; - ret = octrl->query(client, &v); - if (ret >= 0) { - ctrl->value = v; + 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; - return octrl->tweak(client, ctrl->value); + ret = octrl->tweak(client, ctrl->value); + if (ret >= 0) + return 0; + return ret; } @@ -883,7 +1157,6 @@ static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) - /* * Basic i2c stuff. */ @@ -893,15 +1166,14 @@ static int ov7670_attach(struct i2c_adapter *adapter) { int ret; struct i2c_client *client; + struct ov7670_info *info; - printk(KERN_ERR "ov7670 attach, id = %d\n", adapter->id); /* * For now: only deal with adapters we recognize. */ if (adapter->id != I2C_HW_SMBUS_CAFE) return -ENODEV; - printk(KERN_ERR "ov7670 accepting\n"); client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL); if (! client) return -ENOMEM; @@ -909,18 +1181,29 @@ static int ov7670_attach(struct i2c_adapter *adapter) client->addr = OV7670_I2C_ADDR; client->driver = &ov7670_driver, strcpy(client->name, "OV7670"); - /* Do we need clientdata? */ + /* + * 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); - printk(KERN_ERR "detect result is %d\n", ret); if (ret) - goto out_free; + goto out_free_info; i2c_attach_client(client); return 0; + out_free_info: + kfree(info); out_free: kfree(client); return ret; @@ -930,6 +1213,7 @@ static int ov7670_attach(struct i2c_adapter *adapter) static int ov7670_detach(struct i2c_client *client) { i2c_detach_client(client); + kfree(i2c_get_clientdata(client)); kfree(client); return 0; } -- cgit v0.10.2 From 76db93d03f1e9a9a3371f787ae30780cdf10400c Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 19 Nov 2006 19:45:26 -0300 Subject: V4L/DVB (4846): Create new lgh06xf atsc tuner module This patch creates a new atsc tuner module for the LG TDVS-H06xF ATSC tuners, called lgh06xf. The purpose of this change is to reduce some duplicated code, and to allow the lgh06xf tuner code to take advantage of dvb_attach(). As a side effect, the dependency of dvb-bt8xx on dvb-pll has been removed, since the lgh06xf module itself will use dvb-pll, while remaining optional for the dvb-bt8xx driver through the use of DVB_FE_CUSTOMISE Acked-by: Andrew de Quincey Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig index a0dcd59..7987595 100644 --- a/drivers/media/dvb/b2c2/Kconfig +++ b/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/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index b8ba878..c2b35e3 100644 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -14,7 +14,7 @@ #include "stv0297.h" #include "mt312.h" #include "lgdt330x.h" -#include "lg_h06xf.h" +#include "lgh06xf.h" #include "dvb-pll.h" /* lnb control */ @@ -303,12 +303,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, @@ -533,7 +527,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/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig index ae2ff5d..dd66b60 100644 --- a/drivers/media/dvb/bt8xx/Kconfig +++ b/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/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c index 14e69a7..80a85cb 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -34,7 +34,6 @@ #include "dvb_frontend.h" #include "dvb-bt8xx.h" #include "bt878.h" -#include "dvb-pll.h" static int debug; @@ -568,12 +567,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, @@ -616,7 +609,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/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h index 4745a90..e75f417 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.h @@ -37,7 +37,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/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index dfd53d5..bfe5691 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/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 diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 4e2eef8..ae039b9 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/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/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 7279e58..af314bb 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/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/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 593152e..3fa6e5d 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/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/drivers/media/dvb/frontends/lg_h06xf.h b/drivers/media/dvb/frontends/lg_h06xf.h deleted file mode 100644 index 754d51d..0000000 --- a/drivers/media/dvb/frontends/lg_h06xf.h +++ /dev/null @@ -1,64 +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. */ - buf[0] = buf[2]; - buf[0] &= ~0x20; - buf[0] |= 0x18; - buf[1] = 0x50; - msg.len = 2; - 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/drivers/media/dvb/frontends/lgh06xf.c b/drivers/media/dvb/frontends/lgh06xf.c new file mode 100644 index 0000000..badbea8 --- /dev/null +++ b/drivers/media/dvb/frontends/lgh06xf.c @@ -0,0 +1,139 @@ +/* + * 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 div; + int i; + 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(priv->i2c, &msg, 1)) != 1) { + printk(KERN_WARNING "lgh06xf: %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. */ + buf[0] = buf[2]; + buf[0] &= ~0x20; + buf[0] |= 0x18; + buf[1] = 0x50; + msg.len = 2; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((err = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + printk(KERN_WARNING "lgh06xf: %s error " + "(addr %02x <- %02x, err = %i)\n", + __FUNCTION__, buf[0], buf[1], err); + if (err < 0) + return err; + else + return -EREMOTEIO; + } + + // calculate the frequency we set it to + for (i = 0; i < dvb_pll_lg_tdvs_h06xf.count; i++) { + if (params->frequency > dvb_pll_lg_tdvs_h06xf.entries[i].limit) + continue; + break; + } + div = (params->frequency + dvb_pll_lg_tdvs_h06xf.entries[i].offset) / + dvb_pll_lg_tdvs_h06xf.entries[i].stepsize; + priv->frequency = (div * dvb_pll_lg_tdvs_h06xf.entries[i].stepsize) - + dvb_pll_lg_tdvs_h06xf.entries[i].offset; + + 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/drivers/media/dvb/frontends/lgh06xf.h b/drivers/media/dvb/frontends/lgh06xf.h new file mode 100644 index 0000000..41c41fc --- /dev/null +++ b/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/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index 0f9d969..b2a66ba 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/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/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 8150c09..0266a36 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -42,7 +42,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" @@ -392,18 +392,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; @@ -719,7 +707,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; @@ -737,7 +726,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; -- cgit v0.10.2 From 2213918a99d6292767b6d4aae3e3f4b0520528a5 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 19 Nov 2006 19:49:11 -0300 Subject: V4L/DVB (4847): Drivers/media/dvb/frontends: kfree() cleanups We don't have to check for NULL before kfree() Acked-by: Andrew de Quincey Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index b7e7108..78114fc 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -478,8 +478,7 @@ 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; } diff --git a/drivers/media/dvb/frontends/tda826x.c b/drivers/media/dvb/frontends/tda826x.c index 34815b0..4f01d7b 100644 --- a/drivers/media/dvb/frontends/tda826x.c +++ b/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; } diff --git a/drivers/media/dvb/frontends/tua6100.c b/drivers/media/dvb/frontends/tua6100.c index 8855439..6ba0029 100644 --- a/drivers/media/dvb/frontends/tua6100.c +++ b/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; } -- cgit v0.10.2 From fa4cdd0dc4c066a1a4df19005912bf453dc37b6d Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Sun, 19 Nov 2006 22:58:19 -0300 Subject: V4L/DVB (4850): Lgh06xf: fix compiler error when not selected fix build error: lgh06xf.h:29: error: syntax error before '{' token Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/lgh06xf.h b/drivers/media/dvb/frontends/lgh06xf.h index 41c41fc..510b4be 100644 --- a/drivers/media/dvb/frontends/lgh06xf.h +++ b/drivers/media/dvb/frontends/lgh06xf.h @@ -25,7 +25,7 @@ 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); + struct i2c_adapter *i2c) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); return NULL; -- cgit v0.10.2 From ff67c614e23bf5a3c16968e2c42ab442121c4beb Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 19 Nov 2006 20:50:31 -0300 Subject: V4L/DVB (4852): Pvrusb2: Fix horizontal resolution setting problem for 24xxx devices Fix long-standing issue which prevented use of 24xxx devices at any horizontal resolution other an 720. The problem is in the cx25840 module, if no attempt is made to initialize VBI there. Such a thing should not be needed, but the pvrusb2 driver is updated now to deal with this. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index c80c26b..848fb233 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/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 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/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 1f78733..d200496 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -356,28 +356,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. */ @@ -758,10 +736,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", -- cgit v0.10.2 From b07b4783fb30dee8c542fc76ed8993108d46d6aa Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 20 Nov 2006 10:23:04 -0300 Subject: V4L/DVB (4854): Handle errors from input_register_device() Also sprinkled some input_sync() throughout the code. Acked-by: Ricardo Cerqueira Acked-by: Oliver Endriss Acked-by: Andrew de Quincey Signed-off-by: Dmitry Torokhov Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c index 9123147..d64b96c 100644 --- a/drivers/media/dvb/cinergyT2/cinergyT2.c +++ b/drivers/media/dvb/cinergyT2/cinergyT2.c @@ -746,6 +746,7 @@ static void cinergyt2_query_rc (struct work_struct *work) 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; @@ -783,6 +784,7 @@ static void cinergyt2_query_rc (struct work_struct *work) 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; } } @@ -798,8 +800,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; @@ -817,7 +820,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/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index 794e447..19ff597 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -90,7 +90,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 || @@ -100,23 +102,24 @@ 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); - d->rc_input_dev->cdev.dev = &d->udev->dev; + 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); + input_dev->cdev.dev = &d->udev->dev; /* 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. */ @@ -124,10 +127,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_DELAYED_WORK(&d->rc_query_work, dvb_usb_read_remote_control); diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c index d54bbcd..e4544ea 100644 --- a/drivers/media/dvb/ttpci/av7110_ir.c +++ b/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/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index cd5ec48..25d0dfc 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -143,14 +143,14 @@ static void msp430_ir_debounce(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; + input_event(dev, EV_KEY, key_map[dev->repeat_key], 0); + } else { + 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 */ } - - 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 */ + input_sync(dev); } static void msp430_ir_interrupt(unsigned long data) @@ -169,7 +169,7 @@ static void msp430_ir_interrupt(unsigned long data) return; } del_timer(&dev->timer); - input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0); + input_event(dev, EV_KEY, key_map[dev->repeat_key], 0); } if (!key_map[code]) { @@ -177,15 +177,14 @@ static void msp430_ir_interrupt(unsigned long data) return; } + input_event(dev, EV_KEY, key_map[code], 1); + input_sync(dev); + /* 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); + mod_timer(&dev->timer, jiffies + msecs_to_jiffies(350)); } } @@ -194,8 +193,9 @@ 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; + int err; - budget_ci->input_dev = input_dev = input_allocate_device(); + input_dev = input_allocate_device(); if (!input_dev) return -ENOMEM; @@ -208,10 +208,16 @@ static int msp430_ir_init(struct budget_ci *budget_ci) if (key_map[i]) set_bit(key_map[i], input_dev->keybit); - input_register_device(budget_ci->input_dev); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } input_dev->timer.function = msp430_ir_debounce; + budget_ci->input_dev = input_dev; + saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); @@ -226,8 +232,10 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); - if (del_timer(&dev->timer)) - input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0); + if (del_timer(&dev->timer)) { + input_event(dev, EV_KEY, key_map[dev->repeat_key], 0); + input_sync(dev); + } input_unregister_device(dev); } diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c index 10b121a..bd6e7ba 100644 --- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -238,6 +238,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); } @@ -1187,11 +1188,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; @@ -1205,8 +1207,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/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index 933d6db..cbc012f 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -259,24 +259,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) { @@ -348,10 +383,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) { @@ -389,32 +423,26 @@ int bttv_input_init(struct bttv *btv) input_dev->cdev.dev = &btv->c.pci->dev; 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) @@ -422,22 +450,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/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index e60a0a5..c6d412b 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -155,6 +155,35 @@ static void cx88_ir_work(struct work_struct *work) 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) @@ -163,14 +192,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; @@ -280,9 +307,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 */ @@ -307,23 +333,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); - 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) @@ -334,15 +359,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/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index ab87e7b..dd6341f 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -305,15 +305,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; @@ -361,26 +360,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) */ @@ -389,15 +389,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); @@ -407,6 +409,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) @@ -414,7 +423,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/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 83887d1..5d2bf15 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -131,6 +131,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; @@ -141,6 +158,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; @@ -267,9 +285,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; @@ -300,18 +317,22 @@ int saa7134_input_init1(struct saa7134_dev *dev) } input_dev->cdev.dev = &dev->pci->dev; - /* 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) @@ -319,8 +340,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/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index bbf2bee..ec0ff22 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -86,6 +86,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)); @@ -106,7 +107,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) -- cgit v0.10.2 From 61cebe9db7992c2c6ccb91f179f56008e74c2de1 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Sun, 19 Nov 2006 14:10:59 -0300 Subject: V4L/DVB (4855): Support KNC1 DVBC cards with alternative tda10021 i2c address For some reason, some of these cards have the tda10021 configured to a different address. This adds support for such cards Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index c731242..461e8d7 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/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, +}; + @@ -1013,6 +1017,10 @@ static void frontend_init(struct budget_av *budget_av) 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; -- cgit v0.10.2 From c6aeb11127a8a4266d613563f737a1d60cfae628 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 20 Nov 2006 12:06:54 -0300 Subject: V4L/DVB (4857): Cleans some ioctl structs before calling V4L2 counterpart Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index 1d899e2..06221a0 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -714,6 +714,7 @@ v4l_compat_translate_ioctl(struct inode *inode, case VIDIOCGFREQ: /* get frequency */ { unsigned long *freq = arg; + memset(&freq2,0,sizeof(freq2)); freq2.tuner = 0; err = drv(inode, file, VIDIOC_G_FREQUENCY, &freq2); @@ -726,8 +727,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); @@ -738,6 +739,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) { @@ -898,6 +900,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); -- cgit v0.10.2 From 2aa92ffd58493887adde850fe510031e48f12bac Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 20 Nov 2006 12:10:04 -0300 Subject: V4L/DVB (4858): Fix: implement missing VIDIOCSTUNER on v4l1-compat module Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index 06221a0..701856e 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -708,7 +708,16 @@ v4l_compat_translate_ioctl(struct inode *inode, } case VIDIOCSTUNER: /* select a tuner input */ { - err = 0; + 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); + break; } case VIDIOCGFREQ: /* get frequency */ -- cgit v0.10.2 From 7964b1b1c27dea88eb89b10f774893ec11cf973d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 20 Nov 2006 12:10:43 -0300 Subject: V4L/DVB (4859): Fix initializations on some video_ioctl2 handlers Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 41ec0c4..9024ea2 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -1088,9 +1088,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) @@ -1329,8 +1333,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, " @@ -1363,6 +1373,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", -- cgit v0.10.2 From 207705cd7f82b9f160c6ed552d5788a823701fd1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 20 Nov 2006 12:13:25 -0300 Subject: V4L/DVB (4860): Optimization of v4l1 handling Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 9024ea2..ad42dee 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -428,6 +428,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: @@ -1409,12 +1413,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) { -- cgit v0.10.2 From e75f9cee32827853fc2f9d1ceb6352e3edc33e9d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 20 Nov 2006 13:19:20 -0300 Subject: V4L/DVB (4861): Remove the need of a STD array for drivers using video_ioctl2 video_ioctl2 will auto-generate standard entries at ENUM_FMT. Also, now, a driver may return a subset of the video array at the return, to be stored as the current_norm. For example, a driver may ask for V4L2_STD_PAL. At return, driver may change it to V4L2_STD_PAL_B. This way, a futher call to G_STD will return the exact detected video std. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index dc6e16c..30864da 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -1679,24 +1679,11 @@ static int cafe_vidioc_s_input(struct file *filp, void *priv, unsigned int i) } /* from vivi.c */ -static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id a) +static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) { return 0; } - -/* - * The TV Norm stuff is weird - we're a camera with little to do with TV, - * really. The following is what vivi does. - */ -static struct v4l2_tvnorm cafe_tvnorm[] = { - { - .name = "NTSC-M", - .id = V4L2_STD_NTSC_M, - } -}; - - static void cafe_v4l_dev_release(struct video_device *vd) { struct cafe_camera *cam = container_of(vd, struct cafe_camera, v4ldev); @@ -1726,8 +1713,7 @@ static struct video_device cafe_v4l_template = { .type = VFL_TYPE_GRABBER, .type2 = VID_TYPE_CAPTURE, .minor = -1, /* Get one dynamically */ - .tvnorms = cafe_tvnorm, - .tvnormsize = 1, + .tvnorms = V4L2_STD_NTSC_M, .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */ .fops = &cafe_v4l_fops, diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index b0b4abe..752c82c 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -87,6 +87,78 @@ MODULE_LICENSE("GPL"); */ +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, @@ -1453,6 +1525,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/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index ad42dee..1be7127 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -833,20 +833,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, " @@ -872,39 +937,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; } @@ -1296,25 +1345,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)); diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 9986de5..474ddb7 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -1044,16 +1044,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; } @@ -1333,8 +1325,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 @@ -1361,8 +1353,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/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 6a11d77..fb96472 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -43,6 +43,7 @@ /* Video standard functions */ extern unsigned int v4l2_video_std_fps(struct v4l2_standard *vs); +extern char *v4l2_norm_to_name(v4l2_std_id id); extern int v4l2_video_std_construct(struct v4l2_standard *vs, int id, char *name); @@ -81,12 +82,6 @@ extern long v4l_compat_ioctl32(struct file *file, unsigned int cmd, * This version moves redundant code from video device code to * the common handler */ -struct v4l2_tvnorm { - char *name; - v4l2_std_id id; - - void *priv_data; -}; struct video_device { @@ -104,9 +99,8 @@ struct video_device int debug; /* Activates debug level*/ /* Video standard vars */ - int tvnormsize; /* Size of tvnorm array */ - v4l2_std_id current_norm; /* Current tvnorm */ - struct v4l2_tvnorm *tvnorms; + v4l2_std_id tvnorms; /* Supported tv norms */ + v4l2_std_id current_norm; /* Current tvnorm */ /* callbacks */ void (*release)(struct video_device *vfd); @@ -211,7 +205,7 @@ struct video_device /* Standard handling G_STD and ENUMSTD are handled by videodev.c */ - int (*vidioc_s_std) (struct file *file, void *fh, v4l2_std_id a); + int (*vidioc_s_std) (struct file *file, void *fh, v4l2_std_id *norm); int (*vidioc_querystd) (struct file *file, void *fh, v4l2_std_id *a); /* Input handling */ -- cgit v0.10.2 From 499c18698990463b84c23ddc98104517fe759ee8 Mon Sep 17 00:00:00 2001 From: "audetto@tiscali.it" Date: Mon, 20 Nov 2006 18:27:44 -0300 Subject: V4L/DVB (4862): Fixes uninitialized variables passed to VIDIOC_G_FBUF. Signed-off-by: Andrea A Odetti Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index 701856e..8a13e59 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -350,6 +350,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) { @@ -616,6 +617,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); -- cgit v0.10.2 From 8949f1a2424a38a4080a2a3561a2698f215a7c4a Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Tue, 21 Nov 2006 05:35:10 -0300 Subject: V4L/DVB (4864): TDA826x I2C read with 2 messages Added a dump I2C message to the TDA826x-driver to fix I2C read for identification with ttusb2-driver. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/tda826x.c b/drivers/media/dvb/frontends/tda826x.c index 4f01d7b..79f971d 100644 --- a/drivers/media/dvb/frontends/tda826x.c +++ b/drivers/media/dvb/frontends/tda826x.c @@ -132,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; -- cgit v0.10.2 From bc2e3913c786d7387e21ee0818c1a3b66a571703 Mon Sep 17 00:00:00 2001 From: Patrick Boettcher Date: Sat, 2 Dec 2006 21:16:04 -0200 Subject: V4L/DVB (4863): Adding support for Pinnacle PCTV 400e DVB-S Adding support for Pinnacle PCTV 400e DVB-S. The module name is called ttusb2, because it this device (and other Pinnacle devices) is using the USB-protocol originally used by Technotrend device. I'm suspecting Technotrend as the device-designer. Signed-off-by: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index bfe5691..ad52143 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -160,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/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index e239107..154d593 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/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/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c new file mode 100644 index 0000000..95d2997 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/ttusb2.c @@ -0,0 +1,270 @@ +/* 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 + * Copyright (c) 2003 Felix Domke + * Copyright (C) 2005-6 Patrick Boettcher + * + * 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); +} + + +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 "); +MODULE_DESCRIPTION("Driver for Pinnacle PCTV 400e DVB-S USB2.0"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/ttusb2.h b/drivers/media/dvb/dvb-usb/ttusb2.h new file mode 100644 index 0000000..52a63af --- /dev/null +++ b/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 + * Copyright (c) 2003 Felix Domke + * Copyright (C) 2005-6 Patrick Boettcher + * + * 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 + * + * in message (complete block is always 0x40 bytes long) + * 0x55 + * + * id is incremented for each transaction + */ + +#define CMD_DSP_DOWNLOAD 0x13 +/* out data: [28] + * last block must be empty */ + +#define CMD_DSP_BOOT 0x14 +/* out data: nothing */ + +#define CMD_POWER 0x15 +/* out data: */ + +#define CMD_LNB 0x16 +/* out data: <18V=0,13V=1> */ + +#define CMD_GET_VERSION 0x17 +/* in data: [5] */ + +#define CMD_DISEQC 0x18 +/* out data: [cmdlen] */ + +#define CMD_PID_ENABLE 0x22 +/* out data: */ + +#define CMD_PID_DISABLE 0x23 +/* out data: */ + +#define CMD_FILTER_ENABLE 0x24 +/* out data: [12] [12] */ + +#define CMD_FILTER_DISABLE 0x25 +/* out data: */ + +#define CMD_GET_DSP_VERSION 0x26 +/* in data: [28] */ + +#define CMD_I2C_XFER 0x31 +/* out data: [sndlen] + * in data: [rcvlen] */ + +#define CMD_I2C_BITRATE 0x32 +/* out data: */ + +#endif -- cgit v0.10.2 From 47ae9ae895a5228e7a3ca47b1c9ab00a3f641923 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 20 Nov 2006 16:38:42 -0300 Subject: V4L/DVB (4867): Whitespace cleanup - adhere to 80-column limit - replace some spaces with tabs Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index 78114fc..b0785d5 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -488,7 +488,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; @@ -516,12 +517,14 @@ 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; @@ -535,7 +538,8 @@ 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; if (fe->ops.i2c_gate_ctrl) @@ -550,14 +554,18 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, struct dvb_frontend_param 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; + 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->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; @@ -573,7 +581,8 @@ 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; buf[0] = priv->pll_i2c_address; @@ -583,8 +592,10 @@ static int dvb_pll_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parame 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; + 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->bandwidth = bandwidth; return 5; @@ -613,10 +624,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; @@ -639,7 +653,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/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h index ed5ac5a..681186a 100644 --- a/drivers/media/dvb/frontends/dvb-pll.h +++ b/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/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 461e8d7..8bdc90a 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -835,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) -- cgit v0.10.2 From 89faeefcf875c16d8923eaed50bd57731c221bcc Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 20 Nov 2006 16:45:29 -0300 Subject: V4L/DVB (4868): Dvb-pll: return frequency set by dvb_pll_configure() This patch removes some duplicated code by returning the frequency set by dvb_pll_configure(), instead of recalculating it again in dvb_pll_set_params() and dvb_pll_calc_regs(). If the return value of dvb_pll_configure is less than zero, it is an error code. Otherwise, the return value is the frequency actually set by the function. Signed-off-by: Michael Krufky Acked-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index b0785d5..62de760 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -472,7 +472,8 @@ 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); @@ -526,9 +527,7 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, { .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; @@ -538,9 +537,11 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, 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); @@ -548,16 +549,7 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, 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; @@ -569,9 +561,7 @@ static int dvb_pll_calc_regs(struct dvb_frontend *fe, { 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; @@ -582,20 +572,14 @@ static int dvb_pll_calc_regs(struct dvb_frontend *fe, } if ((result = dvb_pll_configure(priv->pll_desc, buf+1, - params->frequency, bandwidth)) != 0) + 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; -- cgit v0.10.2 From fd83e2487e9149d8cd7c8b1e7d6f0e5231b79f8a Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Mon, 20 Nov 2006 17:03:48 -0300 Subject: V4L/DVB (4869): Lgh06xf: use return value of dvb_pll_configure() Use return value of dvb_pll_configure() to fill priv->frequency In lgh06xf_set_params: Rename int variable "err" to "result". Remove needless calculation of the set frequency, since this value is now being returned by dvb_pll_configure(). Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/lgh06xf.c b/drivers/media/dvb/frontends/lgh06xf.c index badbea8..2202d0c 100644 --- a/drivers/media/dvb/frontends/lgh06xf.c +++ b/drivers/media/dvb/frontends/lgh06xf.c @@ -40,19 +40,23 @@ static int lgh06xf_set_params(struct dvb_frontend* fe, u8 buf[4]; struct i2c_msg msg = { .addr = LG_H06XF_PLL_I2C_ADDR, .flags = 0, .buf = buf, .len = sizeof(buf) }; - u32 div; - int i; - int err; + u32 frequency; + int result; + + if ((result = dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, + params->frequency, 0)) < 0) + return result; + else + frequency = result; - 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(priv->i2c, &msg, 1)) != 1) { + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { printk(KERN_WARNING "lgh06xf: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; + "(addr %02x <- %02x, result = %i)\n", + __FUNCTION__, buf[0], buf[1], result); + if (result < 0) + return result; else return -EREMOTEIO; } @@ -65,26 +69,17 @@ static int lgh06xf_set_params(struct dvb_frontend* fe, msg.len = 2; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if ((err = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { printk(KERN_WARNING "lgh06xf: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; + "(addr %02x <- %02x, result = %i)\n", + __FUNCTION__, buf[0], buf[1], result); + if (result < 0) + return result; else return -EREMOTEIO; } - // calculate the frequency we set it to - for (i = 0; i < dvb_pll_lg_tdvs_h06xf.count; i++) { - if (params->frequency > dvb_pll_lg_tdvs_h06xf.entries[i].limit) - continue; - break; - } - div = (params->frequency + dvb_pll_lg_tdvs_h06xf.entries[i].offset) / - dvb_pll_lg_tdvs_h06xf.entries[i].stepsize; - priv->frequency = (div * dvb_pll_lg_tdvs_h06xf.entries[i].stepsize) - - dvb_pll_lg_tdvs_h06xf.entries[i].offset; + priv->frequency = frequency; return 0; } -- cgit v0.10.2 From 444dadc4391b978b43a778a5a56dfaed1c4ddc61 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 21 Nov 2006 11:32:12 -0300 Subject: V4L/DVB (4871): Fix i2c dependencies of VIDEO_TVEEPROM and VIDEO_TUNER Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 9f7e1fe..87410db 100644 --- a/drivers/media/Kconfig +++ b/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" -- cgit v0.10.2 From 5cfbacd0d3ee0c9f68e1ad3f24c4e1f879cc47a7 Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Tue, 21 Nov 2006 19:59:27 -0300 Subject: V4L/DVB (4872): Saa7134-alsa improvements The change does the following: - At device open, it sets the recording source to the current input instead of LINE2. So it is no longer necessary to set the recording source with a mixer application. - Connects the mixer volume control to the input sensitivity selection of the analog sound inputs. This allows only one 6db step. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index 4abf5c0..ffb0f64 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/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 @@ -614,13 +610,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); @@ -713,6 +714,8 @@ static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol) { snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); + struct saa7134_dev *dev = chip->dev; + int change, addr = kcontrol->private_value; int left, right; @@ -727,10 +730,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; } -- cgit v0.10.2 From dd2f398ccb73530a99582e972aa57e3ed2fd73c6 Mon Sep 17 00:00:00 2001 From: David Hardeman Date: Sat, 2 Dec 2006 21:16:05 -0200 Subject: V4L/DVB (4875): budget-ci IR: groundwork for following patches This patch lays down some groundwork for later budget-ci patches. The bulk of this consists of moving a few members into a new struct and renaming a few of them. It also allocates extra space for the device name (the "name" field in /proc/bus/input/devices and in the relevant sysfs dir) to avoid truncation and uses snprintf rather than sprintf in case the extra space shouldn't be enough. Taken from Darren Salt's dvb-ir patchset Signed-off-by: Darren Salt Signed-off-by: David Hardeman Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 25d0dfc..2893e75 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -72,15 +72,19 @@ #define SLOTSTATUS_READY 8 #define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) +struct budget_ci_ir { + struct input_dev *dev; + struct tasklet_struct msp430_irq_tasklet; + char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ +}; + 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 */ }; @@ -156,7 +160,7 @@ static void msp430_ir_debounce(unsigned long data) 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; + struct input_dev *dev = budget_ci->ir.dev; unsigned int code = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; @@ -191,17 +195,17 @@ static void msp430_ir_interrupt(unsigned long data) static int msp430_ir_init(struct budget_ci *budget_ci) { struct saa7146_dev *saa = budget_ci->budget.dev; - struct input_dev *input_dev; + struct input_dev *input_dev = budget_ci->ir.dev; int i; int err; - input_dev = input_allocate_device(); + budget_ci->ir.dev = input_dev = input_allocate_device(); if (!input_dev) return -ENOMEM; - sprintf(budget_ci->ir_dev_name, "Budget-CI dvb ir receiver %s", saa->name); - - input_dev->name = budget_ci->ir_dev_name; + snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name), + "Budget-CI dvb ir receiver %s", saa->name); + input_dev->name = budget_ci->ir.name; set_bit(EV_KEY, input_dev->evbit); for (i = 0; i < ARRAY_SIZE(key_map); i++) @@ -214,9 +218,9 @@ static int msp430_ir_init(struct budget_ci *budget_ci) return err; } - input_dev->timer.function = msp430_ir_debounce; + input_register_device(budget_ci->ir.dev); - budget_ci->input_dev = input_dev; + input_dev->timer.function = msp430_ir_debounce; saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); @@ -227,7 +231,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) 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); @@ -548,7 +552,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); @@ -1105,7 +1109,7 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio return err; } - tasklet_init(&budget_ci->msp430_irq_tasklet, msp430_ir_interrupt, + tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt, (unsigned long) budget_ci); msp430_ir_init(budget_ci); @@ -1134,7 +1138,7 @@ static int budget_ci_detach(struct saa7146_dev *dev) } err = ttpci_budget_deinit(&budget_ci->budget); - tasklet_kill(&budget_ci->msp430_irq_tasklet); + tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); msp430_ir_deinit(budget_ci); -- cgit v0.10.2 From 5cc8ae000233e8dad667edcdbb921668c317b627 Mon Sep 17 00:00:00 2001 From: David Hardeman Date: Sat, 2 Dec 2006 21:16:05 -0200 Subject: V4L/DVB (4876): budget-ci IR: support EVIOCGPHYS This patch adds a 'phys' string, of the same form as used by various other DVB cards' IR drivers, for access by any program which uses the EVIOCPHYS ioctl or may read /sys/class/input/*/phys (e.g. udev) to identify input device nodes. Taken from Darren Salt's dvb-ir patchset. Signed-off-by: Darren Salt Signed-off-by: David Hardeman Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 2893e75..0809fb9 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -76,6 +76,7 @@ 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 budget_ci { @@ -205,8 +206,27 @@ static int msp430_ir_init(struct budget_ci *budget_ci) 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)); + input_dev->name = budget_ci->ir.name; + 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 + set_bit(EV_KEY, input_dev->evbit); for (i = 0; i < ARRAY_SIZE(key_map); i++) if (key_map[i]) -- cgit v0.10.2 From 8cc532ef5bf1230ed6345aebe5bd11ea8567ba52 Mon Sep 17 00:00:00 2001 From: David Hardeman Date: Sat, 2 Dec 2006 21:16:05 -0200 Subject: V4L/DVB (4877): budget-ci IR: improve error checking in init and deinit functions Improve the error checking in the IR init and deinit functions. Based on Darren Salt's dvb-ir patchset. Signed-off-by: David Hardeman Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 0809fb9..b2869ab 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -198,11 +198,13 @@ static int msp430_ir_init(struct budget_ci *budget_ci) struct saa7146_dev *saa = budget_ci->budget.dev; struct input_dev *input_dev = budget_ci->ir.dev; int i; - int err; + int error; budget_ci->ir.dev = input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; + if (!input_dev) { + error = -ENOMEM; + goto out1; + } snprintf(budget_ci->ir.name, sizeof(budget_ci->ir.name), "Budget-CI dvb ir receiver %s", saa->name); @@ -232,20 +234,26 @@ static int msp430_ir_init(struct budget_ci *budget_ci) if (key_map[i]) set_bit(key_map[i], input_dev->keybit); - err = input_register_device(input_dev); - if (err) { - input_free_device(input_dev); - return err; + 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; } - input_register_device(budget_ci->ir.dev); - 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) @@ -255,6 +263,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) 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); @@ -1115,8 +1124,11 @@ 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 = kmalloc(sizeof(struct budget_ci), GFP_KERNEL); + if (!budget_ci) { + err = -ENOMEM; + goto out1; + } dprintk(2, "budget_ci: %p\n", budget_ci); @@ -1124,15 +1136,13 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio dev->ext_priv = budget_ci; - if ((err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE))) { - kfree(budget_ci); - return err; - } + err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE); + if (err) + goto out2; - tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt, - (unsigned long) budget_ci); - - msp430_ir_init(budget_ci); + err = msp430_ir_init(budget_ci); + if (err) + goto out3; ciintf_init(budget_ci); @@ -1142,6 +1152,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) @@ -1152,16 +1169,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->ir.msp430_irq_tasklet); - - msp430_ir_deinit(budget_ci); - // disable frontend and CI interface saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT); -- cgit v0.10.2 From ee579bc9332ef8a67e20f512e7bc91e3efb92225 Mon Sep 17 00:00:00 2001 From: David Hardeman Date: Sat, 2 Dec 2006 21:16:05 -0200 Subject: V4L/DVB (4878): budget-ci IR: be more verbose in case of init failure Trivial change to help the user understand what went wrong. Taken from Darren Salt's dvb-ir patchset. Signed-off-by: Darren Salt Signed-off-by: David Hardeman Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index b2869ab..c34b5d3 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -202,6 +202,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) 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; } @@ -1124,7 +1125,7 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio struct budget_ci *budget_ci; int err; - budget_ci = kmalloc(sizeof(struct budget_ci), GFP_KERNEL); + budget_ci = kzalloc(sizeof(struct budget_ci), GFP_KERNEL); if (!budget_ci) { err = -ENOMEM; goto out1; @@ -1132,8 +1133,6 @@ static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio dprintk(2, "budget_ci: %p\n", budget_ci); - budget_ci->budget.ci_present = 0; - dev->ext_priv = budget_ci; err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE); -- cgit v0.10.2 From 2520fffd8b7a312e77009414d3201c9751d6448c Mon Sep 17 00:00:00 2001 From: David Hardeman Date: Sat, 2 Dec 2006 21:16:05 -0200 Subject: V4L/DVB (4879): budget-ci IR: integrate with ir-common This converts the budget-ci driver so that it uses ir-common for some of its IR processing. In particular, the keymap for the Nova-T (sub 13c2:1011) is switched to the Hauppauge grey/black keymap, of which the keys on the supplied R808 remote control form a subset. The old budget-ci keymap is moved to ir-keymaps.c and is used for other remotes. The debounce logic for buggy remotes (i.e. Zenith) is made conditional the new debounce parameter and defaults to off (so that repeat keypresses aren't ignored for all working remotes). Some parts are based on Darren Salt's dvb-ir patchset. Signed-off-by: David Hardeman Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index db75344..f51e02fe 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -1552,3 +1552,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/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index 95531a6..eec7ccf 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/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/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index c34b5d3..d1b5402 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "dvb_ca_en50221.h" #include "stv0299.h" @@ -72,11 +73,24 @@ #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 + +/* 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)"); + 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; }; struct budget_ci { @@ -89,59 +103,44 @@ struct budget_ci { 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_keyup(unsigned long data) +{ + 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->ir.dev; + static int bounces = 0; + u32 ir_key; + u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; + + if (command & 0x40) { + ir_key = command & 0x3f; + + if (ir_key != dev->repeat_key && del_timer(&dev->timer)) + /* We were still waiting for a keyup event but this is a new key */ + ir_input_nokey(dev, &budget_ci->ir.state); + + if (ir_key == dev->repeat_key && bounces > 0 && timer_pending(&dev->timer)) { + /* Ignore repeated key sequences if requested */ + bounces--; + return; + } + + if (!timer_pending(&dev->timer)) + /* New keypress */ + bounces = debounce; + + /* Prepare a keyup event sometime in the future */ + mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT)); + + /* Generate a new or repeated keypress */ + ir_input_keydown(dev, &budget_ci->ir.state, ir_key, command); + } +} static void msp430_ir_debounce(unsigned long data) { @@ -197,7 +196,6 @@ static int msp430_ir_init(struct budget_ci *budget_ci) { struct saa7146_dev *saa = budget_ci->budget.dev; struct input_dev *input_dev = budget_ci->ir.dev; - int i; int error; budget_ci->ir.dev = input_dev = input_allocate_device(); @@ -230,10 +228,30 @@ static int msp430_ir_init(struct budget_ci *budget_ci) input_dev->dev = &saa->pci->dev; # endif - 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); + /* Select keymap */ + 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); + break; + default: + /* unknown remote */ + ir_input_init(input_dev, &budget_ci->ir.state, + IR_TYPE_RC5, ir_codes_budget_ci_old); + break; + } + + /* initialise the key-up timeout handler */ + input_dev->timer.function = msp430_ir_keyup; + input_dev->timer.data = (unsigned long) &budget_ci->ir; + input_dev->rep[REP_DELAY] = 1; + input_dev->rep[REP_PERIOD] = 1; error = input_register_device(input_dev); if (error) { @@ -241,8 +259,6 @@ static int msp430_ir_init(struct budget_ci *budget_ci) goto out2; } - input_dev->timer.function = msp430_ir_debounce; - tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt, (unsigned long) budget_ci); @@ -267,7 +283,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); if (del_timer(&dev->timer)) { - input_event(dev, EV_KEY, key_map[dev->repeat_key], 0); + ir_input_nokey(dev, &budget_ci->ir.state); input_sync(dev); } diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 8f58406..2b25f5c 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -92,6 +92,7 @@ extern IR_KEYTAB_TYPE ir_codes_hauppauge_new[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_npgtech[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_proteus_2309[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE]; #endif -- cgit v0.10.2 From 64741b70cf2030ffe017e5ed731620b4a4570f77 Mon Sep 17 00:00:00 2001 From: David Hardeman Date: Sat, 2 Dec 2006 21:16:05 -0200 Subject: V4L/DVB (4880): budget-ci IR: decode rc5 device byte Decode the RC5 device byte as well as the command byte. Introduce a parameter to set the device events to listen for. Default to try to auto-detect the proper device code, otherwise, listen to any device as the old code did. Based on Darren Salt's dvb-ir patchset. Signed-off-by: Darren Salt Signed-off-by: David Hardeman Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index d1b5402..76d3b66 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -78,6 +78,9 @@ */ #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 */ @@ -85,12 +88,17 @@ 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)"); + 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 { @@ -114,32 +122,56 @@ static void msp430_ir_interrupt(unsigned long data) struct budget_ci *budget_ci = (struct budget_ci *) data; struct input_dev *dev = budget_ci->ir.dev; static int bounces = 0; - u32 ir_key; + 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) { ir_key = command & 0x3f; + return; + } - if (ir_key != dev->repeat_key && del_timer(&dev->timer)) - /* We were still waiting for a keyup event but this is a new key */ - ir_input_nokey(dev, &budget_ci->ir.state); + /* It's a RC5 device byte */ + device = command & 0x1f; + toggle = command & 0x20; - if (ir_key == dev->repeat_key && bounces > 0 && timer_pending(&dev->timer)) { - /* Ignore repeated key sequences if requested */ - bounces--; - return; - } + if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != device) + return; - if (!timer_pending(&dev->timer)) - /* New keypress */ - bounces = debounce; + /* Are we still waiting for a keyup event while this is a new key? */ + if ((ir_key != dev->repeat_key || toggle != prev_toggle) && del_timer(&dev->timer)) + ir_input_nokey(dev, &budget_ci->ir.state); - /* Prepare a keyup event sometime in the future */ - mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT)); + prev_toggle = toggle; - /* Generate a new or repeated keypress */ - ir_input_keydown(dev, &budget_ci->ir.state, ir_key, command); + /* Ignore repeated key sequences if requested */ + if (ir_key == dev->repeat_key && bounces > 0 && timer_pending(&dev->timer)) { + bounces--; + return; } + + /* New keypress? */ + if (!timer_pending(&dev->timer)) + bounces = debounce; + + /* Prepare a keyup event sometime in the future */ + mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT)); + + /* Generate a new or repeated keypress */ + ir_input_keydown(dev, &budget_ci->ir.state, ir_key, ((device << 8) | command)); } static void msp430_ir_debounce(unsigned long data) @@ -228,7 +260,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) input_dev->dev = &saa->pci->dev; # endif - /* Select keymap */ + /* Select keymap and address */ switch (budget_ci->budget.dev->pci->subsystem_device) { case 0x100c: case 0x100f: @@ -239,11 +271,21 @@ static int msp430_ir_init(struct budget_ci *budget_ci) /* 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; } -- cgit v0.10.2 From b5471a27b48801507c5bd0eadd83730230515016 Mon Sep 17 00:00:00 2001 From: David Hardeman Date: Sat, 2 Dec 2006 21:16:05 -0200 Subject: V4L/DVB (4881): budget-ci IR: add IR debugging information This adds a ir_debug parameter which is useful in tracking down IR decoding problems. Based on Darren Salt's dvb-ir patchset. Signed-off-by: Darren Salt Signed-off-by: David Hardeman Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 76d3b66..2a3707c 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -92,6 +92,10 @@ 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; @@ -140,11 +144,15 @@ static void msp430_ir_interrupt(unsigned long data) /* 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; } /* 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; -- cgit v0.10.2 From 59236d46dab429646b023026ac92d9e8c732c18a Mon Sep 17 00:00:00 2001 From: David Hardeman Date: Sat, 2 Dec 2006 21:16:06 -0200 Subject: V4L/DVB (4882): budget-ci IR: make debounce logic conditional Change the debounce logic so that it is not used at all unless the debounce parameter has been set. This makes for a much "snappier" remote for most users as there is no timeout to wait for (the debounce logic has a 350ms timer for the next repeat, but with the RC5 protocol, one event per ~110ms is possible) Signed-off-by: David Hardeman Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 2a3707c..83a1c5f 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -159,27 +159,32 @@ static void msp430_ir_interrupt(unsigned long data) if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != device) return; - /* Are we still waiting for a keyup event while this is a new key? */ - if ((ir_key != dev->repeat_key || toggle != prev_toggle) && del_timer(&dev->timer)) - ir_input_nokey(dev, &budget_ci->ir.state); - - prev_toggle = toggle; - /* Ignore repeated key sequences if requested */ - if (ir_key == dev->repeat_key && bounces > 0 && timer_pending(&dev->timer)) { + 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; - /* New keypress? */ - if (!timer_pending(&dev->timer)) - bounces = debounce; + /* Are we still waiting for a keyup event? */ + if (del_timer(&dev->timer)) + ir_input_nokey(dev, &budget_ci->ir.state); - /* Prepare a keyup event sometime in the future */ - mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT)); + /* 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))); - /* Generate a new or repeated keypress */ - ir_input_keydown(dev, &budget_ci->ir.state, ir_key, ((device << 8) | command)); + /* 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 void msp430_ir_debounce(unsigned long data) @@ -297,11 +302,9 @@ static int msp430_ir_init(struct budget_ci *budget_ci) break; } - /* initialise the key-up timeout handler */ + /* initialise the key-up debounce timeout handler */ input_dev->timer.function = msp430_ir_keyup; input_dev->timer.data = (unsigned long) &budget_ci->ir; - input_dev->rep[REP_DELAY] = 1; - input_dev->rep[REP_PERIOD] = 1; error = input_register_device(input_dev); if (error) { -- cgit v0.10.2 From 9abec6189aa8b6659222544bf893158fc96e8822 Mon Sep 17 00:00:00 2001 From: Thomas Kaiser Date: Wed, 22 Nov 2006 18:15:19 -0300 Subject: V4L/DVB (4883): Fix TD1316 tuner for DVBC If your device is using the philips tda1316 tuner, i think there is a problem in setting the correct Band. 162 MHz and above should be band 2 (Mid-Band). But in dvbc_philips_tdm1316l_tuner_set_params band 1 is set for frequencies below 200 MHz. Signed-off-by: Thomas Kaiser Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 83a1c5f..a68f5bf 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -946,7 +946,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; -- cgit v0.10.2 From e457579079642924da621e92cb39d8fb90a4d5a7 Mon Sep 17 00:00:00 2001 From: Andrew de Quincey Date: Fri, 24 Nov 2006 12:58:42 -0300 Subject: V4L/DVB (4884): Remove stray IR code left from patchset This caused compilation to fail - completely replaced by new style functions, so can be removed. Signed-off-by: Andrew de Quincey Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index a68f5bf..8f71124 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -187,56 +187,6 @@ static void msp430_ir_interrupt(unsigned long data) } } -static void msp430_ir_debounce(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); - } else { - 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 */ - } - input_sync(dev); -} - -static void msp430_ir_interrupt(unsigned long data) -{ - struct budget_ci *budget_ci = (struct budget_ci *) data; - struct input_dev *dev = budget_ci->ir.dev; - unsigned int code = - ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; - - if (code & 0x40) { - code &= 0x3f; - - 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 (!key_map[code]) { - printk("DVB (%s): no key for %02x!\n", __FUNCTION__, code); - return; - } - - input_event(dev, EV_KEY, key_map[code], 1); - input_sync(dev); - - /* initialize debounce and repeat */ - dev->repeat_key = code; - /* Zenith remote _always_ sends 2 sequences */ - dev->rep[0] = ~0; - mod_timer(&dev->timer, jiffies + msecs_to_jiffies(350)); - } -} - static int msp430_ir_init(struct budget_ci *budget_ci) { struct saa7146_dev *saa = budget_ci->budget.dev; -- cgit v0.10.2 From d0d30c03ed7d51bef6e40805988134bdd7042740 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 25 Nov 2006 09:45:50 -0300 Subject: V4L/DVB (4886): Minor coding style improvements Remove coding style inconsistencies. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index f28398d..c2374ed 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -851,7 +851,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, @@ -907,7 +907,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; } @@ -932,11 +932,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 @@ -1464,13 +1464,13 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind) client->driver = &i2c_driver_saa711x; snprintf(client->name, sizeof(client->name) - 1, "saa7115"); - 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; -- cgit v0.10.2 From af249982dc385b18ea340323f50f585e989b76e5 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sun, 26 Nov 2006 09:47:07 -0300 Subject: V4L/DVB (4887): Remove the broken VIDEO_ZR36120 driver The VIDEO_ZR36120 driver has: - already been marked as BROKEN in 2.6.0 three years ago and - is still marked as BROKEN. Drivers that had been marked as BROKEN for such a long time seem to be unlikely to be revived in the forseeable future. But if anyone wants to ever revive this driver, the code is still present in the older kernel releases. Signed-off-by: Adrian Bunk Acked-by: Mauro Carvalho Chehab diff --git a/Documentation/video4linux/zr36120.txt b/Documentation/video4linux/zr36120.txt deleted file mode 100644 index 1a1c2d0..0000000 --- a/Documentation/video4linux/zr36120.txt +++ /dev/null @@ -1,162 +0,0 @@ -Driver for Trust Computer Products Framegrabber, version 0.6.1 ------- --- ----- -------- -------- ------------ ------- - - - - -- ZORAN ------------------------------------------------------ - Author: Pauline Middelink - Date: 18 September 1999 -Version: 0.6.1 - -- Description ------------------------------------------------ - -Video4Linux compatible driver for an unknown brand framegrabber -(Sold in the Netherlands by TRUST Computer Products) and various -other zoran zr36120 based framegrabbers. - -The card contains a ZR36120 Multimedia PCI Interface and a Philips -SAA7110 Onechip Frontend videodecoder. There is also an DSP of -which I have forgotten the number, since i will never get that thing -to work without specs from the vendor itself. - -The SAA711x are capable of processing 6 different video inputs, -CVBS1..6 and Y1+C1, Y2+C2, Y3+C3. All in 50/60Hz, NTSC, PAL or -SECAM and delivering a YUV datastream. On my card the input -'CVBS-0' corresponds to channel CVBS2 and 'S-Video' to Y2+C2. - -I have some reports of other cards working with the mentioned -chip sets. For a list of other working cards please have a look -at the cards named in the tvcards struct in the beginning of -zr36120.c - -After some testing, I discovered that the carddesigner messed up -on the I2C interface. The Zoran chip includes 2 lines SDA and SCL -which (s)he connected reversely. So we have to clock on the SDA -and r/w data on the SCL pin. Life is fun... Each cardtype now has -a bit which signifies if you have a card with the same deficiency. - -Oh, for the completeness of this story I must mention that my -card delivers the VSYNC pulse of the SAA chip to GIRQ1, not -GIRQ0 as some other cards have. This is also incorporated in -the driver be clearing/setting the 'useirq1' bit in the tvcard -description. - -Another problems of continuous capturing data with a Zoran chip -is something nasty inside the chip. It effectively halves the -fps we ought to get... Here is the scenario: capturing frames -to memory is done in the so-called snapshot mode. In this mode -the Zoran stops after capturing a frame worth of data and wait -till the application set GRAB bit to indicate readiness for the -next frame. After detecting a set bit, the chip neatly waits -till the start of a frame, captures it and it goes back to off. -Smart ppl will notice the problem here. Its the waiting on the -_next_ frame each time we set the GRAB bit... Oh well, 12,5 fps -is still plenty fast for me. --- update 28/7/1999 -- -Don't believe a word I just said... Proof is the output -of `streamer -t 300 -r 25 -f avi15 -o /dev/null` - ++--+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25 - +-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25 - +-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25 - +-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25 - +-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25 - +-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25 - +-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25 - +-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25 - +-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25 - +-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25 - +-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- 25/25 - +-s+-+-+-+-+-+-+-+-+-+-+-+-+-s+-+-+-+-+-+-+-+-+-+-+- - syncer: done - writer: done -(note the /dev/null is prudent here, my system is not able to - grab /and/ write 25 fps to a file... gifts welcome :) ) -The technical reasoning follows: The zoran completed the last -frame, the VSYNC goes low, and GRAB is cleared. The interrupt -routine starts to work since its VSYNC driven, and again -activates the GRAB bit. A few ms later the VSYNC (re-)rises and -the zoran starts to work on a new and freshly broadcasted frame.... - -For pointers I used the specs of both chips. Below are the URLs: - http://www.zoran.com/ftp/download/devices/pci/ZR36120/36120data.pdf - http://www-us.semiconductor.philips.com/acrobat/datasheets/SAA_7110_A_1.pdf -Some alternatives for the Philips SAA 7110 datasheet are: - http://www.datasheetcatalog.com/datasheets_pdf/S/A/A/7/SAA7110.shtml - http://www.datasheetarchive.com/search.php?search=SAA7110&sType=part - -The documentation has very little on absolute numbers or timings -needed for the various modes/resolutions, but there are other -programs you can borrow those from. - ------- Install -------------------------------------------- -Read the file called TODO. Note its long list of limitations. - -Build a kernel with VIDEO4LINUX enabled. Activate the -BT848 driver; we need this because we have need for the -other modules (i2c and videodev) it enables. - -To install this software, extract it into a suitable directory. -Examine the makefile and change anything you don't like. Type "make". - -After making the modules check if you have the much needed -/dev/video devices. If not, execute the following 4 lines: - mknod /dev/video c 81 0 - mknod /dev/video1 c 81 1 - mknod /dev/video2 c 81 2 - mknod /dev/video3 c 81 3 - mknod /dev/video4 c 81 4 - -After making/checking the devices do: - modprobe i2c - modprobe videodev - modprobe saa7110 (optional) - modprobe saa7111 (optional) - modprobe tuner (optional) - insmod zoran cardtype= - - is the cardtype of the card you have. The cardnumber can -be found in the source of zr36120. Look for tvcards. If your -card is not there, please try if any other card gives some -response, and mail me if you got a working tvcard addition. - -PS. 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 diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 2adb56d..20b9cbc 100644 --- a/drivers/media/video/Makefile +++ b/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 diff --git a/drivers/media/video/zr36120.c b/drivers/media/video/zr36120.c deleted file mode 100644 index 0cbf564..0000000 --- a/drivers/media/video/zr36120.c +++ /dev/null @@ -1,2079 +0,0 @@ -/* - zr36120.c - Zoran 36120/36125 based framegrabbers - - Copyright (C) 1998-1999 Pauline Middelink - - 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#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 "); -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 */ -/*0*/ { "Trust Victor II", - 2, 0, T, T, T, T, 0x7F, 0x80, { 1, SVHS(6) }, { 0 } }, - /* reported working by */ -/*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 */ -/*3*/ { "Guillemot Maxi-TV PCI", - 2, 0, T, T, T, T, 0x7F, 0x80, { 1, SVHS(6) }, { 0 } }, - /* reported working by "Craig Whitmore */ -/*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 -void zoran_irq(int irq, void *dev_id) -{ - u32 stat,estat; - int count = 0; - struct zoran *ztv = dev_id; - - UNUSED(irq); - 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; ix,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; ioverinfo.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; ix < 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 - * 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; ivideo_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; optrw; 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; optrbpl; 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; optrw; x++) - __put_user(iptr[x*2], optr++); - /* and clear the rest of the line */ - for (;optrbpl; 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; ireadinfo[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; igrabinfo[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; ireadinfo[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;idev->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 -#include - -#include -#include - -#include - -/* - * 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 diff --git a/drivers/media/video/zr36120_i2c.c b/drivers/media/video/zr36120_i2c.c deleted file mode 100644 index 21fde43..0000000 --- a/drivers/media/video/zr36120_i2c.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - zr36120_i2c.c - Zoran 36120/36125 based framegrabbers - - Copyright (C) 1998-1999 Pauline Middelink - - 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 -#include -#include - -#include -#include - -#include "tuner.h" -#include "zr36120.h" - -/* ----------------------------------------------------------------------- */ -/* I2C functions */ -/* ----------------------------------------------------------------------- */ - -/* software I2C functions */ - -#define I2C_DELAY 10 - -static void i2c_setlines(struct i2c_bus *bus,int ctrl,int data) -{ - struct zoran *ztv = (struct zoran*)bus->data; - unsigned int b = 0; - if (data) b |= ztv->card->swapi2c ? ZORAN_I2C_SCL : ZORAN_I2C_SDA; - if (ctrl) b |= ztv->card->swapi2c ? ZORAN_I2C_SDA : ZORAN_I2C_SCL; - zrwrite(b, ZORAN_I2C); - udelay(I2C_DELAY); -} - -static int i2c_getdataline(struct i2c_bus *bus) -{ - struct zoran *ztv = (struct zoran*)bus->data; - if (ztv->card->swapi2c) - return zrread(ZORAN_I2C) & ZORAN_I2C_SCL; - return zrread(ZORAN_I2C) & ZORAN_I2C_SDA; -} - -static -void attach_inform(struct i2c_bus *bus, int id) -{ - struct zoran *ztv = (struct zoran*)bus->data; - struct video_decoder_capability dc; - int rv; - - switch (id) { - case I2C_DRIVERID_VIDEODECODER: - DEBUG(printk(CARD_INFO "decoder attached\n",CARD)); - - /* fetch the capabilities of the decoder */ - rv = i2c_control_device(&ztv->i2c, I2C_DRIVERID_VIDEODECODER, DECODER_GET_CAPABILITIES, &dc); - if (rv) { - DEBUG(printk(CARD_DEBUG "decoder is not V4L aware!\n",CARD)); - break; - } - DEBUG(printk(CARD_DEBUG "capabilities %d %d %d\n",CARD,dc.flags,dc.inputs,dc.outputs)); - - /* Test if the decoder can de VBI transfers */ - if (dc.flags & 16 /*VIDEO_DECODER_VBI*/) - ztv->have_decoder = 2; - else - ztv->have_decoder = 1; - break; - case I2C_DRIVERID_TUNER: - ztv->have_tuner = 1; - DEBUG(printk(CARD_INFO "tuner attached\n",CARD)); - if (ztv->tuner_type >= 0) - { - if (i2c_control_device(&ztv->i2c,I2C_DRIVERID_TUNER,TUNER_SET_TYPE,&ztv->tuner_type)<0) - DEBUG(printk(CARD_INFO "attach_inform; tuner won't be set to type %d\n",CARD,ztv->tuner_type)); - } - break; - default: - DEBUG(printk(CARD_INFO "attach_inform; unknown device id=%d\n",CARD,id)); - break; - } -} - -static -void detach_inform(struct i2c_bus *bus, int id) -{ - struct zoran *ztv = (struct zoran*)bus->data; - - switch (id) { - case I2C_DRIVERID_VIDEODECODER: - ztv->have_decoder = 0; - DEBUG(printk(CARD_INFO "decoder detached\n",CARD)); - break; - case I2C_DRIVERID_TUNER: - ztv->have_tuner = 0; - DEBUG(printk(CARD_INFO "tuner detached\n",CARD)); - break; - default: - DEBUG(printk(CARD_INFO "detach_inform; unknown device id=%d\n",CARD,id)); - break; - } -} - -struct i2c_bus zoran_i2c_bus_template = -{ - "ZR36120", - I2C_BUSID_ZORAN, - NULL, - - SPIN_LOCK_UNLOCKED, - - attach_inform, - detach_inform, - - i2c_setlines, - i2c_getdataline, - NULL, - NULL -}; diff --git a/drivers/media/video/zr36120_mem.c b/drivers/media/video/zr36120_mem.c deleted file mode 100644 index 416eaa9..0000000 --- a/drivers/media/video/zr36120_mem.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - zr36120_mem.c - Zoran 36120/36125 based framegrabbers - - Copyright (C) 1998-1999 Pauline Middelink - - 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 -#include -#include -#include -#include -#ifdef CONFIG_BIGPHYS_AREA -#include -#endif - -#include "zr36120.h" -#include "zr36120_mem.h" - -/*******************************/ -/* Memory management functions */ -/*******************************/ - -void* bmalloc(unsigned long size) -{ - void* mem; -#ifdef CONFIG_BIGPHYS_AREA - mem = bigphysarea_alloc_pages(size/PAGE_SIZE, 1, GFP_KERNEL); -#else - /* - * The following function got a lot of memory at boottime, - * so we know its always there... - */ - mem = (void*)__get_free_pages(GFP_USER|GFP_DMA,get_order(size)); -#endif - if (mem) { - unsigned long adr = (unsigned long)mem; - while (size > 0) { - SetPageReserved(virt_to_page(phys_to_virt(adr))); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - } - return mem; -} - -void bfree(void* mem, unsigned long size) -{ - if (mem) { - unsigned long adr = (unsigned long)mem; - unsigned long siz = size; - while (siz > 0) { - ClearPageReserved(virt_to_page(phys_to_virt(adr))); - adr += PAGE_SIZE; - siz -= PAGE_SIZE; - } -#ifdef CONFIG_BIGPHYS_AREA - bigphysarea_free_pages(mem); -#else - free_pages((unsigned long)mem,get_order(size)); -#endif - } -} - -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/zr36120_mem.h b/drivers/media/video/zr36120_mem.h deleted file mode 100644 index aad117a..0000000 --- a/drivers/media/video/zr36120_mem.h +++ /dev/null @@ -1,3 +0,0 @@ -/* either kmalloc() or bigphysarea() alloced memory - continuous */ -void* bmalloc(unsigned long size); -void bfree(void* mem, unsigned long size); -- cgit v0.10.2 From 1b5b32bf877d6f77d7eb6580d43623cc113a3b60 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 28 Nov 2006 02:16:20 -0300 Subject: V4L/DVB (4890): Lgdt330x: fix signal / lock status detection bug In some cases when using VSB, the AGC status register has been known to falsely report "no signal" when in fact there is a carrier lock. The datasheet labels these status flags as QAM only, yet the lgdt330x module is using these flags for both QAM and VSB. This patch allows for the carrier recovery lock status register to be tested, even if the agc signal status register falsely reports no signal. Thanks to jcrews from #linuxtv in irc, for initially reporting this bug. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c index db3ef58..75be6e4 100644 --- a/drivers/media/dvb/frontends/lgdt330x.c +++ b/drivers/media/dvb/frontends/lgdt330x.c @@ -437,9 +437,6 @@ static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status) /* Test signal does not exist flag */ /* as well as the AGC lock flag. */ *status |= FE_HAS_SIGNAL; - } else { - /* Without a signal all other status bits are meaningless */ - return 0; } /* @@ -502,9 +499,6 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status) /* Test input signal does not exist flag */ /* as well as the AGC lock flag. */ *status |= FE_HAS_SIGNAL; - } else { - /* Without a signal all other status bits are meaningless */ - return 0; } /* Carrier Recovery Lock Status Register */ -- cgit v0.10.2 From 0b6389ff16d57d8706bb516bde0211207aae73af Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 28 Nov 2006 02:35:02 -0300 Subject: V4L/DVB (4891): Lgdt330x: fix broken whitespace Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c index 75be6e4..68aad0f 100644 --- a/drivers/media/dvb/frontends/lgdt330x.c +++ b/drivers/media/dvb/frontends/lgdt330x.c @@ -304,10 +304,10 @@ static int lgdt330x_set_parameters(struct dvb_frontend* fe, static u8 lgdt3303_8vsb_44_data[] = { 0x04, 0x00, 0x0d, 0x40, - 0x0e, 0x87, - 0x0f, 0x8e, - 0x10, 0x01, - 0x47, 0x8b }; + 0x0e, 0x87, + 0x0f, 0x8e, + 0x10, 0x01, + 0x47, 0x8b }; /* * Array of byte pairs -- cgit v0.10.2 From 036171e75ef866b4fd8650c5b890359482ba2a4c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 28 Nov 2006 05:09:22 -0300 Subject: V4L/DVB: Dvb-budget ci fix drivers/media/dvb/ttpci/budget-ci.c:220:6: warning: "LINUX_VERSION_CODE" is not defined drivers/media/dvb/ttpci/budget-ci.c:220:28: warning: "KERNEL_VERSION" is not defined drivers/media/dvb/ttpci/budget-ci.c:220:42: error: missing binary operator before token "(" Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 8f71124..f793a27 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -217,11 +217,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) 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 /* Select keymap and address */ switch (budget_ci->budget.dev->pci->subsystem_device) { -- cgit v0.10.2 From a08cc44ec12e0a994ee85ba365315dbee982d6a7 Mon Sep 17 00:00:00 2001 From: Michael Hunold Date: Tue, 28 Nov 2006 08:13:58 -0300 Subject: V4L/DVB (4894): Mxb: fix to load the proper i2c modules Change order of module requests, so that tuner module is loaded at the end, because the tuner module probes multiple i2c addresses and might grab an i2c address that is not a tuner but something else. Signed-off-by: Michael Hunold Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index b0aea40..152cc6b 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -160,10 +160,6 @@ static int mxb_probe(struct saa7146_dev* dev) printk("mxb: saa7111 i2c module not available.\n"); return -ENODEV; } - if ((result = request_module("tuner")) < 0) { - printk("mxb: tuner i2c module not available.\n"); - return -ENODEV; - } if ((result = request_module("tea6420")) < 0) { printk("mxb: tea6420 i2c module not available.\n"); return -ENODEV; @@ -176,6 +172,10 @@ static int mxb_probe(struct saa7146_dev* dev) printk("mxb: tda9840 i2c module not available.\n"); return -ENODEV; } + if ((result = request_module("tuner")) < 0) { + printk("mxb: tuner i2c module not available.\n"); + return -ENODEV; + } mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); if( NULL == mxb ) { -- cgit v0.10.2 From c28089a65ca03890db120ee2bf6a7eb0cf603817 Mon Sep 17 00:00:00 2001 From: Michael Hunold Date: Tue, 28 Nov 2006 08:14:44 -0300 Subject: V4L/DVB (4895): Accept tuners on saa7146 i2c bus only on address 0x60. Signed-off-by: Michael Hunold Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 40590ba..705daaa 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -443,6 +443,10 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) printk("%02x ",buffer[i]); printk("\n"); } + /* HACK: This test were added to avoid tuner to probe tda9840 and tea6415c on the MXB card */ + if (adap->id == I2C_HW_SAA7146 && addr < 0x4a) + return -ENODEV; + /* autodetection code based on the i2c addr */ if (!no_autodetect) { switch (addr) { -- cgit v0.10.2 From 4be3276a66c09e68a539253ce660913bb2585984 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Fri, 24 Nov 2006 17:04:24 -0300 Subject: V4L/DVB (4896): Dvb-usb: fix vendor ID ordering The vendor IDs in this file were all in alphabetical order except for this one. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 6a55ea2..299382d 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -32,8 +32,8 @@ #define USB_VID_PINNACLE 0x2304 #define USB_VID_VISIONPLUS 0x13d3 #define USB_VID_TWINHAN 0x1822 -#define USB_VID_UNIWILL 0x1584 #define USB_VID_ULTIMA_ELECTRONIC 0x05d8 +#define USB_VID_UNIWILL 0x1584 #define USB_VID_WIDEVIEW 0x14aa /* Product IDs */ -- cgit v0.10.2 From 177aaaf826856cd8c82e790cf125186b5634f492 Mon Sep 17 00:00:00 2001 From: Thomas Genty Date: Wed, 29 Nov 2006 21:57:24 -0300 Subject: V4L/DVB (4898): Saa7134: add support for remote control of Hauppauge HVR1110 This patch adds support for the remote control bundled with the Hauppauge HVR1110 Signed-off-by: Thomas Genty Signed-off-by: Nickolay V. Shmyrev Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index dd6341f..59edf58 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -354,6 +354,7 @@ static int ir_attach(struct i2c_adapter *adap, int addr, break; case 0x7a: case 0x47: + case 0x71: /* Handled by saa7134-input */ name = "SAA713x remote"; ir_type = IR_TYPE_OTHER; @@ -448,7 +449,7 @@ static int ir_probe(struct i2c_adapter *adap) */ static const int probe_bttv[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1}; - static const int probe_saa7134[] = { 0x7a, 0x47, -1 }; + static const int probe_saa7134[] = { 0x7a, 0x47, 0x71, -1 }; static const int probe_em28XX[] = { 0x30, 0x47, -1 }; const int *probe = NULL; struct i2c_client c; diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index d0ddc65..4dead84 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3135,17 +3135,23 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .mpeg = SAA7134_MPEG_DVB, - .gpiomask = 0x000200000, .inputs = {{ .name = name_tv, .vmux = 1, .amux = TV, .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, /* FIXME: audio doesn't work on svideo/composite */ + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, /* FIXME: audio doesn't work on svideo/composite */ }}, .radio = { .name = name_radio, .amux = TV, - .gpio = 0x0200000, }, }, [SAA7134_BOARD_CINERGY_HT_PCMCIA] = { @@ -3976,6 +3982,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_PINNACLE_PCTV_110i: case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_UPMOST_PURPLE_TV: + case SAA7134_BOARD_HAUPPAUGE_HVR1110: dev->has_remote = SAA7134_REMOTE_I2C; break; case SAA7134_BOARD_AVERMEDIA_A169_B: diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 6162550..6f9fe86 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -341,6 +341,7 @@ static int attach_inform(struct i2c_client *client) switch (client->addr) { case 0x7a: case 0x47: + case 0x71: { struct IR_i2c *ir = i2c_get_clientdata(client); d1printk("%s i2c IR detected (%s).\n", diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 5d2bf15..60b38de 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -112,6 +112,27 @@ static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } +static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char buf[5], cod4, code3, code4; + + /* poll IR chip */ + if (5 != i2c_master_recv(&ir->c,buf,5)) + return -EIO; + + cod4 = buf[4]; + code4 = (cod4 >> 2); + code3 = buf[3]; + if (code3 == 0) + /* no key pressed */ + return 0; + + /* return key */ + *ir_key = code4; + *ir_raw = code4; + return 1; +} + void saa7134_input_irq(struct saa7134_dev *dev) { struct saa7134_ir *ir = dev->remote; @@ -371,6 +392,11 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir) ir->get_key = get_key_purpletv; ir->ir_codes = ir_codes_purpletv; break; + case SAA7134_BOARD_HAUPPAUGE_HVR1110: + snprintf(ir->c.name, sizeof(ir->c.name), "HVR 1110"); + ir->get_key = get_key_hvr1110; + ir->ir_codes = ir_codes_hauppauge_new; + break; default: dprintk("Shouldn't get here: Unknown board %x for I2C IR?\n",dev->board); break; -- cgit v0.10.2 From bf5dbed6b499787809c78710b209efc76939592b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 1 Dec 2006 12:39:46 -0300 Subject: V4L/DVB (4901): Improve debug msgs to show fourcc and buffer length on video_ioctl2 Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 1be7127..6a0e8ca 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -342,7 +342,7 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd, dbgarg (cmd, "%02ld:%02d:%02d.%08ld index=%d, type=%s, " "bytesused=%d, flags=0x%08d, " - "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx\n", + "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx, length=%d\n", (p->timestamp.tv_sec/3600), (int)(p->timestamp.tv_sec/60)%60, (int)(p->timestamp.tv_sec%60), @@ -352,7 +352,7 @@ static void dbgbuf(unsigned int cmd, struct video_device *vfd, p->bytesused,p->flags, p->field,p->sequence, prt_names(p->memory,v4l2_memory_names), - p->m.userptr); + p->m.userptr, p->length); dbgarg2 ("timecode= %02d:%02d:%02d type=%d, " "flags=0x%08d, frames=%d, userbits=0x%08x\n", tc->hours,tc->minutes,tc->seconds, @@ -369,9 +369,13 @@ static inline void dbgrect(struct video_device *vfd, char *s, static inline void v4l_print_pix_fmt (struct video_device *vfd, struct v4l2_pix_format *fmt) { - dbgarg2 ("width=%d, height=%d, format=0x%08x, field=%s, " + dbgarg2 ("width=%d, height=%d, format=%c%c%c%c, field=%s, " "bytesperline=%d sizeimage=%d, colorspace=%d\n", - fmt->width,fmt->height,fmt->pixelformat, + fmt->width,fmt->height, + (fmt->pixelformat & 0xff), + (fmt->pixelformat >> 8) & 0xff, + (fmt->pixelformat >> 16) & 0xff, + (fmt->pixelformat >> 24) & 0xff, prt_names(fmt->field,v4l2_field_names_FIXME), fmt->bytesperline,fmt->sizeimage,fmt->colorspace); }; @@ -530,12 +534,13 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, } if (!ret) dbgarg (cmd, "index=%d, type=%d, flags=%d, " - "description=%s," - " pixelformat=0x%8x\n", + "pixelformat=%c%c%c%c, description='%s'\n", f->index, f->type, f->flags, - f->description, - f->pixelformat); - + (f->pixelformat & 0xff), + (f->pixelformat >> 8) & 0xff, + (f->pixelformat >> 16) & 0xff, + (f->pixelformat >> 24) & 0xff, + f->description); break; } case VIDIOC_G_FMT: -- cgit v0.10.2 From a66d23368068d6027feedc010304d510e6d1fe56 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 1 Dec 2006 15:37:49 -0300 Subject: V4L/DVB (4908): Remove the fake RGB32 format from cafe_ccic Remove RGB32, useful for debugging, but with no place in production. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 30864da..9d9844e 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -612,16 +612,6 @@ static void cafe_ctlr_image(struct cafe_camera *cam) 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, @@ -902,11 +892,8 @@ static int cafe_alloc_dma_bufs(struct cafe_camera *cam, int loadtime) cafe_set_config_needed(cam, 1); if (loadtime) cam->dma_buf_size = dma_buf_size; - else { + 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; @@ -1751,26 +1738,6 @@ static struct video_device cafe_v4l_template = { * 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++; - } -} static void cafe_frame_tasklet(unsigned long data) @@ -1800,11 +1767,8 @@ static void cafe_frame_tasklet(unsigned long data) */ 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); + 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; diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index b7d824e..7d380d7 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -497,19 +497,6 @@ static struct ov7670_format_struct { .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])) @@ -694,8 +681,6 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, 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; } -- cgit v0.10.2 From c8f5b2f5607e78c61df229259c539a5d9488a013 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 1 Dec 2006 15:50:59 -0300 Subject: V4L/DVB (4909): Add s/g_parm to cafe_ccic Add s/g_parm support allowing applications to tweak the frame rate. Signed-off-by: Jonathan Corbet Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 9d9844e..e347c7e 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -1671,6 +1671,37 @@ static int cafe_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) return 0; } +/* + * G/S_PARM. Most of this is done by the sensor, but we are + * the level which controls the number of read buffers. + */ +static int cafe_vidioc_g_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parms) +{ + struct cafe_camera *cam = priv; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_G_PARM, parms); + mutex_unlock(&cam->s_mutex); + parms->parm.capture.readbuffers = n_dma_bufs; + return ret; +} + +static int cafe_vidioc_s_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parms) +{ + struct cafe_camera *cam = priv; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __cafe_cam_cmd(cam, VIDIOC_S_PARM, parms); + mutex_unlock(&cam->s_mutex); + parms->parm.capture.readbuffers = n_dma_bufs; + return ret; +} + + static void cafe_v4l_dev_release(struct video_device *vd) { struct cafe_camera *cam = container_of(vd, struct cafe_camera, v4ldev); @@ -1724,7 +1755,8 @@ static struct video_device cafe_v4l_template = { .vidioc_queryctrl = cafe_vidioc_queryctrl, .vidioc_g_ctrl = cafe_vidioc_g_ctrl, .vidioc_s_ctrl = cafe_vidioc_s_ctrl, - /* Do cropping someday */ + .vidioc_g_parm = cafe_vidioc_g_parm, + .vidioc_s_parm = cafe_vidioc_s_parm, }; diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 7d380d7..89dd18c3 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -36,6 +36,11 @@ MODULE_LICENSE("GPL"); #define QCIF_HEIGHT 144 /* + * Our nominal (default) frame rate. + */ +#define OV7670_FRAME_RATE 30 + +/* * The 7670 sits on i2c with ID 0x42 */ #define OV7670_I2C_ADDR 0x42 @@ -291,7 +296,7 @@ static struct regval_list ov7670_default_regs[] = { { 0xc9, 0x60 }, { REG_COM16, 0x38 }, { 0x56, 0x40 }, - { 0x34, 0x11 }, { REG_COM11, COM11_EXP }, + { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, { 0xa4, 0x88 }, { 0x96, 0 }, { 0x97, 0x30 }, { 0x98, 0x20 }, { 0x99, 0x30 }, { 0x9a, 0x84 }, @@ -722,6 +727,63 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) } /* + * Implement G/S_PARM. There is a "high quality" mode we could try + * to do someday; for now, we just do the frame rate tweak. + */ +static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + unsigned char clkrc; + int ret; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = ov7670_read(c, REG_CLKRC, &clkrc); + if (ret < 0) + return ret; + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = OV7670_FRAME_RATE; + if ((clkrc & CLK_EXT) == 0 && (clkrc & CLK_SCALE) > 1) + cp->timeperframe.denominator /= (clkrc & CLK_SCALE); + return 0; +} + +static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + unsigned char clkrc; + int ret, div; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + /* + * CLKRC has a reserved bit, so let's preserve it. + */ + ret = ov7670_read(c, REG_CLKRC, &clkrc); + if (ret < 0) + return ret; + if (tpf->numerator == 0 || tpf->denominator == 0) + div = 1; /* Reset to full rate */ + else + div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator; + if (div == 0) + div = 1; + else if (div > CLK_SCALE) + div = CLK_SCALE; + clkrc = (clkrc & 0x80) | div; + tpf->numerator = 1; + tpf->denominator = OV7670_FRAME_RATE/div; + return ov7670_write(c, REG_CLKRC, clkrc); +} + + + +/* * Code for dealing with controls. */ @@ -1231,10 +1293,10 @@ static int ov7670_command(struct i2c_client *client, unsigned int cmd, 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 - */ + case VIDIOC_S_PARM: + return ov7670_s_parm(client, (struct v4l2_streamparm *) arg); + case VIDIOC_G_PARM: + return ov7670_g_parm(client, (struct v4l2_streamparm *) arg); } return -EINVAL; } -- cgit v0.10.2 From 122b5dbea6fa3727fc99fbdc2cf5e4d9b8a5bb00 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 3 Dec 2006 06:45:07 -0300 Subject: V4L/DVB (4913): Fix broken TUNER_LG_NTSC_TAPE radio support The TUNER_LG_NTSC_TAPE is identical in all respects to the TUNER_PHILIPS_FM1236_MK3. So use the params struct for the Philips tuner. Also add this LG_NTSC_TAPE tuner to the switches where radio specific parameters are set so it behaves like a TUNER_PHILIPS_FM1236_MK3. This change fixes the radio support for this tuner (the wrong bandswitch byte was used). Thanks to Andy Walls for finding this bug. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c index 63db4e9..fcf8693 100644 --- a/drivers/media/video/tuner-simple.c +++ b/drivers/media/video/tuner-simple.c @@ -108,6 +108,7 @@ static int tuner_stereo(struct i2c_client *c) case TUNER_PHILIPS_FM1216ME_MK3: case TUNER_PHILIPS_FM1236_MK3: case TUNER_PHILIPS_FM1256_IH3: + case TUNER_LG_NTSC_TAPE: stereo = ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3); break; default: @@ -421,6 +422,7 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq) case TUNER_PHILIPS_FM1216ME_MK3: case TUNER_PHILIPS_FM1236_MK3: case TUNER_PHILIPS_FMD1216ME_MK3: + case TUNER_LG_NTSC_TAPE: buffer[3] = 0x19; break; case TUNER_TNF_5335MF: diff --git a/drivers/media/video/tuner-types.c b/drivers/media/video/tuner-types.c index 7816823..1256c64 100644 --- a/drivers/media/video/tuner-types.c +++ b/drivers/media/video/tuner-types.c @@ -672,16 +672,6 @@ static struct tuner_params tuner_panasonic_vp27_params[] = { }, }; -/* ------------ TUNER_LG_NTSC_TAPE - LGINNOTEK NTSC ------------ */ - -static struct tuner_params tuner_lg_ntsc_tape_params[] = { - { - .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_fm1236_mk3_ntsc_ranges, - .count = ARRAY_SIZE(tuner_fm1236_mk3_ntsc_ranges), - }, -}; - /* ------------ TUNER_TNF_8831BGFF - Philips PAL ------------ */ static struct tuner_range tuner_tnf_8831bgff_pal_ranges[] = { @@ -1331,8 +1321,8 @@ struct tunertype tuners[] = { }, [TUNER_LG_NTSC_TAPE] = { /* LGINNOTEK NTSC */ .name = "LG NTSC (TAPE series)", - .params = tuner_lg_ntsc_tape_params, - .count = ARRAY_SIZE(tuner_lg_ntsc_tape_params), + .params = tuner_fm1236_mk3_params, + .count = ARRAY_SIZE(tuner_fm1236_mk3_params), }, [TUNER_TNF_8831BGFF] = { /* Philips PAL */ .name = "Tenna TNF 8831 BGFF)", -- cgit v0.10.2 From 88bbdf74fcfa7ed1fd1a3c825ee5575752344326 Mon Sep 17 00:00:00 2001 From: Hartmut Birr Date: Wed, 1 Nov 2006 12:46:32 -0300 Subject: V4L/DVB (4914): Saa7146: Protect access to the IER register by a spinlock Protect the access to the IER register of the SAA7146 by the device spinlock. I2C transfers may use interrupt mode now. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/saa7146.h b/include/media/saa7146.h index fee579f..afa5ff6 100644 --- a/include/media/saa7146.h +++ b/include/media/saa7146.h @@ -42,10 +42,20 @@ extern unsigned int saa7146_debug; #define DEB_INT(x) if (0!=(DEBUG_VARIABLE&0x20)) { DEBUG_PROLOG; printk x; } /* interrupt debug messages */ #define DEB_CAP(x) if (0!=(DEBUG_VARIABLE&0x40)) { DEBUG_PROLOG; printk x; } /* capture debug messages */ -#define SAA7146_IER_DISABLE(x,y) \ - saa7146_write(x, IER, saa7146_read(x, IER) & ~(y)); -#define SAA7146_IER_ENABLE(x,y) \ - saa7146_write(x, IER, saa7146_read(x, IER) | (y)); +#define SAA7146_IER_DISABLE(x,y) \ + do { \ + unsigned int flags; \ + spin_lock_irqsave(&x->int_slock, flags); \ + saa7146_write(x, IER, saa7146_read(x, IER) & ~(y)); \ + spin_unlock_irqrestore(&x->int_slock, flags); \ + } while(0) +#define SAA7146_IER_ENABLE(x,y) \ + do { \ + unsigned int flags; \ + spin_lock_irqsave(&x->int_slock, flags); \ + saa7146_write(x, IER, saa7146_read(x, IER) | (y)); \ + spin_unlock_irqrestore(&x->int_slock, flags); \ + } while(0) #define SAA7146_ISR_CLEAR(x,y) \ saa7146_write(x, ISR, (y)); -- cgit v0.10.2 From 35e55255bbe1775c3cdb5d9cff494d72d5a49bf3 Mon Sep 17 00:00:00 2001 From: Hartmut Birr Date: Wed, 1 Nov 2006 13:01:42 -0300 Subject: V4L/DVB (4915): Saa7146: Add timeout protection for I2C interrupt Add a timeout to the wait for the i2c-interrupt. The timeout prevents from endless waiting if the interrupt gets lost. Signed-off-by: Hartmut Birr Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c index 5297a36..8c85efc 100644 --- a/drivers/media/common/saa7146_i2c.c +++ b/drivers/media/common/saa7146_i2c.c @@ -189,13 +189,21 @@ static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_d saa7146_write(dev, I2C_TRANSFER, *dword); dev->i2c_op = 1; + SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); SAA7146_IER_ENABLE(dev, MASK_16|MASK_17); saa7146_write(dev, MC2, (MASK_00 | MASK_16)); - wait_event_interruptible(dev->i2c_wq, dev->i2c_op == 0); - if (signal_pending (current)) { - /* a signal arrived */ - return -ERESTARTSYS; + timeout = HZ/100 + 1; /* 10ms */ + timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout); + if (timeout == -ERESTARTSYS || dev->i2c_op) { + SAA7146_IER_DISABLE(dev, MASK_16|MASK_17); + SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17); + if (timeout == -ERESTARTSYS) + /* a signal arrived */ + return -ERESTARTSYS; + + printk(KERN_WARNING "saa7146_i2c_writeout: timed out waiting for end of xfer\n"); + return -EIO; } status = saa7146_read(dev, I2C_STATUS); } else { -- cgit v0.10.2 From 00c4cc67512ada1d195b8bf3ef1db1d6b3951605 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Wed, 1 Nov 2006 13:09:51 -0300 Subject: V4L/DVB (4916): Av7110,budget,budget-ci,budget-av: Use interrupt mode for I2C transfers Use interrupt mode for I2C transfers. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index bba23bc..366c137 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -2828,7 +2828,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl); static struct saa7146_extension av7110_extension = { .name = "dvb", - .flags = SAA7146_I2C_SHORT_DELAY, + .flags = SAA7146_USE_I2C_IRQ, .module = THIS_MODULE, .pci_tbl = &pci_tbl[0], diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 8bdc90a..9478d59 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -1290,7 +1290,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl); static struct saa7146_extension budget_extension = { .name = "budget_av", - .flags = SAA7146_I2C_SHORT_DELAY, + .flags = SAA7146_USE_I2C_IRQ, .pci_tbl = pci_tbl, diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index f793a27..0a11b55 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -1222,7 +1222,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl); static struct saa7146_extension budget_extension = { .name = "budget_ci dvb", - .flags = SAA7146_I2C_SHORT_DELAY, + .flags = SAA7146_USE_I2C_IRQ, .module = THIS_MODULE, .pci_tbl = &pci_tbl[0], diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c index 56f1c80..9268a82 100644 --- a/drivers/media/dvb/ttpci/budget.c +++ b/drivers/media/dvb/ttpci/budget.c @@ -555,7 +555,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl); static struct saa7146_extension budget_extension = { .name = "budget dvb", - .flags = SAA7146_I2C_SHORT_DELAY, + .flags = SAA7146_USE_I2C_IRQ, .module = THIS_MODULE, .pci_tbl = pci_tbl, -- cgit v0.10.2 From 4207700d9b238c2db1bcba47a23251c6024687b6 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Fri, 3 Nov 2006 15:13:52 -0300 Subject: V4L/DVB (4917): Saa7146: Convert SAA7146_IER_{DIS,EN}ABLE macros to inline functions Convert SAA7146_IER_{DIS,EN}ABLE macros to inline functions. Fixes a problem with macro expansion (reported by C.Y.M). Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/include/media/saa7146.h b/include/media/saa7146.h index afa5ff6..796bcf1 100644 --- a/include/media/saa7146.h +++ b/include/media/saa7146.h @@ -42,20 +42,6 @@ extern unsigned int saa7146_debug; #define DEB_INT(x) if (0!=(DEBUG_VARIABLE&0x20)) { DEBUG_PROLOG; printk x; } /* interrupt debug messages */ #define DEB_CAP(x) if (0!=(DEBUG_VARIABLE&0x40)) { DEBUG_PROLOG; printk x; } /* capture debug messages */ -#define SAA7146_IER_DISABLE(x,y) \ - do { \ - unsigned int flags; \ - spin_lock_irqsave(&x->int_slock, flags); \ - saa7146_write(x, IER, saa7146_read(x, IER) & ~(y)); \ - spin_unlock_irqrestore(&x->int_slock, flags); \ - } while(0) -#define SAA7146_IER_ENABLE(x,y) \ - do { \ - unsigned int flags; \ - spin_lock_irqsave(&x->int_slock, flags); \ - saa7146_write(x, IER, saa7146_read(x, IER) | (y)); \ - spin_unlock_irqrestore(&x->int_slock, flags); \ - } while(0) #define SAA7146_ISR_CLEAR(x,y) \ saa7146_write(x, ISR, (y)); @@ -451,4 +437,20 @@ int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop); #define SAA7146_I2C_BUS_BIT_RATE_80 (0x200) #define SAA7146_I2C_BUS_BIT_RATE_60 (0x300) +static inline void SAA7146_IER_DISABLE(struct saa7146_dev *x, unsigned y) +{ + unsigned long flags; + spin_lock_irqsave(&x->int_slock, flags); + saa7146_write(x, IER, saa7146_read(x, IER) & ~y); + spin_unlock_irqrestore(&x->int_slock, flags); +} + +static inline void SAA7146_IER_ENABLE(struct saa7146_dev *x, unsigned y) +{ + unsigned long flags; + spin_lock_irqsave(&x->int_slock, flags); + saa7146_write(x, IER, saa7146_read(x, IER) | y); + spin_unlock_irqrestore(&x->int_slock, flags); +} + #endif -- cgit v0.10.2 From 740cf9e15e5b2274841ee9e6d6d717e37b3f31cf Mon Sep 17 00:00:00 2001 From: Hartmut Birr Date: Fri, 3 Nov 2006 15:34:18 -0300 Subject: V4L/DVB (4918): Budget-av: Add delay for frontend power-on Added a delay to give the frontend a little bit time for power-on. Signed-off-by: Hartmut Birr Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 9478d59..4d19540 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -952,6 +952,9 @@ static void frontend_init(struct budget_av *budget_av) /* Enable / PowerON Frontend */ saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); + /* Wait for PowerON */ + msleep(100); + /* additional setup necessary for the PLUS cards */ switch (saa->pci->subsystem_device) { case SUBID_DVBS_KNC1_PLUS: -- cgit v0.10.2 From 97a2cf05c409f0f73346d3d258b643c782879c77 Mon Sep 17 00:00:00 2001 From: Hartmut Birr Date: Sun, 3 Dec 2006 10:49:47 -0300 Subject: V4L/DVB (4919): Budget-ci: Use SAA7146_IER_ENABLE/DISABLE to enable or disable a hardware interrupt Use SAA7146_IER_ENABLE/DISABLE to enable or disable a hardware interrupt. Signed-off-by: Hartmut Birr Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 0a11b55..0dc5291 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -261,7 +261,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) 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_IER_ENABLE(saa, MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI); return 0; @@ -277,7 +277,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) struct saa7146_dev *saa = budget_ci->budget.dev; struct input_dev *dev = budget_ci->ir.dev; - saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06); + SAA7146_IER_DISABLE(saa, MASK_06); saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); @@ -537,7 +537,7 @@ static int ciintf_init(struct budget_ci *budget_ci) } else { saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); } - saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03); + SAA7146_IER_ENABLE(saa, MASK_03); } // enable interface @@ -569,7 +569,7 @@ static void ciintf_deinit(struct budget_ci *budget_ci) // disable CI interrupts if (budget_ci->ci_irq) { - saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03); + SAA7146_IER_DISABLE(saa, MASK_03); saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); tasklet_kill(&budget_ci->ciintf_irq_tasklet); } -- cgit v0.10.2 From 2a893dea0f186efc2eb4bcdaee145b20c87d239d Mon Sep 17 00:00:00 2001 From: Hartmut Birr Date: Sun, 3 Dec 2006 21:08:08 -0300 Subject: V4L/DVB (4921): Budget-av, budget-ci: Fix MC1 register programming Remove bogus read during MC1 programming. A '1' bit could never be cleared using the old code. Use MASK_xx macros. Signed-off-by: Hartmut Birr Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 4d19540..89ab4b5 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -360,7 +360,7 @@ static int ciintf_init(struct budget_av *budget_av) saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTLO); /* Enable DEBI pins */ - saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800); + saa7146_write(saa, MC1, MASK_27 | MASK_11); /* register CI interface */ budget_av->ca.owner = THIS_MODULE; @@ -386,7 +386,7 @@ static int ciintf_init(struct budget_av *budget_av) return 0; error: - saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16)); + saa7146_write(saa, MC1, MASK_27); return result; } @@ -403,7 +403,7 @@ static void ciintf_deinit(struct budget_av *budget_av) dvb_ca_en50221_release(&budget_av->ca); /* disable DEBI pins */ - saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16)); + saa7146_write(saa, MC1, MASK_27); } diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 0dc5291..f2066b4 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -485,7 +485,7 @@ static int ciintf_init(struct budget_ci *budget_ci) memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221)); // enable DEBI pins - saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800); + saa7146_write(saa, MC1, MASK_27 | MASK_11); // test if it is there ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0); @@ -559,7 +559,7 @@ static int ciintf_init(struct budget_ci *budget_ci) return 0; error: - saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16)); + saa7146_write(saa, MC1, MASK_27); return result; } @@ -587,7 +587,7 @@ static void ciintf_deinit(struct budget_ci *budget_ci) dvb_ca_en50221_release(&budget_ci->ca); // disable DEBI pins - saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16)); + saa7146_write(saa, MC1, MASK_27); } static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) -- cgit v0.10.2 From 781aa1d1ab7ba13314af0af6c5d70c0eb0e96bf4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 4 Dec 2006 08:30:53 -0300 Subject: V4L/DVB (4922): Add usbvision driver This patch adds usbvision into V4L/DVB HG tree. Usbvision driver is a GPL driver, made by: Joerg Heckenbach and Dwaine Garden V4L2 migration made by: Thierry Merle Kconfig/Makefile scripts by: Mauro Carvalho Chehab Signed-off-by: Joerg Heckenbach Signed-off-by: Dwaine Garden Signed-off-by: Thierry Merle Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index ef80310..9365a8d 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -686,6 +686,18 @@ source "drivers/media/video/pvrusb2/Kconfig" source "drivers/media/video/em28xx/Kconfig" +config VIDEO_USBVISION + tristate "USB video devices based on NT1003/1005/1005" + depends on I2C && VIDEO_V4L2 + select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO + ---help--- + There are more than 50 different USB video devices based on + NT1003/1004/1005 USB Bridges. This driver enables using those + devices. + + To compile this driver as a module, choose M here: the + module will be called ovcamchip. + source "drivers/media/video/usbvideo/Kconfig" source "drivers/media/video/et61x251/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 20b9cbc..9b1f3f0 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ +obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile new file mode 100644 index 0000000..bad2eee --- /dev/null +++ b/drivers/media/video/usbvision/Makefile @@ -0,0 +1,5 @@ +usbvision-objs := usbvision-core.o usbvision-i2c.o + +obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o + +EXTRA_CFLAGS += -Idrivers/media/video diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c new file mode 100644 index 0000000..62699ca --- /dev/null +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -0,0 +1,5880 @@ +/* + * USB USBVISION Video device driver 0.9.8.3cvs (For Kernel 2.4.19-2.4.32 + 2.6.0-2.6.16) + * + * + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * + * This module is part of usbvision driver project. + * + * 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. + * + * Let's call the version 0.... until compression decoding is completely + * implemented. + * + * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach. + * It was based on USB CPiA driver written by Peter Pregler, + * Scott J. Bertin and Johannes Erdfelt + * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler & + * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink + * Updates to driver completed by Dwaine P. Garden + * + * History: + * + * Mar. 2000 - 15.12.2000: (0.0.0 - 0.2.0) + * Several alpha drivers and the first beta. + * + * Since Dec. 2000: (0.2.1) or (v2.1) + * Code changes or updates by Dwaine Garden and every other person. + * + * Added: New Hauppauge TV device Vendor ID: 0x0573 + * Product ID: 0x4D01 + * (Thanks to Giovanni Garberoglio) + * + * Added: UK Hauppauge WinTV-USB Vendor ID: 0x0573 + * Product ID: 0x4D02 + * (Thanks to Derek Freeman-Jones) + * + * Feb, 2001 - Apr 08, 2001: (0.3.0) + * - Some fixes. Driver is now more stable. + * - Scratch is organized as ring-buffer now for better performance + * - DGA (overlay) is now supported. + * !!!!Danger!!!! Clipping is not yet implemented. Your system will + * crash if your video window leaves the screen!!! + * - Max. Framesize is set to 320x240. There isn't more memory on the + * nt1003 video device for the FIFO. + * - Supported video palettes: RGB565, RGB555, RGB24, RGB32 + * + * + * Apr 15, 2001: (0.3.1-test...) + * - Clipping is implemented + * - NTSC is now coloured (Thanks to Dwaine Garden) + * - Added SECAM colour detection in saa7111-new + * - Added: French Hauppauge WinTV USB Vendor ID: 0x0573 + * Product ID: 0x4D03 + * (Thanks to Julius Hrivnac) + * - Added: US Hauppauge WINTV USB Vendor ID: 0x0573 + * Product ID: 0x4D00 + * (Thanks to Derrick J Brashear) + * - Changes for adding new devices. There's now a table in usbvision.h. + * Adding your devices data to the usbvision_device_data table is all + * you need to add a new device. + * + * May 11, 2001: (0.3.2-test...) (Thanks to Derek Freeman-Jones) + * - Support YUV422 raw format for people with hardware scaling. + * - Only power on the device when opened (use option PowerOnAtOpen=0 to disable it). + * - Turn off audio so we can listen to Line In. + * + * July 5, 2001 - (Patch the driver to run with Kernel 2.4.6) + * - Fixed a problem with the number of parameters passed to video_register_device. + * + * July 6, 2001 - Added: HAUPPAUGE WINTV-USB FM USA Vendor ID: 0x0573 + * Product ID: 0x4D10 + * (Thanks to Braddock Gaskill) + * Added: USBGear USBG-V1 resp. HAMA USB + * Vendor ID: 0x0573 + * Product ID: 0x0003 + * (Thanks to Bradley A. Singletary and Juergen Weigert) + * + * Jan 24, 2002 - (0.3.3-test...) + * - Moved all global variables that are device specific the usb_usbvision struct + * - Fixed the 64x48 unchangable image in xawtv when starting it with overlay + * - Add VideoNorm and TunerType to the usb_device_data table + * - Checked the audio channels and mute for HAUPPAUGE WinTV USB FM + * - Implemented the power on when opening the device. But some software opens + * the device several times when starting. So the i2c parts are just registered + * by an open, when they become deregistered by the next close. You can speed + * up tuner detection, when adding "options tuner addr=your_addr" to /etc/modules.conf + * - Begin to resize the frame in width and height. So it will be possible to watch i.e. + * 384x288 pixels at 23 fps. + * + * Feb 10, 2002 + * - Added radio device + * + * + * Jul 30, 2002 - (Thanks Cameron Maxwell) + * - Changes to usbvision.h --fixed usbvision device data structure, incorrectly had (0x0573, 0x4d21) for WinTV-USB II, should be 0x4d20. + * - Changes for device WinTV-USB II (0x0573. 0x4D21). It does not have a FM tuner. + * - Added the real device HAUPPAUGE WINTV-USB II (PAL) to the device structure in usbvision.h. + * - Changes to saa7113-new, the video is 8 bit data for the Phillips SAA7113 not 16bit like SAA7111. + * - Tuned lots of setup registers for the Phillips SAA7113 video chipset. + * - Changes to the supplied makefile. (Dwaine Garden) Still needs to be fixed so it will compile modules on different distrubutions. + * + * + * Aug 10, 2002 - (Thanks Mike Klinke) + * - Changes to usbvision.txt -- Fixed instructions on the location to copy the contents of the tgz file. + * - Added device WinTV-USB FM Model 621 (0x0573. 0x4D30). There is another device which carries the same name. Kept that device in the device structure. + * + * Aug 12, 2002 - Dwaine Garden + * - Added the ability to read the NT100x chip for the MaxISOPacketLength and USB Bandwidth + * Setting of the video device. + * - Adjustments to the SAA7113H code for proper video output. + * - Changes to usbvision.h, so all the devices with FM tuners are working. + * + * Feb 10, 2003 - Joerg Heckenbach + * - fixed endian bug for Motorola PPC + * + * Feb 13, 2003 - Joerg Heckenbach + * - fixed Vin_Reg setting and presetting from usbvision_device_data() + * + * Apr 19, 2003 - Dwaine Garden + * - Fixed compiling errors under RedHat v9.0. from uvirt_to_kva and usbvision_mmap. (Thanks Cameron Maxwell) + * - Changed pte_offset to pte_offset_kernel. + * - Changed remap_page_range and added additional parameter to function. + * - Change setup parameters for the D-Link V100 USB device + * - Added a new device to the usbvision driver. Pinnacle Studio PCTV USB (PAL) 0x2304 0x0110 + * - Screwed up the sourceforge.net cvs respository! 8*) + * + * Apr 22, 2002 - Dwaine Garden + * - Added a new device to the usbvision driver. Dazzle DVC-80 (PAL) 0x07d0 0x0004. (Thanks Carl Anderson) + * - Changes to some of the comments. + * + * June 06, 2002 - Ivan, Dwaine Garden + * - Ivan updates for fine tuning device parameters without driver recompiling. (Ivan) + * - Changes to some of the comments. (Dwaine Garden) + * - Changes to the makefile - Better CPU settings. (Ivan) + * - Changes to device Hauppauge WinTv-USB III (PAL) FM Model 568 - Fine tuning parameters (Ivan) + * + * + * Oct 16, 2003 - 0.9.0 - Joerg Heckenbach + * - Implementation of the first part of the decompression algorithm for intra frames. + * The resolution has to be 320x240. A dynamic adaption of compression deepth is + * missing yet. + * + * Oct 22, 2003 - 0.9.1 - Joerg Heckenbach + * - Implementation of the decompression algorithm for inter frames. + * The resolution still has to be 320x240. + * + * Nov 2003 - Feb 2004 - 0.9.2 to 0.9.3 - Joerg Heckenbach + * - Implement last unknown compressed block type. But color is still noisy. + * - Finding criteria for adaptive compression adjustment. + * - Porting to 2.6 kernels, but still working under 2.4 + * + * Feb 04, 2004 - 0.9.4 Joerg Heckenbach + * - Found bug in color decompression. Color is OK now. + * + * Feb 09, 2004 - 0.9.5 Joerg Heckenbach + * - Add auto-recognition of chip type NT1003 or NT1004. + * - Add adaptive compression adjustment. + * - Patched saa7113 multiplexer switching (Thanks to Orlando F.S. Bordoni) + * + * Feb 24, 2004 - 0.9.6 Joerg Heckenbach + * - Add a timer to wait before poweroff at close, to save start time in + * some video applications + * + * Mar 4, 2004 - 0.9.6 Dwaine Garden + * - Added device Global Village GV-007 (NTSC) to usbvision.h (Thanks to Abe Skolnik) + * - Forgot to add this device to the driver. 8*) + * + * June 2, 2004 - 0.9.6 Dwaine Garden + * - Fixed sourceforge.net cvs repository. + * - Added #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,26) for .owner to help compiling under kernels 2.4.x which do not have the i2c v2.8.x updates. + * - Added device Hauppauge WinTv-USB III (PAL) FM Model 597 to usbvision.h + * + * July 1, 2004 -0.9.6 Dwaine Garden + * - Patch was submitted by Hal Finkel to fix the problem with the tuner not working under kernel 2.6.7. + * - Thanks Hal..... + * + * July 30, 2004 - 0.9.6 Dwaine Garden + * - Patch was submitted by Tobias Diaz to fix Model ID mismatch in usbvision.h. + * - Thanks..... + * + * August 12, 2004 - 0.9.6 Dwaine Garden + * - Updated the readme file so people could install the driver under the configuration file for kernel 2.6.x recompiles. Now people can use make xconfig! + * - Added new device "Camtel Technology Corp TVB330-USB FM". + * - Sourceforge.net CVS has been updated with all the changes. + * + * August 20, 2004 - 0.9.7 Dwaine Garden + * - Added Device "Hauppauge USB Live Model 600" + * - Fixed up all the devices which did not have a default tuner type in usbvision.h. It's best guess, at least until someone with the device tells me otherwise. + * - Sourceforge.net CVS has been updated with all the changes. + * - Clean up the driver. + * + * September 13, 2004 - 0.9.8 Dwaine Garden + * - Changed usbvision_muxsel to address the problem with black & white s-video output for NT1004 devices with saa7114 video decoder. Thanks to Emmanuel for the patch and testing. + * - Fixed up SECAM devices which could not properly output video. Changes to usbmuxsel. Thanks to Emmanuel for the patch and everyone with a SECAM device which help test. + * - Removed some commented out code. Clean up. + * - Tried to fix up the annoying empty directories in the sourceforge.net cvs. Fuck it up again. 8*( + * + * November 15, 2004 - 0.9.8 Dwaine Garden + * - Release new tar - 0.9.8 on sourceforge.net + * - Added some new devices to usbvision.h WinTV USB Model 602 40201 Rev B282, Hauppague WinTV USB Model 602 40201 Rev B285 + * - Added better compatibility for 2.6.x kernels. + * - Hardware full screen scaling in grabdisplay mode. + * - Better support for sysfs. More code to follow for both video device and radio device. Device information is located at /sys/class/video4linux/video0 + * - Added module_param so loaded module parameters are displayed in sysfs. Driver parameters should show up in /sys/module/usbvision + * - Adjusted the SAA7111 registers to match the 2.6.x kernel SAA7111 code. Thanks to the person which helped test. + * - Changed to wait_event_interruptible. For all the people running Fedora 2. + * - Added some screenshots of actual video captures on sourceforge.net. + * + * November 24, 2004 - 0.9.8.1cvs Dwaine Garden + * - Added patch to check for palette and format in VIDIOCSPICT. Helix Producer should work fine with the driver now. Thanks Jason Simpson + * - Two device description changes and two additions for the maintainer of usb.ids. + * + * December 2, 2004 - 0.9.8.1cvs Dwaine Garden + * - Added patch for YUV420P and YUV422P video output. Thanks to Alex Smith. + * - Better support for mythtv. + * + * January 2, 2005 - 0.9.8.1cvs Dwaine Garden + * - Setup that you can specify which device is used for video. Default is auto detect next available device number eg. /dev/videoX + * - Setup that you can specify which device is used for radio. Default is auto detect next available device number eg. /dev/radioX + * - usb_unlink_urb() is deprecated for synchronous unlinks. Using usb_kill_urb instead. + * - usbvision_kvirt_to_pa is deprecated. Removed. + * - Changes are related to kernel changes for 2.6.10. (Fedora 4) + * + * February 2, 2005 - 0.9.8.1cvs Dwaine Garden + * - Added a new device to usbvision.h Dazzle DVC 50. Thanks to Luiz S. + * + * March 29, 2005 - 0.9.8.1cvs Dwaine Garden + * - Fixed compile error with saa7113 under kernels 2.6.11+ + * - Added module parameter to help people with Black and White output with using s-video input. Some cables and input device are wired differently. + * - Removed the .id from the i2c usbvision template. There was a change to the i2c with kernels 2.6.11+. + * + * April 9, 2005 - 0.9.8.1cvs Dwaine Garden + * - Added in the 2.4 and 2.6 readme files the SwitchSVideoInput parameter information. This will help people setup the right values for the parameter. + * If your device experiences Black and White images with the S-Video Input. Set this parameter to 1 when loading the module. + * - Replaced the wrong 2.6 readme file. I lost the right version. Someone sent me the right version by e-mail. Thanks. + * - Released new module version on sourceforge.net. So everyone can enjoy all the fixes and additional device support. + * + * April 20, 2005 - 0.9.8.2cvs Dwaine Garden + * - Release lock in usbvision_v4l_read_done. -Thanks to nplanel for the patch. + * - Additional comments to the driver. + * - Fixed some spelling mistakes. 8*) + * + * April 23, 2005 - 0.9.8.2cvs Joerg Heckenbach + * - Found bug in usbvision line counting. Now there should be no spurious lines in the image any longer. + * - Swapped usbvision_register_video and usbvision_configure_video to fix problem with PowerOnAtOpen=0. + * Thanks to Erwan Velu + * + * April 26, 2005 - 0.9.8.2cvs Joerg Heckenbach + * - Fixed problem with rmmod module and oppses. Replaced vfree(usbvision->overlay_base) with iounmap(usbvision->overlay_base). + * - Added function usb_get_dev(dev) and ; To help with unloading the module multiple times without crashing. + * (Keep the reference count in kobjects correct) + * + * June 14, 2005 - 0.9.8.2cvs Dwaine + * - Missed a change in saa7113.c for checking kernel version. Added conditional if's. + * + * June 15, 2005 - 0.9.8.2cvs Dwaine + * - Added new device WinTV device VendorId 0573 and ProductId 4d29. + * - Hacked some support for newer NT1005 devices. This devices only seem to have one configuration, not multiple configurations like the NT1004. + * + * June 29, 2005 - 0.9.8.2cvs Dwaine + * - Added new device WinTV device VendorId 0573 and ProductId 4d37. + * - Because of the first empty entry in usbvision_table, modutils failed to create the necessary entries for modules.usbmap. + * This means hotplug won't work for usbvision. Thanks to Gary Ng. + * - Sent an e-mail to the maintainer of usb.ids. New devices identified need to be added. + * - Fixed compile error with saa7113 under kernel 2.6.12. + * + * July 6, 2005 - 0.9.8.2cvs Dwaine + * - Patch submitted by Gary Ng for two additional procfs entries. Device Input and Frequency setting. + * + * July 12, 2005 - 0.9.8.2cvs Dwaine + * - New tuner identified for some devices it's called TCL_MFPE05. This tuner uses the same API as tuner 38 in tuner.c. + * - Thanks to lynx31 for contacting Hauppage and asking them. + * - I have no clue as to which devices use this new tuner, so people will have to contact me and tell me. + * + * July 21, 2005 - 0.9.8.2cvs Dwaine + * - Patched usbvision.c with missing ifdef kernversion statement so the module will compile with older kernels and v4l. + * - Thanks to cipe007...... + * + * May 19, 2006 - 0.9.8.3cvs Dwaine + * - Patched usbvision.c and i2c-algo.c so they will compile with kernel 2.6.16 + * - Adjust device "Pinnacle Studio PCTV USB (PAL) FM" values in usbvision.h + * + * May 24, 2006 - 0.9.8.3cvs Dwaine + * -Pinnacle Studio PCTV USB (NTSC) FM uses saa7111, not saa7113 like first thought. + * -Updated usbvision.h + * + * Aug 15, 2006 - 0.9.8.3cvs Dwaine + * -Added saa711x module into cvs, since the newer saa7115 module in newer kernels is v4l2. The usbvision driver is only v4l. + * -Updated makefile to put compiled modules into correct location. + * + * Aug 21, 2006 - 0.9.8.3cvs Dwaine + * -Changed number of bytes for i2c write to 4 as per the NT100X spec sheet. Thanks to Merlum for finding it. + * -Remove the radio option for device Hauppauge WinTV USB device Model 40219 Rev E189. This device does not have a FM radio. Thanks to Shadwell. + * -Added radio option for device Hauppauge WinTV USB device Model 40219 Rev E189 again. Just got an e-mail indicating their device has one. 8*) + * + * Aug 27, 2006 - 0.9.8.3cvs Dwaine + * -Changed ifdef statement so the usbvision driver will compile with kernels at 2.6.12. + * -Updated readme files for new updated tuner list for v4l devices. + * + * + * + * TODO: + * - use submit_urb for all setup packets + * - Fix memory settings for nt1004. It is 4 times as big as the + * nt1003 memory. + * - Add audio on endpoint 3 for nt1004 chip. Seems impossible, needs a codec interface. Which one? + * - Clean up the driver. + * - optimization for performance. + * - Add Videotext capability (VBI). Working on it..... + * - Check audio for other devices + * - Add v4l2 interface + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbvision-i2c.h" + +#define USBVISION_DRIVER_VERSION_MAJOR 0 +#define USBVISION_DRIVER_VERSION_MINOR 8 +#define USBVISION_DRIVER_VERSION_PATCHLEVEL 0 + +#define USBVISION_VERSION __stringify(USBVISION_DRIVER_VERSION_MAJOR) "." __stringify(USBVISION_DRIVER_VERSION_MINOR) "." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL) " " USBVISION_DRIVER_VERSION_COMMENT +#define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,USBVISION_DRIVER_VERSION_MINOR,USBVISION_DRIVER_VERSION_PATCHLEVEL) + +#include +#include +#include +#include + + #include + #include + +#ifdef CONFIG_KMOD +#include +#endif + +#include "usbvision.h" +#include "usbvision_ioctl.h" + + +#define DRIVER_VERSION "0.9.8.3cvs for Linux kernels 2.4.19-2.4.32 + 2.6.0-2.6.17, compiled at "__DATE__", "__TIME__ +#define EMAIL "joerg@heckenbach-aw.de" +#define DRIVER_AUTHOR "Joerg Heckenbach , Dwaine Garden " +#define DRIVER_DESC "USBVision USB Video Device Driver for Linux" +#define DRIVER_LICENSE "GPL" +#define DRIVER_ALIAS "USBVision" + +#define ENABLE_HEXDUMP 0 /* Enable if you need it */ + + +#define USBVISION_DEBUG /* Turn on debug messages */ + +#ifdef USBVISION_DEBUG + #define PDEBUG(level, fmt, args...) \ + if (debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args) +#else + #define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +#define DBG_IOCTL 1<<3 +#define DBG_IO 1<<4 +#define DBG_RIO 1<<5 +#define DBG_HEADER 1<<7 +#define DBG_PROBE 1<<8 +#define DBG_IRQ 1<<9 +#define DBG_ISOC 1<<10 +#define DBG_PARSE 1<<11 +#define DBG_SCRATCH 1<<12 +#define DBG_FUNC 1<<13 +#define DBG_I2C 1<<14 + +#define DEBUG(x...) /* General Debug */ +#define IODEBUG(x...) /* Debug IO */ +#define OVDEBUG(x...) /* Debug overlay */ +#define MDEBUG(x...) /* Debug memory management */ + +//String operations +#define rmspace(str) while(*str==' ') str++; +#define goto2next(str) while(*str!=' ') str++; while(*str==' ') str++; + + +static int usbvision_nr = 0; // sequential number of usbvision device + + +static const int max_imgwidth = MAX_FRAME_WIDTH; +static const int max_imgheight = MAX_FRAME_HEIGHT; +static const int min_imgwidth = MIN_FRAME_WIDTH; +static const int min_imgheight = MIN_FRAME_HEIGHT; + +#define FRAMERATE_MIN 0 +#define FRAMERATE_MAX 31 + + +enum { + ISOC_MODE_YUV422 = 0x03, + ISOC_MODE_YUV420 = 0x14, + ISOC_MODE_COMPRESS = 0x60, +}; + +/* + * The value of 'scratch_buf_size' affects quality of the picture + * in many ways. Shorter buffers may cause loss of data when client + * is too slow. Larger buffers are memory-consuming and take longer + * to work with. This setting can be adjusted, but the default value + * should be OK for most desktop users. + */ +#define DEFAULT_SCRATCH_BUF_SIZE (0x20000) // 128kB memory scratch buffer +static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE; + +static int init_brightness = 128; // Initalize the brightness of the video device +static int init_contrast = 192; // Initalize the contrast of the video device +static int init_saturation = 128; // Initalize the staturation mode of the video device +static int init_hue = 128; // Initalize the Hue settings of the video device + +// Function prototypes +static int usbvision_restart_isoc(struct usb_usbvision *usbvision); +static int usbvision_begin_streaming(struct usb_usbvision *usbvision); +static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel, int norm); +static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, short len); +static int usbvision_i2c_read(void *data, unsigned char addr, char *buf, short len); +static int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg); +static int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, unsigned char value); +static int usbvision_request_intra (struct usb_usbvision *usbvision); +static int usbvision_unrequest_intra (struct usb_usbvision *usbvision); +static int usbvision_adjust_compression (struct usb_usbvision *usbvision); +static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision); +static void usbvision_release(struct usb_usbvision *usbvision); +static int usbvision_set_input(struct usb_usbvision *usbvision); +static int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height); +static void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, void *arg); + + +// Bit flags (options) +#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) +#define FLAGS_MONOCHROME (1 << 1) +#define FLAGS_DISPLAY_HINTS (1 << 2) +#define FLAGS_OSD_STATS (1 << 3) +#define FLAGS_FORCE_TESTPATTERN (1 << 4) +#define FLAGS_SEPARATE_FRAMES (1 << 5) +#define FLAGS_CLEAN_FRAMES (1 << 6) + +// Default initalization of device driver parameters +static int flags = 0; // Set the default Overlay Display mode of the device driver +static int debug = 0; // Set the default Debug Mode of the device driver +static int isocMode = ISOC_MODE_COMPRESS; // Set the default format for ISOC endpoint +static int adjustCompression = 1; // Set the compression to be adaptive +static int dga = 1; // Set the default Direct Graphic Access +static int PowerOnAtOpen = 1; // Set the default device to power on at startup +static int SwitchSVideoInput = 0; // To help people with Black and White output with using s-video input. Some cables and input device are wired differently. +static int video_nr = -1; // Sequential Number of Video Device +static int radio_nr = -1; // Sequential Number of Radio Device +static int vbi_nr = -1; // Sequential Number of VBI Device +static char *CustomDevice=NULL; // Set as nothing.... + +// Grab parameters for the device driver + +#if defined(module_param) // Showing parameters under SYSFS +module_param(flags, int, 0444); +module_param(debug, int, 0444); +module_param(isocMode, int, 0444); +module_param(adjustCompression, int, 0444); +module_param(dga, int, 0444); +module_param(PowerOnAtOpen, int, 0444); +module_param(SwitchSVideoInput, int, 0444); +module_param(video_nr, int, 0444); +module_param(radio_nr, int, 0444); +module_param(vbi_nr, int, 0444); +module_param(CustomDevice, charp, 0444); +#else // Old Style +MODULE_PARM(flags, "i"); // Grab the Overlay Display mode of the device driver +MODULE_PARM(debug, "i"); // Grab the Debug Mode of the device driver +MODULE_PARM(isocMode, "i"); // Grab the video format of the video device +MODULE_PARM(adjustCompression, "i"); // Grab the compression to be adaptive +MODULE_PARM(dga, "i"); // Grab the Direct Graphic Access +MODULE_PARM(PowerOnAtOpen, "i"); // Grab the device to power on at startup +MODULE_PARM(SwitchSVideoInput, "i"); // To help people with Black and White output with using s-video input. Some cables and input device are wired differently. +MODULE_PARM(video_nr, "i"); // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...) +MODULE_PARM(radio_nr, "i"); // radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...) +MODULE_PARM(vbi_nr, "i"); // vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...) +MODULE_PARM(CustomDevice, "s"); // .... CustomDevice +#endif + +MODULE_PARM_DESC(flags, " Set the default Overlay Display mode of the device driver. Default: 0 (Off)"); +MODULE_PARM_DESC(debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)"); +MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); +MODULE_PARM_DESC(adjustCompression, " Set the ADPCM compression for the device. Default: 1 (On)"); +MODULE_PARM_DESC(dga, " Set the Direct Graphic Access for the device. Default: 1 (On)"); +MODULE_PARM_DESC(PowerOnAtOpen, " Set the default device to power on when device is opened. Default: 1 (On)"); +MODULE_PARM_DESC(SwitchSVideoInput, " Set the S-Video input. Some cables and input device are wired differently. Default: 0 (Off)"); +MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); +MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); +MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX). Default: -1 (autodetect)"); +MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the device. Default: null"); + + +// Misc stuff +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + MODULE_VERSION(DRIVER_VERSION); + MODULE_ALIAS(DRIVER_ALIAS); + +#ifdef MODULE +static unsigned int autoload = 1; +#else +static unsigned int autoload = 0; +#endif + + +/****************************************************************************************/ +/* SYSFS Code - Copied from the stv680.c usb module. */ +/* Device information is located at /sys/class/video4linux/video0 */ +/* Device parameters information is located at /sys/module/usbvision */ +/* Device USB Information is located at /sys/bus/usb/drivers/USBVision Video Grabber */ +/****************************************************************************************/ + + +#define YES_NO(x) ((x) ? "Yes" : "No") + +static inline struct usb_usbvision *cd_to_usbvision(struct class_device *cd) +{ + struct video_device *vdev = to_video_device(cd); + return video_get_drvdata(vdev); +} + +static ssize_t show_version(struct class_device *cd, char *buf) +{ + return sprintf(buf, "%s\n", DRIVER_VERSION); +} +static CLASS_DEVICE_ATTR(version, S_IRUGO, show_version, NULL); + +static ssize_t show_model(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", usbvision_device_data[usbvision->DevModel].ModelString); +} +static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL); + +static ssize_t show_hue(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%d\n", usbvision->hue >> 8); +} +static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); + +static ssize_t show_contrast(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%d\n", usbvision->contrast >> 8); +} +static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); + +static ssize_t show_brightness(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%d\n", usbvision->brightness >> 8); +} +static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); + +static ssize_t show_saturation(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%d\n", usbvision->saturation >> 8); +} +static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); + +static ssize_t show_streaming(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", YES_NO(usbvision->streaming)); +} +static CLASS_DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL); + +static ssize_t show_overlay(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", YES_NO(usbvision->overlay)); +} +static CLASS_DEVICE_ATTR(overlay, S_IRUGO, show_overlay, NULL); + +static ssize_t show_compression(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", YES_NO(usbvision->isocMode==ISOC_MODE_COMPRESS)); +} +static CLASS_DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL); + +static ssize_t show_device_bridge(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%d\n", usbvision->bridgeType); +} +static CLASS_DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL); + +static void usbvision_create_sysfs(struct video_device *vdev) +{ + if (vdev) { + video_device_create_file(vdev, &class_device_attr_version); + video_device_create_file(vdev, &class_device_attr_model); + video_device_create_file(vdev, &class_device_attr_hue); + video_device_create_file(vdev, &class_device_attr_contrast); + video_device_create_file(vdev, &class_device_attr_brightness); + video_device_create_file(vdev, &class_device_attr_saturation); + video_device_create_file(vdev, &class_device_attr_streaming); + video_device_create_file(vdev, &class_device_attr_overlay); + video_device_create_file(vdev, &class_device_attr_compression); + video_device_create_file(vdev, &class_device_attr_bridge); + } +} + +static void usbvision_remove_sysfs(struct video_device *vdev) +{ + if (vdev) { + video_device_remove_file(vdev, &class_device_attr_version); + video_device_remove_file(vdev, &class_device_attr_model); + video_device_remove_file(vdev, &class_device_attr_hue); + video_device_remove_file(vdev, &class_device_attr_contrast); + video_device_remove_file(vdev, &class_device_attr_brightness); + video_device_remove_file(vdev, &class_device_attr_saturation); + video_device_remove_file(vdev, &class_device_attr_streaming); + video_device_remove_file(vdev, &class_device_attr_overlay); + video_device_remove_file(vdev, &class_device_attr_compression); + video_device_remove_file(vdev, &class_device_attr_bridge); + } +} + + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +/* + * Here we want the physical address of the memory. + * This is used when initializing the contents of the area. + */ + + +void *usbvision_rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + mem_map_reserve(vmalloc_to_page((void *)adr)); + #else + SetPageReserved(vmalloc_to_page((void *)adr)); + #endif + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +void usbvision_rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + mem_map_unreserve(vmalloc_to_page((void *)adr)); + #else + ClearPageReserved(vmalloc_to_page((void *)adr)); + #endif + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + + + + + + +#if ENABLE_HEXDUMP +static void usbvision_hexdump(const unsigned char *data, int len) +{ + char tmp[80]; + int i, k; + + for (i = k = 0; len > 0; i++, len--) { + if (i > 0 && (i % 16 == 0)) { + printk("%s\n", tmp); + k = 0; + } + k += sprintf(&tmp[k], "%02x ", data[i]); + } + if (k > 0) + printk("%s\n", tmp); +} +#endif + + +/* These procedures handle the scratch ring buffer */ +int scratch_len(struct usb_usbvision *usbvision) /*This returns the amount of data actually in the buffer */ +{ + int len = usbvision->scratch_write_ptr - usbvision->scratch_read_ptr; + if (len < 0) { + len += scratch_buf_size; + } + PDEBUG(DBG_SCRATCH, "scratch_len() = %d\n", len); + + return len; +} + + +/* This returns the free space left in the buffer */ +int scratch_free(struct usb_usbvision *usbvision) +{ + int free = usbvision->scratch_read_ptr - usbvision->scratch_write_ptr; + if (free <= 0) { + free += scratch_buf_size; + } + if (free) { + free -= 1; /* at least one byte in the buffer must */ + /* left blank, otherwise there is no chance to differ between full and empty */ + } + PDEBUG(DBG_SCRATCH, "return %d\n", free); + + return free; +} + + +void *debug_memcpy(void *dest, void *src, size_t len) +{ + printk(KERN_DEBUG "memcpy(%p, %p, %d);\n", dest, src, len); + return memcpy(dest, src, len); +} + + +/* This puts data into the buffer */ +int scratch_put(struct usb_usbvision *usbvision, unsigned char *data, int len) +{ + int len_part; + + if (usbvision->scratch_write_ptr + len < scratch_buf_size) { + memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len); + usbvision->scratch_write_ptr += len; + } + else { + len_part = scratch_buf_size - usbvision->scratch_write_ptr; + memcpy(usbvision->scratch + usbvision->scratch_write_ptr, data, len_part); + if (len == len_part) { + usbvision->scratch_write_ptr = 0; /* just set write_ptr to zero */ + } + else { + memcpy(usbvision->scratch, data + len_part, len - len_part); + usbvision->scratch_write_ptr = len - len_part; + } + } + + PDEBUG(DBG_SCRATCH, "len=%d, new write_ptr=%d\n", len, usbvision->scratch_write_ptr); + + return len; +} + +/* This marks the write_ptr as position of new frame header */ +void scratch_mark_header(struct usb_usbvision *usbvision) +{ + PDEBUG(DBG_SCRATCH, "header at write_ptr=%d\n", usbvision->scratch_headermarker_write_ptr); + + usbvision->scratch_headermarker[usbvision->scratch_headermarker_write_ptr] = + usbvision->scratch_write_ptr; + usbvision->scratch_headermarker_write_ptr += 1; + usbvision->scratch_headermarker_write_ptr %= USBVISION_NUM_HEADERMARKER; +} + +/* This gets data from the buffer at the given "ptr" position */ +int scratch_get_extra(struct usb_usbvision *usbvision, unsigned char *data, int *ptr, int len) +{ + int len_part; + if (*ptr + len < scratch_buf_size) { + memcpy(data, usbvision->scratch + *ptr, len); + *ptr += len; + } + else { + len_part = scratch_buf_size - *ptr; + memcpy(data, usbvision->scratch + *ptr, len_part); + if (len == len_part) { + *ptr = 0; /* just set the y_ptr to zero */ + } + else { + memcpy(data + len_part, usbvision->scratch, len - len_part); + *ptr = len - len_part; + } + } + + PDEBUG(DBG_SCRATCH, "len=%d, new ptr=%d\n", len, *ptr); + + return len; +} + + +/* This sets the scratch extra read pointer */ +void scratch_set_extra_ptr(struct usb_usbvision *usbvision, int *ptr, int len) +{ + *ptr = (usbvision->scratch_read_ptr + len)%scratch_buf_size; + + PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr); +} + + +/*This increments the scratch extra read pointer */ +void scratch_inc_extra_ptr(int *ptr, int len) +{ + *ptr = (*ptr + len) % scratch_buf_size; + + PDEBUG(DBG_SCRATCH, "ptr=%d\n", *ptr); +} + + +/* This gets data from the buffer */ +int scratch_get(struct usb_usbvision *usbvision, unsigned char *data, int len) +{ + int len_part; + if (usbvision->scratch_read_ptr + len < scratch_buf_size) { + memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len); + usbvision->scratch_read_ptr += len; + } + else { + len_part = scratch_buf_size - usbvision->scratch_read_ptr; + memcpy(data, usbvision->scratch + usbvision->scratch_read_ptr, len_part); + if (len == len_part) { + usbvision->scratch_read_ptr = 0; /* just set the read_ptr to zero */ + } + else { + memcpy(data + len_part, usbvision->scratch, len - len_part); + usbvision->scratch_read_ptr = len - len_part; + } + } + + PDEBUG(DBG_SCRATCH, "len=%d, new read_ptr=%d\n", len, usbvision->scratch_read_ptr); + + return len; +} + + +/* This sets read pointer to next header and returns it */ +int scratch_get_header(struct usb_usbvision *usbvision,struct usbvision_frame_header *header) +{ + int errCode = 0; + + PDEBUG(DBG_SCRATCH, "from read_ptr=%d", usbvision->scratch_headermarker_read_ptr); + + while (usbvision->scratch_headermarker_write_ptr - + usbvision->scratch_headermarker_read_ptr != 0) { + usbvision->scratch_read_ptr = + usbvision->scratch_headermarker[usbvision->scratch_headermarker_read_ptr]; + usbvision->scratch_headermarker_read_ptr += 1; + usbvision->scratch_headermarker_read_ptr %= USBVISION_NUM_HEADERMARKER; + scratch_get(usbvision, (unsigned char *)header, USBVISION_HEADER_LENGTH); + if ((header->magic_1 == USBVISION_MAGIC_1) + && (header->magic_2 == USBVISION_MAGIC_2) + && (header->headerLength == USBVISION_HEADER_LENGTH)) { + errCode = USBVISION_HEADER_LENGTH; + header->frameWidth = header->frameWidthLo + (header->frameWidthHi << 8); + header->frameHeight = header->frameHeightLo + (header->frameHeightHi << 8); + break; + } + } + + return errCode; +} + + +/*This removes len bytes of old data from the buffer */ +void scratch_rm_old(struct usb_usbvision *usbvision, int len) +{ + + usbvision->scratch_read_ptr += len; + usbvision->scratch_read_ptr %= scratch_buf_size; + PDEBUG(DBG_SCRATCH, "read_ptr is now %d\n", usbvision->scratch_read_ptr); +} + + +/*This resets the buffer - kills all data in it too */ +void scratch_reset(struct usb_usbvision *usbvision) +{ + PDEBUG(DBG_SCRATCH, "\n"); + + usbvision->scratch_read_ptr = 0; + usbvision->scratch_write_ptr = 0; + usbvision->scratch_headermarker_read_ptr = 0; + usbvision->scratch_headermarker_write_ptr = 0; + usbvision->isocstate = IsocState_NoFrame; +} + + + +/* Here comes the OVERLAY stuff */ + +/* Tell the interrupt handler what to to. */ +static +void usbvision_cap(struct usb_usbvision* usbvision, int on) +{ + DEBUG(printk(KERN_DEBUG "usbvision_cap: overlay was %d, set it to %d\n", usbvision->overlay, on);) + + if (on) { + usbvision->overlay = 1; + } + else { + usbvision->overlay = 0; + } +} + + + + +/* append a new clipregion to the vector of video_clips */ +static +void usbvision_new_clip(struct v4l2_format* vf, struct v4l2_clip* vcp, int x, int y, int w, int h) +{ + vcp[vf->fmt.win.clipcount].c.left = x; + vcp[vf->fmt.win.clipcount].c.top = y; + vcp[vf->fmt.win.clipcount].c.width = w; + vcp[vf->fmt.win.clipcount].c.height = h; + vf->fmt.win.clipcount++; +} + + +#define mark_pixel(x,y) usbvision->clipmask[((x) + (y) * MAX_FRAME_WIDTH)/32] |= 0x00000001<<((x)%32) +#define clipped_pixel(index) usbvision->clipmask[(index)/32] & (0x00000001<<((index)%32)) + +static +void usbvision_built_overlay(struct usb_usbvision* usbvision, int count, struct v4l2_clip *vcp) +{ + usbvision->overlay_win = usbvision->overlay_base + + (signed int)usbvision->vid_win.fmt.win.w.left * usbvision->depth / 8 + + (signed int)usbvision->vid_win.fmt.win.w.top * usbvision->vid_buf.fmt.bytesperline; + + IODEBUG(printk(KERN_DEBUG "built_overlay base=%p, win=%p, bpl=%d, clips=%d, size=%dx%d\n", + usbvision->overlay_base, usbvision->overlay_win, + usbvision->vid_buf.fmt.bytesperline, count, + usbvision->vid_win.fmt.win.w.width, usbvision->vid_win.fmt.win.w.height);) + + + /* Add here generation of clipping mask */ +{ + int x_start, x_end, y_start, y_end; + int clip_index, x, y; + + memset(usbvision->clipmask, 0, USBVISION_CLIPMASK_SIZE); + + OVDEBUG(printk(KERN_DEBUG "clips = %d\n", count);) + + for(clip_index = 0; clip_index < count; clip_index++) { + OVDEBUG(printk(KERN_DEBUG "clip: %d,%d,%d,%d\n", vcp[clip_index].x, + vcp[clip_index].y, + vcp[clip_index].width, + vcp[clip_index].height);) + + x_start = vcp[clip_index].c.left; + if(x_start >= (int)usbvision->vid_win.fmt.win.w.width) { + OVDEBUG(printk(KERN_DEBUG "x_start=%d\n", x_start);) + continue; //clipping window is right of overlay window + } + x_end = x_start + vcp[clip_index].c.width; + if(x_end <= 0) { + OVDEBUG(printk(KERN_DEBUG "x_end=%d\n", x_end);) + continue; //clipping window is left of overlay window + } + + y_start = vcp[clip_index].c.top; + if(y_start >= (int)usbvision->vid_win.fmt.win.w.height) { + OVDEBUG(printk(KERN_DEBUG "y_start=%d\n", y_start);) + continue; //clipping window is below overlay window + } + y_end = y_start + vcp[clip_index].c.height; + if(y_end <= 0) { + OVDEBUG(printk(KERN_DEBUG "y_end=%d\n", y_end);) + continue; //clipping window is above overlay window + } + + //clip the clipping window + if (x_start < 0) { + x_start = 0; + } + if (x_end > (int)usbvision->vid_win.fmt.win.w.width) { + x_end = (int)usbvision->vid_win.fmt.win.w.width; + } + if (y_start < 0) { + y_start = 0; + } + if (y_end > (int)usbvision->vid_win.fmt.win.w.height) { + y_end = (int)usbvision->vid_win.fmt.win.w.height; + } + + OVDEBUG(printk(KERN_DEBUG "clip_o: %d,%d,%d,%d\n", x_start, y_start, x_end, y_end);) + + + + for(y = y_start; y < y_end; y++) { + for(x = x_start; x < x_end; x++) { + mark_pixel(x,y); + } + } + } +} + +} + + + +void usbvision_osd_char(struct usb_usbvision *usbvision, + struct usbvision_frame *frame, int x, int y, int ch) +{ + static const unsigned short digits[16] = { + 0xF6DE, /* 0 */ + 0x2492, /* 1 */ + 0xE7CE, /* 2 */ + 0xE79E, /* 3 */ + 0xB792, /* 4 */ + 0xF39E, /* 5 */ + 0xF3DE, /* 6 */ + 0xF492, /* 7 */ + 0xF7DE, /* 8 */ + 0xF79E, /* 9 */ + 0x77DA, /* a */ + 0xD75C, /* b */ + 0xF24E, /* c */ + 0xD6DC, /* d */ + 0xF34E, /* e */ + 0xF348 /* f */ + }; + unsigned short digit; + int ix, iy; + + if ((usbvision == NULL) || (frame == NULL)) + return; + + if (ch >= '0' && ch <= '9') + ch -= '0'; + else if (ch >= 'A' && ch <= 'F') + ch = 10 + (ch - 'A'); + else if (ch >= 'a' && ch <= 'f') + ch = 10 + (ch - 'a'); + else + return; + digit = digits[ch]; + + for (iy = 0; iy < 5; iy++) { + for (ix = 0; ix < 3; ix++) { + if (digit & 0x8000) { + // USBVISION_PUTPIXEL(frame, x + ix, y + iy, + // 0xFF, 0xFF, 0xFF); + } + digit = digit << 1; + } + } +} + + +void usbvision_osd_string(struct usb_usbvision *usbvision, + struct usbvision_frame *frame, + int x, int y, const char *str) +{ + while (*str) { + usbvision_osd_char(usbvision, frame, x, y, *str); + str++; + x += 4; /* 3 pixels character + 1 space */ + } +} + +/* + * usb_usbvision_osd_stats() + * + * On screen display of important debugging information. + * + */ +void usbvision_osd_stats(struct usb_usbvision *usbvision, + struct usbvision_frame *frame) +{ + const int y_diff = 8; + char tmp[16]; + int x = 10; + int y = 10; + + sprintf(tmp, "%8x", usbvision->frame_num); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", usbvision->isocUrbCount); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", usbvision->urb_length); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", usbvision->isocDataCount); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", usbvision->header_count); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", usbvision->scratch_ovf_count); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", usbvision->isocSkipCount); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", usbvision->isocErrCount); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", usbvision->saturation); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", usbvision->hue); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", usbvision->brightness >> 8); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", usbvision->contrast >> 12); + usbvision_osd_string(usbvision, frame, x, y, tmp); + y += y_diff; + +} + +/* + * usbvision_testpattern() + * + * Procedure forms a test pattern (yellow grid on blue background). + * + * Parameters: + * fullframe: if TRUE then entire frame is filled, otherwise the procedure + * continues from the current scanline. + * pmode 0: fill the frame with solid blue color (like on VCR or TV) + * 1: Draw a colored grid + * + */ +void usbvision_testpattern(struct usb_usbvision *usbvision, int fullframe, + int pmode) +{ + static const char proc[] = "usbvision_testpattern"; + struct usbvision_frame *frame; + unsigned char *f; + int num_cell = 0; + int scan_length = 0; + static int num_pass = 0; + + if (usbvision == NULL) { + printk(KERN_ERR "%s: usbvision == NULL\n", proc); + return; + } + if ((usbvision->curFrameNum < 0) + || (usbvision->curFrameNum >= USBVISION_NUMFRAMES)) { + printk(KERN_ERR "%s: usbvision->curFrameNum=%d.\n", proc, + usbvision->curFrameNum); + return; + } + + /* Grab the current frame */ + frame = &usbvision->frame[usbvision->curFrameNum]; + + /* Optionally start at the beginning */ + if (fullframe) { + frame->curline = 0; + frame->scanlength = 0; + } + + /* Form every scan line */ + for (; frame->curline < frame->frmheight; frame->curline++) { + int i; + + f = frame->data + (usbvision->curwidth * 3 * frame->curline); + for (i = 0; i < usbvision->curwidth; i++) { + unsigned char cb = 0x80; + unsigned char cg = 0; + unsigned char cr = 0; + + if (pmode == 1) { + if (frame->curline % 32 == 0) + cb = 0, cg = cr = 0xFF; + else if (i % 32 == 0) { + if (frame->curline % 32 == 1) + num_cell++; + cb = 0, cg = cr = 0xFF; + } else { + cb = + ((num_cell * 7) + + num_pass) & 0xFF; + cg = + ((num_cell * 5) + + num_pass * 2) & 0xFF; + cr = + ((num_cell * 3) + + num_pass * 3) & 0xFF; + } + } else { + /* Just the blue screen */ + } + + *f++ = cb; + *f++ = cg; + *f++ = cr; + scan_length += 3; + } + } + + frame->grabstate = FrameState_Done; + frame->scanlength += scan_length; + ++num_pass; + + /* We do this unconditionally, regardless of FLAGS_OSD_STATS */ + usbvision_osd_stats(usbvision, frame); +} + +/* + * Here comes the data parsing stuff that is run as interrupt + */ + +/* + * usbvision_find_header() + * + * Locate one of supported header markers in the scratch buffer. + */ +static enum ParseState usbvision_find_header(struct usb_usbvision *usbvision) +{ + struct usbvision_frame *frame; + int foundHeader = 0; + + if (usbvision->overlay) { + frame = &usbvision->overlay_frame; + } + else { + frame = &usbvision->frame[usbvision->curFrameNum]; + } + + while (scratch_get_header(usbvision, &frame->isocHeader) == USBVISION_HEADER_LENGTH) { + // found header in scratch + PDEBUG(DBG_HEADER, "found header: 0x%02x%02x %d %d %d %d %#x 0x%02x %u %u", + frame->isocHeader.magic_2, + frame->isocHeader.magic_1, + frame->isocHeader.headerLength, + frame->isocHeader.frameNum, + frame->isocHeader.framePhase, + frame->isocHeader.frameLatency, + frame->isocHeader.dataFormat, + frame->isocHeader.formatParam, + frame->isocHeader.frameWidth, + frame->isocHeader.frameHeight); + + if (usbvision->requestIntra) { + if (frame->isocHeader.formatParam & 0x80) { + foundHeader = 1; + usbvision->lastIsocFrameNum = -1; // do not check for lost frames this time + usbvision_unrequest_intra(usbvision); + break; + } + } + else { + foundHeader = 1; + break; + } + } + + if (foundHeader) { + frame->frmwidth = frame->isocHeader.frameWidth * usbvision->stretch_width; + frame->frmheight = frame->isocHeader.frameHeight * usbvision->stretch_height; + frame->v4l2_linesize = (frame->frmwidth * frame->v4l2_format.depth)>> 3; + usbvision->curFrame = frame; + } + else { // no header found + PDEBUG(DBG_HEADER, "skipping scratch data, no header"); + scratch_reset(usbvision); + return ParseState_EndParse; + } + + // found header + if (frame->isocHeader.dataFormat==ISOC_MODE_COMPRESS) { + //check isocHeader.frameNum for lost frames + if (usbvision->lastIsocFrameNum >= 0) { + if (((usbvision->lastIsocFrameNum + 1) % 32) != frame->isocHeader.frameNum) { + // unexpected frame drop: need to request new intra frame + PDEBUG(DBG_HEADER, "Lost frame before %d on USB", frame->isocHeader.frameNum); + usbvision_request_intra(usbvision); + return ParseState_NextFrame; + } + } + usbvision->lastIsocFrameNum = frame->isocHeader.frameNum; + } + usbvision->header_count++; + frame->scanstate = ScanState_Lines; + frame->curline = 0; + + if (flags & FLAGS_FORCE_TESTPATTERN) { + usbvision_testpattern(usbvision, 1, 1); + return ParseState_NextFrame; + } + return ParseState_Continue; +} + +static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision, + long *pcopylen) +{ + volatile struct usbvision_frame *frame; + unsigned char *f; + int len; + int i; + unsigned char yuyv[4]={180, 128, 10, 128}; // YUV components + unsigned char rv, gv, bv; // RGB components + int clipmask_index, bytes_per_pixel; + int overlay = usbvision->overlay; + int stretch_bytes, clipmask_add; + + if (overlay) { + frame = &usbvision->overlay_frame; + if (usbvision->overlay_base == NULL) { + //video_buffer is not set yet + return ParseState_NextFrame; + } + f = usbvision->overlay_win + frame->curline * + usbvision->vid_buf.fmt.bytesperline; + } + else { + frame = &usbvision->frame[usbvision->curFrameNum]; + f = frame->data + (frame->v4l2_linesize * frame->curline); + } + + /* Make sure there's enough data for the entire line */ + len = (frame->isocHeader.frameWidth * 2)+5; + if (scratch_len(usbvision) < len) { + PDEBUG(DBG_PARSE, "out of data in line %d, need %u.\n", frame->curline, len); + return ParseState_Out; + } + + if ((frame->curline + 1) >= frame->frmheight) { + return ParseState_NextFrame; + } + + bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; + stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel; + clipmask_index = frame->curline * MAX_FRAME_WIDTH; + clipmask_add = usbvision->stretch_width; + + for (i = 0; i < frame->frmwidth; i+=(2 * usbvision->stretch_width)) { + + scratch_get(usbvision, &yuyv[0], 4); + + if((overlay) && (clipped_pixel(clipmask_index))) { + f += bytes_per_pixel; + } + else if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f++ = yuyv[0]; // Y + *f++ = yuyv[3]; // U + } + else { + + YUV_TO_RGB_BY_THE_BOOK(yuyv[0], yuyv[1], yuyv[3], rv, gv, bv); + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3)); + *f++ = (0x07 & (gv >> 5)) | (0xF8 & rv); + break; + case V4L2_PIX_FMT_RGB24: + *f++ = bv; + *f++ = gv; + *f++ = rv; + break; + case V4L2_PIX_FMT_RGB32: + *f++ = bv; + *f++ = gv; + *f++ = rv; + f++; + break; + case V4L2_PIX_FMT_RGB555: + *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2)); + *f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1)); + break; + } + } + clipmask_index += clipmask_add; + f += stretch_bytes; + + if((overlay) && (clipped_pixel(clipmask_index))) { + f += bytes_per_pixel; + } + else if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f++ = yuyv[2]; // Y + *f++ = yuyv[1]; // V + } + else { + + YUV_TO_RGB_BY_THE_BOOK(yuyv[2], yuyv[1], yuyv[3], rv, gv, bv); + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3)); + *f++ = (0x07 & (gv >> 5)) | (0xF8 & rv); + break; + case V4L2_PIX_FMT_RGB24: + *f++ = bv; + *f++ = gv; + *f++ = rv; + break; + case V4L2_PIX_FMT_RGB32: + *f++ = bv; + *f++ = gv; + *f++ = rv; + f++; + break; + case V4L2_PIX_FMT_RGB555: + *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2)); + *f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1)); + break; + } + } + clipmask_index += clipmask_add; + f += stretch_bytes; + } + + frame->curline += usbvision->stretch_height; + *pcopylen += frame->v4l2_linesize * usbvision->stretch_height; + + if (frame->curline >= frame->frmheight) { + return ParseState_NextFrame; + } + else { + return ParseState_Continue; + } +} + + +static int usbvision_decompress(struct usb_usbvision *usbvision,unsigned char *Compressed, + unsigned char *Decompressed, int *StartPos, + int *BlockTypeStartPos, int Len) +{ + int RestPixel, Idx, MaxPos, Pos, ExtraPos, BlockLen, BlockTypePos, BlockTypeLen; + unsigned char BlockByte, BlockCode, BlockType, BlockTypeByte, Integrator; + + Integrator = 0; + Pos = *StartPos; + BlockTypePos = *BlockTypeStartPos; + MaxPos = 396; //Pos + Len; + ExtraPos = Pos; + BlockLen = 0; + BlockByte = 0; + BlockCode = 0; + BlockType = 0; + BlockTypeByte = 0; + BlockTypeLen = 0; + RestPixel = Len; + + for (Idx = 0; Idx < Len; Idx++) { + + if (BlockLen == 0) { + if (BlockTypeLen==0) { + BlockTypeByte = Compressed[BlockTypePos]; + BlockTypePos++; + BlockTypeLen = 4; + } + BlockType = (BlockTypeByte & 0xC0) >> 6; + + //statistic: + usbvision->ComprBlockTypes[BlockType]++; + + Pos = ExtraPos; + if (BlockType == 0) { + if(RestPixel >= 24) { + Idx += 23; + RestPixel -= 24; + Integrator = Decompressed[Idx]; + } else { + Idx += RestPixel - 1; + RestPixel = 0; + } + } else { + BlockCode = Compressed[Pos]; + Pos++; + if (RestPixel >= 24) { + BlockLen = 24; + } else { + BlockLen = RestPixel; + } + RestPixel -= BlockLen; + ExtraPos = Pos + (BlockLen / 4); + } + BlockTypeByte <<= 2; + BlockTypeLen -= 1; + } + if (BlockLen > 0) { + if ((BlockLen%4) == 0) { + BlockByte = Compressed[Pos]; + Pos++; + } + if (BlockType == 1) { //inter Block + Integrator = Decompressed[Idx]; + } + switch (BlockByte & 0xC0) { + case 0x03<<6: + Integrator += Compressed[ExtraPos]; + ExtraPos++; + break; + case 0x02<<6: + Integrator += BlockCode; + break; + case 0x00: + Integrator -= BlockCode; + break; + } + Decompressed[Idx] = Integrator; + BlockByte <<= 2; + BlockLen -= 1; + } + } + *StartPos = ExtraPos; + *BlockTypeStartPos = BlockTypePos; + return Idx; +} + + +/* + * usbvision_parse_compress() + * + * Parse compressed frame from the scratch buffer, put + * decoded RGB value into the current frame buffer and add the written + * number of bytes (RGB) to the *pcopylen. + * + */ +static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision, + long *pcopylen) +{ +#define USBVISION_STRIP_MAGIC 0x5A +#define USBVISION_STRIP_LEN_MAX 400 +#define USBVISION_STRIP_HEADER_LEN 3 + + struct usbvision_frame *frame; + unsigned char *f,*u = NULL ,*v = NULL; + unsigned char StripData[USBVISION_STRIP_LEN_MAX]; + unsigned char StripHeader[USBVISION_STRIP_HEADER_LEN]; + int Idx, IdxEnd, StripLen, StripPtr, StartBlockPos, BlockPos, BlockTypePos; + int clipmask_index, bytes_per_pixel, rc; + int overlay = usbvision->overlay; + int imageSize; + unsigned char rv, gv, bv; + static unsigned char *Y, *U, *V; + + if (overlay) { + frame = &usbvision->overlay_frame; + imageSize = frame->frmwidth * frame->frmheight; + if (usbvision->overlay_base == NULL) { + //video_buffer is not set yet + return ParseState_NextFrame; + } + f = usbvision->overlay_win + frame->curline * + usbvision->vid_buf.fmt.bytesperline; + } + else { + frame = &usbvision->frame[usbvision->curFrameNum]; + imageSize = frame->frmwidth * frame->frmheight; + if ( (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) || + (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) ) + { // this is a planar format + //... v4l2_linesize not used here. + f = frame->data + (frame->width * frame->curline); + } else + f = frame->data + (frame->v4l2_linesize * frame->curline); + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV){ //initialise u and v pointers + // get base of u and b planes add halfoffset + + u = frame->data + + imageSize + + (frame->frmwidth >>1) * frame->curline ; + v = u + (imageSize >>1 ); + + } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420){ + + v = frame->data + imageSize + ((frame->curline* (frame->width))>>2) ; + u = v + (imageSize >>2) ; + } + } + + if (frame->curline == 0) { + usbvision_adjust_compression(usbvision); + } + + if (scratch_len(usbvision) < USBVISION_STRIP_HEADER_LEN) { + return ParseState_Out; + } + + //get strip header without changing the scratch_read_ptr + scratch_set_extra_ptr(usbvision, &StripPtr, 0); + scratch_get_extra(usbvision, &StripHeader[0], &StripPtr, + USBVISION_STRIP_HEADER_LEN); + + if (StripHeader[0] != USBVISION_STRIP_MAGIC) { + // wrong strip magic + usbvision->stripMagicErrors++; + return ParseState_NextFrame; + } + + if (frame->curline != (int)StripHeader[2]) { + //line number missmatch error + usbvision->stripLineNumberErrors++; + } + + StripLen = 2 * (unsigned int)StripHeader[1]; + if (StripLen > USBVISION_STRIP_LEN_MAX) { + // strip overrun + // I think this never happens + usbvision_request_intra(usbvision); + } + + if (scratch_len(usbvision) < StripLen) { + //there is not enough data for the strip + return ParseState_Out; + } + + if (usbvision->IntraFrameBuffer) { + Y = usbvision->IntraFrameBuffer + frame->frmwidth * frame->curline; + U = usbvision->IntraFrameBuffer + imageSize + (frame->frmwidth / 2) * (frame->curline / 2); + V = usbvision->IntraFrameBuffer + imageSize / 4 * 5 + (frame->frmwidth / 2) * (frame->curline / 2); + } + else { + return ParseState_NextFrame; + } + + bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; + clipmask_index = frame->curline * MAX_FRAME_WIDTH; + + scratch_get(usbvision, StripData, StripLen); + + IdxEnd = frame->frmwidth; + BlockTypePos = USBVISION_STRIP_HEADER_LEN; + StartBlockPos = BlockTypePos + (IdxEnd - 1) / 96 + (IdxEnd / 2 - 1) / 96 + 2; + BlockPos = StartBlockPos; + + usbvision->BlockPos = BlockPos; + + if ((rc = usbvision_decompress(usbvision, StripData, Y, &BlockPos, &BlockTypePos, IdxEnd)) != IdxEnd) { + //return ParseState_Continue; + } + if (StripLen > usbvision->maxStripLen) { + usbvision->maxStripLen = StripLen; + } + + if (frame->curline%2) { + if ((rc = usbvision_decompress(usbvision, StripData, V, &BlockPos, &BlockTypePos, IdxEnd/2)) != IdxEnd/2) { + //return ParseState_Continue; + } + } + else { + if ((rc = usbvision_decompress(usbvision, StripData, U, &BlockPos, &BlockTypePos, IdxEnd/2)) != IdxEnd/2) { + //return ParseState_Continue; + } + } + + if (BlockPos > usbvision->comprBlockPos) { + usbvision->comprBlockPos = BlockPos; + } + if (BlockPos > StripLen) { + usbvision->stripLenErrors++; + } + + for (Idx = 0; Idx < IdxEnd; Idx++) { + if((overlay) && (clipped_pixel(clipmask_index))) { + f += bytes_per_pixel; + } + else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f++ = Y[Idx]; + *f++ = Idx & 0x01 ? U[Idx/2] : V[Idx/2]; + } + else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) { + *f++ = Y[Idx]; + if ( Idx & 0x01) + *u++ = U[Idx>>1] ; + else + *v++ = V[Idx>>1]; + } + else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) { + *f++ = Y [Idx]; + if ( !(( Idx & 0x01 ) | ( frame->curline & 0x01 )) ){ + +/* only need do this for 1 in 4 pixels */ +/* intraframe buffer is YUV420 format */ + + *u++ = U[Idx >>1]; + *v++ = V[Idx >>1]; + } + + } + else { + YUV_TO_RGB_BY_THE_BOOK(Y[Idx], U[Idx/2], V[Idx/2], rv, gv, bv); + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_GREY: + *f++ = Y[Idx]; + break; + case V4L2_PIX_FMT_RGB555: + *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 2)); + *f++ = (0x03 & (gv >> 6)) | (0x7C & (rv >> 1)); + break; + case V4L2_PIX_FMT_RGB565: + *f++ = (0x1F & (bv >> 3)) | (0xE0 & (gv << 3)); + *f++ = (0x07 & (gv >> 5)) | (0xF8 & rv); + break; + case V4L2_PIX_FMT_RGB24: + *f++ = bv; + *f++ = gv; + *f++ = rv; + break; + case V4L2_PIX_FMT_RGB32: + *f++ = bv; + *f++ = gv; + *f++ = rv; + f++; + break; + } + } + clipmask_index++; + } + /* Deal with non-integer no. of bytes for YUV420P */ + if (frame->v4l2_format.format != V4L2_PIX_FMT_YVU420 ) + *pcopylen += frame->v4l2_linesize; + else + *pcopylen += frame->curline & 0x01 ? frame->v4l2_linesize : frame->v4l2_linesize << 1; + + frame->curline += 1; + + if (frame->curline >= frame->frmheight) { + return ParseState_NextFrame; + } + else { + return ParseState_Continue; + } + +} + + +/* + * usbvision_parse_lines_420() + * + * Parse two lines from the scratch buffer, put + * decoded RGB value into the current frame buffer and add the written + * number of bytes (RGB) to the *pcopylen. + * + */ +static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision, + long *pcopylen) +{ + struct usbvision_frame *frame; + unsigned char *f_even = NULL, *f_odd = NULL; + unsigned int pixel_per_line, block; + int pixel, block_split; + int y_ptr, u_ptr, v_ptr, y_odd_offset; + const int y_block_size = 128; + const int uv_block_size = 64; + const int sub_block_size = 32; + const int y_step[] = { 0, 0, 0, 2 }, y_step_size = 4; + const int uv_step[]= { 0, 0, 0, 4 }, uv_step_size = 4; + unsigned char y[2], u, v; /* YUV components */ + int y_, u_, v_, vb, uvg, ur; + int r_, g_, b_; /* RGB components */ + unsigned char g; + int clipmask_even_index, clipmask_odd_index, bytes_per_pixel; + int clipmask_add, stretch_bytes; + int overlay = usbvision->overlay; + + if (overlay) { + frame = &usbvision->overlay_frame; + if (usbvision->overlay_base == NULL) { + //video_buffer is not set yet + return ParseState_NextFrame; + } + f_even = usbvision->overlay_win + frame->curline * + usbvision->vid_buf.fmt.bytesperline; + f_odd = f_even + usbvision->vid_buf.fmt.bytesperline * usbvision->stretch_height; + } + else { + frame = &usbvision->frame[usbvision->curFrameNum]; + f_even = frame->data + (frame->v4l2_linesize * frame->curline); + f_odd = f_even + frame->v4l2_linesize * usbvision->stretch_height; + } + + /* Make sure there's enough data for the entire line */ + /* In this mode usbvision transfer 3 bytes for every 2 pixels */ + /* I need two lines to decode the color */ + bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; + stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel; + clipmask_even_index = frame->curline * MAX_FRAME_WIDTH; + clipmask_odd_index = clipmask_even_index + MAX_FRAME_WIDTH; + clipmask_add = usbvision->stretch_width; + pixel_per_line = frame->isocHeader.frameWidth; + + if (scratch_len(usbvision) < (int)pixel_per_line * 3) { + //printk(KERN_DEBUG "out of data, need %d\n", len); + return ParseState_Out; + } + + if ((frame->curline + 1) >= frame->frmheight) { + return ParseState_NextFrame; + } + + block_split = (pixel_per_line%y_block_size) ? 1 : 0; //are some blocks splitted into different lines? + + y_odd_offset = (pixel_per_line / y_block_size) * (y_block_size + uv_block_size) + + block_split * uv_block_size; + + scratch_set_extra_ptr(usbvision, &y_ptr, y_odd_offset); + scratch_set_extra_ptr(usbvision, &u_ptr, y_block_size); + scratch_set_extra_ptr(usbvision, &v_ptr, y_odd_offset + + (4 - block_split) * sub_block_size); + + for (block = 0; block < (pixel_per_line / sub_block_size); + block++) { + + + for (pixel = 0; pixel < sub_block_size; pixel +=2) { + scratch_get(usbvision, &y[0], 2); + scratch_get_extra(usbvision, &u, &u_ptr, 1); + scratch_get_extra(usbvision, &v, &v_ptr, 1); + + //I don't use the YUV_TO_RGB macro for better performance + v_ = v - 128; + u_ = u - 128; + vb = 132252 * v_; + uvg= -53281 * u_ - 25625 * v_; + ur = 104595 * u_; + + if((overlay) && (clipped_pixel(clipmask_even_index))) { + f_even += bytes_per_pixel; + } + else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_even++ = y[0]; + *f_even++ = v; + } + else { + y_ = 76284 * (y[0] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg)>> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3)); + *f_even++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_even++ = LIMIT_RGB(b_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(r_); + break; + case V4L2_PIX_FMT_RGB32: + *f_even++ = LIMIT_RGB(b_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(r_); + f_even++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2)); + *f_even++ = (0x03 & ( g >> 6)) | + (0x7C & (LIMIT_RGB(r_) >> 1)); + break; + } + } + clipmask_even_index += clipmask_add; + f_even += stretch_bytes; + + if((overlay) && (clipped_pixel(clipmask_even_index))) { + f_even += bytes_per_pixel; + } + else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_even++ = y[1]; + *f_even++ = u; + } + else { + y_ = 76284 * (y[1] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg)>> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3)); + *f_even++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_even++ = LIMIT_RGB(b_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(r_); + break; + case V4L2_PIX_FMT_RGB32: + *f_even++ = LIMIT_RGB(b_); + *f_even++ = LIMIT_RGB(g_); + *f_even++ = LIMIT_RGB(r_); + f_even++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_even++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2)); + *f_even++ = (0x03 & ( g >> 6)) | + (0x7C & (LIMIT_RGB(r_) >> 1)); + break; + } + } + clipmask_even_index += clipmask_add; + f_even += stretch_bytes; + + scratch_get_extra(usbvision, &y[0], &y_ptr, 2); + + if ((overlay) && (clipped_pixel(clipmask_odd_index))) { + f_odd += bytes_per_pixel; + } + else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_odd++ = y[0]; + *f_odd++ = v; + } + else { + y_ = 76284 * (y[0] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg)>> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3)); + *f_odd++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_odd++ = LIMIT_RGB(b_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(r_); + break; + case V4L2_PIX_FMT_RGB32: + *f_odd++ = LIMIT_RGB(b_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(r_); + f_odd++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2)); + *f_odd++ = (0x03 & ( g >> 6)) | + (0x7C & (LIMIT_RGB(r_) >> 1)); + break; + } + } + clipmask_odd_index += clipmask_add; + f_odd += stretch_bytes; + + if((overlay) && (clipped_pixel(clipmask_odd_index))) { + f_odd += bytes_per_pixel; + } + else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + *f_odd++ = y[1]; + *f_odd++ = u; + } + else { + y_ = 76284 * (y[1] - 16); + + b_ = (y_ + vb) >> 16; + g_ = (y_ + uvg)>> 16; + r_ = (y_ + ur) >> 16; + + switch (frame->v4l2_format.format) { + case V4L2_PIX_FMT_RGB565: + g = LIMIT_RGB(g_); + *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 3)); + *f_odd++ = (0x07 & ( g >> 5)) | (0xF8 & LIMIT_RGB(r_)); + break; + case V4L2_PIX_FMT_RGB24: + *f_odd++ = LIMIT_RGB(b_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(r_); + break; + case V4L2_PIX_FMT_RGB32: + *f_odd++ = LIMIT_RGB(b_); + *f_odd++ = LIMIT_RGB(g_); + *f_odd++ = LIMIT_RGB(r_); + f_odd++; + break; + case V4L2_PIX_FMT_RGB555: + g = LIMIT_RGB(g_); + *f_odd++ = (0x1F & (LIMIT_RGB(b_) >> 3)) | (0xE0 & (g << 2)); + *f_odd++ = (0x03 & ( g >> 6)) | + (0x7C & (LIMIT_RGB(r_) >> 1)); + break; + } + } + clipmask_odd_index += clipmask_add; + f_odd += stretch_bytes; + } + + scratch_rm_old(usbvision,y_step[block % y_step_size] * sub_block_size); + scratch_inc_extra_ptr(&y_ptr, y_step[(block + 2 * block_split) % y_step_size] + * sub_block_size); + scratch_inc_extra_ptr(&u_ptr, uv_step[block % uv_step_size] + * sub_block_size); + scratch_inc_extra_ptr(&v_ptr, uv_step[(block + 2 * block_split) % uv_step_size] + * sub_block_size); + } + + scratch_rm_old(usbvision, pixel_per_line * 3 / 2 + + block_split * sub_block_size); + + frame->curline += 2 * usbvision->stretch_height; + *pcopylen += frame->v4l2_linesize * 2 * usbvision->stretch_height; + + if (frame->curline >= frame->frmheight) + return ParseState_NextFrame; + else + return ParseState_Continue; +} + +/* + * usbvision_parse_data() + * + * Generic routine to parse the scratch buffer. It employs either + * usbvision_find_header() or usbvision_parse_lines() to do most + * of work. + * + */ +static void usbvision_parse_data(struct usb_usbvision *usbvision) +{ + struct usbvision_frame *frame; + enum ParseState newstate; + long copylen = 0; + + if (usbvision->overlay) { + frame = &usbvision->overlay_frame; + } + else { + frame = &usbvision->frame[usbvision->curFrameNum]; + } + + PDEBUG(DBG_PARSE, "parsing len=%d\n", scratch_len(usbvision)); + + + while (1) { + + newstate = ParseState_Out; + if (scratch_len(usbvision)) { + if (frame->scanstate == ScanState_Scanning) { + newstate = usbvision_find_header(usbvision); + } + else if (frame->scanstate == ScanState_Lines) { + if (usbvision->isocMode == ISOC_MODE_YUV420) { + newstate = usbvision_parse_lines_420(usbvision, ©len); + } + else if (usbvision->isocMode == ISOC_MODE_YUV422) { + newstate = usbvision_parse_lines_422(usbvision, ©len); + } + else if (usbvision->isocMode == ISOC_MODE_COMPRESS) { + newstate = usbvision_parse_compress(usbvision, ©len); + } + + } + } + if (newstate == ParseState_Continue) { + continue; + } + else if ((newstate == ParseState_NextFrame) || (newstate == ParseState_Out)) { + break; + } + else { + return; /* ParseState_EndParse */ + } + } + + if (newstate == ParseState_NextFrame) { + frame->grabstate = FrameState_Done; + do_gettimeofday(&(frame->timestamp)); + frame->sequence = usbvision->frame_num; + if (usbvision->overlay) { + frame->grabstate = FrameState_Grabbing; + frame->scanstate = ScanState_Scanning; + frame->scanlength = 0; + copylen = 0; + } + else { + usbvision->curFrameNum = -1; + } + usbvision->frame_num++; + + /* Optionally display statistics on the screen */ + if (flags & FLAGS_OSD_STATS) + usbvision_osd_stats(usbvision, frame); + + /* This will cause the process to request another frame. */ + if (waitqueue_active(&frame->wq)) { + wake_up_interruptible(&frame->wq); + } + } + + /* Update the frame's uncompressed length. */ + frame->scanlength += copylen; +} + + +/* + * Make all of the blocks of data contiguous + */ +static int usbvision_compress_isochronous(struct usb_usbvision *usbvision, + struct urb *urb) +{ + unsigned char *packet_data; + int i, totlen = 0; + + for (i = 0; i < urb->number_of_packets; i++) { + int packet_len = urb->iso_frame_desc[i].actual_length; + int packet_stat = urb->iso_frame_desc[i].status; + + packet_data = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (packet_stat) { // packet_stat != 0 ????????????? + PDEBUG(DBG_ISOC, "data error: [%d] len=%d, status=%X", i, packet_len, packet_stat); + usbvision->isocErrCount++; + continue; + } + + /* Detect and ignore empty packets */ + if (packet_len < 0) { + PDEBUG(DBG_ISOC, "error packet [%d]", i); + usbvision->isocSkipCount++; + continue; + } + else if (packet_len == 0) { /* Frame end ????? */ + PDEBUG(DBG_ISOC, "null packet [%d]", i); + usbvision->isocstate=IsocState_NoFrame; + usbvision->isocSkipCount++; + continue; + } + else if (packet_len > usbvision->isocPacketSize) { + PDEBUG(DBG_ISOC, "packet[%d] > isocPacketSize", i); + usbvision->isocSkipCount++; + continue; + } + + PDEBUG(DBG_ISOC, "packet ok [%d] len=%d", i, packet_len); + + if (usbvision->isocstate==IsocState_NoFrame) { //new frame begins + usbvision->isocstate=IsocState_InFrame; + scratch_mark_header(usbvision); + usbvision_measure_bandwidth(usbvision); + PDEBUG(DBG_ISOC, "packet with header"); + } + + /* + * If usbvision continues to feed us with data but there is no + * consumption (if, for example, V4L client fell asleep) we + * may overflow the buffer. We have to move old data over to + * free room for new data. This is bad for old data. If we + * just drop new data then it's bad for new data... choose + * your favorite evil here. + */ + if (scratch_free(usbvision) < packet_len) { + + usbvision->scratch_ovf_count++; + PDEBUG(DBG_ISOC, "scratch buf overflow! scr_len: %d, n: %d", + scratch_len(usbvision), packet_len); + scratch_rm_old(usbvision, packet_len - scratch_free(usbvision)); + } + + /* Now we know that there is enough room in scratch buffer */ + scratch_put(usbvision, packet_data, packet_len); + totlen += packet_len; + usbvision->isocDataCount += packet_len; + usbvision->isocPacketCount++; + } +#if ENABLE_HEXDUMP + if (totlen > 0) { + static int foo = 0; + if (foo < 1) { + printk(KERN_DEBUG "+%d.\n", usbvision->scratchlen); + usbvision_hexdump(data0, (totlen > 64) ? 64 : totlen); + ++foo; + } + } +#endif + return totlen; +} + +static void usbvision_isocIrq(struct urb *urb, struct pt_regs *regs) +{ + int errCode = 0; + int len; + struct usb_usbvision *usbvision = urb->context; + int i; + unsigned long startTime = jiffies; + + /* We don't want to do anything if we are about to be removed! */ + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return; + + if (!usbvision->streaming) { + PDEBUG(DBG_IRQ, "oops, not streaming, but interrupt"); + return; + } + + /* Copy the data received into our scratch buffer */ + len = usbvision_compress_isochronous(usbvision, urb); + + usbvision->isocUrbCount++; + usbvision->urb_length = len; + + for (i = 0; i < USBVISION_URB_FRAMES; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + urb->dev = usbvision->dev; + errCode = usb_submit_urb (urb, GFP_ATOMIC); + +/* Disable this warning. By design of the driver. */ +// if(errCode) { +// err("%s: usb_submit_urb failed: error %d", __FUNCTION__, errCode); +// } + + /* If we collected enough data let's parse! */ + if (scratch_len(usbvision) > USBVISION_HEADER_LENGTH) { /* 12 == header_length */ + /*If we don't have a frame we're current working on, complain */ + if ((usbvision->curFrameNum >= 0) || (usbvision->overlay)) + usbvision_parse_data(usbvision); + else { + PDEBUG(DBG_IRQ, "received data, but no one needs it"); + scratch_reset(usbvision); + } + } + usbvision->timeInIrq += jiffies - startTime; + return; +} + +/*************************************/ +/* Low level usbvision access functions */ +/*************************************/ + +/* + * usbvision_read_reg() + * + * return < 0 -> Error + * >= 0 -> Data + */ + +static int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg) +{ + int errCode = 0; + unsigned char buffer[1]; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -1; + + errCode = usb_control_msg(usbvision->dev, usb_rcvctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + 0, (__u16) reg, buffer, 1, HZ); + + if (errCode < 0) { + err("%s: failed: error %d", __FUNCTION__, errCode); + return errCode; + } + return buffer[0]; +} + +/* + * usbvision_write_reg() + * + * return 1 -> Reg written + * 0 -> usbvision is not yet ready + * -1 -> Something went wrong + */ + +static int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, + unsigned char value) +{ + int errCode = 0; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + errCode = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ); + + if (errCode < 0) { + err("%s: failed: error %d", __FUNCTION__, errCode); + } + return errCode; +} + + +static void usbvision_ctrlUrb_complete(struct urb *urb, struct pt_regs *regs) +{ + struct usb_usbvision *usbvision = (struct usb_usbvision *)urb->context; + + PDEBUG(DBG_IRQ, ""); + usbvision->ctrlUrbBusy = 0; + if (waitqueue_active(&usbvision->ctrlUrb_wq)) { + wake_up_interruptible(&usbvision->ctrlUrb_wq); + } +} + + +static int usbvision_write_reg_irq(struct usb_usbvision *usbvision,int address, + unsigned char *data, int len) +{ + int errCode = 0; + + PDEBUG(DBG_IRQ, ""); + if (len > 8) { + return -EFAULT; + } +// down(&usbvision->ctrlUrbLock); + if (usbvision->ctrlUrbBusy) { +// up(&usbvision->ctrlUrbLock); + return -EBUSY; + } + usbvision->ctrlUrbBusy = 1; +// up(&usbvision->ctrlUrbLock); + + usbvision->ctrlUrbSetup.bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + usbvision->ctrlUrbSetup.bRequest = USBVISION_OP_CODE; + usbvision->ctrlUrbSetup.wValue = 0; + usbvision->ctrlUrbSetup.wIndex = cpu_to_le16(address); + usbvision->ctrlUrbSetup.wLength = cpu_to_le16(len); + usb_fill_control_urb (usbvision->ctrlUrb, usbvision->dev, + usb_sndctrlpipe(usbvision->dev, 1), + (unsigned char *)&usbvision->ctrlUrbSetup, + (void *)usbvision->ctrlUrbBuffer, len, + usbvision_ctrlUrb_complete, + (void *)usbvision); + + memcpy(usbvision->ctrlUrbBuffer, data, len); + + errCode = usb_submit_urb(usbvision->ctrlUrb, GFP_ATOMIC); + if (errCode < 0) { + // error in usb_submit_urb() + usbvision->ctrlUrbBusy = 0; + } + PDEBUG(DBG_IRQ, "submit %d byte: error %d", len, errCode); + return errCode; +} + + + + +static int usbvision_init_compression(struct usb_usbvision *usbvision) +{ + int errCode = 0; + + usbvision->lastIsocFrameNum = -1; + usbvision->isocDataCount = 0; + usbvision->isocPacketCount = 0; + usbvision->isocSkipCount = 0; + usbvision->comprLevel = 50; + usbvision->lastComprLevel = -1; + usbvision->isocUrbCount = 0; + usbvision->requestIntra = 1; + usbvision->isocMeasureBandwidthCount = 0; + + return errCode; +} + +/* this function measures the used bandwidth since last call + * return: 0 : no error + * sets usedBandwidth to 1-100 : 1-100% of full bandwidth resp. to isocPacketSize + */ +static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision) +{ + int errCode = 0; + + if (usbvision->isocMeasureBandwidthCount < 2) { // this gives an average bandwidth of 3 frames + usbvision->isocMeasureBandwidthCount++; + return errCode; + } + if ((usbvision->isocPacketSize > 0) && (usbvision->isocPacketCount > 0)) { + usbvision->usedBandwidth = usbvision->isocDataCount / + (usbvision->isocPacketCount + usbvision->isocSkipCount) * + 100 / usbvision->isocPacketSize; + } + usbvision->isocMeasureBandwidthCount = 0; + usbvision->isocDataCount = 0; + usbvision->isocPacketCount = 0; + usbvision->isocSkipCount = 0; + return errCode; +} + +static int usbvision_adjust_compression (struct usb_usbvision *usbvision) +{ + int errCode = 0; + unsigned char buffer[6]; + + PDEBUG(DBG_IRQ, ""); + if ((adjustCompression) && (usbvision->usedBandwidth > 0)) { + usbvision->comprLevel += (usbvision->usedBandwidth - 90) / 2; + RESTRICT_TO_RANGE(usbvision->comprLevel, 0, 100); + if (usbvision->comprLevel != usbvision->lastComprLevel) { + int distorsion; + if (usbvision->bridgeType == BRIDGE_NT1004 || usbvision->bridgeType == BRIDGE_NT1005) { + buffer[0] = (unsigned char)(4 + 16 * usbvision->comprLevel / 100); // PCM Threshold 1 + buffer[1] = (unsigned char)(4 + 8 * usbvision->comprLevel / 100); // PCM Threshold 2 + distorsion = 7 + 248 * usbvision->comprLevel / 100; + buffer[2] = (unsigned char)(distorsion & 0xFF); // Average distorsion Threshold (inter) + buffer[3] = (unsigned char)(distorsion & 0xFF); // Average distorsion Threshold (intra) + distorsion = 1 + 42 * usbvision->comprLevel / 100; + buffer[4] = (unsigned char)(distorsion & 0xFF); // Maximum distorsion Threshold (inter) + buffer[5] = (unsigned char)(distorsion & 0xFF); // Maximum distorsion Threshold (intra) + } + else { //BRIDGE_NT1003 + buffer[0] = (unsigned char)(4 + 16 * usbvision->comprLevel / 100); // PCM threshold 1 + buffer[1] = (unsigned char)(4 + 8 * usbvision->comprLevel / 100); // PCM threshold 2 + distorsion = 2 + 253 * usbvision->comprLevel / 100; + buffer[2] = (unsigned char)(distorsion & 0xFF); // distorsion threshold bit0-7 + buffer[3] = 0; //(unsigned char)((distorsion >> 8) & 0x0F); // distorsion threshold bit 8-11 + distorsion = 0 + 43 * usbvision->comprLevel / 100; + buffer[4] = (unsigned char)(distorsion & 0xFF); // maximum distorsion bit0-7 + buffer[5] = 0; //(unsigned char)((distorsion >> 8) & 0x01); // maximum distorsion bit 8 + } + errCode = usbvision_write_reg_irq(usbvision, USBVISION_PCM_THR1, buffer, 6); + if (errCode == 0){ + PDEBUG(DBG_IRQ, "new compr params %#02x %#02x %#02x %#02x %#02x %#02x", buffer[0], + buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]); + usbvision->lastComprLevel = usbvision->comprLevel; + } + } + } + return errCode; +} + +static int usbvision_request_intra (struct usb_usbvision *usbvision) +{ + int errCode = 0; + unsigned char buffer[1]; + + PDEBUG(DBG_IRQ, ""); + usbvision->requestIntra = 1; + buffer[0] = 1; + usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1); + return errCode; +} + +static int usbvision_unrequest_intra (struct usb_usbvision *usbvision) +{ + int errCode = 0; + unsigned char buffer[1]; + + PDEBUG(DBG_IRQ, ""); + usbvision->requestIntra = 0; + buffer[0] = 0; + usbvision_write_reg_irq(usbvision, USBVISION_FORCE_INTRA, buffer, 1); + return errCode; +} + +/* ----------------------------------------------------------------------- */ +/* I2C functions */ +/* ----------------------------------------------------------------------- */ + +static void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, + void *arg) +{ + + int i; + + for (i = 0; i < USBVISION_I2C_CLIENTS_MAX; i++) { + if (NULL == usbvision->i2c_clients[i]) + continue; + if (NULL == usbvision->i2c_clients[i]->driver->command) + continue; + usbvision->i2c_clients[i]->driver->command(usbvision->i2c_clients[i], cmd, arg); + } +} + +static int attach_inform(struct i2c_client *client) +{ + struct usb_usbvision *usbvision; + struct tuner_setup tun_addr; + int i; + v4l2_std_id stdId; + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + usbvision = (struct usb_usbvision *)client->adapter->data; + #else + usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); + #endif + + for (i = 0; i < USBVISION_I2C_CLIENTS_MAX; i++) { + if (usbvision->i2c_clients[i] == NULL || + usbvision->i2c_clients[i]->driver->id == + client->driver->id) { + usbvision->i2c_clients[i] = client; + break; + } + } + if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) { + tun_addr.mode_mask = T_ANALOG_TV; + tun_addr.type = usbvision->tuner_type; + tun_addr.addr = ADDR_UNSET; + client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_addr); + + call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->input.index); + } + // FIXME : need to add a call VIDIOC_S_CTRL for each control +/* call_i2c_clients(usbvision, DECODER_SET_PICTURE, &usbvision->vpic); */ + stdId = usbvision->input.std; + call_i2c_clients(usbvision, VIDIOC_S_STD, &stdId); + + PDEBUG(DBG_I2C, "usbvision[%d] attaches %s", usbvision->nr, client->name); + + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + struct usb_usbvision *usbvision; + int i; + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + usbvision = (struct usb_usbvision *)client->adapter->data; + #else + usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); + #endif + + PDEBUG(DBG_I2C, "usbvision[%d] detaches %s", usbvision->nr, client->name); + for (i = 0; i < USBVISION_I2C_CLIENTS_MAX; i++) { + if (NULL != usbvision->i2c_clients[i] && + usbvision->i2c_clients[i]->driver->id == + client->driver->id) { + usbvision->i2c_clients[i] = NULL; + break; + } + } + return 0; +} + +static int +usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr, + char *buf, short len) +{ + int rc, retries; + + for (retries = 5;;) { + rc = usbvision_write_reg(usbvision, USBVISION_SER_ADRS, addr); + if (rc < 0) + return rc; + + /* Initiate byte read cycle */ + /* USBVISION_SER_CONT <- d0-d2 n. of bytes to r/w */ + /* d3 0=Wr 1=Rd */ + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, + (len & 0x07) | 0x18); + if (rc < 0) + return rc; + + /* Test for Busy and ACK */ + do { + /* USBVISION_SER_CONT -> d4 == 0 busy */ + rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); + } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ + if (rc < 0) + return rc; + + /* USBVISION_SER_CONT -> d5 == 1 Not ack */ + if ((rc & 0x20) == 0) /* Ack? */ + break; + + /* I2C abort */ + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); + if (rc < 0) + return rc; + + if (--retries < 0) + return -1; + } + + switch (len) { + case 4: + buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4); + case 3: + buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3); + case 2: + buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2); + case 1: + buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1); + break; + default: + printk(KERN_ERR + "usbvision_i2c_read_max4: buffer length > 4\n"); + } + + if (debug & DBG_I2C) { + int idx; + for (idx = 0; idx < len; idx++) { + PDEBUG(DBG_I2C, "read %x from address %x", (unsigned char)buf[idx], addr); + } + } + return len; +} + + +static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision, + unsigned char addr, const char *buf, + short len) +{ + int rc, retries; + int i; + unsigned char value[6]; + unsigned char ser_cont; + + ser_cont = (len & 0x07) | 0x10; + + value[0] = addr; + value[1] = ser_cont; + for (i = 0; i < len; i++) + value[i + 2] = buf[i]; + + for (retries = 5;;) { + rc = usb_control_msg(usbvision->dev, + usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_SER_ADRS, value, + len + 2, HZ); + + if (rc < 0) + return rc; + + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, + (len & 0x07) | 0x10); + if (rc < 0) + return rc; + + /* Test for Busy and ACK */ + do { + rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); + } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ + if (rc < 0) + return rc; + + if ((rc & 0x20) == 0) /* Ack? */ + break; + + /* I2C abort */ + usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); + + if (--retries < 0) + return -1; + + } + + if (debug & DBG_I2C) { + int idx; + for (idx = 0; idx < len; idx++) { + PDEBUG(DBG_I2C, "wrote %x at address %x", (unsigned char)buf[idx], addr); + } + } + return len; +} + +static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, + short len) +{ + char *bufPtr = buf; + int retval; + int wrcount = 0; + int count; + int maxLen = 4; + struct usb_usbvision *usbvision = (struct usb_usbvision *) data; + + while (len > 0) { + count = (len > maxLen) ? maxLen : len; + retval = usbvision_i2c_write_max4(usbvision, addr, bufPtr, count); + if (retval > 0) { + len -= count; + bufPtr += count; + wrcount += count; + } else + return (retval < 0) ? retval : -EFAULT; + } + return wrcount; +} + +static int usbvision_i2c_read(void *data, unsigned char addr, char *buf, + short len) +{ + char temp[4]; + int retval, i; + int rdcount = 0; + int count; + struct usb_usbvision *usbvision = (struct usb_usbvision *) data; + + while (len > 0) { + count = (len > 3) ? 4 : len; + retval = usbvision_i2c_read_max4(usbvision, addr, temp, count); + if (retval > 0) { + for (i = 0; i < len; i++) + buf[rdcount + i] = temp[i]; + len -= count; + rdcount += count; + } else + return (retval < 0) ? retval : -EFAULT; + } + return rdcount; +} + +static struct i2c_algo_usb_data i2c_algo_template = { + .data = NULL, + .inb = usbvision_i2c_read, + .outb = usbvision_i2c_write, + .udelay = 10, + .mdelay = 10, + .timeout = 100, +}; + +static struct i2c_adapter i2c_adap_template = { + .owner = THIS_MODULE, + .name = "usbvision", + .id = I2C_HW_B_BT848, /* FIXME */ + .algo = NULL, + .algo_data = NULL, + .client_register = attach_inform, + .client_unregister = detach_inform, +#if defined (I2C_ADAP_CLASS_TV_ANALOG) + .class = I2C_ADAP_CLASS_TV_ANALOG, +#elif defined (I2C_CLASS_TV_ANALOG) + .class = I2C_CLASS_TV_ANALOG, +#endif +}; + +static struct i2c_client i2c_client_template = { + .name = "usbvision internal", + .flags = 0, + .addr = 0, + .adapter = NULL, + .driver = NULL, +}; + +static int usbvision_init_i2c(struct usb_usbvision *usbvision) +{ + memcpy(&usbvision->i2c_adap, &i2c_adap_template, + sizeof(struct i2c_adapter)); + memcpy(&usbvision->i2c_algo, &i2c_algo_template, + sizeof(struct i2c_algo_usb_data)); + memcpy(&usbvision->i2c_client, &i2c_client_template, + sizeof(struct i2c_client)); + + sprintf(usbvision->i2c_adap.name + strlen(usbvision->i2c_adap.name), + " #%d", usbvision->vdev->minor & 0x1f); + PDEBUG(DBG_I2C, "Adaptername: %s", usbvision->i2c_adap.name); + + i2c_set_adapdata(&usbvision->i2c_adap, usbvision); + i2c_set_clientdata(&usbvision->i2c_client, usbvision); + i2c_set_algo_usb_data(&usbvision->i2c_algo, usbvision); + + usbvision->i2c_adap.algo_data = &usbvision->i2c_algo; + usbvision->i2c_client.adapter = &usbvision->i2c_adap; + + if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) { + printk(KERN_ERR "usbvision_init_i2c: can't wirte reg\n"); + return -EBUSY; + } + +#ifdef CONFIG_KMOD + /* Request the load of the i2c modules we need */ + if (autoload) { + switch (usbvision_device_data[usbvision->DevModel].Codec) { + case CODEC_SAA7113: + request_module("saa7115"); + break; + case CODEC_SAA7111: + request_module("saa7115"); + break; + } + if (usbvision_device_data[usbvision->DevModel].Tuner == 1) { + request_module("tuner"); + } + } +#endif + + usbvision->i2c_ok = usbvision_i2c_usb_add_bus(&usbvision->i2c_adap); + + return usbvision->i2c_ok; + +} + + +/****************************/ +/* usbvision utility functions */ +/****************************/ + +static int usbvision_power_off(struct usb_usbvision *usbvision) +{ + int errCode = 0; + + PDEBUG(DBG_FUNC, ""); + + errCode = usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN); + if (errCode == 1) { + usbvision->power = 0; + } + PDEBUG(DBG_FUNC, "%s: errCode %d", (errCode!=1)?"ERROR":"power is off", errCode); + return errCode; +} + + +// to call usbvision_power_off from task queue +static void call_usbvision_power_off(void *_usbvision) +{ + struct usb_usbvision *usbvision = _usbvision; + + PDEBUG(DBG_FUNC, ""); + down_interruptible(&usbvision->lock); + if(usbvision->user == 0) { + usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_power_off(usbvision); + usbvision->initialized = 0; + } + up(&usbvision->lock); +} + + +/* + * usbvision_set_video_format() + * + */ +static int usbvision_set_video_format(struct usb_usbvision *usbvision, int format) +{ + static const char proc[] = "usbvision_set_video_format"; + int rc; + unsigned char value[2]; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + PDEBUG(DBG_FUNC, "isocMode %#02x", format); + + if ((format != ISOC_MODE_YUV422) + && (format != ISOC_MODE_YUV420) + && (format != ISOC_MODE_COMPRESS)) { + printk(KERN_ERR "usbvision: unknown video format %02x, using default YUV420", + format); + format = ISOC_MODE_YUV420; + } + value[0] = 0x0A; //TODO: See the effect of the filter + value[1] = format; + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_FILT_CONT, value, 2, HZ); + + if (rc < 0) { + printk(KERN_ERR "%s: ERROR=%d. USBVISION stopped - " + "reconnect or reload driver.\n", proc, rc); + } + usbvision->isocMode = format; + return rc; +} + +/* + * usbvision_set_output() + * + */ + +static int usbvision_set_output(struct usb_usbvision *usbvision, int width, + int height) +{ + int errCode = 0; + int UsbWidth, UsbHeight; + unsigned int frameRate=0, frameDrop=0; + unsigned char value[4]; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) { + return 0; + } + + if (width > MAX_USB_WIDTH) { + UsbWidth = width / 2; + usbvision->stretch_width = 2; + } + else { + UsbWidth = width; + usbvision->stretch_width = 1; + } + + if (height > MAX_USB_HEIGHT) { + UsbHeight = height / 2; + usbvision->stretch_height = 2; + } + else { + UsbHeight = height; + usbvision->stretch_height = 1; + } + + RESTRICT_TO_RANGE(UsbWidth, MIN_FRAME_WIDTH, MAX_USB_WIDTH); + UsbWidth &= ~(MIN_FRAME_WIDTH-1); + RESTRICT_TO_RANGE(UsbHeight, MIN_FRAME_HEIGHT, MAX_USB_HEIGHT); + UsbHeight &= ~(1); + + PDEBUG(DBG_FUNC, "usb %dx%d; screen %dx%d; stretch %dx%d", + UsbWidth, UsbHeight, width, height, + usbvision->stretch_width, usbvision->stretch_height); + + /* I'll not rewrite the same values */ + if ((UsbWidth != usbvision->curwidth) || (UsbHeight != usbvision->curheight)) { + value[0] = UsbWidth & 0xff; //LSB + value[1] = (UsbWidth >> 8) & 0x03; //MSB + value[2] = UsbHeight & 0xff; //LSB + value[3] = (UsbHeight >> 8) & 0x03; //MSB + + errCode = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + 0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ); + + if (errCode < 0) { + err("%s failed: error %d", __FUNCTION__, errCode); + return errCode; + } + usbvision->curwidth = usbvision->stretch_width * UsbWidth; + usbvision->curheight = usbvision->stretch_height * UsbHeight; + } + + if (usbvision->isocMode == ISOC_MODE_YUV422) { + frameRate = (usbvision->isocPacketSize * 1000) / (UsbWidth * UsbHeight * 2); + } + else if (usbvision->isocMode == ISOC_MODE_YUV420) { + frameRate = (usbvision->isocPacketSize * 1000) / ((UsbWidth * UsbHeight * 12) / 8); + } + else { + frameRate = FRAMERATE_MAX; + } + + if (usbvision->input.std & V4L2_STD_625_50) { + frameDrop = frameRate * 32 / 25 - 1; + } + else if (usbvision->input.std & V4L2_STD_525_60) { + frameDrop = frameRate * 32 / 30 - 1; + } + + RESTRICT_TO_RANGE(frameDrop, FRAMERATE_MIN, FRAMERATE_MAX); + + PDEBUG(DBG_FUNC, "frameRate %d fps, frameDrop %d", frameRate, frameDrop); + + frameDrop = FRAMERATE_MAX; // We can allow the maximum here, because dropping is controlled + + /* frameDrop = 7; => framePhase = 1, 5, 9, 13, 17, 21, 25, 0, 4, 8, ... + => frameSkip = 4; + => frameRate = (7 + 1) * 25 / 32 = 200 / 32 = 6.25; + + frameDrop = 9; => framePhase = 1, 5, 8, 11, 14, 17, 21, 24, 27, 1, 4, 8, ... + => frameSkip = 4, 3, 3, 3, 3, 4, 3, 3, 3, 3, 4, ... + => frameRate = (9 + 1) * 25 / 32 = 250 / 32 = 7.8125; + */ + errCode = usbvision_write_reg(usbvision, USBVISION_FRM_RATE, frameDrop); + return errCode; +} + + +/* + * usbvision_set_compress_params() + * + */ + +static int usbvision_set_compress_params(struct usb_usbvision *usbvision) +{ + static const char proc[] = "usbvision_set_compresion_params: "; + int rc; + unsigned char value[6]; + + value[0] = 0x0F; // Intra-Compression cycle + value[1] = 0x01; // Reg.45 one line per strip + value[2] = 0x00; // Reg.46 Force intra mode on all new frames + value[3] = 0x00; // Reg.47 FORCE_UP <- 0 normal operation (not force) + value[4] = 0xA2; // Reg.48 BUF_THR I'm not sure if this does something in not compressed mode. + value[5] = 0x00; // Reg.49 DVI_YUV This has nothing to do with compression + + //catched values for NT1004 + // value[0] = 0xFF; // Never apply intra mode automatically + // value[1] = 0xF1; // Use full frame height for virtual strip width; One line per strip + // value[2] = 0x01; // Force intra mode on all new frames + // value[3] = 0x00; // Strip size 400 Bytes; do not force up + // value[4] = 0xA2; // + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_INTRA_CYC, value, 5, HZ); + + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " + "reconnect or reload driver.\n", proc, rc); + return rc; + } + + if (usbvision->bridgeType == BRIDGE_NT1004) { + value[0] = 20; // PCM Threshold 1 + value[1] = 12; // PCM Threshold 2 + value[2] = 255; // Distorsion Threshold inter + value[3] = 255; // Distorsion Threshold intra + value[4] = 43; // Max Distorsion inter + value[5] = 43; // Max Distorsion intra + } + else { + value[0] = 20; // PCM Threshold 1 + value[1] = 12; // PCM Threshold 2 + value[2] = 255; // Distorsion Threshold d7-d0 + value[3] = 0; // Distorsion Threshold d11-d8 + value[4] = 43; // Max Distorsion d7-d0 + value[5] = 0; // Max Distorsion d8 + } + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_PCM_THR1, value, 6, HZ); + + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " + "reconnect or reload driver.\n", proc, rc); + return rc; + } + + + return rc; +} + + +/* + * usbvision_set_input() + * + * Set the input (saa711x, ...) size x y and other misc input params + * I've no idea if this parameters are right + * + */ +static int usbvision_set_input(struct usb_usbvision *usbvision) +{ + static const char proc[] = "usbvision_set_input: "; + int rc; + unsigned char value[8]; + unsigned char dvi_yuv_value; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + /* Set input format expected from decoder*/ + if (usbvision_device_data[usbvision->DevModel].Vin_Reg1 >= 0) { + value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1 & 0xff; + } else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) { + /* SAA7113 uses 8 bit output */ + value[0] = USBVISION_8_422_SYNC; + } else { + /* I'm sure only about d2-d0 [010] 16 bit 4:2:2 usin sync pulses + * as that is how saa7111 is configured */ + value[0] = USBVISION_16_422_SYNC; + /* | USBVISION_VSNC_POL | USBVISION_VCLK_POL);*/ + } + + rc = usbvision_write_reg(usbvision, USBVISION_VIN_REG1, value[0]); + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " + "reconnect or reload driver.\n", proc, rc); + return rc; + } + + + if (usbvision->input.std & V4L2_STD_PAL) { + value[0] = 0xC0; + value[1] = 0x02; //0x02C0 -> 704 Input video line length + value[2] = 0x20; + value[3] = 0x01; //0x0120 -> 288 Input video n. of lines + value[4] = 0x60; + value[5] = 0x00; //0x0060 -> 96 Input video h offset + value[6] = 0x16; + value[7] = 0x00; //0x0016 -> 22 Input video v offset + } else if (usbvision->input.std & V4L2_STD_SECAM) { + value[0] = 0xC0; + value[1] = 0x02; //0x02C0 -> 704 Input video line length + value[2] = 0x20; + value[3] = 0x01; //0x0120 -> 288 Input video n. of lines + value[4] = 0x01; + value[5] = 0x00; //0x0001 -> 01 Input video h offset + value[6] = 0x01; + value[7] = 0x00; //0x0001 -> 01 Input video v offset + } else { /* V4L2_STD_NTSC */ + value[0] = 0xD0; + value[1] = 0x02; //0x02D0 -> 720 Input video line length + value[2] = 0xF0; + value[3] = 0x00; //0x00F0 -> 240 Input video number of lines + value[4] = 0x50; + value[5] = 0x00; //0x0050 -> 80 Input video h offset + value[6] = 0x10; + value[7] = 0x00; //0x0010 -> 16 Input video v offset + } + + if (usbvision_device_data[usbvision->DevModel].X_Offset >= 0) { + value[4]=usbvision_device_data[usbvision->DevModel].X_Offset & 0xff; + value[5]=(usbvision_device_data[usbvision->DevModel].X_Offset & 0x0300) >> 8; + } + + if (usbvision_device_data[usbvision->DevModel].Y_Offset >= 0) { + value[6]=usbvision_device_data[usbvision->DevModel].Y_Offset & 0xff; + value[7]=(usbvision_device_data[usbvision->DevModel].Y_Offset & 0x0300) >> 8; + } + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, /* USBVISION specific code */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_LXSIZE_I, value, 8, HZ); + if (rc < 0) { + printk(KERN_ERR "%sERROR=%d. USBVISION stopped - " + "reconnect or reload driver.\n", proc, rc); + return rc; + } + + + dvi_yuv_value = 0x00; /* U comes after V, Ya comes after U/V, Yb comes after Yb */ + + if(usbvision_device_data[usbvision->DevModel].Dvi_yuv >= 0){ + dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv & 0xff; + } + else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) { + /* This changes as the fine sync control changes. Further investigation necessary */ + dvi_yuv_value = 0x06; + } + + return (usbvision_write_reg(usbvision, USBVISION_DVI_YUV, dvi_yuv_value)); +} + + +/* + * usbvision_set_dram_settings() + * + * Set the buffer address needed by the usbvision dram to operate + * This values has been taken with usbsnoop. + * + */ + +static int usbvision_set_dram_settings(struct usb_usbvision *usbvision) +{ + int rc; + unsigned char value[8]; + + if (usbvision->isocMode == ISOC_MODE_COMPRESS) { + value[0] = 0x42; + value[1] = 0x71; + value[2] = 0xff; + value[3] = 0x00; + value[4] = 0x98; + value[5] = 0xe0; + value[6] = 0x71; + value[7] = 0xff; + // UR: 0x0E200-0x3FFFF = 204288 Words (1 Word = 2 Byte) + // FDL: 0x00000-0x0E099 = 57498 Words + // VDW: 0x0E3FF-0x3FFFF + } + else { + value[0] = 0x42; + value[1] = 0x00; + value[2] = 0xff; + value[3] = 0x00; + value[4] = 0x00; + value[5] = 0x00; + value[6] = 0x00; + value[7] = 0xff; + } + /* These are the values of the address of the video buffer, + * they have to be loaded into the USBVISION_DRM_PRM1-8 + * + * Start address of video output buffer for read: drm_prm1-2 -> 0x00000 + * End address of video output buffer for read: drm_prm1-3 -> 0x1ffff + * Start address of video frame delay buffer: drm_prm1-4 -> 0x20000 + * Only used in compressed mode + * End address of video frame delay buffer: drm_prm1-5-6 -> 0x3ffff + * Only used in compressed mode + * Start address of video output buffer for write: drm_prm1-7 -> 0x00000 + * End address of video output buffer for write: drm_prm1-8 -> 0x1ffff + */ + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return 0; + + rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, /* USBVISION specific code */ + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_DRM_PRM1, value, 8, HZ); + + if (rc < 0) { + err("%sERROR=%d", __FUNCTION__, rc); + return rc; + } + + /* Restart the video buffer logic */ + if ((rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, USBVISION_RES_UR | + USBVISION_RES_FDL | USBVISION_RES_VDW)) < 0) + return rc; + rc = usbvision_write_reg(usbvision, USBVISION_DRM_CONT, 0x00); + + return rc; +} + +/* + * () + * + * Power on the device, enables suspend-resume logic + * & reset the isoc End-Point + * + */ + +static int usbvision_power_on(struct usb_usbvision *usbvision) +{ + int errCode = 0; + + PDEBUG(DBG_FUNC, ""); + + usbvision_write_reg(usbvision, USBVISION_PWR_REG, USBVISION_SSPND_EN); + usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_RES2); + usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID); + errCode = usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID | USBVISION_RES2); + if (errCode == 1) { + usbvision->power = 1; + } + PDEBUG(DBG_FUNC, "%s: errCode %d", (errCode<0)?"ERROR":"power is on", errCode); + return errCode; +} + + +static void usbvision_powerOffTimer(unsigned long data) +{ + struct usb_usbvision *usbvision = (void *) data; + + PDEBUG(DBG_FUNC, ""); + del_timer(&usbvision->powerOffTimer); + INIT_WORK(&usbvision->powerOffWork, call_usbvision_power_off, usbvision); + (void) schedule_work(&usbvision->powerOffWork); + +} + + +/* + * usbvision_begin_streaming() + * Sure you have to put bit 7 to 0, if not incoming frames are droped, but no + * idea about the rest + */ +static int usbvision_begin_streaming(struct usb_usbvision *usbvision) +{ + int errCode = 0; + + if (usbvision->isocMode == ISOC_MODE_COMPRESS) { + usbvision_init_compression(usbvision); + } + errCode = usbvision_write_reg(usbvision, USBVISION_VIN_REG2, USBVISION_NOHVALID | + usbvision->Vin_Reg2_Preset); + return errCode; +} + +/* + * usbvision_restart_isoc() + * Not sure yet if touching here PWR_REG make loose the config + */ + +static int usbvision_restart_isoc(struct usb_usbvision *usbvision) +{ + int ret; + + if ( + (ret = + usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID)) < 0) + return ret; + if ( + (ret = + usbvision_write_reg(usbvision, USBVISION_PWR_REG, + USBVISION_SSPND_EN | USBVISION_PWR_VID | + USBVISION_RES2)) < 0) + return ret; + if ( + (ret = + usbvision_write_reg(usbvision, USBVISION_VIN_REG2, + USBVISION_KEEP_BLANK | USBVISION_NOHVALID | + usbvision->Vin_Reg2_Preset)) < 0) return ret; + + /* TODO: schedule timeout */ + while ((usbvision_read_reg(usbvision, USBVISION_STATUS_REG) && 0x01) != 1); + + return 0; +} + +static int usbvision_audio_on(struct usb_usbvision *usbvision) +{ + if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, usbvision->AudioChannel) < 0) { + printk(KERN_ERR "usbvision_audio_on: can't wirte reg\n"); + return -1; + } + DEBUG(printk(KERN_DEBUG "usbvision_audio_on: channel %d\n", usbvision->AudioChannel)); + usbvision->AudioMute = 0; + return 0; +} + +static int usbvision_audio_mute(struct usb_usbvision *usbvision) +{ + if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, 0x03) < 0) { + printk(KERN_ERR "usbvision_audio_mute: can't wirte reg\n"); + return -1; + } + DEBUG(printk(KERN_DEBUG "usbvision_audio_mute: audio mute\n")); + usbvision->AudioMute = 1; + return 0; +} + +static int usbvision_audio_off(struct usb_usbvision *usbvision) +{ + if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_AUDIO_MUTE) < 0) { + printk(KERN_ERR "usbvision_audio_off: can't wirte reg\n"); + return -1; + } + DEBUG(printk(KERN_DEBUG "usbvision_audio_off: audio off\n")); + usbvision->AudioMute = 0; + usbvision->AudioChannel = USBVISION_AUDIO_MUTE; + return 0; +} + +static int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel) +{ + if (!usbvision->AudioMute) { + if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, AudioChannel) < 0) { + printk(KERN_ERR "usbvision_set_audio: can't write iopin register for audio switching\n"); + return -1; + } + } + DEBUG(printk(KERN_DEBUG "usbvision_set_audio: channel %d\n", AudioChannel)); + usbvision->AudioChannel = AudioChannel; + return 0; +} + +static int usbvision_setup(struct usb_usbvision *usbvision) +{ + usbvision_set_video_format(usbvision, isocMode); + usbvision_set_dram_settings(usbvision); + usbvision_set_compress_params(usbvision); + usbvision_set_input(usbvision); + usbvision_set_output(usbvision, MAX_USB_WIDTH, MAX_USB_HEIGHT); + usbvision_restart_isoc(usbvision); + + /* cosas del PCM */ + return USBVISION_IS_OPERATIONAL(usbvision); +} + + +/* + * usbvision_init_isoc() + * + */ +static int usbvision_init_isoc(struct usb_usbvision *usbvision) +{ + struct usb_device *dev = usbvision->dev; + int bufIdx, errCode, regValue; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EFAULT; + + usbvision->curFrameNum = -1; + scratch_reset(usbvision); + + /* Alternate interface 1 is is the biggest frame size */ + errCode = usb_set_interface(dev, usbvision->iface, usbvision->ifaceAltActive); + if (errCode < 0) { + usbvision->last_error = errCode; + return -EBUSY; + } + + regValue = (16 - usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F; + usbvision->isocPacketSize = (regValue == 0) ? 0 : (regValue * 64) - 1; + PDEBUG(DBG_ISOC, "ISO Packet Length:%d", usbvision->isocPacketSize); + + usbvision->usb_bandwidth = regValue >> 1; + PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", usbvision->usb_bandwidth); + + + + /* We double buffer the Iso lists */ + + for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) { + int j, k; + struct urb *urb; + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + urb = usb_alloc_urb(USBVISION_URB_FRAMES); + #else + urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); + #endif + if (urb == NULL) { + err("%s: usb_alloc_urb() failed", __FUNCTION__); + return -ENOMEM; + } + usbvision->sbuf[bufIdx].urb = urb; + urb->dev = dev; + urb->context = usbvision; + urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp); + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + urb->transfer_flags = USB_ISO_ASAP; + #else + urb->transfer_flags = URB_ISO_ASAP; + urb->interval = 1; + #endif + urb->transfer_buffer = usbvision->sbuf[bufIdx].data; + urb->complete = usbvision_isocIrq; + urb->number_of_packets = USBVISION_URB_FRAMES; + urb->transfer_buffer_length = + usbvision->isocPacketSize * USBVISION_URB_FRAMES; + for (j = k = 0; j < USBVISION_URB_FRAMES; j++, + k += usbvision->isocPacketSize) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = usbvision->isocPacketSize; + } + } + + + /* Submit all URBs */ + for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) { + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb); + #else + errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb, GFP_KERNEL); + #endif + if (errCode) { + err("%s: usb_submit_urb(%d) failed: error %d", __FUNCTION__, bufIdx, errCode); + } + } + + usbvision->streaming = 1; + PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x", __FUNCTION__, usbvision->video_endp); + return 0; +} + +/* + * usbvision_stop_isoc() + * + * This procedure stops streaming and deallocates URBs. Then it + * activates zero-bandwidth alt. setting of the video interface. + * + */ +static void usbvision_stop_isoc(struct usb_usbvision *usbvision) +{ + int bufIdx, errCode, regValue; + + if (!usbvision->streaming || (usbvision->dev == NULL)) + return; + + /* Unschedule all of the iso td's */ + for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) { + usb_kill_urb(usbvision->sbuf[bufIdx].urb); + usb_free_urb(usbvision->sbuf[bufIdx].urb); + usbvision->sbuf[bufIdx].urb = NULL; + } + + + PDEBUG(DBG_ISOC, "%s: streaming=0\n", __FUNCTION__); + usbvision->streaming = 0; + + + if (!usbvision->remove_pending) { + + /* Set packet size to 0 */ + errCode = usb_set_interface(usbvision->dev, usbvision->iface, + usbvision->ifaceAltInactive); + if (errCode < 0) { + err("%s: usb_set_interface() failed: error %d", __FUNCTION__, errCode); + usbvision->last_error = errCode; + } + regValue = (16 - usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F; + usbvision->isocPacketSize = (regValue == 0) ? 0 : (regValue * 64) - 1; + PDEBUG(DBG_ISOC, "ISO Packet Length:%d", usbvision->isocPacketSize); + + usbvision->usb_bandwidth = regValue >> 1; + PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", usbvision->usb_bandwidth); + } +} + +/* + * usbvision_new_frame() + * + */ +static int usbvision_new_frame(struct usb_usbvision *usbvision, int framenum) +{ + struct usbvision_frame *frame; + int n; //byhec , width, height; + + /* If we're not grabbing a frame right now and the other frame is */ + /* ready to be grabbed into, then use it instead */ + if (usbvision->curFrameNum != -1) + return 0; + + n = (framenum - 1 + USBVISION_NUMFRAMES) % USBVISION_NUMFRAMES; + if (usbvision->frame[n].grabstate == FrameState_Ready) + framenum = n; + + frame = &usbvision->frame[framenum]; + + frame->grabstate = FrameState_Grabbing; + frame->scanstate = ScanState_Scanning; + frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ + usbvision->curFrameNum = framenum; + + /* + * Normally we would want to copy previous frame into the current one + * before we even start filling it with data; this allows us to stop + * filling at any moment; top portion of the frame will be new and + * bottom portion will stay as it was in previous frame. If we don't + * do that then missing chunks of video stream will result in flickering + * portions of old data whatever it was before. + * + * If we choose not to copy previous frame (to, for example, save few + * bus cycles - the frame can be pretty large!) then we have an option + * to clear the frame before using. If we experience losses in this + * mode then missing picture will be black (flickering). + * + * Finally, if user chooses not to clean the current frame before + * filling it with data then the old data will be visible if we fail + * to refill entire frame with new data. + */ + if (!(flags & FLAGS_SEPARATE_FRAMES)) { + /* This copies previous frame into this one to mask losses */ + memmove(frame->data, usbvision->frame[1 - framenum].data, + MAX_FRAME_SIZE); + } else { + if (flags & FLAGS_CLEAN_FRAMES) { + /*This provides a "clean" frame but slows things down */ + memset(frame->data, 0, MAX_FRAME_SIZE); + } + } + return 0; +} + +static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel, int norm) +{ + int mode[4]; + int audio[]= {1, 0, 0, 0}; + struct v4l2_routing route; + //channel 0 is TV with audiochannel 1 (tuner mono) + //channel 1 is Composite with audio channel 0 (line in) + //channel 2 is S-Video with audio channel 0 (line in) + //channel 3 is additional video inputs to the device with audio channel 0 (line in) + + RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs); + /* set the new video norm */ + if (usbvision->input.std != norm) { + v4l2_std_id video_command = norm; + + route.input = SAA7115_COMPOSITE1; + call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route); + call_i2c_clients(usbvision, VIDIOC_S_STD, &video_command); + usbvision->input.std = norm; + call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->input.index); //set norm in tuner + } + + // set the new channel + // Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video + // Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red + + switch (usbvision_device_data[usbvision->DevModel].Codec) { + case CODEC_SAA7113: + if (SwitchSVideoInput) { // To handle problems with S-Video Input for some devices. Use SwitchSVideoInput parameter when loading the module. + mode[2] = 1; + } + else { + mode[2] = 7; + } + if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { + mode[0] = 0; mode[1] = 2; mode[3] = 3; // Special for four input devices + } + else { + mode[0] = 0; mode[1] = 2; //modes for regular saa7113 devices + } + break; + case CODEC_SAA7111: + mode[0] = 0; mode[1] = 1; mode[2] = 7; //modes for saa7111 + break; + default: + mode[0] = 0; mode[1] = 1; mode[2] = 7; //default modes + } + route.input = mode[channel]; + call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route); + usbvision->channel = channel; + usbvision_set_audio(usbvision, audio[channel]); + return 0; +} + + +/* + * usbvision_open() + * + * This is part of Video 4 Linux API. The driver can be opened by one + * client only (checks internal counter 'usbvision->user'). The procedure + * then allocates buffers needed for video processing. + * + */ +static int usbvision_v4l2_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + const int sb_size = USBVISION_URB_FRAMES * USBVISION_MAX_ISOC_PACKET_SIZE; + int i, errCode = 0; + + PDEBUG(DBG_IO, "open"); + + + if (timer_pending(&usbvision->powerOffTimer)) { + del_timer(&usbvision->powerOffTimer); + } + + if (usbvision->user) + errCode = -EBUSY; + else { + /* Clean pointers so we know if we allocated something */ + for (i = 0; i < USBVISION_NUMSBUF; i++) + usbvision->sbuf[i].data = NULL; + + /* Allocate memory for the frame buffers */ + usbvision->max_frame_size = MAX_FRAME_SIZE; + usbvision->fbuf_size = USBVISION_NUMFRAMES * usbvision->max_frame_size; + usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size); + usbvision->scratch = vmalloc(scratch_buf_size); + scratch_reset(usbvision); + if ((usbvision->fbuf == NULL) || (usbvision->scratch == NULL)) { + err("%s: unable to allocate %d bytes for fbuf and %d bytes for scratch", + __FUNCTION__, usbvision->fbuf_size, scratch_buf_size); + errCode = -ENOMEM; + } + else { + /* Allocate all buffers */ + for (i = 0; i < USBVISION_NUMFRAMES; i++) { + usbvision->frame[i].grabstate = FrameState_Unused; + usbvision->frame[i].data = usbvision->fbuf + + i * MAX_FRAME_SIZE; + /* + * Set default sizes in case IOCTL + * (VIDIOCMCAPTURE) + * is not used (using read() instead). + */ + usbvision->stretch_width = 1; + usbvision->stretch_height = 1; + usbvision->frame[i].width = usbvision->curwidth; + usbvision->frame[i].height = usbvision->curheight; + usbvision->frame[i].bytes_read = 0; + } + if (dga) { //set default for DGA + usbvision->overlay_frame.grabstate = FrameState_Unused; + usbvision->overlay_frame.scanstate = ScanState_Scanning; + usbvision->overlay_frame.data = NULL; + usbvision->overlay_frame.width = usbvision->curwidth; + usbvision->overlay_frame.height = usbvision->curheight; + usbvision->overlay_frame.bytes_read = 0; + } + for (i = 0; i < USBVISION_NUMSBUF; i++) { + usbvision->sbuf[i].data = kzalloc(sb_size, GFP_KERNEL); + if (usbvision->sbuf[i].data == NULL) { + err("%s: unable to allocate %d bytes for sbuf", __FUNCTION__, sb_size); + errCode = -ENOMEM; + break; + } + } + } + if ((!errCode) && (usbvision->isocMode==ISOC_MODE_COMPRESS)) { + int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2; + usbvision->IntraFrameBuffer = vmalloc(IFB_size); + if (usbvision->IntraFrameBuffer == NULL) { + err("%s: unable to allocate %d for compr. frame buffer", __FUNCTION__, IFB_size); + errCode = -ENOMEM; + } + + } + if (errCode) { + /* Have to free all that memory */ + if (usbvision->fbuf != NULL) { + usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size); + usbvision->fbuf = NULL; + } + if (usbvision->scratch != NULL) { + vfree(usbvision->scratch); + usbvision->scratch = NULL; + } + for (i = 0; i < USBVISION_NUMSBUF; i++) { + if (usbvision->sbuf[i].data != NULL) { + kfree(usbvision->sbuf[i].data); + usbvision->sbuf[i].data = NULL; + } + } + if (usbvision->IntraFrameBuffer != NULL) { + vfree(usbvision->IntraFrameBuffer); + usbvision->IntraFrameBuffer = NULL; + } + } + } + + /* If so far no errors then we shall start the camera */ + if (!errCode) { + down(&usbvision->lock); + if (usbvision->power == 0) { + usbvision_power_on(usbvision); + usbvision_init_i2c(usbvision); + } + + /* Send init sequence only once, it's large! */ + if (!usbvision->initialized) { + int setup_ok = 0; + setup_ok = usbvision_setup(usbvision); + if (setup_ok) + usbvision->initialized = 1; + else + errCode = -EBUSY; + } + + if (!errCode) { + usbvision_begin_streaming(usbvision); + errCode = usbvision_init_isoc(usbvision); + usbvision->user++; + } + else { + if (PowerOnAtOpen) { + usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_power_off(usbvision); + usbvision->initialized = 0; + } + } + up(&usbvision->lock); + } + + if (errCode) { + } + + PDEBUG(DBG_IO, "success"); + return errCode; +} + +/* + * usbvision_v4l2_close() + * + * This is part of Video 4 Linux API. The procedure + * stops streaming and deallocates all buffers that were earlier + * allocated in usbvision_v4l2_open(). + * + */ +static int usbvision_v4l2_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + int i; + + PDEBUG(DBG_IO, "close"); + down(&usbvision->lock); + + usbvision_audio_off(usbvision); + usbvision_restart_isoc(usbvision); + usbvision_stop_isoc(usbvision); + + if (usbvision->IntraFrameBuffer != NULL) { + vfree(usbvision->IntraFrameBuffer); + usbvision->IntraFrameBuffer = NULL; + } + + usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size); + vfree(usbvision->scratch); + for (i = 0; i < USBVISION_NUMSBUF; i++) + kfree(usbvision->sbuf[i].data); + + usbvision->user--; + + if (PowerOnAtOpen) { + mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME); + usbvision->initialized = 0; + } + + up(&usbvision->lock); + + if (usbvision->remove_pending) { + info("%s: Final disconnect", __FUNCTION__); + usbvision_release(usbvision); + } + + PDEBUG(DBG_IO, "success"); + + + return 0; +} + + +/* + * usbvision_ioctl() + * + * This is part of Video 4 Linux API. The procedure handles ioctl() calls. + * + */ +static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EFAULT; + + switch (cmd) { + case UVIOCSREG: + { + struct usbvision_reg *usbvision_reg = arg; + int errCode; + + errCode = usbvision_write_reg(usbvision, usbvision_reg->addr, usbvision_reg->value); + + if (errCode < 0) { + err("%s: UVIOCSREG failed: error %d", __FUNCTION__, errCode); + } + else { + PDEBUG(DBG_IOCTL, "UVIOCSREG addr=0x%02X, value=0x%02X", + usbvision_reg->addr, usbvision_reg->value); + errCode = 0; + } + return errCode; + } + case UVIOCGREG: + { + struct usbvision_reg *usbvision_reg = arg; + int errCode; + + errCode = usbvision_read_reg(usbvision, usbvision_reg->addr); + + if (errCode < 0) { + err("%s: UVIOCGREG failed: error %d", __FUNCTION__, errCode); + } + else { + usbvision_reg->value=(unsigned char)errCode; + PDEBUG(DBG_IOCTL, "UVIOCGREG addr=0x%02X, value=0x%02X", + usbvision_reg->addr, usbvision_reg->value); + errCode = 0; // No error + } + return errCode; + } + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *vc=arg; + *vc = usbvision->vcap; + PDEBUG(DBG_IOCTL, "VIDIOC_QUERYCAP"); + return 0; + } + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *vi = arg; + int chan; + + if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) ) + return -EINVAL; + if (usbvision->have_tuner) { + chan = vi->index; + } + else { + chan = vi->index + 1; //skip Television string + } + switch(chan) { + case 0: + if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { + strcpy(vi->name, "White Video Input"); + } + else { + strcpy(vi->name, "Television"); + vi->type = V4L2_INPUT_TYPE_TUNER; + vi->audioset = 1; + vi->tuner = chan; + vi->std = V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM; + } + break; + case 1: + vi->type = V4L2_INPUT_TYPE_CAMERA; + if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { + strcpy(vi->name, "Green Video Input"); + } + else { + strcpy(vi->name, "Composite Video Input"); + } + vi->std = V4L2_STD_PAL; + break; + case 2: + vi->type = V4L2_INPUT_TYPE_CAMERA; + if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { + strcpy(vi->name, "Yellow Video Input"); + } + else { + strcpy(vi->name, "S-Video Input"); + } + vi->std = V4L2_STD_PAL; + break; + case 3: + vi->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(vi->name, "Red Video Input"); + vi->std = V4L2_STD_PAL; + break; + } + PDEBUG(DBG_IOCTL, "VIDIOC_ENUMINPUT name=%s:%d tuners=%d type=%d norm=%x", vi->name, vi->index, vi->tuner,vi->type,(int)vi->std); + return 0; + } + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *vs = arg; + switch(vs->index) { + case 0: + vs->id = V4L2_STD_PAL; + strcpy(vs->name,"PAL"); + vs->frameperiod.numerator = 1; + vs->frameperiod.denominator = 25; + vs->framelines = 625; + break; + case 1: + vs->id = V4L2_STD_NTSC; + strcpy(vs->name,"NTSC"); + vs->frameperiod.numerator = 1001; + vs->frameperiod.denominator = 30000; + vs->framelines = 525; + break; + case 2: + vs->id = V4L2_STD_SECAM; + strcpy(vs->name,"SECAM"); + vs->frameperiod.numerator = 1; + vs->frameperiod.denominator = 25; + vs->framelines = 625; + break; + default: + return -EINVAL; + } + return 0; + } + case VIDIOC_G_INPUT: + { + int *input = arg; + *input = usbvision->input.index; + return 0; + } + case VIDIOC_S_INPUT: + { + int *input = arg; + if ((*input >= usbvision->video_inputs) || (*input < 0) ) + return -EINVAL; + usbvision->input.index = *input; + + down(&usbvision->lock); + usbvision_muxsel(usbvision, usbvision->input.index, usbvision->input.std); + usbvision_set_input(usbvision); + usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight); + up(&usbvision->lock); + return 0; + } + case VIDIOC_G_STD: + { + v4l2_std_id *std = arg; + *std = usbvision->input.std; + PDEBUG(DBG_IOCTL, "VIDIOC_G_STD std_id=%x", (unsigned)*std); + return 0; + } + case VIDIOC_S_STD: + { + v4l2_std_id *std = arg; + + down(&usbvision->lock); + usbvision_muxsel(usbvision, usbvision->input.index, *std); + usbvision_set_input(usbvision); + usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight); + up(&usbvision->lock); + + usbvision->input.std = *std; + PDEBUG(DBG_IOCTL, "VIDIOC_S_STD std_id=%x", (unsigned)*std); + return 0; + } + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *vt = arg; + struct v4l2_tuner status; + + if (!usbvision->have_tuner || vt->index) // Only tuner 0 + return -EINVAL; + strcpy(vt->name, "Television"); + vt->type = V4L2_TUNER_ANALOG_TV; + vt->capability = V4L2_TUNER_CAP_NORM; + vt->rangelow = 0; + vt->rangehigh = ~0; + vt->audmode = V4L2_TUNER_MODE_MONO; + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + call_i2c_clients(usbvision,VIDIOC_G_TUNER,&status); + vt->signal = status.signal; + + PDEBUG(DBG_IOCTL, "VIDIOC_G_TUNER"); + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *vt = arg; + + // Only no or one tuner for now + if (!usbvision->have_tuner || vt->index) + return -EINVAL; + // FIXME vt->audmode Radio mode (STEREO/MONO/...) + // vt->reserved Radio freq + // usbvision_muxsel(usbvision, vt->index, vt->mode); + PDEBUG(DBG_IOCTL, "VIDIOC_S_TUNER"); + return 0; + } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *freq = arg; + freq->tuner = 0; // Only one tuner + freq->type = V4L2_TUNER_ANALOG_TV; + freq->frequency = usbvision->freq; + PDEBUG(DBG_IOCTL, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)freq->frequency); + return 0; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *freq = arg; + usbvision->freq = freq->frequency; + call_i2c_clients(usbvision, cmd, freq); + PDEBUG(DBG_IOCTL, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)freq->frequency); + return 0; + } + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *v = arg; + memset(v,0, sizeof(v)); + strcpy(v->name, "TV"); + PDEBUG(DBG_IOCTL, "VIDIOC_G_AUDIO"); + // FIXME: no more processings ??? + return 0; + } + case VIDIOC_S_AUDIO: + { + struct v4l2_audio *v = arg; + if(v->index) { + return -EINVAL; + } + PDEBUG(DBG_IOCTL, "VIDIOC_S_AUDIO"); + // FIXME: void function ??? + return 0; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + switch(qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill_std(qc); + break; + default: + return -EINVAL; + } + return 0; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = usbvision->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = usbvision->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = usbvision->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = usbvision->hue; + break; + case V4L2_CID_AUDIO_VOLUME: + /* ctrl->value = usbvision->volume; */ + break; + case V4L2_CID_AUDIO_MUTE: + ctrl->value = usbvision->AudioMute; + break; + default: + return -EINVAL; + } + PDEBUG(DBG_IOCTL, "VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); + return 0; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + PDEBUG(DBG_IOCTL, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value); + call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); + return 0; + } + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *vr = arg; + // FIXME : normally we allocate the requested number of buffers. + // this driver allocates statically the buffers. + vr->count = 2; + if(vr->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + return 0; + } + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *vb = arg; + struct usbvision_frame *frame; + + // FIXME : works only on VIDEO_CAPTURE MODE, MMAP. + if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { + return -EINVAL; + } + if(vb->index>1) { + return -EINVAL; + } + vb->flags = 0; + frame = &usbvision->frame[vb->index]; + if(frame->grabstate == FrameState_Grabbing) + vb->flags |= V4L2_BUF_FLAG_QUEUED; + if(frame->grabstate >= FrameState_Done) + vb->flags |= V4L2_BUF_FLAG_DONE; + if(frame->grabstate == FrameState_Unused) + vb->flags |= V4L2_BUF_FLAG_MAPPED; + vb->memory = V4L2_MEMORY_MMAP; + if(vb->index == 0) { + vb->m.offset = 0; + } + else { + vb->m.offset = MAX_FRAME_SIZE; + } + vb->length = MAX_FRAME_SIZE; + vb->timestamp = usbvision->frame[vb->index].timestamp; + vb->sequence = usbvision->frame_num; + return 0; + } + case VIDIOC_QBUF: // VIDIOCMCAPTURE + VIDIOCSYNC + { + struct v4l2_buffer *vb = arg; + struct usbvision_frame *frame; + + // FIXME : works only on VIDEO_CAPTURE MODE, MMAP. + if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { + return -EINVAL; + } + if(vb->index>1) { + return -EINVAL; + } + + frame = &usbvision->frame[vb->index]; + + if (frame->grabstate == FrameState_Grabbing) { + return -EBUSY; + } + + usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight); + + /* Mark it as ready */ + frame->grabstate = FrameState_Ready; + vb->flags &= ~V4L2_BUF_FLAG_DONE; + + /* set v4l2_format index */ + frame->v4l2_format = usbvision->palette; + + return usbvision_new_frame(usbvision, vb->index); + } + case VIDIOC_DQBUF: + { + struct v4l2_buffer *vb = arg; + int errCode = 0; + + DECLARE_WAITQUEUE(wait, current); + // FIXME : not the proper way to get the last filled frame + vb->index=-1; + if(usbvision->curFrameNum != -1) vb->index=usbvision->curFrameNum; + else { + if(usbvision->frame[1].grabstate >= FrameState_Done) + vb->index = 1; + else if(usbvision->frame[0].grabstate >= FrameState_Done) + vb->index = 0; + // If no FRAME_DONE, look for a FRAME_GRABBING state. + // See if a frame is in process (grabbing), then use it. + if (vb->index == -1) { + if (usbvision->frame[1].grabstate == FrameState_Grabbing) + vb->index = 1; + else if (usbvision->frame[0].grabstate == FrameState_Grabbing) + vb->index = 0; + } + } + if (vb->index == -1) + return -EINVAL; + + PDEBUG(DBG_IOCTL, "VIDIOC_DQBUF frame=%d, grabstate=%d", + vb->index, usbvision->frame[vb->index].grabstate); + + switch (usbvision->frame[vb->index].grabstate) { + case FrameState_Unused: + errCode = -EINVAL; + break; + case FrameState_Grabbing: + add_wait_queue(&usbvision->frame[vb->index].wq, &wait); + current->state = TASK_INTERRUPTIBLE; + while (usbvision->frame[vb->index].grabstate == FrameState_Grabbing) { + schedule(); + if (signal_pending(current)) { + remove_wait_queue(&usbvision->frame[vb->index].wq, &wait); + current->state = TASK_RUNNING; + return -EINTR; + } + } + remove_wait_queue(&usbvision->frame[vb->index].wq, &wait); + current->state = TASK_RUNNING; + case FrameState_Ready: + case FrameState_Error: + case FrameState_Done: + errCode = (usbvision->frame[vb->index].grabstate == FrameState_Error) ? -EIO : 0; + vb->memory = V4L2_MEMORY_MMAP; + vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE; + vb->sequence = usbvision->frame[vb->index].sequence; + usbvision->frame[vb->index].grabstate = FrameState_Unused; + break; + } + + usbvision->frame[vb->index].grabstate = FrameState_Unused; + return errCode; + } + case VIDIOC_STREAMON: + { + int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; + call_i2c_clients(usbvision,VIDIOC_STREAMON , &b); + return 0; + } + case VIDIOC_STREAMOFF: + { + int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; + down(&usbvision->lock); + // Stop all video streamings + call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b); + usbvision->frame_num = -1; + usbvision->frame[0].grabstate = FrameState_Unused; + usbvision->frame[1].grabstate = FrameState_Unused; + up(&usbvision->lock); + return 0; + } + case VIDIOC_G_FBUF: + { + struct v4l2_framebuffer *vb = arg; + + if (dga) { + *vb = usbvision->vid_buf; + } + else { + memset(vb, 0, sizeof(vb)); //dga not supported, not used + } + PDEBUG(DBG_IOCTL, "VIDIOC_G_FBUF base=%p, width=%d, height=%d, pixelformat=%d, bpl=%d", + vb->base, vb->fmt.width, vb->fmt.height, vb->fmt.pixelformat,vb->fmt.bytesperline); + return 0; + } + case VIDIOC_S_FBUF: + { + struct v4l2_framebuffer *vb = arg; + int formatIdx; + + if (dga == 0) { + return -EINVAL; + } + + if(!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_ADMIN)) { + return -EPERM; + } + + PDEBUG(DBG_IOCTL, "VIDIOC_S_FBUF base=%p, width=%d, height=%d, pixelformat=%d, bpl=%d", + vb->base, vb->fmt.width, vb->fmt.height, vb->fmt.pixelformat,vb->fmt.bytesperline); + + for (formatIdx=0; formatIdx <= USBVISION_SUPPORTED_PALETTES; formatIdx++) { + if (formatIdx == USBVISION_SUPPORTED_PALETTES) { + return -EINVAL; // no matching video_format + } + if ((vb->fmt.pixelformat == usbvision_v4l2_format[formatIdx].format) && + (usbvision_v4l2_format[formatIdx].supported)) { + break; //found matching video_format + } + } + + if (vb->fmt.bytesperline<1) { + return -EINVAL; + } + if (usbvision->overlay) { + return -EBUSY; + } + down(&usbvision->lock); + if (usbvision->overlay_base) { + iounmap(usbvision->overlay_base); + usbvision->vid_buf_valid = 0; + } + usbvision->overlay_base = ioremap((ulong)vb->base, vb->fmt.height * vb->fmt.bytesperline); + if (usbvision->overlay_base) { + usbvision->vid_buf_valid = 1; + } + usbvision->vid_buf = *vb; + usbvision->overlay_frame.v4l2_format = usbvision_v4l2_format[formatIdx]; + up(&usbvision->lock); + return 0; + } + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *vfd = arg; + if ( (dga == 0) && + (vfd->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) && + (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && + (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { + return -EINVAL; + } + if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) { + return -EINVAL; + } + vfd->flags = 0; + strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc); + vfd->pixelformat = usbvision_v4l2_format[vfd->index].format; + return 0; + } + case VIDIOC_G_FMT: + { + struct v4l2_format *vf = arg; + + if ( (dga == 0) && + (vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) && + (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && + (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { + return -EINVAL; + } + down(&usbvision->lock); + *vf = usbvision->vid_win; + up(&usbvision->lock); + PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT x=%d, y=%d, w=%d, h=%d, chroma=%x, clips=%d", + vf->fmt.win.w.left, vf->fmt.win.w.top, vf->fmt.win.w.width, vf->fmt.win.w.height, vf->fmt.win.chromakey, vf->fmt.win.clipcount); + return 0; + } + case VIDIOC_S_FMT: + { + struct v4l2_format *vf = arg; + struct v4l2_clip *vc=NULL; + int on,formatIdx; + + if ( (dga == 0) && + (vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) && + (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && + (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { + return -EINVAL; + } + if(vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) { + if (vf->fmt.win.clipcount>256) { + return -EDOM; /* Too many clips! */ + } + // Do every clips. + vc = vmalloc(sizeof(struct v4l2_clip)*(vf->fmt.win.clipcount+4)); + if (vc == NULL) { + return -ENOMEM; + } + if (vf->fmt.win.clipcount && copy_from_user(vc,vf->fmt.win.clips,sizeof(struct v4l2_clip)*vf->fmt.win.clipcount)) { + return -EFAULT; + } + on = usbvision->overlay; // Save overlay state + if (on) { + usbvision_cap(usbvision, 0); + } + + // strange, it seems xawtv sometimes calls us with 0 + // width and/or height. Ignore these values + if (vf->fmt.win.w.left == 0) { + vf->fmt.win.w.left = usbvision->vid_win.fmt.win.w.left; + } + if (vf->fmt.win.w.top == 0) { + vf->fmt.win.w.top = usbvision->vid_win.fmt.win.w.top; + } + + // by now we are committed to the new data... + down(&usbvision->lock); + RESTRICT_TO_RANGE(vf->fmt.win.w.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); + RESTRICT_TO_RANGE(vf->fmt.win.w.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); + usbvision->vid_win = *vf; + usbvision->overlay_frame.width = vf->fmt.win.w.width; + usbvision->overlay_frame.height = vf->fmt.win.w.height; + usbvision_set_output(usbvision, vf->fmt.win.w.width, vf->fmt.win.w.height); + up(&usbvision->lock); + + // Impose display clips + if (vf->fmt.win.w.left+vf->fmt.win.w.width > (unsigned int)usbvision->vid_buf.fmt.width) { + usbvision_new_clip(vf, vc, usbvision->vid_buf.fmt.width-vf->fmt.win.w.left, 0, vf->fmt.win.w.width-1, vf->fmt.win.w.height-1); + } + if (vf->fmt.win.w.top+vf->fmt.win.w.height > (unsigned int)usbvision->vid_buf.fmt.height) { + usbvision_new_clip(vf, vc, 0, usbvision->vid_buf.fmt.height-vf->fmt.win.w.top, vf->fmt.win.w.width-1, vf->fmt.win.w.height-1); + } + + // built the requested clipping zones + usbvision_built_overlay(usbvision, vf->fmt.win.clipcount, vc); + vfree(vc); + + // restore overlay state + if (on) { + usbvision_cap(usbvision, 1); + } + usbvision->vid_win_valid = 1; + PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT overlay x=%d, y=%d, w=%d, h=%d, chroma=%x, clips=%d", + vf->fmt.win.w.left, vf->fmt.win.w.top, vf->fmt.win.w.width, vf->fmt.win.w.height, vf->fmt.win.chromakey, vf->fmt.win.clipcount); + } + else { + /* Find requested format in available ones */ + for(formatIdx=0;formatIdxfmt.pix.pixelformat == usbvision_v4l2_format[formatIdx].format) { + usbvision->palette = usbvision_v4l2_format[formatIdx]; + break; + } + } + /* robustness */ + if(formatIdx == USBVISION_SUPPORTED_PALETTES) { + return -EINVAL; + } + usbvision->vid_win.fmt.pix.pixelformat = vf->fmt.pix.pixelformat; + usbvision->vid_win.fmt.pix.width = vf->fmt.pix.width; //Image width in pixels. + usbvision->vid_win.fmt.pix.height = vf->fmt.pix.height; // Image height in pixels. + + // by now we are committed to the new data... + down(&usbvision->lock); + RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); + RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); + usbvision->vid_win = *vf; + usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height); + up(&usbvision->lock); + + usbvision->vid_win_valid = 1; + PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT grabdisplay w=%d, h=%d, ", + vf->fmt.pix.width, vf->fmt.pix.height); + } + return 0; + } + case VIDIOC_OVERLAY: + { + int *v = arg; + + if ( (dga == 0) && + (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && + (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { + PDEBUG(DBG_IOCTL, "VIDIOC_OVERLAY DGA disabled"); + return -EINVAL; + } + + if (*v == 0) { + usbvision_cap(usbvision, 0); + } + else { + // are VIDIOCSFBUF and VIDIOCSWIN done? + if ((usbvision->vid_buf_valid == 0) || (usbvision->vid_win_valid == 0)) { + PDEBUG(DBG_IOCTL, "VIDIOC_OVERLAY vid_buf_valid %d; vid_win_valid %d", + usbvision->vid_buf_valid, usbvision->vid_win_valid); + return -EINVAL; + } + usbvision_cap(usbvision, 1); + } + PDEBUG(DBG_IOCTL, "VIDIOC_OVERLAY %s", (*v)?"on":"off"); + return 0; + } + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int usbvision_v4l2_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, usbvision_v4l2_do_ioctl); +} + + +static ssize_t usbvision_v4l2_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + int noblock = file->f_flags & O_NONBLOCK; + + int frmx = -1; + int rc = 0; + struct usbvision_frame *frame; + + PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __FUNCTION__, (unsigned long)count, noblock); + + if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL)) + return -EFAULT; + + down(&usbvision->lock); + //code for testing compression + if (usbvision->isocMode == ISOC_MODE_COMPRESS) { + usbvision->frame[0].v4l2_format = usbvision_v4l2_format[0]; //V4L2_PIX_FMT_GREY; + usbvision->frame[1].v4l2_format = usbvision_v4l2_format[0]; // V4L2_PIX_FMT_GREY; + } + + // See if a frame is completed, then use it. + if (usbvision->frame[0].grabstate >= FrameState_Done) // _Done or _Error + frmx = 0; + else if (usbvision->frame[1].grabstate >= FrameState_Done)// _Done or _Error + frmx = 1; + + if (noblock && (frmx == -1)) { + count = -EAGAIN; + goto usbvision_v4l2_read_done; + } + + // If no FRAME_DONE, look for a FRAME_GRABBING state. + // See if a frame is in process (grabbing), then use it. + if (frmx == -1) { + if (usbvision->frame[0].grabstate == FrameState_Grabbing) + frmx = 0; + else if (usbvision->frame[1].grabstate == FrameState_Grabbing) + frmx = 1; + } + + // If no frame is active, start one. + if (frmx == -1) + usbvision_new_frame(usbvision, frmx = 0); + + frame = &usbvision->frame[frmx]; + + restart: + if (!USBVISION_IS_OPERATIONAL(usbvision)) { + count = -EIO; + goto usbvision_v4l2_read_done; + } + PDEBUG(DBG_IO, "Waiting frame grabbing"); + rc = wait_event_interruptible(frame->wq, (frame->grabstate == FrameState_Done) || + (frame->grabstate == FrameState_Error)); + if (rc) { + goto usbvision_v4l2_read_done; + } + + if (frame->grabstate == FrameState_Error) { + frame->bytes_read = 0; + if (usbvision_new_frame(usbvision, frmx)) { + err("%s: usbvision_new_frame() failed", __FUNCTION__); + } + goto restart; + } + + PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", __FUNCTION__, + frmx, frame->bytes_read, frame->scanlength); + + /* copy bytes to user space; we allow for partials reads */ + if ((count + frame->bytes_read) > (unsigned long)frame->scanlength) + count = frame->scanlength - frame->bytes_read; + + if (copy_to_user(buf, frame->data + frame->bytes_read, count)) { + count = -EFAULT; + goto usbvision_v4l2_read_done; + } + + frame->bytes_read += count; + PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", __FUNCTION__, + (unsigned long)count, frame->bytes_read); + + if (frame->bytes_read >= frame->scanlength) {// All data has been read + frame->bytes_read = 0; + + /* Mark it as available to be used again. */ + usbvision->frame[frmx].grabstate = FrameState_Unused; + if (usbvision_new_frame(usbvision, frmx ? 0 : 1)) + err("%s: usbvision_new_frame() failed", __FUNCTION__); + } + +usbvision_v4l2_read_done: + up(&usbvision->lock); + return count; +} + +static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + + unsigned long page, pos; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EFAULT; + + if (size > (((USBVISION_NUMFRAMES * usbvision->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + return -EINVAL; + + pos = (unsigned long) usbvision->fbuf; + while (size > 0) { + +// Really ugly.... + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) //Compatibility for 2.6.10+ kernels + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + return -EAGAIN; + } + #else //Compatibility for 2.6.0 - 2.6.9 kernels + page = usbvision_kvirt_to_pa(pos); + if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + return -EAGAIN; + } + #endif + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + + +/* + * Here comes the stuff for radio on usbvision based devices + * + */ +static int usbvision_radio_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + struct v4l2_frequency freq; + int errCode = 0; + + PDEBUG(DBG_RIO, "%s:", __FUNCTION__); + + down(&usbvision->lock); + + if (usbvision->user) { + err("%s: Someone tried to open an already opened USBVision Radio!", __FUNCTION__); + errCode = -EBUSY; + } + else { + if(PowerOnAtOpen) { + if (timer_pending(&usbvision->powerOffTimer)) { + del_timer(&usbvision->powerOffTimer); + } + if (usbvision->power == 0) { + usbvision_power_on(usbvision); + usbvision_init_i2c(usbvision); + } + } + + // If so far no errors then we shall start the radio + usbvision->radio = 1; + call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type); + freq.frequency = 1517; //SWR3 @ 94.8MHz + call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, &freq); + usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); + usbvision->user++; + } + + if (errCode) { + if (PowerOnAtOpen) { + usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_power_off(usbvision); + usbvision->initialized = 0; + } + } + up(&usbvision->lock); + return errCode; +} + + +static int usbvision_radio_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + int errCode = 0; + + PDEBUG(DBG_RIO, ""); + + down(&usbvision->lock); + + usbvision_audio_off(usbvision); + usbvision->radio=0; + usbvision->user--; + + if (PowerOnAtOpen) { + mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME); + usbvision->initialized = 0; + } + + up(&usbvision->lock); + + if (usbvision->remove_pending) { + info("%s: Final disconnect", __FUNCTION__); + usbvision_release(usbvision); + } + + + PDEBUG(DBG_RIO, "success"); + + return errCode; +} + +static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EIO; + + switch (cmd) { + /*************************** + * V4L2 IOCTLs * + ***************************/ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *vc=arg; + memset(vc, 0, sizeof(struct v4l2_capability)); + strcpy(vc->driver,"usbvision radio"); + strcpy(vc->card,usbvision->vcap.card); + strcpy(vc->bus_info,"usb"); + vc->version = USBVISION_DRIVER_VERSION; /* version */ + vc->capabilities = V4L2_CAP_TUNER; /* capabilities */ + PDEBUG(DBG_RIO, "%s: VIDIOC_QUERYCAP", __FUNCTION__); + return 0; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + switch(qc->id) + { + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill_std(qc); + break; + default: + return -EINVAL; + } + return 0; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + PDEBUG(DBG_IOCTL, "VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); + switch(ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + /* ctrl->value = usbvision->volume; */ + break; + case V4L2_CID_AUDIO_MUTE: + ctrl->value = usbvision->AudioMute; + break; + default: + return -EINVAL; + } + return 0; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); + + PDEBUG(DBG_RIO, "%s: VIDIOC_S_CTRL id=%x value=%x", __FUNCTION__,ctrl->id,ctrl->value); + return 0; + } + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *vt = arg; + + if (!usbvision->have_tuner || vt->index) // Only tuner 0 + return -EINVAL; + strcpy(vt->name, "Radio"); + vt->type = V4L2_TUNER_RADIO; + vt->capability = V4L2_TUNER_CAP_STEREO; + // japan: 76.0 MHz - 89.9 MHz + // western europe: 87.5 MHz - 108.0 MHz + // russia: 65.0 MHz - 108.0 MHz + vt->rangelow = (int)(65*16);; + vt->rangehigh = (int)(108*16); + vt->audmode = V4L2_TUNER_MODE_STEREO; + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + call_i2c_clients(usbvision,VIDIOC_G_TUNER,&vt); + + PDEBUG(DBG_RIO, "%s: VIDIOC_G_TUNER signal=%d", __FUNCTION__, vt->signal); + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *vt = arg; + + // Only channel 0 has a tuner + if((vt->index) || (usbvision->channel)) { + return -EINVAL; + } + PDEBUG(DBG_RIO, "%s: VIDIOC_S_TUNER", __FUNCTION__); + return 0; + } + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *va = arg; + memset(va,0, sizeof(va)); + va->capability = V4L2_AUDCAP_STEREO; + strcpy(va->name, "Radio"); + PDEBUG(DBG_IOCTL, "VIDIOC_G_AUDIO"); + return 0; + } + case VIDIOC_S_AUDIO: + { + struct v4l2_audio *v = arg; + if(v->index) { + return -EINVAL; + } + PDEBUG(DBG_IOCTL, "VIDIOC_S_AUDIO"); + // FIXME: void function ??? + return 0; + } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *freq = arg; + freq->tuner = 0; // Only one tuner + freq->type = V4L2_TUNER_RADIO; + freq->frequency = usbvision->freq; + PDEBUG(DBG_RIO, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)freq->frequency); + return 0; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *freq = arg; + usbvision->freq = freq->frequency; + call_i2c_clients(usbvision, cmd, freq); + PDEBUG(DBG_RIO, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)freq->frequency); + return 0; + } + + /*************************** + * V4L1 IOCTLs * + ***************************/ + case VIDIOCGCAP: + { + struct video_capability *vc = arg; + + memset(vc, 0, sizeof(struct video_capability)); + strcpy(vc->name,usbvision->vcap.card); + vc->type = VID_TYPE_TUNER; + vc->channels = 1; + vc->audios = 1; + PDEBUG(DBG_RIO, "%s: VIDIOCGCAP", __FUNCTION__); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *vt = arg; + + if((vt->tuner) || (usbvision->channel)) { /* Only tuner 0 */ + return -EINVAL; + } + strcpy(vt->name, "Radio"); + // japan: 76.0 MHz - 89.9 MHz + // western europe: 87.5 MHz - 108.0 MHz + // russia: 65.0 MHz - 108.0 MHz + vt->rangelow=(int)(65*16); + vt->rangehigh=(int)(108*16); + vt->flags= 0; + vt->mode = 0; + call_i2c_clients(usbvision,cmd,vt); + PDEBUG(DBG_RIO, "%s: VIDIOCGTUNER signal=%d", __FUNCTION__, vt->signal); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *vt = arg; + + // Only channel 0 has a tuner + if((vt->tuner) || (usbvision->channel)) { + return -EINVAL; + } + PDEBUG(DBG_RIO, "%s: VIDIOCSTUNER", __FUNCTION__); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + memset(va,0, sizeof(struct video_audio)); + call_i2c_clients(usbvision, cmd, va); + va->flags|=VIDEO_AUDIO_MUTABLE; + va->volume=1; + va->step=1; + strcpy(va->name, "Radio"); + PDEBUG(DBG_RIO, "%s: VIDIOCGAUDIO", __FUNCTION__); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + if(va->audio) { + return -EINVAL; + } + + if(va->flags & VIDEO_AUDIO_MUTE) { + if (usbvision_audio_mute(usbvision)) { + return -EFAULT; + } + } + else { + if (usbvision_audio_on(usbvision)) { + return -EFAULT; + } + } + PDEBUG(DBG_RIO, "%s: VIDIOCSAUDIO flags=0x%x)", __FUNCTION__, va->flags); + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + + *freq = usbvision->freq; + PDEBUG(DBG_RIO, "%s: VIDIOCGFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4); + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + + usbvision->freq = *freq; + call_i2c_clients(usbvision, cmd, freq); + PDEBUG(DBG_RIO, "%s: VIDIOCSFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4); + return 0; + } + default: + { + PDEBUG(DBG_RIO, "%s: Unknown command %x", __FUNCTION__, cmd); + return -ENOIOCTLCMD; + } + } + return 0; +} + + +static int usbvision_radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, usbvision_do_radio_ioctl); +} + + +/* + * Here comes the stuff for vbi on usbvision based devices + * + */ +static int usbvision_vbi_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + unsigned long freq; + int errCode = 0; + + PDEBUG(DBG_RIO, "%s:", __FUNCTION__); + + down(&usbvision->lock); + + if (usbvision->user) { + err("%s: Someone tried to open an already opened USBVision VBI!", __FUNCTION__); + errCode = -EBUSY; + } + else { + if(PowerOnAtOpen) { + if (timer_pending(&usbvision->powerOffTimer)) { + del_timer(&usbvision->powerOffTimer); + } + if (usbvision->power == 0) { + usbvision_power_on(usbvision); + usbvision_init_i2c(usbvision); + } + } + + // If so far no errors then we shall start the vbi device + //usbvision->vbi = 1; + call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type); + freq = 1517; //SWR3 @ 94.8MHz + call_i2c_clients(usbvision, VIDIOCSFREQ, &freq); + usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); + usbvision->user++; + } + + if (errCode) { + if (PowerOnAtOpen) { + usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_power_off(usbvision); + usbvision->initialized = 0; + } + } + up(&usbvision->lock); + return errCode; +} + +static int usbvision_vbi_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + int errCode = 0; + + PDEBUG(DBG_RIO, ""); + + down(&usbvision->lock); + + usbvision_audio_off(usbvision); + usbvision->vbi=0; + usbvision->user--; + + if (PowerOnAtOpen) { + mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME); + usbvision->initialized = 0; + } + + up(&usbvision->lock); + + if (usbvision->remove_pending) { + info("%s: Final disconnect", __FUNCTION__); + usbvision_release(usbvision); + } + + + PDEBUG(DBG_RIO, "success"); + + return errCode; +} + +static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EIO; + + switch (cmd) { + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *vc=arg; + memset(vc, 0, sizeof(struct v4l2_capability)); + strcpy(vc->driver,"usbvision vbi"); + strcpy(vc->card,usbvision->vcap.card); + strcpy(vc->bus_info,"usb"); + vc->version = USBVISION_DRIVER_VERSION; /* version */ + vc->capabilities = V4L2_CAP_VBI_CAPTURE; /* capabilities */ + PDEBUG(DBG_RIO, "%s: VIDIOC_QUERYCAP", __FUNCTION__); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner *vt = arg; + + if((vt->tuner) || (usbvision->channel)) { /* Only tuner 0 */ + return -EINVAL; + } + strcpy(vt->name, "vbi"); + // japan: 76.0 MHz - 89.9 MHz + // western europe: 87.5 MHz - 108.0 MHz + // russia: 65.0 MHz - 108.0 MHz + vt->rangelow=(int)(65*16); + vt->rangehigh=(int)(108*16); + vt->flags= 0; + vt->mode = 0; + call_i2c_clients(usbvision,cmd,vt); + PDEBUG(DBG_RIO, "%s: VIDIOCGTUNER signal=%d", __FUNCTION__, vt->signal); + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner *vt = arg; + + // Only channel 0 has a tuner + if((vt->tuner) || (usbvision->channel)) { + return -EINVAL; + } + PDEBUG(DBG_RIO, "%s: VIDIOCSTUNER", __FUNCTION__); + return 0; + } + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + memset(va,0, sizeof(struct video_audio)); + call_i2c_clients(usbvision, cmd, va); + va->flags|=VIDEO_AUDIO_MUTABLE; + va->volume=1; + va->step=1; + strcpy(va->name, "vbi"); + PDEBUG(DBG_RIO, "%s: VIDIOCGAUDIO", __FUNCTION__); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + if(va->audio) { + return -EINVAL; + } + + if(va->flags & VIDEO_AUDIO_MUTE) { + if (usbvision_audio_mute(usbvision)) { + return -EFAULT; + } + } + else { + if (usbvision_audio_on(usbvision)) { + return -EFAULT; + } + } + PDEBUG(DBG_RIO, "%s: VIDIOCSAUDIO flags=0x%x)", __FUNCTION__, va->flags); + return 0; + } + case VIDIOCGFREQ: + { + unsigned long *freq = arg; + + *freq = usbvision->freq; + PDEBUG(DBG_RIO, "%s: VIDIOCGFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4); + return 0; + } + case VIDIOCSFREQ: + { + unsigned long *freq = arg; + + usbvision->freq = *freq; + call_i2c_clients(usbvision, cmd, freq); + PDEBUG(DBG_RIO, "%s: VIDIOCSFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4); + return 0; + } + default: + { + PDEBUG(DBG_RIO, "%s: Unknown command %d", __FUNCTION__, cmd); + return -ENOIOCTLCMD; + } + } + return 0; +} + +static int usbvision_vbi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, usbvision_do_vbi_ioctl); +} + + + +static void usbvision_configure_video(struct usb_usbvision *usbvision) +{ + int model; + + if (usbvision == NULL) + return; + + model = usbvision->DevModel; + + RESTRICT_TO_RANGE(init_brightness, 0, 255); + RESTRICT_TO_RANGE(init_contrast, 0, 255); + RESTRICT_TO_RANGE(init_saturation, 0, 255); + RESTRICT_TO_RANGE(init_hue, 0, 255); + + usbvision->saturation = init_saturation << 8; + usbvision->hue = init_hue << 8; + usbvision->brightness = init_brightness << 8; + usbvision->contrast = init_contrast << 8; + usbvision->depth = 24; + usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24; + + if (usbvision_device_data[usbvision->DevModel].Vin_Reg2 >= 0) { + usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2 & 0xff; + } else { + usbvision->Vin_Reg2_Preset = 0; + } + + memset(&usbvision->vcap, 0, sizeof(usbvision->vcap)); + strcpy(usbvision->vcap.driver, "USBVision"); + usbvision->vcap.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + (dga ? (V4L2_FBUF_CAP_LIST_CLIPPING | V4L2_CAP_VIDEO_OVERLAY) : 0) | + (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); + usbvision->vcap.version = USBVISION_DRIVER_VERSION; /* version */ + + usbvision->video_inputs = usbvision_device_data[model].VideoChannels; + + memset(&usbvision->input, 0, sizeof(usbvision->input)); + usbvision->input.tuner = 0; + usbvision->input.type = V4L2_INPUT_TYPE_TUNER|VIDEO_TYPE_CAMERA; + usbvision->input.std = usbvision_device_data[model].VideoNorm; + + /* This should be here to make i2c clients to be able to register */ + usbvision_audio_off(usbvision); //first switch off audio + if (!PowerOnAtOpen) { + usbvision_power_on(usbvision); //and then power up the noisy tuner + usbvision_init_i2c(usbvision); + } +} + +// +// Video registration stuff +// + +// Video template +static struct file_operations usbvision_fops = { + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .open = usbvision_v4l2_open, + .release = usbvision_v4l2_close, + .read = usbvision_v4l2_read, + .mmap = usbvision_v4l2_mmap, + .ioctl = usbvision_v4l2_ioctl, + .llseek = no_llseek, +}; +static struct video_device usbvision_video_template = { + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .type = VID_TYPE_TUNER | VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_USBVISION, + .fops = &usbvision_fops, + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + .name = "usbvision-video", + .release = video_device_release, + #endif + .minor = -1, +}; + + +// Radio template +static struct file_operations usbvision_radio_fops = { + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .open = usbvision_radio_open, + .release = usbvision_radio_close, + .ioctl = usbvision_radio_ioctl, + .llseek = no_llseek, +}; + +static struct video_device usbvision_radio_template= +{ + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_USBVISION, + .fops = &usbvision_radio_fops, + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + .release = video_device_release, + .name = "usbvision-radio", + #endif + .minor = -1, +}; + + +// vbi template +static struct file_operations usbvision_vbi_fops = { + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .open = usbvision_vbi_open, + .release = usbvision_vbi_close, + .ioctl = usbvision_vbi_ioctl, + .llseek = no_llseek, +}; + +static struct video_device usbvision_vbi_template= +{ + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_USBVISION, + .fops = &usbvision_vbi_fops, + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + .release = video_device_release, + .name = "usbvision-vbi", + #endif + .minor = -1, +}; + + +static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, + struct video_device *vdev_template, + char *name) +{ + struct usb_device *usb_dev = usbvision->dev; + struct video_device *vdev; + + if (usb_dev == NULL) { + err("%s: usbvision->dev is not set", __FUNCTION__); + return NULL; + } + + vdev = video_device_alloc(); + if (NULL == vdev) { + return NULL; + } + *vdev = *vdev_template; +// vdev->minor = -1; + vdev->dev = &usb_dev->dev; + snprintf(vdev->name, sizeof(vdev->name), "%s", name); + video_set_drvdata(vdev, usbvision); + return vdev; +} + +// unregister video4linux devices +static void usbvision_unregister_video(struct usb_usbvision *usbvision) +{ + // vbi Device: + if (usbvision->vbi) { + PDEBUG(DBG_PROBE, "unregister /dev/vbi%d [v4l2]", usbvision->vbi->minor & 0x1f); + if (usbvision->vbi->minor != -1) { + video_unregister_device(usbvision->vbi); + } + else { + video_device_release(usbvision->vbi); + } + usbvision->vbi = NULL; + } + + // Radio Device: + if (usbvision->rdev) { + PDEBUG(DBG_PROBE, "unregister /dev/radio%d [v4l2]", usbvision->rdev->minor & 0x1f); + if (usbvision->rdev->minor != -1) { + video_unregister_device(usbvision->rdev); + } + else { + video_device_release(usbvision->rdev); + } + usbvision->rdev = NULL; + } + + // Video Device: + if (usbvision->vdev) { + PDEBUG(DBG_PROBE, "unregister /dev/video%d [v4l2]", usbvision->vdev->minor & 0x1f); + if (usbvision->vdev->minor != -1) { + video_unregister_device(usbvision->vdev); + } + else { + video_device_release(usbvision->vdev); + } + usbvision->vdev = NULL; + } +} + +// register video4linux devices +static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) +{ + // Video Device: + usbvision->vdev = usbvision_vdev_init(usbvision, &usbvision_video_template, "USBVision Video"); + if (usbvision->vdev == NULL) { + goto err_exit; + } + if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr)<0) { + goto err_exit; + } + info("USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2]", usbvision->nr,usbvision->vdev->minor & 0x1f); + + // Radio Device: + if (usbvision_device_data[usbvision->DevModel].Radio) { + // usbvision has radio + usbvision->rdev = usbvision_vdev_init(usbvision, &usbvision_radio_template, "USBVision Radio"); + if (usbvision->rdev == NULL) { + goto err_exit; + } + if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr)<0) { + goto err_exit; + } + info("USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]", usbvision->nr, usbvision->rdev->minor & 0x1f); + } + // vbi Device: + if (usbvision_device_data[usbvision->DevModel].vbi) { + usbvision->vbi = usbvision_vdev_init(usbvision, &usbvision_vbi_template, "USBVision VBI"); + if (usbvision->vdev == NULL) { + goto err_exit; + } + if (video_register_device(usbvision->vbi, VFL_TYPE_VBI, vbi_nr)<0) { + goto err_exit; + } + info("USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!)", usbvision->nr,usbvision->vbi->minor & 0x1f); + } + // all done + return 0; + + err_exit: + err("USBVision[%d]: video_register_device() failed", usbvision->nr); + usbvision_unregister_video(usbvision); + return -1; +} + +/* + * usbvision_alloc() + * + * This code allocates the struct usb_usbvision. It is filled with default values. + * + * Returns NULL on error, a pointer to usb_usbvision else. + * + */ +static struct usb_usbvision *usbvision_alloc(struct usb_device *dev) +{ + struct usb_usbvision *usbvision; + int FrameIdx; + + if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) == NULL) { + goto err_exit; + } + + usbvision->dev = dev; + + for (FrameIdx = 0; FrameIdx < USBVISION_NUMFRAMES; FrameIdx++) { + init_waitqueue_head(&usbvision->frame[FrameIdx].wq); + } + init_waitqueue_head(&usbvision->overlay_frame.wq); + init_MUTEX(&usbvision->lock); /* to 1 == available */ + + // prepare control urb for control messages during interrupts + usbvision->ctrlUrb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); + if (usbvision->ctrlUrb == NULL) { + goto err_exit; + } + init_waitqueue_head(&usbvision->ctrlUrb_wq); + init_MUTEX(&usbvision->ctrlUrbLock); + + init_timer(&usbvision->powerOffTimer); + usbvision->powerOffTimer.data = (long) usbvision; + usbvision->powerOffTimer.function = usbvision_powerOffTimer; + + return usbvision; + +err_exit: + if (usbvision && usbvision->ctrlUrb) { + usb_free_urb(usbvision->ctrlUrb); + } + if (usbvision) { + kfree(usbvision); + } + return NULL; +} + +/* + * usbvision_release() + * + * This code does final release of struct usb_usbvision. This happens + * after the device is disconnected -and- all clients closed their files. + * + */ +static void usbvision_release(struct usb_usbvision *usbvision) +{ + PDEBUG(DBG_PROBE, ""); + + down(&usbvision->lock); + + if (timer_pending(&usbvision->powerOffTimer)) { + del_timer(&usbvision->powerOffTimer); + } + + usbvision->usbvision_used = 0; + usbvision->initialized = 0; + + up(&usbvision->lock); + + usbvision_remove_sysfs(usbvision->vdev); + usbvision_unregister_video(usbvision); + if(dga) { + if (usbvision->overlay_base) { + iounmap(usbvision->overlay_base); + } + } + + if (usbvision->ctrlUrb) { + usb_free_urb(usbvision->ctrlUrb); + } + + kfree(usbvision); + + PDEBUG(DBG_PROBE, "success"); +} + + +/* + * usbvision_probe() + * + * This procedure queries device descriptor and accepts the interface + * if it looks like USBVISION video device + * + */ +static int __devinit usbvision_probe(struct usb_interface *intf, const struct usb_device_id *devid) +{ + struct usb_device *dev = interface_to_usbdev(intf); + __u8 ifnum = intf->altsetting->desc.bInterfaceNumber; + const struct usb_host_interface *interface; + struct usb_usbvision *usbvision = NULL; + const struct usb_endpoint_descriptor *endpoint; + int model; + + PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u", + dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); + + /* Is it an USBVISION video dev? */ + model = 0; + for(model = 0; usbvision_device_data[model].idVendor; model++) { + if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) { + continue; + } + if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) { + continue; + } + + info("%s: %s found", __FUNCTION__, usbvision_device_data[model].ModelString); + break; + } + + if (usbvision_device_data[model].idVendor == 0) { + return -ENODEV; //no matching device + } + if (usbvision_device_data[model].Interface >= 0) { + interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0]; + } + else { + interface = &dev->actconfig->interface[ifnum]->altsetting[0]; + } + endpoint = &interface->endpoint[1].desc; + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) { + err("%s: interface %d. has non-ISO endpoint!", __FUNCTION__, ifnum); + err("%s: Endpoint attribures %d", __FUNCTION__, endpoint->bmAttributes); + return -ENODEV; + } + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { + err("%s: interface %d. has ISO OUT endpoint!", __FUNCTION__, ifnum); + return -ENODEV; + } + + usb_get_dev(dev); + + if ((usbvision = usbvision_alloc(dev)) == NULL) { + err("%s: couldn't allocate USBVision struct", __FUNCTION__); + return -ENOMEM; + } + if (dev->descriptor.bNumConfigurations > 1) { + usbvision->bridgeType = BRIDGE_NT1004; + } + else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") { + usbvision->bridgeType = BRIDGE_NT1005; + } + else { + usbvision->bridgeType = BRIDGE_NT1003; + } + PDEBUG(DBG_PROBE, "bridgeType %d", usbvision->bridgeType); + + down(&usbvision->lock); + + usbvision->nr = usbvision_nr++; + + usbvision->have_tuner = usbvision_device_data[model].Tuner; + if (usbvision->have_tuner) { + usbvision->tuner_type = usbvision_device_data[model].TunerType; + } + + usbvision->DevModel = model; + usbvision->remove_pending = 0; + usbvision->last_error = 0; + usbvision->iface = ifnum; + usbvision->ifaceAltInactive = 0; + usbvision->ifaceAltActive = 1; + usbvision->video_endp = endpoint->bEndpointAddress; + usbvision->isocPacketSize = 0; + usbvision->usb_bandwidth = 0; + usbvision->user = 0; + + usbvision_register_video(usbvision); + usbvision_configure_video(usbvision); + up(&usbvision->lock); + + + usb_set_intfdata (intf, usbvision); + usbvision_create_sysfs(usbvision->vdev); + + PDEBUG(DBG_PROBE, "success"); + return 0; +} + + +/* + * usbvision_disconnect() + * + * This procedure stops all driver activity, deallocates interface-private + * structure (pointed by 'ptr') and after that driver should be removable + * with no ill consequences. + * + */ +static void __devexit usbvision_disconnect(struct usb_interface *intf) +{ + struct usb_usbvision *usbvision = usb_get_intfdata(intf); + + PDEBUG(DBG_PROBE, ""); + + if (usbvision == NULL) { + err("%s: usb_get_intfdata() failed", __FUNCTION__); + return; + } + usb_set_intfdata (intf, NULL); + + down(&usbvision->lock); + + // At this time we ask to cancel outstanding URBs + usbvision_stop_isoc(usbvision); + + if (usbvision->power) { + usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_power_off(usbvision); + } + usbvision->remove_pending = 1; // Now all ISO data will be ignored + + usb_put_dev(usbvision->dev); + usbvision->dev = NULL; // USB device is no more + + up(&usbvision->lock); + + if (usbvision->user) { + info("%s: In use, disconnect pending", __FUNCTION__); + } + else { + usbvision_release(usbvision); + } + + PDEBUG(DBG_PROBE, "success"); + +} + +MODULE_DEVICE_TABLE (usb, usbvision_table); + +static struct usb_driver usbvision_driver = { + .name = "usbvision", + .id_table = usbvision_table, + .probe = usbvision_probe, + .disconnect = usbvision_disconnect +}; + +/* + * customdevice_process() + * + * This procedure preprocesses CustomDevice parameter if any + * + */ +void customdevice_process(void) +{ + usbvision_device_data[0]=usbvision_device_data[1]; + usbvision_table[0]=usbvision_table[1]; + + if(CustomDevice) + { + char *parse=CustomDevice; + + PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice); + + /*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1" + usbvision_device_data[0].idVendor; + usbvision_device_data[0].idProduct; + usbvision_device_data[0].Interface; + usbvision_device_data[0].Codec; + usbvision_device_data[0].VideoChannels; + usbvision_device_data[0].VideoNorm; + usbvision_device_data[0].AudioChannels; + usbvision_device_data[0].Radio; + usbvision_device_data[0].Tuner; + usbvision_device_data[0].TunerType; + usbvision_device_data[0].Vin_Reg1; + usbvision_device_data[0].Vin_Reg2; + usbvision_device_data[0].X_Offset; + usbvision_device_data[0].Y_Offset; + usbvision_device_data[0].Dvi_yuv; + usbvision_device_data[0].ModelString; + */ + + rmspace(parse); + usbvision_device_data[0].ModelString="USBVISION Custom Device"; + + parse+=2; + sscanf(parse,"%x",&usbvision_device_data[0].idVendor); + goto2next(parse); + PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_device_data[0].idVendor); + parse+=2; + sscanf(parse,"%x",&usbvision_device_data[0].idProduct); + goto2next(parse); + PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_device_data[0].idProduct); + sscanf(parse,"%d",&usbvision_device_data[0].Interface); + goto2next(parse); + PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface); + sscanf(parse,"%d",&usbvision_device_data[0].Codec); + goto2next(parse); + PDEBUG(DBG_PROBE, "Codec=%d", usbvision_device_data[0].Codec); + sscanf(parse,"%d",&usbvision_device_data[0].VideoChannels); + goto2next(parse); + PDEBUG(DBG_PROBE, "VideoChannels=%d", usbvision_device_data[0].VideoChannels); + + switch(*parse) + { + case 'P': + PDEBUG(DBG_PROBE, "VideoNorm=PAL"); + usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL; + break; + + case 'S': + PDEBUG(DBG_PROBE, "VideoNorm=SECAM"); + usbvision_device_data[0].VideoNorm=VIDEO_MODE_SECAM; + break; + + case 'N': + PDEBUG(DBG_PROBE, "VideoNorm=NTSC"); + usbvision_device_data[0].VideoNorm=VIDEO_MODE_NTSC; + break; + + default: + PDEBUG(DBG_PROBE, "VideoNorm=PAL (by default)"); + usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL; + break; + } + goto2next(parse); + + sscanf(parse,"%d",&usbvision_device_data[0].AudioChannels); + goto2next(parse); + PDEBUG(DBG_PROBE, "AudioChannels=%d", usbvision_device_data[0].AudioChannels); + sscanf(parse,"%d",&usbvision_device_data[0].Radio); + goto2next(parse); + PDEBUG(DBG_PROBE, "Radio=%d", usbvision_device_data[0].Radio); + sscanf(parse,"%d",&usbvision_device_data[0].Tuner); + goto2next(parse); + PDEBUG(DBG_PROBE, "Tuner=%d", usbvision_device_data[0].Tuner); + sscanf(parse,"%d",&usbvision_device_data[0].TunerType); + goto2next(parse); + PDEBUG(DBG_PROBE, "TunerType=%d", usbvision_device_data[0].TunerType); + sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg1); + goto2next(parse); + PDEBUG(DBG_PROBE, "Vin_Reg1=%d", usbvision_device_data[0].Vin_Reg1); + sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg2); + goto2next(parse); + PDEBUG(DBG_PROBE, "Vin_Reg2=%d", usbvision_device_data[0].Vin_Reg2); + sscanf(parse,"%d",&usbvision_device_data[0].X_Offset); + goto2next(parse); + PDEBUG(DBG_PROBE, "X_Offset=%d", usbvision_device_data[0].X_Offset); + sscanf(parse,"%d",&usbvision_device_data[0].Y_Offset); + goto2next(parse); + PDEBUG(DBG_PROBE, "Y_Offset=%d", usbvision_device_data[0].Y_Offset); + sscanf(parse,"%d",&usbvision_device_data[0].Dvi_yuv); + PDEBUG(DBG_PROBE, "Dvi_yuv=%d", usbvision_device_data[0].Dvi_yuv); + + //add to usbvision_table also + usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE; + usbvision_table[0].idVendor=usbvision_device_data[0].idVendor; + usbvision_table[0].idProduct=usbvision_device_data[0].idProduct; + + } +} + + + +/* + * usbvision_init() + * + * This code is run to initialize the driver. + * + */ +static int __init usbvision_init(void) +{ + int errCode; + + PDEBUG(DBG_PROBE, ""); + + PDEBUG(DBG_IOCTL, "IOCTL debugging is enabled"); + PDEBUG(DBG_IO, "IO debugging is enabled"); + PDEBUG(DBG_RIO, "RIO debugging is enabled"); + PDEBUG(DBG_HEADER, "HEADER debugging is enabled"); + PDEBUG(DBG_PROBE, "PROBE debugging is enabled"); + PDEBUG(DBG_IRQ, "IRQ debugging is enabled"); + PDEBUG(DBG_ISOC, "ISOC debugging is enabled"); + PDEBUG(DBG_PARSE, "PARSE debugging is enabled"); + PDEBUG(DBG_SCRATCH, "SCRATCH debugging is enabled"); + PDEBUG(DBG_FUNC, "FUNC debugging is enabled"); + PDEBUG(DBG_I2C, "I2C debugging is enabled"); + + /* disable planar mode support unless compression enabled */ + if (isocMode != ISOC_MODE_COMPRESS ) { + // FIXME : not the right way to set supported flag + usbvision_v4l2_format[6].supported = 0; // V4L2_PIX_FMT_YVU420 + usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P + } + + customdevice_process(); + + errCode = usb_register(&usbvision_driver); + + if (errCode == 0) { + info(DRIVER_DESC " : " DRIVER_VERSION); + PDEBUG(DBG_PROBE, "success"); + } + return errCode; +} + +static void __exit usbvision_exit(void) +{ + PDEBUG(DBG_PROBE, ""); + + usb_deregister(&usbvision_driver); + PDEBUG(DBG_PROBE, "success"); +} + +module_init(usbvision_init); +module_exit(usbvision_exit); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c new file mode 100644 index 0000000..e050e55 --- /dev/null +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -0,0 +1,265 @@ +/* + * I2C_ALGO_USB.C + * i2c algorithm for USB-I2C Bridges + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * Dwaine Garden + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + * + * 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 +#include +#include +#include +#include + #include +#include +#include +#include +#include +#include +#include +#include +#include "usbvision-i2c.h" + +static int debug = 0; + +#if defined(module_param) // Showing parameters under SYSFS +module_param (debug, int, 0444); // debug mode of the device driver +#else +MODULE_PARM(debug, "i"); // debug mode of the device driver +#endif + +MODULE_AUTHOR("Joerg Heckenbach"); +MODULE_DESCRIPTION("I2C algorithm for USB-I2C-bridges"); +MODULE_LICENSE("GPL"); + + +static inline int try_write_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct i2c_algo_usb_data *adap = i2c_adap->algo_data; + void *data; + int i, ret = -1; + char buf[4]; + + data = i2c_get_adapdata(i2c_adap); + buf[0] = 0x00; + for (i = 0; i <= retries; i++) { + ret = (adap->outb(data, addr, buf, 1)); + if (ret == 1) + break; /* success! */ + udelay(5 /*adap->udelay */ ); + if (i == retries) /* no success */ + break; + udelay(adap->udelay); + } + if (debug) { + if (i) { + info("%s: Needed %d retries for address %#2x", __FUNCTION__, i, addr); + info("%s: Maybe there's no device at this address", __FUNCTION__); + } + } + return ret; +} + +static inline int try_read_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct i2c_algo_usb_data *adap = i2c_adap->algo_data; + void *data; + int i, ret = -1; + char buf[4]; + + data = i2c_get_adapdata(i2c_adap); + for (i = 0; i <= retries; i++) { + ret = (adap->inb(data, addr, buf, 1)); + if (ret == 1) + break; /* success! */ + udelay(5 /*adap->udelay */ ); + if (i == retries) /* no success */ + break; + udelay(adap->udelay); + } + if (debug) { + if (i) { + info("%s: Needed %d retries for address %#2x", __FUNCTION__, i, addr); + info("%s: Maybe there's no device at this address", __FUNCTION__); + } + } + return ret; +} + +static inline int usb_find_address(struct i2c_adapter *i2c_adap, + struct i2c_msg *msg, int retries, + unsigned char *add) +{ + unsigned short flags = msg->flags; + + unsigned char addr; + int ret; + if ((flags & I2C_M_TEN)) { + /* a ten bit address */ + addr = 0xf0 | ((msg->addr >> 7) & 0x03); + /* try extended address code... */ + ret = try_write_address(i2c_adap, addr, retries); + if (ret != 1) { + err("died at extended address code, while writing"); + return -EREMOTEIO; + } + add[0] = addr; + if (flags & I2C_M_RD) { + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_read_address(i2c_adap, addr, retries); + if (ret != 1) { + err("died at extended address code, while reading"); + return -EREMOTEIO; + } + } + + } else { /* normal 7bit address */ + addr = (msg->addr << 1); + if (flags & I2C_M_RD) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR) + addr ^= 1; + + add[0] = addr; + if (flags & I2C_M_RD) + ret = try_read_address(i2c_adap, addr, retries); + else + ret = try_write_address(i2c_adap, addr, retries); + + if (ret != 1) { + return -EREMOTEIO; + } + } + return 0; +} + +static int +usb_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + struct i2c_algo_usb_data *adap = i2c_adap->algo_data; + void *data; + int i, ret; + unsigned char addr; + + data = i2c_get_adapdata(i2c_adap); + + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr); + if (ret != 0) { + if (debug) { + info("%s: got NAK from device, message #%d\n", __FUNCTION__, i); + } + return (ret < 0) ? ret : -EREMOTEIO; + } + + if (pmsg->flags & I2C_M_RD) { + /* read bytes into buffer */ + ret = (adap->inb(data, addr, pmsg->buf, pmsg->len)); + if (ret < pmsg->len) { + return (ret < 0) ? ret : -EREMOTEIO; + } + } else { + /* write bytes from buffer */ + ret = (adap->outb(data, addr, pmsg->buf, pmsg->len)); + if (ret < pmsg->len) { + return (ret < 0) ? ret : -EREMOTEIO; + } + } + } + return num; +} + +static int algo_control(struct i2c_adapter *adapter, unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 usb_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm i2c_usb_algo = { + .master_xfer = usb_xfer, + .smbus_xfer = NULL, + .slave_send = NULL, + .slave_recv = NULL, + .algo_control = algo_control, + .functionality = usb_func, +}; + + +/* + * registering functions to load algorithms at runtime + */ +int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap) +{ + /* register new adapter to i2c module... */ + + adap->algo = &i2c_usb_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + +#ifdef MODULE + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 21) + MOD_INC_USE_COUNT; + #endif +#endif + + i2c_add_adapter(adap); + + if (debug) { + info("i2c bus for %s registered", adap->name); + } + + return 0; +} + + +int usbvision_i2c_usb_del_bus(struct i2c_adapter *adap) +{ + + i2c_del_adapter(adap); + + if (debug) { + info("i2c bus for %s unregistered", adap->name); + } +#ifdef MODULE + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 21) + MOD_DEC_USE_COUNT; + #endif +#endif + + return 0; +} + +EXPORT_SYMBOL(usbvision_i2c_usb_add_bus); +EXPORT_SYMBOL(usbvision_i2c_usb_del_bus); diff --git a/drivers/media/video/usbvision/usbvision-i2c.h b/drivers/media/video/usbvision/usbvision-i2c.h new file mode 100644 index 0000000..a2df8db --- /dev/null +++ b/drivers/media/video/usbvision/usbvision-i2c.h @@ -0,0 +1,58 @@ +/* + * I2C_ALGO_USB.H + * i2c algorithm for USB-I2C Bridges + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + * + * 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 I2C_ALGO_USB_H +#define I2C_ALGO_USB_H 1 + +#include + +struct i2c_algo_usb_data { + void *data; /* private data for lowlevel routines */ + int (*inb) (void *data, unsigned char addr, char *buf, short len); + int (*outb) (void *data, unsigned char addr, char *buf, short len); + + /* local settings */ + int udelay; + int mdelay; + int timeout; +}; + +#define I2C_USB_ADAP_MAX 16 + +int usbvision_i2c_usb_add_bus(struct i2c_adapter *); +int usbvision_i2c_usb_del_bus(struct i2c_adapter *); + +static inline void *i2c_get_algo_usb_data (struct i2c_algo_usb_data *dev) +{ + return dev->data; +} + +static inline void i2c_set_algo_usb_data (struct i2c_algo_usb_data *dev, void *data) +{ + dev->data = data; +} + + +#endif //I2C_ALGO_USB_H diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h new file mode 100644 index 0000000..b03e4ea --- /dev/null +++ b/drivers/media/video/usbvision/usbvision.h @@ -0,0 +1,594 @@ +/* + * USBVISION.H + * usbvision header file + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + * + * 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 __LINUX_USBVISION_H +#define __LINUX_USBVISION_H + +#include + +#ifndef VID_HARDWARE_USBVISION + #define VID_HARDWARE_USBVISION 34 /* USBVision Video Grabber */ +#endif + +#define USBVISION_PWR_REG 0x00 + #define USBVISION_SSPND_EN (1 << 1) + #define USBVISION_RES2 (1 << 2) + #define USBVISION_PWR_VID (1 << 5) + #define USBVISION_E2_EN (1 << 7) +#define USBVISION_CONFIG_REG 0x01 +#define USBVISION_ADRS_REG 0x02 +#define USBVISION_ALTER_REG 0x03 +#define USBVISION_FORCE_ALTER_REG 0x04 +#define USBVISION_STATUS_REG 0x05 +#define USBVISION_IOPIN_REG 0x06 + #define USBVISION_IO_1 (1 << 0) + #define USBVISION_IO_2 (1 << 1) + #define USBVISION_AUDIO_IN 0 + #define USBVISION_AUDIO_TV 1 + #define USBVISION_AUDIO_RADIO 2 + #define USBVISION_AUDIO_MUTE 3 +#define USBVISION_SER_MODE 0x07 +#define USBVISION_SER_ADRS 0x08 +#define USBVISION_SER_CONT 0x09 +#define USBVISION_SER_DAT1 0x0A +#define USBVISION_SER_DAT2 0x0B +#define USBVISION_SER_DAT3 0x0C +#define USBVISION_SER_DAT4 0x0D +#define USBVISION_EE_DATA 0x0E +#define USBVISION_EE_LSBAD 0x0F +#define USBVISION_EE_CONT 0x10 +#define USBVISION_DRM_CONT 0x12 + #define USBVISION_REF (1 << 0) + #define USBVISION_RES_UR (1 << 2) + #define USBVISION_RES_FDL (1 << 3) + #define USBVISION_RES_VDW (1 << 4) +#define USBVISION_DRM_PRM1 0x13 +#define USBVISION_DRM_PRM2 0x14 +#define USBVISION_DRM_PRM3 0x15 +#define USBVISION_DRM_PRM4 0x16 +#define USBVISION_DRM_PRM5 0x17 +#define USBVISION_DRM_PRM6 0x18 +#define USBVISION_DRM_PRM7 0x19 +#define USBVISION_DRM_PRM8 0x1A +#define USBVISION_VIN_REG1 0x1B + #define USBVISION_8_422_SYNC 0x01 + #define USBVISION_16_422_SYNC 0x02 + #define USBVISION_VSNC_POL (1 << 3) + #define USBVISION_HSNC_POL (1 << 4) + #define USBVISION_FID_POL (1 << 5) + #define USBVISION_HVALID_PO (1 << 6) + #define USBVISION_VCLK_POL (1 << 7) +#define USBVISION_VIN_REG2 0x1C + #define USBVISION_AUTO_FID (1 << 0) + #define USBVISION_NONE_INTER (1 << 1) + #define USBVISION_NOHVALID (1 << 2) + #define USBVISION_UV_ID (1 << 3) + #define USBVISION_FIX_2C (1 << 4) + #define USBVISION_SEND_FID (1 << 5) + #define USBVISION_KEEP_BLANK (1 << 7) +#define USBVISION_LXSIZE_I 0x1D +#define USBVISION_MXSIZE_I 0x1E +#define USBVISION_LYSIZE_I 0x1F +#define USBVISION_MYSIZE_I 0x20 +#define USBVISION_LX_OFFST 0x21 +#define USBVISION_MX_OFFST 0x22 +#define USBVISION_LY_OFFST 0x23 +#define USBVISION_MY_OFFST 0x24 +#define USBVISION_FRM_RATE 0x25 +#define USBVISION_LXSIZE_O 0x26 +#define USBVISION_MXSIZE_O 0x27 +#define USBVISION_LYSIZE_O 0x28 +#define USBVISION_MYSIZE_O 0x29 +#define USBVISION_FILT_CONT 0x2A +#define USBVISION_VO_MODE 0x2B +#define USBVISION_INTRA_CYC 0x2C +#define USBVISION_STRIP_SZ 0x2D +#define USBVISION_FORCE_INTRA 0x2E +#define USBVISION_FORCE_UP 0x2F +#define USBVISION_BUF_THR 0x30 +#define USBVISION_DVI_YUV 0x31 +#define USBVISION_AUDIO_CONT 0x32 +#define USBVISION_AUD_PK_LEN 0x33 +#define USBVISION_BLK_PK_LEN 0x34 +#define USBVISION_PCM_THR1 0x38 +#define USBVISION_PCM_THR2 0x39 +#define USBVISION_DIST_THR_L 0x3A +#define USBVISION_DIST_THR_H 0x3B +#define USBVISION_MAX_DIST_L 0x3C +#define USBVISION_MAX_DIST_H 0x3D +#define USBVISION_OP_CODE 0x33 + +#define MAX_BYTES_PER_PIXEL 4 + +#define MIN_FRAME_WIDTH 64 +#define MAX_USB_WIDTH 320 //384 +#define MAX_FRAME_WIDTH 320 //384 /*streching sometimes causes crashes*/ + +#define MIN_FRAME_HEIGHT 48 +#define MAX_USB_HEIGHT 240 //288 +#define MAX_FRAME_HEIGHT 240 //288 /*Streching sometimes causes crashes*/ + +#define MAX_FRAME_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * MAX_BYTES_PER_PIXEL) +#define USBVISION_CLIPMASK_SIZE (MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT / 8) //bytesize of clipmask + +#define USBVISION_URB_FRAMES 32 +#define USBVISION_MAX_ISOC_PACKET_SIZE 959 // NT1003 Specs Document says 1023 + +#define USBVISION_NUM_HEADERMARKER 20 +#define USBVISION_NUMFRAMES 2 +#define USBVISION_NUMSBUF 2 + +#define USBVISION_POWEROFF_TIME 3 * (HZ) // 3 seconds + +/* This macro restricts an int variable to an inclusive range */ +#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } + +/* + * We use macros to do YUV -> RGB conversion because this is + * very important for speed and totally unimportant for size. + * + * YUV -> RGB Conversion + * --------------------- + * + * B = 1.164*(Y-16) + 2.018*(V-128) + * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) + * R = 1.164*(Y-16) + 1.596*(U-128) + * + * If you fancy integer arithmetics (as you should), hear this: + * + * 65536*B = 76284*(Y-16) + 132252*(V-128) + * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) + * 65536*R = 76284*(Y-16) + 104595*(U-128) + * + * Make sure the output values are within [0..255] range. + */ +#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) +#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \ + int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ + mm_y = (my) - 16; \ + mm_u = (mu) - 128; \ + mm_v = (mv) - 128; \ + mm_yc= mm_y * 76284; \ + mm_b = (mm_yc + 132252*mm_v ) >> 16; \ + mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \ + mm_r = (mm_yc + 104595*mm_u ) >> 16; \ + mb = LIMIT_RGB(mm_b); \ + mg = LIMIT_RGB(mm_g); \ + mr = LIMIT_RGB(mm_r); \ +} + + + +/* Debugging aid */ +#define USBVISION_SAY_AND_WAIT(what) { \ + wait_queue_head_t wq; \ + init_waitqueue_head(&wq); \ + printk(KERN_INFO "Say: %s\n", what); \ + interruptible_sleep_on_timeout (&wq, HZ*3); \ +} + +/* + * This macro checks if usbvision is still operational. The 'usbvision' + * pointer must be valid, usbvision->dev must be valid, we are not + * removing the device and the device has not erred on us. + */ +#define USBVISION_IS_OPERATIONAL(udevice) (\ + (udevice != NULL) && \ + ((udevice)->dev != NULL) && \ + ((udevice)->last_error == 0) && \ + (!(udevice)->remove_pending)) + + +enum ScanState { + ScanState_Scanning, /* Scanning for header */ + ScanState_Lines /* Parsing lines */ +}; + +/* Completion states of the data parser */ +enum ParseState { + ParseState_Continue, /* Just parse next item */ + ParseState_NextFrame, /* Frame done, send it to V4L */ + ParseState_Out, /* Not enough data for frame */ + ParseState_EndParse /* End parsing */ +}; + +enum FrameState { + FrameState_Unused, /* Unused (no MCAPTURE) */ + FrameState_Ready, /* Ready to start grabbing */ + FrameState_Grabbing, /* In the process of being grabbed into */ + FrameState_Done, /* Finished grabbing, but not been synced yet */ + FrameState_DoneHold, /* Are syncing or reading */ + FrameState_Error, /* Something bad happened while processing */ +}; + +enum IsocState { + IsocState_InFrame, /* Isoc packet is member of frame */ + IsocState_NoFrame, /* Isoc packet is not member of any frame */ +}; + +struct usb_device; + +struct usbvision_sbuf { + char *data; + struct urb *urb; +}; + +#define USBVISION_MAGIC_1 0x55 +#define USBVISION_MAGIC_2 0xAA +#define USBVISION_HEADER_LENGTH 0x0c +#define USBVISION_SAA7111_ADDR 0x48 +#define USBVISION_SAA7113_ADDR 0x4a +#define USBVISION_IIC_LRACK 0x20 +#define USBVISION_IIC_LRNACK 0x30 +#define USBVISION_FRAME_FORMAT_PARAM_INTRA (1<<7) + +static struct usbvision_v4l2_format_st { + int supported; + int bytes_per_pixel; + int depth; + int format; + char *desc; +} usbvision_v4l2_format[] = { + { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" }, + { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" }, + { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" }, + { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" }, + { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" }, + { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" }, + { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, // 1.5 ! + { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" } +}; +#define USBVISION_SUPPORTED_PALETTES ARRAY_SIZE(usbvision_v4l2_format) + +struct usbvision_frame_header { + unsigned char magic_1; /* 0 magic */ + unsigned char magic_2; /* 1 magic */ + unsigned char headerLength; /* 2 */ + unsigned char frameNum; /* 3 */ + unsigned char framePhase; /* 4 */ + unsigned char frameLatency; /* 5 */ + unsigned char dataFormat; /* 6 */ + unsigned char formatParam; /* 7 */ + unsigned char frameWidthLo; /* 8 */ + unsigned char frameWidthHi; /* 9 */ + unsigned char frameHeightLo; /* 10 */ + unsigned char frameHeightHi; /* 11 */ + __u16 frameWidth; /* 8 - 9 after endian correction*/ + __u16 frameHeight; /* 10 - 11 after endian correction*/ +}; + +struct usbvision_frame { + char *data; /* Frame buffer */ + struct usbvision_frame_header isocHeader; /* Header from stream */ + + int width; /* Width application is expecting */ + int height; /* Height */ + + int frmwidth; /* Width the frame actually is */ + int frmheight; /* Height */ + + volatile int grabstate; /* State of grabbing */ + int scanstate; /* State of scanning */ + + int curline; /* Line of frame we're working on */ + + long scanlength; /* uncompressed, raw data length of frame */ + long bytes_read; /* amount of scanlength that has been read from data */ + struct usbvision_v4l2_format_st v4l2_format; /* format the user needs*/ + int v4l2_linesize; /* bytes for one videoline*/ + struct timeval timestamp; + wait_queue_head_t wq; /* Processes waiting */ + int sequence; // How many video frames we send to user +}; + +#define CODEC_SAA7113 7113 +#define CODEC_SAA7111 7111 +#define BRIDGE_NT1003 1003 +#define BRIDGE_NT1004 1004 +#define BRIDGE_NT1005 1005 + +/* Supported Devices: A table for usbvision.c*/ + +static struct usbvision_device_data_st { + int idVendor; + int idProduct; + int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ + int Codec; + int VideoChannels; + __u64 VideoNorm; + int AudioChannels; + int Radio; + int vbi; + int Tuner; + int TunerType; + int Vin_Reg1; + int Vin_Reg2; + int X_Offset; + int Y_Offset; + int Dvi_yuv; + char *ModelString; +} usbvision_device_data[] = { + {0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"}, + {0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Xanboo"}, + {0x050D, 0x0208, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Belkin USBView II"}, + {0x0571, 0x0002, 0, CODEC_SAA7111, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, -1, -1, 7, "echoFX InterView Lite"}, + {0x0573, 0x0003, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "USBGear USBG-V1 resp. HAMA USB"}, + {0x0573, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "D-Link V100"}, + {0x0573, 0x2000, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "X10 USB Camera"}, + {0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, -1, 3, 7, "Osprey 50"}, + {0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Hauppauge USB-Live Model 600"}, + {0x0573, 0x2101, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 2, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Zoran Co. PMD (Nogatech) AV-grabber Manhattan"}, + {0x0573, 0x4100, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Nogatech USB-TV (NTSC) FM"}, + {0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"}, + {0x0573, 0x4450, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "PixelView PlayTv-USB PRO (PAL) FM"}, + {0x0573, 0x4550, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "ZTV ZT-721 2.4GHz USB A/V Receiver"}, + {0x0573, 0x4d00, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Hauppauge WinTv-USB USA"}, + {0x0573, 0x4d01, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, + {0x0573, 0x4d02, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC)"}, + {0x0573, 0x4d03, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (SECAM) "}, + {0x0573, 0x4d10, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC) FM"}, + {0x0573, 0x4d11, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, + {0x0573, 0x4d12, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, + {0x0573, 0x4d2a, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B285"}, + {0x0573, 0x4d2b, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B282"}, + {0x0573, 0x4d2c, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (PAL/SECAM) 40209 Rev E1A5"}, + {0x0573, 0x4d20, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226"}, + {0x0573, 0x4d21, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL)"}, + {0x0573, 0x4d22, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) MODEL 566"}, + {0x0573, 0x4d23, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) 4D23"}, + {0x0573, 0x4d25, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B234"}, + {0x0573, 0x4d26, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B243"}, + {0x0573, 0x4d27, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B281"}, + {0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B283"}, + {0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40205 Rev B298"}, + {0x0573, 0x4d30, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB FM Model 40211 Rev B123"}, + {0x0573, 0x4d31, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 568"}, + {0x0573, 0x4d32, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 573"}, + {0x0573, 0x4d35, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 40219 Rev B252"}, + {0x0573, 0x4d37, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTV USB device Model 40219 Rev E189"}, + {0x0768, 0x0006, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 5, 5, -1, "Camtel Technology USB TV Genie Pro FM Model TVB330"}, + {0x07d0, 0x0001, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Digital Video Creator I"}, + {0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 82, 20, 7, "Global Village GV-007 (NTSC)"}, + {0x07d0, 0x0003, 0, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)"}, + {0x07d0, 0x0004, 0, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-80 Rev 1 (PAL)"}, + {0x07d0, 0x0005, 0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"}, + {0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"}, + {0x2304, 0x0109, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (SECAM)"}, + {0x2304, 0x0110, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1,128, 23, -1, "Pinnacle Studio PCTV USB (PAL) FM"}, + {0x2304, 0x0111, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Miro PCTV USB"}, + {0x2304, 0x0112, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (NTSC) FM"}, + {0x2304, 0x0210, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, + {0x2304, 0x0212, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_TEMIC_4039FR5_NTSC, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (NTSC) FM"}, + {0x2304, 0x0214, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, + {0x2304, 0x0300, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (NTSC)"}, + {0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (PAL)"}, + {0x2304, 0x0419, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle PCTV Bungee USB (PAL) FM"}, + {0x2400, 0x4200, -1, CODEC_SAA7111, 3, VIDEO_MODE_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, + {} /* Terminating entry */ +}; + + +/* Supported Devices: A table for the usb.c*/ + +static struct usb_device_id usbvision_table [] = { + { USB_DEVICE(0xFFF0, 0xFFF0) }, /* Custom Dummy USBVision Device */ + { USB_DEVICE(0x0A6F, 0x0400) }, /* Xanboo */ + { USB_DEVICE(0x050d, 0x0208) }, /* Belkin USBView II */ + { USB_DEVICE(0x0571, 0x0002) }, /* echoFX InterView Lite */ + { USB_DEVICE(0x0573, 0x0003) }, /* USBGear USBG-V1 */ + { USB_DEVICE(0x0573, 0x0400) }, /* D-Link V100 */ + { USB_DEVICE(0x0573, 0x2000) }, /* X10 USB Camera */ + { USB_DEVICE(0x0573, 0x2d00) }, /* Osprey 50 */ + { USB_DEVICE(0x0573, 0x2d01) }, /* Hauppauge USB-Live Model 600 */ + { USB_DEVICE(0x0573, 0x2101) }, /* Zoran Co. PMD (Nogatech) AV-grabber Manhattan */ + { USB_DEVICE(0x0573, 0x4100) }, /* Nogatech USB-TV FM (NTSC) */ + { USB_DEVICE(0x0573, 0x4110) }, /* PNY USB-TV (NTSC) FM */ + { USB_DEVICE(0x0573, 0x4450) }, /* PixelView PlayTv-USB PRO (PAL) FM */ + { USB_DEVICE(0x0573, 0x4550) }, /* ZTV ZT-721 2.4GHz USB A/V Receiver */ + { USB_DEVICE(0x0573, 0x4d00) }, /* Hauppauge WinTv-USB USA */ + { USB_DEVICE(0x0573, 0x4d01) }, /* Hauppauge WinTv-USB */ + { USB_DEVICE(0x0573, 0x4d02) }, /* Hauppauge WinTv-USB UK */ + { USB_DEVICE(0x0573, 0x4d03) }, /* Hauppauge WinTv-USB France */ + { USB_DEVICE(0x0573, 0x4d10) }, /* Hauppauge WinTv-USB with FM USA radio */ + { USB_DEVICE(0x0573, 0x4d11) }, /* Hauppauge WinTv-USB (PAL) with FM radio */ + { USB_DEVICE(0x0573, 0x4d12) }, /* Hauppauge WinTv-USB UK with FM Radio */ + { USB_DEVICE(0x0573, 0x4d2a) }, /* Hauppague WinTv USB Model 602 40201 Rev B285 */ + { USB_DEVICE(0x0573, 0x4d2b) }, /* Hauppague WinTv USB Model 602 40201 Rev B282 */ + { USB_DEVICE(0x0573, 0x4d2c) }, /* Hauppague WinTv USB Model 40209 Rev. E1A5 PAL*/ + { USB_DEVICE(0x0573, 0x4d20) }, /* Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226 */ + { USB_DEVICE(0x0573, 0x4d21) }, /* Hauppauge WinTv-USB II (PAL) with FM radio*/ + { USB_DEVICE(0x0573, 0x4d22) }, /* Hauppauge WinTv-USB II (PAL) Model 566 */ + { USB_DEVICE(0x0573, 0x4d23) }, /* Hauppauge WinTv-USB France 4D23*/ + { USB_DEVICE(0x0573, 0x4d25) }, /* Hauppauge WinTv-USB Model 40209 rev B234 */ + { USB_DEVICE(0x0573, 0x4d26) }, /* Hauppauge WinTv-USB Model 40209 Rev B243 */ + { USB_DEVICE(0x0573, 0x4d27) }, /* Hauppauge WinTv-USB Model 40204 Rev B281 */ + { USB_DEVICE(0x0573, 0x4d28) }, /* Hauppauge WinTv-USB Model 40204 Rev B283 */ + { USB_DEVICE(0x0573, 0x4d29) }, /* Hauppauge WinTv-USB Model 40205 Rev B298 */ + { USB_DEVICE(0x0573, 0x4d30) }, /* Hauppauge WinTv-USB FM Model 40211 Rev B123 */ + { USB_DEVICE(0x0573, 0x4d31) }, /* Hauppauge WinTv-USB III (PAL) with FM radio Model 568 */ + { USB_DEVICE(0x0573, 0x4d32) }, /* Hauppauge WinTv-USB III (PAL) FM Model 573 */ + { USB_DEVICE(0x0573, 0x4d35) }, /* Hauppauge WinTv-USB III (SECAM) FM Model 40219 Rev B252 */ + { USB_DEVICE(0x0573, 0x4d37) }, /* Hauppauge WinTv-USB Model 40219 Rev E189 */ + { USB_DEVICE(0x0768, 0x0006) }, /* Camtel Technology USB TV Genie Pro FM Model TVB330 */ + { USB_DEVICE(0x07d0, 0x0001) }, /* Digital Video Creator I */ + { USB_DEVICE(0x07d0, 0x0002) }, /* Global Village GV-007 (NTSC) */ + { USB_DEVICE(0x07d0, 0x0003) }, /* Dazzle Fusion Model DVC-50 Rev 1 (NTSC) */ + { USB_DEVICE(0x07d0, 0x0004) }, /* Dazzle Fusion Model DVC-80 Rev 1 (PAL) */ + { USB_DEVICE(0x07d0, 0x0005) }, /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */ + { USB_DEVICE(0x2304, 0x010d) }, /* Pinnacle Studio PCTV USB (PAL) */ + { USB_DEVICE(0x2304, 0x0109) }, /* Pinnacle Studio PCTV USB (SECAM) */ + { USB_DEVICE(0x2304, 0x0110) }, /* Pinnacle Studio PCTV USB (PAL) */ + { USB_DEVICE(0x2304, 0x0111) }, /* Miro PCTV USB */ + { USB_DEVICE(0x2304, 0x0112) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ + { USB_DEVICE(0x2304, 0x0210) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ + { USB_DEVICE(0x2304, 0x0212) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ + { USB_DEVICE(0x2304, 0x0214) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ + { USB_DEVICE(0x2304, 0x0300) }, /* Pinnacle Studio Linx Video input cable (NTSC) */ + { USB_DEVICE(0x2304, 0x0301) }, /* Pinnacle Studio Linx Video input cable (PAL) */ + { USB_DEVICE(0x2304, 0x0419) }, /* Pinnacle PCTV Bungee USB (PAL) FM */ + + { USB_DEVICE(0x2400, 0x4200) }, /* Hauppauge WinTv-USB2 Model 42012 */ + + { } /* Terminating entry */ +}; + + +#define USBVISION_I2C_CLIENTS_MAX 8 + +struct usb_usbvision { + struct video_device *vdev; /* Video Device */ + struct video_device *rdev; /* Radio Device */ + struct video_device *vbi; /* VBI Device */ + struct video_audio audio_dev; /* Current audio params */ + + /* i2c Declaration Section*/ + struct i2c_adapter i2c_adap; + struct i2c_algo_usb_data i2c_algo; + struct i2c_client i2c_client; + int i2c_state, i2c_ok; + struct i2c_client *i2c_clients[USBVISION_I2C_CLIENTS_MAX]; + + struct urb *ctrlUrb; + unsigned char ctrlUrbBuffer[8]; + int ctrlUrbBusy; + struct usb_ctrlrequest ctrlUrbSetup; + wait_queue_head_t ctrlUrb_wq; // Processes waiting + struct semaphore ctrlUrbLock; + + int have_tuner; + int tuner_type; + int bridgeType; // NT1003, NT1004, NT1005 + int channel; + int radio; + int video_inputs; // # of inputs + unsigned long freq; + int AudioMute; + int AudioChannel; + int isocMode; // format of video data for the usb isoc-transfer + unsigned int nr; // Number of the device < MAX_USBVISION + + /* Device structure */ + struct usb_device *dev; + unsigned char iface; /* Video interface number */ + unsigned char ifaceAltActive, ifaceAltInactive; /* Alt settings */ + unsigned char Vin_Reg2_Preset; + struct semaphore lock; + struct timer_list powerOffTimer; + struct work_struct powerOffWork; + int power; /* is the device powered on? */ + int user; /* user count for exclusive use */ + int usbvision_used; /* Is this structure in use? */ + int initialized; /* Had we already sent init sequence? */ + int DevModel; /* What type of USBVISION device we got? */ + int streaming; /* Are we streaming Isochronous? */ + int last_error; /* What calamity struck us? */ + int curwidth; /* width of the frame the device is currently set to*/ + int curheight; /* height of the frame the device is currently set to*/ + int stretch_width; /* stretch-factor for frame width (from usb to screen)*/ + int stretch_height; /* stretch-factor for frame height (from usb to screen)*/ + char *fbuf; /* Videodev buffer area for mmap*/ + int max_frame_size; /* Bytes in one video frame */ + int fbuf_size; /* Videodev buffer size */ + int curFrameNum; // number of current frame in frame buffer mode + struct usbvision_frame *curFrame; // pointer to current frame, set by usbvision_find_header + struct usbvision_frame frame[USBVISION_NUMFRAMES]; // frame buffer + int curSbufNum; // number of current receiving sbuf + struct usbvision_sbuf sbuf[USBVISION_NUMSBUF]; // S buffering + volatile int remove_pending; /* If set then about to exit */ + + /* Scratch space from the Isochronous Pipe.*/ + unsigned char *scratch; + int scratch_read_ptr; + int scratch_write_ptr; + int scratch_headermarker[USBVISION_NUM_HEADERMARKER]; + int scratch_headermarker_read_ptr; + int scratch_headermarker_write_ptr; + int isocstate; + /* color controls */ + int saturation; + int hue; + int brightness; + int contrast; + int depth; + struct usbvision_v4l2_format_st palette; + + struct v4l2_capability vcap; /* Video capabilities */ + struct v4l2_input input; /* May be used for tuner support */ + unsigned char video_endp; /* 0x82 for USBVISION devices based */ + + // Overlay stuff: + struct v4l2_framebuffer vid_buf; + struct v4l2_format vid_win; + int vid_buf_valid; // Status: video buffer is valid (set) + int vid_win_valid; // Status: video window is valid (set) + int overlay; /*Status: Are we overlaying? */ + unsigned int clipmask[USBVISION_CLIPMASK_SIZE / 4]; + unsigned char *overlay_base; /* Virtual base address of video buffer */ + unsigned char *overlay_win; /* virt start address of overlay window */ + struct usbvision_frame overlay_frame; + + // Decompression stuff: + unsigned char *IntraFrameBuffer; /* Buffer for reference frame */ + int BlockPos; //for test only + int requestIntra; // 0 = normal; 1 = intra frame is requested; + int lastIsocFrameNum; // check for lost isoc frames + int isocPacketSize; // need to calculate usedBandwidth + int usedBandwidth; // used bandwidth 0-100%, need to set comprLevel + int comprLevel; // How strong (100) or weak (0) is compression + int lastComprLevel; // How strong (100) or weak (0) was compression + int usb_bandwidth; /* Mbit/s */ + + /* /proc entries, relative to /proc/video/usbvision/ */ + struct proc_dir_entry *proc_devdir; /* Per-device proc directory */ + struct proc_dir_entry *proc_info; /* /info entry */ + struct proc_dir_entry *proc_register; /* /register entry */ + struct proc_dir_entry *proc_freq; /* /freq entry */ + struct proc_dir_entry *proc_input; /* /input entry */ + struct proc_dir_entry *proc_frame; /* /frame entry */ + struct proc_dir_entry *proc_button; /* /button entry */ + struct proc_dir_entry *proc_control; /* /control entry */ + + /* Statistics that can be overlayed on the screen */ + unsigned long isocUrbCount; // How many URBs we received so far + unsigned long urb_length; /* Length of last URB */ + unsigned long isocDataCount; /* How many bytes we received */ + unsigned long header_count; /* How many frame headers we found */ + unsigned long scratch_ovf_count; /* How many times we overflowed scratch */ + unsigned long isocSkipCount; /* How many empty ISO packets received */ + unsigned long isocErrCount; /* How many bad ISO packets received */ + unsigned long isocPacketCount; // How many packets we totally got + unsigned long timeInIrq; // How long do we need for interrupt + int isocMeasureBandwidthCount; + int frame_num; // How many video frames we send to user + int maxStripLen; // How big is the biggest strip + int comprBlockPos; + int stripLenErrors; // How many times was BlockPos greater than StripLen + int stripMagicErrors; + int stripLineNumberErrors; + int ComprBlockTypes[4]; +}; + +#endif /* __LINUX_USBVISION_H */ + diff --git a/drivers/media/video/usbvision/usbvision_ioctl.h b/drivers/media/video/usbvision/usbvision_ioctl.h new file mode 100644 index 0000000..7204c5c --- /dev/null +++ b/drivers/media/video/usbvision/usbvision_ioctl.h @@ -0,0 +1,34 @@ +/* + * USBVISION_IOCTL.H + * IOCTL for usbvision + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + * + * 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. + */ + + +struct usbvision_reg { + unsigned char addr; + unsigned char value; +}; + +#define UVIOCGREG _IOWR('u',240,struct usbvision_reg) // get register +#define UVIOCSREG _IOW('u',241,struct usbvision_reg) // set register +#define UVIOCSVINM _IOW('u',242,int) // set usbvision vin_mode + -- cgit v0.10.2 From 6714b01263b06b2e7d054625486451a9fd8e03f4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 4 Dec 2006 08:31:01 -0300 Subject: V4L/DVB (4923): Splitted usbvision cards from usbvison.h Having the cards description into a separated file makes easier to maintain and follows the same standard as other drivers. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile index bad2eee..ebdf78a 100644 --- a/drivers/media/video/usbvision/Makefile +++ b/drivers/media/video/usbvision/Makefile @@ -1,4 +1,4 @@ -usbvision-objs := usbvision-core.o usbvision-i2c.o +usbvision-objs := usbvision-core.o usbvision-i2c.o usbvision-cards.o obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c new file mode 100644 index 0000000..16c8890 --- /dev/null +++ b/drivers/media/video/usbvision/usbvision-cards.c @@ -0,0 +1,158 @@ +/* + * USBVISION.H + * usbvision header file + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * + * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden + * + * 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 +#include +#include +#include +#include "usbvision.h" + +/* Supported Devices: A table for usbvision.c*/ + +struct usbvision_device_data_st usbvision_device_data[] = { + {0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"}, + {0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Xanboo"}, + {0x050D, 0x0208, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Belkin USBView II"}, + {0x0571, 0x0002, 0, CODEC_SAA7111, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, -1, -1, 7, "echoFX InterView Lite"}, + {0x0573, 0x0003, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "USBGear USBG-V1 resp. HAMA USB"}, + {0x0573, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "D-Link V100"}, + {0x0573, 0x2000, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "X10 USB Camera"}, + {0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, -1, 3, 7, "Osprey 50"}, + {0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Hauppauge USB-Live Model 600"}, + {0x0573, 0x2101, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 2, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Zoran Co. PMD (Nogatech) AV-grabber Manhattan"}, + {0x0573, 0x4100, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Nogatech USB-TV (NTSC) FM"}, + {0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"}, + {0x0573, 0x4450, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "PixelView PlayTv-USB PRO (PAL) FM"}, + {0x0573, 0x4550, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "ZTV ZT-721 2.4GHz USB A/V Receiver"}, + {0x0573, 0x4d00, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Hauppauge WinTv-USB USA"}, + {0x0573, 0x4d01, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, + {0x0573, 0x4d02, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC)"}, + {0x0573, 0x4d03, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (SECAM) "}, + {0x0573, 0x4d10, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC) FM"}, + {0x0573, 0x4d11, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, + {0x0573, 0x4d12, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, + {0x0573, 0x4d2a, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B285"}, + {0x0573, 0x4d2b, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B282"}, + {0x0573, 0x4d2c, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (PAL/SECAM) 40209 Rev E1A5"}, + {0x0573, 0x4d20, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226"}, + {0x0573, 0x4d21, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL)"}, + {0x0573, 0x4d22, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) MODEL 566"}, + {0x0573, 0x4d23, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) 4D23"}, + {0x0573, 0x4d25, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B234"}, + {0x0573, 0x4d26, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B243"}, + {0x0573, 0x4d27, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B281"}, + {0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B283"}, + {0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40205 Rev B298"}, + {0x0573, 0x4d30, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB FM Model 40211 Rev B123"}, + {0x0573, 0x4d31, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 568"}, + {0x0573, 0x4d32, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 573"}, + {0x0573, 0x4d35, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 40219 Rev B252"}, + {0x0573, 0x4d37, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTV USB device Model 40219 Rev E189"}, + {0x0768, 0x0006, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 5, 5, -1, "Camtel Technology USB TV Genie Pro FM Model TVB330"}, + {0x07d0, 0x0001, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Digital Video Creator I"}, + {0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 82, 20, 7, "Global Village GV-007 (NTSC)"}, + {0x07d0, 0x0003, 0, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)"}, + {0x07d0, 0x0004, 0, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-80 Rev 1 (PAL)"}, + {0x07d0, 0x0005, 0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"}, + {0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"}, + {0x2304, 0x0109, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (SECAM)"}, + {0x2304, 0x0110, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1,128, 23, -1, "Pinnacle Studio PCTV USB (PAL) FM"}, + {0x2304, 0x0111, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Miro PCTV USB"}, + {0x2304, 0x0112, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (NTSC) FM"}, + {0x2304, 0x0210, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, + {0x2304, 0x0212, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_TEMIC_4039FR5_NTSC, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (NTSC) FM"}, + {0x2304, 0x0214, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, + {0x2304, 0x0300, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (NTSC)"}, + {0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (PAL)"}, + {0x2304, 0x0419, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle PCTV Bungee USB (PAL) FM"}, + {0x2400, 0x4200, -1, CODEC_SAA7111, 3, VIDEO_MODE_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, + {} /* Terminating entry */ +}; + +/* Supported Devices */ + +struct usb_device_id usbvision_table [] = { + { USB_DEVICE(0xFFF0, 0xFFF0) }, /* Custom Dummy USBVision Device */ + { USB_DEVICE(0x0A6F, 0x0400) }, /* Xanboo */ + { USB_DEVICE(0x050d, 0x0208) }, /* Belkin USBView II */ + { USB_DEVICE(0x0571, 0x0002) }, /* echoFX InterView Lite */ + { USB_DEVICE(0x0573, 0x0003) }, /* USBGear USBG-V1 */ + { USB_DEVICE(0x0573, 0x0400) }, /* D-Link V100 */ + { USB_DEVICE(0x0573, 0x2000) }, /* X10 USB Camera */ + { USB_DEVICE(0x0573, 0x2d00) }, /* Osprey 50 */ + { USB_DEVICE(0x0573, 0x2d01) }, /* Hauppauge USB-Live Model 600 */ + { USB_DEVICE(0x0573, 0x2101) }, /* Zoran Co. PMD (Nogatech) AV-grabber Manhattan */ + { USB_DEVICE(0x0573, 0x4100) }, /* Nogatech USB-TV FM (NTSC) */ + { USB_DEVICE(0x0573, 0x4110) }, /* PNY USB-TV (NTSC) FM */ + { USB_DEVICE(0x0573, 0x4450) }, /* PixelView PlayTv-USB PRO (PAL) FM */ + { USB_DEVICE(0x0573, 0x4550) }, /* ZTV ZT-721 2.4GHz USB A/V Receiver */ + { USB_DEVICE(0x0573, 0x4d00) }, /* Hauppauge WinTv-USB USA */ + { USB_DEVICE(0x0573, 0x4d01) }, /* Hauppauge WinTv-USB */ + { USB_DEVICE(0x0573, 0x4d02) }, /* Hauppauge WinTv-USB UK */ + { USB_DEVICE(0x0573, 0x4d03) }, /* Hauppauge WinTv-USB France */ + { USB_DEVICE(0x0573, 0x4d10) }, /* Hauppauge WinTv-USB with FM USA radio */ + { USB_DEVICE(0x0573, 0x4d11) }, /* Hauppauge WinTv-USB (PAL) with FM radio */ + { USB_DEVICE(0x0573, 0x4d12) }, /* Hauppauge WinTv-USB UK with FM Radio */ + { USB_DEVICE(0x0573, 0x4d2a) }, /* Hauppague WinTv USB Model 602 40201 Rev B285 */ + { USB_DEVICE(0x0573, 0x4d2b) }, /* Hauppague WinTv USB Model 602 40201 Rev B282 */ + { USB_DEVICE(0x0573, 0x4d2c) }, /* Hauppague WinTv USB Model 40209 Rev. E1A5 PAL*/ + { USB_DEVICE(0x0573, 0x4d20) }, /* Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226 */ + { USB_DEVICE(0x0573, 0x4d21) }, /* Hauppauge WinTv-USB II (PAL) with FM radio*/ + { USB_DEVICE(0x0573, 0x4d22) }, /* Hauppauge WinTv-USB II (PAL) Model 566 */ + { USB_DEVICE(0x0573, 0x4d23) }, /* Hauppauge WinTv-USB France 4D23*/ + { USB_DEVICE(0x0573, 0x4d25) }, /* Hauppauge WinTv-USB Model 40209 rev B234 */ + { USB_DEVICE(0x0573, 0x4d26) }, /* Hauppauge WinTv-USB Model 40209 Rev B243 */ + { USB_DEVICE(0x0573, 0x4d27) }, /* Hauppauge WinTv-USB Model 40204 Rev B281 */ + { USB_DEVICE(0x0573, 0x4d28) }, /* Hauppauge WinTv-USB Model 40204 Rev B283 */ + { USB_DEVICE(0x0573, 0x4d29) }, /* Hauppauge WinTv-USB Model 40205 Rev B298 */ + { USB_DEVICE(0x0573, 0x4d30) }, /* Hauppauge WinTv-USB FM Model 40211 Rev B123 */ + { USB_DEVICE(0x0573, 0x4d31) }, /* Hauppauge WinTv-USB III (PAL) with FM radio Model 568 */ + { USB_DEVICE(0x0573, 0x4d32) }, /* Hauppauge WinTv-USB III (PAL) FM Model 573 */ + { USB_DEVICE(0x0573, 0x4d35) }, /* Hauppauge WinTv-USB III (SECAM) FM Model 40219 Rev B252 */ + { USB_DEVICE(0x0573, 0x4d37) }, /* Hauppauge WinTv-USB Model 40219 Rev E189 */ + { USB_DEVICE(0x0768, 0x0006) }, /* Camtel Technology USB TV Genie Pro FM Model TVB330 */ + { USB_DEVICE(0x07d0, 0x0001) }, /* Digital Video Creator I */ + { USB_DEVICE(0x07d0, 0x0002) }, /* Global Village GV-007 (NTSC) */ + { USB_DEVICE(0x07d0, 0x0003) }, /* Dazzle Fusion Model DVC-50 Rev 1 (NTSC) */ + { USB_DEVICE(0x07d0, 0x0004) }, /* Dazzle Fusion Model DVC-80 Rev 1 (PAL) */ + { USB_DEVICE(0x07d0, 0x0005) }, /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */ + { USB_DEVICE(0x2304, 0x010d) }, /* Pinnacle Studio PCTV USB (PAL) */ + { USB_DEVICE(0x2304, 0x0109) }, /* Pinnacle Studio PCTV USB (SECAM) */ + { USB_DEVICE(0x2304, 0x0110) }, /* Pinnacle Studio PCTV USB (PAL) */ + { USB_DEVICE(0x2304, 0x0111) }, /* Miro PCTV USB */ + { USB_DEVICE(0x2304, 0x0112) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ + { USB_DEVICE(0x2304, 0x0210) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ + { USB_DEVICE(0x2304, 0x0212) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ + { USB_DEVICE(0x2304, 0x0214) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ + { USB_DEVICE(0x2304, 0x0300) }, /* Pinnacle Studio Linx Video input cable (NTSC) */ + { USB_DEVICE(0x2304, 0x0301) }, /* Pinnacle Studio Linx Video input cable (PAL) */ + { USB_DEVICE(0x2304, 0x0419) }, /* Pinnacle PCTV Bungee USB (PAL) FM */ + + { USB_DEVICE(0x2400, 0x4200) }, /* Hauppauge WinTv-USB2 Model 42012 */ + + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE (usb, usbvision_table); diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 62699ca..a23fcd1 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -339,12 +339,10 @@ #include #include #include -#include #include #include #include #include -#include "usbvision-i2c.h" #define USBVISION_DRIVER_VERSION_MAJOR 0 #define USBVISION_DRIVER_VERSION_MINOR 8 @@ -428,6 +426,18 @@ enum { ISOC_MODE_COMPRESS = 0x60, }; +static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { + { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" }, + { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" }, + { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" }, + { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" }, + { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" }, + { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" }, + { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, // 1.5 ! + { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" } +}; + + /* * The value of 'scratch_buf_size' affects quality of the picture * in many ways. Shorter buffers may cause loss of data when client @@ -5691,8 +5701,6 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) } -MODULE_DEVICE_TABLE (usb, usbvision_table); - static struct usb_driver usbvision_driver = { .name = "usbvision", .id_table = usbvision_table, diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index b03e4ea..813b258 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -27,6 +27,8 @@ #define __LINUX_USBVISION_H #include +#include +#include "usbvision-i2c.h" #ifndef VID_HARDWARE_USBVISION #define VID_HARDWARE_USBVISION 34 /* USBVision Video Grabber */ @@ -244,21 +246,12 @@ struct usbvision_sbuf { #define USBVISION_IIC_LRNACK 0x30 #define USBVISION_FRAME_FORMAT_PARAM_INTRA (1<<7) -static struct usbvision_v4l2_format_st { +struct usbvision_v4l2_format_st { int supported; int bytes_per_pixel; int depth; int format; char *desc; -} usbvision_v4l2_format[] = { - { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" }, - { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" }, - { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" }, - { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" }, - { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" }, - { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" }, - { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, // 1.5 ! - { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" } }; #define USBVISION_SUPPORTED_PALETTES ARRAY_SIZE(usbvision_v4l2_format) @@ -309,153 +302,31 @@ struct usbvision_frame { #define BRIDGE_NT1004 1004 #define BRIDGE_NT1005 1005 -/* Supported Devices: A table for usbvision.c*/ - -static struct usbvision_device_data_st { - int idVendor; - int idProduct; - int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ - int Codec; - int VideoChannels; - __u64 VideoNorm; - int AudioChannels; - int Radio; - int vbi; - int Tuner; - int TunerType; - int Vin_Reg1; - int Vin_Reg2; - int X_Offset; - int Y_Offset; - int Dvi_yuv; - char *ModelString; -} usbvision_device_data[] = { - {0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"}, - {0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Xanboo"}, - {0x050D, 0x0208, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Belkin USBView II"}, - {0x0571, 0x0002, 0, CODEC_SAA7111, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, -1, -1, 7, "echoFX InterView Lite"}, - {0x0573, 0x0003, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "USBGear USBG-V1 resp. HAMA USB"}, - {0x0573, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "D-Link V100"}, - {0x0573, 0x2000, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "X10 USB Camera"}, - {0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, -1, 3, 7, "Osprey 50"}, - {0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Hauppauge USB-Live Model 600"}, - {0x0573, 0x2101, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 2, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Zoran Co. PMD (Nogatech) AV-grabber Manhattan"}, - {0x0573, 0x4100, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Nogatech USB-TV (NTSC) FM"}, - {0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"}, - {0x0573, 0x4450, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "PixelView PlayTv-USB PRO (PAL) FM"}, - {0x0573, 0x4550, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "ZTV ZT-721 2.4GHz USB A/V Receiver"}, - {0x0573, 0x4d00, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Hauppauge WinTv-USB USA"}, - {0x0573, 0x4d01, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, - {0x0573, 0x4d02, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC)"}, - {0x0573, 0x4d03, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (SECAM) "}, - {0x0573, 0x4d10, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC) FM"}, - {0x0573, 0x4d11, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, - {0x0573, 0x4d12, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, - {0x0573, 0x4d2a, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B285"}, - {0x0573, 0x4d2b, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B282"}, - {0x0573, 0x4d2c, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (PAL/SECAM) 40209 Rev E1A5"}, - {0x0573, 0x4d20, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226"}, - {0x0573, 0x4d21, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL)"}, - {0x0573, 0x4d22, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) MODEL 566"}, - {0x0573, 0x4d23, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) 4D23"}, - {0x0573, 0x4d25, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B234"}, - {0x0573, 0x4d26, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B243"}, - {0x0573, 0x4d27, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B281"}, - {0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B283"}, - {0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40205 Rev B298"}, - {0x0573, 0x4d30, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB FM Model 40211 Rev B123"}, - {0x0573, 0x4d31, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 568"}, - {0x0573, 0x4d32, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 573"}, - {0x0573, 0x4d35, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 40219 Rev B252"}, - {0x0573, 0x4d37, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTV USB device Model 40219 Rev E189"}, - {0x0768, 0x0006, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 5, 5, -1, "Camtel Technology USB TV Genie Pro FM Model TVB330"}, - {0x07d0, 0x0001, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Digital Video Creator I"}, - {0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 82, 20, 7, "Global Village GV-007 (NTSC)"}, - {0x07d0, 0x0003, 0, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)"}, - {0x07d0, 0x0004, 0, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-80 Rev 1 (PAL)"}, - {0x07d0, 0x0005, 0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"}, - {0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"}, - {0x2304, 0x0109, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (SECAM)"}, - {0x2304, 0x0110, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1,128, 23, -1, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0111, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Miro PCTV USB"}, - {0x2304, 0x0112, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (NTSC) FM"}, - {0x2304, 0x0210, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0212, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_TEMIC_4039FR5_NTSC, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (NTSC) FM"}, - {0x2304, 0x0214, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0300, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (NTSC)"}, - {0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (PAL)"}, - {0x2304, 0x0419, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle PCTV Bungee USB (PAL) FM"}, - {0x2400, 0x4200, -1, CODEC_SAA7111, 3, VIDEO_MODE_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, - {} /* Terminating entry */ -}; - +#define USBVISION_I2C_CLIENTS_MAX 8 -/* Supported Devices: A table for the usb.c*/ - -static struct usb_device_id usbvision_table [] = { - { USB_DEVICE(0xFFF0, 0xFFF0) }, /* Custom Dummy USBVision Device */ - { USB_DEVICE(0x0A6F, 0x0400) }, /* Xanboo */ - { USB_DEVICE(0x050d, 0x0208) }, /* Belkin USBView II */ - { USB_DEVICE(0x0571, 0x0002) }, /* echoFX InterView Lite */ - { USB_DEVICE(0x0573, 0x0003) }, /* USBGear USBG-V1 */ - { USB_DEVICE(0x0573, 0x0400) }, /* D-Link V100 */ - { USB_DEVICE(0x0573, 0x2000) }, /* X10 USB Camera */ - { USB_DEVICE(0x0573, 0x2d00) }, /* Osprey 50 */ - { USB_DEVICE(0x0573, 0x2d01) }, /* Hauppauge USB-Live Model 600 */ - { USB_DEVICE(0x0573, 0x2101) }, /* Zoran Co. PMD (Nogatech) AV-grabber Manhattan */ - { USB_DEVICE(0x0573, 0x4100) }, /* Nogatech USB-TV FM (NTSC) */ - { USB_DEVICE(0x0573, 0x4110) }, /* PNY USB-TV (NTSC) FM */ - { USB_DEVICE(0x0573, 0x4450) }, /* PixelView PlayTv-USB PRO (PAL) FM */ - { USB_DEVICE(0x0573, 0x4550) }, /* ZTV ZT-721 2.4GHz USB A/V Receiver */ - { USB_DEVICE(0x0573, 0x4d00) }, /* Hauppauge WinTv-USB USA */ - { USB_DEVICE(0x0573, 0x4d01) }, /* Hauppauge WinTv-USB */ - { USB_DEVICE(0x0573, 0x4d02) }, /* Hauppauge WinTv-USB UK */ - { USB_DEVICE(0x0573, 0x4d03) }, /* Hauppauge WinTv-USB France */ - { USB_DEVICE(0x0573, 0x4d10) }, /* Hauppauge WinTv-USB with FM USA radio */ - { USB_DEVICE(0x0573, 0x4d11) }, /* Hauppauge WinTv-USB (PAL) with FM radio */ - { USB_DEVICE(0x0573, 0x4d12) }, /* Hauppauge WinTv-USB UK with FM Radio */ - { USB_DEVICE(0x0573, 0x4d2a) }, /* Hauppague WinTv USB Model 602 40201 Rev B285 */ - { USB_DEVICE(0x0573, 0x4d2b) }, /* Hauppague WinTv USB Model 602 40201 Rev B282 */ - { USB_DEVICE(0x0573, 0x4d2c) }, /* Hauppague WinTv USB Model 40209 Rev. E1A5 PAL*/ - { USB_DEVICE(0x0573, 0x4d20) }, /* Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226 */ - { USB_DEVICE(0x0573, 0x4d21) }, /* Hauppauge WinTv-USB II (PAL) with FM radio*/ - { USB_DEVICE(0x0573, 0x4d22) }, /* Hauppauge WinTv-USB II (PAL) Model 566 */ - { USB_DEVICE(0x0573, 0x4d23) }, /* Hauppauge WinTv-USB France 4D23*/ - { USB_DEVICE(0x0573, 0x4d25) }, /* Hauppauge WinTv-USB Model 40209 rev B234 */ - { USB_DEVICE(0x0573, 0x4d26) }, /* Hauppauge WinTv-USB Model 40209 Rev B243 */ - { USB_DEVICE(0x0573, 0x4d27) }, /* Hauppauge WinTv-USB Model 40204 Rev B281 */ - { USB_DEVICE(0x0573, 0x4d28) }, /* Hauppauge WinTv-USB Model 40204 Rev B283 */ - { USB_DEVICE(0x0573, 0x4d29) }, /* Hauppauge WinTv-USB Model 40205 Rev B298 */ - { USB_DEVICE(0x0573, 0x4d30) }, /* Hauppauge WinTv-USB FM Model 40211 Rev B123 */ - { USB_DEVICE(0x0573, 0x4d31) }, /* Hauppauge WinTv-USB III (PAL) with FM radio Model 568 */ - { USB_DEVICE(0x0573, 0x4d32) }, /* Hauppauge WinTv-USB III (PAL) FM Model 573 */ - { USB_DEVICE(0x0573, 0x4d35) }, /* Hauppauge WinTv-USB III (SECAM) FM Model 40219 Rev B252 */ - { USB_DEVICE(0x0573, 0x4d37) }, /* Hauppauge WinTv-USB Model 40219 Rev E189 */ - { USB_DEVICE(0x0768, 0x0006) }, /* Camtel Technology USB TV Genie Pro FM Model TVB330 */ - { USB_DEVICE(0x07d0, 0x0001) }, /* Digital Video Creator I */ - { USB_DEVICE(0x07d0, 0x0002) }, /* Global Village GV-007 (NTSC) */ - { USB_DEVICE(0x07d0, 0x0003) }, /* Dazzle Fusion Model DVC-50 Rev 1 (NTSC) */ - { USB_DEVICE(0x07d0, 0x0004) }, /* Dazzle Fusion Model DVC-80 Rev 1 (PAL) */ - { USB_DEVICE(0x07d0, 0x0005) }, /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */ - { USB_DEVICE(0x2304, 0x010d) }, /* Pinnacle Studio PCTV USB (PAL) */ - { USB_DEVICE(0x2304, 0x0109) }, /* Pinnacle Studio PCTV USB (SECAM) */ - { USB_DEVICE(0x2304, 0x0110) }, /* Pinnacle Studio PCTV USB (PAL) */ - { USB_DEVICE(0x2304, 0x0111) }, /* Miro PCTV USB */ - { USB_DEVICE(0x2304, 0x0112) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ - { USB_DEVICE(0x2304, 0x0210) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ - { USB_DEVICE(0x2304, 0x0212) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ - { USB_DEVICE(0x2304, 0x0214) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ - { USB_DEVICE(0x2304, 0x0300) }, /* Pinnacle Studio Linx Video input cable (NTSC) */ - { USB_DEVICE(0x2304, 0x0301) }, /* Pinnacle Studio Linx Video input cable (PAL) */ - { USB_DEVICE(0x2304, 0x0419) }, /* Pinnacle PCTV Bungee USB (PAL) FM */ - - { USB_DEVICE(0x2400, 0x4200) }, /* Hauppauge WinTv-USB2 Model 42012 */ - - { } /* Terminating entry */ +struct usbvision_device_data_st { + int idVendor; + int idProduct; + int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ + int Codec; + int VideoChannels; + __u64 VideoNorm; + int AudioChannels; + int Radio; + int vbi; + int Tuner; + int TunerType; + int Vin_Reg1; + int Vin_Reg2; + int X_Offset; + int Y_Offset; + int Dvi_yuv; + char *ModelString; }; - -#define USBVISION_I2C_CLIENTS_MAX 8 +/* Declared on usbvision-cards.c */ +extern struct usbvision_device_data_st usbvision_device_data[]; +extern struct usb_device_id usbvision_table[]; struct usb_usbvision { struct video_device *vdev; /* Video Device */ -- cgit v0.10.2 From c8400c700723936d9333c977e51fa77062d26dd4 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Mon, 4 Dec 2006 08:31:04 -0300 Subject: V4L/DVB (4924): Fix some bugs on usbvision due to the merge into one module Found the bug that prevented the driver from loading : a module param conflict between usbvision-i2c and usbvision-core (debug parameter). - correct the module param "debug" conflics in usbvision-i2c.c and usbvision-core.c - add some debug printouts in usbvision-core.c VDIOC_QBUF/VIDIOC_DQBUF - usbvision-core.c : add vb->field = V4L2_FIELD_NONE in VIDIOC_DQBUF Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index a23fcd1..da422e7 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -4240,6 +4240,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, /* set v4l2_format index */ frame->v4l2_format = usbvision->palette; + PDEBUG(DBG_IOCTL, "VIDIOC_QBUF frame=%d",vb->index); return usbvision_new_frame(usbvision, vb->index); } @@ -4269,8 +4270,8 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, if (vb->index == -1) return -EINVAL; - PDEBUG(DBG_IOCTL, "VIDIOC_DQBUF frame=%d, grabstate=%d", - vb->index, usbvision->frame[vb->index].grabstate); + PDEBUG(DBG_IOCTL, "VIDIOC_DQBUF frame=%d, grabstate=%d, curframeNum=%d", + vb->index, usbvision->frame[vb->index].grabstate,usbvision->curFrameNum); switch (usbvision->frame[vb->index].grabstate) { case FrameState_Unused: @@ -4295,6 +4296,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, errCode = (usbvision->frame[vb->index].grabstate == FrameState_Error) ? -EIO : 0; vb->memory = V4L2_MEMORY_MMAP; vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE; + vb->field = V4L2_FIELD_NONE; vb->sequence = usbvision->frame[vb->index].sequence; usbvision->frame[vb->index].grabstate = FrameState_Unused; break; diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index e050e55..466e11f 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -39,18 +39,14 @@ #include #include "usbvision-i2c.h" -static int debug = 0; +static int debug_i2c_usb = 0; #if defined(module_param) // Showing parameters under SYSFS -module_param (debug, int, 0444); // debug mode of the device driver +module_param (debug_i2c_usb, int, 0444); // debug_i2c_usb mode of the device driver #else -MODULE_PARM(debug, "i"); // debug mode of the device driver +MODULE_PARM(debug_i2c_usb, "i"); // debug_i2c_usb mode of the device driver #endif -MODULE_AUTHOR("Joerg Heckenbach"); -MODULE_DESCRIPTION("I2C algorithm for USB-I2C-bridges"); -MODULE_LICENSE("GPL"); - static inline int try_write_address(struct i2c_adapter *i2c_adap, unsigned char addr, int retries) @@ -71,7 +67,7 @@ static inline int try_write_address(struct i2c_adapter *i2c_adap, break; udelay(adap->udelay); } - if (debug) { + if (debug_i2c_usb) { if (i) { info("%s: Needed %d retries for address %#2x", __FUNCTION__, i, addr); info("%s: Maybe there's no device at this address", __FUNCTION__); @@ -98,7 +94,7 @@ static inline int try_read_address(struct i2c_adapter *i2c_adap, break; udelay(adap->udelay); } - if (debug) { + if (debug_i2c_usb) { if (i) { info("%s: Needed %d retries for address %#2x", __FUNCTION__, i, addr); info("%s: Maybe there's no device at this address", __FUNCTION__); @@ -170,7 +166,7 @@ usb_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) pmsg = &msgs[i]; ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr); if (ret != 0) { - if (debug) { + if (debug_i2c_usb) { info("%s: got NAK from device, message #%d\n", __FUNCTION__, i); } return (ret < 0) ? ret : -EREMOTEIO; @@ -236,7 +232,7 @@ int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap) i2c_add_adapter(adap); - if (debug) { + if (debug_i2c_usb) { info("i2c bus for %s registered", adap->name); } @@ -249,7 +245,7 @@ int usbvision_i2c_usb_del_bus(struct i2c_adapter *adap) i2c_del_adapter(adap); - if (debug) { + if (debug_i2c_usb) { info("i2c bus for %s unregistered", adap->name); } #ifdef MODULE -- cgit v0.10.2 From cc9e595a585a4f067dcfdc67e7c4432702ea28ac Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Mon, 4 Dec 2006 08:31:07 -0300 Subject: V4L/DVB (4925): Corrected and separated the Kconfig for usbvision Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 9365a8d..29a11c1 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -686,17 +686,7 @@ source "drivers/media/video/pvrusb2/Kconfig" source "drivers/media/video/em28xx/Kconfig" -config VIDEO_USBVISION - tristate "USB video devices based on NT1003/1005/1005" - depends on I2C && VIDEO_V4L2 - select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO - ---help--- - There are more than 50 different USB video devices based on - NT1003/1004/1005 USB Bridges. This driver enables using those - devices. - - To compile this driver as a module, choose M here: the - module will be called ovcamchip. +source "drivers/media/video/usbvision/Kconfig" source "drivers/media/video/usbvideo/Kconfig" diff --git a/drivers/media/video/usbvision/Kconfig b/drivers/media/video/usbvision/Kconfig new file mode 100644 index 0000000..fc24ef0 --- /dev/null +++ b/drivers/media/video/usbvision/Kconfig @@ -0,0 +1,12 @@ +config VIDEO_USBVISION + tristate "USB video devices based on Nogatech NT1003/1004/1005" + depends on I2C && VIDEO_V4L2 + select VIDEO_TUNER + select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO + ---help--- + There are more than 50 different USB video devices based on + NT1003/1004/1005 USB Bridges. This driver enables using those + devices. + + To compile this driver as a module, choose M here: the + module will be called usbvision. -- cgit v0.10.2 From 18d8a4540caddaa9a42fb4dbc04c75c4b806278b Mon Sep 17 00:00:00 2001 From: Dwaine Garden Date: Mon, 4 Dec 2006 08:31:11 -0300 Subject: V4L/DVB (4926): Fix USBVision handling of VIDIOC_QUERYCTRL There's a better (and recommended) way for handling VIDIOC_QUERYCTRL. This patch will fix the issue where the hardware control values were invalid and stops the SAA7115 module from complaining about invalid values. saa7115 4-0024: invalid brightness setting 32768 saa7115 4-0024: invalid contrast setting 49152 saa7115 4-0024: invalid hue setting 32768 saa7115 4-0024: invalid saturation setting 32768 Signed-off-by: Dwaine Garden Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index da422e7..f225701 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -1,7 +1,7 @@ /* * USB USBVISION Video device driver 0.9.8.3cvs (For Kernel 2.4.19-2.4.32 + 2.6.0-2.6.16) * - * + * * * Copyright (c) 1999-2005 Joerg Heckenbach * @@ -181,7 +181,7 @@ * June 2, 2004 - 0.9.6 Dwaine Garden * - Fixed sourceforge.net cvs repository. * - Added #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,26) for .owner to help compiling under kernels 2.4.x which do not have the i2c v2.8.x updates. - * - Added device Hauppauge WinTv-USB III (PAL) FM Model 597 to usbvision.h + * - Added device Hauppauge WinTv-USB III (PAL) FM Model 597 to usbvision.h * * July 1, 2004 -0.9.6 Dwaine Garden * - Patch was submitted by Hal Finkel to fix the problem with the tuner not working under kernel 2.6.7. @@ -206,7 +206,7 @@ * - Changed usbvision_muxsel to address the problem with black & white s-video output for NT1004 devices with saa7114 video decoder. Thanks to Emmanuel for the patch and testing. * - Fixed up SECAM devices which could not properly output video. Changes to usbmuxsel. Thanks to Emmanuel for the patch and everyone with a SECAM device which help test. * - Removed some commented out code. Clean up. - * - Tried to fix up the annoying empty directories in the sourceforge.net cvs. Fuck it up again. 8*( + * - Tried to fix up the annoying empty directories in the sourceforge.net cvs. Fuck it up again. 8*( * * November 15, 2004 - 0.9.8 Dwaine Garden * - Release new tar - 0.9.8 on sourceforge.net @@ -228,7 +228,7 @@ * - Better support for mythtv. * * January 2, 2005 - 0.9.8.1cvs Dwaine Garden - * - Setup that you can specify which device is used for video. Default is auto detect next available device number eg. /dev/videoX + * - Setup that you can specify which device is used for video. Default is auto detect next available device number eg. /dev/videoX * - Setup that you can specify which device is used for radio. Default is auto detect next available device number eg. /dev/radioX * - usb_unlink_urb() is deprecated for synchronous unlinks. Using usb_kill_urb instead. * - usbvision_kvirt_to_pa is deprecated. Removed. @@ -251,39 +251,39 @@ * April 20, 2005 - 0.9.8.2cvs Dwaine Garden * - Release lock in usbvision_v4l_read_done. -Thanks to nplanel for the patch. * - Additional comments to the driver. - * - Fixed some spelling mistakes. 8*) + * - Fixed some spelling mistakes. 8*) * * April 23, 2005 - 0.9.8.2cvs Joerg Heckenbach * - Found bug in usbvision line counting. Now there should be no spurious lines in the image any longer. - * - Swapped usbvision_register_video and usbvision_configure_video to fix problem with PowerOnAtOpen=0. + * - Swapped usbvision_register_video and usbvision_configure_video to fix problem with PowerOnAtOpen=0. * Thanks to Erwan Velu * * April 26, 2005 - 0.9.8.2cvs Joerg Heckenbach * - Fixed problem with rmmod module and oppses. Replaced vfree(usbvision->overlay_base) with iounmap(usbvision->overlay_base). - * - Added function usb_get_dev(dev) and ; To help with unloading the module multiple times without crashing. + * - Added function usb_get_dev(dev) and ; To help with unloading the module multiple times without crashing. * (Keep the reference count in kobjects correct) * * June 14, 2005 - 0.9.8.2cvs Dwaine - * - Missed a change in saa7113.c for checking kernel version. Added conditional if's. + * - Missed a change in saa7113.c for checking kernel version. Added conditional if's. * * June 15, 2005 - 0.9.8.2cvs Dwaine - * - Added new device WinTV device VendorId 0573 and ProductId 4d29. + * - Added new device WinTV device VendorId 0573 and ProductId 4d29. * - Hacked some support for newer NT1005 devices. This devices only seem to have one configuration, not multiple configurations like the NT1004. * * June 29, 2005 - 0.9.8.2cvs Dwaine - * - Added new device WinTV device VendorId 0573 and ProductId 4d37. - * - Because of the first empty entry in usbvision_table, modutils failed to create the necessary entries for modules.usbmap. - * This means hotplug won't work for usbvision. Thanks to Gary Ng. + * - Added new device WinTV device VendorId 0573 and ProductId 4d37. + * - Because of the first empty entry in usbvision_table, modutils failed to create the necessary entries for modules.usbmap. + * This means hotplug won't work for usbvision. Thanks to Gary Ng. * - Sent an e-mail to the maintainer of usb.ids. New devices identified need to be added. - * - Fixed compile error with saa7113 under kernel 2.6.12. + * - Fixed compile error with saa7113 under kernel 2.6.12. * * July 6, 2005 - 0.9.8.2cvs Dwaine - * - Patch submitted by Gary Ng for two additional procfs entries. Device Input and Frequency setting. + * - Patch submitted by Gary Ng for two additional procfs entries. Device Input and Frequency setting. * * July 12, 2005 - 0.9.8.2cvs Dwaine * - New tuner identified for some devices it's called TCL_MFPE05. This tuner uses the same API as tuner 38 in tuner.c. * - Thanks to lynx31 for contacting Hauppage and asking them. - * - I have no clue as to which devices use this new tuner, so people will have to contact me and tell me. + * - I have no clue as to which devices use this new tuner, so people will have to contact me and tell me. * * July 21, 2005 - 0.9.8.2cvs Dwaine * - Patched usbvision.c with missing ifdef kernversion statement so the module will compile with older kernels and v4l. @@ -305,11 +305,11 @@ * -Changed number of bytes for i2c write to 4 as per the NT100X spec sheet. Thanks to Merlum for finding it. * -Remove the radio option for device Hauppauge WinTV USB device Model 40219 Rev E189. This device does not have a FM radio. Thanks to Shadwell. * -Added radio option for device Hauppauge WinTV USB device Model 40219 Rev E189 again. Just got an e-mail indicating their device has one. 8*) - * + * * Aug 27, 2006 - 0.9.8.3cvs Dwaine * -Changed ifdef statement so the usbvision driver will compile with kernels at 2.6.12. * -Updated readme files for new updated tuner list for v4l devices. - * + * * * * TODO: @@ -351,8 +351,8 @@ #define USBVISION_VERSION __stringify(USBVISION_DRIVER_VERSION_MAJOR) "." __stringify(USBVISION_DRIVER_VERSION_MINOR) "." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL) " " USBVISION_DRIVER_VERSION_COMMENT #define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,USBVISION_DRIVER_VERSION_MINOR,USBVISION_DRIVER_VERSION_PATCHLEVEL) -#include -#include +#include +#include #include #include @@ -427,14 +427,14 @@ enum { }; static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { - { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" }, - { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" }, - { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" }, - { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" }, - { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" }, - { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" }, - { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, // 1.5 ! - { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" } + { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" }, + { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" }, + { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" }, + { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" }, + { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" }, + { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" }, + { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, // 1.5 ! + { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" } }; @@ -448,11 +448,6 @@ static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { #define DEFAULT_SCRATCH_BUF_SIZE (0x20000) // 128kB memory scratch buffer static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE; -static int init_brightness = 128; // Initalize the brightness of the video device -static int init_contrast = 192; // Initalize the contrast of the video device -static int init_saturation = 128; // Initalize the staturation mode of the video device -static int init_hue = 128; // Initalize the Hue settings of the video device - // Function prototypes static int usbvision_restart_isoc(struct usb_usbvision *usbvision); static int usbvision_begin_streaming(struct usb_usbvision *usbvision); @@ -466,9 +461,9 @@ static int usbvision_unrequest_intra (struct usb_usbvision *usbvision); static int usbvision_adjust_compression (struct usb_usbvision *usbvision); static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision); static void usbvision_release(struct usb_usbvision *usbvision); -static int usbvision_set_input(struct usb_usbvision *usbvision); -static int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height); -static void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, void *arg); +static int usbvision_set_input(struct usb_usbvision *usbvision); +static int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height); +static void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, void *arg); // Bit flags (options) @@ -488,8 +483,8 @@ static int adjustCompression = 1; // Set the compression to be adaptive static int dga = 1; // Set the default Direct Graphic Access static int PowerOnAtOpen = 1; // Set the default device to power on at startup static int SwitchSVideoInput = 0; // To help people with Black and White output with using s-video input. Some cables and input device are wired differently. -static int video_nr = -1; // Sequential Number of Video Device -static int radio_nr = -1; // Sequential Number of Radio Device +static int video_nr = -1; // Sequential Number of Video Device +static int radio_nr = -1; // Sequential Number of Radio Device static int vbi_nr = -1; // Sequential Number of VBI Device static char *CustomDevice=NULL; // Set as nothing.... @@ -515,10 +510,10 @@ MODULE_PARM(adjustCompression, "i"); // Grab the compression to be adaptive MODULE_PARM(dga, "i"); // Grab the Direct Graphic Access MODULE_PARM(PowerOnAtOpen, "i"); // Grab the device to power on at startup MODULE_PARM(SwitchSVideoInput, "i"); // To help people with Black and White output with using s-video input. Some cables and input device are wired differently. -MODULE_PARM(video_nr, "i"); // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...) +MODULE_PARM(video_nr, "i"); // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...) MODULE_PARM(radio_nr, "i"); // radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...) MODULE_PARM(vbi_nr, "i"); // vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...) -MODULE_PARM(CustomDevice, "s"); // .... CustomDevice +MODULE_PARM(CustomDevice, "s"); // .... CustomDevice #endif MODULE_PARM_DESC(flags, " Set the default Overlay Display mode of the device driver. Default: 0 (Off)"); @@ -539,7 +534,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE(DRIVER_LICENSE); MODULE_VERSION(DRIVER_VERSION); - MODULE_ALIAS(DRIVER_ALIAS); + MODULE_ALIAS(DRIVER_ALIAS); #ifdef MODULE static unsigned int autoload = 1; @@ -549,8 +544,8 @@ static unsigned int autoload = 0; /****************************************************************************************/ -/* SYSFS Code - Copied from the stv680.c usb module. */ -/* Device information is located at /sys/class/video4linux/video0 */ +/* SYSFS Code - Copied from the stv680.c usb module. */ +/* Device information is located at /sys/class/video4linux/video0 */ /* Device parameters information is located at /sys/module/usbvision */ /* Device USB Information is located at /sys/bus/usb/drivers/USBVision Video Grabber */ /****************************************************************************************/ @@ -567,79 +562,79 @@ static inline struct usb_usbvision *cd_to_usbvision(struct class_device *cd) static ssize_t show_version(struct class_device *cd, char *buf) { return sprintf(buf, "%s\n", DRIVER_VERSION); -} +} static CLASS_DEVICE_ATTR(version, S_IRUGO, show_version, NULL); static ssize_t show_model(struct class_device *class_dev, char *buf) { - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%s\n", usbvision_device_data[usbvision->DevModel].ModelString); -} +} static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL); static ssize_t show_hue(struct class_device *class_dev, char *buf) { - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%d\n", usbvision->hue >> 8); -} +} static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); static ssize_t show_contrast(struct class_device *class_dev, char *buf) { - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%d\n", usbvision->contrast >> 8); -} +} static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); static ssize_t show_brightness(struct class_device *class_dev, char *buf) { - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%d\n", usbvision->brightness >> 8); -} +} static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); static ssize_t show_saturation(struct class_device *class_dev, char *buf) { - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%d\n", usbvision->saturation >> 8); -} +} static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); static ssize_t show_streaming(struct class_device *class_dev, char *buf) { - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%s\n", YES_NO(usbvision->streaming)); -} +} static CLASS_DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL); static ssize_t show_overlay(struct class_device *class_dev, char *buf) { - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%s\n", YES_NO(usbvision->overlay)); -} +} static CLASS_DEVICE_ATTR(overlay, S_IRUGO, show_overlay, NULL); static ssize_t show_compression(struct class_device *class_dev, char *buf) { - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%s\n", YES_NO(usbvision->isocMode==ISOC_MODE_COMPRESS)); -} +} static CLASS_DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL); static ssize_t show_device_bridge(struct class_device *class_dev, char *buf) { - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%d\n", usbvision->bridgeType); -} +} static CLASS_DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL); static void usbvision_create_sysfs(struct video_device *vdev) @@ -1195,9 +1190,9 @@ void usbvision_osd_stats(struct usb_usbvision *usbvision, * * Parameters: * fullframe: if TRUE then entire frame is filled, otherwise the procedure - * continues from the current scanline. + * continues from the current scanline. * pmode 0: fill the frame with solid blue color (like on VCR or TV) - * 1: Draw a colored grid + * 1: Draw a colored grid * */ void usbvision_testpattern(struct usb_usbvision *usbvision, int fullframe, @@ -1608,7 +1603,7 @@ static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision, if (overlay) { frame = &usbvision->overlay_frame; - imageSize = frame->frmwidth * frame->frmheight; + imageSize = frame->frmwidth * frame->frmheight; if (usbvision->overlay_base == NULL) { //video_buffer is not set yet return ParseState_NextFrame; @@ -1618,26 +1613,26 @@ static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision, } else { frame = &usbvision->frame[usbvision->curFrameNum]; - imageSize = frame->frmwidth * frame->frmheight; + imageSize = frame->frmwidth * frame->frmheight; if ( (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) || (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) ) - { // this is a planar format - //... v4l2_linesize not used here. + { // this is a planar format + //... v4l2_linesize not used here. f = frame->data + (frame->width * frame->curline); - } else + } else f = frame->data + (frame->v4l2_linesize * frame->curline); - + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV){ //initialise u and v pointers - // get base of u and b planes add halfoffset - - u = frame->data - + imageSize + // get base of u and b planes add halfoffset + + u = frame->data + + imageSize + (frame->frmwidth >>1) * frame->curline ; v = u + (imageSize >>1 ); - + } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420){ - - v = frame->data + imageSize + ((frame->curline* (frame->width))>>2) ; + + v = frame->data + imageSize + ((frame->curline* (frame->width))>>2) ; u = v + (imageSize >>2) ; } } @@ -1687,7 +1682,7 @@ static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision, return ParseState_NextFrame; } - bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; + bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; clipmask_index = frame->curline * MAX_FRAME_WIDTH; scratch_get(usbvision, StripData, StripLen); @@ -1723,7 +1718,7 @@ static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision, if (BlockPos > StripLen) { usbvision->stripLenErrors++; } - + for (Idx = 0; Idx < IdxEnd; Idx++) { if((overlay) && (clipped_pixel(clipmask_index))) { f += bytes_per_pixel; @@ -1741,15 +1736,15 @@ static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision, } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) { *f++ = Y [Idx]; - if ( !(( Idx & 0x01 ) | ( frame->curline & 0x01 )) ){ - + if ( !(( Idx & 0x01 ) | ( frame->curline & 0x01 )) ){ + /* only need do this for 1 in 4 pixels */ /* intraframe buffer is YUV420 format */ *u++ = U[Idx >>1]; *v++ = V[Idx >>1]; } - + } else { YUV_TO_RGB_BY_THE_BOOK(Y[Idx], U[Idx/2], V[Idx/2], rv, gv, bv); @@ -1780,12 +1775,12 @@ static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision, } clipmask_index++; } - /* Deal with non-integer no. of bytes for YUV420P */ - if (frame->v4l2_format.format != V4L2_PIX_FMT_YVU420 ) - *pcopylen += frame->v4l2_linesize; - else - *pcopylen += frame->curline & 0x01 ? frame->v4l2_linesize : frame->v4l2_linesize << 1; - + /* Deal with non-integer no. of bytes for YUV420P */ + if (frame->v4l2_format.format != V4L2_PIX_FMT_YVU420 ) + *pcopylen += frame->v4l2_linesize; + else + *pcopylen += frame->curline & 0x01 ? frame->v4l2_linesize : frame->v4l2_linesize << 1; + frame->curline += 1; if (frame->curline >= frame->frmheight) { @@ -1846,7 +1841,7 @@ static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision /* Make sure there's enough data for the entire line */ /* In this mode usbvision transfer 3 bytes for every 2 pixels */ /* I need two lines to decode the color */ - bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; + bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; stretch_bytes = (usbvision->stretch_width - 1) * bytes_per_pixel; clipmask_even_index = frame->curline * MAX_FRAME_WIDTH; clipmask_odd_index = clipmask_even_index + MAX_FRAME_WIDTH; @@ -2276,7 +2271,7 @@ static void usbvision_isocIrq(struct urb *urb, struct pt_regs *regs) urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } - urb->status = 0; + urb->status = 0; urb->dev = usbvision->dev; errCode = usb_submit_urb (urb, GFP_ATOMIC); @@ -2562,10 +2557,10 @@ static int attach_inform(struct i2c_client *client) } if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) { tun_addr.mode_mask = T_ANALOG_TV; - tun_addr.type = usbvision->tuner_type; - tun_addr.addr = ADDR_UNSET; - client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_addr); - + tun_addr.type = usbvision->tuner_type; + tun_addr.addr = ADDR_UNSET; + client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_addr); + call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->input.index); } // FIXME : need to add a call VIDIOC_S_CTRL for each control @@ -2733,7 +2728,7 @@ static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, int retval; int wrcount = 0; int count; - int maxLen = 4; + int maxLen = 4; struct usb_usbvision *usbvision = (struct usb_usbvision *) data; while (len > 0) { @@ -2841,7 +2836,7 @@ static int usbvision_init_i2c(struct usb_usbvision *usbvision) break; } if (usbvision_device_data[usbvision->DevModel].Tuner == 1) { - request_module("tuner"); + request_module("tuner"); } } #endif @@ -3001,7 +2996,7 @@ static int usbvision_set_output(struct usb_usbvision *usbvision, int width, } if (usbvision->input.std & V4L2_STD_625_50) { - frameDrop = frameRate * 32 / 25 - 1; + frameDrop = frameRate * 32 / 25 - 1; } else if (usbvision->input.std & V4L2_STD_525_60) { frameDrop = frameRate * 32 / 30 - 1; @@ -3042,7 +3037,7 @@ static int usbvision_set_compress_params(struct usb_usbvision *usbvision) value[2] = 0x00; // Reg.46 Force intra mode on all new frames value[3] = 0x00; // Reg.47 FORCE_UP <- 0 normal operation (not force) value[4] = 0xA2; // Reg.48 BUF_THR I'm not sure if this does something in not compressed mode. - value[5] = 0x00; // Reg.49 DVI_YUV This has nothing to do with compression + value[5] = 0x00; // Reg.49 DVI_YUV This has nothing to do with compression //catched values for NT1004 // value[0] = 0xFF; // Never apply intra mode automatically @@ -3196,7 +3191,7 @@ static int usbvision_set_input(struct usb_usbvision *usbvision) dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv & 0xff; } else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) { - /* This changes as the fine sync control changes. Further investigation necessary */ + /* This changes as the fine sync control changes. Further investigation necessary */ dvi_yuv_value = 0x06; } @@ -3313,7 +3308,7 @@ static void usbvision_powerOffTimer(unsigned long data) del_timer(&usbvision->powerOffTimer); INIT_WORK(&usbvision->powerOffWork, call_usbvision_power_off, usbvision); (void) schedule_work(&usbvision->powerOffWork); - + } @@ -3421,7 +3416,7 @@ static int usbvision_setup(struct usb_usbvision *usbvision) usbvision_set_input(usbvision); usbvision_set_output(usbvision, MAX_USB_WIDTH, MAX_USB_HEIGHT); usbvision_restart_isoc(usbvision); - + /* cosas del PCM */ return USBVISION_IS_OPERATIONAL(usbvision); } @@ -3532,12 +3527,12 @@ static void usbvision_stop_isoc(struct usb_usbvision *usbvision) usb_kill_urb(usbvision->sbuf[bufIdx].urb); usb_free_urb(usbvision->sbuf[bufIdx].urb); usbvision->sbuf[bufIdx].urb = NULL; - } + } PDEBUG(DBG_ISOC, "%s: streaming=0\n", __FUNCTION__); usbvision->streaming = 0; - + if (!usbvision->remove_pending) { @@ -3615,7 +3610,7 @@ static int usbvision_new_frame(struct usb_usbvision *usbvision, int framenum) static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel, int norm) { int mode[4]; - int audio[]= {1, 0, 0, 0}; + int audio[]= {1, 0, 0, 0}; struct v4l2_routing route; //channel 0 is TV with audiochannel 1 (tuner mono) //channel 1 is Composite with audio channel 0 (line in) @@ -3636,7 +3631,7 @@ static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel, int no // set the new channel // Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video - // Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red + // Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red switch (usbvision_device_data[usbvision->DevModel].Codec) { case CODEC_SAA7113: @@ -3646,10 +3641,10 @@ static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel, int no else { mode[2] = 7; } - if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { + if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { mode[0] = 0; mode[1] = 2; mode[3] = 3; // Special for four input devices } - else { + else { mode[0] = 0; mode[1] = 2; //modes for regular saa7113 devices } break; @@ -3748,7 +3743,7 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) err("%s: unable to allocate %d for compr. frame buffer", __FUNCTION__, IFB_size); errCode = -ENOMEM; } - + } if (errCode) { /* Have to free all that memory */ @@ -3940,8 +3935,8 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, case 0: if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { strcpy(vi->name, "White Video Input"); - } - else { + } + else { strcpy(vi->name, "Television"); vi->type = V4L2_INPUT_TYPE_TUNER; vi->audioset = 1; @@ -3950,30 +3945,30 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, } break; case 1: - vi->type = V4L2_INPUT_TYPE_CAMERA; + vi->type = V4L2_INPUT_TYPE_CAMERA; if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { strcpy(vi->name, "Green Video Input"); - } - else { + } + else { strcpy(vi->name, "Composite Video Input"); } vi->std = V4L2_STD_PAL; break; case 2: - vi->type = V4L2_INPUT_TYPE_CAMERA; + vi->type = V4L2_INPUT_TYPE_CAMERA; if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { strcpy(vi->name, "Yellow Video Input"); - } - else { + } + else { strcpy(vi->name, "S-Video Input"); } vi->std = V4L2_STD_PAL; break; case 3: - vi->type = V4L2_INPUT_TYPE_CAMERA; + vi->type = V4L2_INPUT_TYPE_CAMERA; strcpy(vi->name, "Red Video Input"); vi->std = V4L2_STD_PAL; - break; + break; } PDEBUG(DBG_IOCTL, "VIDIOC_ENUMINPUT name=%s:%d tuners=%d type=%d norm=%x", vi->name, vi->index, vi->tuner,vi->type,(int)vi->std); return 0; @@ -4008,13 +4003,13 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, } return 0; } - case VIDIOC_G_INPUT: + case VIDIOC_G_INPUT: { int *input = arg; *input = usbvision->input.index; return 0; } - case VIDIOC_S_INPUT: + case VIDIOC_S_INPUT: { int *input = arg; if ((*input >= usbvision->video_inputs) || (*input < 0) ) @@ -4028,14 +4023,14 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, up(&usbvision->lock); return 0; } - case VIDIOC_G_STD: + case VIDIOC_G_STD: { v4l2_std_id *std = arg; *std = usbvision->input.std; PDEBUG(DBG_IOCTL, "VIDIOC_G_STD std_id=%x", (unsigned)*std); return 0; } - case VIDIOC_S_STD: + case VIDIOC_S_STD: { v4l2_std_id *std = arg; @@ -4049,7 +4044,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, PDEBUG(DBG_IOCTL, "VIDIOC_S_STD std_id=%x", (unsigned)*std); return 0; } - case VIDIOC_G_TUNER: + case VIDIOC_G_TUNER: { struct v4l2_tuner *vt = arg; struct v4l2_tuner status; @@ -4118,55 +4113,35 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, // FIXME: void function ??? return 0; } - case VIDIOC_QUERYCTRL: + case VIDIOC_QUERYCTRL: { - struct v4l2_queryctrl *qc = arg; - switch(qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_HUE: - case V4L2_CID_SATURATION: - case V4L2_CID_CONTRAST: - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill_std(qc); - break; - default: + struct v4l2_queryctrl *ctrl = arg; + int id=ctrl->id; + + memset(ctrl,0,sizeof(*ctrl)); + ctrl->id=id; + + i2c_clients_command(&usbvision->i2c_adap, cmd, arg); + + if (ctrl->type) + return 0; + else return -EINVAL; - } - return 0; + + PDEBUG(DBG_IOCTL,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type); } case VIDIOC_G_CTRL: { struct v4l2_control *ctrl = arg; - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = usbvision->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = usbvision->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = usbvision->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = usbvision->hue; - break; - case V4L2_CID_AUDIO_VOLUME: - /* ctrl->value = usbvision->volume; */ - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = usbvision->AudioMute; - break; - default: - return -EINVAL; - } - PDEBUG(DBG_IOCTL, "VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); + PDEBUG(DBG_IOCTL,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); + call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); return 0; } case VIDIOC_S_CTRL: { struct v4l2_control *ctrl = arg; + PDEBUG(DBG_IOCTL, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value); call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); return 0; @@ -4213,7 +4188,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, vb->sequence = usbvision->frame_num; return 0; } - case VIDIOC_QBUF: // VIDIOCMCAPTURE + VIDIOCSYNC + case VIDIOC_QBUF: // VIDIOCMCAPTURE + VIDIOCSYNC { struct v4l2_buffer *vb = arg; struct usbvision_frame *frame; @@ -4308,7 +4283,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_STREAMON: { int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; - call_i2c_clients(usbvision,VIDIOC_STREAMON , &b); + call_i2c_clients(usbvision,VIDIOC_STREAMON , &b); return 0; } case VIDIOC_STREAMOFF: @@ -4323,7 +4298,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, up(&usbvision->lock); return 0; } - case VIDIOC_G_FBUF: + case VIDIOC_G_FBUF: { struct v4l2_framebuffer *vb = arg; @@ -4389,7 +4364,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, if ( (dga == 0) && (vfd->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) && (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && - (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { + (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { return -EINVAL; } if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) { @@ -4403,11 +4378,11 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_FMT: { struct v4l2_format *vf = arg; - + if ( (dga == 0) && (vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) && (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && - (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { + (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { return -EINVAL; } down(&usbvision->lock); @@ -4417,7 +4392,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, vf->fmt.win.w.left, vf->fmt.win.w.top, vf->fmt.win.w.width, vf->fmt.win.w.height, vf->fmt.win.chromakey, vf->fmt.win.clipcount); return 0; } - case VIDIOC_S_FMT: + case VIDIOC_S_FMT: { struct v4l2_format *vf = arg; struct v4l2_clip *vc=NULL; @@ -4426,7 +4401,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, if ( (dga == 0) && (vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) && (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && - (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { + (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { return -EINVAL; } if(vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) { @@ -4518,10 +4493,10 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_OVERLAY: { int *v = arg; - + if ( (dga == 0) && (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && - (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { + (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { PDEBUG(DBG_IOCTL, "VIDIOC_OVERLAY DGA disabled"); return -EINVAL; } @@ -4609,7 +4584,7 @@ static ssize_t usbvision_v4l2_read(struct file *file, char *buf, goto usbvision_v4l2_read_done; } PDEBUG(DBG_IO, "Waiting frame grabbing"); - rc = wait_event_interruptible(frame->wq, (frame->grabstate == FrameState_Done) || + rc = wait_event_interruptible(frame->wq, (frame->grabstate == FrameState_Done) || (frame->grabstate == FrameState_Error)); if (rc) { goto usbvision_v4l2_read_done; @@ -4670,11 +4645,11 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) pos = (unsigned long) usbvision->fbuf; while (size > 0) { - + // Really ugly.... - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) //Compatibility for 2.6.10+ kernels + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) //Compatibility for 2.6.10+ kernels page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { return -EAGAIN; } #else //Compatibility for 2.6.0 - 2.6.9 kernels @@ -4791,7 +4766,7 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, /*************************** * V4L2 IOCTLs * ***************************/ - case VIDIOC_QUERYCAP: + case VIDIOC_QUERYCAP: { struct v4l2_capability *vc=arg; memset(vc, 0, sizeof(struct v4l2_capability)); @@ -4803,7 +4778,7 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, PDEBUG(DBG_RIO, "%s: VIDIOC_QUERYCAP", __FUNCTION__); return 0; } - case VIDIOC_QUERYCTRL: + case VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *qc = arg; switch(qc->id) @@ -4817,7 +4792,7 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, } return 0; } - case VIDIOC_G_CTRL: + case VIDIOC_G_CTRL: { struct v4l2_control *ctrl = arg; PDEBUG(DBG_IOCTL, "VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); @@ -4833,7 +4808,7 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, } return 0; } - case VIDIOC_S_CTRL: + case VIDIOC_S_CTRL: { struct v4l2_control *ctrl = arg; call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); @@ -4841,7 +4816,7 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, PDEBUG(DBG_RIO, "%s: VIDIOC_S_CTRL id=%x value=%x", __FUNCTION__,ctrl->id,ctrl->value); return 0; } - case VIDIOC_G_TUNER: + case VIDIOC_G_TUNER: { struct v4l2_tuner *vt = arg; @@ -4862,7 +4837,7 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, PDEBUG(DBG_RIO, "%s: VIDIOC_G_TUNER signal=%d", __FUNCTION__, vt->signal); return 0; } - case VIDIOC_S_TUNER: + case VIDIOC_S_TUNER: { struct v4l2_tuner *vt = arg; @@ -4873,7 +4848,7 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, PDEBUG(DBG_RIO, "%s: VIDIOC_S_TUNER", __FUNCTION__); return 0; } - case VIDIOC_G_AUDIO: + case VIDIOC_G_AUDIO: { struct v4l2_audio *va = arg; memset(va,0, sizeof(va)); @@ -4882,7 +4857,7 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, PDEBUG(DBG_IOCTL, "VIDIOC_G_AUDIO"); return 0; } - case VIDIOC_S_AUDIO: + case VIDIOC_S_AUDIO: { struct v4l2_audio *v = arg; if(v->index) { @@ -4892,7 +4867,7 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, // FIXME: void function ??? return 0; } - case VIDIOC_G_FREQUENCY: + case VIDIOC_G_FREQUENCY: { struct v4l2_frequency *freq = arg; freq->tuner = 0; // Only one tuner @@ -4901,7 +4876,7 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, PDEBUG(DBG_RIO, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)freq->frequency); return 0; } - case VIDIOC_S_FREQUENCY: + case VIDIOC_S_FREQUENCY: { struct v4l2_frequency *freq = arg; usbvision->freq = freq->frequency; @@ -4913,7 +4888,7 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, /*************************** * V4L1 IOCTLs * ***************************/ - case VIDIOCGCAP: + case VIDIOCGCAP: { struct video_capability *vc = arg; @@ -5227,18 +5202,8 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) if (usbvision == NULL) return; - - model = usbvision->DevModel; - RESTRICT_TO_RANGE(init_brightness, 0, 255); - RESTRICT_TO_RANGE(init_contrast, 0, 255); - RESTRICT_TO_RANGE(init_saturation, 0, 255); - RESTRICT_TO_RANGE(init_hue, 0, 255); - - usbvision->saturation = init_saturation << 8; - usbvision->hue = init_hue << 8; - usbvision->brightness = init_brightness << 8; - usbvision->contrast = init_contrast << 8; + model = usbvision->DevModel; usbvision->depth = 24; usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24; @@ -5393,7 +5358,7 @@ static void usbvision_unregister_video(struct usb_usbvision *usbvision) } usbvision->vbi = NULL; } - + // Radio Device: if (usbvision->rdev) { PDEBUG(DBG_PROBE, "unregister /dev/radio%d [v4l2]", usbvision->rdev->minor & 0x1f); @@ -5444,7 +5409,7 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) } info("USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]", usbvision->nr, usbvision->rdev->minor & 0x1f); } - // vbi Device: + // vbi Device: if (usbvision_device_data[usbvision->DevModel].vbi) { usbvision->vbi = usbvision_vdev_init(usbvision, &usbvision_vbi_template, "USBVision VBI"); if (usbvision->vdev == NULL) { @@ -5575,10 +5540,10 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us /* Is it an USBVISION video dev? */ model = 0; for(model = 0; usbvision_device_data[model].idVendor; model++) { - if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) { + if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) { continue; } - if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) { + if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) { continue; } @@ -5607,7 +5572,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us } usb_get_dev(dev); - + if ((usbvision = usbvision_alloc(dev)) == NULL) { err("%s: couldn't allocate USBVision struct", __FUNCTION__); return -ENOMEM; @@ -5617,7 +5582,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us } else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") { usbvision->bridgeType = BRIDGE_NT1005; - } + } else { usbvision->bridgeType = BRIDGE_NT1003; } @@ -5649,7 +5614,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us usb_set_intfdata (intf, usbvision); - usbvision_create_sysfs(usbvision->vdev); + usbvision_create_sysfs(usbvision->vdev); PDEBUG(DBG_PROBE, "success"); return 0; @@ -5724,7 +5689,7 @@ void customdevice_process(void) if(CustomDevice) { char *parse=CustomDevice; - + PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice); /*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1" @@ -5743,9 +5708,9 @@ void customdevice_process(void) usbvision_device_data[0].X_Offset; usbvision_device_data[0].Y_Offset; usbvision_device_data[0].Dvi_yuv; - usbvision_device_data[0].ModelString; + usbvision_device_data[0].ModelString; */ - + rmspace(parse); usbvision_device_data[0].ModelString="USBVISION Custom Device"; @@ -5852,12 +5817,12 @@ static int __init usbvision_init(void) PDEBUG(DBG_FUNC, "FUNC debugging is enabled"); PDEBUG(DBG_I2C, "I2C debugging is enabled"); - /* disable planar mode support unless compression enabled */ - if (isocMode != ISOC_MODE_COMPRESS ) { + /* disable planar mode support unless compression enabled */ + if (isocMode != ISOC_MODE_COMPRESS ) { // FIXME : not the right way to set supported flag usbvision_v4l2_format[6].supported = 0; // V4L2_PIX_FMT_YVU420 usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P - } + } customdevice_process(); -- cgit v0.10.2 From f2242ee5474f46d87a45cd4e214b5c3aa02ff293 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Mon, 4 Dec 2006 08:31:14 -0300 Subject: V4L/DVB (4927): Enhancements on usbvision driver Enhance the buffer management of this driver + some corrections - linux list.h usage for buffer management - VIDIOC_ENUMSTD/VIDIOC_G_STD/VIDIOC_S_STD simplification (use of v4l2_video_std_construct) - create_sysfs : remove of warnings for video_device_create_file return code - make the driver compatible with 2.6.19 kernel version (remove slave_send and slave_recv in usbvision-i2c, change ctrlUrb_complete function prototype) - deactivated v4l2_read because this code was not the priority but working on it :) Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index f225701..06e8e67 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -437,6 +437,25 @@ static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" } }; +/* supported tv norms */ +static struct usbvision_tvnorm tvnorms[] = { + { + .name = "PAL", + .id = V4L2_STD_PAL, + }, { + .name = "NTSC", + .id = V4L2_STD_NTSC, + }, { + .name = "SECAM", + .id = V4L2_STD_SECAM, + }, { + .name = "PAL-M", + .id = V4L2_STD_PAL_M, + } +}; + +#define TVNORMS ARRAY_SIZE(tvnorms) + /* * The value of 'scratch_buf_size' affects quality of the picture @@ -451,7 +470,7 @@ static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE; // Function prototypes static int usbvision_restart_isoc(struct usb_usbvision *usbvision); static int usbvision_begin_streaming(struct usb_usbvision *usbvision); -static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel, int norm); +static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel); static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, short len); static int usbvision_i2c_read(void *data, unsigned char addr, char *buf, short len); static int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg); @@ -463,6 +482,8 @@ static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision); static void usbvision_release(struct usb_usbvision *usbvision); static int usbvision_set_input(struct usb_usbvision *usbvision); static int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height); +static void usbvision_empty_framequeues(struct usb_usbvision *dev); +static int usbvision_stream_interrupt(struct usb_usbvision *dev); static void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, void *arg); @@ -609,7 +630,7 @@ static ssize_t show_streaming(struct class_device *class_dev, char *buf) { struct video_device *vdev = to_video_device(class_dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%s\n", YES_NO(usbvision->streaming)); + return sprintf(buf, "%s\n", YES_NO(usbvision->streaming==Stream_On?1:0)); } static CLASS_DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL); @@ -639,17 +660,18 @@ static CLASS_DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL); static void usbvision_create_sysfs(struct video_device *vdev) { + int res; if (vdev) { - video_device_create_file(vdev, &class_device_attr_version); - video_device_create_file(vdev, &class_device_attr_model); - video_device_create_file(vdev, &class_device_attr_hue); - video_device_create_file(vdev, &class_device_attr_contrast); - video_device_create_file(vdev, &class_device_attr_brightness); - video_device_create_file(vdev, &class_device_attr_saturation); - video_device_create_file(vdev, &class_device_attr_streaming); - video_device_create_file(vdev, &class_device_attr_overlay); - video_device_create_file(vdev, &class_device_attr_compression); - video_device_create_file(vdev, &class_device_attr_bridge); + res=video_device_create_file(vdev, &class_device_attr_version); + res=video_device_create_file(vdev, &class_device_attr_model); + res=video_device_create_file(vdev, &class_device_attr_hue); + res=video_device_create_file(vdev, &class_device_attr_contrast); + res=video_device_create_file(vdev, &class_device_attr_brightness); + res=video_device_create_file(vdev, &class_device_attr_saturation); + res=video_device_create_file(vdev, &class_device_attr_streaming); + res=video_device_create_file(vdev, &class_device_attr_overlay); + res=video_device_create_file(vdev, &class_device_attr_compression); + res=video_device_create_file(vdev, &class_device_attr_bridge); } } @@ -1209,15 +1231,13 @@ void usbvision_testpattern(struct usb_usbvision *usbvision, int fullframe, printk(KERN_ERR "%s: usbvision == NULL\n", proc); return; } - if ((usbvision->curFrameNum < 0) - || (usbvision->curFrameNum >= USBVISION_NUMFRAMES)) { - printk(KERN_ERR "%s: usbvision->curFrameNum=%d.\n", proc, - usbvision->curFrameNum); + if (usbvision->curFrame == NULL) { + printk(KERN_ERR "%s: usbvision->curFrame is NULL.\n", proc); return; } /* Grab the current frame */ - frame = &usbvision->frame[usbvision->curFrameNum]; + frame = usbvision->curFrame; /* Optionally start at the beginning */ if (fullframe) { @@ -1290,7 +1310,7 @@ static enum ParseState usbvision_find_header(struct usb_usbvision *usbvision) frame = &usbvision->overlay_frame; } else { - frame = &usbvision->frame[usbvision->curFrameNum]; + frame = usbvision->curFrame; } while (scratch_get_header(usbvision, &frame->isocHeader) == USBVISION_HEADER_LENGTH) { @@ -1325,7 +1345,6 @@ static enum ParseState usbvision_find_header(struct usb_usbvision *usbvision) frame->frmwidth = frame->isocHeader.frameWidth * usbvision->stretch_width; frame->frmheight = frame->isocHeader.frameHeight * usbvision->stretch_height; frame->v4l2_linesize = (frame->frmwidth * frame->v4l2_format.depth)>> 3; - usbvision->curFrame = frame; } else { // no header found PDEBUG(DBG_HEADER, "skipping scratch data, no header"); @@ -1380,7 +1399,7 @@ static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision usbvision->vid_buf.fmt.bytesperline; } else { - frame = &usbvision->frame[usbvision->curFrameNum]; + frame = usbvision->curFrame; f = frame->data + (frame->v4l2_linesize * frame->curline); } @@ -1612,7 +1631,7 @@ static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision, usbvision->vid_buf.fmt.bytesperline; } else { - frame = &usbvision->frame[usbvision->curFrameNum]; + frame = usbvision->curFrame; imageSize = frame->frmwidth * frame->frmheight; if ( (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) || (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) ) @@ -1833,7 +1852,7 @@ static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision f_odd = f_even + usbvision->vid_buf.fmt.bytesperline * usbvision->stretch_height; } else { - frame = &usbvision->frame[usbvision->curFrameNum]; + frame = usbvision->curFrame; f_even = frame->data + (frame->v4l2_linesize * frame->curline); f_odd = f_even + frame->v4l2_linesize * usbvision->stretch_height; } @@ -2088,17 +2107,17 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) struct usbvision_frame *frame; enum ParseState newstate; long copylen = 0; + unsigned long lock_flags; if (usbvision->overlay) { frame = &usbvision->overlay_frame; } else { - frame = &usbvision->frame[usbvision->curFrameNum]; + frame = usbvision->curFrame; } PDEBUG(DBG_PARSE, "parsing len=%d\n", scratch_len(usbvision)); - while (1) { newstate = ParseState_Out; @@ -2141,7 +2160,10 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) copylen = 0; } else { - usbvision->curFrameNum = -1; + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_move_tail(&(frame->frame), &usbvision->outqueue); + usbvision->curFrame = NULL; + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); } usbvision->frame_num++; @@ -2150,10 +2172,14 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) usbvision_osd_stats(usbvision, frame); /* This will cause the process to request another frame. */ - if (waitqueue_active(&frame->wq)) { - wake_up_interruptible(&frame->wq); + if (waitqueue_active(&usbvision->wait_frame)) { + PDEBUG(DBG_PARSE, "Wake up !"); + wake_up_interruptible(&usbvision->wait_frame); } } + else + frame->grabstate = FrameState_Grabbing; + /* Update the frame's uncompressed length. */ frame->scanlength += copylen; @@ -2164,7 +2190,7 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) * Make all of the blocks of data contiguous */ static int usbvision_compress_isochronous(struct usb_usbvision *usbvision, - struct urb *urb) + struct urb *urb) { unsigned char *packet_data; int i, totlen = 0; @@ -2246,51 +2272,70 @@ static int usbvision_compress_isochronous(struct usb_usbvision *usbvision, static void usbvision_isocIrq(struct urb *urb, struct pt_regs *regs) { - int errCode = 0; - int len; - struct usb_usbvision *usbvision = urb->context; - int i; - unsigned long startTime = jiffies; - - /* We don't want to do anything if we are about to be removed! */ - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return; + int errCode = 0; + int len; + struct usb_usbvision *usbvision = urb->context; + int i; + unsigned long startTime = jiffies; + struct usbvision_frame **f; - if (!usbvision->streaming) { - PDEBUG(DBG_IRQ, "oops, not streaming, but interrupt"); - return; - } + /* We don't want to do anything if we are about to be removed! */ + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return; - /* Copy the data received into our scratch buffer */ - len = usbvision_compress_isochronous(usbvision, urb); + f = &usbvision->curFrame; - usbvision->isocUrbCount++; - usbvision->urb_length = len; + /* Manage streaming interruption */ + if (usbvision->streaming == Stream_Interrupt) { + usbvision->streaming = Stream_Off; + if ((*f)) { + (*f)->grabstate = FrameState_Ready; + (*f)->scanstate = ScanState_Scanning; + } + PDEBUG(DBG_IRQ, "stream interrupted"); + wake_up_interruptible(&usbvision->wait_stream); + } - for (i = 0; i < USBVISION_URB_FRAMES; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - urb->status = 0; - urb->dev = usbvision->dev; - errCode = usb_submit_urb (urb, GFP_ATOMIC); + /* Copy the data received into our scratch buffer */ + len = usbvision_compress_isochronous(usbvision, urb); -/* Disable this warning. By design of the driver. */ -// if(errCode) { -// err("%s: usb_submit_urb failed: error %d", __FUNCTION__, errCode); -// } + usbvision->isocUrbCount++; + usbvision->urb_length = len; - /* If we collected enough data let's parse! */ - if (scratch_len(usbvision) > USBVISION_HEADER_LENGTH) { /* 12 == header_length */ - /*If we don't have a frame we're current working on, complain */ - if ((usbvision->curFrameNum >= 0) || (usbvision->overlay)) - usbvision_parse_data(usbvision); - else { - PDEBUG(DBG_IRQ, "received data, but no one needs it"); - scratch_reset(usbvision); + if (usbvision->streaming == Stream_On) { + + /* If we collected enough data let's parse! */ + if (scratch_len(usbvision) > USBVISION_HEADER_LENGTH) { /* 12 == header_length */ + /*If we don't have a frame we're current working on, complain */ + if((!list_empty(&(usbvision->inqueue))) || (usbvision->overlay)) { + if (!(*f)) { + (*f) = list_entry(usbvision->inqueue.next,struct usbvision_frame, frame); + } + usbvision_parse_data(usbvision); + } + else { + PDEBUG(DBG_IRQ, "received data, but no one needs it"); + scratch_reset(usbvision); + } } } + usbvision->timeInIrq += jiffies - startTime; + + for (i = 0; i < USBVISION_URB_FRAMES; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + urb->status = 0; + urb->dev = usbvision->dev; + errCode = usb_submit_urb (urb, GFP_ATOMIC); + + /* Disable this warning. By design of the driver. */ + // if(errCode) { + // err("%s: usb_submit_urb failed: error %d", __FUNCTION__, errCode); + // } + return; } @@ -2539,7 +2584,6 @@ static int attach_inform(struct i2c_client *client) struct usb_usbvision *usbvision; struct tuner_setup tun_addr; int i; - v4l2_std_id stdId; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) usbvision = (struct usb_usbvision *)client->adapter->data; @@ -2560,13 +2604,11 @@ static int attach_inform(struct i2c_client *client) tun_addr.type = usbvision->tuner_type; tun_addr.addr = ADDR_UNSET; client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_addr); - - call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->input.index); + call_i2c_clients(usbvision, VIDIOC_INT_RESET, NULL); + call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->ctl_input); + call_i2c_clients(usbvision, VIDIOC_STREAMON, NULL); } - // FIXME : need to add a call VIDIOC_S_CTRL for each control -/* call_i2c_clients(usbvision, DECODER_SET_PICTURE, &usbvision->vpic); */ - stdId = usbvision->input.std; - call_i2c_clients(usbvision, VIDIOC_S_STD, &stdId); + call_i2c_clients(usbvision, VIDIOC_S_STD, &usbvision->tvnorm->id); PDEBUG(DBG_I2C, "usbvision[%d] attaches %s", usbvision->nr, client->name); @@ -2995,10 +3037,10 @@ static int usbvision_set_output(struct usb_usbvision *usbvision, int width, frameRate = FRAMERATE_MAX; } - if (usbvision->input.std & V4L2_STD_625_50) { + if (usbvision->tvnorm->id & V4L2_STD_625_50) { frameDrop = frameRate * 32 / 25 - 1; } - else if (usbvision->input.std & V4L2_STD_525_60) { + else if (usbvision->tvnorm->id & V4L2_STD_525_60) { frameDrop = frameRate * 32 / 30 - 1; } @@ -3022,6 +3064,40 @@ static int usbvision_set_output(struct usb_usbvision *usbvision, int width, /* + * usbvision_empty_framequeues() + * prepare queues for incoming and outgoing frames + */ +static void usbvision_empty_framequeues(struct usb_usbvision *usbvision) +{ + u32 i; + + INIT_LIST_HEAD(&(usbvision->inqueue)); + INIT_LIST_HEAD(&(usbvision->outqueue)); + + for (i = 0; i < USBVISION_NUMFRAMES; i++) { + usbvision->frame[i].grabstate = FrameState_Unused; + usbvision->frame[i].bytes_read = 0; + } +} + +/* + * usbvision_stream_interrupt() + * stops streaming + */ +static int usbvision_stream_interrupt(struct usb_usbvision *usbvision) +{ + int ret = 0; + + /* stop reading from the device */ + + usbvision->streaming = Stream_Interrupt; + ret = wait_event_timeout(usbvision->wait_stream, + (usbvision->streaming == Stream_Off), + msecs_to_jiffies(USBVISION_NUMSBUF*USBVISION_URB_FRAMES)); + return ret; +} + +/* * usbvision_set_compress_params() * */ @@ -3135,7 +3211,7 @@ static int usbvision_set_input(struct usb_usbvision *usbvision) } - if (usbvision->input.std & V4L2_STD_PAL) { + if (usbvision->tvnorm->id & V4L2_STD_PAL) { value[0] = 0xC0; value[1] = 0x02; //0x02C0 -> 704 Input video line length value[2] = 0x20; @@ -3144,7 +3220,7 @@ static int usbvision_set_input(struct usb_usbvision *usbvision) value[5] = 0x00; //0x0060 -> 96 Input video h offset value[6] = 0x16; value[7] = 0x00; //0x0016 -> 22 Input video v offset - } else if (usbvision->input.std & V4L2_STD_SECAM) { + } else if (usbvision->tvnorm->id & V4L2_STD_SECAM) { value[0] = 0xC0; value[1] = 0x02; //0x02C0 -> 704 Input video line length value[2] = 0x20; @@ -3434,7 +3510,7 @@ static int usbvision_init_isoc(struct usb_usbvision *usbvision) if (!USBVISION_IS_OPERATIONAL(usbvision)) return -EFAULT; - usbvision->curFrameNum = -1; + usbvision->curFrame = NULL; scratch_reset(usbvision); /* Alternate interface 1 is is the biggest frame size */ @@ -3503,7 +3579,7 @@ static int usbvision_init_isoc(struct usb_usbvision *usbvision) } } - usbvision->streaming = 1; + usbvision->streaming = Stream_On; PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x", __FUNCTION__, usbvision->video_endp); return 0; } @@ -3519,7 +3595,8 @@ static void usbvision_stop_isoc(struct usb_usbvision *usbvision) { int bufIdx, errCode, regValue; - if (!usbvision->streaming || (usbvision->dev == NULL)) + // FIXME : removed the streaming==Stream_Off. This field has not the same signification than before ! + if (usbvision->dev == NULL) return; /* Unschedule all of the iso td's */ @@ -3530,9 +3607,8 @@ static void usbvision_stop_isoc(struct usb_usbvision *usbvision) } - PDEBUG(DBG_ISOC, "%s: streaming=0\n", __FUNCTION__); - usbvision->streaming = 0; - + PDEBUG(DBG_ISOC, "%s: streaming=Stream_Off\n", __FUNCTION__); + usbvision->streaming = Stream_Off; if (!usbvision->remove_pending) { @@ -3552,62 +3628,7 @@ static void usbvision_stop_isoc(struct usb_usbvision *usbvision) } } -/* - * usbvision_new_frame() - * - */ -static int usbvision_new_frame(struct usb_usbvision *usbvision, int framenum) -{ - struct usbvision_frame *frame; - int n; //byhec , width, height; - - /* If we're not grabbing a frame right now and the other frame is */ - /* ready to be grabbed into, then use it instead */ - if (usbvision->curFrameNum != -1) - return 0; - - n = (framenum - 1 + USBVISION_NUMFRAMES) % USBVISION_NUMFRAMES; - if (usbvision->frame[n].grabstate == FrameState_Ready) - framenum = n; - - frame = &usbvision->frame[framenum]; - - frame->grabstate = FrameState_Grabbing; - frame->scanstate = ScanState_Scanning; - frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ - usbvision->curFrameNum = framenum; - - /* - * Normally we would want to copy previous frame into the current one - * before we even start filling it with data; this allows us to stop - * filling at any moment; top portion of the frame will be new and - * bottom portion will stay as it was in previous frame. If we don't - * do that then missing chunks of video stream will result in flickering - * portions of old data whatever it was before. - * - * If we choose not to copy previous frame (to, for example, save few - * bus cycles - the frame can be pretty large!) then we have an option - * to clear the frame before using. If we experience losses in this - * mode then missing picture will be black (flickering). - * - * Finally, if user chooses not to clean the current frame before - * filling it with data then the old data will be visible if we fail - * to refill entire frame with new data. - */ - if (!(flags & FLAGS_SEPARATE_FRAMES)) { - /* This copies previous frame into this one to mask losses */ - memmove(frame->data, usbvision->frame[1 - framenum].data, - MAX_FRAME_SIZE); - } else { - if (flags & FLAGS_CLEAN_FRAMES) { - /*This provides a "clean" frame but slows things down */ - memset(frame->data, 0, MAX_FRAME_SIZE); - } - } - return 0; -} - -static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel, int norm) +static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) { int mode[4]; int audio[]= {1, 0, 0, 0}; @@ -3618,16 +3639,10 @@ static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel, int no //channel 3 is additional video inputs to the device with audio channel 0 (line in) RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs); - /* set the new video norm */ - if (usbvision->input.std != norm) { - v4l2_std_id video_command = norm; - + usbvision->ctl_input = channel; route.input = SAA7115_COMPOSITE1; call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route); - call_i2c_clients(usbvision, VIDIOC_S_STD, &video_command); - usbvision->input.std = norm; - call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->input.index); //set norm in tuner - } + call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->ctl_input); // set the new channel // Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video @@ -3703,8 +3718,13 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) errCode = -ENOMEM; } else { + spin_lock_init(&usbvision->queue_lock); + init_waitqueue_head(&usbvision->wait_frame); + init_waitqueue_head(&usbvision->wait_stream); + /* Allocate all buffers */ for (i = 0; i < USBVISION_NUMFRAMES; i++) { + usbvision->frame[i].index = i; usbvision->frame[i].grabstate = FrameState_Unused; usbvision->frame[i].data = usbvision->fbuf + i * MAX_FRAME_SIZE; @@ -3804,6 +3824,9 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) if (errCode) { } + /* prepare queues */ + usbvision_empty_framequeues(usbvision); + PDEBUG(DBG_IO, "success"); return errCode; } @@ -3875,6 +3898,8 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, if (!USBVISION_IS_OPERATIONAL(usbvision)) return -EFAULT; + // if (debug & DBG_IOCTL) v4l_printk_ioctl(cmd); + switch (cmd) { case UVIOCSREG: { @@ -3914,7 +3939,20 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_QUERYCAP: { struct v4l2_capability *vc=arg; - *vc = usbvision->vcap; + + memset(vc, 0, sizeof(*vc)); + strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); + strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString, + sizeof(vc->card)); + strlcpy(vc->bus_info, usbvision->dev->dev.bus_id, + sizeof(vc->bus_info)); + vc->version = USBVISION_DRIVER_VERSION; + vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + (dga ? (V4L2_FBUF_CAP_LIST_CLIPPING | V4L2_CAP_VIDEO_OVERLAY) : 0) | + (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); PDEBUG(DBG_IOCTL, "VIDIOC_QUERYCAP"); return 0; } @@ -3975,38 +4013,24 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, } case VIDIOC_ENUMSTD: { - struct v4l2_standard *vs = arg; - switch(vs->index) { - case 0: - vs->id = V4L2_STD_PAL; - strcpy(vs->name,"PAL"); - vs->frameperiod.numerator = 1; - vs->frameperiod.denominator = 25; - vs->framelines = 625; - break; - case 1: - vs->id = V4L2_STD_NTSC; - strcpy(vs->name,"NTSC"); - vs->frameperiod.numerator = 1001; - vs->frameperiod.denominator = 30000; - vs->framelines = 525; - break; - case 2: - vs->id = V4L2_STD_SECAM; - strcpy(vs->name,"SECAM"); - vs->frameperiod.numerator = 1; - vs->frameperiod.denominator = 25; - vs->framelines = 625; - break; - default: + struct v4l2_standard *e = arg; + unsigned int i; + int ret; + + i = e->index; + if (i >= TVNORMS) return -EINVAL; - } + ret = v4l2_video_std_construct(e, tvnorms[e->index].id, + tvnorms[e->index].name); + e->index = i; + if (ret < 0) + return ret; return 0; } case VIDIOC_G_INPUT: { int *input = arg; - *input = usbvision->input.index; + *input = usbvision->ctl_input; return 0; } case VIDIOC_S_INPUT: @@ -4014,10 +4038,10 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, int *input = arg; if ((*input >= usbvision->video_inputs) || (*input < 0) ) return -EINVAL; - usbvision->input.index = *input; + usbvision->ctl_input = *input; down(&usbvision->lock); - usbvision_muxsel(usbvision, usbvision->input.index, usbvision->input.std); + usbvision_muxsel(usbvision, usbvision->ctl_input); usbvision_set_input(usbvision); usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight); up(&usbvision->lock); @@ -4025,43 +4049,50 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, } case VIDIOC_G_STD: { - v4l2_std_id *std = arg; - *std = usbvision->input.std; - PDEBUG(DBG_IOCTL, "VIDIOC_G_STD std_id=%x", (unsigned)*std); + v4l2_std_id *id = arg; + + *id = usbvision->tvnorm->id; + + PDEBUG(DBG_IOCTL, "VIDIOC_G_STD std_id=%s", usbvision->tvnorm->name); return 0; } case VIDIOC_S_STD: { - v4l2_std_id *std = arg; + v4l2_std_id *id = arg; + unsigned int i; + + for (i = 0; i < TVNORMS; i++) + if (*id == tvnorms[i].id) + break; + if (i == TVNORMS) + for (i = 0; i < TVNORMS; i++) + if (*id & tvnorms[i].id) + break; + if (i == TVNORMS) + return -EINVAL; down(&usbvision->lock); - usbvision_muxsel(usbvision, usbvision->input.index, *std); - usbvision_set_input(usbvision); - usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight); + usbvision->tvnorm = &tvnorms[i]; + + call_i2c_clients(usbvision, VIDIOC_S_STD, + &usbvision->tvnorm->id); + up(&usbvision->lock); - usbvision->input.std = *std; - PDEBUG(DBG_IOCTL, "VIDIOC_S_STD std_id=%x", (unsigned)*std); + PDEBUG(DBG_IOCTL, "VIDIOC_S_STD std_id=%s", usbvision->tvnorm->name); return 0; } case VIDIOC_G_TUNER: { struct v4l2_tuner *vt = arg; - struct v4l2_tuner status; if (!usbvision->have_tuner || vt->index) // Only tuner 0 return -EINVAL; strcpy(vt->name, "Television"); - vt->type = V4L2_TUNER_ANALOG_TV; - vt->capability = V4L2_TUNER_CAP_NORM; - vt->rangelow = 0; - vt->rangehigh = ~0; - vt->audmode = V4L2_TUNER_MODE_MONO; - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - call_i2c_clients(usbvision,VIDIOC_G_TUNER,&status); - vt->signal = status.signal; - - PDEBUG(DBG_IOCTL, "VIDIOC_G_TUNER"); + /* Let clients fill in the remainder of this struct */ + call_i2c_clients(usbvision,VIDIOC_G_TUNER,vt); + + PDEBUG(DBG_IOCTL, "VIDIOC_G_TUNER signal=%x, afc=%x",vt->signal,vt->afc); return 0; } case VIDIOC_S_TUNER: @@ -4071,15 +4102,16 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, // Only no or one tuner for now if (!usbvision->have_tuner || vt->index) return -EINVAL; - // FIXME vt->audmode Radio mode (STEREO/MONO/...) - // vt->reserved Radio freq - // usbvision_muxsel(usbvision, vt->index, vt->mode); + /* let clients handle this */ + call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt); + PDEBUG(DBG_IOCTL, "VIDIOC_S_TUNER"); return 0; } case VIDIOC_G_FREQUENCY: { struct v4l2_frequency *freq = arg; + freq->tuner = 0; // Only one tuner freq->type = V4L2_TUNER_ANALOG_TV; freq->frequency = usbvision->freq; @@ -4089,6 +4121,11 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_S_FREQUENCY: { struct v4l2_frequency *freq = arg; + + // Only no or one tuner for now + if (!usbvision->have_tuner || freq->tuner) + return -EINVAL; + usbvision->freq = freq->frequency; call_i2c_clients(usbvision, cmd, freq); PDEBUG(DBG_IOCTL, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)freq->frequency); @@ -4149,11 +4186,27 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_REQBUFS: { struct v4l2_requestbuffers *vr = arg; - // FIXME : normally we allocate the requested number of buffers. - // this driver allocates statically the buffers. - vr->count = 2; - if(vr->memory != V4L2_MEMORY_MMAP) + int ret; + + RESTRICT_TO_RANGE(vr->count,1,USBVISION_NUMFRAMES); + + // Check input validity : the user must do a VIDEO CAPTURE and MMAP method. + if((vr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || + (vr->memory != V4L2_MEMORY_MMAP)) return -EINVAL; + + // FIXME : before this, we must control if buffers are still mapped. + // Then interrupt streaming if so... + if(usbvision->streaming == Stream_On) { + if ((ret = usbvision_stream_interrupt(usbvision))) + return ret; + } + + usbvision_empty_framequeues(usbvision); + + usbvision->curFrame = NULL; + + PDEBUG(DBG_IOCTL, "VIDIOC_REQBUFS count=%d",vr->count); return 0; } case VIDIOC_QUERYBUF: @@ -4161,16 +4214,18 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, struct v4l2_buffer *vb = arg; struct usbvision_frame *frame; - // FIXME : works only on VIDEO_CAPTURE MODE, MMAP. + // FIXME : must control that buffers are mapped (VIDIOC_REQBUFS has been called) + if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { return -EINVAL; } - if(vb->index>1) { + if(vb->index>=USBVISION_NUMFRAMES) { return -EINVAL; } + // Updating the corresponding frame state vb->flags = 0; frame = &usbvision->frame[vb->index]; - if(frame->grabstate == FrameState_Grabbing) + if(frame->grabstate >= FrameState_Ready) vb->flags |= V4L2_BUF_FLAG_QUEUED; if(frame->grabstate >= FrameState_Done) vb->flags |= V4L2_BUF_FLAG_DONE; @@ -4183,119 +4238,115 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, else { vb->m.offset = MAX_FRAME_SIZE; } + vb->memory = V4L2_MEMORY_MMAP; + vb->field = V4L2_FIELD_NONE; vb->length = MAX_FRAME_SIZE; vb->timestamp = usbvision->frame[vb->index].timestamp; - vb->sequence = usbvision->frame_num; + vb->sequence = usbvision->frame[vb->index].sequence; return 0; } - case VIDIOC_QBUF: // VIDIOCMCAPTURE + VIDIOCSYNC + case VIDIOC_QBUF: { struct v4l2_buffer *vb = arg; struct usbvision_frame *frame; + unsigned long lock_flags; // FIXME : works only on VIDEO_CAPTURE MODE, MMAP. if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { return -EINVAL; } - if(vb->index>1) { + if(vb->index>=USBVISION_NUMFRAMES) { return -EINVAL; } frame = &usbvision->frame[vb->index]; - if (frame->grabstate == FrameState_Grabbing) { - return -EBUSY; + if (frame->grabstate != FrameState_Unused) { + return -EAGAIN; } - usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight); - - /* Mark it as ready */ + /* Mark it as ready and enqueue frame */ frame->grabstate = FrameState_Ready; + frame->scanstate = ScanState_Scanning; + frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ + vb->flags &= ~V4L2_BUF_FLAG_DONE; /* set v4l2_format index */ frame->v4l2_format = usbvision->palette; - PDEBUG(DBG_IOCTL, "VIDIOC_QBUF frame=%d",vb->index); - return usbvision_new_frame(usbvision, vb->index); + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + return 0; } case VIDIOC_DQBUF: { struct v4l2_buffer *vb = arg; - int errCode = 0; + int ret; + struct usbvision_frame *f; + unsigned long lock_flags; - DECLARE_WAITQUEUE(wait, current); - // FIXME : not the proper way to get the last filled frame - vb->index=-1; - if(usbvision->curFrameNum != -1) vb->index=usbvision->curFrameNum; - else { - if(usbvision->frame[1].grabstate >= FrameState_Done) - vb->index = 1; - else if(usbvision->frame[0].grabstate >= FrameState_Done) - vb->index = 0; - // If no FRAME_DONE, look for a FRAME_GRABBING state. - // See if a frame is in process (grabbing), then use it. - if (vb->index == -1) { - if (usbvision->frame[1].grabstate == FrameState_Grabbing) - vb->index = 1; - else if (usbvision->frame[0].grabstate == FrameState_Grabbing) - vb->index = 0; - } - } - if (vb->index == -1) + if (vb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - PDEBUG(DBG_IOCTL, "VIDIOC_DQBUF frame=%d, grabstate=%d, curframeNum=%d", - vb->index, usbvision->frame[vb->index].grabstate,usbvision->curFrameNum); - - switch (usbvision->frame[vb->index].grabstate) { - case FrameState_Unused: - errCode = -EINVAL; - break; - case FrameState_Grabbing: - add_wait_queue(&usbvision->frame[vb->index].wq, &wait); - current->state = TASK_INTERRUPTIBLE; - while (usbvision->frame[vb->index].grabstate == FrameState_Grabbing) { - schedule(); - if (signal_pending(current)) { - remove_wait_queue(&usbvision->frame[vb->index].wq, &wait); - current->state = TASK_RUNNING; - return -EINTR; - } - } - remove_wait_queue(&usbvision->frame[vb->index].wq, &wait); - current->state = TASK_RUNNING; - case FrameState_Ready: - case FrameState_Error: - case FrameState_Done: - errCode = (usbvision->frame[vb->index].grabstate == FrameState_Error) ? -EIO : 0; - vb->memory = V4L2_MEMORY_MMAP; - vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE; - vb->field = V4L2_FIELD_NONE; - vb->sequence = usbvision->frame[vb->index].sequence; - usbvision->frame[vb->index].grabstate = FrameState_Unused; - break; + if (list_empty(&(usbvision->outqueue))) { + if (usbvision->streaming == Stream_Off) + return -EINVAL; + ret = wait_event_interruptible + (usbvision->wait_frame, + !list_empty(&(usbvision->outqueue))); + if (ret) + return ret; } - usbvision->frame[vb->index].grabstate = FrameState_Unused; - return errCode; + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + f = list_entry(usbvision->outqueue.next, + struct usbvision_frame, frame); + list_del(usbvision->outqueue.next); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + f->grabstate = FrameState_Unused; + + vb->memory = V4L2_MEMORY_MMAP; + vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE; + vb->index = f->index; + vb->sequence = f->sequence; + + return 0; } case VIDIOC_STREAMON: { int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (list_empty(&usbvision->inqueue)) + return -EINVAL; + + usbvision->streaming = Stream_On; + call_i2c_clients(usbvision,VIDIOC_STREAMON , &b); + + PDEBUG(DBG_IOCTL, "VIDIOC_STREAMON"); + return 0; } case VIDIOC_STREAMOFF: { + int *type = arg; int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; - down(&usbvision->lock); - // Stop all video streamings - call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b); - usbvision->frame_num = -1; - usbvision->frame[0].grabstate = FrameState_Unused; - usbvision->frame[1].grabstate = FrameState_Unused; - up(&usbvision->lock); + + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if(usbvision->streaming == Stream_On) { + usbvision_stream_interrupt(usbvision); + // Stop all video streamings + call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b); + } + usbvision_empty_framequeues(usbvision); + + PDEBUG(DBG_IOCTL, "VIDIOC_STREAMOFF"); return 0; } case VIDIOC_G_FBUF: @@ -4361,6 +4412,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUM_FMT: { struct v4l2_fmtdesc *vfd = arg; + if ( (dga == 0) && (vfd->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) && (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && @@ -4539,7 +4591,7 @@ static ssize_t usbvision_v4l2_read(struct file *file, char *buf, int frmx = -1; int rc = 0; struct usbvision_frame *frame; - +return -EINVAL; PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __FUNCTION__, (unsigned long)count, noblock); if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL)) @@ -4574,7 +4626,8 @@ static ssize_t usbvision_v4l2_read(struct file *file, char *buf, // If no frame is active, start one. if (frmx == -1) - usbvision_new_frame(usbvision, frmx = 0); + // FIXME: enqueue all inqueue... + /* usbvision_new_frame(usbvision, frmx = 0); */ frame = &usbvision->frame[frmx]; @@ -4584,7 +4637,7 @@ static ssize_t usbvision_v4l2_read(struct file *file, char *buf, goto usbvision_v4l2_read_done; } PDEBUG(DBG_IO, "Waiting frame grabbing"); - rc = wait_event_interruptible(frame->wq, (frame->grabstate == FrameState_Done) || + rc = wait_event_interruptible(usbvision->wait_frame, (frame->grabstate == FrameState_Done) || (frame->grabstate == FrameState_Error)); if (rc) { goto usbvision_v4l2_read_done; @@ -4592,9 +4645,10 @@ static ssize_t usbvision_v4l2_read(struct file *file, char *buf, if (frame->grabstate == FrameState_Error) { frame->bytes_read = 0; - if (usbvision_new_frame(usbvision, frmx)) { - err("%s: usbvision_new_frame() failed", __FUNCTION__); - } + // FIXME: enqueue all inqueue... +/* if (usbvision_new_frame(usbvision, frmx)) { */ +/* err("%s: usbvision_new_frame() failed", __FUNCTION__); */ +/* } */ goto restart; } @@ -4619,8 +4673,9 @@ static ssize_t usbvision_v4l2_read(struct file *file, char *buf, /* Mark it as available to be used again. */ usbvision->frame[frmx].grabstate = FrameState_Unused; - if (usbvision_new_frame(usbvision, frmx ? 0 : 1)) - err("%s: usbvision_new_frame() failed", __FUNCTION__); + // FIXME enqueue another frame +/* if (usbvision_new_frame(usbvision, frmx ? 0 : 1)) */ +/* err("%s: usbvision_new_frame() failed", __FUNCTION__); */ } usbvision_v4l2_read_done: @@ -5198,7 +5253,7 @@ static int usbvision_vbi_ioctl(struct inode *inode, struct file *file, static void usbvision_configure_video(struct usb_usbvision *usbvision) { - int model; + int model,i; if (usbvision == NULL) return; @@ -5215,17 +5270,26 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) memset(&usbvision->vcap, 0, sizeof(usbvision->vcap)); strcpy(usbvision->vcap.driver, "USBVision"); - usbvision->vcap.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | + strlcpy(usbvision->vcap.bus_info, usbvision->dev->dev.bus_id, + sizeof(usbvision->vcap.bus_info)); + usbvision->vcap.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | (dga ? (V4L2_FBUF_CAP_LIST_CLIPPING | V4L2_CAP_VIDEO_OVERLAY) : 0) | (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); usbvision->vcap.version = USBVISION_DRIVER_VERSION; /* version */ - usbvision->video_inputs = usbvision_device_data[model].VideoChannels; - memset(&usbvision->input, 0, sizeof(usbvision->input)); - usbvision->input.tuner = 0; - usbvision->input.type = V4L2_INPUT_TYPE_TUNER|VIDEO_TYPE_CAMERA; - usbvision->input.std = usbvision_device_data[model].VideoNorm; + for (i = 0; i < TVNORMS; i++) + if (usbvision_device_data[model].VideoNorm == tvnorms[i].mode) + break; + if (i == TVNORMS) + i = 0; + usbvision->tvnorm = &tvnorms[i]; /* set default norm */ + call_i2c_clients(usbvision, VIDIOC_S_STD, + &usbvision->tvnorm->id); + + usbvision->video_inputs = usbvision_device_data[model].VideoChannels; + usbvision->ctl_input = 0; +/* usbvision_muxsel(usbvision, usbvision->ctl_input); */ /* This should be here to make i2c clients to be able to register */ usbvision_audio_off(usbvision); //first switch off audio @@ -5440,7 +5504,6 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) static struct usb_usbvision *usbvision_alloc(struct usb_device *dev) { struct usb_usbvision *usbvision; - int FrameIdx; if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) == NULL) { goto err_exit; @@ -5448,10 +5511,6 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev) usbvision->dev = dev; - for (FrameIdx = 0; FrameIdx < USBVISION_NUMFRAMES; FrameIdx++) { - init_waitqueue_head(&usbvision->frame[FrameIdx].wq); - } - init_waitqueue_head(&usbvision->overlay_frame.wq); init_MUTEX(&usbvision->lock); /* to 1 == available */ // prepare control urb for control messages during interrupts @@ -5460,7 +5519,7 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev) goto err_exit; } init_waitqueue_head(&usbvision->ctrlUrb_wq); - init_MUTEX(&usbvision->ctrlUrbLock); + init_MUTEX(&usbvision->ctrlUrbLock); /* to 1 == available */ init_timer(&usbvision->powerOffTimer); usbvision->powerOffTimer.data = (long) usbvision; @@ -5536,7 +5595,6 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); - /* Is it an USBVISION video dev? */ model = 0; for(model = 0; usbvision_device_data[model].idVendor; model++) { @@ -5655,6 +5713,9 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) usb_put_dev(usbvision->dev); usbvision->dev = NULL; // USB device is no more + wake_up_interruptible(&usbvision->wait_frame); + wake_up_interruptible(&usbvision->wait_stream); + up(&usbvision->lock); if (usbvision->user) { diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index 466e11f..48afcd2 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -1,4 +1,4 @@ -/* +/* * I2C_ALGO_USB.C * i2c algorithm for USB-I2C Bridges * @@ -39,7 +39,7 @@ #include #include "usbvision-i2c.h" -static int debug_i2c_usb = 0; +static int debug_i2c_usb = 0; #if defined(module_param) // Showing parameters under SYSFS module_param (debug_i2c_usb, int, 0444); // debug_i2c_usb mode of the device driver @@ -108,7 +108,7 @@ static inline int usb_find_address(struct i2c_adapter *i2c_adap, unsigned char *add) { unsigned short flags = msg->flags; - + unsigned char addr; int ret; if ((flags & I2C_M_TEN)) { @@ -205,8 +205,6 @@ static u32 usb_func(struct i2c_adapter *adap) static struct i2c_algorithm i2c_usb_algo = { .master_xfer = usb_xfer, .smbus_xfer = NULL, - .slave_send = NULL, - .slave_recv = NULL, .algo_control = algo_control, .functionality = usb_func, }; diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index 813b258..870c0cc 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -1,4 +1,4 @@ -/* +/* * USBVISION.H * usbvision header file * @@ -38,7 +38,7 @@ #define USBVISION_SSPND_EN (1 << 1) #define USBVISION_RES2 (1 << 2) #define USBVISION_PWR_VID (1 << 5) - #define USBVISION_E2_EN (1 << 7) + #define USBVISION_E2_EN (1 << 7) #define USBVISION_CONFIG_REG 0x01 #define USBVISION_ADRS_REG 0x02 #define USBVISION_ALTER_REG 0x03 @@ -139,7 +139,7 @@ #define USBVISION_MAX_ISOC_PACKET_SIZE 959 // NT1003 Specs Document says 1023 #define USBVISION_NUM_HEADERMARKER 20 -#define USBVISION_NUMFRAMES 2 +#define USBVISION_NUMFRAMES 3 #define USBVISION_NUMSBUF 2 #define USBVISION_POWEROFF_TIME 3 * (HZ) // 3 seconds @@ -225,6 +225,13 @@ enum FrameState { FrameState_Error, /* Something bad happened while processing */ }; +/* stream states */ +enum StreamState { + Stream_Off, + Stream_Interrupt, + Stream_On, +}; + enum IsocState { IsocState_InFrame, /* Isoc packet is member of frame */ IsocState_NoFrame, /* Isoc packet is not member of any frame */ @@ -272,27 +279,36 @@ struct usbvision_frame_header { __u16 frameHeight; /* 10 - 11 after endian correction*/ }; +/* tvnorms */ +struct usbvision_tvnorm { + char *name; + v4l2_std_id id; + /* mode for saa7113h */ + int mode; +}; + struct usbvision_frame { char *data; /* Frame buffer */ struct usbvision_frame_header isocHeader; /* Header from stream */ int width; /* Width application is expecting */ int height; /* Height */ - + int index; /* Frame index */ int frmwidth; /* Width the frame actually is */ int frmheight; /* Height */ volatile int grabstate; /* State of grabbing */ int scanstate; /* State of scanning */ + struct list_head frame; + int curline; /* Line of frame we're working on */ long scanlength; /* uncompressed, raw data length of frame */ long bytes_read; /* amount of scanlength that has been read from data */ struct usbvision_v4l2_format_st v4l2_format; /* format the user needs*/ int v4l2_linesize; /* bytes for one videoline*/ - struct timeval timestamp; - wait_queue_head_t wq; /* Processes waiting */ + struct timeval timestamp; int sequence; // How many video frames we send to user }; @@ -305,23 +321,23 @@ struct usbvision_frame { #define USBVISION_I2C_CLIENTS_MAX 8 struct usbvision_device_data_st { - int idVendor; - int idProduct; - int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ - int Codec; - int VideoChannels; - __u64 VideoNorm; - int AudioChannels; - int Radio; - int vbi; - int Tuner; - int TunerType; - int Vin_Reg1; - int Vin_Reg2; - int X_Offset; - int Y_Offset; - int Dvi_yuv; - char *ModelString; + int idVendor; + int idProduct; + int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ + int Codec; + int VideoChannels; + __u64 VideoNorm; + int AudioChannels; + int Radio; + int vbi; + int Tuner; + int TunerType; + int Vin_Reg1; + int Vin_Reg2; + int X_Offset; + int Y_Offset; + int Dvi_yuv; + char *ModelString; }; /* Declared on usbvision-cards.c */ @@ -332,7 +348,7 @@ struct usb_usbvision { struct video_device *vdev; /* Video Device */ struct video_device *rdev; /* Radio Device */ struct video_device *vbi; /* VBI Device */ - struct video_audio audio_dev; /* Current audio params */ + struct video_audio audio_dev; /* Current audio params */ /* i2c Declaration Section*/ struct i2c_adapter i2c_adap; @@ -373,7 +389,7 @@ struct usb_usbvision { int usbvision_used; /* Is this structure in use? */ int initialized; /* Had we already sent init sequence? */ int DevModel; /* What type of USBVISION device we got? */ - int streaming; /* Are we streaming Isochronous? */ + enum StreamState streaming; /* Are we streaming Isochronous? */ int last_error; /* What calamity struck us? */ int curwidth; /* width of the frame the device is currently set to*/ int curheight; /* height of the frame the device is currently set to*/ @@ -382,7 +398,10 @@ struct usb_usbvision { char *fbuf; /* Videodev buffer area for mmap*/ int max_frame_size; /* Bytes in one video frame */ int fbuf_size; /* Videodev buffer size */ - int curFrameNum; // number of current frame in frame buffer mode + spinlock_t queue_lock; /* spinlock for protecting mods on inqueue and outqueue */ + struct list_head inqueue, outqueue; /* queued frame list and ready to dequeue frame list */ + wait_queue_head_t wait_frame; /* Processes waiting */ + wait_queue_head_t wait_stream; /* Processes waiting */ struct usbvision_frame *curFrame; // pointer to current frame, set by usbvision_find_header struct usbvision_frame frame[USBVISION_NUMFRAMES]; // frame buffer int curSbufNum; // number of current receiving sbuf @@ -397,20 +416,21 @@ struct usb_usbvision { int scratch_headermarker_read_ptr; int scratch_headermarker_write_ptr; int isocstate; - /* color controls */ + /* color controls */ int saturation; int hue; int brightness; - int contrast; + int contrast; int depth; struct usbvision_v4l2_format_st palette; struct v4l2_capability vcap; /* Video capabilities */ - struct v4l2_input input; /* May be used for tuner support */ + unsigned int ctl_input; /* selected input */ + struct usbvision_tvnorm *tvnorm; /* selected tv norm */ unsigned char video_endp; /* 0x82 for USBVISION devices based */ // Overlay stuff: - struct v4l2_framebuffer vid_buf; + struct v4l2_framebuffer vid_buf; struct v4l2_format vid_win; int vid_buf_valid; // Status: video buffer is valid (set) int vid_win_valid; // Status: video window is valid (set) @@ -435,8 +455,8 @@ struct usb_usbvision { struct proc_dir_entry *proc_devdir; /* Per-device proc directory */ struct proc_dir_entry *proc_info; /* /info entry */ struct proc_dir_entry *proc_register; /* /register entry */ - struct proc_dir_entry *proc_freq; /* /freq entry */ - struct proc_dir_entry *proc_input; /* /input entry */ + struct proc_dir_entry *proc_freq; /* /freq entry */ + struct proc_dir_entry *proc_input; /* /input entry */ struct proc_dir_entry *proc_frame; /* /frame entry */ struct proc_dir_entry *proc_button; /* /button entry */ struct proc_dir_entry *proc_control; /* /control entry */ @@ -463,3 +483,10 @@ struct usb_usbvision { #endif /* __LINUX_USBVISION_H */ +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ -- cgit v0.10.2 From 5f7fb877be14da92803f0b5b60955e071ebe2d58 Mon Sep 17 00:00:00 2001 From: Thierry Date: Mon, 4 Dec 2006 08:31:17 -0300 Subject: V4L/DVB (4928): Usbvision_v4l2 robustness on disconnect This patch corrects 2 bugs (causes kernel oops) that occur when unplugging the peripheral whereas nobody has opened it yet : - do not call usbvision_stop_isoc if usbvision_init_isoc has not been called - do not call wakeup_interruptible on waitqueues that did not have been initialized with init_waitqueue_head Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 06e8e67..5a61ed7 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -2287,7 +2287,7 @@ static void usbvision_isocIrq(struct urb *urb, struct pt_regs *regs) /* Manage streaming interruption */ if (usbvision->streaming == Stream_Interrupt) { - usbvision->streaming = Stream_Off; + usbvision->streaming = Stream_Idle; if ((*f)) { (*f)->grabstate = FrameState_Ready; (*f)->scanstate = ScanState_Scanning; @@ -3092,7 +3092,7 @@ static int usbvision_stream_interrupt(struct usb_usbvision *usbvision) usbvision->streaming = Stream_Interrupt; ret = wait_event_timeout(usbvision->wait_stream, - (usbvision->streaming == Stream_Off), + (usbvision->streaming == Stream_Idle), msecs_to_jiffies(USBVISION_NUMSBUF*USBVISION_URB_FRAMES)); return ret; } @@ -3579,7 +3579,7 @@ static int usbvision_init_isoc(struct usb_usbvision *usbvision) } } - usbvision->streaming = Stream_On; + usbvision->streaming = Stream_Idle; PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x", __FUNCTION__, usbvision->video_endp); return 0; } @@ -3595,8 +3595,7 @@ static void usbvision_stop_isoc(struct usb_usbvision *usbvision) { int bufIdx, errCode, regValue; - // FIXME : removed the streaming==Stream_Off. This field has not the same signification than before ! - if (usbvision->dev == NULL) + if ((usbvision->streaming == Stream_Off) || (usbvision->dev == NULL)) return; /* Unschedule all of the iso td's */ @@ -4292,7 +4291,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, return -EINVAL; if (list_empty(&(usbvision->outqueue))) { - if (usbvision->streaming == Stream_Off) + if (usbvision->streaming == Stream_Idle) return -EINVAL; ret = wait_event_interruptible (usbvision->wait_frame, @@ -5665,6 +5664,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us usbvision->isocPacketSize = 0; usbvision->usb_bandwidth = 0; usbvision->user = 0; + usbvision->streaming = Stream_Off; usbvision_register_video(usbvision); usbvision_configure_video(usbvision); @@ -5713,13 +5713,12 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) usb_put_dev(usbvision->dev); usbvision->dev = NULL; // USB device is no more - wake_up_interruptible(&usbvision->wait_frame); - wake_up_interruptible(&usbvision->wait_stream); - up(&usbvision->lock); if (usbvision->user) { info("%s: In use, disconnect pending", __FUNCTION__); + wake_up_interruptible(&usbvision->wait_frame); + wake_up_interruptible(&usbvision->wait_stream); } else { usbvision_release(usbvision); diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index 870c0cc..ef83a5c 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -227,9 +227,10 @@ enum FrameState { /* stream states */ enum StreamState { - Stream_Off, - Stream_Interrupt, - Stream_On, + Stream_Off, /* Driver streaming is completely OFF */ + Stream_Idle, /* Driver streaming is ready to be put ON by the application */ + Stream_Interrupt, /* Driver streaming must be interrupted */ + Stream_On, /* Driver streaming is put ON by the application */ }; enum IsocState { -- cgit v0.10.2 From 3084920b555b3ba73590430b2e03d9167db23e8e Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Mon, 4 Dec 2006 08:31:21 -0300 Subject: V4L/DVB (4929): Read() implementation + format set/get simplifications - implement read() entry point that works with linux list.h - rework of VIDIOC_ENUM_FMT/VIDIOC_S_FMT/VIDIOC_G_FMT - VIDIOC_STREAMON : allows streaming whereas there is no queued buffer (xdtv does VIDIOC_STREAMON before VIDIOC_QBUFs) Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 5a61ed7..3833f3f 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -409,7 +409,8 @@ static int usbvision_nr = 0; // sequential number of usbvision device - +static unsigned long usbvision_timestamp = 0; // timestamp in jiffies of a hundred frame +static unsigned long usbvision_counter = 0; // frame counter static const int max_imgwidth = MAX_FRAME_WIDTH; static const int max_imgheight = MAX_FRAME_HEIGHT; @@ -4278,6 +4279,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue); spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + PDEBUG(DBG_IOCTL, "VIDIOC_QBUF frame #%d",vb->index); return 0; } case VIDIOC_DQBUF: @@ -4312,18 +4314,31 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE; vb->index = f->index; vb->sequence = f->sequence; + vb->timestamp = f->timestamp; + vb->field = V4L2_FIELD_NONE; + vb->bytesused = f->scanlength; + if(debug & DBG_IOCTL) { // do not spend computing time for debug stuff if not needed ! + if(usbvision_counter == 100) { + PDEBUG(DBG_IOCTL, "VIDIOC_DQBUF delta=%d",(unsigned)(jiffies-usbvision_timestamp)); + usbvision_counter = 0; + usbvision_timestamp = jiffies; + } + else { + usbvision_counter++; + } + PDEBUG(DBG_IOCTL, "VIDIOC_DQBUF frame #%d",vb->index); + } return 0; } case VIDIOC_STREAMON: { int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (list_empty(&usbvision->inqueue)) - return -EINVAL; - usbvision->streaming = Stream_On; + if(debug & DBG_IOCTL) usbvision_timestamp = jiffies; + call_i2c_clients(usbvision,VIDIOC_STREAMON , &b); PDEBUG(DBG_IOCTL, "VIDIOC_STREAMON"); @@ -4412,134 +4427,132 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, { struct v4l2_fmtdesc *vfd = arg; - if ( (dga == 0) && - (vfd->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) && - (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && - (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { - return -EINVAL; - } if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) { return -EINVAL; } vfd->flags = 0; + vfd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc); vfd->pixelformat = usbvision_v4l2_format[vfd->index].format; + memset(vfd->reserved, 0, sizeof(vfd->reserved)); return 0; } case VIDIOC_G_FMT: { struct v4l2_format *vf = arg; - if ( (dga == 0) && - (vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) && - (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && - (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { - return -EINVAL; + switch (vf->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + { + vf->fmt.pix.width = usbvision->curwidth; + vf->fmt.pix.height = usbvision->curheight; + vf->fmt.pix.pixelformat = usbvision->palette.format; + vf->fmt.pix.bytesperline = usbvision->curwidth*usbvision->palette.bytes_per_pixel; + vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*usbvision->curheight; + vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ + } + return 0; + default: + PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT invalid type %d",vf->type); + return -EINVAL; } - down(&usbvision->lock); - *vf = usbvision->vid_win; - up(&usbvision->lock); - PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT x=%d, y=%d, w=%d, h=%d, chroma=%x, clips=%d", - vf->fmt.win.w.left, vf->fmt.win.w.top, vf->fmt.win.w.width, vf->fmt.win.w.height, vf->fmt.win.chromakey, vf->fmt.win.clipcount); + PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT w=%d, h=%d",vf->fmt.win.w.width, vf->fmt.win.w.height); return 0; } + case VIDIOC_TRY_FMT: case VIDIOC_S_FMT: { struct v4l2_format *vf = arg; struct v4l2_clip *vc=NULL; int on,formatIdx; - if ( (dga == 0) && - (vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) && - (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && - (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { - return -EINVAL; - } - if(vf->type == V4L2_BUF_TYPE_VIDEO_OVERLAY) { - if (vf->fmt.win.clipcount>256) { - return -EDOM; /* Too many clips! */ - } - // Do every clips. - vc = vmalloc(sizeof(struct v4l2_clip)*(vf->fmt.win.clipcount+4)); - if (vc == NULL) { - return -ENOMEM; - } - if (vf->fmt.win.clipcount && copy_from_user(vc,vf->fmt.win.clips,sizeof(struct v4l2_clip)*vf->fmt.win.clipcount)) { - return -EFAULT; - } - on = usbvision->overlay; // Save overlay state - if (on) { - usbvision_cap(usbvision, 0); - } + switch(vf->type) { + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + { + if (vf->fmt.win.clipcount>256) { + return -EDOM; /* Too many clips! */ + } + // Do every clips. + vc = vmalloc(sizeof(struct v4l2_clip)*(vf->fmt.win.clipcount+4)); + if (vc == NULL) { + return -ENOMEM; + } + if (vf->fmt.win.clipcount && copy_from_user(vc,vf->fmt.win.clips,sizeof(struct v4l2_clip)*vf->fmt.win.clipcount)) { + return -EFAULT; + } + on = usbvision->overlay; // Save overlay state + if (on) { + usbvision_cap(usbvision, 0); + } - // strange, it seems xawtv sometimes calls us with 0 - // width and/or height. Ignore these values - if (vf->fmt.win.w.left == 0) { - vf->fmt.win.w.left = usbvision->vid_win.fmt.win.w.left; - } - if (vf->fmt.win.w.top == 0) { - vf->fmt.win.w.top = usbvision->vid_win.fmt.win.w.top; - } + // strange, it seems xawtv sometimes calls us with 0 + // width and/or height. Ignore these values + if (vf->fmt.win.w.left == 0) { + vf->fmt.win.w.left = usbvision->vid_win.fmt.win.w.left; + } + if (vf->fmt.win.w.top == 0) { + vf->fmt.win.w.top = usbvision->vid_win.fmt.win.w.top; + } - // by now we are committed to the new data... - down(&usbvision->lock); - RESTRICT_TO_RANGE(vf->fmt.win.w.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); - RESTRICT_TO_RANGE(vf->fmt.win.w.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); - usbvision->vid_win = *vf; - usbvision->overlay_frame.width = vf->fmt.win.w.width; - usbvision->overlay_frame.height = vf->fmt.win.w.height; - usbvision_set_output(usbvision, vf->fmt.win.w.width, vf->fmt.win.w.height); - up(&usbvision->lock); - - // Impose display clips - if (vf->fmt.win.w.left+vf->fmt.win.w.width > (unsigned int)usbvision->vid_buf.fmt.width) { - usbvision_new_clip(vf, vc, usbvision->vid_buf.fmt.width-vf->fmt.win.w.left, 0, vf->fmt.win.w.width-1, vf->fmt.win.w.height-1); - } - if (vf->fmt.win.w.top+vf->fmt.win.w.height > (unsigned int)usbvision->vid_buf.fmt.height) { - usbvision_new_clip(vf, vc, 0, usbvision->vid_buf.fmt.height-vf->fmt.win.w.top, vf->fmt.win.w.width-1, vf->fmt.win.w.height-1); - } + // by now we are committed to the new data... + down(&usbvision->lock); + RESTRICT_TO_RANGE(vf->fmt.win.w.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); + RESTRICT_TO_RANGE(vf->fmt.win.w.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); + usbvision->vid_win = *vf; + usbvision->overlay_frame.width = vf->fmt.win.w.width; + usbvision->overlay_frame.height = vf->fmt.win.w.height; + usbvision_set_output(usbvision, vf->fmt.win.w.width, vf->fmt.win.w.height); + up(&usbvision->lock); + + // Impose display clips + if (vf->fmt.win.w.left+vf->fmt.win.w.width > (unsigned int)usbvision->vid_buf.fmt.width) { + usbvision_new_clip(vf, vc, usbvision->vid_buf.fmt.width-vf->fmt.win.w.left, 0, vf->fmt.win.w.width-1, vf->fmt.win.w.height-1); + } + if (vf->fmt.win.w.top+vf->fmt.win.w.height > (unsigned int)usbvision->vid_buf.fmt.height) { + usbvision_new_clip(vf, vc, 0, usbvision->vid_buf.fmt.height-vf->fmt.win.w.top, vf->fmt.win.w.width-1, vf->fmt.win.w.height-1); + } - // built the requested clipping zones - usbvision_built_overlay(usbvision, vf->fmt.win.clipcount, vc); - vfree(vc); + // built the requested clipping zones + usbvision_built_overlay(usbvision, vf->fmt.win.clipcount, vc); + vfree(vc); - // restore overlay state - if (on) { - usbvision_cap(usbvision, 1); + // restore overlay state + if (on) { + usbvision_cap(usbvision, 1); + } + usbvision->vid_win_valid = 1; + PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT overlay x=%d, y=%d, w=%d, h=%d, chroma=%x, clips=%d", + vf->fmt.win.w.left, vf->fmt.win.w.top, vf->fmt.win.w.width, vf->fmt.win.w.height, vf->fmt.win.chromakey, vf->fmt.win.clipcount); + return 0; } - usbvision->vid_win_valid = 1; - PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT overlay x=%d, y=%d, w=%d, h=%d, chroma=%x, clips=%d", - vf->fmt.win.w.left, vf->fmt.win.w.top, vf->fmt.win.w.width, vf->fmt.win.w.height, vf->fmt.win.chromakey, vf->fmt.win.clipcount); - } - else { - /* Find requested format in available ones */ - for(formatIdx=0;formatIdxfmt.pix.pixelformat == usbvision_v4l2_format[formatIdx].format) { - usbvision->palette = usbvision_v4l2_format[formatIdx]; - break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + { + /* Find requested format in available ones */ + for(formatIdx=0;formatIdxfmt.pix.pixelformat == usbvision_v4l2_format[formatIdx].format) { + usbvision->palette = usbvision_v4l2_format[formatIdx]; + break; + } + } + /* robustness */ + if(formatIdx == USBVISION_SUPPORTED_PALETTES) { + return -EINVAL; } + RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); + RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); + // by now we are committed to the new data... + down(&usbvision->lock); + usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height); + up(&usbvision->lock); + + PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT grabdisplay w=%d, h=%d, format=%s", + vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc); + return 0; } - /* robustness */ - if(formatIdx == USBVISION_SUPPORTED_PALETTES) { + default: return -EINVAL; - } - usbvision->vid_win.fmt.pix.pixelformat = vf->fmt.pix.pixelformat; - usbvision->vid_win.fmt.pix.width = vf->fmt.pix.width; //Image width in pixels. - usbvision->vid_win.fmt.pix.height = vf->fmt.pix.height; // Image height in pixels. - - // by now we are committed to the new data... - down(&usbvision->lock); - RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); - RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); - usbvision->vid_win = *vf; - usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height); - up(&usbvision->lock); - - usbvision->vid_win_valid = 1; - PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT grabdisplay w=%d, h=%d, ", - vf->fmt.pix.width, vf->fmt.pix.height); } - return 0; } case VIDIOC_OVERLAY: { @@ -4586,99 +4599,97 @@ static ssize_t usbvision_v4l2_read(struct file *file, char *buf, struct video_device *dev = video_devdata(file); struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); int noblock = file->f_flags & O_NONBLOCK; + unsigned long lock_flags; int frmx = -1; - int rc = 0; + int ret,i; struct usbvision_frame *frame; -return -EINVAL; + PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __FUNCTION__, (unsigned long)count, noblock); if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL)) return -EFAULT; - down(&usbvision->lock); - //code for testing compression - if (usbvision->isocMode == ISOC_MODE_COMPRESS) { - usbvision->frame[0].v4l2_format = usbvision_v4l2_format[0]; //V4L2_PIX_FMT_GREY; - usbvision->frame[1].v4l2_format = usbvision_v4l2_format[0]; // V4L2_PIX_FMT_GREY; - } + /* no stream is running, make it running ! */ + usbvision->streaming = Stream_On; + call_i2c_clients(usbvision,VIDIOC_STREAMON , NULL); - // See if a frame is completed, then use it. - if (usbvision->frame[0].grabstate >= FrameState_Done) // _Done or _Error - frmx = 0; - else if (usbvision->frame[1].grabstate >= FrameState_Done)// _Done or _Error - frmx = 1; + /* First, enqueue as many frames as possible (like a user of VIDIOC_QBUF would do) */ + for(i=0;iframe[i]; + if(frame->grabstate == FrameState_Unused) { + /* Mark it as ready and enqueue frame */ + frame->grabstate = FrameState_Ready; + frame->scanstate = ScanState_Scanning; + frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ - if (noblock && (frmx == -1)) { - count = -EAGAIN; - goto usbvision_v4l2_read_done; - } + /* set v4l2_format index */ + frame->v4l2_format = usbvision->palette; - // If no FRAME_DONE, look for a FRAME_GRABBING state. - // See if a frame is in process (grabbing), then use it. - if (frmx == -1) { - if (usbvision->frame[0].grabstate == FrameState_Grabbing) - frmx = 0; - else if (usbvision->frame[1].grabstate == FrameState_Grabbing) - frmx = 1; + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_add_tail(&frame->frame, &usbvision->inqueue); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + } } - // If no frame is active, start one. - if (frmx == -1) - // FIXME: enqueue all inqueue... - /* usbvision_new_frame(usbvision, frmx = 0); */ - - frame = &usbvision->frame[frmx]; + /* Then try to steal a frame (like a VIDIOC_DQBUF would do) */ + if (list_empty(&(usbvision->outqueue))) { + if(noblock) + return -EAGAIN; - restart: - if (!USBVISION_IS_OPERATIONAL(usbvision)) { - count = -EIO; - goto usbvision_v4l2_read_done; + ret = wait_event_interruptible + (usbvision->wait_frame, + !list_empty(&(usbvision->outqueue))); + if (ret) + return ret; } - PDEBUG(DBG_IO, "Waiting frame grabbing"); - rc = wait_event_interruptible(usbvision->wait_frame, (frame->grabstate == FrameState_Done) || - (frame->grabstate == FrameState_Error)); - if (rc) { - goto usbvision_v4l2_read_done; + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + frame = list_entry(usbvision->outqueue.next, + struct usbvision_frame, frame); + list_del(usbvision->outqueue.next); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + if(debug & DBG_IOCTL) { // do not spend computing time for debug stuff if not needed ! + if(usbvision_counter == 100) { + PDEBUG(DBG_IOCTL, "VIDIOC_DQBUF delta=%d",(unsigned)(jiffies-usbvision_timestamp)); + usbvision_counter = 0; + usbvision_timestamp = jiffies; + } + else { + usbvision_counter++; + } } + /* An error returns an empty frame */ if (frame->grabstate == FrameState_Error) { frame->bytes_read = 0; - // FIXME: enqueue all inqueue... -/* if (usbvision_new_frame(usbvision, frmx)) { */ -/* err("%s: usbvision_new_frame() failed", __FUNCTION__); */ -/* } */ - goto restart; + return 0; } PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", __FUNCTION__, - frmx, frame->bytes_read, frame->scanlength); + frame->index, frame->bytes_read, frame->scanlength); /* copy bytes to user space; we allow for partials reads */ if ((count + frame->bytes_read) > (unsigned long)frame->scanlength) count = frame->scanlength - frame->bytes_read; if (copy_to_user(buf, frame->data + frame->bytes_read, count)) { - count = -EFAULT; - goto usbvision_v4l2_read_done; + return -EFAULT; } frame->bytes_read += count; PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", __FUNCTION__, (unsigned long)count, frame->bytes_read); - if (frame->bytes_read >= frame->scanlength) {// All data has been read + // For now, forget the frame if it has not been read in one shot. +/* if (frame->bytes_read >= frame->scanlength) {// All data has been read */ frame->bytes_read = 0; /* Mark it as available to be used again. */ usbvision->frame[frmx].grabstate = FrameState_Unused; - // FIXME enqueue another frame -/* if (usbvision_new_frame(usbvision, frmx ? 0 : 1)) */ -/* err("%s: usbvision_new_frame() failed", __FUNCTION__); */ - } +/* } */ -usbvision_v4l2_read_done: - up(&usbvision->lock); return count; } -- cgit v0.10.2 From 40bad67840aa6856cb39a431510a57d940017898 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Mon, 4 Dec 2006 08:31:24 -0300 Subject: V4L/DVB (4930): Usbvision_v4l2 : mmap corrected to get all frames - private ioctls UVIOCSREG/UVIOCGREG translated to the VIDIOC_INT_G_REGISTER/VIDIOC_INT_S_REGISTER - lost frame bug corrected (mmap rework) - reset scratch buffer is no buffer is queued (prevents useless scratch overflow management) Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 3833f3f..eb8f4ca 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -364,8 +364,6 @@ #endif #include "usbvision.h" -#include "usbvision_ioctl.h" - #define DRIVER_VERSION "0.9.8.3cvs for Linux kernels 2.4.19-2.4.32 + 2.6.0-2.6.17, compiled at "__DATE__", "__TIME__ #define EMAIL "joerg@heckenbach-aw.de" @@ -2320,6 +2318,10 @@ static void usbvision_isocIrq(struct urb *urb, struct pt_regs *regs) } } } + else { + PDEBUG(DBG_IRQ, "received data, but no one needs it"); + scratch_reset(usbvision); + } usbvision->timeInIrq += jiffies - startTime; @@ -3901,41 +3903,50 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, // if (debug & DBG_IOCTL) v4l_printk_ioctl(cmd); switch (cmd) { - case UVIOCSREG: + +#ifdef CONFIG_VIDEO_ADV_DEBUG + /* ioctls to allow direct acces to the NT100x registers */ + case VIDIOC_INT_G_REGISTER: { - struct usbvision_reg *usbvision_reg = arg; + struct v4l2_register *reg = arg; int errCode; - errCode = usbvision_write_reg(usbvision, usbvision_reg->addr, usbvision_reg->value); - + if (reg->i2c_id != 0) + return -EINVAL; + /* NT100x has a 8-bit register space */ + errCode = usbvision_read_reg(usbvision, reg->reg&0xff); if (errCode < 0) { - err("%s: UVIOCSREG failed: error %d", __FUNCTION__, errCode); + err("%s: VIDIOC_INT_G_REGISTER failed: error %d", __FUNCTION__, errCode); } else { - PDEBUG(DBG_IOCTL, "UVIOCSREG addr=0x%02X, value=0x%02X", - usbvision_reg->addr, usbvision_reg->value); - errCode = 0; + reg->val=(unsigned char)errCode; + PDEBUG(DBG_IOCTL, "VIDIOC_INT_G_REGISTER reg=0x%02X, value=0x%02X", + (unsigned int)reg->reg, reg->val); + errCode = 0; // No error } return errCode; } - case UVIOCGREG: + case VIDIOC_INT_S_REGISTER: { - struct usbvision_reg *usbvision_reg = arg; + struct v4l2_register *reg = arg; int errCode; - errCode = usbvision_read_reg(usbvision, usbvision_reg->addr); - + if (reg->i2c_id != 0) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val); if (errCode < 0) { - err("%s: UVIOCGREG failed: error %d", __FUNCTION__, errCode); + err("%s: VIDIOC_INT_S_REGISTER failed: error %d", __FUNCTION__, errCode); } else { - usbvision_reg->value=(unsigned char)errCode; - PDEBUG(DBG_IOCTL, "UVIOCGREG addr=0x%02X, value=0x%02X", - usbvision_reg->addr, usbvision_reg->value); - errCode = 0; // No error + PDEBUG(DBG_IOCTL, "VIDIOC_INT_S_REGISTER reg=0x%02X, value=0x%02X", + (unsigned int)reg->reg, reg->val); + errCode = 0; } - return errCode; + return 0; } +#endif case VIDIOC_QUERYCAP: { struct v4l2_capability *vc=arg; @@ -4232,12 +4243,9 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, if(frame->grabstate == FrameState_Unused) vb->flags |= V4L2_BUF_FLAG_MAPPED; vb->memory = V4L2_MEMORY_MMAP; - if(vb->index == 0) { - vb->m.offset = 0; - } - else { - vb->m.offset = MAX_FRAME_SIZE; - } + + vb->m.offset = vb->index*MAX_FRAME_SIZE; + vb->memory = V4L2_MEMORY_MMAP; vb->field = V4L2_FIELD_NONE; vb->length = MAX_FRAME_SIZE; @@ -4695,40 +4703,45 @@ static ssize_t usbvision_v4l2_read(struct file *file, char *buf, static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) { + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + struct video_device *dev = video_devdata(file); struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - - unsigned long page, pos; if (!USBVISION_IS_OPERATIONAL(usbvision)) return -EFAULT; - if (size > (((USBVISION_NUMFRAMES * usbvision->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + if (!(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(usbvision->max_frame_size)) { + return -EINVAL; + } + + for (i = 0; i < USBVISION_NUMFRAMES; i++) { + if (((usbvision->max_frame_size*i) >> PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (i == USBVISION_NUMFRAMES) { + PDEBUG(DBG_FUNC, "mmap: user supplied mapping address is out of range"); return -EINVAL; + } - pos = (unsigned long) usbvision->fbuf; + /* VM_IO is eventually going to replace PageReserved altogether */ + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ + + pos = usbvision->frame[i].data; while (size > 0) { -// Really ugly.... - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) //Compatibility for 2.6.10+ kernels - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - return -EAGAIN; - } - #else //Compatibility for 2.6.0 - 2.6.9 kernels - page = usbvision_kvirt_to_pa(pos); - if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + PDEBUG(DBG_FUNC, "mmap: vm_insert_page failed"); return -EAGAIN; } - #endif start += PAGE_SIZE; pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; + size -= PAGE_SIZE; } return 0; -- cgit v0.10.2 From ee5407c5019eb720872abea2e0a1376c35e2304a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 4 Dec 2006 08:31:27 -0300 Subject: V4L/DVB (4931): Removed usbvision_ioctl.h, since it isn't required anymore Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision_ioctl.h b/drivers/media/video/usbvision/usbvision_ioctl.h deleted file mode 100644 index 7204c5c..0000000 --- a/drivers/media/video/usbvision/usbvision_ioctl.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * USBVISION_IOCTL.H - * IOCTL for usbvision - * - * Copyright (c) 1999-2005 Joerg Heckenbach - * - * This module is part of usbvision driver project. - * Updates to driver completed by Dwaine P. Garden - * - * 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. - */ - - -struct usbvision_reg { - unsigned char addr; - unsigned char value; -}; - -#define UVIOCGREG _IOWR('u',240,struct usbvision_reg) // get register -#define UVIOCSREG _IOW('u',241,struct usbvision_reg) // set register -#define UVIOCSVINM _IOW('u',242,int) // set usbvision vin_mode - -- cgit v0.10.2 From 957883d0b56a649389d0652a727324dd8ba2e83c Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Mon, 4 Dec 2006 08:31:30 -0300 Subject: V4L/DVB (4932): Usbvision_v4l2: fix norm setting problems Patch contents: - fix i2c command broadcast (caused problems for SECAM norm setting) - default input selection at driver open Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index eb8f4ca..f87bd0a 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -2570,58 +2570,61 @@ static int usbvision_unrequest_intra (struct usb_usbvision *usbvision) static void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, void *arg) { - - int i; - - for (i = 0; i < USBVISION_I2C_CLIENTS_MAX; i++) { - if (NULL == usbvision->i2c_clients[i]) - continue; - if (NULL == usbvision->i2c_clients[i]->driver->command) - continue; - usbvision->i2c_clients[i]->driver->command(usbvision->i2c_clients[i], cmd, arg); - } + BUG_ON(NULL == usbvision->i2c_adap.algo_data); + i2c_clients_command(&usbvision->i2c_adap, cmd, arg); } static int attach_inform(struct i2c_client *client) { struct usb_usbvision *usbvision; - struct tuner_setup tun_addr; - int i; - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) - usbvision = (struct usb_usbvision *)client->adapter->data; - #else - usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); - #endif + usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); + + switch (client->addr << 1) { + case 0x43: + case 0x4b: + { + struct tuner_setup tun_setup; - for (i = 0; i < USBVISION_I2C_CLIENTS_MAX; i++) { - if (usbvision->i2c_clients[i] == NULL || - usbvision->i2c_clients[i]->driver->id == - client->driver->id) { - usbvision->i2c_clients[i] = client; + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = TUNER_TDA9887; + tun_setup.addr = client->addr; + + call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); break; } - } - if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) { - tun_addr.mode_mask = T_ANALOG_TV; - tun_addr.type = usbvision->tuner_type; - tun_addr.addr = ADDR_UNSET; - client->driver->command(client,TUNER_SET_TYPE_ADDR, &tun_addr); - call_i2c_clients(usbvision, VIDIOC_INT_RESET, NULL); - call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->ctl_input); - call_i2c_clients(usbvision, VIDIOC_STREAMON, NULL); - } - call_i2c_clients(usbvision, VIDIOC_S_STD, &usbvision->tvnorm->id); + case 0x42: + PDEBUG(DBG_I2C,"attach_inform: saa7114 detected.\n"); + break; + case 0x4a: + PDEBUG(DBG_I2C,"attach_inform: saa7113 detected.\n"); + break; + case 0xa0: + PDEBUG(DBG_I2C,"attach_inform: eeprom detected.\n"); + break; - PDEBUG(DBG_I2C, "usbvision[%d] attaches %s", usbvision->nr, client->name); + default: + PDEBUG(DBG_I2C,"attach inform: detected I2C address %x\n", client->addr << 1); + { + struct tuner_setup tun_setup; + usbvision->tuner_addr = client->addr; + + if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) { + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = usbvision->tuner_type; + tun_setup.addr = usbvision->tuner_addr; + call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); + } + } + break; + } return 0; } static int detach_inform(struct i2c_client *client) { struct usb_usbvision *usbvision; - int i; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) usbvision = (struct usb_usbvision *)client->adapter->data; @@ -2630,14 +2633,6 @@ static int detach_inform(struct i2c_client *client) #endif PDEBUG(DBG_I2C, "usbvision[%d] detaches %s", usbvision->nr, client->name); - for (i = 0; i < USBVISION_I2C_CLIENTS_MAX; i++) { - if (NULL != usbvision->i2c_clients[i] && - usbvision->i2c_clients[i]->driver->id == - client->driver->id) { - usbvision->i2c_clients[i] = NULL; - break; - } - } return 0; } @@ -2886,10 +2881,7 @@ static int usbvision_init_i2c(struct usb_usbvision *usbvision) } #endif - usbvision->i2c_ok = usbvision_i2c_usb_add_bus(&usbvision->i2c_adap); - - return usbvision->i2c_ok; - + return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap); } @@ -3811,6 +3803,8 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) if (!errCode) { usbvision_begin_streaming(usbvision); errCode = usbvision_init_isoc(usbvision); + /* device needs to be initialized before isoc transfer */ + usbvision_muxsel(usbvision,0); usbvision->user++; } else { @@ -4169,7 +4163,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, memset(ctrl,0,sizeof(*ctrl)); ctrl->id=id; - i2c_clients_command(&usbvision->i2c_adap, cmd, arg); + call_i2c_clients(usbvision, cmd, arg); if (ctrl->type) return 0; @@ -5300,19 +5294,15 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); usbvision->vcap.version = USBVISION_DRIVER_VERSION; /* version */ - for (i = 0; i < TVNORMS; i++) if (usbvision_device_data[model].VideoNorm == tvnorms[i].mode) break; if (i == TVNORMS) i = 0; - usbvision->tvnorm = &tvnorms[i]; /* set default norm */ - call_i2c_clients(usbvision, VIDIOC_S_STD, - &usbvision->tvnorm->id); + usbvision->tvnorm = &tvnorms[i]; /* set default norm */ usbvision->video_inputs = usbvision_device_data[model].VideoChannels; usbvision->ctl_input = 0; -/* usbvision_muxsel(usbvision, usbvision->ctl_input); */ /* This should be here to make i2c clients to be able to register */ usbvision_audio_off(usbvision); //first switch off audio @@ -5678,6 +5668,8 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us usbvision->tuner_type = usbvision_device_data[model].TunerType; } + usbvision->tuner_addr = ADDR_UNSET; + usbvision->DevModel = model; usbvision->remove_pending = 0; usbvision->last_error = 0; @@ -5689,7 +5681,6 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us usbvision->usb_bandwidth = 0; usbvision->user = 0; usbvision->streaming = Stream_Off; - usbvision_register_video(usbvision); usbvision_configure_video(usbvision); up(&usbvision->lock); diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index ef83a5c..f304e66 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -319,8 +319,6 @@ struct usbvision_frame { #define BRIDGE_NT1004 1004 #define BRIDGE_NT1005 1005 -#define USBVISION_I2C_CLIENTS_MAX 8 - struct usbvision_device_data_st { int idVendor; int idProduct; @@ -355,8 +353,6 @@ struct usb_usbvision { struct i2c_adapter i2c_adap; struct i2c_algo_usb_data i2c_algo; struct i2c_client i2c_client; - int i2c_state, i2c_ok; - struct i2c_client *i2c_clients[USBVISION_I2C_CLIENTS_MAX]; struct urb *ctrlUrb; unsigned char ctrlUrbBuffer[8]; @@ -367,6 +363,7 @@ struct usb_usbvision { int have_tuner; int tuner_type; + int tuner_addr; int bridgeType; // NT1003, NT1004, NT1005 int channel; int radio; -- cgit v0.10.2 From 3086d6cb0a2ec93f17bc215af3113c54af6080c1 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Mon, 4 Dec 2006 08:31:32 -0300 Subject: V4L/DVB (4933): Usbvision_v4l2: radio interface / tda9887 problem ? - implement the v4l2 radio interface Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index f87bd0a..0365c7f 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -4835,220 +4835,120 @@ static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, return -EIO; switch (cmd) { - /*************************** - * V4L2 IOCTLs * - ***************************/ case VIDIOC_QUERYCAP: { struct v4l2_capability *vc=arg; - memset(vc, 0, sizeof(struct v4l2_capability)); - strcpy(vc->driver,"usbvision radio"); - strcpy(vc->card,usbvision->vcap.card); - strcpy(vc->bus_info,"usb"); - vc->version = USBVISION_DRIVER_VERSION; /* version */ - vc->capabilities = V4L2_CAP_TUNER; /* capabilities */ - PDEBUG(DBG_RIO, "%s: VIDIOC_QUERYCAP", __FUNCTION__); + + memset(vc, 0, sizeof(*vc)); + strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); + strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString, + sizeof(vc->card)); + strlcpy(vc->bus_info, usbvision->dev->dev.bus_id, + sizeof(vc->bus_info)); + vc->version = USBVISION_DRIVER_VERSION; + vc->capabilities = (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); + PDEBUG(DBG_RIO, "VIDIOC_QUERYCAP"); return 0; } case VIDIOC_QUERYCTRL: { - struct v4l2_queryctrl *qc = arg; - switch(qc->id) - { - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill_std(qc); - break; - default: - return -EINVAL; - } - return 0; + struct v4l2_queryctrl *ctrl = arg; + int id=ctrl->id; + + memset(ctrl,0,sizeof(*ctrl)); + ctrl->id=id; + + call_i2c_clients(usbvision, cmd, arg); + PDEBUG(DBG_RIO,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type); + + if (ctrl->type) + return 0; + else + return -EINVAL; + } case VIDIOC_G_CTRL: { struct v4l2_control *ctrl = arg; - PDEBUG(DBG_IOCTL, "VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); - switch(ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - /* ctrl->value = usbvision->volume; */ - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = usbvision->AudioMute; - break; - default: - return -EINVAL; - } + + call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); + PDEBUG(DBG_RIO,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); return 0; } case VIDIOC_S_CTRL: { struct v4l2_control *ctrl = arg; - call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); - PDEBUG(DBG_RIO, "%s: VIDIOC_S_CTRL id=%x value=%x", __FUNCTION__,ctrl->id,ctrl->value); + call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); + PDEBUG(DBG_RIO, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value); return 0; } case VIDIOC_G_TUNER: { - struct v4l2_tuner *vt = arg; + struct v4l2_tuner *t = arg; - if (!usbvision->have_tuner || vt->index) // Only tuner 0 + if (t->index > 0) return -EINVAL; - strcpy(vt->name, "Radio"); - vt->type = V4L2_TUNER_RADIO; - vt->capability = V4L2_TUNER_CAP_STEREO; - // japan: 76.0 MHz - 89.9 MHz - // western europe: 87.5 MHz - 108.0 MHz - // russia: 65.0 MHz - 108.0 MHz - vt->rangelow = (int)(65*16);; - vt->rangehigh = (int)(108*16); - vt->audmode = V4L2_TUNER_MODE_STEREO; - vt->rxsubchans = V4L2_TUNER_SUB_STEREO; - call_i2c_clients(usbvision,VIDIOC_G_TUNER,&vt); - PDEBUG(DBG_RIO, "%s: VIDIOC_G_TUNER signal=%d", __FUNCTION__, vt->signal); + memset(t,0,sizeof(*t)); + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + /* Let clients fill in the remainder of this struct */ + call_i2c_clients(usbvision,VIDIOC_G_TUNER,t); + PDEBUG(DBG_RIO, "VIDIOC_G_TUNER signal=%x, afc=%x",t->signal,t->afc); return 0; } case VIDIOC_S_TUNER: { struct v4l2_tuner *vt = arg; - // Only channel 0 has a tuner - if((vt->index) || (usbvision->channel)) { + // Only no or one tuner for now + if (!usbvision->have_tuner || vt->index) return -EINVAL; - } - PDEBUG(DBG_RIO, "%s: VIDIOC_S_TUNER", __FUNCTION__); + /* let clients handle this */ + call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt); + + PDEBUG(DBG_RIO, "VIDIOC_S_TUNER"); return 0; } case VIDIOC_G_AUDIO: { - struct v4l2_audio *va = arg; - memset(va,0, sizeof(va)); - va->capability = V4L2_AUDCAP_STEREO; - strcpy(va->name, "Radio"); - PDEBUG(DBG_IOCTL, "VIDIOC_G_AUDIO"); + struct v4l2_audio *a = arg; + + memset(a,0,sizeof(*a)); + strcpy(a->name,"Radio"); + PDEBUG(DBG_RIO, "VIDIOC_G_AUDIO"); return 0; } case VIDIOC_S_AUDIO: - { - struct v4l2_audio *v = arg; - if(v->index) { - return -EINVAL; - } - PDEBUG(DBG_IOCTL, "VIDIOC_S_AUDIO"); - // FIXME: void function ??? - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *freq = arg; - freq->tuner = 0; // Only one tuner - freq->type = V4L2_TUNER_RADIO; - freq->frequency = usbvision->freq; - PDEBUG(DBG_RIO, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)freq->frequency); - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *freq = arg; - usbvision->freq = freq->frequency; - call_i2c_clients(usbvision, cmd, freq); - PDEBUG(DBG_RIO, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)freq->frequency); - return 0; - } + case VIDIOC_S_INPUT: + case VIDIOC_S_STD: + return 0; - /*************************** - * V4L1 IOCTLs * - ***************************/ - case VIDIOCGCAP: - { - struct video_capability *vc = arg; - - memset(vc, 0, sizeof(struct video_capability)); - strcpy(vc->name,usbvision->vcap.card); - vc->type = VID_TYPE_TUNER; - vc->channels = 1; - vc->audios = 1; - PDEBUG(DBG_RIO, "%s: VIDIOCGCAP", __FUNCTION__); - return 0; - } - case VIDIOCGTUNER: + case VIDIOC_G_FREQUENCY: { - struct video_tuner *vt = arg; + struct v4l2_frequency *f = arg; - if((vt->tuner) || (usbvision->channel)) { /* Only tuner 0 */ - return -EINVAL; - } - strcpy(vt->name, "Radio"); - // japan: 76.0 MHz - 89.9 MHz - // western europe: 87.5 MHz - 108.0 MHz - // russia: 65.0 MHz - 108.0 MHz - vt->rangelow=(int)(65*16); - vt->rangehigh=(int)(108*16); - vt->flags= 0; - vt->mode = 0; - call_i2c_clients(usbvision,cmd,vt); - PDEBUG(DBG_RIO, "%s: VIDIOCGTUNER signal=%d", __FUNCTION__, vt->signal); - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner *vt = arg; + memset(f,0,sizeof(*f)); - // Only channel 0 has a tuner - if((vt->tuner) || (usbvision->channel)) { - return -EINVAL; - } - PDEBUG(DBG_RIO, "%s: VIDIOCSTUNER", __FUNCTION__); - return 0; - } - case VIDIOCGAUDIO: - { - struct video_audio *va = arg; - memset(va,0, sizeof(struct video_audio)); - call_i2c_clients(usbvision, cmd, va); - va->flags|=VIDEO_AUDIO_MUTABLE; - va->volume=1; - va->step=1; - strcpy(va->name, "Radio"); - PDEBUG(DBG_RIO, "%s: VIDIOCGAUDIO", __FUNCTION__); - return 0; - } - case VIDIOCSAUDIO: - { - struct video_audio *va = arg; - if(va->audio) { - return -EINVAL; - } + f->type = V4L2_TUNER_RADIO; + f->frequency = usbvision->freq; + call_i2c_clients(usbvision, cmd, f); + PDEBUG(DBG_RIO, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)f->frequency); - if(va->flags & VIDEO_AUDIO_MUTE) { - if (usbvision_audio_mute(usbvision)) { - return -EFAULT; - } - } - else { - if (usbvision_audio_on(usbvision)) { - return -EFAULT; - } - } - PDEBUG(DBG_RIO, "%s: VIDIOCSAUDIO flags=0x%x)", __FUNCTION__, va->flags); return 0; } - case VIDIOCGFREQ: + case VIDIOC_S_FREQUENCY: { - unsigned long *freq = arg; + struct v4l2_frequency *f = arg; - *freq = usbvision->freq; - PDEBUG(DBG_RIO, "%s: VIDIOCGFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4); - return 0; - } - case VIDIOCSFREQ: - { - unsigned long *freq = arg; + if (f->tuner != 0) + return -EINVAL; + usbvision->freq = f->frequency; + call_i2c_clients(usbvision, cmd, f); + PDEBUG(DBG_RIO, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)f->frequency); - usbvision->freq = *freq; - call_i2c_clients(usbvision, cmd, freq); - PDEBUG(DBG_RIO, "%s: VIDIOCSFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4); return 0; } default: @@ -5102,7 +5002,7 @@ static int usbvision_vbi_open(struct inode *inode, struct file *file) //usbvision->vbi = 1; call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type); freq = 1517; //SWR3 @ 94.8MHz - call_i2c_clients(usbvision, VIDIOCSFREQ, &freq); + call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, &freq); usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); usbvision->user++; } -- cgit v0.10.2 From cefccc8011c58b559498396f0ccefa0a0ffa3f53 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 4 Dec 2006 08:31:35 -0300 Subject: V4L/DVB (4934): Usbvision radio requires GainNormal at e register Adds an option to select GainNormal at tda9887 and make usbvision to use it. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index 87ffb0e..fde576f 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -482,6 +482,12 @@ static int tda9887_set_config(struct tuner *t, char *buf) buf[1] &= ~cQSS; if (t->tda9887_config & TDA9887_GATING_18) buf[3] &= ~cGating_36; + + if (t->tda9887_config & TDA9887_GAIN_NORMAL) { + radio_stereo.e &= ~cTunerGainLow; + radio_mono.e &= ~cTunerGainLow; + } + return 0; } diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 0365c7f..b5aa318 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -2585,12 +2585,15 @@ static int attach_inform(struct i2c_client *client) case 0x4b: { struct tuner_setup tun_setup; + unsigned int tda9887_conf = TDA9887_GAIN_NORMAL; tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; tun_setup.type = TUNER_TDA9887; tun_setup.addr = client->addr; call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); + call_i2c_clients(usbvision,TDA9887_SET_CONFIG, &tda9887_conf); + break; } case 0x42: diff --git a/include/media/tuner.h b/include/media/tuner.h index 3116e75..99acf84 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -145,6 +145,7 @@ extern int tuner_debug; #define TDA9887_DEEMPHASIS_75 (3<<16) #define TDA9887_AUTOMUTE (1<<18) #define TDA9887_GATING_18 (1<<19) +#define TDA9887_GAIN_NORMAL (1<<20) #ifdef __KERNEL__ -- cgit v0.10.2 From 483deb0f2b38060c1f2de216c09f05b1869b0d52 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 4 Dec 2006 08:31:38 -0300 Subject: V4L/DVB (4935): Added the capability of selecting fm gain by tuner Some tuners require using cGainNormal instead of cGainLow for high sensibility on FM reception. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c index fcf8693..1b9b074 100644 --- a/drivers/media/video/tuner-simple.c +++ b/drivers/media/video/tuner-simple.c @@ -467,6 +467,8 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq) config |= TDA9887_INTERCARRIER; /* if (params->port1_set_for_fm_mono) config &= ~TDA9887_PORT1_ACTIVE;*/ + if (params->fm_gain_normal) + config |= TDA9887_GAIN_NORMAL; i2c_clients_command(c->adapter, TDA9887_SET_CONFIG, &config); } if (4 != (rc = i2c_master_send(c,buffer,4))) diff --git a/include/media/tuner-types.h b/include/media/tuner-types.h index 37dad07..e5ad3fc 100644 --- a/include/media/tuner-types.h +++ b/include/media/tuner-types.h @@ -50,6 +50,10 @@ struct tuner_params { sensitivity. If this setting is 1, then set PORT2 to 1 to get proper FM reception. */ unsigned int port2_fm_high_sensitivity:1; + /* Some Philips tuners use tda9887 cGainNormal to select the FM radio + sensitivity. If this setting is 1, e register will use cGainNormal + instead of cGainLow. */ + unsigned int fm_gain_normal:1; /* Most tuners with a tda9887 use QSS mode. Some (cheaper) tuners use Intercarrier mode. If this setting is 1, then the tuner needs to be set to intercarrier mode. */ -- cgit v0.10.2 From d8159a3684007e0ded915cb7465c9534a2650c53 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Mon, 4 Dec 2006 08:31:42 -0300 Subject: V4L/DVB (4936): Make MT4049FM5 tuner to set FM Gain to Normal - remove any specific TDA9887_SET_CONFIG of usbvision driver - add fm_gain_normal=1 to the MT4049FM5 tuner for radio functionality Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/tuner-types.c b/drivers/media/video/tuner-types.c index 1256c64..74c3e6f 100644 --- a/drivers/media/video/tuner-types.c +++ b/drivers/media/video/tuner-types.c @@ -651,6 +651,7 @@ static struct tuner_params tuner_microtune_4049_fm5_params[] = { .has_tda9887 = 1, .port1_invert_for_secam_lc = 1, .default_pll_gating_18 = 1, + .fm_gain_normal=1, }, }; diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index b5aa318..0eb3ddc 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -2585,14 +2585,12 @@ static int attach_inform(struct i2c_client *client) case 0x4b: { struct tuner_setup tun_setup; - unsigned int tda9887_conf = TDA9887_GAIN_NORMAL; tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; tun_setup.type = TUNER_TDA9887; tun_setup.addr = client->addr; call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); - call_i2c_clients(usbvision,TDA9887_SET_CONFIG, &tda9887_conf); break; } -- cgit v0.10.2 From 483dfdb64fd4a9f240c84e0e225a90c044d65402 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Mon, 4 Dec 2006 08:31:45 -0300 Subject: V4L/DVB (4937): Usbvision cleanup and code reorganization - removal of overlay stuff - reorganization of functions in 3 files: * usbvision-i2c for I2C-related stuff * usbvision-video for v4l2 entry points * usbvision-core for all peripheral controls and utilities Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/Makefile b/drivers/media/video/usbvision/Makefile index ebdf78a..9ac92a8 100644 --- a/drivers/media/video/usbvision/Makefile +++ b/drivers/media/video/usbvision/Makefile @@ -1,4 +1,4 @@ -usbvision-objs := usbvision-core.o usbvision-i2c.o usbvision-cards.o +usbvision-objs := usbvision-core.o usbvision-video.o usbvision-i2c.o usbvision-cards.o obj-$(CONFIG_VIDEO_USBVISION) += usbvision.o diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c index 16c8890..f1fb9e7 100644 --- a/drivers/media/video/usbvision/usbvision-cards.c +++ b/drivers/media/video/usbvision/usbvision-cards.c @@ -30,7 +30,6 @@ #include "usbvision.h" /* Supported Devices: A table for usbvision.c*/ - struct usbvision_device_data_st usbvision_device_data[] = { {0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"}, {0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Xanboo"}, diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 0eb3ddc..cf787d8 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -1,11 +1,12 @@ /* - * USB USBVISION Video device driver 0.9.8.3cvs (For Kernel 2.4.19-2.4.32 + 2.6.0-2.6.16) - * - * + * usbvision-core.c - driver for NT100x USB video capture devices + * * * Copyright (c) 1999-2005 Joerg Heckenbach + * Dwaine Garden * * This module is part of usbvision driver project. + * Updates to driver completed by Dwaine P. Garden * * 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 @@ -20,309 +21,6 @@ * 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. - * - * Let's call the version 0.... until compression decoding is completely - * implemented. - * - * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach. - * It was based on USB CPiA driver written by Peter Pregler, - * Scott J. Bertin and Johannes Erdfelt - * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler & - * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink - * Updates to driver completed by Dwaine P. Garden - * - * History: - * - * Mar. 2000 - 15.12.2000: (0.0.0 - 0.2.0) - * Several alpha drivers and the first beta. - * - * Since Dec. 2000: (0.2.1) or (v2.1) - * Code changes or updates by Dwaine Garden and every other person. - * - * Added: New Hauppauge TV device Vendor ID: 0x0573 - * Product ID: 0x4D01 - * (Thanks to Giovanni Garberoglio) - * - * Added: UK Hauppauge WinTV-USB Vendor ID: 0x0573 - * Product ID: 0x4D02 - * (Thanks to Derek Freeman-Jones) - * - * Feb, 2001 - Apr 08, 2001: (0.3.0) - * - Some fixes. Driver is now more stable. - * - Scratch is organized as ring-buffer now for better performance - * - DGA (overlay) is now supported. - * !!!!Danger!!!! Clipping is not yet implemented. Your system will - * crash if your video window leaves the screen!!! - * - Max. Framesize is set to 320x240. There isn't more memory on the - * nt1003 video device for the FIFO. - * - Supported video palettes: RGB565, RGB555, RGB24, RGB32 - * - * - * Apr 15, 2001: (0.3.1-test...) - * - Clipping is implemented - * - NTSC is now coloured (Thanks to Dwaine Garden) - * - Added SECAM colour detection in saa7111-new - * - Added: French Hauppauge WinTV USB Vendor ID: 0x0573 - * Product ID: 0x4D03 - * (Thanks to Julius Hrivnac) - * - Added: US Hauppauge WINTV USB Vendor ID: 0x0573 - * Product ID: 0x4D00 - * (Thanks to Derrick J Brashear) - * - Changes for adding new devices. There's now a table in usbvision.h. - * Adding your devices data to the usbvision_device_data table is all - * you need to add a new device. - * - * May 11, 2001: (0.3.2-test...) (Thanks to Derek Freeman-Jones) - * - Support YUV422 raw format for people with hardware scaling. - * - Only power on the device when opened (use option PowerOnAtOpen=0 to disable it). - * - Turn off audio so we can listen to Line In. - * - * July 5, 2001 - (Patch the driver to run with Kernel 2.4.6) - * - Fixed a problem with the number of parameters passed to video_register_device. - * - * July 6, 2001 - Added: HAUPPAUGE WINTV-USB FM USA Vendor ID: 0x0573 - * Product ID: 0x4D10 - * (Thanks to Braddock Gaskill) - * Added: USBGear USBG-V1 resp. HAMA USB - * Vendor ID: 0x0573 - * Product ID: 0x0003 - * (Thanks to Bradley A. Singletary and Juergen Weigert) - * - * Jan 24, 2002 - (0.3.3-test...) - * - Moved all global variables that are device specific the usb_usbvision struct - * - Fixed the 64x48 unchangable image in xawtv when starting it with overlay - * - Add VideoNorm and TunerType to the usb_device_data table - * - Checked the audio channels and mute for HAUPPAUGE WinTV USB FM - * - Implemented the power on when opening the device. But some software opens - * the device several times when starting. So the i2c parts are just registered - * by an open, when they become deregistered by the next close. You can speed - * up tuner detection, when adding "options tuner addr=your_addr" to /etc/modules.conf - * - Begin to resize the frame in width and height. So it will be possible to watch i.e. - * 384x288 pixels at 23 fps. - * - * Feb 10, 2002 - * - Added radio device - * - * - * Jul 30, 2002 - (Thanks Cameron Maxwell) - * - Changes to usbvision.h --fixed usbvision device data structure, incorrectly had (0x0573, 0x4d21) for WinTV-USB II, should be 0x4d20. - * - Changes for device WinTV-USB II (0x0573. 0x4D21). It does not have a FM tuner. - * - Added the real device HAUPPAUGE WINTV-USB II (PAL) to the device structure in usbvision.h. - * - Changes to saa7113-new, the video is 8 bit data for the Phillips SAA7113 not 16bit like SAA7111. - * - Tuned lots of setup registers for the Phillips SAA7113 video chipset. - * - Changes to the supplied makefile. (Dwaine Garden) Still needs to be fixed so it will compile modules on different distrubutions. - * - * - * Aug 10, 2002 - (Thanks Mike Klinke) - * - Changes to usbvision.txt -- Fixed instructions on the location to copy the contents of the tgz file. - * - Added device WinTV-USB FM Model 621 (0x0573. 0x4D30). There is another device which carries the same name. Kept that device in the device structure. - * - * Aug 12, 2002 - Dwaine Garden - * - Added the ability to read the NT100x chip for the MaxISOPacketLength and USB Bandwidth - * Setting of the video device. - * - Adjustments to the SAA7113H code for proper video output. - * - Changes to usbvision.h, so all the devices with FM tuners are working. - * - * Feb 10, 2003 - Joerg Heckenbach - * - fixed endian bug for Motorola PPC - * - * Feb 13, 2003 - Joerg Heckenbach - * - fixed Vin_Reg setting and presetting from usbvision_device_data() - * - * Apr 19, 2003 - Dwaine Garden - * - Fixed compiling errors under RedHat v9.0. from uvirt_to_kva and usbvision_mmap. (Thanks Cameron Maxwell) - * - Changed pte_offset to pte_offset_kernel. - * - Changed remap_page_range and added additional parameter to function. - * - Change setup parameters for the D-Link V100 USB device - * - Added a new device to the usbvision driver. Pinnacle Studio PCTV USB (PAL) 0x2304 0x0110 - * - Screwed up the sourceforge.net cvs respository! 8*) - * - * Apr 22, 2002 - Dwaine Garden - * - Added a new device to the usbvision driver. Dazzle DVC-80 (PAL) 0x07d0 0x0004. (Thanks Carl Anderson) - * - Changes to some of the comments. - * - * June 06, 2002 - Ivan, Dwaine Garden - * - Ivan updates for fine tuning device parameters without driver recompiling. (Ivan) - * - Changes to some of the comments. (Dwaine Garden) - * - Changes to the makefile - Better CPU settings. (Ivan) - * - Changes to device Hauppauge WinTv-USB III (PAL) FM Model 568 - Fine tuning parameters (Ivan) - * - * - * Oct 16, 2003 - 0.9.0 - Joerg Heckenbach - * - Implementation of the first part of the decompression algorithm for intra frames. - * The resolution has to be 320x240. A dynamic adaption of compression deepth is - * missing yet. - * - * Oct 22, 2003 - 0.9.1 - Joerg Heckenbach - * - Implementation of the decompression algorithm for inter frames. - * The resolution still has to be 320x240. - * - * Nov 2003 - Feb 2004 - 0.9.2 to 0.9.3 - Joerg Heckenbach - * - Implement last unknown compressed block type. But color is still noisy. - * - Finding criteria for adaptive compression adjustment. - * - Porting to 2.6 kernels, but still working under 2.4 - * - * Feb 04, 2004 - 0.9.4 Joerg Heckenbach - * - Found bug in color decompression. Color is OK now. - * - * Feb 09, 2004 - 0.9.5 Joerg Heckenbach - * - Add auto-recognition of chip type NT1003 or NT1004. - * - Add adaptive compression adjustment. - * - Patched saa7113 multiplexer switching (Thanks to Orlando F.S. Bordoni) - * - * Feb 24, 2004 - 0.9.6 Joerg Heckenbach - * - Add a timer to wait before poweroff at close, to save start time in - * some video applications - * - * Mar 4, 2004 - 0.9.6 Dwaine Garden - * - Added device Global Village GV-007 (NTSC) to usbvision.h (Thanks to Abe Skolnik) - * - Forgot to add this device to the driver. 8*) - * - * June 2, 2004 - 0.9.6 Dwaine Garden - * - Fixed sourceforge.net cvs repository. - * - Added #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,26) for .owner to help compiling under kernels 2.4.x which do not have the i2c v2.8.x updates. - * - Added device Hauppauge WinTv-USB III (PAL) FM Model 597 to usbvision.h - * - * July 1, 2004 -0.9.6 Dwaine Garden - * - Patch was submitted by Hal Finkel to fix the problem with the tuner not working under kernel 2.6.7. - * - Thanks Hal..... - * - * July 30, 2004 - 0.9.6 Dwaine Garden - * - Patch was submitted by Tobias Diaz to fix Model ID mismatch in usbvision.h. - * - Thanks..... - * - * August 12, 2004 - 0.9.6 Dwaine Garden - * - Updated the readme file so people could install the driver under the configuration file for kernel 2.6.x recompiles. Now people can use make xconfig! - * - Added new device "Camtel Technology Corp TVB330-USB FM". - * - Sourceforge.net CVS has been updated with all the changes. - * - * August 20, 2004 - 0.9.7 Dwaine Garden - * - Added Device "Hauppauge USB Live Model 600" - * - Fixed up all the devices which did not have a default tuner type in usbvision.h. It's best guess, at least until someone with the device tells me otherwise. - * - Sourceforge.net CVS has been updated with all the changes. - * - Clean up the driver. - * - * September 13, 2004 - 0.9.8 Dwaine Garden - * - Changed usbvision_muxsel to address the problem with black & white s-video output for NT1004 devices with saa7114 video decoder. Thanks to Emmanuel for the patch and testing. - * - Fixed up SECAM devices which could not properly output video. Changes to usbmuxsel. Thanks to Emmanuel for the patch and everyone with a SECAM device which help test. - * - Removed some commented out code. Clean up. - * - Tried to fix up the annoying empty directories in the sourceforge.net cvs. Fuck it up again. 8*( - * - * November 15, 2004 - 0.9.8 Dwaine Garden - * - Release new tar - 0.9.8 on sourceforge.net - * - Added some new devices to usbvision.h WinTV USB Model 602 40201 Rev B282, Hauppague WinTV USB Model 602 40201 Rev B285 - * - Added better compatibility for 2.6.x kernels. - * - Hardware full screen scaling in grabdisplay mode. - * - Better support for sysfs. More code to follow for both video device and radio device. Device information is located at /sys/class/video4linux/video0 - * - Added module_param so loaded module parameters are displayed in sysfs. Driver parameters should show up in /sys/module/usbvision - * - Adjusted the SAA7111 registers to match the 2.6.x kernel SAA7111 code. Thanks to the person which helped test. - * - Changed to wait_event_interruptible. For all the people running Fedora 2. - * - Added some screenshots of actual video captures on sourceforge.net. - * - * November 24, 2004 - 0.9.8.1cvs Dwaine Garden - * - Added patch to check for palette and format in VIDIOCSPICT. Helix Producer should work fine with the driver now. Thanks Jason Simpson - * - Two device description changes and two additions for the maintainer of usb.ids. - * - * December 2, 2004 - 0.9.8.1cvs Dwaine Garden - * - Added patch for YUV420P and YUV422P video output. Thanks to Alex Smith. - * - Better support for mythtv. - * - * January 2, 2005 - 0.9.8.1cvs Dwaine Garden - * - Setup that you can specify which device is used for video. Default is auto detect next available device number eg. /dev/videoX - * - Setup that you can specify which device is used for radio. Default is auto detect next available device number eg. /dev/radioX - * - usb_unlink_urb() is deprecated for synchronous unlinks. Using usb_kill_urb instead. - * - usbvision_kvirt_to_pa is deprecated. Removed. - * - Changes are related to kernel changes for 2.6.10. (Fedora 4) - * - * February 2, 2005 - 0.9.8.1cvs Dwaine Garden - * - Added a new device to usbvision.h Dazzle DVC 50. Thanks to Luiz S. - * - * March 29, 2005 - 0.9.8.1cvs Dwaine Garden - * - Fixed compile error with saa7113 under kernels 2.6.11+ - * - Added module parameter to help people with Black and White output with using s-video input. Some cables and input device are wired differently. - * - Removed the .id from the i2c usbvision template. There was a change to the i2c with kernels 2.6.11+. - * - * April 9, 2005 - 0.9.8.1cvs Dwaine Garden - * - Added in the 2.4 and 2.6 readme files the SwitchSVideoInput parameter information. This will help people setup the right values for the parameter. - * If your device experiences Black and White images with the S-Video Input. Set this parameter to 1 when loading the module. - * - Replaced the wrong 2.6 readme file. I lost the right version. Someone sent me the right version by e-mail. Thanks. - * - Released new module version on sourceforge.net. So everyone can enjoy all the fixes and additional device support. - * - * April 20, 2005 - 0.9.8.2cvs Dwaine Garden - * - Release lock in usbvision_v4l_read_done. -Thanks to nplanel for the patch. - * - Additional comments to the driver. - * - Fixed some spelling mistakes. 8*) - * - * April 23, 2005 - 0.9.8.2cvs Joerg Heckenbach - * - Found bug in usbvision line counting. Now there should be no spurious lines in the image any longer. - * - Swapped usbvision_register_video and usbvision_configure_video to fix problem with PowerOnAtOpen=0. - * Thanks to Erwan Velu - * - * April 26, 2005 - 0.9.8.2cvs Joerg Heckenbach - * - Fixed problem with rmmod module and oppses. Replaced vfree(usbvision->overlay_base) with iounmap(usbvision->overlay_base). - * - Added function usb_get_dev(dev) and ; To help with unloading the module multiple times without crashing. - * (Keep the reference count in kobjects correct) - * - * June 14, 2005 - 0.9.8.2cvs Dwaine - * - Missed a change in saa7113.c for checking kernel version. Added conditional if's. - * - * June 15, 2005 - 0.9.8.2cvs Dwaine - * - Added new device WinTV device VendorId 0573 and ProductId 4d29. - * - Hacked some support for newer NT1005 devices. This devices only seem to have one configuration, not multiple configurations like the NT1004. - * - * June 29, 2005 - 0.9.8.2cvs Dwaine - * - Added new device WinTV device VendorId 0573 and ProductId 4d37. - * - Because of the first empty entry in usbvision_table, modutils failed to create the necessary entries for modules.usbmap. - * This means hotplug won't work for usbvision. Thanks to Gary Ng. - * - Sent an e-mail to the maintainer of usb.ids. New devices identified need to be added. - * - Fixed compile error with saa7113 under kernel 2.6.12. - * - * July 6, 2005 - 0.9.8.2cvs Dwaine - * - Patch submitted by Gary Ng for two additional procfs entries. Device Input and Frequency setting. - * - * July 12, 2005 - 0.9.8.2cvs Dwaine - * - New tuner identified for some devices it's called TCL_MFPE05. This tuner uses the same API as tuner 38 in tuner.c. - * - Thanks to lynx31 for contacting Hauppage and asking them. - * - I have no clue as to which devices use this new tuner, so people will have to contact me and tell me. - * - * July 21, 2005 - 0.9.8.2cvs Dwaine - * - Patched usbvision.c with missing ifdef kernversion statement so the module will compile with older kernels and v4l. - * - Thanks to cipe007...... - * - * May 19, 2006 - 0.9.8.3cvs Dwaine - * - Patched usbvision.c and i2c-algo.c so they will compile with kernel 2.6.16 - * - Adjust device "Pinnacle Studio PCTV USB (PAL) FM" values in usbvision.h - * - * May 24, 2006 - 0.9.8.3cvs Dwaine - * -Pinnacle Studio PCTV USB (NTSC) FM uses saa7111, not saa7113 like first thought. - * -Updated usbvision.h - * - * Aug 15, 2006 - 0.9.8.3cvs Dwaine - * -Added saa711x module into cvs, since the newer saa7115 module in newer kernels is v4l2. The usbvision driver is only v4l. - * -Updated makefile to put compiled modules into correct location. - * - * Aug 21, 2006 - 0.9.8.3cvs Dwaine - * -Changed number of bytes for i2c write to 4 as per the NT100X spec sheet. Thanks to Merlum for finding it. - * -Remove the radio option for device Hauppauge WinTV USB device Model 40219 Rev E189. This device does not have a FM radio. Thanks to Shadwell. - * -Added radio option for device Hauppauge WinTV USB device Model 40219 Rev E189 again. Just got an e-mail indicating their device has one. 8*) - * - * Aug 27, 2006 - 0.9.8.3cvs Dwaine - * -Changed ifdef statement so the usbvision driver will compile with kernels at 2.6.12. - * -Updated readme files for new updated tuner list for v4l devices. - * - * - * - * TODO: - * - use submit_urb for all setup packets - * - Fix memory settings for nt1004. It is 4 times as big as the - * nt1003 memory. - * - Add audio on endpoint 3 for nt1004 chip. Seems impossible, needs a codec interface. Which one? - * - Clean up the driver. - * - optimization for performance. - * - Add Videotext capability (VBI). Working on it..... - * - Check audio for other devices - * - Add v4l2 interface - * */ #include @@ -344,13 +42,6 @@ #include #include -#define USBVISION_DRIVER_VERSION_MAJOR 0 -#define USBVISION_DRIVER_VERSION_MINOR 8 -#define USBVISION_DRIVER_VERSION_PATCHLEVEL 0 - -#define USBVISION_VERSION __stringify(USBVISION_DRIVER_VERSION_MAJOR) "." __stringify(USBVISION_DRIVER_VERSION_MINOR) "." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL) " " USBVISION_DRIVER_VERSION_COMMENT -#define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,USBVISION_DRIVER_VERSION_MINOR,USBVISION_DRIVER_VERSION_PATCHLEVEL) - #include #include #include @@ -365,99 +56,44 @@ #include "usbvision.h" -#define DRIVER_VERSION "0.9.8.3cvs for Linux kernels 2.4.19-2.4.32 + 2.6.0-2.6.17, compiled at "__DATE__", "__TIME__ -#define EMAIL "joerg@heckenbach-aw.de" -#define DRIVER_AUTHOR "Joerg Heckenbach , Dwaine Garden " -#define DRIVER_DESC "USBVision USB Video Device Driver for Linux" -#define DRIVER_LICENSE "GPL" -#define DRIVER_ALIAS "USBVision" +static unsigned int core_debug = 0; +module_param(core_debug,int,0644); +MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); -#define ENABLE_HEXDUMP 0 /* Enable if you need it */ +static unsigned int force_testpattern = 0; +module_param(force_testpattern,int,0644); +MODULE_PARM_DESC(force_testpattern,"enable test pattern display [core]"); + +static int adjustCompression = 1; // Set the compression to be adaptive +module_param(adjustCompression, int, 0444); +MODULE_PARM_DESC(adjustCompression, " Set the ADPCM compression for the device. Default: 1 (On)"); + +static int SwitchSVideoInput = 0; // To help people with Black and White output with using s-video input. Some cables and input device are wired differently. +module_param(SwitchSVideoInput, int, 0444); +MODULE_PARM_DESC(SwitchSVideoInput, " Set the S-Video input. Some cables and input device are wired differently. Default: 0 (Off)"); +#define ENABLE_HEXDUMP 0 /* Enable if you need it */ -#define USBVISION_DEBUG /* Turn on debug messages */ #ifdef USBVISION_DEBUG #define PDEBUG(level, fmt, args...) \ - if (debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args) + if (core_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args) #else #define PDEBUG(level, fmt, args...) do {} while(0) #endif -#define DBG_IOCTL 1<<3 -#define DBG_IO 1<<4 -#define DBG_RIO 1<<5 -#define DBG_HEADER 1<<7 -#define DBG_PROBE 1<<8 -#define DBG_IRQ 1<<9 -#define DBG_ISOC 1<<10 -#define DBG_PARSE 1<<11 -#define DBG_SCRATCH 1<<12 -#define DBG_FUNC 1<<13 -#define DBG_I2C 1<<14 - -#define DEBUG(x...) /* General Debug */ -#define IODEBUG(x...) /* Debug IO */ -#define OVDEBUG(x...) /* Debug overlay */ -#define MDEBUG(x...) /* Debug memory management */ - -//String operations -#define rmspace(str) while(*str==' ') str++; -#define goto2next(str) while(*str!=' ') str++; while(*str==' ') str++; - - -static int usbvision_nr = 0; // sequential number of usbvision device -static unsigned long usbvision_timestamp = 0; // timestamp in jiffies of a hundred frame -static unsigned long usbvision_counter = 0; // frame counter +#define DBG_HEADER 1<<0 +#define DBG_IRQ 1<<1 +#define DBG_ISOC 1<<2 +#define DBG_PARSE 1<<3 +#define DBG_SCRATCH 1<<4 static const int max_imgwidth = MAX_FRAME_WIDTH; static const int max_imgheight = MAX_FRAME_HEIGHT; static const int min_imgwidth = MIN_FRAME_WIDTH; static const int min_imgheight = MIN_FRAME_HEIGHT; -#define FRAMERATE_MIN 0 -#define FRAMERATE_MAX 31 - - -enum { - ISOC_MODE_YUV422 = 0x03, - ISOC_MODE_YUV420 = 0x14, - ISOC_MODE_COMPRESS = 0x60, -}; - -static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { - { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" }, - { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" }, - { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" }, - { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" }, - { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" }, - { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" }, - { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, // 1.5 ! - { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" } -}; - -/* supported tv norms */ -static struct usbvision_tvnorm tvnorms[] = { - { - .name = "PAL", - .id = V4L2_STD_PAL, - }, { - .name = "NTSC", - .id = V4L2_STD_NTSC, - }, { - .name = "SECAM", - .id = V4L2_STD_SECAM, - }, { - .name = "PAL-M", - .id = V4L2_STD_PAL_M, - } -}; - -#define TVNORMS ARRAY_SIZE(tvnorms) - - -/* - * The value of 'scratch_buf_size' affects quality of the picture +/* The value of 'scratch_buf_size' affects quality of the picture * in many ways. Shorter buffers may cause loss of data when client * is too slow. Larger buffers are memory-consuming and take longer * to work with. This setting can be adjusted, but the default value @@ -467,229 +103,10 @@ static struct usbvision_tvnorm tvnorms[] = { static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE; // Function prototypes -static int usbvision_restart_isoc(struct usb_usbvision *usbvision); -static int usbvision_begin_streaming(struct usb_usbvision *usbvision); -static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel); -static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, short len); -static int usbvision_i2c_read(void *data, unsigned char addr, char *buf, short len); -static int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg); -static int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, unsigned char value); static int usbvision_request_intra (struct usb_usbvision *usbvision); static int usbvision_unrequest_intra (struct usb_usbvision *usbvision); static int usbvision_adjust_compression (struct usb_usbvision *usbvision); static int usbvision_measure_bandwidth (struct usb_usbvision *usbvision); -static void usbvision_release(struct usb_usbvision *usbvision); -static int usbvision_set_input(struct usb_usbvision *usbvision); -static int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height); -static void usbvision_empty_framequeues(struct usb_usbvision *dev); -static int usbvision_stream_interrupt(struct usb_usbvision *dev); -static void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, void *arg); - - -// Bit flags (options) -#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) -#define FLAGS_MONOCHROME (1 << 1) -#define FLAGS_DISPLAY_HINTS (1 << 2) -#define FLAGS_OSD_STATS (1 << 3) -#define FLAGS_FORCE_TESTPATTERN (1 << 4) -#define FLAGS_SEPARATE_FRAMES (1 << 5) -#define FLAGS_CLEAN_FRAMES (1 << 6) - -// Default initalization of device driver parameters -static int flags = 0; // Set the default Overlay Display mode of the device driver -static int debug = 0; // Set the default Debug Mode of the device driver -static int isocMode = ISOC_MODE_COMPRESS; // Set the default format for ISOC endpoint -static int adjustCompression = 1; // Set the compression to be adaptive -static int dga = 1; // Set the default Direct Graphic Access -static int PowerOnAtOpen = 1; // Set the default device to power on at startup -static int SwitchSVideoInput = 0; // To help people with Black and White output with using s-video input. Some cables and input device are wired differently. -static int video_nr = -1; // Sequential Number of Video Device -static int radio_nr = -1; // Sequential Number of Radio Device -static int vbi_nr = -1; // Sequential Number of VBI Device -static char *CustomDevice=NULL; // Set as nothing.... - -// Grab parameters for the device driver - -#if defined(module_param) // Showing parameters under SYSFS -module_param(flags, int, 0444); -module_param(debug, int, 0444); -module_param(isocMode, int, 0444); -module_param(adjustCompression, int, 0444); -module_param(dga, int, 0444); -module_param(PowerOnAtOpen, int, 0444); -module_param(SwitchSVideoInput, int, 0444); -module_param(video_nr, int, 0444); -module_param(radio_nr, int, 0444); -module_param(vbi_nr, int, 0444); -module_param(CustomDevice, charp, 0444); -#else // Old Style -MODULE_PARM(flags, "i"); // Grab the Overlay Display mode of the device driver -MODULE_PARM(debug, "i"); // Grab the Debug Mode of the device driver -MODULE_PARM(isocMode, "i"); // Grab the video format of the video device -MODULE_PARM(adjustCompression, "i"); // Grab the compression to be adaptive -MODULE_PARM(dga, "i"); // Grab the Direct Graphic Access -MODULE_PARM(PowerOnAtOpen, "i"); // Grab the device to power on at startup -MODULE_PARM(SwitchSVideoInput, "i"); // To help people with Black and White output with using s-video input. Some cables and input device are wired differently. -MODULE_PARM(video_nr, "i"); // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...) -MODULE_PARM(radio_nr, "i"); // radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...) -MODULE_PARM(vbi_nr, "i"); // vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...) -MODULE_PARM(CustomDevice, "s"); // .... CustomDevice -#endif - -MODULE_PARM_DESC(flags, " Set the default Overlay Display mode of the device driver. Default: 0 (Off)"); -MODULE_PARM_DESC(debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)"); -MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); -MODULE_PARM_DESC(adjustCompression, " Set the ADPCM compression for the device. Default: 1 (On)"); -MODULE_PARM_DESC(dga, " Set the Direct Graphic Access for the device. Default: 1 (On)"); -MODULE_PARM_DESC(PowerOnAtOpen, " Set the default device to power on when device is opened. Default: 1 (On)"); -MODULE_PARM_DESC(SwitchSVideoInput, " Set the S-Video input. Some cables and input device are wired differently. Default: 0 (Off)"); -MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); -MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); -MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX). Default: -1 (autodetect)"); -MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the device. Default: null"); - - -// Misc stuff -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE(DRIVER_LICENSE); - MODULE_VERSION(DRIVER_VERSION); - MODULE_ALIAS(DRIVER_ALIAS); - -#ifdef MODULE -static unsigned int autoload = 1; -#else -static unsigned int autoload = 0; -#endif - - -/****************************************************************************************/ -/* SYSFS Code - Copied from the stv680.c usb module. */ -/* Device information is located at /sys/class/video4linux/video0 */ -/* Device parameters information is located at /sys/module/usbvision */ -/* Device USB Information is located at /sys/bus/usb/drivers/USBVision Video Grabber */ -/****************************************************************************************/ - - -#define YES_NO(x) ((x) ? "Yes" : "No") - -static inline struct usb_usbvision *cd_to_usbvision(struct class_device *cd) -{ - struct video_device *vdev = to_video_device(cd); - return video_get_drvdata(vdev); -} - -static ssize_t show_version(struct class_device *cd, char *buf) -{ - return sprintf(buf, "%s\n", DRIVER_VERSION); -} -static CLASS_DEVICE_ATTR(version, S_IRUGO, show_version, NULL); - -static ssize_t show_model(struct class_device *class_dev, char *buf) -{ - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%s\n", usbvision_device_data[usbvision->DevModel].ModelString); -} -static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL); - -static ssize_t show_hue(struct class_device *class_dev, char *buf) -{ - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%d\n", usbvision->hue >> 8); -} -static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); - -static ssize_t show_contrast(struct class_device *class_dev, char *buf) -{ - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%d\n", usbvision->contrast >> 8); -} -static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); - -static ssize_t show_brightness(struct class_device *class_dev, char *buf) -{ - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%d\n", usbvision->brightness >> 8); -} -static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); - -static ssize_t show_saturation(struct class_device *class_dev, char *buf) -{ - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%d\n", usbvision->saturation >> 8); -} -static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); - -static ssize_t show_streaming(struct class_device *class_dev, char *buf) -{ - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%s\n", YES_NO(usbvision->streaming==Stream_On?1:0)); -} -static CLASS_DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL); - -static ssize_t show_overlay(struct class_device *class_dev, char *buf) -{ - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%s\n", YES_NO(usbvision->overlay)); -} -static CLASS_DEVICE_ATTR(overlay, S_IRUGO, show_overlay, NULL); - -static ssize_t show_compression(struct class_device *class_dev, char *buf) -{ - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%s\n", YES_NO(usbvision->isocMode==ISOC_MODE_COMPRESS)); -} -static CLASS_DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL); - -static ssize_t show_device_bridge(struct class_device *class_dev, char *buf) -{ - struct video_device *vdev = to_video_device(class_dev); - struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%d\n", usbvision->bridgeType); -} -static CLASS_DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL); - -static void usbvision_create_sysfs(struct video_device *vdev) -{ - int res; - if (vdev) { - res=video_device_create_file(vdev, &class_device_attr_version); - res=video_device_create_file(vdev, &class_device_attr_model); - res=video_device_create_file(vdev, &class_device_attr_hue); - res=video_device_create_file(vdev, &class_device_attr_contrast); - res=video_device_create_file(vdev, &class_device_attr_brightness); - res=video_device_create_file(vdev, &class_device_attr_saturation); - res=video_device_create_file(vdev, &class_device_attr_streaming); - res=video_device_create_file(vdev, &class_device_attr_overlay); - res=video_device_create_file(vdev, &class_device_attr_compression); - res=video_device_create_file(vdev, &class_device_attr_bridge); - } -} - -static void usbvision_remove_sysfs(struct video_device *vdev) -{ - if (vdev) { - video_device_remove_file(vdev, &class_device_attr_version); - video_device_remove_file(vdev, &class_device_attr_model); - video_device_remove_file(vdev, &class_device_attr_hue); - video_device_remove_file(vdev, &class_device_attr_contrast); - video_device_remove_file(vdev, &class_device_attr_brightness); - video_device_remove_file(vdev, &class_device_attr_saturation); - video_device_remove_file(vdev, &class_device_attr_streaming); - video_device_remove_file(vdev, &class_device_attr_overlay); - video_device_remove_file(vdev, &class_device_attr_compression); - video_device_remove_file(vdev, &class_device_attr_bridge); - } -} - /*******************************/ /* Memory management functions */ @@ -700,7 +117,6 @@ static void usbvision_remove_sysfs(struct video_device *vdev) * This is used when initializing the contents of the area. */ - void *usbvision_rvmalloc(unsigned long size) { void *mem; @@ -712,65 +128,58 @@ void *usbvision_rvmalloc(unsigned long size) return NULL; memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) - mem_map_reserve(vmalloc_to_page((void *)adr)); - #else - SetPageReserved(vmalloc_to_page((void *)adr)); - #endif - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; } void usbvision_rvfree(void *mem, unsigned long size) { - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) - mem_map_unreserve(vmalloc_to_page((void *)adr)); - #else - ClearPageReserved(vmalloc_to_page((void *)adr)); - #endif - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} + unsigned long adr; + + if (!mem) + return; + size = PAGE_ALIGN(size); + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} #if ENABLE_HEXDUMP static void usbvision_hexdump(const unsigned char *data, int len) { - char tmp[80]; - int i, k; - - for (i = k = 0; len > 0; i++, len--) { - if (i > 0 && (i % 16 == 0)) { - printk("%s\n", tmp); - k = 0; - } - k += sprintf(&tmp[k], "%02x ", data[i]); - } - if (k > 0) - printk("%s\n", tmp); + char tmp[80]; + int i, k; + + for (i = k = 0; len > 0; i++, len--) { + if (i > 0 && (i % 16 == 0)) { + printk("%s\n", tmp); + k = 0; + } + k += sprintf(&tmp[k], "%02x ", data[i]); + } + if (k > 0) + printk("%s\n", tmp); } #endif - -/* These procedures handle the scratch ring buffer */ +/******************************** + * scratch ring buffer handling + ********************************/ int scratch_len(struct usb_usbvision *usbvision) /*This returns the amount of data actually in the buffer */ { int len = usbvision->scratch_write_ptr - usbvision->scratch_read_ptr; @@ -800,13 +209,6 @@ int scratch_free(struct usb_usbvision *usbvision) } -void *debug_memcpy(void *dest, void *src, size_t len) -{ - printk(KERN_DEBUG "memcpy(%p, %p, %d);\n", dest, src, len); - return memcpy(dest, src, len); -} - - /* This puts data into the buffer */ int scratch_put(struct usb_usbvision *usbvision, unsigned char *data, int len) { @@ -964,247 +366,27 @@ void scratch_reset(struct usb_usbvision *usbvision) usbvision->isocstate = IsocState_NoFrame; } - - -/* Here comes the OVERLAY stuff */ - -/* Tell the interrupt handler what to to. */ -static -void usbvision_cap(struct usb_usbvision* usbvision, int on) -{ - DEBUG(printk(KERN_DEBUG "usbvision_cap: overlay was %d, set it to %d\n", usbvision->overlay, on);) - - if (on) { - usbvision->overlay = 1; - } - else { - usbvision->overlay = 0; - } -} - - - - -/* append a new clipregion to the vector of video_clips */ -static -void usbvision_new_clip(struct v4l2_format* vf, struct v4l2_clip* vcp, int x, int y, int w, int h) -{ - vcp[vf->fmt.win.clipcount].c.left = x; - vcp[vf->fmt.win.clipcount].c.top = y; - vcp[vf->fmt.win.clipcount].c.width = w; - vcp[vf->fmt.win.clipcount].c.height = h; - vf->fmt.win.clipcount++; -} - - -#define mark_pixel(x,y) usbvision->clipmask[((x) + (y) * MAX_FRAME_WIDTH)/32] |= 0x00000001<<((x)%32) -#define clipped_pixel(index) usbvision->clipmask[(index)/32] & (0x00000001<<((index)%32)) - -static -void usbvision_built_overlay(struct usb_usbvision* usbvision, int count, struct v4l2_clip *vcp) -{ - usbvision->overlay_win = usbvision->overlay_base + - (signed int)usbvision->vid_win.fmt.win.w.left * usbvision->depth / 8 + - (signed int)usbvision->vid_win.fmt.win.w.top * usbvision->vid_buf.fmt.bytesperline; - - IODEBUG(printk(KERN_DEBUG "built_overlay base=%p, win=%p, bpl=%d, clips=%d, size=%dx%d\n", - usbvision->overlay_base, usbvision->overlay_win, - usbvision->vid_buf.fmt.bytesperline, count, - usbvision->vid_win.fmt.win.w.width, usbvision->vid_win.fmt.win.w.height);) - - - /* Add here generation of clipping mask */ -{ - int x_start, x_end, y_start, y_end; - int clip_index, x, y; - - memset(usbvision->clipmask, 0, USBVISION_CLIPMASK_SIZE); - - OVDEBUG(printk(KERN_DEBUG "clips = %d\n", count);) - - for(clip_index = 0; clip_index < count; clip_index++) { - OVDEBUG(printk(KERN_DEBUG "clip: %d,%d,%d,%d\n", vcp[clip_index].x, - vcp[clip_index].y, - vcp[clip_index].width, - vcp[clip_index].height);) - - x_start = vcp[clip_index].c.left; - if(x_start >= (int)usbvision->vid_win.fmt.win.w.width) { - OVDEBUG(printk(KERN_DEBUG "x_start=%d\n", x_start);) - continue; //clipping window is right of overlay window - } - x_end = x_start + vcp[clip_index].c.width; - if(x_end <= 0) { - OVDEBUG(printk(KERN_DEBUG "x_end=%d\n", x_end);) - continue; //clipping window is left of overlay window - } - - y_start = vcp[clip_index].c.top; - if(y_start >= (int)usbvision->vid_win.fmt.win.w.height) { - OVDEBUG(printk(KERN_DEBUG "y_start=%d\n", y_start);) - continue; //clipping window is below overlay window - } - y_end = y_start + vcp[clip_index].c.height; - if(y_end <= 0) { - OVDEBUG(printk(KERN_DEBUG "y_end=%d\n", y_end);) - continue; //clipping window is above overlay window - } - - //clip the clipping window - if (x_start < 0) { - x_start = 0; - } - if (x_end > (int)usbvision->vid_win.fmt.win.w.width) { - x_end = (int)usbvision->vid_win.fmt.win.w.width; - } - if (y_start < 0) { - y_start = 0; - } - if (y_end > (int)usbvision->vid_win.fmt.win.w.height) { - y_end = (int)usbvision->vid_win.fmt.win.w.height; - } - - OVDEBUG(printk(KERN_DEBUG "clip_o: %d,%d,%d,%d\n", x_start, y_start, x_end, y_end);) - - - - for(y = y_start; y < y_end; y++) { - for(x = x_start; x < x_end; x++) { - mark_pixel(x,y); - } - } - } -} - -} - - - -void usbvision_osd_char(struct usb_usbvision *usbvision, - struct usbvision_frame *frame, int x, int y, int ch) +int usbvision_scratch_alloc(struct usb_usbvision *usbvision) { - static const unsigned short digits[16] = { - 0xF6DE, /* 0 */ - 0x2492, /* 1 */ - 0xE7CE, /* 2 */ - 0xE79E, /* 3 */ - 0xB792, /* 4 */ - 0xF39E, /* 5 */ - 0xF3DE, /* 6 */ - 0xF492, /* 7 */ - 0xF7DE, /* 8 */ - 0xF79E, /* 9 */ - 0x77DA, /* a */ - 0xD75C, /* b */ - 0xF24E, /* c */ - 0xD6DC, /* d */ - 0xF34E, /* e */ - 0xF348 /* f */ - }; - unsigned short digit; - int ix, iy; - - if ((usbvision == NULL) || (frame == NULL)) - return; - - if (ch >= '0' && ch <= '9') - ch -= '0'; - else if (ch >= 'A' && ch <= 'F') - ch = 10 + (ch - 'A'); - else if (ch >= 'a' && ch <= 'f') - ch = 10 + (ch - 'a'); - else - return; - digit = digits[ch]; - - for (iy = 0; iy < 5; iy++) { - for (ix = 0; ix < 3; ix++) { - if (digit & 0x8000) { - // USBVISION_PUTPIXEL(frame, x + ix, y + iy, - // 0xFF, 0xFF, 0xFF); - } - digit = digit << 1; - } + usbvision->scratch = vmalloc(scratch_buf_size); + scratch_reset(usbvision); + if(usbvision->scratch == NULL) { + err("%s: unable to allocate %d bytes for scratch", + __FUNCTION__, scratch_buf_size); + return -ENOMEM; } + return 0; } - -void usbvision_osd_string(struct usb_usbvision *usbvision, - struct usbvision_frame *frame, - int x, int y, const char *str) +void usbvision_scratch_free(struct usb_usbvision *usbvision) { - while (*str) { - usbvision_osd_char(usbvision, frame, x, y, *str); - str++; - x += 4; /* 3 pixels character + 1 space */ + if (usbvision->scratch != NULL) { + vfree(usbvision->scratch); + usbvision->scratch = NULL; } } /* - * usb_usbvision_osd_stats() - * - * On screen display of important debugging information. - * - */ -void usbvision_osd_stats(struct usb_usbvision *usbvision, - struct usbvision_frame *frame) -{ - const int y_diff = 8; - char tmp[16]; - int x = 10; - int y = 10; - - sprintf(tmp, "%8x", usbvision->frame_num); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", usbvision->isocUrbCount); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", usbvision->urb_length); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", usbvision->isocDataCount); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", usbvision->header_count); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", usbvision->scratch_ovf_count); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", usbvision->isocSkipCount); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", usbvision->isocErrCount); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", usbvision->saturation); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", usbvision->hue); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", usbvision->brightness >> 8); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", usbvision->contrast >> 12); - usbvision_osd_string(usbvision, frame, x, y, tmp); - y += y_diff; - -} - -/* * usbvision_testpattern() * * Procedure forms a test pattern (yellow grid on blue background). @@ -1287,14 +469,40 @@ void usbvision_testpattern(struct usb_usbvision *usbvision, int fullframe, frame->scanlength += scan_length; ++num_pass; - /* We do this unconditionally, regardless of FLAGS_OSD_STATS */ - usbvision_osd_stats(usbvision, frame); } /* - * Here comes the data parsing stuff that is run as interrupt + * usbvision_decompress_alloc() + * + * allocates intermediate buffer for decompression */ - +int usbvision_decompress_alloc(struct usb_usbvision *usbvision) +{ + int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2; + usbvision->IntraFrameBuffer = vmalloc(IFB_size); + if (usbvision->IntraFrameBuffer == NULL) { + err("%s: unable to allocate %d for compr. frame buffer", __FUNCTION__, IFB_size); + return -ENOMEM; + } + return 0; +} + +/* + * usbvision_decompress_free() + * + * frees intermediate buffer for decompression + */ +void usbvision_decompress_free(struct usb_usbvision *usbvision) +{ + if (usbvision->IntraFrameBuffer != NULL) { + vfree(usbvision->IntraFrameBuffer); + usbvision->IntraFrameBuffer = NULL; + } +} + +/************************************************************ + * Here comes the data parsing stuff that is run as interrupt + ************************************************************/ /* * usbvision_find_header() * @@ -1305,12 +513,7 @@ static enum ParseState usbvision_find_header(struct usb_usbvision *usbvision) struct usbvision_frame *frame; int foundHeader = 0; - if (usbvision->overlay) { - frame = &usbvision->overlay_frame; - } - else { - frame = usbvision->curFrame; - } + frame = usbvision->curFrame; while (scratch_get_header(usbvision, &frame->isocHeader) == USBVISION_HEADER_LENGTH) { // found header in scratch @@ -1368,7 +571,7 @@ static enum ParseState usbvision_find_header(struct usb_usbvision *usbvision) frame->scanstate = ScanState_Lines; frame->curline = 0; - if (flags & FLAGS_FORCE_TESTPATTERN) { + if (force_testpattern) { usbvision_testpattern(usbvision, 1, 1); return ParseState_NextFrame; } @@ -1385,22 +588,10 @@ static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision unsigned char yuyv[4]={180, 128, 10, 128}; // YUV components unsigned char rv, gv, bv; // RGB components int clipmask_index, bytes_per_pixel; - int overlay = usbvision->overlay; int stretch_bytes, clipmask_add; - if (overlay) { - frame = &usbvision->overlay_frame; - if (usbvision->overlay_base == NULL) { - //video_buffer is not set yet - return ParseState_NextFrame; - } - f = usbvision->overlay_win + frame->curline * - usbvision->vid_buf.fmt.bytesperline; - } - else { - frame = usbvision->curFrame; - f = frame->data + (frame->v4l2_linesize * frame->curline); - } + frame = usbvision->curFrame; + f = frame->data + (frame->v4l2_linesize * frame->curline); /* Make sure there's enough data for the entire line */ len = (frame->isocHeader.frameWidth * 2)+5; @@ -1422,10 +613,7 @@ static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision scratch_get(usbvision, &yuyv[0], 4); - if((overlay) && (clipped_pixel(clipmask_index))) { - f += bytes_per_pixel; - } - else if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { *f++ = yuyv[0]; // Y *f++ = yuyv[3]; // U } @@ -1457,10 +645,7 @@ static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision clipmask_index += clipmask_add; f += stretch_bytes; - if((overlay) && (clipped_pixel(clipmask_index))) { - f += bytes_per_pixel; - } - else if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { *f++ = yuyv[2]; // Y *f++ = yuyv[1]; // V } @@ -1504,7 +689,7 @@ static enum ParseState usbvision_parse_lines_422(struct usb_usbvision *usbvision } } - +/* The decompression routine */ static int usbvision_decompress(struct usb_usbvision *usbvision,unsigned char *Compressed, unsigned char *Decompressed, int *StartPos, int *BlockTypeStartPos, int Len) @@ -1614,45 +799,31 @@ static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision, unsigned char StripHeader[USBVISION_STRIP_HEADER_LEN]; int Idx, IdxEnd, StripLen, StripPtr, StartBlockPos, BlockPos, BlockTypePos; int clipmask_index, bytes_per_pixel, rc; - int overlay = usbvision->overlay; int imageSize; unsigned char rv, gv, bv; static unsigned char *Y, *U, *V; - if (overlay) { - frame = &usbvision->overlay_frame; - imageSize = frame->frmwidth * frame->frmheight; - if (usbvision->overlay_base == NULL) { - //video_buffer is not set yet - return ParseState_NextFrame; - } - f = usbvision->overlay_win + frame->curline * - usbvision->vid_buf.fmt.bytesperline; - } - else { - frame = usbvision->curFrame; - imageSize = frame->frmwidth * frame->frmheight; - if ( (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) || - (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) ) - { // this is a planar format - //... v4l2_linesize not used here. - f = frame->data + (frame->width * frame->curline); - } else - f = frame->data + (frame->v4l2_linesize * frame->curline); - - if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV){ //initialise u and v pointers - // get base of u and b planes add halfoffset - - u = frame->data - + imageSize - + (frame->frmwidth >>1) * frame->curline ; - v = u + (imageSize >>1 ); - - } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420){ - - v = frame->data + imageSize + ((frame->curline* (frame->width))>>2) ; - u = v + (imageSize >>2) ; - } + frame = usbvision->curFrame; + imageSize = frame->frmwidth * frame->frmheight; + if ( (frame->v4l2_format.format == V4L2_PIX_FMT_YUV422P) || + (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420) ) { // this is a planar format + //... v4l2_linesize not used here. + f = frame->data + (frame->width * frame->curline); + } else + f = frame->data + (frame->v4l2_linesize * frame->curline); + + if (frame->v4l2_format.format == V4L2_PIX_FMT_YUYV){ //initialise u and v pointers + // get base of u and b planes add halfoffset + + u = frame->data + + imageSize + + (frame->frmwidth >>1) * frame->curline ; + v = u + (imageSize >>1 ); + + } else if (frame->v4l2_format.format == V4L2_PIX_FMT_YVU420){ + + v = frame->data + imageSize + ((frame->curline* (frame->width))>>2) ; + u = v + (imageSize >>2) ; } if (frame->curline == 0) { @@ -1738,10 +909,7 @@ static enum ParseState usbvision_parse_compress(struct usb_usbvision *usbvision, } for (Idx = 0; Idx < IdxEnd; Idx++) { - if((overlay) && (clipped_pixel(clipmask_index))) { - f += bytes_per_pixel; - } - else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { *f++ = Y[Idx]; *f++ = Idx & 0x01 ? U[Idx/2] : V[Idx/2]; } @@ -1838,23 +1006,10 @@ static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision unsigned char g; int clipmask_even_index, clipmask_odd_index, bytes_per_pixel; int clipmask_add, stretch_bytes; - int overlay = usbvision->overlay; - if (overlay) { - frame = &usbvision->overlay_frame; - if (usbvision->overlay_base == NULL) { - //video_buffer is not set yet - return ParseState_NextFrame; - } - f_even = usbvision->overlay_win + frame->curline * - usbvision->vid_buf.fmt.bytesperline; - f_odd = f_even + usbvision->vid_buf.fmt.bytesperline * usbvision->stretch_height; - } - else { - frame = usbvision->curFrame; - f_even = frame->data + (frame->v4l2_linesize * frame->curline); - f_odd = f_even + frame->v4l2_linesize * usbvision->stretch_height; - } + frame = usbvision->curFrame; + f_even = frame->data + (frame->v4l2_linesize * frame->curline); + f_odd = f_even + frame->v4l2_linesize * usbvision->stretch_height; /* Make sure there's enough data for the entire line */ /* In this mode usbvision transfer 3 bytes for every 2 pixels */ @@ -1901,10 +1056,7 @@ static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision uvg= -53281 * u_ - 25625 * v_; ur = 104595 * u_; - if((overlay) && (clipped_pixel(clipmask_even_index))) { - f_even += bytes_per_pixel; - } - else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { *f_even++ = y[0]; *f_even++ = v; } @@ -1943,10 +1095,7 @@ static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision clipmask_even_index += clipmask_add; f_even += stretch_bytes; - if((overlay) && (clipped_pixel(clipmask_even_index))) { - f_even += bytes_per_pixel; - } - else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { *f_even++ = y[1]; *f_even++ = u; } @@ -1987,10 +1136,7 @@ static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision scratch_get_extra(usbvision, &y[0], &y_ptr, 2); - if ((overlay) && (clipped_pixel(clipmask_odd_index))) { - f_odd += bytes_per_pixel; - } - else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { *f_odd++ = y[0]; *f_odd++ = v; } @@ -2029,10 +1175,7 @@ static enum ParseState usbvision_parse_lines_420(struct usb_usbvision *usbvision clipmask_odd_index += clipmask_add; f_odd += stretch_bytes; - if((overlay) && (clipped_pixel(clipmask_odd_index))) { - f_odd += bytes_per_pixel; - } - else if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { + if(frame->v4l2_format.format == V4L2_PIX_FMT_YUYV) { *f_odd++ = y[1]; *f_odd++ = u; } @@ -2108,12 +1251,7 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) long copylen = 0; unsigned long lock_flags; - if (usbvision->overlay) { - frame = &usbvision->overlay_frame; - } - else { - frame = usbvision->curFrame; - } + frame = usbvision->curFrame; PDEBUG(DBG_PARSE, "parsing len=%d\n", scratch_len(usbvision)); @@ -2152,23 +1290,13 @@ static void usbvision_parse_data(struct usb_usbvision *usbvision) frame->grabstate = FrameState_Done; do_gettimeofday(&(frame->timestamp)); frame->sequence = usbvision->frame_num; - if (usbvision->overlay) { - frame->grabstate = FrameState_Grabbing; - frame->scanstate = ScanState_Scanning; - frame->scanlength = 0; - copylen = 0; - } - else { - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - list_move_tail(&(frame->frame), &usbvision->outqueue); - usbvision->curFrame = NULL; - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); - } - usbvision->frame_num++; - /* Optionally display statistics on the screen */ - if (flags & FLAGS_OSD_STATS) - usbvision_osd_stats(usbvision, frame); + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_move_tail(&(frame->frame), &usbvision->outqueue); + usbvision->curFrame = NULL; + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + usbvision->frame_num++; /* This will cause the process to request another frame. */ if (waitqueue_active(&usbvision->wait_frame)) { @@ -2306,7 +1434,7 @@ static void usbvision_isocIrq(struct urb *urb, struct pt_regs *regs) /* If we collected enough data let's parse! */ if (scratch_len(usbvision) > USBVISION_HEADER_LENGTH) { /* 12 == header_length */ /*If we don't have a frame we're current working on, complain */ - if((!list_empty(&(usbvision->inqueue))) || (usbvision->overlay)) { + if(!list_empty(&(usbvision->inqueue))) { if (!(*f)) { (*f) = list_entry(usbvision->inqueue.next,struct usbvision_frame, frame); } @@ -2353,7 +1481,7 @@ static void usbvision_isocIrq(struct urb *urb, struct pt_regs *regs) * >= 0 -> Data */ -static int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg) +int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg) { int errCode = 0; unsigned char buffer[1]; @@ -2381,7 +1509,7 @@ static int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg * -1 -> Something went wrong */ -static int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, +int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, unsigned char value) { int errCode = 0; @@ -2454,8 +1582,6 @@ static int usbvision_write_reg_irq(struct usb_usbvision *usbvision,int address, } - - static int usbvision_init_compression(struct usb_usbvision *usbvision) { int errCode = 0; @@ -2563,334 +1689,11 @@ static int usbvision_unrequest_intra (struct usb_usbvision *usbvision) return errCode; } -/* ----------------------------------------------------------------------- */ -/* I2C functions */ -/* ----------------------------------------------------------------------- */ - -static void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, - void *arg) -{ - BUG_ON(NULL == usbvision->i2c_adap.algo_data); - i2c_clients_command(&usbvision->i2c_adap, cmd, arg); -} - -static int attach_inform(struct i2c_client *client) -{ - struct usb_usbvision *usbvision; - - usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); - - switch (client->addr << 1) { - case 0x43: - case 0x4b: - { - struct tuner_setup tun_setup; - - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = TUNER_TDA9887; - tun_setup.addr = client->addr; - - call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); - - break; - } - case 0x42: - PDEBUG(DBG_I2C,"attach_inform: saa7114 detected.\n"); - break; - case 0x4a: - PDEBUG(DBG_I2C,"attach_inform: saa7113 detected.\n"); - break; - case 0xa0: - PDEBUG(DBG_I2C,"attach_inform: eeprom detected.\n"); - break; - - default: - PDEBUG(DBG_I2C,"attach inform: detected I2C address %x\n", client->addr << 1); - { - struct tuner_setup tun_setup; - - usbvision->tuner_addr = client->addr; - - if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) { - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = usbvision->tuner_type; - tun_setup.addr = usbvision->tuner_addr; - call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); - } - } - break; - } - return 0; -} - -static int detach_inform(struct i2c_client *client) -{ - struct usb_usbvision *usbvision; - - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) - usbvision = (struct usb_usbvision *)client->adapter->data; - #else - usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); - #endif - - PDEBUG(DBG_I2C, "usbvision[%d] detaches %s", usbvision->nr, client->name); - return 0; -} - -static int -usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr, - char *buf, short len) -{ - int rc, retries; - - for (retries = 5;;) { - rc = usbvision_write_reg(usbvision, USBVISION_SER_ADRS, addr); - if (rc < 0) - return rc; - - /* Initiate byte read cycle */ - /* USBVISION_SER_CONT <- d0-d2 n. of bytes to r/w */ - /* d3 0=Wr 1=Rd */ - rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, - (len & 0x07) | 0x18); - if (rc < 0) - return rc; - - /* Test for Busy and ACK */ - do { - /* USBVISION_SER_CONT -> d4 == 0 busy */ - rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); - } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ - if (rc < 0) - return rc; - - /* USBVISION_SER_CONT -> d5 == 1 Not ack */ - if ((rc & 0x20) == 0) /* Ack? */ - break; - - /* I2C abort */ - rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); - if (rc < 0) - return rc; - - if (--retries < 0) - return -1; - } - - switch (len) { - case 4: - buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4); - case 3: - buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3); - case 2: - buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2); - case 1: - buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1); - break; - default: - printk(KERN_ERR - "usbvision_i2c_read_max4: buffer length > 4\n"); - } - - if (debug & DBG_I2C) { - int idx; - for (idx = 0; idx < len; idx++) { - PDEBUG(DBG_I2C, "read %x from address %x", (unsigned char)buf[idx], addr); - } - } - return len; -} - - -static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision, - unsigned char addr, const char *buf, - short len) -{ - int rc, retries; - int i; - unsigned char value[6]; - unsigned char ser_cont; - - ser_cont = (len & 0x07) | 0x10; - - value[0] = addr; - value[1] = ser_cont; - for (i = 0; i < len; i++) - value[i + 2] = buf[i]; - - for (retries = 5;;) { - rc = usb_control_msg(usbvision->dev, - usb_sndctrlpipe(usbvision->dev, 1), - USBVISION_OP_CODE, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_ENDPOINT, 0, - (__u16) USBVISION_SER_ADRS, value, - len + 2, HZ); - - if (rc < 0) - return rc; - - rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, - (len & 0x07) | 0x10); - if (rc < 0) - return rc; - - /* Test for Busy and ACK */ - do { - rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); - } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ - if (rc < 0) - return rc; - - if ((rc & 0x20) == 0) /* Ack? */ - break; - - /* I2C abort */ - usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); - - if (--retries < 0) - return -1; - - } - - if (debug & DBG_I2C) { - int idx; - for (idx = 0; idx < len; idx++) { - PDEBUG(DBG_I2C, "wrote %x at address %x", (unsigned char)buf[idx], addr); - } - } - return len; -} - -static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, - short len) -{ - char *bufPtr = buf; - int retval; - int wrcount = 0; - int count; - int maxLen = 4; - struct usb_usbvision *usbvision = (struct usb_usbvision *) data; - - while (len > 0) { - count = (len > maxLen) ? maxLen : len; - retval = usbvision_i2c_write_max4(usbvision, addr, bufPtr, count); - if (retval > 0) { - len -= count; - bufPtr += count; - wrcount += count; - } else - return (retval < 0) ? retval : -EFAULT; - } - return wrcount; -} - -static int usbvision_i2c_read(void *data, unsigned char addr, char *buf, - short len) -{ - char temp[4]; - int retval, i; - int rdcount = 0; - int count; - struct usb_usbvision *usbvision = (struct usb_usbvision *) data; - - while (len > 0) { - count = (len > 3) ? 4 : len; - retval = usbvision_i2c_read_max4(usbvision, addr, temp, count); - if (retval > 0) { - for (i = 0; i < len; i++) - buf[rdcount + i] = temp[i]; - len -= count; - rdcount += count; - } else - return (retval < 0) ? retval : -EFAULT; - } - return rdcount; -} - -static struct i2c_algo_usb_data i2c_algo_template = { - .data = NULL, - .inb = usbvision_i2c_read, - .outb = usbvision_i2c_write, - .udelay = 10, - .mdelay = 10, - .timeout = 100, -}; - -static struct i2c_adapter i2c_adap_template = { - .owner = THIS_MODULE, - .name = "usbvision", - .id = I2C_HW_B_BT848, /* FIXME */ - .algo = NULL, - .algo_data = NULL, - .client_register = attach_inform, - .client_unregister = detach_inform, -#if defined (I2C_ADAP_CLASS_TV_ANALOG) - .class = I2C_ADAP_CLASS_TV_ANALOG, -#elif defined (I2C_CLASS_TV_ANALOG) - .class = I2C_CLASS_TV_ANALOG, -#endif -}; - -static struct i2c_client i2c_client_template = { - .name = "usbvision internal", - .flags = 0, - .addr = 0, - .adapter = NULL, - .driver = NULL, -}; - -static int usbvision_init_i2c(struct usb_usbvision *usbvision) -{ - memcpy(&usbvision->i2c_adap, &i2c_adap_template, - sizeof(struct i2c_adapter)); - memcpy(&usbvision->i2c_algo, &i2c_algo_template, - sizeof(struct i2c_algo_usb_data)); - memcpy(&usbvision->i2c_client, &i2c_client_template, - sizeof(struct i2c_client)); - - sprintf(usbvision->i2c_adap.name + strlen(usbvision->i2c_adap.name), - " #%d", usbvision->vdev->minor & 0x1f); - PDEBUG(DBG_I2C, "Adaptername: %s", usbvision->i2c_adap.name); - - i2c_set_adapdata(&usbvision->i2c_adap, usbvision); - i2c_set_clientdata(&usbvision->i2c_client, usbvision); - i2c_set_algo_usb_data(&usbvision->i2c_algo, usbvision); - - usbvision->i2c_adap.algo_data = &usbvision->i2c_algo; - usbvision->i2c_client.adapter = &usbvision->i2c_adap; - - if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) { - printk(KERN_ERR "usbvision_init_i2c: can't wirte reg\n"); - return -EBUSY; - } - -#ifdef CONFIG_KMOD - /* Request the load of the i2c modules we need */ - if (autoload) { - switch (usbvision_device_data[usbvision->DevModel].Codec) { - case CODEC_SAA7113: - request_module("saa7115"); - break; - case CODEC_SAA7111: - request_module("saa7115"); - break; - } - if (usbvision_device_data[usbvision->DevModel].Tuner == 1) { - request_module("tuner"); - } - } -#endif - - return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap); -} +/******************************* + * usbvision utility functions + *******************************/ - -/****************************/ -/* usbvision utility functions */ -/****************************/ - -static int usbvision_power_off(struct usb_usbvision *usbvision) +int usbvision_power_off(struct usb_usbvision *usbvision) { int errCode = 0; @@ -2905,22 +1708,6 @@ static int usbvision_power_off(struct usb_usbvision *usbvision) } -// to call usbvision_power_off from task queue -static void call_usbvision_power_off(void *_usbvision) -{ - struct usb_usbvision *usbvision = _usbvision; - - PDEBUG(DBG_FUNC, ""); - down_interruptible(&usbvision->lock); - if(usbvision->user == 0) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } - up(&usbvision->lock); -} - - /* * usbvision_set_video_format() * @@ -2964,8 +1751,8 @@ static int usbvision_set_video_format(struct usb_usbvision *usbvision, int forma * */ -static int usbvision_set_output(struct usb_usbvision *usbvision, int width, - int height) +int usbvision_set_output(struct usb_usbvision *usbvision, int width, + int height) { int errCode = 0; int UsbWidth, UsbHeight; @@ -3060,10 +1847,62 @@ static int usbvision_set_output(struct usb_usbvision *usbvision, int width, /* + * usbvision_frames_alloc + * allocate the maximum frames this driver can manage + */ +int usbvision_frames_alloc(struct usb_usbvision *usbvision) +{ + int i; + + /* Allocate memory for the frame buffers */ + usbvision->max_frame_size = MAX_FRAME_SIZE; + usbvision->fbuf_size = USBVISION_NUMFRAMES * usbvision->max_frame_size; + usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size); + + if(usbvision->fbuf == NULL) { + err("%s: unable to allocate %d bytes for fbuf ", + __FUNCTION__, usbvision->fbuf_size); + return -ENOMEM; + } + spin_lock_init(&usbvision->queue_lock); + init_waitqueue_head(&usbvision->wait_frame); + init_waitqueue_head(&usbvision->wait_stream); + + /* Allocate all buffers */ + for (i = 0; i < USBVISION_NUMFRAMES; i++) { + usbvision->frame[i].index = i; + usbvision->frame[i].grabstate = FrameState_Unused; + usbvision->frame[i].data = usbvision->fbuf + + i * usbvision->max_frame_size; + /* + * Set default sizes for read operation. + */ + usbvision->stretch_width = 1; + usbvision->stretch_height = 1; + usbvision->frame[i].width = usbvision->curwidth; + usbvision->frame[i].height = usbvision->curheight; + usbvision->frame[i].bytes_read = 0; + } + return 0; +} + +/* + * usbvision_frames_free + * frees memory allocated for the frames + */ +void usbvision_frames_free(struct usb_usbvision *usbvision) +{ + /* Have to free all that memory */ + if (usbvision->fbuf != NULL) { + usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size); + usbvision->fbuf = NULL; + } +} +/* * usbvision_empty_framequeues() * prepare queues for incoming and outgoing frames */ -static void usbvision_empty_framequeues(struct usb_usbvision *usbvision) +void usbvision_empty_framequeues(struct usb_usbvision *usbvision) { u32 i; @@ -3080,7 +1919,7 @@ static void usbvision_empty_framequeues(struct usb_usbvision *usbvision) * usbvision_stream_interrupt() * stops streaming */ -static int usbvision_stream_interrupt(struct usb_usbvision *usbvision) +int usbvision_stream_interrupt(struct usb_usbvision *usbvision) { int ret = 0; @@ -3176,7 +2015,7 @@ static int usbvision_set_compress_params(struct usb_usbvision *usbvision) * I've no idea if this parameters are right * */ -static int usbvision_set_input(struct usb_usbvision *usbvision) +int usbvision_set_input(struct usb_usbvision *usbvision) { static const char proc[] = "usbvision_set_input: "; int rc; @@ -3351,7 +2190,7 @@ static int usbvision_set_dram_settings(struct usb_usbvision *usbvision) * */ -static int usbvision_power_on(struct usb_usbvision *usbvision) +int usbvision_power_on(struct usb_usbvision *usbvision) { int errCode = 0; @@ -3372,6 +2211,25 @@ static int usbvision_power_on(struct usb_usbvision *usbvision) } +/* + * usbvision timer stuff + */ + +// to call usbvision_power_off from task queue +static void call_usbvision_power_off(void *_usbvision) +{ + struct usb_usbvision *usbvision = _usbvision; + + PDEBUG(DBG_FUNC, ""); + down_interruptible(&usbvision->lock); + if(usbvision->user == 0) { + usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_power_off(usbvision); + usbvision->initialized = 0; + } + up(&usbvision->lock); +} + static void usbvision_powerOffTimer(unsigned long data) { struct usb_usbvision *usbvision = (void *) data; @@ -3383,13 +2241,31 @@ static void usbvision_powerOffTimer(unsigned long data) } +void usbvision_init_powerOffTimer(struct usb_usbvision *usbvision) +{ + init_timer(&usbvision->powerOffTimer); + usbvision->powerOffTimer.data = (long) usbvision; + usbvision->powerOffTimer.function = usbvision_powerOffTimer; +} + +void usbvision_set_powerOffTimer(struct usb_usbvision *usbvision) +{ + mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME); +} + +void usbvision_reset_powerOffTimer(struct usb_usbvision *usbvision) +{ + if (timer_pending(&usbvision->powerOffTimer)) { + del_timer(&usbvision->powerOffTimer); + } +} /* * usbvision_begin_streaming() * Sure you have to put bit 7 to 0, if not incoming frames are droped, but no * idea about the rest */ -static int usbvision_begin_streaming(struct usb_usbvision *usbvision) +int usbvision_begin_streaming(struct usb_usbvision *usbvision) { int errCode = 0; @@ -3406,7 +2282,7 @@ static int usbvision_begin_streaming(struct usb_usbvision *usbvision) * Not sure yet if touching here PWR_REG make loose the config */ -static int usbvision_restart_isoc(struct usb_usbvision *usbvision) +int usbvision_restart_isoc(struct usb_usbvision *usbvision) { int ret; @@ -3433,41 +2309,18 @@ static int usbvision_restart_isoc(struct usb_usbvision *usbvision) return 0; } -static int usbvision_audio_on(struct usb_usbvision *usbvision) -{ - if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, usbvision->AudioChannel) < 0) { - printk(KERN_ERR "usbvision_audio_on: can't wirte reg\n"); - return -1; - } - DEBUG(printk(KERN_DEBUG "usbvision_audio_on: channel %d\n", usbvision->AudioChannel)); - usbvision->AudioMute = 0; - return 0; -} - -static int usbvision_audio_mute(struct usb_usbvision *usbvision) -{ - if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, 0x03) < 0) { - printk(KERN_ERR "usbvision_audio_mute: can't wirte reg\n"); - return -1; - } - DEBUG(printk(KERN_DEBUG "usbvision_audio_mute: audio mute\n")); - usbvision->AudioMute = 1; - return 0; -} - -static int usbvision_audio_off(struct usb_usbvision *usbvision) +int usbvision_audio_off(struct usb_usbvision *usbvision) { if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, USBVISION_AUDIO_MUTE) < 0) { printk(KERN_ERR "usbvision_audio_off: can't wirte reg\n"); return -1; } - DEBUG(printk(KERN_DEBUG "usbvision_audio_off: audio off\n")); usbvision->AudioMute = 0; usbvision->AudioChannel = USBVISION_AUDIO_MUTE; return 0; } -static int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel) +int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel) { if (!usbvision->AudioMute) { if (usbvision_write_reg(usbvision, USBVISION_IOPIN_REG, AudioChannel) < 0) { @@ -3475,14 +2328,13 @@ static int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel return -1; } } - DEBUG(printk(KERN_DEBUG "usbvision_set_audio: channel %d\n", AudioChannel)); usbvision->AudioChannel = AudioChannel; return 0; } -static int usbvision_setup(struct usb_usbvision *usbvision) +int usbvision_setup(struct usb_usbvision *usbvision,int format) { - usbvision_set_video_format(usbvision, isocMode); + usbvision_set_video_format(usbvision, format); usbvision_set_dram_settings(usbvision); usbvision_set_compress_params(usbvision); usbvision_set_input(usbvision); @@ -3494,22 +2346,55 @@ static int usbvision_setup(struct usb_usbvision *usbvision) } -/* - * usbvision_init_isoc() - * - */ -static int usbvision_init_isoc(struct usb_usbvision *usbvision) +int usbvision_sbuf_alloc(struct usb_usbvision *usbvision) { - struct usb_device *dev = usbvision->dev; - int bufIdx, errCode, regValue; + int i, errCode = 0; + const int sb_size = USBVISION_URB_FRAMES * USBVISION_MAX_ISOC_PACKET_SIZE; - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return -EFAULT; + /* Clean pointers so we know if we allocated something */ + for (i = 0; i < USBVISION_NUMSBUF; i++) + usbvision->sbuf[i].data = NULL; - usbvision->curFrame = NULL; - scratch_reset(usbvision); + for (i = 0; i < USBVISION_NUMSBUF; i++) { + usbvision->sbuf[i].data = kzalloc(sb_size, GFP_KERNEL); + if (usbvision->sbuf[i].data == NULL) { + err("%s: unable to allocate %d bytes for sbuf", __FUNCTION__, sb_size); + errCode = -ENOMEM; + break; + } + } + return errCode; +} - /* Alternate interface 1 is is the biggest frame size */ + +void usbvision_sbuf_free(struct usb_usbvision *usbvision) +{ + int i; + + for (i = 0; i < USBVISION_NUMSBUF; i++) { + if (usbvision->sbuf[i].data != NULL) { + kfree(usbvision->sbuf[i].data); + usbvision->sbuf[i].data = NULL; + } + } +} + +/* + * usbvision_init_isoc() + * + */ +int usbvision_init_isoc(struct usb_usbvision *usbvision) +{ + struct usb_device *dev = usbvision->dev; + int bufIdx, errCode, regValue; + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EFAULT; + + usbvision->curFrame = NULL; + scratch_reset(usbvision); + + /* Alternate interface 1 is is the biggest frame size */ errCode = usb_set_interface(dev, usbvision->iface, usbvision->ifaceAltActive); if (errCode < 0) { usbvision->last_error = errCode; @@ -3587,7 +2472,7 @@ static int usbvision_init_isoc(struct usb_usbvision *usbvision) * activates zero-bandwidth alt. setting of the video interface. * */ -static void usbvision_stop_isoc(struct usb_usbvision *usbvision) +void usbvision_stop_isoc(struct usb_usbvision *usbvision) { int bufIdx, errCode, regValue; @@ -3623,7 +2508,7 @@ static void usbvision_stop_isoc(struct usb_usbvision *usbvision) } } -static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) +int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) { int mode[4]; int audio[]= {1, 0, 0, 0}; @@ -3671,2157 +2556,6 @@ static int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) return 0; } - -/* - * usbvision_open() - * - * This is part of Video 4 Linux API. The driver can be opened by one - * client only (checks internal counter 'usbvision->user'). The procedure - * then allocates buffers needed for video processing. - * - */ -static int usbvision_v4l2_open(struct inode *inode, struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - const int sb_size = USBVISION_URB_FRAMES * USBVISION_MAX_ISOC_PACKET_SIZE; - int i, errCode = 0; - - PDEBUG(DBG_IO, "open"); - - - if (timer_pending(&usbvision->powerOffTimer)) { - del_timer(&usbvision->powerOffTimer); - } - - if (usbvision->user) - errCode = -EBUSY; - else { - /* Clean pointers so we know if we allocated something */ - for (i = 0; i < USBVISION_NUMSBUF; i++) - usbvision->sbuf[i].data = NULL; - - /* Allocate memory for the frame buffers */ - usbvision->max_frame_size = MAX_FRAME_SIZE; - usbvision->fbuf_size = USBVISION_NUMFRAMES * usbvision->max_frame_size; - usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size); - usbvision->scratch = vmalloc(scratch_buf_size); - scratch_reset(usbvision); - if ((usbvision->fbuf == NULL) || (usbvision->scratch == NULL)) { - err("%s: unable to allocate %d bytes for fbuf and %d bytes for scratch", - __FUNCTION__, usbvision->fbuf_size, scratch_buf_size); - errCode = -ENOMEM; - } - else { - spin_lock_init(&usbvision->queue_lock); - init_waitqueue_head(&usbvision->wait_frame); - init_waitqueue_head(&usbvision->wait_stream); - - /* Allocate all buffers */ - for (i = 0; i < USBVISION_NUMFRAMES; i++) { - usbvision->frame[i].index = i; - usbvision->frame[i].grabstate = FrameState_Unused; - usbvision->frame[i].data = usbvision->fbuf + - i * MAX_FRAME_SIZE; - /* - * Set default sizes in case IOCTL - * (VIDIOCMCAPTURE) - * is not used (using read() instead). - */ - usbvision->stretch_width = 1; - usbvision->stretch_height = 1; - usbvision->frame[i].width = usbvision->curwidth; - usbvision->frame[i].height = usbvision->curheight; - usbvision->frame[i].bytes_read = 0; - } - if (dga) { //set default for DGA - usbvision->overlay_frame.grabstate = FrameState_Unused; - usbvision->overlay_frame.scanstate = ScanState_Scanning; - usbvision->overlay_frame.data = NULL; - usbvision->overlay_frame.width = usbvision->curwidth; - usbvision->overlay_frame.height = usbvision->curheight; - usbvision->overlay_frame.bytes_read = 0; - } - for (i = 0; i < USBVISION_NUMSBUF; i++) { - usbvision->sbuf[i].data = kzalloc(sb_size, GFP_KERNEL); - if (usbvision->sbuf[i].data == NULL) { - err("%s: unable to allocate %d bytes for sbuf", __FUNCTION__, sb_size); - errCode = -ENOMEM; - break; - } - } - } - if ((!errCode) && (usbvision->isocMode==ISOC_MODE_COMPRESS)) { - int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2; - usbvision->IntraFrameBuffer = vmalloc(IFB_size); - if (usbvision->IntraFrameBuffer == NULL) { - err("%s: unable to allocate %d for compr. frame buffer", __FUNCTION__, IFB_size); - errCode = -ENOMEM; - } - - } - if (errCode) { - /* Have to free all that memory */ - if (usbvision->fbuf != NULL) { - usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size); - usbvision->fbuf = NULL; - } - if (usbvision->scratch != NULL) { - vfree(usbvision->scratch); - usbvision->scratch = NULL; - } - for (i = 0; i < USBVISION_NUMSBUF; i++) { - if (usbvision->sbuf[i].data != NULL) { - kfree(usbvision->sbuf[i].data); - usbvision->sbuf[i].data = NULL; - } - } - if (usbvision->IntraFrameBuffer != NULL) { - vfree(usbvision->IntraFrameBuffer); - usbvision->IntraFrameBuffer = NULL; - } - } - } - - /* If so far no errors then we shall start the camera */ - if (!errCode) { - down(&usbvision->lock); - if (usbvision->power == 0) { - usbvision_power_on(usbvision); - usbvision_init_i2c(usbvision); - } - - /* Send init sequence only once, it's large! */ - if (!usbvision->initialized) { - int setup_ok = 0; - setup_ok = usbvision_setup(usbvision); - if (setup_ok) - usbvision->initialized = 1; - else - errCode = -EBUSY; - } - - if (!errCode) { - usbvision_begin_streaming(usbvision); - errCode = usbvision_init_isoc(usbvision); - /* device needs to be initialized before isoc transfer */ - usbvision_muxsel(usbvision,0); - usbvision->user++; - } - else { - if (PowerOnAtOpen) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } - } - up(&usbvision->lock); - } - - if (errCode) { - } - - /* prepare queues */ - usbvision_empty_framequeues(usbvision); - - PDEBUG(DBG_IO, "success"); - return errCode; -} - -/* - * usbvision_v4l2_close() - * - * This is part of Video 4 Linux API. The procedure - * stops streaming and deallocates all buffers that were earlier - * allocated in usbvision_v4l2_open(). - * - */ -static int usbvision_v4l2_close(struct inode *inode, struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - int i; - - PDEBUG(DBG_IO, "close"); - down(&usbvision->lock); - - usbvision_audio_off(usbvision); - usbvision_restart_isoc(usbvision); - usbvision_stop_isoc(usbvision); - - if (usbvision->IntraFrameBuffer != NULL) { - vfree(usbvision->IntraFrameBuffer); - usbvision->IntraFrameBuffer = NULL; - } - - usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size); - vfree(usbvision->scratch); - for (i = 0; i < USBVISION_NUMSBUF; i++) - kfree(usbvision->sbuf[i].data); - - usbvision->user--; - - if (PowerOnAtOpen) { - mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME); - usbvision->initialized = 0; - } - - up(&usbvision->lock); - - if (usbvision->remove_pending) { - info("%s: Final disconnect", __FUNCTION__); - usbvision_release(usbvision); - } - - PDEBUG(DBG_IO, "success"); - - - return 0; -} - - -/* - * usbvision_ioctl() - * - * This is part of Video 4 Linux API. The procedure handles ioctl() calls. - * - */ -static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return -EFAULT; - - // if (debug & DBG_IOCTL) v4l_printk_ioctl(cmd); - - switch (cmd) { - -#ifdef CONFIG_VIDEO_ADV_DEBUG - /* ioctls to allow direct acces to the NT100x registers */ - case VIDIOC_INT_G_REGISTER: - { - struct v4l2_register *reg = arg; - int errCode; - - if (reg->i2c_id != 0) - return -EINVAL; - /* NT100x has a 8-bit register space */ - errCode = usbvision_read_reg(usbvision, reg->reg&0xff); - if (errCode < 0) { - err("%s: VIDIOC_INT_G_REGISTER failed: error %d", __FUNCTION__, errCode); - } - else { - reg->val=(unsigned char)errCode; - PDEBUG(DBG_IOCTL, "VIDIOC_INT_G_REGISTER reg=0x%02X, value=0x%02X", - (unsigned int)reg->reg, reg->val); - errCode = 0; // No error - } - return errCode; - } - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *reg = arg; - int errCode; - - if (reg->i2c_id != 0) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val); - if (errCode < 0) { - err("%s: VIDIOC_INT_S_REGISTER failed: error %d", __FUNCTION__, errCode); - } - else { - PDEBUG(DBG_IOCTL, "VIDIOC_INT_S_REGISTER reg=0x%02X, value=0x%02X", - (unsigned int)reg->reg, reg->val); - errCode = 0; - } - return 0; - } -#endif - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *vc=arg; - - memset(vc, 0, sizeof(*vc)); - strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); - strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString, - sizeof(vc->card)); - strlcpy(vc->bus_info, usbvision->dev->dev.bus_id, - sizeof(vc->bus_info)); - vc->version = USBVISION_DRIVER_VERSION; - vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - (dga ? (V4L2_FBUF_CAP_LIST_CLIPPING | V4L2_CAP_VIDEO_OVERLAY) : 0) | - (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); - PDEBUG(DBG_IOCTL, "VIDIOC_QUERYCAP"); - return 0; - } - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *vi = arg; - int chan; - - if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) ) - return -EINVAL; - if (usbvision->have_tuner) { - chan = vi->index; - } - else { - chan = vi->index + 1; //skip Television string - } - switch(chan) { - case 0: - if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { - strcpy(vi->name, "White Video Input"); - } - else { - strcpy(vi->name, "Television"); - vi->type = V4L2_INPUT_TYPE_TUNER; - vi->audioset = 1; - vi->tuner = chan; - vi->std = V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM; - } - break; - case 1: - vi->type = V4L2_INPUT_TYPE_CAMERA; - if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { - strcpy(vi->name, "Green Video Input"); - } - else { - strcpy(vi->name, "Composite Video Input"); - } - vi->std = V4L2_STD_PAL; - break; - case 2: - vi->type = V4L2_INPUT_TYPE_CAMERA; - if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { - strcpy(vi->name, "Yellow Video Input"); - } - else { - strcpy(vi->name, "S-Video Input"); - } - vi->std = V4L2_STD_PAL; - break; - case 3: - vi->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(vi->name, "Red Video Input"); - vi->std = V4L2_STD_PAL; - break; - } - PDEBUG(DBG_IOCTL, "VIDIOC_ENUMINPUT name=%s:%d tuners=%d type=%d norm=%x", vi->name, vi->index, vi->tuner,vi->type,(int)vi->std); - return 0; - } - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *e = arg; - unsigned int i; - int ret; - - i = e->index; - if (i >= TVNORMS) - return -EINVAL; - ret = v4l2_video_std_construct(e, tvnorms[e->index].id, - tvnorms[e->index].name); - e->index = i; - if (ret < 0) - return ret; - return 0; - } - case VIDIOC_G_INPUT: - { - int *input = arg; - *input = usbvision->ctl_input; - return 0; - } - case VIDIOC_S_INPUT: - { - int *input = arg; - if ((*input >= usbvision->video_inputs) || (*input < 0) ) - return -EINVAL; - usbvision->ctl_input = *input; - - down(&usbvision->lock); - usbvision_muxsel(usbvision, usbvision->ctl_input); - usbvision_set_input(usbvision); - usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight); - up(&usbvision->lock); - return 0; - } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; - - *id = usbvision->tvnorm->id; - - PDEBUG(DBG_IOCTL, "VIDIOC_G_STD std_id=%s", usbvision->tvnorm->name); - return 0; - } - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - unsigned int i; - - for (i = 0; i < TVNORMS; i++) - if (*id == tvnorms[i].id) - break; - if (i == TVNORMS) - for (i = 0; i < TVNORMS; i++) - if (*id & tvnorms[i].id) - break; - if (i == TVNORMS) - return -EINVAL; - - down(&usbvision->lock); - usbvision->tvnorm = &tvnorms[i]; - - call_i2c_clients(usbvision, VIDIOC_S_STD, - &usbvision->tvnorm->id); - - up(&usbvision->lock); - - PDEBUG(DBG_IOCTL, "VIDIOC_S_STD std_id=%s", usbvision->tvnorm->name); - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; - - if (!usbvision->have_tuner || vt->index) // Only tuner 0 - return -EINVAL; - strcpy(vt->name, "Television"); - /* Let clients fill in the remainder of this struct */ - call_i2c_clients(usbvision,VIDIOC_G_TUNER,vt); - - PDEBUG(DBG_IOCTL, "VIDIOC_G_TUNER signal=%x, afc=%x",vt->signal,vt->afc); - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt = arg; - - // Only no or one tuner for now - if (!usbvision->have_tuner || vt->index) - return -EINVAL; - /* let clients handle this */ - call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt); - - PDEBUG(DBG_IOCTL, "VIDIOC_S_TUNER"); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *freq = arg; - - freq->tuner = 0; // Only one tuner - freq->type = V4L2_TUNER_ANALOG_TV; - freq->frequency = usbvision->freq; - PDEBUG(DBG_IOCTL, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)freq->frequency); - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *freq = arg; - - // Only no or one tuner for now - if (!usbvision->have_tuner || freq->tuner) - return -EINVAL; - - usbvision->freq = freq->frequency; - call_i2c_clients(usbvision, cmd, freq); - PDEBUG(DBG_IOCTL, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)freq->frequency); - return 0; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *v = arg; - memset(v,0, sizeof(v)); - strcpy(v->name, "TV"); - PDEBUG(DBG_IOCTL, "VIDIOC_G_AUDIO"); - // FIXME: no more processings ??? - return 0; - } - case VIDIOC_S_AUDIO: - { - struct v4l2_audio *v = arg; - if(v->index) { - return -EINVAL; - } - PDEBUG(DBG_IOCTL, "VIDIOC_S_AUDIO"); - // FIXME: void function ??? - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - int id=ctrl->id; - - memset(ctrl,0,sizeof(*ctrl)); - ctrl->id=id; - - call_i2c_clients(usbvision, cmd, arg); - - if (ctrl->type) - return 0; - else - return -EINVAL; - - PDEBUG(DBG_IOCTL,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type); - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - PDEBUG(DBG_IOCTL,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); - call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); - return 0; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - PDEBUG(DBG_IOCTL, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value); - call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); - return 0; - } - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *vr = arg; - int ret; - - RESTRICT_TO_RANGE(vr->count,1,USBVISION_NUMFRAMES); - - // Check input validity : the user must do a VIDEO CAPTURE and MMAP method. - if((vr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || - (vr->memory != V4L2_MEMORY_MMAP)) - return -EINVAL; - - // FIXME : before this, we must control if buffers are still mapped. - // Then interrupt streaming if so... - if(usbvision->streaming == Stream_On) { - if ((ret = usbvision_stream_interrupt(usbvision))) - return ret; - } - - usbvision_empty_framequeues(usbvision); - - usbvision->curFrame = NULL; - - PDEBUG(DBG_IOCTL, "VIDIOC_REQBUFS count=%d",vr->count); - return 0; - } - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *vb = arg; - struct usbvision_frame *frame; - - // FIXME : must control that buffers are mapped (VIDIOC_REQBUFS has been called) - - if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { - return -EINVAL; - } - if(vb->index>=USBVISION_NUMFRAMES) { - return -EINVAL; - } - // Updating the corresponding frame state - vb->flags = 0; - frame = &usbvision->frame[vb->index]; - if(frame->grabstate >= FrameState_Ready) - vb->flags |= V4L2_BUF_FLAG_QUEUED; - if(frame->grabstate >= FrameState_Done) - vb->flags |= V4L2_BUF_FLAG_DONE; - if(frame->grabstate == FrameState_Unused) - vb->flags |= V4L2_BUF_FLAG_MAPPED; - vb->memory = V4L2_MEMORY_MMAP; - - vb->m.offset = vb->index*MAX_FRAME_SIZE; - - vb->memory = V4L2_MEMORY_MMAP; - vb->field = V4L2_FIELD_NONE; - vb->length = MAX_FRAME_SIZE; - vb->timestamp = usbvision->frame[vb->index].timestamp; - vb->sequence = usbvision->frame[vb->index].sequence; - return 0; - } - case VIDIOC_QBUF: - { - struct v4l2_buffer *vb = arg; - struct usbvision_frame *frame; - unsigned long lock_flags; - - // FIXME : works only on VIDEO_CAPTURE MODE, MMAP. - if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { - return -EINVAL; - } - if(vb->index>=USBVISION_NUMFRAMES) { - return -EINVAL; - } - - frame = &usbvision->frame[vb->index]; - - if (frame->grabstate != FrameState_Unused) { - return -EAGAIN; - } - - /* Mark it as ready and enqueue frame */ - frame->grabstate = FrameState_Ready; - frame->scanstate = ScanState_Scanning; - frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ - - vb->flags &= ~V4L2_BUF_FLAG_DONE; - - /* set v4l2_format index */ - frame->v4l2_format = usbvision->palette; - - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue); - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); - - PDEBUG(DBG_IOCTL, "VIDIOC_QBUF frame #%d",vb->index); - return 0; - } - case VIDIOC_DQBUF: - { - struct v4l2_buffer *vb = arg; - int ret; - struct usbvision_frame *f; - unsigned long lock_flags; - - if (vb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (list_empty(&(usbvision->outqueue))) { - if (usbvision->streaming == Stream_Idle) - return -EINVAL; - ret = wait_event_interruptible - (usbvision->wait_frame, - !list_empty(&(usbvision->outqueue))); - if (ret) - return ret; - } - - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - f = list_entry(usbvision->outqueue.next, - struct usbvision_frame, frame); - list_del(usbvision->outqueue.next); - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); - - f->grabstate = FrameState_Unused; - - vb->memory = V4L2_MEMORY_MMAP; - vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE; - vb->index = f->index; - vb->sequence = f->sequence; - vb->timestamp = f->timestamp; - vb->field = V4L2_FIELD_NONE; - vb->bytesused = f->scanlength; - - if(debug & DBG_IOCTL) { // do not spend computing time for debug stuff if not needed ! - if(usbvision_counter == 100) { - PDEBUG(DBG_IOCTL, "VIDIOC_DQBUF delta=%d",(unsigned)(jiffies-usbvision_timestamp)); - usbvision_counter = 0; - usbvision_timestamp = jiffies; - } - else { - usbvision_counter++; - } - PDEBUG(DBG_IOCTL, "VIDIOC_DQBUF frame #%d",vb->index); - } - return 0; - } - case VIDIOC_STREAMON: - { - int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; - - usbvision->streaming = Stream_On; - - if(debug & DBG_IOCTL) usbvision_timestamp = jiffies; - - call_i2c_clients(usbvision,VIDIOC_STREAMON , &b); - - PDEBUG(DBG_IOCTL, "VIDIOC_STREAMON"); - - return 0; - } - case VIDIOC_STREAMOFF: - { - int *type = arg; - int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if(usbvision->streaming == Stream_On) { - usbvision_stream_interrupt(usbvision); - // Stop all video streamings - call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b); - } - usbvision_empty_framequeues(usbvision); - - PDEBUG(DBG_IOCTL, "VIDIOC_STREAMOFF"); - return 0; - } - case VIDIOC_G_FBUF: - { - struct v4l2_framebuffer *vb = arg; - - if (dga) { - *vb = usbvision->vid_buf; - } - else { - memset(vb, 0, sizeof(vb)); //dga not supported, not used - } - PDEBUG(DBG_IOCTL, "VIDIOC_G_FBUF base=%p, width=%d, height=%d, pixelformat=%d, bpl=%d", - vb->base, vb->fmt.width, vb->fmt.height, vb->fmt.pixelformat,vb->fmt.bytesperline); - return 0; - } - case VIDIOC_S_FBUF: - { - struct v4l2_framebuffer *vb = arg; - int formatIdx; - - if (dga == 0) { - return -EINVAL; - } - - if(!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_ADMIN)) { - return -EPERM; - } - - PDEBUG(DBG_IOCTL, "VIDIOC_S_FBUF base=%p, width=%d, height=%d, pixelformat=%d, bpl=%d", - vb->base, vb->fmt.width, vb->fmt.height, vb->fmt.pixelformat,vb->fmt.bytesperline); - - for (formatIdx=0; formatIdx <= USBVISION_SUPPORTED_PALETTES; formatIdx++) { - if (formatIdx == USBVISION_SUPPORTED_PALETTES) { - return -EINVAL; // no matching video_format - } - if ((vb->fmt.pixelformat == usbvision_v4l2_format[formatIdx].format) && - (usbvision_v4l2_format[formatIdx].supported)) { - break; //found matching video_format - } - } - - if (vb->fmt.bytesperline<1) { - return -EINVAL; - } - if (usbvision->overlay) { - return -EBUSY; - } - down(&usbvision->lock); - if (usbvision->overlay_base) { - iounmap(usbvision->overlay_base); - usbvision->vid_buf_valid = 0; - } - usbvision->overlay_base = ioremap((ulong)vb->base, vb->fmt.height * vb->fmt.bytesperline); - if (usbvision->overlay_base) { - usbvision->vid_buf_valid = 1; - } - usbvision->vid_buf = *vb; - usbvision->overlay_frame.v4l2_format = usbvision_v4l2_format[formatIdx]; - up(&usbvision->lock); - return 0; - } - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *vfd = arg; - - if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) { - return -EINVAL; - } - vfd->flags = 0; - vfd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc); - vfd->pixelformat = usbvision_v4l2_format[vfd->index].format; - memset(vfd->reserved, 0, sizeof(vfd->reserved)); - return 0; - } - case VIDIOC_G_FMT: - { - struct v4l2_format *vf = arg; - - switch (vf->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - { - vf->fmt.pix.width = usbvision->curwidth; - vf->fmt.pix.height = usbvision->curheight; - vf->fmt.pix.pixelformat = usbvision->palette.format; - vf->fmt.pix.bytesperline = usbvision->curwidth*usbvision->palette.bytes_per_pixel; - vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*usbvision->curheight; - vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ - } - return 0; - default: - PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT invalid type %d",vf->type); - return -EINVAL; - } - PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT w=%d, h=%d",vf->fmt.win.w.width, vf->fmt.win.w.height); - return 0; - } - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - { - struct v4l2_format *vf = arg; - struct v4l2_clip *vc=NULL; - int on,formatIdx; - - switch(vf->type) { - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - { - if (vf->fmt.win.clipcount>256) { - return -EDOM; /* Too many clips! */ - } - // Do every clips. - vc = vmalloc(sizeof(struct v4l2_clip)*(vf->fmt.win.clipcount+4)); - if (vc == NULL) { - return -ENOMEM; - } - if (vf->fmt.win.clipcount && copy_from_user(vc,vf->fmt.win.clips,sizeof(struct v4l2_clip)*vf->fmt.win.clipcount)) { - return -EFAULT; - } - on = usbvision->overlay; // Save overlay state - if (on) { - usbvision_cap(usbvision, 0); - } - - // strange, it seems xawtv sometimes calls us with 0 - // width and/or height. Ignore these values - if (vf->fmt.win.w.left == 0) { - vf->fmt.win.w.left = usbvision->vid_win.fmt.win.w.left; - } - if (vf->fmt.win.w.top == 0) { - vf->fmt.win.w.top = usbvision->vid_win.fmt.win.w.top; - } - - // by now we are committed to the new data... - down(&usbvision->lock); - RESTRICT_TO_RANGE(vf->fmt.win.w.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); - RESTRICT_TO_RANGE(vf->fmt.win.w.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); - usbvision->vid_win = *vf; - usbvision->overlay_frame.width = vf->fmt.win.w.width; - usbvision->overlay_frame.height = vf->fmt.win.w.height; - usbvision_set_output(usbvision, vf->fmt.win.w.width, vf->fmt.win.w.height); - up(&usbvision->lock); - - // Impose display clips - if (vf->fmt.win.w.left+vf->fmt.win.w.width > (unsigned int)usbvision->vid_buf.fmt.width) { - usbvision_new_clip(vf, vc, usbvision->vid_buf.fmt.width-vf->fmt.win.w.left, 0, vf->fmt.win.w.width-1, vf->fmt.win.w.height-1); - } - if (vf->fmt.win.w.top+vf->fmt.win.w.height > (unsigned int)usbvision->vid_buf.fmt.height) { - usbvision_new_clip(vf, vc, 0, usbvision->vid_buf.fmt.height-vf->fmt.win.w.top, vf->fmt.win.w.width-1, vf->fmt.win.w.height-1); - } - - // built the requested clipping zones - usbvision_built_overlay(usbvision, vf->fmt.win.clipcount, vc); - vfree(vc); - - // restore overlay state - if (on) { - usbvision_cap(usbvision, 1); - } - usbvision->vid_win_valid = 1; - PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT overlay x=%d, y=%d, w=%d, h=%d, chroma=%x, clips=%d", - vf->fmt.win.w.left, vf->fmt.win.w.top, vf->fmt.win.w.width, vf->fmt.win.w.height, vf->fmt.win.chromakey, vf->fmt.win.clipcount); - return 0; - } - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - { - /* Find requested format in available ones */ - for(formatIdx=0;formatIdxfmt.pix.pixelformat == usbvision_v4l2_format[formatIdx].format) { - usbvision->palette = usbvision_v4l2_format[formatIdx]; - break; - } - } - /* robustness */ - if(formatIdx == USBVISION_SUPPORTED_PALETTES) { - return -EINVAL; - } - RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); - RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); - // by now we are committed to the new data... - down(&usbvision->lock); - usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height); - up(&usbvision->lock); - - PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT grabdisplay w=%d, h=%d, format=%s", - vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc); - return 0; - } - default: - return -EINVAL; - } - } - case VIDIOC_OVERLAY: - { - int *v = arg; - - if ( (dga == 0) && - (usbvision->palette.format != V4L2_PIX_FMT_YVU420) && - (usbvision->palette.format != V4L2_PIX_FMT_YUV422P) ) { - PDEBUG(DBG_IOCTL, "VIDIOC_OVERLAY DGA disabled"); - return -EINVAL; - } - - if (*v == 0) { - usbvision_cap(usbvision, 0); - } - else { - // are VIDIOCSFBUF and VIDIOCSWIN done? - if ((usbvision->vid_buf_valid == 0) || (usbvision->vid_win_valid == 0)) { - PDEBUG(DBG_IOCTL, "VIDIOC_OVERLAY vid_buf_valid %d; vid_win_valid %d", - usbvision->vid_buf_valid, usbvision->vid_win_valid); - return -EINVAL; - } - usbvision_cap(usbvision, 1); - } - PDEBUG(DBG_IOCTL, "VIDIOC_OVERLAY %s", (*v)?"on":"off"); - return 0; - } - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static int usbvision_v4l2_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(inode, file, cmd, arg, usbvision_v4l2_do_ioctl); -} - - -static ssize_t usbvision_v4l2_read(struct file *file, char *buf, - size_t count, loff_t *ppos) -{ - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - int noblock = file->f_flags & O_NONBLOCK; - unsigned long lock_flags; - - int frmx = -1; - int ret,i; - struct usbvision_frame *frame; - - PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __FUNCTION__, (unsigned long)count, noblock); - - if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL)) - return -EFAULT; - - /* no stream is running, make it running ! */ - usbvision->streaming = Stream_On; - call_i2c_clients(usbvision,VIDIOC_STREAMON , NULL); - - /* First, enqueue as many frames as possible (like a user of VIDIOC_QBUF would do) */ - for(i=0;iframe[i]; - if(frame->grabstate == FrameState_Unused) { - /* Mark it as ready and enqueue frame */ - frame->grabstate = FrameState_Ready; - frame->scanstate = ScanState_Scanning; - frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ - - /* set v4l2_format index */ - frame->v4l2_format = usbvision->palette; - - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - list_add_tail(&frame->frame, &usbvision->inqueue); - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); - } - } - - /* Then try to steal a frame (like a VIDIOC_DQBUF would do) */ - if (list_empty(&(usbvision->outqueue))) { - if(noblock) - return -EAGAIN; - - ret = wait_event_interruptible - (usbvision->wait_frame, - !list_empty(&(usbvision->outqueue))); - if (ret) - return ret; - } - - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - frame = list_entry(usbvision->outqueue.next, - struct usbvision_frame, frame); - list_del(usbvision->outqueue.next); - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); - - if(debug & DBG_IOCTL) { // do not spend computing time for debug stuff if not needed ! - if(usbvision_counter == 100) { - PDEBUG(DBG_IOCTL, "VIDIOC_DQBUF delta=%d",(unsigned)(jiffies-usbvision_timestamp)); - usbvision_counter = 0; - usbvision_timestamp = jiffies; - } - else { - usbvision_counter++; - } - } - - /* An error returns an empty frame */ - if (frame->grabstate == FrameState_Error) { - frame->bytes_read = 0; - return 0; - } - - PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", __FUNCTION__, - frame->index, frame->bytes_read, frame->scanlength); - - /* copy bytes to user space; we allow for partials reads */ - if ((count + frame->bytes_read) > (unsigned long)frame->scanlength) - count = frame->scanlength - frame->bytes_read; - - if (copy_to_user(buf, frame->data + frame->bytes_read, count)) { - return -EFAULT; - } - - frame->bytes_read += count; - PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", __FUNCTION__, - (unsigned long)count, frame->bytes_read); - - // For now, forget the frame if it has not been read in one shot. -/* if (frame->bytes_read >= frame->scanlength) {// All data has been read */ - frame->bytes_read = 0; - - /* Mark it as available to be used again. */ - usbvision->frame[frmx].grabstate = FrameState_Unused; -/* } */ - - return count; -} - -static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) -{ - unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; - void *pos; - u32 i; - - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return -EFAULT; - - if (!(vma->vm_flags & VM_WRITE) || - size != PAGE_ALIGN(usbvision->max_frame_size)) { - return -EINVAL; - } - - for (i = 0; i < USBVISION_NUMFRAMES; i++) { - if (((usbvision->max_frame_size*i) >> PAGE_SHIFT) == vma->vm_pgoff) - break; - } - if (i == USBVISION_NUMFRAMES) { - PDEBUG(DBG_FUNC, "mmap: user supplied mapping address is out of range"); - return -EINVAL; - } - - /* VM_IO is eventually going to replace PageReserved altogether */ - vma->vm_flags |= VM_IO; - vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ - - pos = usbvision->frame[i].data; - while (size > 0) { - - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - PDEBUG(DBG_FUNC, "mmap: vm_insert_page failed"); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return 0; -} - - -/* - * Here comes the stuff for radio on usbvision based devices - * - */ -static int usbvision_radio_open(struct inode *inode, struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - struct v4l2_frequency freq; - int errCode = 0; - - PDEBUG(DBG_RIO, "%s:", __FUNCTION__); - - down(&usbvision->lock); - - if (usbvision->user) { - err("%s: Someone tried to open an already opened USBVision Radio!", __FUNCTION__); - errCode = -EBUSY; - } - else { - if(PowerOnAtOpen) { - if (timer_pending(&usbvision->powerOffTimer)) { - del_timer(&usbvision->powerOffTimer); - } - if (usbvision->power == 0) { - usbvision_power_on(usbvision); - usbvision_init_i2c(usbvision); - } - } - - // If so far no errors then we shall start the radio - usbvision->radio = 1; - call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type); - freq.frequency = 1517; //SWR3 @ 94.8MHz - call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, &freq); - usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); - usbvision->user++; - } - - if (errCode) { - if (PowerOnAtOpen) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } - } - up(&usbvision->lock); - return errCode; -} - - -static int usbvision_radio_close(struct inode *inode, struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - int errCode = 0; - - PDEBUG(DBG_RIO, ""); - - down(&usbvision->lock); - - usbvision_audio_off(usbvision); - usbvision->radio=0; - usbvision->user--; - - if (PowerOnAtOpen) { - mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME); - usbvision->initialized = 0; - } - - up(&usbvision->lock); - - if (usbvision->remove_pending) { - info("%s: Final disconnect", __FUNCTION__); - usbvision_release(usbvision); - } - - - PDEBUG(DBG_RIO, "success"); - - return errCode; -} - -static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return -EIO; - - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *vc=arg; - - memset(vc, 0, sizeof(*vc)); - strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); - strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString, - sizeof(vc->card)); - strlcpy(vc->bus_info, usbvision->dev->dev.bus_id, - sizeof(vc->bus_info)); - vc->version = USBVISION_DRIVER_VERSION; - vc->capabilities = (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); - PDEBUG(DBG_RIO, "VIDIOC_QUERYCAP"); - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - int id=ctrl->id; - - memset(ctrl,0,sizeof(*ctrl)); - ctrl->id=id; - - call_i2c_clients(usbvision, cmd, arg); - PDEBUG(DBG_RIO,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type); - - if (ctrl->type) - return 0; - else - return -EINVAL; - - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); - PDEBUG(DBG_RIO,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); - return 0; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); - PDEBUG(DBG_RIO, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value); - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; - - if (t->index > 0) - return -EINVAL; - - memset(t,0,sizeof(*t)); - strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; - - /* Let clients fill in the remainder of this struct */ - call_i2c_clients(usbvision,VIDIOC_G_TUNER,t); - PDEBUG(DBG_RIO, "VIDIOC_G_TUNER signal=%x, afc=%x",t->signal,t->afc); - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt = arg; - - // Only no or one tuner for now - if (!usbvision->have_tuner || vt->index) - return -EINVAL; - /* let clients handle this */ - call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt); - - PDEBUG(DBG_RIO, "VIDIOC_S_TUNER"); - return 0; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *a = arg; - - memset(a,0,sizeof(*a)); - strcpy(a->name,"Radio"); - PDEBUG(DBG_RIO, "VIDIOC_G_AUDIO"); - return 0; - } - case VIDIOC_S_AUDIO: - case VIDIOC_S_INPUT: - case VIDIOC_S_STD: - return 0; - - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - memset(f,0,sizeof(*f)); - - f->type = V4L2_TUNER_RADIO; - f->frequency = usbvision->freq; - call_i2c_clients(usbvision, cmd, f); - PDEBUG(DBG_RIO, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)f->frequency); - - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - if (f->tuner != 0) - return -EINVAL; - usbvision->freq = f->frequency; - call_i2c_clients(usbvision, cmd, f); - PDEBUG(DBG_RIO, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)f->frequency); - - return 0; - } - default: - { - PDEBUG(DBG_RIO, "%s: Unknown command %x", __FUNCTION__, cmd); - return -ENOIOCTLCMD; - } - } - return 0; -} - - -static int usbvision_radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(inode, file, cmd, arg, usbvision_do_radio_ioctl); -} - - -/* - * Here comes the stuff for vbi on usbvision based devices - * - */ -static int usbvision_vbi_open(struct inode *inode, struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - unsigned long freq; - int errCode = 0; - - PDEBUG(DBG_RIO, "%s:", __FUNCTION__); - - down(&usbvision->lock); - - if (usbvision->user) { - err("%s: Someone tried to open an already opened USBVision VBI!", __FUNCTION__); - errCode = -EBUSY; - } - else { - if(PowerOnAtOpen) { - if (timer_pending(&usbvision->powerOffTimer)) { - del_timer(&usbvision->powerOffTimer); - } - if (usbvision->power == 0) { - usbvision_power_on(usbvision); - usbvision_init_i2c(usbvision); - } - } - - // If so far no errors then we shall start the vbi device - //usbvision->vbi = 1; - call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type); - freq = 1517; //SWR3 @ 94.8MHz - call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, &freq); - usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); - usbvision->user++; - } - - if (errCode) { - if (PowerOnAtOpen) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); - usbvision_power_off(usbvision); - usbvision->initialized = 0; - } - } - up(&usbvision->lock); - return errCode; -} - -static int usbvision_vbi_close(struct inode *inode, struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - int errCode = 0; - - PDEBUG(DBG_RIO, ""); - - down(&usbvision->lock); - - usbvision_audio_off(usbvision); - usbvision->vbi=0; - usbvision->user--; - - if (PowerOnAtOpen) { - mod_timer(&usbvision->powerOffTimer, jiffies + USBVISION_POWEROFF_TIME); - usbvision->initialized = 0; - } - - up(&usbvision->lock); - - if (usbvision->remove_pending) { - info("%s: Final disconnect", __FUNCTION__); - usbvision_release(usbvision); - } - - - PDEBUG(DBG_RIO, "success"); - - return errCode; -} - -static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return -EIO; - - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *vc=arg; - memset(vc, 0, sizeof(struct v4l2_capability)); - strcpy(vc->driver,"usbvision vbi"); - strcpy(vc->card,usbvision->vcap.card); - strcpy(vc->bus_info,"usb"); - vc->version = USBVISION_DRIVER_VERSION; /* version */ - vc->capabilities = V4L2_CAP_VBI_CAPTURE; /* capabilities */ - PDEBUG(DBG_RIO, "%s: VIDIOC_QUERYCAP", __FUNCTION__); - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner *vt = arg; - - if((vt->tuner) || (usbvision->channel)) { /* Only tuner 0 */ - return -EINVAL; - } - strcpy(vt->name, "vbi"); - // japan: 76.0 MHz - 89.9 MHz - // western europe: 87.5 MHz - 108.0 MHz - // russia: 65.0 MHz - 108.0 MHz - vt->rangelow=(int)(65*16); - vt->rangehigh=(int)(108*16); - vt->flags= 0; - vt->mode = 0; - call_i2c_clients(usbvision,cmd,vt); - PDEBUG(DBG_RIO, "%s: VIDIOCGTUNER signal=%d", __FUNCTION__, vt->signal); - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner *vt = arg; - - // Only channel 0 has a tuner - if((vt->tuner) || (usbvision->channel)) { - return -EINVAL; - } - PDEBUG(DBG_RIO, "%s: VIDIOCSTUNER", __FUNCTION__); - return 0; - } - case VIDIOCGAUDIO: - { - struct video_audio *va = arg; - memset(va,0, sizeof(struct video_audio)); - call_i2c_clients(usbvision, cmd, va); - va->flags|=VIDEO_AUDIO_MUTABLE; - va->volume=1; - va->step=1; - strcpy(va->name, "vbi"); - PDEBUG(DBG_RIO, "%s: VIDIOCGAUDIO", __FUNCTION__); - return 0; - } - case VIDIOCSAUDIO: - { - struct video_audio *va = arg; - if(va->audio) { - return -EINVAL; - } - - if(va->flags & VIDEO_AUDIO_MUTE) { - if (usbvision_audio_mute(usbvision)) { - return -EFAULT; - } - } - else { - if (usbvision_audio_on(usbvision)) { - return -EFAULT; - } - } - PDEBUG(DBG_RIO, "%s: VIDIOCSAUDIO flags=0x%x)", __FUNCTION__, va->flags); - return 0; - } - case VIDIOCGFREQ: - { - unsigned long *freq = arg; - - *freq = usbvision->freq; - PDEBUG(DBG_RIO, "%s: VIDIOCGFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4); - return 0; - } - case VIDIOCSFREQ: - { - unsigned long *freq = arg; - - usbvision->freq = *freq; - call_i2c_clients(usbvision, cmd, freq); - PDEBUG(DBG_RIO, "%s: VIDIOCSFREQ freq = %ld00 kHz", __FUNCTION__, (*freq * 10)>>4); - return 0; - } - default: - { - PDEBUG(DBG_RIO, "%s: Unknown command %d", __FUNCTION__, cmd); - return -ENOIOCTLCMD; - } - } - return 0; -} - -static int usbvision_vbi_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(inode, file, cmd, arg, usbvision_do_vbi_ioctl); -} - - - -static void usbvision_configure_video(struct usb_usbvision *usbvision) -{ - int model,i; - - if (usbvision == NULL) - return; - - model = usbvision->DevModel; - usbvision->depth = 24; - usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24; - - if (usbvision_device_data[usbvision->DevModel].Vin_Reg2 >= 0) { - usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2 & 0xff; - } else { - usbvision->Vin_Reg2_Preset = 0; - } - - memset(&usbvision->vcap, 0, sizeof(usbvision->vcap)); - strcpy(usbvision->vcap.driver, "USBVision"); - strlcpy(usbvision->vcap.bus_info, usbvision->dev->dev.bus_id, - sizeof(usbvision->vcap.bus_info)); - usbvision->vcap.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | - (dga ? (V4L2_FBUF_CAP_LIST_CLIPPING | V4L2_CAP_VIDEO_OVERLAY) : 0) | - (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); - usbvision->vcap.version = USBVISION_DRIVER_VERSION; /* version */ - - for (i = 0; i < TVNORMS; i++) - if (usbvision_device_data[model].VideoNorm == tvnorms[i].mode) - break; - if (i == TVNORMS) - i = 0; - usbvision->tvnorm = &tvnorms[i]; /* set default norm */ - - usbvision->video_inputs = usbvision_device_data[model].VideoChannels; - usbvision->ctl_input = 0; - - /* This should be here to make i2c clients to be able to register */ - usbvision_audio_off(usbvision); //first switch off audio - if (!PowerOnAtOpen) { - usbvision_power_on(usbvision); //and then power up the noisy tuner - usbvision_init_i2c(usbvision); - } -} - -// -// Video registration stuff -// - -// Video template -static struct file_operations usbvision_fops = { - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) - .owner = THIS_MODULE, - #endif - .open = usbvision_v4l2_open, - .release = usbvision_v4l2_close, - .read = usbvision_v4l2_read, - .mmap = usbvision_v4l2_mmap, - .ioctl = usbvision_v4l2_ioctl, - .llseek = no_llseek, -}; -static struct video_device usbvision_video_template = { - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) - .owner = THIS_MODULE, - #endif - .type = VID_TYPE_TUNER | VID_TYPE_CAPTURE, - .hardware = VID_HARDWARE_USBVISION, - .fops = &usbvision_fops, - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) - .name = "usbvision-video", - .release = video_device_release, - #endif - .minor = -1, -}; - - -// Radio template -static struct file_operations usbvision_radio_fops = { - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) - .owner = THIS_MODULE, - #endif - .open = usbvision_radio_open, - .release = usbvision_radio_close, - .ioctl = usbvision_radio_ioctl, - .llseek = no_llseek, -}; - -static struct video_device usbvision_radio_template= -{ - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) - .owner = THIS_MODULE, - #endif - .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_USBVISION, - .fops = &usbvision_radio_fops, - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) - .release = video_device_release, - .name = "usbvision-radio", - #endif - .minor = -1, -}; - - -// vbi template -static struct file_operations usbvision_vbi_fops = { - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) - .owner = THIS_MODULE, - #endif - .open = usbvision_vbi_open, - .release = usbvision_vbi_close, - .ioctl = usbvision_vbi_ioctl, - .llseek = no_llseek, -}; - -static struct video_device usbvision_vbi_template= -{ - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) - .owner = THIS_MODULE, - #endif - .type = VID_TYPE_TUNER, - .hardware = VID_HARDWARE_USBVISION, - .fops = &usbvision_vbi_fops, - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) - .release = video_device_release, - .name = "usbvision-vbi", - #endif - .minor = -1, -}; - - -static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, - struct video_device *vdev_template, - char *name) -{ - struct usb_device *usb_dev = usbvision->dev; - struct video_device *vdev; - - if (usb_dev == NULL) { - err("%s: usbvision->dev is not set", __FUNCTION__); - return NULL; - } - - vdev = video_device_alloc(); - if (NULL == vdev) { - return NULL; - } - *vdev = *vdev_template; -// vdev->minor = -1; - vdev->dev = &usb_dev->dev; - snprintf(vdev->name, sizeof(vdev->name), "%s", name); - video_set_drvdata(vdev, usbvision); - return vdev; -} - -// unregister video4linux devices -static void usbvision_unregister_video(struct usb_usbvision *usbvision) -{ - // vbi Device: - if (usbvision->vbi) { - PDEBUG(DBG_PROBE, "unregister /dev/vbi%d [v4l2]", usbvision->vbi->minor & 0x1f); - if (usbvision->vbi->minor != -1) { - video_unregister_device(usbvision->vbi); - } - else { - video_device_release(usbvision->vbi); - } - usbvision->vbi = NULL; - } - - // Radio Device: - if (usbvision->rdev) { - PDEBUG(DBG_PROBE, "unregister /dev/radio%d [v4l2]", usbvision->rdev->minor & 0x1f); - if (usbvision->rdev->minor != -1) { - video_unregister_device(usbvision->rdev); - } - else { - video_device_release(usbvision->rdev); - } - usbvision->rdev = NULL; - } - - // Video Device: - if (usbvision->vdev) { - PDEBUG(DBG_PROBE, "unregister /dev/video%d [v4l2]", usbvision->vdev->minor & 0x1f); - if (usbvision->vdev->minor != -1) { - video_unregister_device(usbvision->vdev); - } - else { - video_device_release(usbvision->vdev); - } - usbvision->vdev = NULL; - } -} - -// register video4linux devices -static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) -{ - // Video Device: - usbvision->vdev = usbvision_vdev_init(usbvision, &usbvision_video_template, "USBVision Video"); - if (usbvision->vdev == NULL) { - goto err_exit; - } - if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr)<0) { - goto err_exit; - } - info("USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2]", usbvision->nr,usbvision->vdev->minor & 0x1f); - - // Radio Device: - if (usbvision_device_data[usbvision->DevModel].Radio) { - // usbvision has radio - usbvision->rdev = usbvision_vdev_init(usbvision, &usbvision_radio_template, "USBVision Radio"); - if (usbvision->rdev == NULL) { - goto err_exit; - } - if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr)<0) { - goto err_exit; - } - info("USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]", usbvision->nr, usbvision->rdev->minor & 0x1f); - } - // vbi Device: - if (usbvision_device_data[usbvision->DevModel].vbi) { - usbvision->vbi = usbvision_vdev_init(usbvision, &usbvision_vbi_template, "USBVision VBI"); - if (usbvision->vdev == NULL) { - goto err_exit; - } - if (video_register_device(usbvision->vbi, VFL_TYPE_VBI, vbi_nr)<0) { - goto err_exit; - } - info("USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!)", usbvision->nr,usbvision->vbi->minor & 0x1f); - } - // all done - return 0; - - err_exit: - err("USBVision[%d]: video_register_device() failed", usbvision->nr); - usbvision_unregister_video(usbvision); - return -1; -} - -/* - * usbvision_alloc() - * - * This code allocates the struct usb_usbvision. It is filled with default values. - * - * Returns NULL on error, a pointer to usb_usbvision else. - * - */ -static struct usb_usbvision *usbvision_alloc(struct usb_device *dev) -{ - struct usb_usbvision *usbvision; - - if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) == NULL) { - goto err_exit; - } - - usbvision->dev = dev; - - init_MUTEX(&usbvision->lock); /* to 1 == available */ - - // prepare control urb for control messages during interrupts - usbvision->ctrlUrb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); - if (usbvision->ctrlUrb == NULL) { - goto err_exit; - } - init_waitqueue_head(&usbvision->ctrlUrb_wq); - init_MUTEX(&usbvision->ctrlUrbLock); /* to 1 == available */ - - init_timer(&usbvision->powerOffTimer); - usbvision->powerOffTimer.data = (long) usbvision; - usbvision->powerOffTimer.function = usbvision_powerOffTimer; - - return usbvision; - -err_exit: - if (usbvision && usbvision->ctrlUrb) { - usb_free_urb(usbvision->ctrlUrb); - } - if (usbvision) { - kfree(usbvision); - } - return NULL; -} - -/* - * usbvision_release() - * - * This code does final release of struct usb_usbvision. This happens - * after the device is disconnected -and- all clients closed their files. - * - */ -static void usbvision_release(struct usb_usbvision *usbvision) -{ - PDEBUG(DBG_PROBE, ""); - - down(&usbvision->lock); - - if (timer_pending(&usbvision->powerOffTimer)) { - del_timer(&usbvision->powerOffTimer); - } - - usbvision->usbvision_used = 0; - usbvision->initialized = 0; - - up(&usbvision->lock); - - usbvision_remove_sysfs(usbvision->vdev); - usbvision_unregister_video(usbvision); - if(dga) { - if (usbvision->overlay_base) { - iounmap(usbvision->overlay_base); - } - } - - if (usbvision->ctrlUrb) { - usb_free_urb(usbvision->ctrlUrb); - } - - kfree(usbvision); - - PDEBUG(DBG_PROBE, "success"); -} - - -/* - * usbvision_probe() - * - * This procedure queries device descriptor and accepts the interface - * if it looks like USBVISION video device - * - */ -static int __devinit usbvision_probe(struct usb_interface *intf, const struct usb_device_id *devid) -{ - struct usb_device *dev = interface_to_usbdev(intf); - __u8 ifnum = intf->altsetting->desc.bInterfaceNumber; - const struct usb_host_interface *interface; - struct usb_usbvision *usbvision = NULL; - const struct usb_endpoint_descriptor *endpoint; - int model; - - PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u", - dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); - /* Is it an USBVISION video dev? */ - model = 0; - for(model = 0; usbvision_device_data[model].idVendor; model++) { - if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) { - continue; - } - if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) { - continue; - } - - info("%s: %s found", __FUNCTION__, usbvision_device_data[model].ModelString); - break; - } - - if (usbvision_device_data[model].idVendor == 0) { - return -ENODEV; //no matching device - } - if (usbvision_device_data[model].Interface >= 0) { - interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0]; - } - else { - interface = &dev->actconfig->interface[ifnum]->altsetting[0]; - } - endpoint = &interface->endpoint[1].desc; - if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) { - err("%s: interface %d. has non-ISO endpoint!", __FUNCTION__, ifnum); - err("%s: Endpoint attribures %d", __FUNCTION__, endpoint->bmAttributes); - return -ENODEV; - } - if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { - err("%s: interface %d. has ISO OUT endpoint!", __FUNCTION__, ifnum); - return -ENODEV; - } - - usb_get_dev(dev); - - if ((usbvision = usbvision_alloc(dev)) == NULL) { - err("%s: couldn't allocate USBVision struct", __FUNCTION__); - return -ENOMEM; - } - if (dev->descriptor.bNumConfigurations > 1) { - usbvision->bridgeType = BRIDGE_NT1004; - } - else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") { - usbvision->bridgeType = BRIDGE_NT1005; - } - else { - usbvision->bridgeType = BRIDGE_NT1003; - } - PDEBUG(DBG_PROBE, "bridgeType %d", usbvision->bridgeType); - - down(&usbvision->lock); - - usbvision->nr = usbvision_nr++; - - usbvision->have_tuner = usbvision_device_data[model].Tuner; - if (usbvision->have_tuner) { - usbvision->tuner_type = usbvision_device_data[model].TunerType; - } - - usbvision->tuner_addr = ADDR_UNSET; - - usbvision->DevModel = model; - usbvision->remove_pending = 0; - usbvision->last_error = 0; - usbvision->iface = ifnum; - usbvision->ifaceAltInactive = 0; - usbvision->ifaceAltActive = 1; - usbvision->video_endp = endpoint->bEndpointAddress; - usbvision->isocPacketSize = 0; - usbvision->usb_bandwidth = 0; - usbvision->user = 0; - usbvision->streaming = Stream_Off; - usbvision_register_video(usbvision); - usbvision_configure_video(usbvision); - up(&usbvision->lock); - - - usb_set_intfdata (intf, usbvision); - usbvision_create_sysfs(usbvision->vdev); - - PDEBUG(DBG_PROBE, "success"); - return 0; -} - - -/* - * usbvision_disconnect() - * - * This procedure stops all driver activity, deallocates interface-private - * structure (pointed by 'ptr') and after that driver should be removable - * with no ill consequences. - * - */ -static void __devexit usbvision_disconnect(struct usb_interface *intf) -{ - struct usb_usbvision *usbvision = usb_get_intfdata(intf); - - PDEBUG(DBG_PROBE, ""); - - if (usbvision == NULL) { - err("%s: usb_get_intfdata() failed", __FUNCTION__); - return; - } - usb_set_intfdata (intf, NULL); - - down(&usbvision->lock); - - // At this time we ask to cancel outstanding URBs - usbvision_stop_isoc(usbvision); - - if (usbvision->power) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); - usbvision_power_off(usbvision); - } - usbvision->remove_pending = 1; // Now all ISO data will be ignored - - usb_put_dev(usbvision->dev); - usbvision->dev = NULL; // USB device is no more - - up(&usbvision->lock); - - if (usbvision->user) { - info("%s: In use, disconnect pending", __FUNCTION__); - wake_up_interruptible(&usbvision->wait_frame); - wake_up_interruptible(&usbvision->wait_stream); - } - else { - usbvision_release(usbvision); - } - - PDEBUG(DBG_PROBE, "success"); - -} - -static struct usb_driver usbvision_driver = { - .name = "usbvision", - .id_table = usbvision_table, - .probe = usbvision_probe, - .disconnect = usbvision_disconnect -}; - -/* - * customdevice_process() - * - * This procedure preprocesses CustomDevice parameter if any - * - */ -void customdevice_process(void) -{ - usbvision_device_data[0]=usbvision_device_data[1]; - usbvision_table[0]=usbvision_table[1]; - - if(CustomDevice) - { - char *parse=CustomDevice; - - PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice); - - /*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1" - usbvision_device_data[0].idVendor; - usbvision_device_data[0].idProduct; - usbvision_device_data[0].Interface; - usbvision_device_data[0].Codec; - usbvision_device_data[0].VideoChannels; - usbvision_device_data[0].VideoNorm; - usbvision_device_data[0].AudioChannels; - usbvision_device_data[0].Radio; - usbvision_device_data[0].Tuner; - usbvision_device_data[0].TunerType; - usbvision_device_data[0].Vin_Reg1; - usbvision_device_data[0].Vin_Reg2; - usbvision_device_data[0].X_Offset; - usbvision_device_data[0].Y_Offset; - usbvision_device_data[0].Dvi_yuv; - usbvision_device_data[0].ModelString; - */ - - rmspace(parse); - usbvision_device_data[0].ModelString="USBVISION Custom Device"; - - parse+=2; - sscanf(parse,"%x",&usbvision_device_data[0].idVendor); - goto2next(parse); - PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_device_data[0].idVendor); - parse+=2; - sscanf(parse,"%x",&usbvision_device_data[0].idProduct); - goto2next(parse); - PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_device_data[0].idProduct); - sscanf(parse,"%d",&usbvision_device_data[0].Interface); - goto2next(parse); - PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface); - sscanf(parse,"%d",&usbvision_device_data[0].Codec); - goto2next(parse); - PDEBUG(DBG_PROBE, "Codec=%d", usbvision_device_data[0].Codec); - sscanf(parse,"%d",&usbvision_device_data[0].VideoChannels); - goto2next(parse); - PDEBUG(DBG_PROBE, "VideoChannels=%d", usbvision_device_data[0].VideoChannels); - - switch(*parse) - { - case 'P': - PDEBUG(DBG_PROBE, "VideoNorm=PAL"); - usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL; - break; - - case 'S': - PDEBUG(DBG_PROBE, "VideoNorm=SECAM"); - usbvision_device_data[0].VideoNorm=VIDEO_MODE_SECAM; - break; - - case 'N': - PDEBUG(DBG_PROBE, "VideoNorm=NTSC"); - usbvision_device_data[0].VideoNorm=VIDEO_MODE_NTSC; - break; - - default: - PDEBUG(DBG_PROBE, "VideoNorm=PAL (by default)"); - usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL; - break; - } - goto2next(parse); - - sscanf(parse,"%d",&usbvision_device_data[0].AudioChannels); - goto2next(parse); - PDEBUG(DBG_PROBE, "AudioChannels=%d", usbvision_device_data[0].AudioChannels); - sscanf(parse,"%d",&usbvision_device_data[0].Radio); - goto2next(parse); - PDEBUG(DBG_PROBE, "Radio=%d", usbvision_device_data[0].Radio); - sscanf(parse,"%d",&usbvision_device_data[0].Tuner); - goto2next(parse); - PDEBUG(DBG_PROBE, "Tuner=%d", usbvision_device_data[0].Tuner); - sscanf(parse,"%d",&usbvision_device_data[0].TunerType); - goto2next(parse); - PDEBUG(DBG_PROBE, "TunerType=%d", usbvision_device_data[0].TunerType); - sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg1); - goto2next(parse); - PDEBUG(DBG_PROBE, "Vin_Reg1=%d", usbvision_device_data[0].Vin_Reg1); - sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg2); - goto2next(parse); - PDEBUG(DBG_PROBE, "Vin_Reg2=%d", usbvision_device_data[0].Vin_Reg2); - sscanf(parse,"%d",&usbvision_device_data[0].X_Offset); - goto2next(parse); - PDEBUG(DBG_PROBE, "X_Offset=%d", usbvision_device_data[0].X_Offset); - sscanf(parse,"%d",&usbvision_device_data[0].Y_Offset); - goto2next(parse); - PDEBUG(DBG_PROBE, "Y_Offset=%d", usbvision_device_data[0].Y_Offset); - sscanf(parse,"%d",&usbvision_device_data[0].Dvi_yuv); - PDEBUG(DBG_PROBE, "Dvi_yuv=%d", usbvision_device_data[0].Dvi_yuv); - - //add to usbvision_table also - usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE; - usbvision_table[0].idVendor=usbvision_device_data[0].idVendor; - usbvision_table[0].idProduct=usbvision_device_data[0].idProduct; - - } -} - - - -/* - * usbvision_init() - * - * This code is run to initialize the driver. - * - */ -static int __init usbvision_init(void) -{ - int errCode; - - PDEBUG(DBG_PROBE, ""); - - PDEBUG(DBG_IOCTL, "IOCTL debugging is enabled"); - PDEBUG(DBG_IO, "IO debugging is enabled"); - PDEBUG(DBG_RIO, "RIO debugging is enabled"); - PDEBUG(DBG_HEADER, "HEADER debugging is enabled"); - PDEBUG(DBG_PROBE, "PROBE debugging is enabled"); - PDEBUG(DBG_IRQ, "IRQ debugging is enabled"); - PDEBUG(DBG_ISOC, "ISOC debugging is enabled"); - PDEBUG(DBG_PARSE, "PARSE debugging is enabled"); - PDEBUG(DBG_SCRATCH, "SCRATCH debugging is enabled"); - PDEBUG(DBG_FUNC, "FUNC debugging is enabled"); - PDEBUG(DBG_I2C, "I2C debugging is enabled"); - - /* disable planar mode support unless compression enabled */ - if (isocMode != ISOC_MODE_COMPRESS ) { - // FIXME : not the right way to set supported flag - usbvision_v4l2_format[6].supported = 0; // V4L2_PIX_FMT_YVU420 - usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P - } - - customdevice_process(); - - errCode = usb_register(&usbvision_driver); - - if (errCode == 0) { - info(DRIVER_DESC " : " DRIVER_VERSION); - PDEBUG(DBG_PROBE, "success"); - } - return errCode; -} - -static void __exit usbvision_exit(void) -{ - PDEBUG(DBG_PROBE, ""); - - usb_deregister(&usbvision_driver); - PDEBUG(DBG_PROBE, "success"); -} - -module_init(usbvision_init); -module_exit(usbvision_exit); - /* * Overrides for Emacs so that we follow Linus's tabbing style. * --------------------------------------------------------------------------- diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index 48afcd2..2de74e4 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -37,16 +37,18 @@ #include #include #include -#include "usbvision-i2c.h" +#include "usbvision.h" -static int debug_i2c_usb = 0; +#define DBG_I2C 1<<0 +#define DBG_ALGO 1<<1 -#if defined(module_param) // Showing parameters under SYSFS -module_param (debug_i2c_usb, int, 0444); // debug_i2c_usb mode of the device driver -#else -MODULE_PARM(debug_i2c_usb, "i"); // debug_i2c_usb mode of the device driver -#endif +static int i2c_debug = 0; + +module_param (i2c_debug, int, 0644); // debug_i2c_usb mode of the device driver +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); +#define PDEBUG(level, fmt, args...) \ + if (i2c_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args) static inline int try_write_address(struct i2c_adapter *i2c_adap, unsigned char addr, int retries) @@ -67,11 +69,9 @@ static inline int try_write_address(struct i2c_adapter *i2c_adap, break; udelay(adap->udelay); } - if (debug_i2c_usb) { - if (i) { - info("%s: Needed %d retries for address %#2x", __FUNCTION__, i, addr); - info("%s: Maybe there's no device at this address", __FUNCTION__); - } + if (i) { + PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr); + PDEBUG(DBG_ALGO,"Maybe there's no device at this address"); } return ret; } @@ -94,11 +94,9 @@ static inline int try_read_address(struct i2c_adapter *i2c_adap, break; udelay(adap->udelay); } - if (debug_i2c_usb) { - if (i) { - info("%s: Needed %d retries for address %#2x", __FUNCTION__, i, addr); - info("%s: Maybe there's no device at this address", __FUNCTION__); - } + if (i) { + PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr); + PDEBUG(DBG_ALGO,"Maybe there's no device at this address"); } return ret; } @@ -166,9 +164,7 @@ usb_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) pmsg = &msgs[i]; ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr); if (ret != 0) { - if (debug_i2c_usb) { - info("%s: got NAK from device, message #%d\n", __FUNCTION__, i); - } + PDEBUG(DBG_ALGO,"got NAK from device, message #%d", i); return (ret < 0) ? ret : -EREMOTEIO; } @@ -215,6 +211,9 @@ static struct i2c_algorithm i2c_usb_algo = { */ int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap) { + PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]"); + PDEBUG(DBG_ALGO, "ALGO debugging is enabled [i2c]"); + /* register new adapter to i2c module... */ adap->algo = &i2c_usb_algo; @@ -222,17 +221,9 @@ int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap) adap->timeout = 100; /* default values, should */ adap->retries = 3; /* be replaced by defines */ -#ifdef MODULE - #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 21) - MOD_INC_USE_COUNT; - #endif -#endif - i2c_add_adapter(adap); - if (debug_i2c_usb) { - info("i2c bus for %s registered", adap->name); - } + PDEBUG(DBG_ALGO,"i2c bus for %s registered", adap->name); return 0; } @@ -243,17 +234,338 @@ int usbvision_i2c_usb_del_bus(struct i2c_adapter *adap) i2c_del_adapter(adap); - if (debug_i2c_usb) { - info("i2c bus for %s unregistered", adap->name); + PDEBUG(DBG_ALGO,"i2c bus for %s unregistered", adap->name); + + return 0; +} + + +/* ----------------------------------------------------------------------- */ +/* usbvision specific I2C functions */ +/* ----------------------------------------------------------------------- */ +static struct i2c_adapter i2c_adap_template; +static struct i2c_algo_usb_data i2c_algo_template; +static struct i2c_client i2c_client_template; + +int usbvision_init_i2c(struct usb_usbvision *usbvision) +{ + memcpy(&usbvision->i2c_adap, &i2c_adap_template, + sizeof(struct i2c_adapter)); + memcpy(&usbvision->i2c_algo, &i2c_algo_template, + sizeof(struct i2c_algo_usb_data)); + memcpy(&usbvision->i2c_client, &i2c_client_template, + sizeof(struct i2c_client)); + + sprintf(usbvision->i2c_adap.name + strlen(usbvision->i2c_adap.name), + " #%d", usbvision->vdev->minor & 0x1f); + PDEBUG(DBG_I2C,"Adaptername: %s", usbvision->i2c_adap.name); + + i2c_set_adapdata(&usbvision->i2c_adap, usbvision); + i2c_set_clientdata(&usbvision->i2c_client, usbvision); + i2c_set_algo_usb_data(&usbvision->i2c_algo, usbvision); + + usbvision->i2c_adap.algo_data = &usbvision->i2c_algo; + usbvision->i2c_client.adapter = &usbvision->i2c_adap; + + if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) { + printk(KERN_ERR "usbvision_init_i2c: can't write reg\n"); + return -EBUSY; + } + +#ifdef CONFIG_MODULES + /* Request the load of the i2c modules we need */ + switch (usbvision_device_data[usbvision->DevModel].Codec) { + case CODEC_SAA7113: + request_module("saa7115"); + break; + case CODEC_SAA7111: + request_module("saa7115"); + break; + } + if (usbvision_device_data[usbvision->DevModel].Tuner == 1) { + request_module("tuner"); } -#ifdef MODULE - #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 21) - MOD_DEC_USE_COUNT; - #endif #endif + return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap); +} + +void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, + void *arg) +{ + BUG_ON(NULL == usbvision->i2c_adap.algo_data); + i2c_clients_command(&usbvision->i2c_adap, cmd, arg); +} + +static int attach_inform(struct i2c_client *client) +{ + struct usb_usbvision *usbvision; + + usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); + + switch (client->addr << 1) { + case 0x43: + case 0x4b: + { + struct tuner_setup tun_setup; + + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = TUNER_TDA9887; + tun_setup.addr = client->addr; + + call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); + + break; + } + case 0x42: + PDEBUG(DBG_I2C,"attach_inform: saa7114 detected."); + break; + case 0x4a: + PDEBUG(DBG_I2C,"attach_inform: saa7113 detected."); + break; + case 0xa0: + PDEBUG(DBG_I2C,"attach_inform: eeprom detected."); + break; + + default: + { + struct tuner_setup tun_setup; + + PDEBUG(DBG_I2C,"attach inform: detected I2C address %x", client->addr << 1); + usbvision->tuner_addr = client->addr; + + if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) { + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = usbvision->tuner_type; + tun_setup.addr = usbvision->tuner_addr; + call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); + } + } + break; + } return 0; } +static int detach_inform(struct i2c_client *client) +{ + struct usb_usbvision *usbvision; + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + usbvision = (struct usb_usbvision *)client->adapter->data; + #else + usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); + #endif + + PDEBUG(DBG_I2C,"usbvision[%d] detaches %s", usbvision->nr, client->name); + return 0; +} + +static int +usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr, + char *buf, short len) +{ + int rc, retries; + + for (retries = 5;;) { + rc = usbvision_write_reg(usbvision, USBVISION_SER_ADRS, addr); + if (rc < 0) + return rc; + + /* Initiate byte read cycle */ + /* USBVISION_SER_CONT <- d0-d2 n. of bytes to r/w */ + /* d3 0=Wr 1=Rd */ + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, + (len & 0x07) | 0x18); + if (rc < 0) + return rc; + + /* Test for Busy and ACK */ + do { + /* USBVISION_SER_CONT -> d4 == 0 busy */ + rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); + } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ + if (rc < 0) + return rc; + + /* USBVISION_SER_CONT -> d5 == 1 Not ack */ + if ((rc & 0x20) == 0) /* Ack? */ + break; + + /* I2C abort */ + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); + if (rc < 0) + return rc; + + if (--retries < 0) + return -1; + } + + switch (len) { + case 4: + buf[3] = usbvision_read_reg(usbvision, USBVISION_SER_DAT4); + case 3: + buf[2] = usbvision_read_reg(usbvision, USBVISION_SER_DAT3); + case 2: + buf[1] = usbvision_read_reg(usbvision, USBVISION_SER_DAT2); + case 1: + buf[0] = usbvision_read_reg(usbvision, USBVISION_SER_DAT1); + break; + default: + printk(KERN_ERR + "usbvision_i2c_read_max4: buffer length > 4\n"); + } + + if (i2c_debug & DBG_I2C) { + int idx; + for (idx = 0; idx < len; idx++) { + PDEBUG(DBG_I2C,"read %x from address %x", (unsigned char)buf[idx], addr); + } + } + return len; +} + + +static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision, + unsigned char addr, const char *buf, + short len) +{ + int rc, retries; + int i; + unsigned char value[6]; + unsigned char ser_cont; + + ser_cont = (len & 0x07) | 0x10; + + value[0] = addr; + value[1] = ser_cont; + for (i = 0; i < len; i++) + value[i + 2] = buf[i]; + + for (retries = 5;;) { + rc = usb_control_msg(usbvision->dev, + usb_sndctrlpipe(usbvision->dev, 1), + USBVISION_OP_CODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_ENDPOINT, 0, + (__u16) USBVISION_SER_ADRS, value, + len + 2, HZ); + + if (rc < 0) + return rc; + + rc = usbvision_write_reg(usbvision, USBVISION_SER_CONT, + (len & 0x07) | 0x10); + if (rc < 0) + return rc; + + /* Test for Busy and ACK */ + do { + rc = usbvision_read_reg(usbvision, USBVISION_SER_CONT); + } while (rc > 0 && ((rc & 0x10) != 0)); /* Retry while busy */ + if (rc < 0) + return rc; + + if ((rc & 0x20) == 0) /* Ack? */ + break; + + /* I2C abort */ + usbvision_write_reg(usbvision, USBVISION_SER_CONT, 0x00); + + if (--retries < 0) + return -1; + + } + + if (i2c_debug & DBG_I2C) { + int idx; + for (idx = 0; idx < len; idx++) { + PDEBUG(DBG_I2C,"wrote %x at address %x", (unsigned char)buf[idx], addr); + } + } + return len; +} + +static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, + short len) +{ + char *bufPtr = buf; + int retval; + int wrcount = 0; + int count; + int maxLen = 4; + struct usb_usbvision *usbvision = (struct usb_usbvision *) data; + + while (len > 0) { + count = (len > maxLen) ? maxLen : len; + retval = usbvision_i2c_write_max4(usbvision, addr, bufPtr, count); + if (retval > 0) { + len -= count; + bufPtr += count; + wrcount += count; + } else + return (retval < 0) ? retval : -EFAULT; + } + return wrcount; +} + +static int usbvision_i2c_read(void *data, unsigned char addr, char *buf, + short len) +{ + char temp[4]; + int retval, i; + int rdcount = 0; + int count; + struct usb_usbvision *usbvision = (struct usb_usbvision *) data; + + while (len > 0) { + count = (len > 3) ? 4 : len; + retval = usbvision_i2c_read_max4(usbvision, addr, temp, count); + if (retval > 0) { + for (i = 0; i < len; i++) + buf[rdcount + i] = temp[i]; + len -= count; + rdcount += count; + } else + return (retval < 0) ? retval : -EFAULT; + } + return rdcount; +} + +static struct i2c_algo_usb_data i2c_algo_template = { + .data = NULL, + .inb = usbvision_i2c_read, + .outb = usbvision_i2c_write, + .udelay = 10, + .mdelay = 10, + .timeout = 100, +}; + +static struct i2c_adapter i2c_adap_template = { + .owner = THIS_MODULE, + .name = "usbvision", + .id = I2C_HW_B_BT848, /* FIXME */ + .algo = NULL, + .algo_data = NULL, + .client_register = attach_inform, + .client_unregister = detach_inform, +#if defined (I2C_ADAP_CLASS_TV_ANALOG) + .class = I2C_ADAP_CLASS_TV_ANALOG, +#elif defined (I2C_CLASS_TV_ANALOG) + .class = I2C_CLASS_TV_ANALOG, +#endif +}; + +static struct i2c_client i2c_client_template = { + .name = "usbvision internal", +}; + EXPORT_SYMBOL(usbvision_i2c_usb_add_bus); EXPORT_SYMBOL(usbvision_i2c_usb_del_bus); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/video/usbvision/usbvision-i2c.h b/drivers/media/video/usbvision/usbvision-i2c.h deleted file mode 100644 index a2df8db..0000000 --- a/drivers/media/video/usbvision/usbvision-i2c.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * I2C_ALGO_USB.H - * i2c algorithm for USB-I2C Bridges - * - * Copyright (c) 1999-2005 Joerg Heckenbach - * - * This module is part of usbvision driver project. - * Updates to driver completed by Dwaine P. Garden - * - * 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 I2C_ALGO_USB_H -#define I2C_ALGO_USB_H 1 - -#include - -struct i2c_algo_usb_data { - void *data; /* private data for lowlevel routines */ - int (*inb) (void *data, unsigned char addr, char *buf, short len); - int (*outb) (void *data, unsigned char addr, char *buf, short len); - - /* local settings */ - int udelay; - int mdelay; - int timeout; -}; - -#define I2C_USB_ADAP_MAX 16 - -int usbvision_i2c_usb_add_bus(struct i2c_adapter *); -int usbvision_i2c_usb_del_bus(struct i2c_adapter *); - -static inline void *i2c_get_algo_usb_data (struct i2c_algo_usb_data *dev) -{ - return dev->data; -} - -static inline void i2c_set_algo_usb_data (struct i2c_algo_usb_data *dev, void *data) -{ - dev->data = data; -} - - -#endif //I2C_ALGO_USB_H diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c new file mode 100644 index 0000000..7e26d83 --- /dev/null +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -0,0 +1,2060 @@ +/* + * USB USBVISION Video device driver 0.9.9 + * + * + * + * Copyright (c) 1999-2005 Joerg Heckenbach + * + * This module is part of usbvision driver project. + * + * 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. + * + * Let's call the version 0.... until compression decoding is completely + * implemented. + * + * This driver is written by Jose Ignacio Gijon and Joerg Heckenbach. + * It was based on USB CPiA driver written by Peter Pregler, + * Scott J. Bertin and Johannes Erdfelt + * Ideas are taken from bttv driver by Ralph Metzler, Marcus Metzler & + * Gerd Knorr and zoran 36120/36125 driver by Pauline Middelink + * Updates to driver completed by Dwaine P. Garden + * + * + * TODO: + * - use submit_urb for all setup packets + * - Fix memory settings for nt1004. It is 4 times as big as the + * nt1003 memory. + * - Add audio on endpoint 3 for nt1004 chip. Seems impossible, needs a codec interface. Which one? + * - Clean up the driver. + * - optimization for performance. + * - Add Videotext capability (VBI). Working on it..... + * - Check audio for other devices + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + #include + #include + +#ifdef CONFIG_KMOD +#include +#endif + +#include "usbvision.h" + +#define DRIVER_AUTHOR "Joerg Heckenbach , Dwaine Garden " +#define DRIVER_NAME "usbvision" +#define DRIVER_ALIAS "USBVision" +#define DRIVER_DESC "USBVision USB Video Device Driver for Linux" +#define DRIVER_LICENSE "GPL" +#define USBVISION_DRIVER_VERSION_MAJOR 0 +#define USBVISION_DRIVER_VERSION_MINOR 9 +#define USBVISION_DRIVER_VERSION_PATCHLEVEL 9 +#define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,USBVISION_DRIVER_VERSION_MINOR,USBVISION_DRIVER_VERSION_PATCHLEVEL) +#define USBVISION_VERSION_STRING __stringify(USBVISION_DRIVER_VERSION_MAJOR) "." __stringify(USBVISION_DRIVER_VERSION_MINOR) "." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL) + +#define ENABLE_HEXDUMP 0 /* Enable if you need it */ + + +#define USBVISION_DEBUG /* Turn on debug messages */ + +#ifdef USBVISION_DEBUG + #define PDEBUG(level, fmt, args...) \ + if (video_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args) +#else + #define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +#define DBG_IOCTL 1<<0 +#define DBG_IO 1<<1 +#define DBG_PROBE 1<<2 +#define DBG_FUNC 1<<3 + +//String operations +#define rmspace(str) while(*str==' ') str++; +#define goto2next(str) while(*str!=' ') str++; while(*str==' ') str++; + + +static int usbvision_nr = 0; // sequential number of usbvision device + +static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { + { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" }, + { 1, 2, 16, V4L2_PIX_FMT_RGB565 , "RGB565" }, + { 1, 3, 24, V4L2_PIX_FMT_RGB24 , "RGB24" }, + { 1, 4, 32, V4L2_PIX_FMT_RGB32 , "RGB32" }, + { 1, 2, 16, V4L2_PIX_FMT_RGB555 , "RGB555" }, + { 1, 2, 16, V4L2_PIX_FMT_YUYV , "YUV422" }, + { 1, 2, 12, V4L2_PIX_FMT_YVU420 , "YUV420P" }, // 1.5 ! + { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" } +}; + +/* supported tv norms */ +static struct usbvision_tvnorm tvnorms[] = { + { + .name = "PAL", + .id = V4L2_STD_PAL, + }, { + .name = "NTSC", + .id = V4L2_STD_NTSC, + }, { + .name = "SECAM", + .id = V4L2_STD_SECAM, + }, { + .name = "PAL-M", + .id = V4L2_STD_PAL_M, + } +}; + +#define TVNORMS ARRAY_SIZE(tvnorms) + +// Function prototypes +static void usbvision_release(struct usb_usbvision *usbvision); + +// Default initalization of device driver parameters +static int isocMode = ISOC_MODE_COMPRESS; // Set the default format for ISOC endpoint +static int video_debug = 0; // Set the default Debug Mode of the device driver +static int PowerOnAtOpen = 1; // Set the default device to power on at startup +static int video_nr = -1; // Sequential Number of Video Device +static int radio_nr = -1; // Sequential Number of Radio Device +static int vbi_nr = -1; // Sequential Number of VBI Device +static char *CustomDevice=NULL; // Set as nothing.... + +// Grab parameters for the device driver + +#if defined(module_param) // Showing parameters under SYSFS +module_param(isocMode, int, 0444); +module_param(video_debug, int, 0444); +module_param(PowerOnAtOpen, int, 0444); +module_param(video_nr, int, 0444); +module_param(radio_nr, int, 0444); +module_param(vbi_nr, int, 0444); +module_param(CustomDevice, charp, 0444); +#else // Old Style +MODULE_PARAM(isocMode, "i"); +MODULE_PARM(video_debug, "i"); // Grab the Debug Mode of the device driver +MODULE_PARM(adjustCompression, "i"); // Grab the compression to be adaptive +MODULE_PARM(PowerOnAtOpen, "i"); // Grab the device to power on at startup +MODULE_PARM(SwitchSVideoInput, "i"); // To help people with Black and White output with using s-video input. Some cables and input device are wired differently. +MODULE_PARM(video_nr, "i"); // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...) +MODULE_PARM(radio_nr, "i"); // radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...) +MODULE_PARM(vbi_nr, "i"); // vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...) +MODULE_PARM(CustomDevice, "s"); // .... CustomDevice +#endif + +MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); +MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)"); +MODULE_PARM_DESC(PowerOnAtOpen, " Set the default device to power on when device is opened. Default: 1 (On)"); +MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); +MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); +MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX). Default: -1 (autodetect)"); +MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the device. Default: null"); + + +// Misc stuff +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE(DRIVER_LICENSE); + MODULE_VERSION(USBVISION_VERSION_STRING); + MODULE_ALIAS(DRIVER_ALIAS); + + +/****************************************************************************************/ +/* SYSFS Code - Copied from the stv680.c usb module. */ +/* Device information is located at /sys/class/video4linux/video0 */ +/* Device parameters information is located at /sys/module/usbvision */ +/* Device USB Information is located at /sys/bus/usb/drivers/USBVision Video Grabber */ +/****************************************************************************************/ + + +#define YES_NO(x) ((x) ? "Yes" : "No") + +static inline struct usb_usbvision *cd_to_usbvision(struct class_device *cd) +{ + struct video_device *vdev = to_video_device(cd); + return video_get_drvdata(vdev); +} + +static ssize_t show_version(struct class_device *cd, char *buf) +{ + return sprintf(buf, "%s\n", USBVISION_VERSION_STRING); +} +static CLASS_DEVICE_ATTR(version, S_IRUGO, show_version, NULL); + +static ssize_t show_model(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", usbvision_device_data[usbvision->DevModel].ModelString); +} +static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL); + +static ssize_t show_hue(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct v4l2_control ctrl; + ctrl.id = V4L2_CID_HUE; + ctrl.value = 0; + call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + return sprintf(buf, "%d\n", ctrl.value >> 8); +} +static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); + +static ssize_t show_contrast(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct v4l2_control ctrl; + ctrl.id = V4L2_CID_CONTRAST; + ctrl.value = 0; + call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + return sprintf(buf, "%d\n", ctrl.value >> 8); +} +static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); + +static ssize_t show_brightness(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct v4l2_control ctrl; + ctrl.id = V4L2_CID_BRIGHTNESS; + ctrl.value = 0; + call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + return sprintf(buf, "%d\n", ctrl.value >> 8); +} +static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); + +static ssize_t show_saturation(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + struct v4l2_control ctrl; + ctrl.id = V4L2_CID_SATURATION; + ctrl.value = 0; + call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + return sprintf(buf, "%d\n", ctrl.value >> 8); +} +static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); + +static ssize_t show_streaming(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", YES_NO(usbvision->streaming==Stream_On?1:0)); +} +static CLASS_DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL); + +static ssize_t show_compression(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%s\n", YES_NO(usbvision->isocMode==ISOC_MODE_COMPRESS)); +} +static CLASS_DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL); + +static ssize_t show_device_bridge(struct class_device *class_dev, char *buf) +{ + struct video_device *vdev = to_video_device(class_dev); + struct usb_usbvision *usbvision = video_get_drvdata(vdev); + return sprintf(buf, "%d\n", usbvision->bridgeType); +} +static CLASS_DEVICE_ATTR(bridge, S_IRUGO, show_device_bridge, NULL); + +static void usbvision_create_sysfs(struct video_device *vdev) +{ + int res; + if (vdev) { + res=video_device_create_file(vdev, &class_device_attr_version); + res=video_device_create_file(vdev, &class_device_attr_model); + res=video_device_create_file(vdev, &class_device_attr_hue); + res=video_device_create_file(vdev, &class_device_attr_contrast); + res=video_device_create_file(vdev, &class_device_attr_brightness); + res=video_device_create_file(vdev, &class_device_attr_saturation); + res=video_device_create_file(vdev, &class_device_attr_streaming); + res=video_device_create_file(vdev, &class_device_attr_compression); + res=video_device_create_file(vdev, &class_device_attr_bridge); + } +} + +static void usbvision_remove_sysfs(struct video_device *vdev) +{ + if (vdev) { + video_device_remove_file(vdev, &class_device_attr_version); + video_device_remove_file(vdev, &class_device_attr_model); + video_device_remove_file(vdev, &class_device_attr_hue); + video_device_remove_file(vdev, &class_device_attr_contrast); + video_device_remove_file(vdev, &class_device_attr_brightness); + video_device_remove_file(vdev, &class_device_attr_saturation); + video_device_remove_file(vdev, &class_device_attr_streaming); + video_device_remove_file(vdev, &class_device_attr_compression); + video_device_remove_file(vdev, &class_device_attr_bridge); + } +} + + +/* + * usbvision_open() + * + * This is part of Video 4 Linux API. The driver can be opened by one + * client only (checks internal counter 'usbvision->user'). The procedure + * then allocates buffers needed for video processing. + * + */ +static int usbvision_v4l2_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + int errCode = 0; + + PDEBUG(DBG_IO, "open"); + + + usbvision_reset_powerOffTimer(usbvision); + + if (usbvision->user) + errCode = -EBUSY; + else { + /* Allocate memory for the frame buffers */ + errCode = usbvision_frames_alloc(usbvision); + if(!errCode) { + /* Allocate memory for the scratch ring buffer */ + errCode = usbvision_scratch_alloc(usbvision); + if(!errCode) { + /* Allocate memory for the USB S buffers */ + errCode = usbvision_sbuf_alloc(usbvision); + if ((!errCode) && (usbvision->isocMode==ISOC_MODE_COMPRESS)) { + /* Allocate intermediate decompression buffers only if needed */ + errCode = usbvision_decompress_alloc(usbvision); + } + } + } + if (errCode) { + /* Deallocate all buffers if trouble */ + usbvision_frames_free(usbvision); + usbvision_scratch_free(usbvision); + usbvision_sbuf_free(usbvision); + usbvision_decompress_free(usbvision); + } + } + + /* If so far no errors then we shall start the camera */ + if (!errCode) { + down(&usbvision->lock); + if (usbvision->power == 0) { + usbvision_power_on(usbvision); + usbvision_init_i2c(usbvision); + } + + /* Send init sequence only once, it's large! */ + if (!usbvision->initialized) { + int setup_ok = 0; + setup_ok = usbvision_setup(usbvision,isocMode); + if (setup_ok) + usbvision->initialized = 1; + else + errCode = -EBUSY; + } + + if (!errCode) { + usbvision_begin_streaming(usbvision); + errCode = usbvision_init_isoc(usbvision); + /* device needs to be initialized before isoc transfer */ + usbvision_muxsel(usbvision,0); + usbvision->user++; + } + else { + if (PowerOnAtOpen) { + usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_power_off(usbvision); + usbvision->initialized = 0; + } + } + up(&usbvision->lock); + } + + if (errCode) { + } + + /* prepare queues */ + usbvision_empty_framequeues(usbvision); + + PDEBUG(DBG_IO, "success"); + return errCode; +} + +/* + * usbvision_v4l2_close() + * + * This is part of Video 4 Linux API. The procedure + * stops streaming and deallocates all buffers that were earlier + * allocated in usbvision_v4l2_open(). + * + */ +static int usbvision_v4l2_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + + PDEBUG(DBG_IO, "close"); + down(&usbvision->lock); + + usbvision_audio_off(usbvision); + usbvision_restart_isoc(usbvision); + usbvision_stop_isoc(usbvision); + + usbvision_decompress_free(usbvision); + usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size); + usbvision_scratch_free(usbvision); + usbvision_sbuf_free(usbvision); + + usbvision->user--; + + if (PowerOnAtOpen) { + /* power off in a little while to avoid off/on every close/open short sequences */ + usbvision_set_powerOffTimer(usbvision); + usbvision->initialized = 0; + } + + up(&usbvision->lock); + + if (usbvision->remove_pending) { + info("%s: Final disconnect", __FUNCTION__); + usbvision_release(usbvision); + } + + PDEBUG(DBG_IO, "success"); + + + return 0; +} + + +/* + * usbvision_ioctl() + * + * This is part of Video 4 Linux API. The procedure handles ioctl() calls. + * + */ +static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EFAULT; + + switch (cmd) { + +#ifdef CONFIG_VIDEO_ADV_DEBUG + /* ioctls to allow direct acces to the NT100x registers */ + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *reg = arg; + int errCode; + + if (reg->i2c_id != 0) + return -EINVAL; + /* NT100x has a 8-bit register space */ + errCode = usbvision_read_reg(usbvision, reg->reg&0xff); + if (errCode < 0) { + err("%s: VIDIOC_INT_G_REGISTER failed: error %d", __FUNCTION__, errCode); + } + else { + reg->val=(unsigned char)errCode; + PDEBUG(DBG_IOCTL, "VIDIOC_INT_G_REGISTER reg=0x%02X, value=0x%02X", + (unsigned int)reg->reg, reg->val); + errCode = 0; // No error + } + return errCode; + } + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *reg = arg; + int errCode; + + if (reg->i2c_id != 0) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val); + if (errCode < 0) { + err("%s: VIDIOC_INT_S_REGISTER failed: error %d", __FUNCTION__, errCode); + } + else { + PDEBUG(DBG_IOCTL, "VIDIOC_INT_S_REGISTER reg=0x%02X, value=0x%02X", + (unsigned int)reg->reg, reg->val); + errCode = 0; + } + return 0; + } +#endif + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *vc=arg; + + memset(vc, 0, sizeof(*vc)); + strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); + strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString, + sizeof(vc->card)); + strlcpy(vc->bus_info, usbvision->dev->dev.bus_id, + sizeof(vc->bus_info)); + vc->version = USBVISION_DRIVER_VERSION; + vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); + PDEBUG(DBG_IOCTL, "VIDIOC_QUERYCAP"); + return 0; + } + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *vi = arg; + int chan; + + if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) ) + return -EINVAL; + if (usbvision->have_tuner) { + chan = vi->index; + } + else { + chan = vi->index + 1; //skip Television string + } + switch(chan) { + case 0: + if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { + strcpy(vi->name, "White Video Input"); + } + else { + strcpy(vi->name, "Television"); + vi->type = V4L2_INPUT_TYPE_TUNER; + vi->audioset = 1; + vi->tuner = chan; + vi->std = V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM; + } + break; + case 1: + vi->type = V4L2_INPUT_TYPE_CAMERA; + if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { + strcpy(vi->name, "Green Video Input"); + } + else { + strcpy(vi->name, "Composite Video Input"); + } + vi->std = V4L2_STD_PAL; + break; + case 2: + vi->type = V4L2_INPUT_TYPE_CAMERA; + if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { + strcpy(vi->name, "Yellow Video Input"); + } + else { + strcpy(vi->name, "S-Video Input"); + } + vi->std = V4L2_STD_PAL; + break; + case 3: + vi->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(vi->name, "Red Video Input"); + vi->std = V4L2_STD_PAL; + break; + } + PDEBUG(DBG_IOCTL, "VIDIOC_ENUMINPUT name=%s:%d tuners=%d type=%d norm=%x", + vi->name, vi->index, vi->tuner,vi->type,(int)vi->std); + return 0; + } + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *e = arg; + unsigned int i; + int ret; + + i = e->index; + if (i >= TVNORMS) + return -EINVAL; + ret = v4l2_video_std_construct(e, tvnorms[e->index].id, + tvnorms[e->index].name); + e->index = i; + if (ret < 0) + return ret; + return 0; + } + case VIDIOC_G_INPUT: + { + int *input = arg; + *input = usbvision->ctl_input; + return 0; + } + case VIDIOC_S_INPUT: + { + int *input = arg; + if ((*input >= usbvision->video_inputs) || (*input < 0) ) + return -EINVAL; + usbvision->ctl_input = *input; + + down(&usbvision->lock); + usbvision_muxsel(usbvision, usbvision->ctl_input); + usbvision_set_input(usbvision); + usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight); + up(&usbvision->lock); + return 0; + } + case VIDIOC_G_STD: + { + v4l2_std_id *id = arg; + + *id = usbvision->tvnorm->id; + + PDEBUG(DBG_IOCTL, "VIDIOC_G_STD std_id=%s", usbvision->tvnorm->name); + return 0; + } + case VIDIOC_S_STD: + { + v4l2_std_id *id = arg; + unsigned int i; + + for (i = 0; i < TVNORMS; i++) + if (*id == tvnorms[i].id) + break; + if (i == TVNORMS) + for (i = 0; i < TVNORMS; i++) + if (*id & tvnorms[i].id) + break; + if (i == TVNORMS) + return -EINVAL; + + down(&usbvision->lock); + usbvision->tvnorm = &tvnorms[i]; + + call_i2c_clients(usbvision, VIDIOC_S_STD, + &usbvision->tvnorm->id); + + up(&usbvision->lock); + + PDEBUG(DBG_IOCTL, "VIDIOC_S_STD std_id=%s", usbvision->tvnorm->name); + return 0; + } + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *vt = arg; + + if (!usbvision->have_tuner || vt->index) // Only tuner 0 + return -EINVAL; + strcpy(vt->name, "Television"); + /* Let clients fill in the remainder of this struct */ + call_i2c_clients(usbvision,VIDIOC_G_TUNER,vt); + + PDEBUG(DBG_IOCTL, "VIDIOC_G_TUNER signal=%x, afc=%x",vt->signal,vt->afc); + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *vt = arg; + + // Only no or one tuner for now + if (!usbvision->have_tuner || vt->index) + return -EINVAL; + /* let clients handle this */ + call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt); + + PDEBUG(DBG_IOCTL, "VIDIOC_S_TUNER"); + return 0; + } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *freq = arg; + + freq->tuner = 0; // Only one tuner + freq->type = V4L2_TUNER_ANALOG_TV; + freq->frequency = usbvision->freq; + PDEBUG(DBG_IOCTL, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)freq->frequency); + return 0; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *freq = arg; + + // Only no or one tuner for now + if (!usbvision->have_tuner || freq->tuner) + return -EINVAL; + + usbvision->freq = freq->frequency; + call_i2c_clients(usbvision, cmd, freq); + PDEBUG(DBG_IOCTL, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)freq->frequency); + return 0; + } + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *v = arg; + memset(v,0, sizeof(v)); + strcpy(v->name, "TV"); + PDEBUG(DBG_IOCTL, "VIDIOC_G_AUDIO"); + return 0; + } + case VIDIOC_S_AUDIO: + { + struct v4l2_audio *v = arg; + if(v->index) { + return -EINVAL; + } + PDEBUG(DBG_IOCTL, "VIDIOC_S_AUDIO"); + return 0; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + int id=ctrl->id; + + memset(ctrl,0,sizeof(*ctrl)); + ctrl->id=id; + + call_i2c_clients(usbvision, cmd, arg); + + if (ctrl->type) + return 0; + else + return -EINVAL; + + PDEBUG(DBG_IOCTL,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type); + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + PDEBUG(DBG_IOCTL,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); + call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); + return 0; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + + PDEBUG(DBG_IOCTL, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value); + call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); + return 0; + } + case VIDIOC_REQBUFS: + { + struct v4l2_requestbuffers *vr = arg; + int ret; + + RESTRICT_TO_RANGE(vr->count,1,USBVISION_NUMFRAMES); + + // Check input validity : the user must do a VIDEO CAPTURE and MMAP method. + if((vr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || + (vr->memory != V4L2_MEMORY_MMAP)) + return -EINVAL; + + if(usbvision->streaming == Stream_On) { + if ((ret = usbvision_stream_interrupt(usbvision))) + return ret; + } + + usbvision_empty_framequeues(usbvision); + + usbvision->curFrame = NULL; + + PDEBUG(DBG_IOCTL, "VIDIOC_REQBUFS count=%d",vr->count); + return 0; + } + case VIDIOC_QUERYBUF: + { + struct v4l2_buffer *vb = arg; + struct usbvision_frame *frame; + + // FIXME : must control that buffers are mapped (VIDIOC_REQBUFS has been called) + + if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { + return -EINVAL; + } + if(vb->index>=USBVISION_NUMFRAMES) { + return -EINVAL; + } + // Updating the corresponding frame state + vb->flags = 0; + frame = &usbvision->frame[vb->index]; + if(frame->grabstate >= FrameState_Ready) + vb->flags |= V4L2_BUF_FLAG_QUEUED; + if(frame->grabstate >= FrameState_Done) + vb->flags |= V4L2_BUF_FLAG_DONE; + if(frame->grabstate == FrameState_Unused) + vb->flags |= V4L2_BUF_FLAG_MAPPED; + vb->memory = V4L2_MEMORY_MMAP; + + vb->m.offset = vb->index*usbvision->max_frame_size; + + vb->memory = V4L2_MEMORY_MMAP; + vb->field = V4L2_FIELD_NONE; + vb->length = usbvision->max_frame_size; + vb->timestamp = usbvision->frame[vb->index].timestamp; + vb->sequence = usbvision->frame[vb->index].sequence; + return 0; + } + case VIDIOC_QBUF: + { + struct v4l2_buffer *vb = arg; + struct usbvision_frame *frame; + unsigned long lock_flags; + + // FIXME : works only on VIDEO_CAPTURE MODE, MMAP. + if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { + return -EINVAL; + } + if(vb->index>=USBVISION_NUMFRAMES) { + return -EINVAL; + } + + frame = &usbvision->frame[vb->index]; + + if (frame->grabstate != FrameState_Unused) { + return -EAGAIN; + } + + /* Mark it as ready and enqueue frame */ + frame->grabstate = FrameState_Ready; + frame->scanstate = ScanState_Scanning; + frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ + + vb->flags &= ~V4L2_BUF_FLAG_DONE; + + /* set v4l2_format index */ + frame->v4l2_format = usbvision->palette; + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + PDEBUG(DBG_IOCTL, "VIDIOC_QBUF frame #%d",vb->index); + return 0; + } + case VIDIOC_DQBUF: + { + struct v4l2_buffer *vb = arg; + int ret; + struct usbvision_frame *f; + unsigned long lock_flags; + + if (vb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (list_empty(&(usbvision->outqueue))) { + if (usbvision->streaming == Stream_Idle) + return -EINVAL; + ret = wait_event_interruptible + (usbvision->wait_frame, + !list_empty(&(usbvision->outqueue))); + if (ret) + return ret; + } + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + f = list_entry(usbvision->outqueue.next, + struct usbvision_frame, frame); + list_del(usbvision->outqueue.next); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + f->grabstate = FrameState_Unused; + + vb->memory = V4L2_MEMORY_MMAP; + vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE; + vb->index = f->index; + vb->sequence = f->sequence; + vb->timestamp = f->timestamp; + vb->field = V4L2_FIELD_NONE; + vb->bytesused = f->scanlength; + + return 0; + } + case VIDIOC_STREAMON: + { + int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; + + usbvision->streaming = Stream_On; + + call_i2c_clients(usbvision,VIDIOC_STREAMON , &b); + + PDEBUG(DBG_IOCTL, "VIDIOC_STREAMON"); + + return 0; + } + case VIDIOC_STREAMOFF: + { + int *type = arg; + int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if(usbvision->streaming == Stream_On) { + usbvision_stream_interrupt(usbvision); + // Stop all video streamings + call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b); + } + usbvision_empty_framequeues(usbvision); + + PDEBUG(DBG_IOCTL, "VIDIOC_STREAMOFF"); + return 0; + } + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *vfd = arg; + + if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) { + return -EINVAL; + } + vfd->flags = 0; + vfd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc); + vfd->pixelformat = usbvision_v4l2_format[vfd->index].format; + memset(vfd->reserved, 0, sizeof(vfd->reserved)); + return 0; + } + case VIDIOC_G_FMT: + { + struct v4l2_format *vf = arg; + + switch (vf->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + { + vf->fmt.pix.width = usbvision->curwidth; + vf->fmt.pix.height = usbvision->curheight; + vf->fmt.pix.pixelformat = usbvision->palette.format; + vf->fmt.pix.bytesperline = usbvision->curwidth*usbvision->palette.bytes_per_pixel; + vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*usbvision->curheight; + vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ + } + return 0; + default: + PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT invalid type %d",vf->type); + return -EINVAL; + } + PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT w=%d, h=%d",vf->fmt.win.w.width, vf->fmt.win.w.height); + return 0; + } + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: + { + struct v4l2_format *vf = arg; + int formatIdx,ret; + + switch(vf->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + { + /* Find requested format in available ones */ + for(formatIdx=0;formatIdxfmt.pix.pixelformat == usbvision_v4l2_format[formatIdx].format) { + usbvision->palette = usbvision_v4l2_format[formatIdx]; + break; + } + } + /* robustness */ + if(formatIdx == USBVISION_SUPPORTED_PALETTES) { + return -EINVAL; + } + RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); + RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); + + /* stop io in case it is already in progress */ + if(usbvision->streaming == Stream_On) { + if ((ret = usbvision_stream_interrupt(usbvision))) + return ret; + } + usbvision_empty_framequeues(usbvision); + + usbvision->curFrame = NULL; + + // by now we are committed to the new data... + down(&usbvision->lock); + usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height); + up(&usbvision->lock); + + PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT grabdisplay w=%d, h=%d, format=%s", + vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc); + return 0; + } + default: + return -EINVAL; + } + } + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int usbvision_v4l2_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, usbvision_v4l2_do_ioctl); +} + + +static ssize_t usbvision_v4l2_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + int noblock = file->f_flags & O_NONBLOCK; + unsigned long lock_flags; + + int frmx = -1; + int ret,i; + struct usbvision_frame *frame; + + PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __FUNCTION__, (unsigned long)count, noblock); + + if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL)) + return -EFAULT; + + /* no stream is running, make it running ! */ + usbvision->streaming = Stream_On; + call_i2c_clients(usbvision,VIDIOC_STREAMON , NULL); + + /* First, enqueue as many frames as possible (like a user of VIDIOC_QBUF would do) */ + for(i=0;iframe[i]; + if(frame->grabstate == FrameState_Unused) { + /* Mark it as ready and enqueue frame */ + frame->grabstate = FrameState_Ready; + frame->scanstate = ScanState_Scanning; + frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ + + /* set v4l2_format index */ + frame->v4l2_format = usbvision->palette; + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_add_tail(&frame->frame, &usbvision->inqueue); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + } + } + + /* Then try to steal a frame (like a VIDIOC_DQBUF would do) */ + if (list_empty(&(usbvision->outqueue))) { + if(noblock) + return -EAGAIN; + + ret = wait_event_interruptible + (usbvision->wait_frame, + !list_empty(&(usbvision->outqueue))); + if (ret) + return ret; + } + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + frame = list_entry(usbvision->outqueue.next, + struct usbvision_frame, frame); + list_del(usbvision->outqueue.next); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + /* An error returns an empty frame */ + if (frame->grabstate == FrameState_Error) { + frame->bytes_read = 0; + return 0; + } + + PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", __FUNCTION__, + frame->index, frame->bytes_read, frame->scanlength); + + /* copy bytes to user space; we allow for partials reads */ + if ((count + frame->bytes_read) > (unsigned long)frame->scanlength) + count = frame->scanlength - frame->bytes_read; + + if (copy_to_user(buf, frame->data + frame->bytes_read, count)) { + return -EFAULT; + } + + frame->bytes_read += count; + PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", __FUNCTION__, + (unsigned long)count, frame->bytes_read); + + // For now, forget the frame if it has not been read in one shot. +/* if (frame->bytes_read >= frame->scanlength) {// All data has been read */ + frame->bytes_read = 0; + + /* Mark it as available to be used again. */ + usbvision->frame[frmx].grabstate = FrameState_Unused; +/* } */ + + return count; +} + +static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long size = vma->vm_end - vma->vm_start, + start = vma->vm_start; + void *pos; + u32 i; + + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + + down(&usbvision->lock); + + if (!USBVISION_IS_OPERATIONAL(usbvision)) { + up(&usbvision->lock); + return -EFAULT; + } + + if (!(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(usbvision->max_frame_size)) { + up(&usbvision->lock); + return -EINVAL; + } + + for (i = 0; i < USBVISION_NUMFRAMES; i++) { + if (((usbvision->max_frame_size*i) >> PAGE_SHIFT) == vma->vm_pgoff) + break; + } + if (i == USBVISION_NUMFRAMES) { + PDEBUG(DBG_FUNC, "mmap: user supplied mapping address is out of range"); + up(&usbvision->lock); + return -EINVAL; + } + + /* VM_IO is eventually going to replace PageReserved altogether */ + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */ + + pos = usbvision->frame[i].data; + while (size > 0) { + + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + PDEBUG(DBG_FUNC, "mmap: vm_insert_page failed"); + up(&usbvision->lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + up(&usbvision->lock); + return 0; +} + + +/* + * Here comes the stuff for radio on usbvision based devices + * + */ +static int usbvision_radio_open(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + struct v4l2_frequency freq; + int errCode = 0; + + PDEBUG(DBG_IO, "%s:", __FUNCTION__); + + down(&usbvision->lock); + + if (usbvision->user) { + err("%s: Someone tried to open an already opened USBVision Radio!", __FUNCTION__); + errCode = -EBUSY; + } + else { + if(PowerOnAtOpen) { + usbvision_reset_powerOffTimer(usbvision); + if (usbvision->power == 0) { + usbvision_power_on(usbvision); + usbvision_init_i2c(usbvision); + } + } + + // If so far no errors then we shall start the radio + usbvision->radio = 1; + call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type); + freq.frequency = 1517; //SWR3 @ 94.8MHz + call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, &freq); + usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); + usbvision->user++; + } + + if (errCode) { + if (PowerOnAtOpen) { + usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_power_off(usbvision); + usbvision->initialized = 0; + } + } + up(&usbvision->lock); + return errCode; +} + + +static int usbvision_radio_close(struct inode *inode, struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + int errCode = 0; + + PDEBUG(DBG_IO, ""); + + down(&usbvision->lock); + + usbvision_audio_off(usbvision); + usbvision->radio=0; + usbvision->user--; + + if (PowerOnAtOpen) { + usbvision_set_powerOffTimer(usbvision); + usbvision->initialized = 0; + } + + up(&usbvision->lock); + + if (usbvision->remove_pending) { + info("%s: Final disconnect", __FUNCTION__); + usbvision_release(usbvision); + } + + + PDEBUG(DBG_IO, "success"); + + return errCode; +} + +static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + + if (!USBVISION_IS_OPERATIONAL(usbvision)) + return -EIO; + + switch (cmd) { + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *vc=arg; + + memset(vc, 0, sizeof(*vc)); + strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); + strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString, + sizeof(vc->card)); + strlcpy(vc->bus_info, usbvision->dev->dev.bus_id, + sizeof(vc->bus_info)); + vc->version = USBVISION_DRIVER_VERSION; + vc->capabilities = (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); + PDEBUG(DBG_IO, "VIDIOC_QUERYCAP"); + return 0; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + int id=ctrl->id; + + memset(ctrl,0,sizeof(*ctrl)); + ctrl->id=id; + + call_i2c_clients(usbvision, cmd, arg); + PDEBUG(DBG_IO,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type); + + if (ctrl->type) + return 0; + else + return -EINVAL; + + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + + call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); + PDEBUG(DBG_IO,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); + return 0; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + + call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); + PDEBUG(DBG_IO, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value); + return 0; + } + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *t = arg; + + if (t->index > 0) + return -EINVAL; + + memset(t,0,sizeof(*t)); + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + /* Let clients fill in the remainder of this struct */ + call_i2c_clients(usbvision,VIDIOC_G_TUNER,t); + PDEBUG(DBG_IO, "VIDIOC_G_TUNER signal=%x, afc=%x",t->signal,t->afc); + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *vt = arg; + + // Only no or one tuner for now + if (!usbvision->have_tuner || vt->index) + return -EINVAL; + /* let clients handle this */ + call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt); + + PDEBUG(DBG_IO, "VIDIOC_S_TUNER"); + return 0; + } + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *a = arg; + + memset(a,0,sizeof(*a)); + strcpy(a->name,"Radio"); + PDEBUG(DBG_IO, "VIDIOC_G_AUDIO"); + return 0; + } + case VIDIOC_S_AUDIO: + case VIDIOC_S_INPUT: + case VIDIOC_S_STD: + return 0; + + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + memset(f,0,sizeof(*f)); + + f->type = V4L2_TUNER_RADIO; + f->frequency = usbvision->freq; + call_i2c_clients(usbvision, cmd, f); + PDEBUG(DBG_IO, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)f->frequency); + + return 0; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + if (f->tuner != 0) + return -EINVAL; + usbvision->freq = f->frequency; + call_i2c_clients(usbvision, cmd, f); + PDEBUG(DBG_IO, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)f->frequency); + + return 0; + } + default: + { + PDEBUG(DBG_IO, "%s: Unknown command %x", __FUNCTION__, cmd); + return -ENOIOCTLCMD; + } + } + return 0; +} + + +static int usbvision_radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, usbvision_do_radio_ioctl); +} + + +/* + * Here comes the stuff for vbi on usbvision based devices + * + */ +static int usbvision_vbi_open(struct inode *inode, struct file *file) +{ + /* TODO */ + return -EINVAL; + +} + +static int usbvision_vbi_close(struct inode *inode, struct file *file) +{ + /* TODO */ + return -EINVAL; +} + +static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + /* TODO */ + return -EINVAL; +} + +static int usbvision_vbi_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, usbvision_do_vbi_ioctl); +} + + +// +// Video registration stuff +// + +// Video template +static struct file_operations usbvision_fops = { + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .open = usbvision_v4l2_open, + .release = usbvision_v4l2_close, + .read = usbvision_v4l2_read, + .mmap = usbvision_v4l2_mmap, + .ioctl = usbvision_v4l2_ioctl, + .llseek = no_llseek, +}; +static struct video_device usbvision_video_template = { + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .type = VID_TYPE_TUNER | VID_TYPE_CAPTURE, + .hardware = VID_HARDWARE_USBVISION, + .fops = &usbvision_fops, + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + .name = "usbvision-video", + .release = video_device_release, + #endif + .minor = -1, +}; + + +// Radio template +static struct file_operations usbvision_radio_fops = { + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .open = usbvision_radio_open, + .release = usbvision_radio_close, + .ioctl = usbvision_radio_ioctl, + .llseek = no_llseek, +}; + +static struct video_device usbvision_radio_template= +{ + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_USBVISION, + .fops = &usbvision_radio_fops, + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + .release = video_device_release, + .name = "usbvision-radio", + #endif + .minor = -1, +}; + + +// vbi template +static struct file_operations usbvision_vbi_fops = { + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .open = usbvision_vbi_open, + .release = usbvision_vbi_close, + .ioctl = usbvision_vbi_ioctl, + .llseek = no_llseek, +}; + +static struct video_device usbvision_vbi_template= +{ + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) + .owner = THIS_MODULE, + #endif + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_USBVISION, + .fops = &usbvision_vbi_fops, + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + .release = video_device_release, + .name = "usbvision-vbi", + #endif + .minor = -1, +}; + + +static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, + struct video_device *vdev_template, + char *name) +{ + struct usb_device *usb_dev = usbvision->dev; + struct video_device *vdev; + + if (usb_dev == NULL) { + err("%s: usbvision->dev is not set", __FUNCTION__); + return NULL; + } + + vdev = video_device_alloc(); + if (NULL == vdev) { + return NULL; + } + *vdev = *vdev_template; +// vdev->minor = -1; + vdev->dev = &usb_dev->dev; + snprintf(vdev->name, sizeof(vdev->name), "%s", name); + video_set_drvdata(vdev, usbvision); + return vdev; +} + +// unregister video4linux devices +static void usbvision_unregister_video(struct usb_usbvision *usbvision) +{ + // vbi Device: + if (usbvision->vbi) { + PDEBUG(DBG_PROBE, "unregister /dev/vbi%d [v4l2]", usbvision->vbi->minor & 0x1f); + if (usbvision->vbi->minor != -1) { + video_unregister_device(usbvision->vbi); + } + else { + video_device_release(usbvision->vbi); + } + usbvision->vbi = NULL; + } + + // Radio Device: + if (usbvision->rdev) { + PDEBUG(DBG_PROBE, "unregister /dev/radio%d [v4l2]", usbvision->rdev->minor & 0x1f); + if (usbvision->rdev->minor != -1) { + video_unregister_device(usbvision->rdev); + } + else { + video_device_release(usbvision->rdev); + } + usbvision->rdev = NULL; + } + + // Video Device: + if (usbvision->vdev) { + PDEBUG(DBG_PROBE, "unregister /dev/video%d [v4l2]", usbvision->vdev->minor & 0x1f); + if (usbvision->vdev->minor != -1) { + video_unregister_device(usbvision->vdev); + } + else { + video_device_release(usbvision->vdev); + } + usbvision->vdev = NULL; + } +} + +// register video4linux devices +static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) +{ + // Video Device: + usbvision->vdev = usbvision_vdev_init(usbvision, &usbvision_video_template, "USBVision Video"); + if (usbvision->vdev == NULL) { + goto err_exit; + } + if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr)<0) { + goto err_exit; + } + info("USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2]", usbvision->nr,usbvision->vdev->minor & 0x1f); + + // Radio Device: + if (usbvision_device_data[usbvision->DevModel].Radio) { + // usbvision has radio + usbvision->rdev = usbvision_vdev_init(usbvision, &usbvision_radio_template, "USBVision Radio"); + if (usbvision->rdev == NULL) { + goto err_exit; + } + if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr)<0) { + goto err_exit; + } + info("USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]", usbvision->nr, usbvision->rdev->minor & 0x1f); + } + // vbi Device: + if (usbvision_device_data[usbvision->DevModel].vbi) { + usbvision->vbi = usbvision_vdev_init(usbvision, &usbvision_vbi_template, "USBVision VBI"); + if (usbvision->vdev == NULL) { + goto err_exit; + } + if (video_register_device(usbvision->vbi, VFL_TYPE_VBI, vbi_nr)<0) { + goto err_exit; + } + info("USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!)", usbvision->nr,usbvision->vbi->minor & 0x1f); + } + // all done + return 0; + + err_exit: + err("USBVision[%d]: video_register_device() failed", usbvision->nr); + usbvision_unregister_video(usbvision); + return -1; +} + +/* + * usbvision_alloc() + * + * This code allocates the struct usb_usbvision. It is filled with default values. + * + * Returns NULL on error, a pointer to usb_usbvision else. + * + */ +static struct usb_usbvision *usbvision_alloc(struct usb_device *dev) +{ + struct usb_usbvision *usbvision; + + if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) == NULL) { + goto err_exit; + } + + usbvision->dev = dev; + + init_MUTEX(&usbvision->lock); /* to 1 == available */ + + // prepare control urb for control messages during interrupts + usbvision->ctrlUrb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); + if (usbvision->ctrlUrb == NULL) { + goto err_exit; + } + init_waitqueue_head(&usbvision->ctrlUrb_wq); + init_MUTEX(&usbvision->ctrlUrbLock); /* to 1 == available */ + + usbvision_init_powerOffTimer(usbvision); + + return usbvision; + +err_exit: + if (usbvision && usbvision->ctrlUrb) { + usb_free_urb(usbvision->ctrlUrb); + } + if (usbvision) { + kfree(usbvision); + } + return NULL; +} + +/* + * usbvision_release() + * + * This code does final release of struct usb_usbvision. This happens + * after the device is disconnected -and- all clients closed their files. + * + */ +static void usbvision_release(struct usb_usbvision *usbvision) +{ + PDEBUG(DBG_PROBE, ""); + + down(&usbvision->lock); + + usbvision_reset_powerOffTimer(usbvision); + + usbvision->initialized = 0; + + up(&usbvision->lock); + + usbvision_remove_sysfs(usbvision->vdev); + usbvision_unregister_video(usbvision); + + if (usbvision->ctrlUrb) { + usb_free_urb(usbvision->ctrlUrb); + } + + kfree(usbvision); + + PDEBUG(DBG_PROBE, "success"); +} + + +/******************************** usb interface *****************************************/ + +static void usbvision_configure_video(struct usb_usbvision *usbvision) +{ + int model,i; + + if (usbvision == NULL) + return; + + model = usbvision->DevModel; + usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24; + + if (usbvision_device_data[usbvision->DevModel].Vin_Reg2 >= 0) { + usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2 & 0xff; + } else { + usbvision->Vin_Reg2_Preset = 0; + } + + for (i = 0; i < TVNORMS; i++) + if (usbvision_device_data[model].VideoNorm == tvnorms[i].mode) + break; + if (i == TVNORMS) + i = 0; + usbvision->tvnorm = &tvnorms[i]; /* set default norm */ + + usbvision->video_inputs = usbvision_device_data[model].VideoChannels; + usbvision->ctl_input = 0; + + /* This should be here to make i2c clients to be able to register */ + usbvision_audio_off(usbvision); //first switch off audio + if (!PowerOnAtOpen) { + usbvision_power_on(usbvision); //and then power up the noisy tuner + usbvision_init_i2c(usbvision); + } +} + +/* + * usbvision_probe() + * + * This procedure queries device descriptor and accepts the interface + * if it looks like USBVISION video device + * + */ +static int __devinit usbvision_probe(struct usb_interface *intf, const struct usb_device_id *devid) +{ + struct usb_device *dev = interface_to_usbdev(intf); + __u8 ifnum = intf->altsetting->desc.bInterfaceNumber; + const struct usb_host_interface *interface; + struct usb_usbvision *usbvision = NULL; + const struct usb_endpoint_descriptor *endpoint; + int model; + + PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u", + dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); + /* Is it an USBVISION video dev? */ + model = 0; + for(model = 0; usbvision_device_data[model].idVendor; model++) { + if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) { + continue; + } + if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) { + continue; + } + + info("%s: %s found", __FUNCTION__, usbvision_device_data[model].ModelString); + break; + } + + if (usbvision_device_data[model].idVendor == 0) { + return -ENODEV; //no matching device + } + if (usbvision_device_data[model].Interface >= 0) { + interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0]; + } + else { + interface = &dev->actconfig->interface[ifnum]->altsetting[0]; + } + endpoint = &interface->endpoint[1].desc; + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) { + err("%s: interface %d. has non-ISO endpoint!", __FUNCTION__, ifnum); + err("%s: Endpoint attribures %d", __FUNCTION__, endpoint->bmAttributes); + return -ENODEV; + } + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { + err("%s: interface %d. has ISO OUT endpoint!", __FUNCTION__, ifnum); + return -ENODEV; + } + + usb_get_dev(dev); + + if ((usbvision = usbvision_alloc(dev)) == NULL) { + err("%s: couldn't allocate USBVision struct", __FUNCTION__); + return -ENOMEM; + } + if (dev->descriptor.bNumConfigurations > 1) { + usbvision->bridgeType = BRIDGE_NT1004; + } + else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") { + usbvision->bridgeType = BRIDGE_NT1005; + } + else { + usbvision->bridgeType = BRIDGE_NT1003; + } + PDEBUG(DBG_PROBE, "bridgeType %d", usbvision->bridgeType); + + down(&usbvision->lock); + + usbvision->nr = usbvision_nr++; + + usbvision->have_tuner = usbvision_device_data[model].Tuner; + if (usbvision->have_tuner) { + usbvision->tuner_type = usbvision_device_data[model].TunerType; + } + + usbvision->tuner_addr = ADDR_UNSET; + + usbvision->DevModel = model; + usbvision->remove_pending = 0; + usbvision->iface = ifnum; + usbvision->ifaceAltInactive = 0; + usbvision->ifaceAltActive = 1; + usbvision->video_endp = endpoint->bEndpointAddress; + usbvision->isocPacketSize = 0; + usbvision->usb_bandwidth = 0; + usbvision->user = 0; + usbvision->streaming = Stream_Off; + usbvision_register_video(usbvision); + usbvision_configure_video(usbvision); + up(&usbvision->lock); + + + usb_set_intfdata (intf, usbvision); + usbvision_create_sysfs(usbvision->vdev); + + PDEBUG(DBG_PROBE, "success"); + return 0; +} + + +/* + * usbvision_disconnect() + * + * This procedure stops all driver activity, deallocates interface-private + * structure (pointed by 'ptr') and after that driver should be removable + * with no ill consequences. + * + */ +static void __devexit usbvision_disconnect(struct usb_interface *intf) +{ + struct usb_usbvision *usbvision = usb_get_intfdata(intf); + + PDEBUG(DBG_PROBE, ""); + + if (usbvision == NULL) { + err("%s: usb_get_intfdata() failed", __FUNCTION__); + return; + } + usb_set_intfdata (intf, NULL); + + down(&usbvision->lock); + + // At this time we ask to cancel outstanding URBs + usbvision_stop_isoc(usbvision); + + if (usbvision->power) { + usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_power_off(usbvision); + } + usbvision->remove_pending = 1; // Now all ISO data will be ignored + + usb_put_dev(usbvision->dev); + usbvision->dev = NULL; // USB device is no more + + up(&usbvision->lock); + + if (usbvision->user) { + info("%s: In use, disconnect pending", __FUNCTION__); + wake_up_interruptible(&usbvision->wait_frame); + wake_up_interruptible(&usbvision->wait_stream); + } + else { + usbvision_release(usbvision); + } + + PDEBUG(DBG_PROBE, "success"); + +} + +static struct usb_driver usbvision_driver = { + .name = "usbvision", + .id_table = usbvision_table, + .probe = usbvision_probe, + .disconnect = usbvision_disconnect +}; + +/* + * customdevice_process() + * + * This procedure preprocesses CustomDevice parameter if any + * + */ +void customdevice_process(void) +{ + usbvision_device_data[0]=usbvision_device_data[1]; + usbvision_table[0]=usbvision_table[1]; + + if(CustomDevice) + { + char *parse=CustomDevice; + + PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice); + + /*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1" + usbvision_device_data[0].idVendor; + usbvision_device_data[0].idProduct; + usbvision_device_data[0].Interface; + usbvision_device_data[0].Codec; + usbvision_device_data[0].VideoChannels; + usbvision_device_data[0].VideoNorm; + usbvision_device_data[0].AudioChannels; + usbvision_device_data[0].Radio; + usbvision_device_data[0].Tuner; + usbvision_device_data[0].TunerType; + usbvision_device_data[0].Vin_Reg1; + usbvision_device_data[0].Vin_Reg2; + usbvision_device_data[0].X_Offset; + usbvision_device_data[0].Y_Offset; + usbvision_device_data[0].Dvi_yuv; + usbvision_device_data[0].ModelString; + */ + + rmspace(parse); + usbvision_device_data[0].ModelString="USBVISION Custom Device"; + + parse+=2; + sscanf(parse,"%x",&usbvision_device_data[0].idVendor); + goto2next(parse); + PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_device_data[0].idVendor); + parse+=2; + sscanf(parse,"%x",&usbvision_device_data[0].idProduct); + goto2next(parse); + PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_device_data[0].idProduct); + sscanf(parse,"%d",&usbvision_device_data[0].Interface); + goto2next(parse); + PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface); + sscanf(parse,"%d",&usbvision_device_data[0].Codec); + goto2next(parse); + PDEBUG(DBG_PROBE, "Codec=%d", usbvision_device_data[0].Codec); + sscanf(parse,"%d",&usbvision_device_data[0].VideoChannels); + goto2next(parse); + PDEBUG(DBG_PROBE, "VideoChannels=%d", usbvision_device_data[0].VideoChannels); + + switch(*parse) + { + case 'P': + PDEBUG(DBG_PROBE, "VideoNorm=PAL"); + usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL; + break; + + case 'S': + PDEBUG(DBG_PROBE, "VideoNorm=SECAM"); + usbvision_device_data[0].VideoNorm=VIDEO_MODE_SECAM; + break; + + case 'N': + PDEBUG(DBG_PROBE, "VideoNorm=NTSC"); + usbvision_device_data[0].VideoNorm=VIDEO_MODE_NTSC; + break; + + default: + PDEBUG(DBG_PROBE, "VideoNorm=PAL (by default)"); + usbvision_device_data[0].VideoNorm=VIDEO_MODE_PAL; + break; + } + goto2next(parse); + + sscanf(parse,"%d",&usbvision_device_data[0].AudioChannels); + goto2next(parse); + PDEBUG(DBG_PROBE, "AudioChannels=%d", usbvision_device_data[0].AudioChannels); + sscanf(parse,"%d",&usbvision_device_data[0].Radio); + goto2next(parse); + PDEBUG(DBG_PROBE, "Radio=%d", usbvision_device_data[0].Radio); + sscanf(parse,"%d",&usbvision_device_data[0].Tuner); + goto2next(parse); + PDEBUG(DBG_PROBE, "Tuner=%d", usbvision_device_data[0].Tuner); + sscanf(parse,"%d",&usbvision_device_data[0].TunerType); + goto2next(parse); + PDEBUG(DBG_PROBE, "TunerType=%d", usbvision_device_data[0].TunerType); + sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg1); + goto2next(parse); + PDEBUG(DBG_PROBE, "Vin_Reg1=%d", usbvision_device_data[0].Vin_Reg1); + sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg2); + goto2next(parse); + PDEBUG(DBG_PROBE, "Vin_Reg2=%d", usbvision_device_data[0].Vin_Reg2); + sscanf(parse,"%d",&usbvision_device_data[0].X_Offset); + goto2next(parse); + PDEBUG(DBG_PROBE, "X_Offset=%d", usbvision_device_data[0].X_Offset); + sscanf(parse,"%d",&usbvision_device_data[0].Y_Offset); + goto2next(parse); + PDEBUG(DBG_PROBE, "Y_Offset=%d", usbvision_device_data[0].Y_Offset); + sscanf(parse,"%d",&usbvision_device_data[0].Dvi_yuv); + PDEBUG(DBG_PROBE, "Dvi_yuv=%d", usbvision_device_data[0].Dvi_yuv); + + //add to usbvision_table also + usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE; + usbvision_table[0].idVendor=usbvision_device_data[0].idVendor; + usbvision_table[0].idProduct=usbvision_device_data[0].idProduct; + + } +} + + + +/* + * usbvision_init() + * + * This code is run to initialize the driver. + * + */ +static int __init usbvision_init(void) +{ + int errCode; + + PDEBUG(DBG_PROBE, ""); + + PDEBUG(DBG_IOCTL, "IOCTL debugging is enabled [video]"); + PDEBUG(DBG_IO, "IO debugging is enabled [video]"); + PDEBUG(DBG_PROBE, "PROBE debugging is enabled [video]"); + PDEBUG(DBG_FUNC, "FUNC debugging is enabled [video]"); + + /* disable planar mode support unless compression enabled */ + if (isocMode != ISOC_MODE_COMPRESS ) { + // FIXME : not the right way to set supported flag + usbvision_v4l2_format[6].supported = 0; // V4L2_PIX_FMT_YVU420 + usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P + } + + customdevice_process(); + + errCode = usb_register(&usbvision_driver); + + if (errCode == 0) { + info(DRIVER_DESC " : " USBVISION_VERSION_STRING); + PDEBUG(DBG_PROBE, "success"); + } + return errCode; +} + +static void __exit usbvision_exit(void) +{ + PDEBUG(DBG_PROBE, ""); + + usb_deregister(&usbvision_driver); + PDEBUG(DBG_PROBE, "success"); +} + +module_init(usbvision_init); +module_exit(usbvision_exit); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index f304e66..f2eeda8 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -28,7 +28,9 @@ #include #include -#include "usbvision-i2c.h" +#include +#include +#include #ifndef VID_HARDWARE_USBVISION #define VID_HARDWARE_USBVISION 34 /* USBVision Video Grabber */ @@ -139,11 +141,21 @@ #define USBVISION_MAX_ISOC_PACKET_SIZE 959 // NT1003 Specs Document says 1023 #define USBVISION_NUM_HEADERMARKER 20 -#define USBVISION_NUMFRAMES 3 -#define USBVISION_NUMSBUF 2 +#define USBVISION_NUMFRAMES 3 /* Maximum number of frames an application can get */ +#define USBVISION_NUMSBUF 2 /* Dimensioning the USB S buffering */ #define USBVISION_POWEROFF_TIME 3 * (HZ) // 3 seconds + +#define FRAMERATE_MIN 0 +#define FRAMERATE_MAX 31 + +enum { + ISOC_MODE_YUV422 = 0x03, + ISOC_MODE_YUV420 = 0x14, + ISOC_MODE_COMPRESS = 0x60, +}; + /* This macro restricts an int variable to an inclusive range */ #define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } @@ -181,8 +193,6 @@ mr = LIMIT_RGB(mm_r); \ } - - /* Debugging aid */ #define USBVISION_SAY_AND_WAIT(what) { \ wait_queue_head_t wq; \ @@ -202,7 +212,23 @@ ((udevice)->last_error == 0) && \ (!(udevice)->remove_pending)) +/* I2C structures */ +struct i2c_algo_usb_data { + void *data; /* private data for lowlevel routines */ + int (*inb) (void *data, unsigned char addr, char *buf, short len); + int (*outb) (void *data, unsigned char addr, char *buf, short len); + + /* local settings */ + int udelay; + int mdelay; + int timeout; +}; +#define I2C_USB_ADAP_MAX 16 + +/* ----------------------------------------------------------------- */ +/* usbvision video structures */ +/* ----------------------------------------------------------------- */ enum ScanState { ScanState_Scanning, /* Scanning for header */ ScanState_Lines /* Parsing lines */ @@ -347,7 +373,6 @@ struct usb_usbvision { struct video_device *vdev; /* Video Device */ struct video_device *rdev; /* Radio Device */ struct video_device *vbi; /* VBI Device */ - struct video_audio audio_dev; /* Current audio params */ /* i2c Declaration Section*/ struct i2c_adapter i2c_adap; @@ -361,6 +386,7 @@ struct usb_usbvision { wait_queue_head_t ctrlUrb_wq; // Processes waiting struct semaphore ctrlUrbLock; + /* configuration part */ int have_tuner; int tuner_type; int tuner_addr; @@ -372,7 +398,7 @@ struct usb_usbvision { int AudioMute; int AudioChannel; int isocMode; // format of video data for the usb isoc-transfer - unsigned int nr; // Number of the device < MAX_USBVISION + unsigned int nr; // Number of the device /* Device structure */ struct usb_device *dev; @@ -384,7 +410,6 @@ struct usb_usbvision { struct work_struct powerOffWork; int power; /* is the device powered on? */ int user; /* user count for exclusive use */ - int usbvision_used; /* Is this structure in use? */ int initialized; /* Had we already sent init sequence? */ int DevModel; /* What type of USBVISION device we got? */ enum StreamState streaming; /* Are we streaming Isochronous? */ @@ -402,7 +427,6 @@ struct usb_usbvision { wait_queue_head_t wait_stream; /* Processes waiting */ struct usbvision_frame *curFrame; // pointer to current frame, set by usbvision_find_header struct usbvision_frame frame[USBVISION_NUMFRAMES]; // frame buffer - int curSbufNum; // number of current receiving sbuf struct usbvision_sbuf sbuf[USBVISION_NUMSBUF]; // S buffering volatile int remove_pending; /* If set then about to exit */ @@ -413,13 +437,7 @@ struct usb_usbvision { int scratch_headermarker[USBVISION_NUM_HEADERMARKER]; int scratch_headermarker_read_ptr; int scratch_headermarker_write_ptr; - int isocstate; - /* color controls */ - int saturation; - int hue; - int brightness; - int contrast; - int depth; + enum IsocState isocstate; struct usbvision_v4l2_format_st palette; struct v4l2_capability vcap; /* Video capabilities */ @@ -427,17 +445,6 @@ struct usb_usbvision { struct usbvision_tvnorm *tvnorm; /* selected tv norm */ unsigned char video_endp; /* 0x82 for USBVISION devices based */ - // Overlay stuff: - struct v4l2_framebuffer vid_buf; - struct v4l2_format vid_win; - int vid_buf_valid; // Status: video buffer is valid (set) - int vid_win_valid; // Status: video window is valid (set) - int overlay; /*Status: Are we overlaying? */ - unsigned int clipmask[USBVISION_CLIPMASK_SIZE / 4]; - unsigned char *overlay_base; /* Virtual base address of video buffer */ - unsigned char *overlay_win; /* virt start address of overlay window */ - struct usbvision_frame overlay_frame; - // Decompression stuff: unsigned char *IntraFrameBuffer; /* Buffer for reference frame */ int BlockPos; //for test only @@ -449,16 +456,6 @@ struct usb_usbvision { int lastComprLevel; // How strong (100) or weak (0) was compression int usb_bandwidth; /* Mbit/s */ - /* /proc entries, relative to /proc/video/usbvision/ */ - struct proc_dir_entry *proc_devdir; /* Per-device proc directory */ - struct proc_dir_entry *proc_info; /* /info entry */ - struct proc_dir_entry *proc_register; /* /register entry */ - struct proc_dir_entry *proc_freq; /* /freq entry */ - struct proc_dir_entry *proc_input; /* /input entry */ - struct proc_dir_entry *proc_frame; /* /frame entry */ - struct proc_dir_entry *proc_button; /* /button entry */ - struct proc_dir_entry *proc_control; /* /control entry */ - /* Statistics that can be overlayed on the screen */ unsigned long isocUrbCount; // How many URBs we received so far unsigned long urb_length; /* Length of last URB */ @@ -479,6 +476,70 @@ struct usb_usbvision { int ComprBlockTypes[4]; }; + +/* --------------------------------------------------------------- */ +/* defined in usbvision-i2c.c */ +/* i2c-algo-usb declaration */ +/* --------------------------------------------------------------- */ + +int usbvision_i2c_usb_add_bus(struct i2c_adapter *); +int usbvision_i2c_usb_del_bus(struct i2c_adapter *); + +static inline void *i2c_get_algo_usb_data (struct i2c_algo_usb_data *dev) +{ + return dev->data; +} + +static inline void i2c_set_algo_usb_data (struct i2c_algo_usb_data *dev, void *data) +{ + dev->data = data; +} + + +/* ----------------------------------------------------------------------- */ +/* usbvision specific I2C functions */ +/* ----------------------------------------------------------------------- */ +int usbvision_init_i2c(struct usb_usbvision *usbvision); +void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,void *arg); + +/* defined in usbvision-core.c */ +void *usbvision_rvmalloc(unsigned long size); +void usbvision_rvfree(void *mem, unsigned long size); +int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg); +int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, + unsigned char value); + +int usbvision_frames_alloc(struct usb_usbvision *usbvision); +void usbvision_frames_free(struct usb_usbvision *usbvision); +int usbvision_scratch_alloc(struct usb_usbvision *usbvision); +void usbvision_scratch_free(struct usb_usbvision *usbvision); +int usbvision_sbuf_alloc(struct usb_usbvision *usbvision); +void usbvision_sbuf_free(struct usb_usbvision *usbvision); +int usbvision_decompress_alloc(struct usb_usbvision *usbvision); +void usbvision_decompress_free(struct usb_usbvision *usbvision); + +int usbvision_setup(struct usb_usbvision *usbvision,int format); +int usbvision_init_isoc(struct usb_usbvision *usbvision); +int usbvision_restart_isoc(struct usb_usbvision *usbvision); +void usbvision_stop_isoc(struct usb_usbvision *usbvision); + +int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel); +int usbvision_audio_off(struct usb_usbvision *usbvision); + +int usbvision_begin_streaming(struct usb_usbvision *usbvision); +void usbvision_empty_framequeues(struct usb_usbvision *dev); +int usbvision_stream_interrupt(struct usb_usbvision *dev); + +int usbvision_muxsel(struct usb_usbvision *usbvision, int channel); +int usbvision_set_input(struct usb_usbvision *usbvision); +int usbvision_set_output(struct usb_usbvision *usbvision, int width, int height); + +void usbvision_init_powerOffTimer(struct usb_usbvision *usbvision); +void usbvision_set_powerOffTimer(struct usb_usbvision *usbvision); +void usbvision_reset_powerOffTimer(struct usb_usbvision *usbvision); +int usbvision_power_off(struct usb_usbvision *usbvision); +int usbvision_power_on(struct usb_usbvision *usbvision); + #endif /* __LINUX_USBVISION_H */ /* -- cgit v0.10.2 From 7ca659e3c1c3dd67073751385ae3bd25b27526bb Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 9 Dec 2006 11:43:31 -0300 Subject: V4L/DVB (4942): Whitespace cleanups Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c index f1fb9e7..134eb98 100644 --- a/drivers/media/video/usbvision/usbvision-cards.c +++ b/drivers/media/video/usbvision/usbvision-cards.c @@ -1,4 +1,4 @@ -/* +/* * USBVISION.H * usbvision header file * @@ -31,18 +31,18 @@ /* Supported Devices: A table for usbvision.c*/ struct usbvision_device_data_st usbvision_device_data[] = { - {0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"}, - {0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Xanboo"}, + {0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"}, + {0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Xanboo"}, {0x050D, 0x0208, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Belkin USBView II"}, - {0x0571, 0x0002, 0, CODEC_SAA7111, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, -1, -1, 7, "echoFX InterView Lite"}, + {0x0571, 0x0002, 0, CODEC_SAA7111, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, -1, -1, 7, "echoFX InterView Lite"}, {0x0573, 0x0003, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "USBGear USBG-V1 resp. HAMA USB"}, {0x0573, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "D-Link V100"}, {0x0573, 0x2000, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "X10 USB Camera"}, - {0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, -1, 3, 7, "Osprey 50"}, - {0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Hauppauge USB-Live Model 600"}, + {0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, -1, 3, 7, "Osprey 50"}, + {0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Hauppauge USB-Live Model 600"}, {0x0573, 0x2101, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 2, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Zoran Co. PMD (Nogatech) AV-grabber Manhattan"}, {0x0573, 0x4100, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Nogatech USB-TV (NTSC) FM"}, - {0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"}, + {0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"}, {0x0573, 0x4450, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "PixelView PlayTv-USB PRO (PAL) FM"}, {0x0573, 0x4550, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "ZTV ZT-721 2.4GHz USB A/V Receiver"}, {0x0573, 0x4d00, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Hauppauge WinTv-USB USA"}, @@ -52,9 +52,9 @@ struct usbvision_device_data_st usbvision_device_data[] = { {0x0573, 0x4d10, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC) FM"}, {0x0573, 0x4d11, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, {0x0573, 0x4d12, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, - {0x0573, 0x4d2a, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B285"}, + {0x0573, 0x4d2a, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B285"}, {0x0573, 0x4d2b, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B282"}, - {0x0573, 0x4d2c, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (PAL/SECAM) 40209 Rev E1A5"}, + {0x0573, 0x4d2c, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (PAL/SECAM) 40209 Rev E1A5"}, {0x0573, 0x4d20, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226"}, {0x0573, 0x4d21, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL)"}, {0x0573, 0x4d22, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) MODEL 566"}, @@ -62,20 +62,20 @@ struct usbvision_device_data_st usbvision_device_data[] = { {0x0573, 0x4d25, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B234"}, {0x0573, 0x4d26, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B243"}, {0x0573, 0x4d27, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B281"}, - {0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B283"}, - {0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40205 Rev B298"}, + {0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B283"}, + {0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40205 Rev B298"}, {0x0573, 0x4d30, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB FM Model 40211 Rev B123"}, {0x0573, 0x4d31, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 568"}, - {0x0573, 0x4d32, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 573"}, + {0x0573, 0x4d32, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 573"}, {0x0573, 0x4d35, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 40219 Rev B252"}, {0x0573, 0x4d37, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTV USB device Model 40219 Rev E189"}, {0x0768, 0x0006, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 5, 5, -1, "Camtel Technology USB TV Genie Pro FM Model TVB330"}, {0x07d0, 0x0001, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Digital Video Creator I"}, - {0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 82, 20, 7, "Global Village GV-007 (NTSC)"}, + {0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 82, 20, 7, "Global Village GV-007 (NTSC)"}, {0x07d0, 0x0003, 0, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)"}, {0x07d0, 0x0004, 0, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-80 Rev 1 (PAL)"}, - {0x07d0, 0x0005, 0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"}, - {0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"}, + {0x07d0, 0x0005, 0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"}, + {0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"}, {0x2304, 0x0109, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (SECAM)"}, {0x2304, 0x0110, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1,128, 23, -1, "Pinnacle Studio PCTV USB (PAL) FM"}, {0x2304, 0x0111, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Miro PCTV USB"}, @@ -84,7 +84,7 @@ struct usbvision_device_data_st usbvision_device_data[] = { {0x2304, 0x0212, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_TEMIC_4039FR5_NTSC, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (NTSC) FM"}, {0x2304, 0x0214, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, {0x2304, 0x0300, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (NTSC)"}, - {0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (PAL)"}, + {0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (PAL)"}, {0x2304, 0x0419, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle PCTV Bungee USB (PAL) FM"}, {0x2400, 0x4200, -1, CODEC_SAA7111, 3, VIDEO_MODE_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, {} /* Terminating entry */ @@ -94,13 +94,13 @@ struct usbvision_device_data_st usbvision_device_data[] = { struct usb_device_id usbvision_table [] = { { USB_DEVICE(0xFFF0, 0xFFF0) }, /* Custom Dummy USBVision Device */ - { USB_DEVICE(0x0A6F, 0x0400) }, /* Xanboo */ + { USB_DEVICE(0x0A6F, 0x0400) }, /* Xanboo */ { USB_DEVICE(0x050d, 0x0208) }, /* Belkin USBView II */ { USB_DEVICE(0x0571, 0x0002) }, /* echoFX InterView Lite */ { USB_DEVICE(0x0573, 0x0003) }, /* USBGear USBG-V1 */ { USB_DEVICE(0x0573, 0x0400) }, /* D-Link V100 */ { USB_DEVICE(0x0573, 0x2000) }, /* X10 USB Camera */ - { USB_DEVICE(0x0573, 0x2d00) }, /* Osprey 50 */ + { USB_DEVICE(0x0573, 0x2d00) }, /* Osprey 50 */ { USB_DEVICE(0x0573, 0x2d01) }, /* Hauppauge USB-Live Model 600 */ { USB_DEVICE(0x0573, 0x2101) }, /* Zoran Co. PMD (Nogatech) AV-grabber Manhattan */ { USB_DEVICE(0x0573, 0x4100) }, /* Nogatech USB-TV FM (NTSC) */ @@ -114,9 +114,9 @@ struct usb_device_id usbvision_table [] = { { USB_DEVICE(0x0573, 0x4d10) }, /* Hauppauge WinTv-USB with FM USA radio */ { USB_DEVICE(0x0573, 0x4d11) }, /* Hauppauge WinTv-USB (PAL) with FM radio */ { USB_DEVICE(0x0573, 0x4d12) }, /* Hauppauge WinTv-USB UK with FM Radio */ - { USB_DEVICE(0x0573, 0x4d2a) }, /* Hauppague WinTv USB Model 602 40201 Rev B285 */ + { USB_DEVICE(0x0573, 0x4d2a) }, /* Hauppague WinTv USB Model 602 40201 Rev B285 */ { USB_DEVICE(0x0573, 0x4d2b) }, /* Hauppague WinTv USB Model 602 40201 Rev B282 */ - { USB_DEVICE(0x0573, 0x4d2c) }, /* Hauppague WinTv USB Model 40209 Rev. E1A5 PAL*/ + { USB_DEVICE(0x0573, 0x4d2c) }, /* Hauppague WinTv USB Model 40209 Rev. E1A5 PAL*/ { USB_DEVICE(0x0573, 0x4d20) }, /* Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226 */ { USB_DEVICE(0x0573, 0x4d21) }, /* Hauppauge WinTv-USB II (PAL) with FM radio*/ { USB_DEVICE(0x0573, 0x4d22) }, /* Hauppauge WinTv-USB II (PAL) Model 566 */ @@ -125,7 +125,7 @@ struct usb_device_id usbvision_table [] = { { USB_DEVICE(0x0573, 0x4d26) }, /* Hauppauge WinTv-USB Model 40209 Rev B243 */ { USB_DEVICE(0x0573, 0x4d27) }, /* Hauppauge WinTv-USB Model 40204 Rev B281 */ { USB_DEVICE(0x0573, 0x4d28) }, /* Hauppauge WinTv-USB Model 40204 Rev B283 */ - { USB_DEVICE(0x0573, 0x4d29) }, /* Hauppauge WinTv-USB Model 40205 Rev B298 */ + { USB_DEVICE(0x0573, 0x4d29) }, /* Hauppauge WinTv-USB Model 40205 Rev B298 */ { USB_DEVICE(0x0573, 0x4d30) }, /* Hauppauge WinTv-USB FM Model 40211 Rev B123 */ { USB_DEVICE(0x0573, 0x4d31) }, /* Hauppauge WinTv-USB III (PAL) with FM radio Model 568 */ { USB_DEVICE(0x0573, 0x4d32) }, /* Hauppauge WinTv-USB III (PAL) FM Model 573 */ @@ -136,7 +136,7 @@ struct usb_device_id usbvision_table [] = { { USB_DEVICE(0x07d0, 0x0002) }, /* Global Village GV-007 (NTSC) */ { USB_DEVICE(0x07d0, 0x0003) }, /* Dazzle Fusion Model DVC-50 Rev 1 (NTSC) */ { USB_DEVICE(0x07d0, 0x0004) }, /* Dazzle Fusion Model DVC-80 Rev 1 (PAL) */ - { USB_DEVICE(0x07d0, 0x0005) }, /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */ + { USB_DEVICE(0x07d0, 0x0005) }, /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */ { USB_DEVICE(0x2304, 0x010d) }, /* Pinnacle Studio PCTV USB (PAL) */ { USB_DEVICE(0x2304, 0x0109) }, /* Pinnacle Studio PCTV USB (SECAM) */ { USB_DEVICE(0x2304, 0x0110) }, /* Pinnacle Studio PCTV USB (PAL) */ @@ -146,7 +146,7 @@ struct usb_device_id usbvision_table [] = { { USB_DEVICE(0x2304, 0x0212) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ { USB_DEVICE(0x2304, 0x0214) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ { USB_DEVICE(0x2304, 0x0300) }, /* Pinnacle Studio Linx Video input cable (NTSC) */ - { USB_DEVICE(0x2304, 0x0301) }, /* Pinnacle Studio Linx Video input cable (PAL) */ + { USB_DEVICE(0x2304, 0x0301) }, /* Pinnacle Studio Linx Video input cable (PAL) */ { USB_DEVICE(0x2304, 0x0419) }, /* Pinnacle PCTV Bungee USB (PAL) FM */ { USB_DEVICE(0x2400, 0x4200) }, /* Hauppauge WinTv-USB2 Model 42012 */ diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index cf787d8..cf5d477 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -1,6 +1,6 @@ /* * usbvision-core.c - driver for NT100x USB video capture devices - * + * * * Copyright (c) 1999-2005 Joerg Heckenbach * Dwaine Garden diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 7e26d83..ab29ddb 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -1,5 +1,5 @@ /* - * USB USBVISION Video device driver 0.9.9 + * USB USBVISION Video device driver 0.9.9 * * * -- cgit v0.10.2 From a1ed551cdb4f29157466340760fde68bcda704a9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 9 Dec 2006 11:41:59 -0300 Subject: V4L/DVB (4941): Remove LINUX_VERSION_CODE and fix identations Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index cf5d477..e660a91 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -47,8 +47,8 @@ #include #include - #include - #include +#include +#include #ifdef CONFIG_KMOD #include @@ -1397,7 +1397,7 @@ static int usbvision_compress_isochronous(struct usb_usbvision *usbvision, return totlen; } -static void usbvision_isocIrq(struct urb *urb, struct pt_regs *regs) +static void usbvision_isocIrq(struct urb *urb) { int errCode = 0; int len; @@ -1529,7 +1529,7 @@ int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, } -static void usbvision_ctrlUrb_complete(struct urb *urb, struct pt_regs *regs) +static void usbvision_ctrlUrb_complete(struct urb *urb) { struct usb_usbvision *usbvision = (struct usb_usbvision *)urb->context; @@ -2416,11 +2416,7 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) int j, k; struct urb *urb; - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) - urb = usb_alloc_urb(USBVISION_URB_FRAMES); - #else - urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); - #endif + urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); if (urb == NULL) { err("%s: usb_alloc_urb() failed", __FUNCTION__); return -ENOMEM; @@ -2429,12 +2425,8 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) urb->dev = dev; urb->context = usbvision; urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp); - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) - urb->transfer_flags = USB_ISO_ASAP; - #else urb->transfer_flags = URB_ISO_ASAP; urb->interval = 1; - #endif urb->transfer_buffer = usbvision->sbuf[bufIdx].data; urb->complete = usbvision_isocIrq; urb->number_of_packets = USBVISION_URB_FRAMES; @@ -2450,11 +2442,7 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) /* Submit all URBs */ for (bufIdx = 0; bufIdx < USBVISION_NUMSBUF; bufIdx++) { - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) - errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb); - #else errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb, GFP_KERNEL); - #endif if (errCode) { err("%s: usb_submit_urb(%d) failed: error %d", __FUNCTION__, bufIdx, errCode); } diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index 2de74e4..92bf9a1 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -29,7 +29,7 @@ #include #include #include - #include +#include #include #include #include @@ -350,13 +350,9 @@ static int detach_inform(struct i2c_client *client) { struct usb_usbvision *usbvision; - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) - usbvision = (struct usb_usbvision *)client->adapter->data; - #else - usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); - #endif + usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); - PDEBUG(DBG_I2C,"usbvision[%d] detaches %s", usbvision->nr, client->name); + PDEBUG(DBG_I2C,"usbvision[%d] detaches %s", usbvision->nr, client->name); return 0; } @@ -548,9 +544,9 @@ static struct i2c_adapter i2c_adap_template = { .algo_data = NULL, .client_register = attach_inform, .client_unregister = detach_inform, -#if defined (I2C_ADAP_CLASS_TV_ANALOG) +#ifdef I2C_ADAP_CLASS_TV_ANALOG .class = I2C_ADAP_CLASS_TV_ANALOG, -#elif defined (I2C_CLASS_TV_ANALOG) +#else .class = I2C_CLASS_TV_ANALOG, #endif }; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index ab29ddb..1285b93 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -68,8 +68,8 @@ #include #include - #include - #include +#include +#include #ifdef CONFIG_KMOD #include @@ -189,8 +189,8 @@ MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the devic MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE(DRIVER_LICENSE); - MODULE_VERSION(USBVISION_VERSION_STRING); - MODULE_ALIAS(DRIVER_ALIAS); +MODULE_VERSION(USBVISION_VERSION_STRING); +MODULE_ALIAS(DRIVER_ALIAS); /****************************************************************************************/ @@ -1430,9 +1430,7 @@ static int usbvision_vbi_ioctl(struct inode *inode, struct file *file, // Video template static struct file_operations usbvision_fops = { - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) .owner = THIS_MODULE, - #endif .open = usbvision_v4l2_open, .release = usbvision_v4l2_close, .read = usbvision_v4l2_read, @@ -1441,25 +1439,19 @@ static struct file_operations usbvision_fops = { .llseek = no_llseek, }; static struct video_device usbvision_video_template = { - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) .owner = THIS_MODULE, - #endif .type = VID_TYPE_TUNER | VID_TYPE_CAPTURE, .hardware = VID_HARDWARE_USBVISION, .fops = &usbvision_fops, - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) .name = "usbvision-video", .release = video_device_release, - #endif .minor = -1, }; // Radio template static struct file_operations usbvision_radio_fops = { - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) .owner = THIS_MODULE, - #endif .open = usbvision_radio_open, .release = usbvision_radio_close, .ioctl = usbvision_radio_ioctl, @@ -1468,25 +1460,19 @@ static struct file_operations usbvision_radio_fops = { static struct video_device usbvision_radio_template= { - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) .owner = THIS_MODULE, - #endif .type = VID_TYPE_TUNER, .hardware = VID_HARDWARE_USBVISION, .fops = &usbvision_radio_fops, - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) .release = video_device_release, .name = "usbvision-radio", - #endif .minor = -1, }; // vbi template static struct file_operations usbvision_vbi_fops = { - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) .owner = THIS_MODULE, - #endif .open = usbvision_vbi_open, .release = usbvision_vbi_close, .ioctl = usbvision_vbi_ioctl, @@ -1495,16 +1481,12 @@ static struct file_operations usbvision_vbi_fops = { static struct video_device usbvision_vbi_template= { - #if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31) .owner = THIS_MODULE, - #endif .type = VID_TYPE_TUNER, .hardware = VID_HARDWARE_USBVISION, .fops = &usbvision_vbi_fops, - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) .release = video_device_release, .name = "usbvision-vbi", - #endif .minor = -1, }; -- cgit v0.10.2 From 1c5ee876d697a9d1b70117e4277eaa445d14a728 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Mon, 4 Dec 2006 18:04:11 -0300 Subject: V4L/DVB (4938): Cx88: Convert lgdt3302 tuning function to use dvb_pll_attach There was a still a pre-dvb-pll set_params function for the lgdt3302 in the cx88-dvb driver. This patch removes that function and uses dvb_pll_attach() for the cards that were using it (Dvico FusionHDTV 3 GOLD {Q,T}). This way the set_params function from dvb-pll is used. dvb_attach() is in turn used on dvb_pll_attach(), eliminating some static dependencies on dvb-pll. There are still a couple static dependencies on dvb-pll remaining. Signed-off-by: Trent Piepho Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 0266a36..95db017 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -360,38 +360,6 @@ static struct or51132_config pchdtv_hd3000 = { .set_ts_params = or51132_set_ts_param, }; -static int lgdt3302_tuner_set_params(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) -{ - /* FIXME make this routine use the tuner-simple code. - * It could probably be shared with a number of ATSC - * frontends. Many share the same tuner with analog TV. */ - - struct cx8802_dev *dev= fe->dvb->priv; - struct cx88_core *core = dev->core; - u8 buf[4]; - struct i2c_msg msg = - { .addr = dev->core->pll_addr, .flags = 0, .buf = buf, .len = 4 }; - int err; - - dvb_pll_configure(core->pll_desc, buf, params->frequency, 0); - dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n", - __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((err = i2c_transfer(&core->i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "cx88-dvb: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, buf[0], buf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - return 0; -} - static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index) { struct cx8802_dev *dev= fe->dvb->priv; @@ -669,7 +637,8 @@ static int dvb_register(struct cx8802_dev *dev) &fusionhdtv_3_gold, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params; + dvb_attach(dvb_pll_attach, dev->dvb.frontend, dev->core->pll_addr, + &dev->core->i2c_adap, dev->core->pll_desc); } } break; @@ -689,7 +658,8 @@ static int dvb_register(struct cx8802_dev *dev) &fusionhdtv_3_gold, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params; + dvb_attach(dvb_pll_attach, dev->dvb.frontend, dev->core->pll_addr, + &dev->core->i2c_adap, dev->core->pll_desc); } } break; -- cgit v0.10.2 From b2fb7f55a1837e39a9f74e2fd7be990ccba90eb4 Mon Sep 17 00:00:00 2001 From: Rusty Scott Date: Mon, 4 Dec 2006 18:04:15 -0300 Subject: V4L/DVB (4939): Or51132: Changed SNR and signal strength reporting Removes embedded log functions and makes use of the DVB math functions to provide SNR in dB. The changes are modeled after recent changes made to the LGDT330x frontends in lgdt330x.c Signed-off-by: Rusty Scott Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c index d20ab30..5a3a6e5 100644 --- a/drivers/media/dvb/frontends/or51132.c +++ b/drivers/media/dvb/frontends/or51132.c @@ -40,6 +40,7 @@ #include #include +#include "dvb_math.h" #include "dvb_frontend.h" #include "dvb-pll.h" #include "or51132.h" @@ -62,6 +63,7 @@ struct or51132_state /* Demodulator private data */ fe_modulation_t current_modulation; + u32 snr; /* Result of last SNR calculation */ /* Tuner private data */ u32 current_frequency; @@ -465,124 +467,128 @@ static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status) return 0; } -/* log10-1 table at .5 increments from 1 to 100.5 */ -static unsigned int i100x20log10[] = { - 0, 352, 602, 795, 954, 1088, 1204, 1306, 1397, 1480, - 1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042, - 2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380, - 2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623, - 2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813, - 2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968, - 2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100, - 3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214, - 3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316, - 3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406, - 3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488, - 3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563, - 3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632, - 3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696, - 3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755, - 3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811, - 3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863, - 3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913, - 3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960, - 3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004, -}; +/* Calculate SNR estimation (scaled by 2^24) -static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000}; + 8-VSB SNR and QAM equations from Oren datasheets -static unsigned int i20Log10(unsigned short val) -{ - unsigned int rntval = 100; - unsigned int tmp = val; - unsigned int exp = 1; + For 8-VSB: + SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) - K + + Where K = 0 if NTSC rejection filter is OFF; and + K = 3 if NTSC rejection filter is ON + + For QAM64: + SNR[dB] = 10 * log10(897152044.8282 / MSE^2 ) - while(tmp > 100) {tmp /= 100; exp++;} + For QAM256: + SNR[dB] = 10 * log10(907832426.314266 / MSE^2 ) - val = (2 * val)/denom[exp]; - if (exp > 1) rntval = 2000*exp; + We re-write the snr equation as: + SNR * 2^24 = 10*(c - 2*intlog10(MSE)) + Where for QAM256, c = log10(907832426.314266) * 2^24 + and for 8-VSB and QAM64, c = log10(897152044.8282) * 2^24 */ - rntval += i100x20log10[val]; - return rntval; +static u32 calculate_snr(u32 mse, u32 c) +{ + if (mse == 0) /* No signal */ + return 0; + + mse = 2*intlog10(mse); + if (mse > c) { + /* Negative SNR, which is possible, but realisticly the + demod will lose lock before the signal gets this bad. The + API only allows for unsigned values, so just return 0 */ + return 0; + } + return 10*(c - mse); } -static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength) +static int or51132_read_snr(struct dvb_frontend* fe, u16* snr) { struct or51132_state* state = fe->demodulator_priv; - unsigned char rec_buf[2]; - unsigned char snd_buf[2]; - u8 rcvr_stat; - u16 snr_equ; - u32 signal_strength; - int usK; + u8 rec_buf[2]; + u8 snd_buf[2]; + u32 noise; + u32 c; + u32 usK; + /* Register is same for VSB or QAM firmware */ snd_buf[0]=0x04; snd_buf[1]=0x02; /* SNR after Equalizer */ msleep(30); /* 30ms */ if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) { - printk(KERN_WARNING "or51132: read_status write error\n"); - return -1; + printk(KERN_WARNING "or51132: snr write error\n"); + return -EREMOTEIO; } msleep(30); /* 30ms */ if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: read_status read error\n"); - return -1; + printk(KERN_WARNING "or51132: snr read error\n"); + return -EREMOTEIO; } - snr_equ = rec_buf[0] | (rec_buf[1] << 8); - dprintk("read_signal_strength snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ); + noise = rec_buf[0] | (rec_buf[1] << 8); + dprintk("read_snr noise %x %x (%i)\n",rec_buf[0],rec_buf[1],noise); - /* Receiver Status */ + /* Read status, contains modulation type for QAM_AUTO and + NTSC filter for VSB */ snd_buf[0]=0x04; - snd_buf[1]=0x00; + snd_buf[1]=0x00; /* Status register */ msleep(30); /* 30ms */ if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) { - printk(KERN_WARNING "or51132: read_signal_strength read_status write error\n"); - return -1; + printk(KERN_WARNING "or51132: status write error\n"); + return -EREMOTEIO; } msleep(30); /* 30ms */ if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: read_signal_strength read_status read error\n"); - return -1; + printk(KERN_WARNING "or51132: status read error\n"); + return -EREMOTEIO; } - dprintk("read_signal_strength read_status %x %x\n",rec_buf[0],rec_buf[1]); - rcvr_stat = rec_buf[1]; - usK = (rcvr_stat & 0x10) ? 3 : 0; - /* The value reported back from the frontend will be FFFF=100% 0000=0% */ - signal_strength = (((8952 - i20Log10(snr_equ) - usK*100)/3+5)*65535)/1000; - if (signal_strength > 0xffff) - *strength = 0xffff; - else - *strength = signal_strength; - dprintk("read_signal_strength %i\n",*strength); + usK = 0; + switch (rec_buf[0]) { + case 0x06: + usK = (rec_buf[1] & 0x10) ? 0x03000000 : 0; + /* Fall through to QAM64 case */ + case 0x43: + c = 150204167; + break; + case 0x45: + c = 150290396; + break; + default: + printk(KERN_ERR "or51132: unknown status 0x%02x\n", rec_buf[0]); + return -EREMOTEIO; + } + dprintk("%s: modulation %02x, NTSC rej O%s\n", __FUNCTION__, + rec_buf[0], rec_buf[1]&0x10?"n":"ff"); + + /* Calculate SNR using noise, c, and NTSC rejection correction */ + state->snr = calculate_snr(noise, c) - usK; + *snr = (state->snr) >> 16; + + dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise, + state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); return 0; } -static int or51132_read_snr(struct dvb_frontend* fe, u16* snr) +static int or51132_read_signal_strength(struct dvb_frontend* fe, u16* strength) { - struct or51132_state* state = fe->demodulator_priv; - unsigned char rec_buf[2]; - unsigned char snd_buf[2]; - u16 snr_equ; - - snd_buf[0]=0x04; - snd_buf[1]=0x02; /* SNR after Equalizer */ - msleep(30); /* 30ms */ - if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) { - printk(KERN_WARNING "or51132: read_snr write error\n"); - return -1; - } - msleep(30); /* 30ms */ - if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: read_snr dvr read error\n"); - return -1; - } - snr_equ = rec_buf[0] | (rec_buf[1] << 8); - dprintk("read_snr snr_equ %x %x (%i)\n",rec_buf[0],rec_buf[1],snr_equ); + /* Calculate Strength from SNR up to 35dB */ + /* Even though the SNR can go higher than 35dB, there is some comfort */ + /* factor in having a range of strong signals that can show at 100% */ + struct or51132_state* state = (struct or51132_state*) fe->demodulator_priv; + u16 snr; + int ret; - *snr = 0xFFFF - snr_equ; - dprintk("read_snr %i\n",*snr); + ret = fe->ops.read_snr(fe, &snr); + if (ret != 0) + return ret; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; return 0; } -- cgit v0.10.2 From 1444e5f591b8c68d7a326f5b0ad6c63a984d56dd Mon Sep 17 00:00:00 2001 From: Rusty Scott Date: Mon, 4 Dec 2006 18:04:16 -0300 Subject: V4L/DVB (4940): Or51211: Changed SNR and signal strength calculations Removes embedded log functions and makes use of the DVB math functions to provide SNR in dB. The changes are modeled after recent changes made to the LGDT330x frontends in lgdt330x.c Signed-off-by: Rusty Scott Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c index 2bf124b..048d7cf 100644 --- a/drivers/media/dvb/frontends/or51211.c +++ b/drivers/media/dvb/frontends/or51211.c @@ -39,6 +39,7 @@ #include #include +#include "dvb_math.h" #include "dvb_frontend.h" #include "or51211.h" @@ -63,6 +64,7 @@ struct or51211_state { /* Demodulator private data */ u8 initialized:1; + u32 snr; /* Result of last SNR claculation */ /* Tuner private data */ u32 current_frequency; @@ -292,107 +294,81 @@ static int or51211_read_status(struct dvb_frontend* fe, fe_status_t* status) return 0; } -/* log10-1 table at .5 increments from 1 to 100.5 */ -static unsigned int i100x20log10[] = { - 0, 352, 602, 795, 954, 1088, 1204, 1306, 1397, 1480, - 1556, 1625, 1690, 1750, 1806, 1858, 1908, 1955, 2000, 2042, - 2082, 2121, 2158, 2193, 2227, 2260, 2292, 2322, 2352, 2380, - 2408, 2434, 2460, 2486, 2510, 2534, 2557, 2580, 2602, 2623, - 2644, 2664, 2684, 2704, 2723, 2742, 2760, 2778, 2795, 2813, - 2829, 2846, 2862, 2878, 2894, 2909, 2924, 2939, 2954, 2968, - 2982, 2996, 3010, 3023, 3037, 3050, 3062, 3075, 3088, 3100, - 3112, 3124, 3136, 3148, 3159, 3170, 3182, 3193, 3204, 3214, - 3225, 3236, 3246, 3256, 3266, 3276, 3286, 3296, 3306, 3316, - 3325, 3334, 3344, 3353, 3362, 3371, 3380, 3389, 3397, 3406, - 3415, 3423, 3432, 3440, 3448, 3456, 3464, 3472, 3480, 3488, - 3496, 3504, 3511, 3519, 3526, 3534, 3541, 3549, 3556, 3563, - 3570, 3577, 3584, 3591, 3598, 3605, 3612, 3619, 3625, 3632, - 3639, 3645, 3652, 3658, 3665, 3671, 3677, 3683, 3690, 3696, - 3702, 3708, 3714, 3720, 3726, 3732, 3738, 3744, 3750, 3755, - 3761, 3767, 3772, 3778, 3784, 3789, 3795, 3800, 3806, 3811, - 3816, 3822, 3827, 3832, 3838, 3843, 3848, 3853, 3858, 3863, - 3868, 3874, 3879, 3884, 3888, 3893, 3898, 3903, 3908, 3913, - 3918, 3922, 3927, 3932, 3936, 3941, 3946, 3950, 3955, 3960, - 3964, 3969, 3973, 3978, 3982, 3986, 3991, 3995, 4000, 4004, -}; - -static unsigned int denom[] = {1,1,100,1000,10000,100000,1000000,10000000,100000000}; +/* Calculate SNR estimation (scaled by 2^24) -static unsigned int i20Log10(unsigned short val) -{ - unsigned int rntval = 100; - unsigned int tmp = val; - unsigned int exp = 1; + 8-VSB SNR equation from Oren datasheets - while(tmp > 100) {tmp /= 100; exp++;} + For 8-VSB: + SNR[dB] = 10 * log10(219037.9454 / MSE^2 ) - val = (2 * val)/denom[exp]; - if (exp > 1) rntval = 2000*exp; + We re-write the snr equation as: + SNR * 2^24 = 10*(c - 2*intlog10(MSE)) + Where for 8-VSB, c = log10(219037.9454) * 2^24 */ - rntval += i100x20log10[val]; - return rntval; +static u32 calculate_snr(u32 mse, u32 c) +{ + if (mse == 0) /* No signal */ + return 0; + + mse = 2*intlog10(mse); + if (mse > c) { + /* Negative SNR, which is possible, but realisticly the + demod will lose lock before the signal gets this bad. The + API only allows for unsigned values, so just return 0 */ + return 0; + } + return 10*(c - mse); } -static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength) +static int or51211_read_snr(struct dvb_frontend* fe, u16* snr) { struct or51211_state* state = fe->demodulator_priv; u8 rec_buf[2]; - u8 snd_buf[4]; - u8 snr_equ; - u32 signal_strength; + u8 snd_buf[3]; /* SNR after Equalizer */ snd_buf[0] = 0x04; snd_buf[1] = 0x00; snd_buf[2] = 0x04; - snd_buf[3] = 0x00; if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { - printk(KERN_WARNING "or51211: read_status write error\n"); + printk(KERN_WARNING "%s: error writing snr reg\n", + __FUNCTION__); return -1; } - msleep(3); if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51211: read_status read error\n"); + printk(KERN_WARNING "%s: read_status read error\n", + __FUNCTION__); return -1; } - snr_equ = rec_buf[0] & 0xff; - /* The value reported back from the frontend will be FFFF=100% 0000=0% */ - signal_strength = (((5334 - i20Log10(snr_equ))/3+5)*65535)/1000; - if (signal_strength > 0xffff) - *strength = 0xffff; - else - *strength = signal_strength; - dprintk("read_signal_strength %i\n",*strength); + state->snr = calculate_snr(rec_buf[0], 89599047); + *snr = (state->snr) >> 16; + + dprintk("%s: noise = 0x%02x, snr = %d.%02d dB\n", __FUNCTION__, rec_buf[0], + state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); return 0; } -static int or51211_read_snr(struct dvb_frontend* fe, u16* snr) +static int or51211_read_signal_strength(struct dvb_frontend* fe, u16* strength) { - struct or51211_state* state = fe->demodulator_priv; - u8 rec_buf[2]; - u8 snd_buf[4]; - - /* SNR after Equalizer */ - snd_buf[0] = 0x04; - snd_buf[1] = 0x00; - snd_buf[2] = 0x04; - snd_buf[3] = 0x00; - - if (i2c_writebytes(state,state->config->demod_address,snd_buf,3)) { - printk(KERN_WARNING "or51211: read_status write error\n"); - return -1; - } - msleep(3); - if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51211: read_status read error\n"); - return -1; - } - *snr = rec_buf[0] & 0xff; - - dprintk("read_snr %i\n",*snr); + /* Calculate Strength from SNR up to 35dB */ + /* Even though the SNR can go higher than 35dB, there is some comfort */ + /* factor in having a range of strong signals that can show at 100% */ + struct or51211_state* state = (struct or51211_state*)fe->demodulator_priv; + u16 snr; + int ret; + + ret = fe->ops.read_snr(fe, &snr); + if (ret != 0) + return ret; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; return 0; } -- cgit v0.10.2 From f30ebd43597e9b2bbbc8b0eb650f5aad545e255b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 9 Dec 2006 12:32:18 -0300 Subject: V4L/DVB (4951): Add version.h, since it is required for VIDIOC_QUERYCAP Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 1285b93..3f3839b 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -44,6 +44,7 @@ * */ +#include #include #include #include -- cgit v0.10.2 From c876a3468d0f136710af81595177889953d1ff71 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Sat, 9 Dec 2006 16:42:54 -0300 Subject: V4L/DVB (4953): Usbvision minor fixes - fix debug outputs - fix returned parameters on VIDIOC_G_FMT, VIDIOC_S_FMT and VIDIOC_TRY_FMT and mmap size setting Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index e660a91..9163b60 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -87,6 +87,7 @@ MODULE_PARM_DESC(SwitchSVideoInput, " Set the S-Video input. Some cables and in #define DBG_ISOC 1<<2 #define DBG_PARSE 1<<3 #define DBG_SCRATCH 1<<4 +#define DBG_FUNC 1<<5 static const int max_imgwidth = MAX_FRAME_WIDTH; static const int max_imgheight = MAX_FRAME_HEIGHT; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 3f3839b..864446c 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -92,8 +92,6 @@ #define ENABLE_HEXDUMP 0 /* Enable if you need it */ -#define USBVISION_DEBUG /* Turn on debug messages */ - #ifdef USBVISION_DEBUG #define PDEBUG(level, fmt, args...) \ if (video_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args) @@ -104,7 +102,7 @@ #define DBG_IOCTL 1<<0 #define DBG_IO 1<<1 #define DBG_PROBE 1<<2 -#define DBG_FUNC 1<<3 +#define DBG_MMAP 1<<3 //String operations #define rmspace(str) while(*str==' ') str++; @@ -821,7 +819,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, vb->memory = V4L2_MEMORY_MMAP; vb->field = V4L2_FIELD_NONE; - vb->length = usbvision->max_frame_size; + vb->length = usbvision->curwidth*usbvision->curheight*usbvision->palette.bytes_per_pixel; vb->timestamp = usbvision->frame[vb->index].timestamp; vb->sequence = usbvision->frame[vb->index].sequence; return 0; @@ -959,13 +957,14 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*usbvision->curheight; vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ + PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT w=%d, h=%d, format=%s", + vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc); + return 0; } - return 0; default: PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT invalid type %d",vf->type); return -EINVAL; } - PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT w=%d, h=%d",vf->fmt.win.w.width, vf->fmt.win.w.height); return 0; } case VIDIOC_TRY_FMT: @@ -991,6 +990,15 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); + vf->fmt.pix.bytesperline = vf->fmt.pix.width*usbvision->palette.bytes_per_pixel; + vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height; + + if(cmd == VIDIOC_TRY_FMT) { + PDEBUG(DBG_IOCTL, "VIDIOC_TRY_FMT grabdisplay w=%d, h=%d, format=%s", + vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc); + return 0; + } + /* stop io in case it is already in progress */ if(usbvision->streaming == Stream_On) { if ((ret = usbvision_stream_interrupt(usbvision))) @@ -1133,7 +1141,7 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) } if (!(vma->vm_flags & VM_WRITE) || - size != PAGE_ALIGN(usbvision->max_frame_size)) { + size != PAGE_ALIGN(usbvision->curwidth*usbvision->curheight*usbvision->palette.bytes_per_pixel)) { up(&usbvision->lock); return -EINVAL; } @@ -1143,7 +1151,7 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) break; } if (i == USBVISION_NUMFRAMES) { - PDEBUG(DBG_FUNC, "mmap: user supplied mapping address is out of range"); + PDEBUG(DBG_MMAP, "mmap: user supplied mapping address is out of range"); up(&usbvision->lock); return -EINVAL; } @@ -1156,7 +1164,7 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) while (size > 0) { if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - PDEBUG(DBG_FUNC, "mmap: vm_insert_page failed"); + PDEBUG(DBG_MMAP, "mmap: vm_insert_page failed"); up(&usbvision->lock); return -EAGAIN; } @@ -2003,7 +2011,7 @@ static int __init usbvision_init(void) PDEBUG(DBG_IOCTL, "IOCTL debugging is enabled [video]"); PDEBUG(DBG_IO, "IO debugging is enabled [video]"); PDEBUG(DBG_PROBE, "PROBE debugging is enabled [video]"); - PDEBUG(DBG_FUNC, "FUNC debugging is enabled [video]"); + PDEBUG(DBG_MMAP, "MMAP debugging is enabled [video]"); /* disable planar mode support unless compression enabled */ if (isocMode != ISOC_MODE_COMPRESS ) { diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index f2eeda8..0e7e3d6 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -3,9 +3,14 @@ * usbvision header file * * Copyright (c) 1999-2005 Joerg Heckenbach + * Dwaine Garden + * + * + * Report problems to v4l MailingList : http://www.redhat.com/mailman/listinfo/video4linux-list * * This module is part of usbvision driver project. * Updates to driver completed by Dwaine P. Garden + * v4l2 conversion by Thierry Merle * * 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 @@ -32,6 +37,8 @@ #include #include +#define USBVISION_DEBUG /* Turn on debug messages */ + #ifndef VID_HARDWARE_USBVISION #define VID_HARDWARE_USBVISION 34 /* USBVision Video Grabber */ #endif -- cgit v0.10.2 From 1d4bb7d3c154167c8f0b80cfd72914d8732d3d01 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 5 Dec 2006 01:01:39 -0300 Subject: V4L/DVB (4943): Cx88: cleanup dvb_pll_attach for lgdt3302 tuners Since we're using dvb_pll_attach now, we dont have to populate dev->core->pll_addr or dev->core->pll_desc anymore. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 95db017..5f6d549 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -631,14 +631,13 @@ static int dvb_register(struct cx8802_dev *dev) /* Select RF connector callback */ fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set; - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_microtune_4042; dev->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_3_gold, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, dev->core->pll_addr, - &dev->core->i2c_adap, dev->core->pll_desc); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_microtune_4042); } } break; @@ -652,14 +651,13 @@ static int dvb_register(struct cx8802_dev *dev) mdelay(100); cx_set(MO_GP0_IO, 9); mdelay(200); - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_thomson_dtt761x; dev->dvb.frontend = dvb_attach(lgdt330x_attach, &fusionhdtv_3_gold, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, dev->core->pll_addr, - &dev->core->i2c_adap, dev->core->pll_desc); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_thomson_dtt761x); } } break; -- cgit v0.10.2 From 5786a34b439b660256e236f870c0418e39dd16d0 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 5 Dec 2006 01:21:19 -0300 Subject: V4L/DVB (4944): Cx88: Convert DViCO FusionHDTV Hybrid to use dvb_pll_attach Converted DViCO FusionHDTV Hybrid to use dvb_pll_attach, removing another static dependency of cx88-dvb on dvb-pll. Acked-by: Chris Pascoe Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 5f6d549..60dd80f 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -268,35 +268,6 @@ static struct mt352_config dntv_live_dvbt_pro_config = { }; #endif -static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params) -{ - u8 pllbuf[4]; - struct cx8802_dev *dev= fe->dvb->priv; - struct i2c_msg msg = - { .addr = dev->core->pll_addr, .flags = 0, - .buf = pllbuf, .len = 4 }; - int err; - - dvb_pll_configure(dev->core->pll_desc, pllbuf, - params->frequency, - params->u.ofdm.bandwidth); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { - printk(KERN_WARNING "cx88-dvb: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, pllbuf[0], pllbuf[1], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - return 0; -} - static struct zl10353_config dvico_fusionhdtv_hybrid = { .demod_address = 0x0f, .no_tuner = 1, @@ -599,13 +570,13 @@ static int dvb_register(struct cx8802_dev *dev) #endif break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID: - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_thomson_fe6600; dev->dvb.frontend = dvb_attach(zl10353_attach, &dvico_fusionhdtv_hybrid, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dev->dvb.frontend->ops.tuner_ops.set_params = dvico_hybrid_tuner_set_params; + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_thomson_fe6600); } break; case CX88_BOARD_PCHDTV_HD3000: -- cgit v0.10.2 From ed35526027f3b45a7c799f73916166b1161bf082 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 5 Dec 2006 01:34:56 -0300 Subject: V4L/DVB (4945): Cx88: consolidate cx22702_config structs There are five cx22702_config structs used by cx88-dvb, only two of which are unique. This patch removes the duplicates and sets each card to use one of the two remaining config structs. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 60dd80f..d9907ce8 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -282,15 +282,7 @@ static struct cx22702_config connexant_refboard_config = { .output_mode = CX22702_SERIAL_OUTPUT, }; -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 = { +static struct cx22702_config hauppauge_hvr_config = { .demod_address = 0x63, .output_mode = CX22702_SERIAL_OUTPUT, }; @@ -313,11 +305,6 @@ static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, return ret; } -static struct cx22702_config hauppauge_hvr1300_config = { - .demod_address = 0x63, - .output_mode = CX22702_SERIAL_OUTPUT, -}; - static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured) { @@ -465,7 +452,7 @@ static int dvb_register(struct cx8802_dev *dev) switch (dev->core->board) { case CX88_BOARD_HAUPPAUGE_DVB_T1: dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_novat_config, + &connexant_refboard_config, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, @@ -490,7 +477,7 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR1100LP: dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr1100_config, + &hauppauge_hvr_config, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, @@ -709,7 +696,7 @@ static int dvb_register(struct cx8802_dev *dev) break; case CX88_BOARD_HAUPPAUGE_HVR1300: dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr1300_config, + &hauppauge_hvr_config, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, @@ -719,7 +706,7 @@ static int dvb_register(struct cx8802_dev *dev) break; case CX88_BOARD_HAUPPAUGE_HVR3000: dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr3000_config, + &hauppauge_hvr_config, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, -- cgit v0.10.2 From 22f3f17dd705c476d2c8a2d879ddacaa8f9237e3 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 5 Dec 2006 01:38:58 -0300 Subject: V4L/DVB (4946): Cx88: Move cx88_dvb_bus_ctrl out of the card-specific area This function should not be in the card-specific area of the file. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index d9907ce8..694002a 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -100,6 +100,26 @@ static struct videobuf_queue_ops dvb_qops = { }; /* ------------------------------------------------------------------ */ + +static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire) +{ + struct cx8802_dev *dev= fe->dvb->priv; + struct cx8802_driver *drv = NULL; + int ret = 0; + + drv = cx8802_get_driver(dev, CX88_MPEG_DVB); + if (drv) { + if(acquire) + ret = drv->request_acquire(drv); + else + ret = drv->request_release(drv); + } + + return ret; +} + +/* ------------------------------------------------------------------ */ + static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe) { static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x39 }; @@ -287,24 +307,6 @@ static struct cx22702_config hauppauge_hvr_config = { .output_mode = CX22702_SERIAL_OUTPUT, }; -static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, - int acquire) -{ - struct cx8802_dev *dev= fe->dvb->priv; - struct cx8802_driver *drv = NULL; - int ret = 0; - - drv = cx8802_get_driver(dev, CX88_MPEG_DVB); - if (drv) { - if(acquire) - ret = drv->request_acquire(drv); - else - ret = drv->request_release(drv); - } - - return ret; -} - static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured) { -- cgit v0.10.2 From 4a390558a691445e3d710998b850570fe8a20d31 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 5 Dec 2006 02:00:53 -0300 Subject: V4L/DVB (4947): Cx88: trivial cleanups - small whitespace cleanups - 80-column whitespace cleanups - remove unneeded brackets Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 694002a..8b20335 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -74,8 +74,8 @@ static int dvb_buf_setup(struct videobuf_queue *q, return 0; } -static int dvb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static int dvb_buf_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, enum v4l2_field field) { struct cx8802_dev *dev = q->priv_data; return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field); @@ -87,7 +87,8 @@ static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) cx8802_buf_queue(dev, (struct cx88_buffer*)vb); } -static void dvb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +static void dvb_buf_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) { cx88_free_buffer(q, (struct cx88_buffer*)vb); } @@ -109,7 +110,7 @@ static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire) drv = cx8802_get_driver(dev, CX88_MPEG_DVB); if (drv) { - if(acquire) + if (acquire) ret = drv->request_acquire(drv); else ret = drv->request_release(drv); @@ -307,8 +308,7 @@ static struct cx22702_config hauppauge_hvr_config = { .output_mode = CX22702_SERIAL_OUTPUT, }; -static int or51132_set_ts_param(struct dvb_frontend* fe, - int is_punctured) +static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured) { struct cx8802_dev *dev= fe->dvb->priv; dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00; @@ -364,8 +364,7 @@ static struct lgdt330x_config pchdtv_hd5500 = { .set_ts_params = lgdt330x_set_ts_param, }; -static int nxt200x_set_ts_param(struct dvb_frontend* fe, - int is_punctured) +static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured) { struct cx8802_dev *dev= fe->dvb->priv; dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00; @@ -401,11 +400,10 @@ static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe, struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; - if (voltage == SEC_VOLTAGE_OFF) { + if (voltage == SEC_VOLTAGE_OFF) cx_write(MO_GP0_IO, 0x000006fb); - } else { + else cx_write(MO_GP0_IO, 0x000006f9); - } if (core->prev_set_voltage) return core->prev_set_voltage(fe, voltage); @@ -483,8 +481,7 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_fmd1216me); + &dev->core->i2c_adap, &dvb_pll_fmd1216me); } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: @@ -569,8 +566,7 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_PCHDTV_HD3000: - dev->dvb.frontend = dvb_attach(or51132_attach, - &pchdtv_hd3000, + dev->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, @@ -702,8 +698,7 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_fmd1216me); + &dev->core->i2c_adap, &dvb_pll_fmd1216me); } break; case CX88_BOARD_HAUPPAUGE_HVR3000: @@ -712,8 +707,7 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - &dvb_pll_fmd1216me); + &dev->core->i2c_adap, &dvb_pll_fmd1216me); } break; default: -- cgit v0.10.2 From 79a54cbd73afe05d807b2128fa41a92cb4acd637 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 5 Dec 2006 14:20:06 -0300 Subject: V4L/DVB (4948): Cxusb: Convert tuner functions to use dvb_pll_attach Converted dee1601, lgz201 and dtt8579 to use dvb_pll_attach in dvb-usb-cxusb. Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index ae039b9..ee39ebe 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -367,25 +367,21 @@ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap) { - adap->pll_addr = 0x61; - adap->pll_desc = &dvb_pll_thomson_dtt7579; - adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; + dvb_attach(dvb_pll_attach, adap->fe, 0x61, + NULL, &dvb_pll_thomson_dtt7579); return 0; } static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap) { - adap->pll_addr = 0x61; - adap->pll_desc = &dvb_pll_lg_z201; - adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; + dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, &dvb_pll_lg_z201); return 0; } static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap) { - adap->pll_addr = 0x60; - adap->pll_desc = &dvb_pll_thomson_dtt7579; - adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; + dvb_attach(dvb_pll_attach, adap->fe, 0x60, + NULL, &dvb_pll_thomson_dtt7579); return 0; } -- cgit v0.10.2 From f35db23c1b1c4da0642e932a7035b5c577377974 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 5 Dec 2006 14:53:39 -0300 Subject: V4L/DVB (4949): Cxusb: codingstyle cleanups Trivial whitespace / 80-column limit cleanups Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index ee39ebe..15d12fc 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -17,9 +17,9 @@ * Copyright (C) 2006 Michael Krufky (mkrufky@linuxtv.org) * Copyright (C) 2006 Chris Pascoe (c.pascoe@itee.uq.edu.au) * - * 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. + * 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 */ @@ -34,22 +34,22 @@ /* debug */ int dvb_usb_cxusb_debug; -module_param_named(debug,dvb_usb_cxusb_debug, int, 0644); +module_param_named(debug, dvb_usb_cxusb_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); static int cxusb_ctrl_msg(struct dvb_usb_device *d, - u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen) + u8 cmd, u8 *wbuf, int wlen, u8 *rbuf, int rlen) { int wo = (rbuf == NULL || rlen == 0); /* write-only */ u8 sndbuf[1+wlen]; - memset(sndbuf,0,1+wlen); + memset(sndbuf, 0, 1+wlen); sndbuf[0] = cmd; - memcpy(&sndbuf[1],wbuf,wlen); + memcpy(&sndbuf[1], wbuf, wlen); if (wo) - dvb_usb_generic_write(d,sndbuf,1+wlen); + dvb_usb_generic_write(d, sndbuf, 1+wlen); else - dvb_usb_generic_rw(d,sndbuf,1+wlen,rbuf,rlen,0); + dvb_usb_generic_rw(d, sndbuf, 1+wlen, rbuf, rlen, 0); return 0; } @@ -58,14 +58,14 @@ static int cxusb_ctrl_msg(struct dvb_usb_device *d, static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff) { struct cxusb_state *st = d->priv; - u8 o[2],i; + u8 o[2], i; if (st->gpio_write_state[GPIO_TUNER] == onoff) return; o[0] = GPIO_TUNER; o[1] = onoff; - cxusb_ctrl_msg(d,CMD_GPIO_WRITE,o,2,&i,1); + cxusb_ctrl_msg(d, CMD_GPIO_WRITE, o, 2, &i, 1); if (i != 0x01) deb_info("gpio_write failed.\n"); @@ -74,7 +74,8 @@ static void cxusb_gpio_tuner(struct dvb_usb_device *d, int onoff) } /* I2C */ -static int cxusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +static int cxusb_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); int i; @@ -89,12 +90,12 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) if (d->udev->descriptor.idVendor == USB_VID_MEDION) switch (msg[i].addr) { - case 0x63: - cxusb_gpio_tuner(d,0); - break; - default: - cxusb_gpio_tuner(d,1); - break; + case 0x63: + cxusb_gpio_tuner(d, 0); + break; + default: + cxusb_gpio_tuner(d, 1); + break; } /* read request */ @@ -103,26 +104,27 @@ static int cxusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) obuf[0] = msg[i].len; obuf[1] = msg[i+1].len; obuf[2] = msg[i].addr; - memcpy(&obuf[3],msg[i].buf,msg[i].len); + memcpy(&obuf[3], msg[i].buf, msg[i].len); if (cxusb_ctrl_msg(d, CMD_I2C_READ, - obuf, 3+msg[i].len, - ibuf, 1+msg[i+1].len) < 0) + obuf, 3+msg[i].len, + ibuf, 1+msg[i+1].len) < 0) break; if (ibuf[0] != 0x08) deb_i2c("i2c read may have failed\n"); - memcpy(msg[i+1].buf,&ibuf[1],msg[i+1].len); + memcpy(msg[i+1].buf, &ibuf[1], msg[i+1].len); i++; } else { /* write */ u8 obuf[2+msg[i].len], ibuf; obuf[0] = msg[i].addr; obuf[1] = msg[i].len; - memcpy(&obuf[2],msg[i].buf,msg[i].len); + memcpy(&obuf[2], msg[i].buf, msg[i].len); - if (cxusb_ctrl_msg(d,CMD_I2C_WRITE, obuf, 2+msg[i].len, &ibuf,1) < 0) + if (cxusb_ctrl_msg(d, CMD_I2C_WRITE, obuf, + 2+msg[i].len, &ibuf,1) < 0) break; if (ibuf != 0x08) deb_i2c("i2c write may have failed\n"); @@ -326,7 +328,6 @@ static int cxusb_mt352_demod_init(struct dvb_frontend* fe) static struct cx22702_config cxusb_cx22702_config = { .demod_address = 0x63, - .output_mode = CX22702_PARALLEL_OUTPUT, }; @@ -399,7 +400,8 @@ static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, &b, 1); - if ((adap->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, &adap->dev->i2c_adap)) != NULL) + if ((adap->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, + &adap->dev->i2c_adap)) != NULL) return 0; return -EIO; @@ -412,7 +414,8 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_adapter *adap) cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); - if ((adap->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config, &adap->dev->i2c_adap)) != NULL) + if ((adap->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config, + &adap->dev->i2c_adap)) != NULL) return 0; return -EIO; @@ -426,7 +429,8 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_adapter *adap) cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); - if ((adap->fe = dvb_attach(mt352_attach, &cxusb_mt352_config, &adap->dev->i2c_adap)) != NULL) + if ((adap->fe = dvb_attach(mt352_attach, &cxusb_mt352_config, + &adap->dev->i2c_adap)) != NULL) return 0; return -EIO; @@ -439,8 +443,11 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap) cxusb_ctrl_msg(adap->dev, CMD_DIGITAL, NULL, 0, NULL, 0); - if (((adap->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, &adap->dev->i2c_adap)) != NULL) || - ((adap->fe = dvb_attach(zl10353_attach, &cxusb_zl10353_dee1601_config, &adap->dev->i2c_adap)) != NULL)) + if (((adap->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, + &adap->dev->i2c_adap)) != NULL) || + ((adap->fe = dvb_attach(zl10353_attach, + &cxusb_zl10353_dee1601_config, + &adap->dev->i2c_adap)) != NULL)) return 0; return -EIO; @@ -452,7 +459,8 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_adapter *adap) */ #define BLUEBIRD_01_ID_OFFSET 6638 -static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, const struct firmware *fw) +static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, + const struct firmware *fw) { if (fw->size < BLUEBIRD_01_ID_OFFSET + 4) return -EINVAL; @@ -460,10 +468,12 @@ static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, const if (fw->data[BLUEBIRD_01_ID_OFFSET] == (USB_VID_DVICO & 0xff) && fw->data[BLUEBIRD_01_ID_OFFSET + 1] == USB_VID_DVICO >> 8) { - fw->data[BLUEBIRD_01_ID_OFFSET + 2] = udev->descriptor.idProduct + 1; - fw->data[BLUEBIRD_01_ID_OFFSET + 3] = udev->descriptor.idProduct >> 8; + fw->data[BLUEBIRD_01_ID_OFFSET + 2] = + udev->descriptor.idProduct + 1; + fw->data[BLUEBIRD_01_ID_OFFSET + 3] = + udev->descriptor.idProduct >> 8; - return usb_cypress_load_firmware(udev,fw,CYPRESS_FX2); + return usb_cypress_load_firmware(udev, fw, CYPRESS_FX2); } return -EINVAL; @@ -477,7 +487,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_lgz201_properties; static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties; static int cxusb_probe(struct usb_interface *intf, - const struct usb_device_id *id) + const struct usb_device_id *id) { if (dvb_usb_device_init(intf,&cxusb_medion_properties,THIS_MODULE,NULL) == 0 || dvb_usb_device_init(intf,&cxusb_bluebird_lgh064f_properties,THIS_MODULE,NULL) == 0 || @@ -491,20 +501,20 @@ static int cxusb_probe(struct usb_interface *intf, } static struct usb_device_id cxusb_table [] = { - { USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) }, - { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) }, - {} /* Terminating entry */ + { USB_DEVICE(USB_VID_MEDION, USB_PID_MEDION_MD95700) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LG064F_WARM) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_1_WARM) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_LGZ201_WARM) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_TH7579_WARM) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_COLD) }, + { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_2_WARM) }, + {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, cxusb_table); @@ -765,7 +775,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_dtt7579_properties = { static struct usb_driver cxusb_driver = { .name = "dvb_usb_cxusb", .probe = cxusb_probe, - .disconnect = dvb_usb_device_exit, + .disconnect = dvb_usb_device_exit, .id_table = cxusb_table, }; -- cgit v0.10.2 From 67952e8cb111c5b2f9e675dba1bbde4876f9fcf3 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 10 Dec 2006 00:14:21 -0200 Subject: V4L/DVB (4949a): Fix INIT_WORK INIT_WORK syntax changed on kernel 2.6.19. Fixing it on usbvision and cx88-input. Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index c6d412b..8136673 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -158,7 +158,7 @@ static void cx88_ir_work(struct work_struct *work) 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_WORK(&ir->work, cx88_ir_work); init_timer(&ir->timer); ir->timer.function = ir_timer; ir->timer.data = (unsigned long)ir; diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 9163b60..9ff93e7 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -1708,7 +1708,6 @@ int usbvision_power_off(struct usb_usbvision *usbvision) return errCode; } - /* * usbvision_set_video_format() * @@ -2217,14 +2216,15 @@ int usbvision_power_on(struct usb_usbvision *usbvision) */ // to call usbvision_power_off from task queue -static void call_usbvision_power_off(void *_usbvision) +static void call_usbvision_power_off(struct work_struct *work) { - struct usb_usbvision *usbvision = _usbvision; + struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, work); PDEBUG(DBG_FUNC, ""); down_interruptible(&usbvision->lock); if(usbvision->user == 0) { usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_power_off(usbvision); usbvision->initialized = 0; } @@ -2237,7 +2237,7 @@ static void usbvision_powerOffTimer(unsigned long data) PDEBUG(DBG_FUNC, ""); del_timer(&usbvision->powerOffTimer); - INIT_WORK(&usbvision->powerOffWork, call_usbvision_power_off, usbvision); + INIT_WORK(&usbvision->powerOffWork, call_usbvision_power_off); (void) schedule_work(&usbvision->powerOffWork); } diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index 0e7e3d6..782abf4 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -381,6 +381,8 @@ struct usb_usbvision { struct video_device *rdev; /* Radio Device */ struct video_device *vbi; /* VBI Device */ + struct work_struct work; + /* i2c Declaration Section*/ struct i2c_adapter i2c_adap; struct i2c_algo_usb_data i2c_algo; -- cgit v0.10.2 From 1de1bf06330920802d3b7646a088965bdd918356 Mon Sep 17 00:00:00 2001 From: Thierry MERLE Date: Sun, 10 Dec 2006 13:31:04 -0300 Subject: V4L/DVB 4949b: Fix container_of pointer retreival Signed-off-by: Thierry MERLE Signed-off-by: Mauro Carvalho Chehab diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 9ff93e7..797b97b 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -2218,7 +2218,7 @@ int usbvision_power_on(struct usb_usbvision *usbvision) // to call usbvision_power_off from task queue static void call_usbvision_power_off(struct work_struct *work) { - struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, work); + struct usb_usbvision *usbvision = container_of(work, struct usb_usbvision, powerOffWork); PDEBUG(DBG_FUNC, ""); down_interruptible(&usbvision->lock); diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index 782abf4..0e7e3d6 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -381,8 +381,6 @@ struct usb_usbvision { struct video_device *rdev; /* Radio Device */ struct video_device *vbi; /* VBI Device */ - struct work_struct work; - /* i2c Declaration Section*/ struct i2c_adapter i2c_adap; struct i2c_algo_usb_data i2c_algo; -- cgit v0.10.2