diff options
Diffstat (limited to 'drivers/media/video')
215 files changed, 18951 insertions, 5288 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 1832966f53f..fe9a4cc1414 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -270,6 +270,15 @@ config VIDEO_SAA711X To compile this driver as a module, choose M here: the module will be called saa7115. +config VIDEO_SAA717X + tristate "Philips SAA7171/3/4 audio/video decoders" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Philips SAA7171/3/4 audio/video decoders. + + To compile this driver as a module, choose M here: the + module will be called saa717x. + config VIDEO_SAA7191 tristate "Philips SAA7191 video decoder" depends on VIDEO_V4L1 && I2C @@ -689,6 +698,8 @@ source "drivers/media/video/cx88/Kconfig" source "drivers/media/video/cx23885/Kconfig" +source "drivers/media/video/au0828/Kconfig" + source "drivers/media/video/ivtv/Kconfig" config VIDEO_M32R_AR @@ -836,4 +847,49 @@ config USB_STKWEBCAM endif # V4L_USB_DRIVERS +config SOC_CAMERA + tristate "SoC camera support" + depends on VIDEO_V4L2 + select VIDEOBUF_DMA_SG + help + SoC Camera is a common API to several cameras, not connecting + over a bus like PCI or USB. For example some i2c camera connected + directly to the data bus of an SoC. + +config SOC_CAMERA_MT9M001 + tristate "mt9m001 support" + depends on SOC_CAMERA + select GPIO_PCA953X if MT9M001_PCA9536_SWITCH + help + This driver supports MT9M001 cameras from Micron, monochrome + and colour models. + +config MT9M001_PCA9536_SWITCH + bool "pca9536 datawidth switch for mt9m001" + depends on SOC_CAMERA_MT9M001 && GENERIC_GPIO + help + Select this if your MT9M001 camera uses a PCA9536 I2C GPIO + extender to switch between 8 and 10 bit datawidth modes + +config SOC_CAMERA_MT9V022 + tristate "mt9v022 support" + depends on SOC_CAMERA + select GPIO_PCA953X if MT9V022_PCA9536_SWITCH + help + This driver supports MT9V022 cameras from Micron + +config MT9V022_PCA9536_SWITCH + bool "pca9536 datawidth switch for mt9v022" + depends on SOC_CAMERA_MT9V022 && GENERIC_GPIO + help + Select this if your MT9V022 camera uses a PCA9536 I2C GPIO + extender to switch between 8 and 10 bit datawidth modes + +config VIDEO_PXA27x + tristate "PXA27x Quick Capture Interface driver" + depends on VIDEO_DEV && PXA27x + select SOC_CAMERA + ---help--- + This is a v4l2 driver for the PXA27x Quick Capture Interface + endif # VIDEO_CAPTURE_DRIVERS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 3f209b32eea..be14227f372 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -4,7 +4,7 @@ zr36067-objs := zoran_procfs.o zoran_device.o \ zoran_driver.o zoran_card.o -tuner-objs := tuner-core.o tuner-types.o +tuner-objs := tuner-core.o msp3400-objs := msp3400-driver.o msp3400-kthreads.o @@ -38,6 +38,7 @@ obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o obj-$(CONFIG_VIDEO_SAA7111) += saa7111.o obj-$(CONFIG_VIDEO_SAA7114) += saa7114.o obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o +obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o @@ -87,6 +88,8 @@ obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_TUNER_XC2028) += tuner-xc2028.o obj-$(CONFIG_TUNER_SIMPLE) += tuner-simple.o +# tuner-types will be merged into tuner-simple, in the future +obj-$(CONFIG_TUNER_SIMPLE) += tuner-types.o obj-$(CONFIG_TUNER_MT20XX) += mt20xx.o obj-$(CONFIG_TUNER_TDA8290) += tda8290.o obj-$(CONFIG_TUNER_TEA5767) += tea5767.o @@ -135,5 +138,12 @@ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ +obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o +obj-$(CONFIG_SOC_CAMERA) += soc_camera.o +obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o +obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o + +obj-$(CONFIG_VIDEO_AU0828) += au0828/ + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c index fea2e723e34..f794f2dbfb3 100644 --- a/drivers/media/video/adv7170.c +++ b/drivers/media/video/adv7170.c @@ -56,7 +56,7 @@ MODULE_LICENSE("GPL"); #define I2C_NAME(x) (x)->name -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c index 10d4d89623f..8ee07a68f70 100644 --- a/drivers/media/video/adv7175.c +++ b/drivers/media/video/adv7175.c @@ -52,7 +52,7 @@ MODULE_LICENSE("GPL"); #define I2C_NAME(s) (s)->name -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c index c94a4d0f280..8c7d1958856 100644 --- a/drivers/media/video/arv.c +++ b/drivers/media/video/arv.c @@ -125,8 +125,8 @@ static unsigned char yuv[MAX_AR_FRAME_BYTES]; /* default frequency */ #define DEFAULT_FREQ 50 /* 50 or 75 (MHz) is available as BCLK */ static int freq = DEFAULT_FREQ; /* BCLK: available 50 or 70 (MHz) */ -static int vga = 0; /* default mode(0:QVGA mode, other:VGA mode) */ -static int vga_interlace = 0; /* 0 is normal mode for, else interlace mode */ +static int vga; /* default mode(0:QVGA mode, other:VGA mode) */ +static int vga_interlace; /* 0 is normal mode for, else interlace mode */ module_param(freq, int, 0); module_param(vga, int, 0); module_param(vga_interlace, int, 0); @@ -747,7 +747,9 @@ static const struct file_operations ar_fops = { .release = video_exclusive_release, .read = ar_read, .ioctl = ar_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, }; diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig new file mode 100644 index 00000000000..c97c4bd2484 --- /dev/null +++ b/drivers/media/video/au0828/Kconfig @@ -0,0 +1,12 @@ + +config VIDEO_AU0828 + tristate "Auvitek AU0828 support" + depends on VIDEO_DEV && I2C && INPUT + select I2C_ALGOBIT + select DVB_AU8522 if !DVB_FE_CUSTOMIZE + select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE + ---help--- + This is a video4linux driver for Auvitek's USB device. + + To compile this driver as a module, choose M here: the + module will be called au0828 diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile new file mode 100644 index 00000000000..9f4f572c89c --- /dev/null +++ b/drivers/media/video/au0828/Makefile @@ -0,0 +1,9 @@ +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o + +obj-$(CONFIG_VIDEO_AU0828) += au0828.o + +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends + +EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c new file mode 100644 index 00000000000..8ca91f81427 --- /dev/null +++ b/drivers/media/video/au0828/au0828-cards.c @@ -0,0 +1,182 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * 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 "au0828.h" +#include "au0828-cards.h" + +struct au0828_board au0828_boards[] = { + [AU0828_BOARD_UNKNOWN] = { + .name = "Unknown board", + }, + [AU0828_BOARD_HAUPPAUGE_HVR850] = { + .name = "Hauppauge HVR850", + }, + [AU0828_BOARD_HAUPPAUGE_HVR950Q] = { + .name = "Hauppauge HVR950Q", + }, + [AU0828_BOARD_DVICO_FUSIONHDTV7] = { + .name = "DViCO FusionHDTV USB", + }, +}; +const unsigned int au0828_bcount = ARRAY_SIZE(au0828_boards); + +/* Tuner callback function for au0828 boards. Currently only needed + * for HVR1500Q, which has an xc5000 tuner. + */ +int au0828_tuner_callback(void *priv, int command, int arg) +{ + struct au0828_dev *dev = priv; + + dprintk(1, "%s()\n", __func__); + + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_DVICO_FUSIONHDTV7: + if (command == 0) { + /* Tuner Reset Command from xc5000 */ + /* Drive the tuner into reset and out */ + au0828_clear(dev, REG_001, 2); + mdelay(200); + au0828_set(dev, REG_001, 2); + mdelay(50); + return 0; + } else { + printk(KERN_ERR + "%s(): Unknown command.\n", __func__); + return -EINVAL; + } + break; + } + + return 0; /* Should never be here */ +} + +static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) +{ + struct tveeprom tv; + + tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data); + + /* Make sure we support the board model */ + switch (tv.model) { + case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */ + case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and basic analog video */ + break; + default: + printk(KERN_WARNING "%s: warning: " + "unknown hauppauge model #%d\n", __func__, tv.model); + break; + } + + printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", + __func__, tv.model); +} + +void au0828_card_setup(struct au0828_dev *dev) +{ + static u8 eeprom[256]; + + dprintk(1, "%s()\n", __func__); + + if (dev->i2c_rc == 0) { + dev->i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom)); + } + + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + if (dev->i2c_rc == 0) + hauppauge_eeprom(dev, eeprom+0xa0); + break; + } +} + +/* + * The bridge has between 8 and 12 gpios. + * Regs 1 and 0 deal with output enables. + * Regs 3 and 2 deal with direction. + */ +void au0828_gpio_setup(struct au0828_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + /* GPIO's + * 4 - CS5340 + * 5 - AU8522 Demodulator + * 6 - eeprom W/P + * 9 - XC5000 Tuner + */ + + /* Into reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0x88 | 0x20); + au0828_write(dev, REG_001, 0x0); + au0828_write(dev, REG_000, 0x0); + msleep(100); + + /* Out of reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_001, 0x02); + au0828_write(dev, REG_002, 0x88 | 0x20); + au0828_write(dev, REG_000, 0x88 | 0x20 | 0x40); + msleep(250); + break; + case AU0828_BOARD_DVICO_FUSIONHDTV7: + /* GPIO's + * 6 - ? + * 8 - AU8522 Demodulator + * 9 - XC5000 Tuner + */ + + /* Into reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0xa0); + au0828_write(dev, REG_001, 0x0); + au0828_write(dev, REG_000, 0x0); + msleep(100); + + /* Out of reset */ + au0828_write(dev, REG_003, 0x02); + au0828_write(dev, REG_002, 0xa0); + au0828_write(dev, REG_001, 0x02); + au0828_write(dev, REG_000, 0xa0); + msleep(250); + break; + } +} + +/* table of devices that work with this driver */ +struct usb_device_id au0828_usb_id_table [] = { + { USB_DEVICE(0x2040, 0x7200), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q }, + { USB_DEVICE(0x2040, 0x7240), + .driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 }, + { USB_DEVICE(0x0fe9, 0xd620), + .driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 }, + { }, +}; + +MODULE_DEVICE_TABLE(usb, au0828_usb_id_table); diff --git a/drivers/media/video/au0828/au0828-cards.h b/drivers/media/video/au0828/au0828-cards.h new file mode 100644 index 00000000000..e26f54a961d --- /dev/null +++ b/drivers/media/video/au0828/au0828-cards.h @@ -0,0 +1,25 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * 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. + */ + +#define AU0828_BOARD_UNKNOWN 0 +#define AU0828_BOARD_HAUPPAUGE_HVR950Q 1 +#define AU0828_BOARD_HAUPPAUGE_HVR850 2 +#define AU0828_BOARD_DVICO_FUSIONHDTV7 3 diff --git a/drivers/media/video/au0828/au0828-core.c b/drivers/media/video/au0828/au0828-core.c new file mode 100644 index 00000000000..e65d5642cb1 --- /dev/null +++ b/drivers/media/video/au0828/au0828-core.c @@ -0,0 +1,270 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <linux/mutex.h> + +#include "au0828.h" + +/* + * 1 = General debug messages + * 2 = USB handling + * 4 = I2C related + * 8 = Bridge related + */ +unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +unsigned int usb_debug; +module_param(usb_debug, int, 0644); +MODULE_PARM_DESC(usb_debug, "enable usb debug messages"); + +unsigned int bridge_debug; +module_param(bridge_debug, int, 0644); +MODULE_PARM_DESC(bridge_debug, "enable bridge debug messages"); + +#define _AU0828_BULKPIPE 0x03 +#define _BULKPIPESIZE 0xffff + +static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size); +static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size); + +/* USB Direction */ +#define CMD_REQUEST_IN 0x00 +#define CMD_REQUEST_OUT 0x01 + +u32 au0828_readreg(struct au0828_dev *dev, u16 reg) +{ + recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, dev->ctrlmsg, 1); + dprintk(8, "%s(0x%x) = 0x%x\n", __func__, reg, dev->ctrlmsg[0]); + return dev->ctrlmsg[0]; +} + +u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val) +{ + dprintk(8, "%s(0x%x, 0x%x)\n", __func__, reg, val); + return send_control_msg(dev, CMD_REQUEST_OUT, val, reg, + dev->ctrlmsg, 0); +} + +static void cmd_msg_dump(struct au0828_dev *dev) +{ + int i; + + for (i = 0; i < sizeof(dev->ctrlmsg); i += 16) + dprintk(2, "%s() %02x %02x %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x %02x %02x %02x\n", + __func__, + dev->ctrlmsg[i+0], dev->ctrlmsg[i+1], + dev->ctrlmsg[i+2], dev->ctrlmsg[i+3], + dev->ctrlmsg[i+4], dev->ctrlmsg[i+5], + dev->ctrlmsg[i+6], dev->ctrlmsg[i+7], + dev->ctrlmsg[i+8], dev->ctrlmsg[i+9], + dev->ctrlmsg[i+10], dev->ctrlmsg[i+11], + dev->ctrlmsg[i+12], dev->ctrlmsg[i+13], + dev->ctrlmsg[i+14], dev->ctrlmsg[i+15]); +} + +static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size) +{ + int status = -ENODEV; + mutex_lock(&dev->mutex); + if (dev->usbdev) { + + /* cp must be memory that has been allocated by kmalloc */ + status = usb_control_msg(dev->usbdev, + usb_sndctrlpipe(dev->usbdev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + cp, size, 1000); + + status = min(status, 0); + + if (status < 0) { + printk(KERN_ERR "%s() Failed sending control message, error %d.\n", + __func__, status); + } + + } + mutex_unlock(&dev->mutex); + return status; +} + +static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, + u16 index, unsigned char *cp, u16 size) +{ + int status = -ENODEV; + mutex_lock(&dev->mutex); + if (dev->usbdev) { + + memset(dev->ctrlmsg, 0, sizeof(dev->ctrlmsg)); + + /* cp must be memory that has been allocated by kmalloc */ + status = usb_control_msg(dev->usbdev, + usb_rcvctrlpipe(dev->usbdev, 0), + request, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, + cp, size, 1000); + + status = min(status, 0); + + if (status < 0) { + printk(KERN_ERR "%s() Failed receiving control message, error %d.\n", + __func__, status); + } else + cmd_msg_dump(dev); + } + mutex_unlock(&dev->mutex); + return status; +} + +static void au0828_usb_disconnect(struct usb_interface *interface) +{ + struct au0828_dev *dev = usb_get_intfdata(interface); + + dprintk(1, "%s()\n", __func__); + + /* Digital TV */ + au0828_dvb_unregister(dev); + + /* I2C */ + au0828_i2c_unregister(dev); + + usb_set_intfdata(interface, NULL); + + mutex_lock(&dev->mutex); + dev->usbdev = NULL; + mutex_unlock(&dev->mutex); + + kfree(dev); + +} + +static int au0828_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int ifnum; + struct au0828_dev *dev; + struct usb_device *usbdev = interface_to_usbdev(interface); + + ifnum = interface->altsetting->desc.bInterfaceNumber; + + if (ifnum != 0) + return -ENODEV; + + dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__, + le16_to_cpu(usbdev->descriptor.idVendor), + le16_to_cpu(usbdev->descriptor.idProduct), + ifnum); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + printk(KERN_ERR "%s() Unable to allocate memory\n", __func__); + return -ENOMEM; + } + + mutex_init(&dev->mutex); + mutex_init(&dev->dvb.lock); + dev->usbdev = usbdev; + dev->board = id->driver_info; + + usb_set_intfdata(interface, dev); + + /* Power Up the bridge */ + au0828_write(dev, REG_600, 1 << 4); + + /* Bring up the GPIO's and supporting devices */ + au0828_gpio_setup(dev); + + /* I2C */ + au0828_i2c_register(dev); + + /* Setup */ + au0828_card_setup(dev); + + /* Digital TV */ + au0828_dvb_register(dev); + + printk(KERN_INFO "Registered device AU0828 [%s]\n", + au0828_boards[dev->board].name == NULL ? "Unset" : + au0828_boards[dev->board].name); + + return 0; +} + +static struct usb_driver au0828_usb_driver = { + .name = DRIVER_NAME, + .probe = au0828_usb_probe, + .disconnect = au0828_usb_disconnect, + .id_table = au0828_usb_id_table, +}; + +static int __init au0828_init(void) +{ + int ret; + + if (debug) + printk(KERN_INFO "%s() Debugging is enabled\n", __func__); + + if (usb_debug) { + printk(KERN_INFO "%s() USB Debugging is enabled\n", __func__); + debug |= 2; + } + + if (i2c_debug) { + printk(KERN_INFO "%s() I2C Debugging is enabled\n", __func__); + debug |= 4; + } + + if (bridge_debug) { + printk(KERN_INFO "%s() Bridge Debugging is enabled\n", + __func__); + debug |= 8; + } + + printk(KERN_INFO "au0828 driver loaded\n"); + + ret = usb_register(&au0828_usb_driver); + if (ret) + printk(KERN_ERR "usb_register failed, error = %d\n", ret); + + return ret; +} + +static void __exit au0828_exit(void) +{ + usb_deregister(&au0828_usb_driver); +} + +module_init(au0828_init); +module_exit(au0828_exit); + +MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products"); +MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c new file mode 100644 index 00000000000..85d0ae9a322 --- /dev/null +++ b/drivers/media/video/au0828/au0828-dvb.c @@ -0,0 +1,373 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/suspend.h> +#include <media/v4l2-common.h> + +#include "au0828.h" +#include "au8522.h" +#include "xc5000.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define _AU0828_BULKPIPE 0x83 +#define _BULKPIPESIZE 0xe522 + +static struct au8522_config hauppauge_hvr950q_config = { + .demod_address = 0x8e >> 1, + .status_mode = AU8522_DEMODLOCKING, +}; + +static struct xc5000_config hauppauge_hvr950q_tunerconfig = { + .i2c_address = 0x61, + .if_khz = 6000, + .tuner_callback = au0828_tuner_callback +}; + +/*-------------------------------------------------------------------*/ +static void urb_completion(struct urb *purb) +{ + u8 *ptr; + struct au0828_dev *dev = purb->context; + int ptype = usb_pipetype(purb->pipe); + + dprintk(2, "%s()\n", __func__); + + if (!dev) + return; + + if (dev->urb_streaming == 0) + return; + + if (ptype != PIPE_BULK) { + printk(KERN_ERR "%s() Unsupported URB type %d\n", + __func__, ptype); + return; + } + + ptr = (u8 *)purb->transfer_buffer; + + /* Feed the transport payload into the kernel demux */ + dvb_dmx_swfilter_packets(&dev->dvb.demux, + purb->transfer_buffer, purb->actual_length / 188); + + /* Clean the buffer before we requeue */ + memset(purb->transfer_buffer, 0, URB_BUFSIZE); + + /* Requeue URB */ + usb_submit_urb(purb, GFP_ATOMIC); +} + +static int stop_urb_transfer(struct au0828_dev *dev) +{ + int i; + + dprintk(2, "%s()\n", __func__); + + for (i = 0; i < URB_COUNT; i++) { + usb_kill_urb(dev->urbs[i]); + kfree(dev->urbs[i]->transfer_buffer); + usb_free_urb(dev->urbs[i]); + } + + dev->urb_streaming = 0; + + return 0; +} + +static int start_urb_transfer(struct au0828_dev *dev) +{ + struct urb *purb; + int i, ret = -ENOMEM; + + dprintk(2, "%s()\n", __func__); + + if (dev->urb_streaming) { + dprintk(2, "%s: iso xfer already running!\n", __func__); + return 0; + } + + for (i = 0; i < URB_COUNT; i++) { + + dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->urbs[i]) + goto err; + + purb = dev->urbs[i]; + + purb->transfer_buffer = kzalloc(URB_BUFSIZE, GFP_KERNEL); + if (!purb->transfer_buffer) { + usb_free_urb(purb); + dev->urbs[i] = 0; + goto err; + } + + purb->status = -EINPROGRESS; + usb_fill_bulk_urb(purb, + dev->usbdev, + usb_rcvbulkpipe(dev->usbdev, _AU0828_BULKPIPE), + purb->transfer_buffer, + URB_BUFSIZE, + urb_completion, + dev); + + } + + for (i = 0; i < URB_COUNT; i++) { + ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC); + if (ret != 0) { + stop_urb_transfer(dev); + printk(KERN_ERR "%s: failed urb submission, " + "err = %d\n", __func__, ret); + return ret; + } + } + + dev->urb_streaming = 1; + ret = 0; + +err: + return ret; +} + +static int au0828_dvb_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct au0828_dev *dev = (struct au0828_dev *) demux->priv; + struct au0828_dvb *dvb = &dev->dvb; + int ret = 0; + + dprintk(1, "%s()\n", __func__); + + if (!demux->dmx.frontend) + return -EINVAL; + + if (dvb) { + mutex_lock(&dvb->lock); + if (dvb->feeding++ == 0) { + /* Start transport */ + au0828_write(dev, 0x608, 0x90); + au0828_write(dev, 0x609, 0x72); + au0828_write(dev, 0x60a, 0x71); + au0828_write(dev, 0x60b, 0x01); + ret = start_urb_transfer(dev); + } + mutex_unlock(&dvb->lock); + } + + return ret; +} + +static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct au0828_dev *dev = (struct au0828_dev *) demux->priv; + struct au0828_dvb *dvb = &dev->dvb; + int ret = 0; + + dprintk(1, "%s()\n", __func__); + + if (dvb) { + mutex_lock(&dvb->lock); + if (--dvb->feeding == 0) { + /* Stop transport */ + au0828_write(dev, 0x608, 0x00); + au0828_write(dev, 0x609, 0x00); + au0828_write(dev, 0x60a, 0x00); + au0828_write(dev, 0x60b, 0x00); + ret = stop_urb_transfer(dev); + } + mutex_unlock(&dvb->lock); + } + + return ret; +} + +int dvb_register(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int result; + + dprintk(1, "%s()\n", __func__); + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE, + &dev->usbdev->dev, adapter_nr); + if (result < 0) { + printk(KERN_ERR "%s: dvb_register_adapter failed " + "(errno = %d)\n", DRIVER_NAME, result); + goto fail_adapter; + } + dvb->adapter.priv = dev; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (result < 0) { + printk(KERN_ERR "%s: dvb_register_frontend failed " + "(errno = %d)\n", DRIVER_NAME, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dev; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = au0828_dvb_start_feed; + dvb->demux.stop_feed = au0828_dvb_stop_feed; + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (result < 0) { + printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_ERR "%s: add_frontend failed " + "(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_ERR "%s: add_frontend failed " + "(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n", + DRIVER_NAME, result); + goto fail_fe_conn; + } + + /* register network adapter */ + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +fail_adapter: + return result; +} + +void au0828_dvb_unregister(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + + dprintk(1, "%s()\n", __func__); + + if (dvb->frontend == NULL) + return; + + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +} + +/* All the DVB attach calls go here, this function get's modified + * for each new card. No other function in this file needs + * to change. + */ +int au0828_dvb_register(struct au0828_dev *dev) +{ + struct au0828_dvb *dvb = &dev->dvb; + int ret; + + dprintk(1, "%s()\n", __func__); + + /* init frontend */ + switch (dev->board) { + case AU0828_BOARD_HAUPPAUGE_HVR850: + case AU0828_BOARD_HAUPPAUGE_HVR950Q: + case AU0828_BOARD_DVICO_FUSIONHDTV7: + dvb->frontend = dvb_attach(au8522_attach, + &hauppauge_hvr950q_config, + &dev->i2c_adap); + if (dvb->frontend != NULL) { + hauppauge_hvr950q_tunerconfig.priv = dev; + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_hvr950q_tunerconfig); + } + break; + default: + printk(KERN_WARNING "The frontend of your DVB/ATSC card " + "isn't supported yet\n"); + break; + } + if (NULL == dvb->frontend) { + printk(KERN_ERR "%s() Frontend initialization failed\n", + __func__); + return -1; + } + + /* Put the analog decoder in standby to keep it quiet */ + au0828_call_i2c_clients(dev, TUNER_SET_STANDBY, NULL); + + if (dvb->frontend->ops.analog_ops.standby) + dvb->frontend->ops.analog_ops.standby(dvb->frontend); + + /* register everything */ + ret = dvb_register(dev); + if (ret < 0) { + if (dvb->frontend->ops.release) + dvb->frontend->ops.release(dvb->frontend); + return ret; + } + + return 0; +} diff --git a/drivers/media/video/au0828/au0828-i2c.c b/drivers/media/video/au0828/au0828-i2c.c new file mode 100644 index 00000000000..94c8b74a665 --- /dev/null +++ b/drivers/media/video/au0828/au0828-i2c.c @@ -0,0 +1,385 @@ +/* + * Driver for the Auvitek AU0828 USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include "au0828.h" + +#include <media/v4l2-common.h> + +unsigned int i2c_debug; +module_param(i2c_debug, int, 0444); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +unsigned int i2c_scan; +module_param(i2c_scan, int, 0444); +MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); + +#define I2C_WAIT_DELAY 512 +#define I2C_WAIT_RETRY 64 + +static inline int i2c_slave_did_write_ack(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, REG_201) & 0x08 ? 0 : 1; +} + +static inline int i2c_slave_did_read_ack(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, REG_201) & 0x02 ? 0 : 1; +} + +static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_slave_did_read_ack(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +static inline int i2c_is_read_busy(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, REG_201) & 0x01 ? 0 : 1; +} + +static int i2c_wait_read_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_is_read_busy(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +static inline int i2c_is_write_done(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, REG_201) & 0x04 ? 1 : 0; +} + +static int i2c_wait_write_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (i2c_is_write_done(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +static inline int i2c_is_busy(struct i2c_adapter *i2c_adap) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + return au0828_read(dev, REG_201) & 0x10 ? 1 : 0; +} + +static int i2c_wait_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_is_busy(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +/* FIXME: Implement join handling correctly */ +static int i2c_sendbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined_rlen) +{ + int i, strobe = 0; + struct au0828_dev *dev = i2c_adap->algo_data; + + dprintk(4, "%s()\n", __func__); + + au0828_write(dev, REG_2FF, 0x01); + au0828_write(dev, REG_202, 0x07); + + /* Hardware needs 8 bit addresses */ + au0828_write(dev, REG_203, msg->addr << 1); + + dprintk(4, "SEND: %02x\n", msg->addr); + + for (i = 0; i < msg->len;) { + + dprintk(4, " %02x\n", msg->buf[i]); + + au0828_write(dev, REG_205, msg->buf[i]); + + strobe++; + i++; + + if ((strobe >= 4) || (i >= msg->len)) { + + /* Strobe the byte into the bus */ + if (i < msg->len) + au0828_write(dev, REG_200, 0x41); + else + au0828_write(dev, REG_200, 0x01); + + /* Reset strobe trigger */ + strobe = 0; + + if (!i2c_wait_write_done(i2c_adap)) + return -EIO; + + } + + } + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + dprintk(4, "\n"); + + return msg->len; +} + +/* FIXME: Implement join handling correctly */ +static int i2c_readbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined) +{ + struct au0828_dev *dev = i2c_adap->algo_data; + int i; + + dprintk(4, "%s()\n", __func__); + + au0828_write(dev, REG_2FF, 0x01); + au0828_write(dev, REG_202, 0x07); + + /* Hardware needs 8 bit addresses */ + au0828_write(dev, REG_203, msg->addr << 1); + + dprintk(4, " RECV:\n"); + + /* Deal with i2c_scan */ + if (msg->len == 0) { + au0828_write(dev, REG_200, 0x20); + if (i2c_wait_read_ack(i2c_adap)) + return -EIO; + return 0; + } + + for (i = 0; i < msg->len;) { + + i++; + + if (i < msg->len) + au0828_write(dev, REG_200, 0x60); + else + au0828_write(dev, REG_200, 0x20); + + if (!i2c_wait_read_done(i2c_adap)) + return -EIO; + + msg->buf[i-1] = au0828_read(dev, REG_209) & 0xff; + + dprintk(4, " %02x\n", msg->buf[i-1]); + } + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + dprintk(4, "\n"); + + return msg->len; +} + +static int i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + int i, retval = 0; + + dprintk(4, "%s(num = %d)\n", __func__, num); + + for (i = 0; i < num; i++) { + dprintk(4, "%s(num = %d) addr = 0x%02x len = 0x%x\n", + __func__, num, msgs[i].addr, msgs[i].len); + if (msgs[i].flags & I2C_M_RD) { + /* read */ + retval = i2c_readbytes(i2c_adap, &msgs[i], 0); + } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* write then read from same address */ + retval = i2c_sendbytes(i2c_adap, &msgs[i], + msgs[i + 1].len); + if (retval < 0) + goto err; + i++; + retval = i2c_readbytes(i2c_adap, &msgs[i], 1); + } else { + /* write */ + retval = i2c_sendbytes(i2c_adap, &msgs[i], 0); + } + if (retval < 0) + goto err; + } + return num; + +err: + return retval; +} + +static int attach_inform(struct i2c_client *client) +{ + dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", + client->driver->driver.name, client->addr, client->name); + + if (!client->driver->command) + return 0; + + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + dprintk(1, "i2c detach [client=%s]\n", client->name); + + return 0; +} + +void au0828_call_i2c_clients(struct au0828_dev *dev, + unsigned int cmd, void *arg) +{ + if (dev->i2c_rc != 0) + return; + + i2c_clients_command(&dev->i2c_adap, cmd, arg); +} + +static u32 au0828_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; +} + +static struct i2c_algorithm au0828_i2c_algo_template = { + .master_xfer = i2c_xfer, + .functionality = au0828_functionality, +}; + +/* ----------------------------------------------------------------------- */ + +static struct i2c_adapter au0828_i2c_adap_template = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .id = I2C_HW_B_AU0828, + .algo = &au0828_i2c_algo_template, + .class = I2C_CLASS_TV_ANALOG, + .client_register = attach_inform, + .client_unregister = detach_inform, +}; + +static struct i2c_client au0828_i2c_client_template = { + .name = "au0828 internal", +}; + +static char *i2c_devs[128] = { + [0x8e >> 1] = "au8522", + [0xa0 >> 1] = "eeprom", + [0xc2 >> 1] = "tuner/xc5000", +}; + +static void do_i2c_scan(char *name, struct i2c_client *c) +{ + unsigned char buf; + int i, rc; + + for (i = 0; i < 128; i++) { + c->addr = i; + rc = i2c_master_recv(c, &buf, 0); + if (rc < 0) + continue; + printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n", + name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + } +} + +/* init + register i2c algo-bit adapter */ +int au0828_i2c_register(struct au0828_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + memcpy(&dev->i2c_adap, &au0828_i2c_adap_template, + sizeof(dev->i2c_adap)); + memcpy(&dev->i2c_algo, &au0828_i2c_algo_template, + sizeof(dev->i2c_algo)); + memcpy(&dev->i2c_client, &au0828_i2c_client_template, + sizeof(dev->i2c_client)); + + dev->i2c_adap.dev.parent = &dev->usbdev->dev; + + strlcpy(dev->i2c_adap.name, DRIVER_NAME, + sizeof(dev->i2c_adap.name)); + + dev->i2c_algo.data = dev; + dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, dev); + i2c_add_adapter(&dev->i2c_adap); + + dev->i2c_client.adapter = &dev->i2c_adap; + + if (0 == dev->i2c_rc) { + printk(KERN_INFO "%s: i2c bus registered\n", DRIVER_NAME); + if (i2c_scan) + do_i2c_scan(DRIVER_NAME, &dev->i2c_client); + } else + printk(KERN_INFO "%s: i2c bus register FAILED\n", DRIVER_NAME); + + return dev->i2c_rc; +} + +int au0828_i2c_unregister(struct au0828_dev *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} + diff --git a/drivers/media/video/au0828/au0828-reg.h b/drivers/media/video/au0828/au0828-reg.h new file mode 100644 index 00000000000..39827550891 --- /dev/null +++ b/drivers/media/video/au0828/au0828-reg.h @@ -0,0 +1,38 @@ +/* + * Driver for the Auvitek USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * 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. + */ + +/* We'll start to rename these registers once we have a better + * understanding of their meaning. + */ +#define REG_000 0x000 +#define REG_001 0x001 +#define REG_002 0x002 +#define REG_003 0x003 + +#define REG_200 0x200 +#define REG_201 0x201 +#define REG_202 0x202 +#define REG_203 0x203 +#define REG_205 0x205 +#define REG_209 0x209 +#define REG_2FF 0x2ff + +#define REG_600 0x600 diff --git a/drivers/media/video/au0828/au0828.h b/drivers/media/video/au0828/au0828.h new file mode 100644 index 00000000000..0200b9fc5dc --- /dev/null +++ b/drivers/media/video/au0828/au0828.h @@ -0,0 +1,128 @@ +/* + * Driver for the Auvitek AU0828 USB bridge + * + * Copyright (c) 2008 Steven Toth <stoth@hauppauge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <media/tveeprom.h> + +/* DVB */ +#include "demux.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_net.h" +#include "dvbdev.h" + +#include "au0828-reg.h" +#include "au0828-cards.h" + +#define DRIVER_NAME "au0828" +#define URB_COUNT 16 +#define URB_BUFSIZE (0xe522) + +struct au0828_board { + char *name; +}; + +struct au0828_dvb { + struct mutex lock; + struct dvb_adapter adapter; + struct dvb_frontend *frontend; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; + int feeding; +}; + +struct au0828_dev { + struct mutex mutex; + struct usb_device *usbdev; + int board; + u8 ctrlmsg[64]; + + /* I2C */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + u32 i2c_rc; + + /* Digital */ + struct au0828_dvb dvb; + + /* USB / URB Related */ + int urb_streaming; + struct urb *urbs[URB_COUNT]; + +}; + +struct au0828_buff { + struct au0828_dev *dev; + struct urb *purb; + struct list_head buff_list; +}; + +/* ----------------------------------------------------------- */ +#define au0828_read(dev, reg) au0828_readreg(dev, reg) +#define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value) +#define au0828_andor(dev, reg, mask, value) \ + au0828_writereg(dev, reg, \ + (au0828_readreg(dev, reg) & ~(mask)) | ((value) & (mask))) + +#define au0828_set(dev, reg, bit) au0828_andor(dev, (reg), (bit), (bit)) +#define au0828_clear(dev, reg, bit) au0828_andor(dev, (reg), (bit), 0) + +/* ----------------------------------------------------------- */ +/* au0828-core.c */ +extern u32 au0828_read(struct au0828_dev *dev, u16 reg); +extern u32 au0828_write(struct au0828_dev *dev, u16 reg, u32 val); +extern unsigned int debug; +extern unsigned int usb_debug; +extern unsigned int bridge_debug; + +/* ----------------------------------------------------------- */ +/* au0828-cards.c */ +extern struct au0828_board au0828_boards[]; +extern struct usb_device_id au0828_usb_id_table[]; +extern const unsigned int au0828_bcount; +extern void au0828_gpio_setup(struct au0828_dev *dev); +extern int au0828_tuner_callback(void *priv, int command, int arg); +extern void au0828_card_setup(struct au0828_dev *dev); + +/* ----------------------------------------------------------- */ +/* au0828-i2c.c */ +extern int au0828_i2c_register(struct au0828_dev *dev); +extern int au0828_i2c_unregister(struct au0828_dev *dev); +extern void au0828_call_i2c_clients(struct au0828_dev *dev, + unsigned int cmd, void *arg); +extern unsigned int i2c_debug; + +/* ----------------------------------------------------------- */ +/* au0828-dvb.c */ +extern int au0828_dvb_register(struct au0828_dev *dev); +extern void au0828_dvb_unregister(struct au0828_dev *dev); + +#define dprintk(level, fmt, arg...)\ + do { if (debug & level)\ + printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\ + } while (0) diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index e663cc045c4..8bfd5c75cb3 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -57,7 +57,7 @@ MODULE_LICENSE("GPL"); #define I2C_NAME(s) (s)->name -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c index 7dee2e3235a..98ee2d8feb3 100644 --- a/drivers/media/video/bt856.c +++ b/drivers/media/video/bt856.c @@ -56,7 +56,7 @@ MODULE_LICENSE("GPL"); #define I2C_NAME(s) (s)->name -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 7374c02dd18..f20a01cfc73 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -71,6 +71,8 @@ static void kodicom4400r_init(struct bttv *btv); static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input); static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input); +static void geovision_muxsel(struct bttv *btv, unsigned int input); + static int terratec_active_radio_upgrade(struct bttv *btv); static int tea5757_read(struct bttv *btv); static int tea5757_write(struct bttv *btv, int value); @@ -301,6 +303,7 @@ static struct CARD { { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" }, { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini "}, { 0xd200dbc0, BTTV_BOARD_DVICO_FUSIONHDTV_2, "DViCO FusionHDTV 2" }, + { 0x763c008a, BTTV_BOARD_GEOVISION_GV600, "GeoVision GV-600" }, { 0, -1, NULL } }; @@ -576,6 +579,8 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_WINVIEW_601] = { .name = "Leadtek WinView 601", @@ -2322,7 +2327,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .muxsel = { 2, 3, 1, 0 }, - .tuner_type = TUNER_PHILIPS_ATSC, + .tuner_type = TUNER_PHILIPS_FCV1236D, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_dvb = 1, @@ -2961,7 +2966,7 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_DVICO_FUSIONHDTV_2] = { .name = "DViCO FusionHDTV 2", .tuner = 0, - .tuner_type = TUNER_PHILIPS_ATSC, /* FCV1236D */ + .tuner_type = TUNER_PHILIPS_FCV1236D, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .video_inputs = 3, @@ -2992,6 +2997,45 @@ struct tvcard bttv_tvcards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, + [BTTV_BOARD_GEOVISION_GV600] = { + /* emhn@usb.ve */ + .name = "Geovision GV-600", + .video_inputs = 16, + .audio_inputs = 0, + .tuner = UNSET, + .svhs = UNSET, + .gpiomask = 0x0, + .muxsel = { 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2 }, + .muxsel_hook = geovision_muxsel, + .gpiomux = { 0 }, + .no_msp34xx = 1, + .pll = PLL_28, + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, + [BTTV_BOARD_KOZUMI_KTV_01C] = { + /* Mauro Lacy <mauro@lacy.com.ar> + * Based on MagicTV and Conceptronic CONTVFMi */ + + .name = "Kozumi KTV-01C", + .video_inputs = 3, + .audio_inputs = 1, + .tuner = 0, + .svhs = 2, + .gpiomask = 0x008007, + .muxsel = { 2, 3, 1, 1 }, + .gpiomux = { 0, 1, 2, 2 }, /* CONTVFMi */ + .gpiomute = 3, /* CONTVFMi */ + .needs_tvaudio = 0, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MK3 */ + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .pll = PLL_28, + .has_radio = 1, + .has_remote = 1, + }, }; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -3331,6 +3375,13 @@ static void sigmaSLC_muxsel(struct bttv *btv, unsigned int input) gpio_bits( 3<<9, inmux<<9 ); } +static void geovision_muxsel(struct bttv *btv, unsigned int input) +{ + unsigned int inmux = input % 16; + gpio_inout(0xf, 0xf); + gpio_bits(0xf, inmux); +} + /* ----------------------------------------------------------------------- */ static void bttv_reset_audio(struct bttv *btv) diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index fcf8f2d208a..2ca3e9cfb2b 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -2372,7 +2372,7 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv, if (check_btres(fh, RESOURCE_OVERLAY)) { struct bttv_buffer *new; - new = videobuf_pci_alloc(sizeof(*new)); + new = videobuf_sg_alloc(sizeof(*new)); new->crop = btv->crop[!!fh->do_crop].rect; bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); retval = bttv_switch_overlay(btv,fh,new); @@ -2760,7 +2760,7 @@ static int bttv_overlay(struct file *file, void *f, unsigned int on) mutex_lock(&fh->cap.vb_lock); if (on) { fh->ov.tvnorm = btv->tvnorm; - new = videobuf_pci_alloc(sizeof(*new)); + new = videobuf_sg_alloc(sizeof(*new)); new->crop = btv->crop[!!fh->do_crop].rect; bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); } else { @@ -2834,7 +2834,7 @@ static int bttv_s_fbuf(struct file *file, void *f, if (check_btres(fh, RESOURCE_OVERLAY)) { struct bttv_buffer *new; - new = videobuf_pci_alloc(sizeof(*new)); + new = videobuf_sg_alloc(sizeof(*new)); new->crop = btv->crop[!!fh->do_crop].rect; bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); retval = bttv_switch_overlay(btv, fh, new); @@ -3117,12 +3117,18 @@ static int bttv_s_crop(struct file *file, void *f, struct v4l2_crop *crop) static int bttv_g_audio(struct file *file, void *priv, struct v4l2_audio *a) { + if (unlikely(a->index)) + return -EINVAL; + strcpy(a->name, "audio"); return 0; } static int bttv_s_audio(struct file *file, void *priv, struct v4l2_audio *a) { + if (unlikely(a->index)) + return -EINVAL; + return 0; } @@ -3184,7 +3190,7 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait) /* need to capture a new frame */ if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM)) goto err; - fh->cap.read_buf = videobuf_pci_alloc(fh->cap.msize); + fh->cap.read_buf = videobuf_sg_alloc(fh->cap.msize); if (NULL == fh->cap.read_buf) goto err; fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR; @@ -3251,14 +3257,14 @@ static int bttv_open(struct inode *inode, struct file *file) fh->ov.setup_ok = 0; v4l2_prio_open(&btv->prio,&fh->prio); - videobuf_queue_pci_init(&fh->cap, &bttv_video_qops, - btv->c.pci, &btv->s_lock, + videobuf_queue_sg_init(&fh->cap, &bttv_video_qops, + &btv->c.pci->dev, &btv->s_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct bttv_buffer), fh); - videobuf_queue_pci_init(&fh->vbi, &bttv_vbi_qops, - btv->c.pci, &btv->s_lock, + videobuf_queue_sg_init(&fh->vbi, &bttv_vbi_qops, + &btv->c.pci->dev, &btv->s_lock, V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_FIELD_SEQ_TB, sizeof(struct bttv_buffer), @@ -3457,6 +3463,9 @@ static int radio_release(struct inode *inode, struct file *file) struct bttv *btv = fh->btv; struct rds_command cmd; + file->private_data = NULL; + kfree(fh); + btv->radio_user--; bttv_call_i2c_clients(btv, RDS_CMD_CLOSE, &cmd); @@ -3510,7 +3519,7 @@ static int radio_enum_input(struct file *file, void *priv, return -EINVAL; strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; + i->type = V4L2_INPUT_TYPE_TUNER; return 0; } @@ -3518,10 +3527,9 @@ static int radio_enum_input(struct file *file, void *priv, static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) { - if (a->index != 0) + if (unlikely(a->index)) return -EINVAL; - memset(a, 0, sizeof(*a)); strcpy(a->name, "Radio"); return 0; @@ -3543,11 +3551,17 @@ static int radio_s_tuner(struct file *file, void *priv, static int radio_s_audio(struct file *file, void *priv, struct v4l2_audio *a) { + if (unlikely(a->index)) + return -EINVAL; + return 0; } static int radio_s_input(struct file *filp, void *priv, unsigned int i) { + if (unlikely(i)) + return -EINVAL; + return 0; } diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index fc9ecb21eec..a38af98f4ca 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -278,6 +278,12 @@ int bttv_input_init(struct bttv *btv) ir->mask_keyup = 0x004000; ir->polling = 50; /* ms */ break; + case BTTV_BOARD_KOZUMI_KTV_01C: + ir_codes = ir_codes_pctv_sedna; + ir->mask_keycode = 0x001f00; + ir->mask_keyup = 0x006000; + ir->polling = 50; /* ms */ + break; } if (NULL == ir_codes) { dprintk(KERN_INFO "Ooops: IR config error [card=%d]\n", btv->c.type); diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c index 75fa82c7c73..bfdbc469e30 100644 --- a/drivers/media/video/bt8xx/bttv-vbi.c +++ b/drivers/media/video/bt8xx/bttv-vbi.c @@ -54,7 +54,7 @@ #define VBI_DEFLINES 16 static unsigned int vbibufs = 4; -static unsigned int vbi_debug = 0; +static unsigned int vbi_debug; module_param(vbibufs, int, 0444); module_param(vbi_debug, int, 0644); diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index bf4c339a520..f2393202904 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -19,6 +19,7 @@ #include <media/ir-common.h> #include <media/ir-kbd-i2c.h> #include <media/i2c-addr.h> +#include <media/tuner.h> /* ---------------------------------------------------------- */ /* exported by bttv-cards.c */ @@ -173,6 +174,8 @@ #define BTTV_BOARD_VOODOOTV_200 0x93 #define BTTV_BOARD_DVICO_FUSIONHDTV_2 0x94 #define BTTV_BOARD_TYPHOON_TVTUNERPCI 0x95 +#define BTTV_BOARD_GEOVISION_GV600 0x96 +#define BTTV_BOARD_KOZUMI_KTV_01C 0x97 /* more card-specific defines */ diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index 1305d315cfc..03816b73f84 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -42,7 +42,6 @@ #include <linux/device.h> #include <media/videobuf-dma-sg.h> -#include <media/tuner.h> #include <media/tveeprom.h> #include <media/ir-common.h> diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index 032265383df..b364adaae78 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -523,7 +523,7 @@ static inline int qc_readbytes(struct qcam_device *q, char buffer[]) int ret=1; unsigned int hi, lo; unsigned int hi2, lo2; - static int state = 0; + static int state; if (buffer == NULL) { @@ -898,7 +898,9 @@ static const struct file_operations qcam_fops = { .open = video_exclusive_open, .release = video_exclusive_release, .ioctl = qcam_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .read = qcam_read, .llseek = no_llseek, }; @@ -912,7 +914,7 @@ static struct video_device qcam_template= #define MAX_CAMS 4 static struct qcam_device *qcams[MAX_CAMS]; -static unsigned int num_cams = 0; +static unsigned int num_cams; static int init_bwqcam(struct parport *port) { diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index cf1546b5a7f..fe1e67bb1ca 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -36,6 +36,7 @@ #include <linux/videodev.h> #include <media/v4l2-common.h> #include <linux/mutex.h> +#include <linux/jiffies.h> #include <asm/uaccess.h> @@ -69,7 +70,7 @@ struct qcam_device { static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 }; static int probe = 2; -static int force_rgb = 0; +static int force_rgb; static int video_nr = -1; static inline void qcam_set_ack(struct qcam_device *qcam, unsigned int i) @@ -95,7 +96,8 @@ static unsigned int qcam_await_ready1(struct qcam_device *qcam, unsigned long oldjiffies = jiffies; unsigned int i; - for (oldjiffies = jiffies; (jiffies - oldjiffies) < msecs_to_jiffies(40); ) + for (oldjiffies = jiffies; + time_before(jiffies, oldjiffies + msecs_to_jiffies(40)); ) if (qcam_ready1(qcam) == value) return 0; @@ -120,7 +122,8 @@ static unsigned int qcam_await_ready2(struct qcam_device *qcam, int value) unsigned long oldjiffies = jiffies; unsigned int i; - for (oldjiffies = jiffies; (jiffies - oldjiffies) < msecs_to_jiffies(40); ) + for (oldjiffies = jiffies; + time_before(jiffies, oldjiffies + msecs_to_jiffies(40)); ) if (qcam_ready2(qcam) == value) return 0; @@ -689,7 +692,9 @@ static const struct file_operations qcam_fops = { .open = video_exclusive_open, .release = video_exclusive_release, .ioctl = qcam_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .read = qcam_read, .llseek = no_llseek, }; @@ -741,7 +746,7 @@ static struct qcam_device *qcam_init(struct parport *port) } static struct qcam_device *qcams[MAX_CAMS]; -static unsigned int num_cams = 0; +static unsigned int num_cams; static int init_cqcam(struct parport *port) { diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 7ae499c9c54..5195b1f3378 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -65,7 +65,7 @@ MODULE_SUPPORTED_DEVICE("Video"); */ #define MAX_DMA_BUFS 3 -static int alloc_bufs_at_read = 0; +static int alloc_bufs_at_read; module_param(alloc_bufs_at_read, bool, 0444); MODULE_PARM_DESC(alloc_bufs_at_read, "Non-zero value causes DMA buffers to be allocated when the " @@ -99,7 +99,7 @@ MODULE_PARM_DESC(max_buffers, "will be allowed to allocate. These buffers are big and live " "in vmalloc space."); -static int flip = 0; +static int flip; module_param(flip, bool, 0444); MODULE_PARM_DESC(flip, "If set, the sensor will be instructed to flip the image " diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index 7c630f5ee72..2a81376ef50 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -3792,7 +3792,9 @@ static const struct file_operations cpia_fops = { .read = cpia_read, .mmap = cpia_mmap, .ioctl = cpia_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, }; diff --git a/drivers/media/video/cpia.h b/drivers/media/video/cpia.h index 78392fb6f94..5096058bf57 100644 --- a/drivers/media/video/cpia.h +++ b/drivers/media/video/cpia.h @@ -412,11 +412,11 @@ void cpia_unregister_camera(struct cam_data *cam); /* ErrorCode */ #define ERROR_FLICKER_BELOW_MIN_EXP 0x01 /*flicker exposure got below minimum exposure */ #define ALOG(fmt,args...) printk(fmt, ##args) -#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __FUNCTION__ , __LINE__ , ##args) +#define LOG(fmt,args...) ALOG(KERN_INFO __FILE__ ":%s(%d):" fmt, __func__ , __LINE__ , ##args) #ifdef _CPIA_DEBUG_ #define ADBG(fmt,args...) printk(fmt, jiffies, ##args) -#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __FUNCTION__, __LINE__ , ##args) +#define DBG(fmt,args...) ADBG(KERN_DEBUG __FILE__" (%ld):%s(%d):" fmt, __func__, __LINE__ , ##args) #else #define DBG(fmn,args...) do {} while(0) #endif diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c index a76bd786cf1..c8b9fdb700f 100644 --- a/drivers/media/video/cpia2/cpia2_core.c +++ b/drivers/media/video/cpia2/cpia2_core.c @@ -34,7 +34,7 @@ #include <linux/slab.h> #include <linux/vmalloc.h> -//#define _CPIA2_DEBUG_ +/* #define _CPIA2_DEBUG_ */ #include "cpia2patch.h" @@ -48,7 +48,7 @@ static const char *block_name[] = { }; #endif -static unsigned int debugs_on = 0;//DEBUG_REG; +static unsigned int debugs_on; /* default 0 - DEBUG_REG */ /****************************************************************************** @@ -570,7 +570,7 @@ int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd) block_name[block_index]); break; default: - LOG("%s: invalid request mode\n",__FUNCTION__); + LOG("%s: invalid request mode\n",__func__); return -EINVAL; } @@ -952,7 +952,7 @@ static int set_default_user_mode(struct camera_data *cam) frame_rate = CPIA2_VP_FRAMERATE_30; break; default: - LOG("%s: Invalid sensor flag value 0x%0X\n",__FUNCTION__, + LOG("%s: Invalid sensor flag value 0x%0X\n",__func__, cam->params.version.sensor_flags); return -EINVAL; } @@ -2356,12 +2356,12 @@ long cpia2_read(struct camera_data *cam, } if (!buf) { - ERR("%s: buffer NULL\n",__FUNCTION__); + ERR("%s: buffer NULL\n",__func__); return -EINVAL; } if (!cam) { - ERR("%s: Internal error, camera_data NULL!\n",__FUNCTION__); + ERR("%s: Internal error, camera_data NULL!\n",__func__); return -EINVAL; } @@ -2370,7 +2370,7 @@ long cpia2_read(struct camera_data *cam, return -ERESTARTSYS; if (!cam->present) { - LOG("%s: camera removed\n",__FUNCTION__); + LOG("%s: camera removed\n",__func__); mutex_unlock(&cam->busy_lock); return 0; /* EOF */ } @@ -2434,7 +2434,7 @@ unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, unsigned int status=0; if(!cam) { - ERR("%s: Internal error, camera_data not found!\n",__FUNCTION__); + ERR("%s: Internal error, camera_data not found!\n",__func__); return POLLERR; } diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c index d8e929863a8..a4574740350 100644 --- a/drivers/media/video/cpia2/cpia2_usb.c +++ b/drivers/media/video/cpia2/cpia2_usb.c @@ -84,7 +84,7 @@ static struct usb_driver cpia2_driver = { *****************************************************************************/ static void process_frame(struct camera_data *cam) { - static int frame_count = 0; + static int frame_count; unsigned char *inbuff = cam->workbuff->data; diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index e378abec806..7ce2789fa97 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -1927,7 +1927,9 @@ static const struct file_operations fops_template = { .poll = cpia2_v4l_poll, .ioctl = cpia2_ioctl, .llseek = no_llseek, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .mmap = cpia2_mmap, }; diff --git a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c index 9da4726eb9b..ef1f8939998 100644 --- a/drivers/media/video/cpia_usb.c +++ b/drivers/media/video/cpia_usb.c @@ -170,7 +170,7 @@ static void cpia_usb_complete(struct urb *urb) /* resubmit */ urb->dev = ucpia->dev; if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) - printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __FUNCTION__, i); + printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __func__, i); } static int cpia_usb_open(void *privdata) diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index 1fd326fe411..ca5fbce3a90 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -8,6 +8,7 @@ config VIDEO_CX23885 select VIDEO_TVEEPROM select VIDEO_IR select VIDEOBUF_DVB + select VIDEO_CX25840 select DVB_TUNER_MT2131 if !DVB_FE_CUSTOMISE select DVB_S5H1409 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE @@ -16,6 +17,7 @@ config VIDEO_CX23885 select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE select DVB_TDA18271 if !DVB_FE_CUSTOMIZE select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE + select DVB_TDA10048 if !DVB_FE_CUSTOMIZE ---help--- This is a video4linux driver for Conexant 23885 based TV cards. diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile index 32c90be5060..d7b0721af06 100644 --- a/drivers/media/video/cx23885/Makefile +++ b/drivers/media/video/cx23885/Makefile @@ -1,4 +1,4 @@ -cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o +cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o obj-$(CONFIG_VIDEO_CX23885) += cx23885.o diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c new file mode 100644 index 00000000000..acdd3b6b3e7 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -0,0 +1,1764 @@ +/* + * + * Support for a cx23417 mpeg encoder via cx23885 host port. + * + * (c) 2004 Jelle Foks <jelle@foks.8m.com> + * (c) 2004 Gerd Knorr <kraxel@bytesex.org> + * (c) 2008 Steven Toth <stoth@hauppauge.com> + * - CX23885/7/8 support + * + * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/), + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/firmware.h> +#include <media/v4l2-common.h> +#include <media/cx2341x.h> + +#include "cx23885.h" +#include "media/cx2341x.h" + +#define CX23885_FIRM_IMAGE_SIZE 376836 +#define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw" + +static unsigned int mpegbufs = 32; +module_param(mpegbufs, int, 0644); +MODULE_PARM_DESC(mpegbufs, "number of mpeg buffers, range 2-32"); +static unsigned int mpeglines = 32; +module_param(mpeglines, int, 0644); +MODULE_PARM_DESC(mpeglines, "number of lines in an MPEG buffer, range 2-32"); +static unsigned int mpeglinesize = 512; +module_param(mpeglinesize, int, 0644); +MODULE_PARM_DESC(mpeglinesize, + "number of bytes in each line of an MPEG buffer, range 512-1024"); + +static unsigned int v4l_debug; +module_param(v4l_debug, int, 0644); +MODULE_PARM_DESC(v4l_debug, "enable V4L debug messages"); + +#define dprintk(level, fmt, arg...)\ + do { if (v4l_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg);\ + } while (0) + +static struct cx23885_tvnorm cx23885_tvnorms[] = { + { + .name = "NTSC-M", + .id = V4L2_STD_NTSC_M, + }, { + .name = "NTSC-JP", + .id = V4L2_STD_NTSC_M_JP, + }, { + .name = "PAL-BG", + .id = V4L2_STD_PAL_BG, + }, { + .name = "PAL-DK", + .id = V4L2_STD_PAL_DK, + }, { + .name = "PAL-I", + .id = V4L2_STD_PAL_I, + }, { + .name = "PAL-M", + .id = V4L2_STD_PAL_M, + }, { + .name = "PAL-N", + .id = V4L2_STD_PAL_N, + }, { + .name = "PAL-Nc", + .id = V4L2_STD_PAL_Nc, + }, { + .name = "PAL-60", + .id = V4L2_STD_PAL_60, + }, { + .name = "SECAM-L", + .id = V4L2_STD_SECAM_L, + }, { + .name = "SECAM-DK", + .id = V4L2_STD_SECAM_DK, + } +}; + +/* ------------------------------------------------------------------ */ +enum cx23885_capture_type { + CX23885_MPEG_CAPTURE, + CX23885_RAW_CAPTURE, + CX23885_RAW_PASSTHRU_CAPTURE +}; +enum cx23885_capture_bits { + CX23885_RAW_BITS_NONE = 0x00, + CX23885_RAW_BITS_YUV_CAPTURE = 0x01, + CX23885_RAW_BITS_PCM_CAPTURE = 0x02, + CX23885_RAW_BITS_VBI_CAPTURE = 0x04, + CX23885_RAW_BITS_PASSTHRU_CAPTURE = 0x08, + CX23885_RAW_BITS_TO_HOST_CAPTURE = 0x10 +}; +enum cx23885_capture_end { + CX23885_END_AT_GOP, /* stop at the end of gop, generate irq */ + CX23885_END_NOW, /* stop immediately, no irq */ +}; +enum cx23885_framerate { + CX23885_FRAMERATE_NTSC_30, /* NTSC: 30fps */ + CX23885_FRAMERATE_PAL_25 /* PAL: 25fps */ +}; +enum cx23885_stream_port { + CX23885_OUTPUT_PORT_MEMORY, + CX23885_OUTPUT_PORT_STREAMING, + CX23885_OUTPUT_PORT_SERIAL +}; +enum cx23885_data_xfer_status { + CX23885_MORE_BUFFERS_FOLLOW, + CX23885_LAST_BUFFER, +}; +enum cx23885_picture_mask { + CX23885_PICTURE_MASK_NONE, + CX23885_PICTURE_MASK_I_FRAMES, + CX23885_PICTURE_MASK_I_P_FRAMES = 0x3, + CX23885_PICTURE_MASK_ALL_FRAMES = 0x7, +}; +enum cx23885_vbi_mode_bits { + CX23885_VBI_BITS_SLICED, + CX23885_VBI_BITS_RAW, +}; +enum cx23885_vbi_insertion_bits { + CX23885_VBI_BITS_INSERT_IN_XTENSION_USR_DATA, + CX23885_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1, + CX23885_VBI_BITS_SEPARATE_STREAM = 0x2 << 1, + CX23885_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, + CX23885_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, +}; +enum cx23885_dma_unit { + CX23885_DMA_BYTES, + CX23885_DMA_FRAMES, +}; +enum cx23885_dma_transfer_status_bits { + CX23885_DMA_TRANSFER_BITS_DONE = 0x01, + CX23885_DMA_TRANSFER_BITS_ERROR = 0x04, + CX23885_DMA_TRANSFER_BITS_LL_ERROR = 0x10, +}; +enum cx23885_pause { + CX23885_PAUSE_ENCODING, + CX23885_RESUME_ENCODING, +}; +enum cx23885_copyright { + CX23885_COPYRIGHT_OFF, + CX23885_COPYRIGHT_ON, +}; +enum cx23885_notification_type { + CX23885_NOTIFICATION_REFRESH, +}; +enum cx23885_notification_status { + CX23885_NOTIFICATION_OFF, + CX23885_NOTIFICATION_ON, +}; +enum cx23885_notification_mailbox { + CX23885_NOTIFICATION_NO_MAILBOX = -1, +}; +enum cx23885_field1_lines { + CX23885_FIELD1_SAA7114 = 0x00EF, /* 239 */ + CX23885_FIELD1_SAA7115 = 0x00F0, /* 240 */ + CX23885_FIELD1_MICRONAS = 0x0105, /* 261 */ +}; +enum cx23885_field2_lines { + CX23885_FIELD2_SAA7114 = 0x00EF, /* 239 */ + CX23885_FIELD2_SAA7115 = 0x00F0, /* 240 */ + CX23885_FIELD2_MICRONAS = 0x0106, /* 262 */ +}; +enum cx23885_custom_data_type { + CX23885_CUSTOM_EXTENSION_USR_DATA, + CX23885_CUSTOM_PRIVATE_PACKET, +}; +enum cx23885_mute { + CX23885_UNMUTE, + CX23885_MUTE, +}; +enum cx23885_mute_video_mask { + CX23885_MUTE_VIDEO_V_MASK = 0x0000FF00, + CX23885_MUTE_VIDEO_U_MASK = 0x00FF0000, + CX23885_MUTE_VIDEO_Y_MASK = 0xFF000000, +}; +enum cx23885_mute_video_shift { + CX23885_MUTE_VIDEO_V_SHIFT = 8, + CX23885_MUTE_VIDEO_U_SHIFT = 16, + CX23885_MUTE_VIDEO_Y_SHIFT = 24, +}; + +/* defines below are from ivtv-driver.h */ +#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF + +/* Firmware API commands */ +#define IVTV_API_STD_TIMEOUT 500 + +/* Registers */ +/* IVTV_REG_OFFSET */ +#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8) +#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC) +#define IVTV_REG_SPU (0x9050) +#define IVTV_REG_HW_BLOCKS (0x9054) +#define IVTV_REG_VPU (0x9058) +#define IVTV_REG_APU (0xA064) + +/**** Bit definitions for MC417_RWD and MC417_OEN registers *** + bits 31-16 ++-----------+ +| Reserved | ++-----------+ + bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 ++-------+-------+-------+-------+-------+-------+-------+-------+ +| MIWR# | MIRD# | MICS# |MIRDY# |MIADDR3|MIADDR2|MIADDR1|MIADDR0| ++-------+-------+-------+-------+-------+-------+-------+-------+ + bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 ++-------+-------+-------+-------+-------+-------+-------+-------+ +|MIDATA7|MIDATA6|MIDATA5|MIDATA4|MIDATA3|MIDATA2|MIDATA1|MIDATA0| ++-------+-------+-------+-------+-------+-------+-------+-------+ +***/ +#define MC417_MIWR 0x8000 +#define MC417_MIRD 0x4000 +#define MC417_MICS 0x2000 +#define MC417_MIRDY 0x1000 +#define MC417_MIADDR 0x0F00 +#define MC417_MIDATA 0x00FF + +/* MIADDR* nibble definitions */ +#define MCI_MEMORY_DATA_BYTE0 0x000 +#define MCI_MEMORY_DATA_BYTE1 0x100 +#define MCI_MEMORY_DATA_BYTE2 0x200 +#define MCI_MEMORY_DATA_BYTE3 0x300 +#define MCI_MEMORY_ADDRESS_BYTE2 0x400 +#define MCI_MEMORY_ADDRESS_BYTE1 0x500 +#define MCI_MEMORY_ADDRESS_BYTE0 0x600 +#define MCI_REGISTER_DATA_BYTE0 0x800 +#define MCI_REGISTER_DATA_BYTE1 0x900 +#define MCI_REGISTER_DATA_BYTE2 0xA00 +#define MCI_REGISTER_DATA_BYTE3 0xB00 +#define MCI_REGISTER_ADDRESS_BYTE0 0xC00 +#define MCI_REGISTER_ADDRESS_BYTE1 0xD00 +#define MCI_REGISTER_MODE 0xE00 + +/* Read and write modes */ +#define MCI_MODE_REGISTER_READ 0 +#define MCI_MODE_REGISTER_WRITE 1 +#define MCI_MODE_MEMORY_READ 0 +#define MCI_MODE_MEMORY_WRITE 0x40 + +/*** Bit definitions for MC417_CTL register **** + bits 31-6 bits 5-4 bit 3 bits 2-1 Bit 0 ++--------+-------------+--------+--------------+------------+ +|Reserved|MC417_SPD_CTL|Reserved|MC417_GPIO_SEL|UART_GPIO_EN| ++--------+-------------+--------+--------------+------------+ +***/ +#define MC417_SPD_CTL(x) (((x) << 4) & 0x00000030) +#define MC417_GPIO_SEL(x) (((x) << 1) & 0x00000006) +#define MC417_UART_GPIO_EN 0x00000001 + +/* Values for speed control */ +#define MC417_SPD_CTL_SLOW 0x1 +#define MC417_SPD_CTL_MEDIUM 0x0 +#define MC417_SPD_CTL_FAST 0x3 /* b'1x, but we use b'11 */ + +/* Values for GPIO select */ +#define MC417_GPIO_SEL_GPIO3 0x3 +#define MC417_GPIO_SEL_GPIO2 0x2 +#define MC417_GPIO_SEL_GPIO1 0x1 +#define MC417_GPIO_SEL_GPIO0 0x0 + +void cx23885_mc417_init(struct cx23885_dev *dev) +{ + u32 regval; + + dprintk(2, "%s()\n", __func__); + + /* Configure MC417_CTL register to defaults. */ + regval = MC417_SPD_CTL(MC417_SPD_CTL_FAST) | + MC417_GPIO_SEL(MC417_GPIO_SEL_GPIO3) | + MC417_UART_GPIO_EN; + cx_write(MC417_CTL, regval); + + /* Configure MC417_OEN to defaults. */ + regval = MC417_MIRDY; + cx_write(MC417_OEN, regval); + + /* Configure MC417_RWD to defaults. */ + regval = MC417_MIWR | MC417_MIRD | MC417_MICS; + cx_write(MC417_RWD, regval); +} + +static int mc417_wait_ready(struct cx23885_dev *dev) +{ + u32 mi_ready; + unsigned long timeout = jiffies + msecs_to_jiffies(1); + + for (;;) { + mi_ready = cx_read(MC417_RWD) & MC417_MIRDY; + if (mi_ready != 0) + return 0; + if (time_after(jiffies, timeout)) + return -1; + udelay(1); + } +} + +static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) +{ + u32 regval; + + /* Enable MC417 GPIO outputs except for MC417_MIRDY, + * which is an input. + */ + cx_write(MC417_OEN, MC417_MIRDY); + + /* Write data byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0 | + (value & 0x000000FF); + cx_write(MC417_RWD, regval); + + /* Transition CS/WR to effect write transaction across bus. */ + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1 | + ((value >> 8) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2 | + ((value >> 16) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 3 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3 | + ((value >> 24) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 | + (address & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 | + ((address >> 8) & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Indicate that this is a write. */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE | + MCI_MODE_REGISTER_WRITE; + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Wait for the trans to complete (MC417_MIRDY asserted). */ + return mc417_wait_ready(dev); +} + +static int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value) +{ + int retval; + u32 regval; + u32 tempval; + u32 dataval; + + /* Enable MC417 GPIO outputs except for MC417_MIRDY, + * which is an input. + */ + cx_write(MC417_OEN, MC417_MIRDY); + + /* Write address byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE0 | + ((address & 0x00FF)); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_ADDRESS_BYTE1 | + ((address >> 8) & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Indicate that this is a register read. */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_MODE | + MCI_MODE_REGISTER_READ; + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Wait for the trans to complete (MC417_MIRDY asserted). */ + retval = mc417_wait_ready(dev); + + /* switch the DAT0-7 GPIO[10:3] to input mode */ + cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA); + + /* Read data byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0; + cx_write(MC417_RWD, regval); + + /* Transition RD to effect read transaction across bus. + * Transtion 0x5000 -> 0x9000 correct (RD/RDY -> WR/RDY)? + * Should it be 0x9000 -> 0xF000 (also why is RDY being set, its + * input only...) + */ + regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE0; + cx_write(MC417_RWD, regval); + + /* Collect byte */ + tempval = cx_read(MC417_RWD); + dataval = tempval & 0x000000FF; + + /* Bring CS and RD high. */ + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE1; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= ((tempval & 0x000000FF) << 8); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE2; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= ((tempval & 0x000000FF) << 16); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 3 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_REGISTER_DATA_BYTE3; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= ((tempval & 0x000000FF) << 24); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + *value = dataval; + + return retval; +} + +int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value) +{ + u32 regval; + + /* Enable MC417 GPIO outputs except for MC417_MIRDY, + * which is an input. + */ + cx_write(MC417_OEN, MC417_MIRDY); + + /* Write data byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0 | + (value & 0x000000FF); + cx_write(MC417_RWD, regval); + + /* Transition CS/WR to effect write transaction across bus. */ + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1 | + ((value >> 8) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2 | + ((value >> 16) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write data byte 3 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3 | + ((value >> 24) & 0x000000FF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 | + MCI_MODE_MEMORY_WRITE | ((address >> 16) & 0x3F); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 | + ((address >> 8) & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 | + (address & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Wait for the trans to complete (MC417_MIRDY asserted). */ + return mc417_wait_ready(dev); +} + +int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value) +{ + int retval; + u32 regval; + u32 tempval; + u32 dataval; + + /* Enable MC417 GPIO outputs except for MC417_MIRDY, + * which is an input. + */ + cx_write(MC417_OEN, MC417_MIRDY); + + /* Write address byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE2 | + MCI_MODE_MEMORY_READ | ((address >> 16) & 0x3F); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE1 | + ((address >> 8) & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Write address byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_ADDRESS_BYTE0 | + (address & 0xFF); + cx_write(MC417_RWD, regval); + regval |= MC417_MICS | MC417_MIWR; + cx_write(MC417_RWD, regval); + + /* Wait for the trans to complete (MC417_MIRDY asserted). */ + retval = mc417_wait_ready(dev); + + /* switch the DAT0-7 GPIO[10:3] to input mode */ + cx_write(MC417_OEN, MC417_MIRDY | MC417_MIDATA); + + /* Read data byte 3 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3; + cx_write(MC417_RWD, regval); + + /* Transition RD to effect read transaction across bus. */ + regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE3; + cx_write(MC417_RWD, regval); + + /* Collect byte */ + tempval = cx_read(MC417_RWD); + dataval = ((tempval & 0x000000FF) << 24); + + /* Bring CS and RD high. */ + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 2 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE2; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= ((tempval & 0x000000FF) << 16); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 1 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE1; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= ((tempval & 0x000000FF) << 8); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + /* Read data byte 0 */ + regval = MC417_MIRD | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0; + cx_write(MC417_RWD, regval); + regval = MC417_MIWR | MC417_MIRDY | MCI_MEMORY_DATA_BYTE0; + cx_write(MC417_RWD, regval); + tempval = cx_read(MC417_RWD); + dataval |= (tempval & 0x000000FF); + regval = MC417_MIWR | MC417_MIRD | MC417_MICS | MC417_MIRDY; + cx_write(MC417_RWD, regval); + + *value = dataval; + + return retval; +} + +/* ------------------------------------------------------------------ */ + +/* MPEG encoder API */ +char *cmd_to_str(int cmd) +{ + switch (cmd) { + case CX2341X_ENC_PING_FW: + return "PING_FW"; + case CX2341X_ENC_START_CAPTURE: + return "START_CAPTURE"; + case CX2341X_ENC_STOP_CAPTURE: + return "STOP_CAPTURE"; + case CX2341X_ENC_SET_AUDIO_ID: + return "SET_AUDIO_ID"; + case CX2341X_ENC_SET_VIDEO_ID: + return "SET_VIDEO_ID"; + case CX2341X_ENC_SET_PCR_ID: + return "SET_PCR_PID"; + case CX2341X_ENC_SET_FRAME_RATE: + return "SET_FRAME_RATE"; + case CX2341X_ENC_SET_FRAME_SIZE: + return "SET_FRAME_SIZE"; + case CX2341X_ENC_SET_BIT_RATE: + return "SET_BIT_RATE"; + case CX2341X_ENC_SET_GOP_PROPERTIES: + return "SET_GOP_PROPERTIES"; + case CX2341X_ENC_SET_ASPECT_RATIO: + return "SET_ASPECT_RATIO"; + case CX2341X_ENC_SET_DNR_FILTER_MODE: + return "SET_DNR_FILTER_PROPS"; + case CX2341X_ENC_SET_DNR_FILTER_PROPS: + return "SET_DNR_FILTER_PROPS"; + case CX2341X_ENC_SET_CORING_LEVELS: + return "SET_CORING_LEVELS"; + case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: + return "SET_SPATIAL_FILTER_TYPE"; + case CX2341X_ENC_SET_VBI_LINE: + return "SET_VBI_LINE"; + case CX2341X_ENC_SET_STREAM_TYPE: + return "SET_STREAM_TYPE"; + case CX2341X_ENC_SET_OUTPUT_PORT: + return "SET_OUTPUT_PORT"; + case CX2341X_ENC_SET_AUDIO_PROPERTIES: + return "SET_AUDIO_PROPERTIES"; + case CX2341X_ENC_HALT_FW: + return "HALT_FW"; + case CX2341X_ENC_GET_VERSION: + return "GET_VERSION"; + case CX2341X_ENC_SET_GOP_CLOSURE: + return "SET_GOP_CLOSURE"; + case CX2341X_ENC_GET_SEQ_END: + return "GET_SEQ_END"; + case CX2341X_ENC_SET_PGM_INDEX_INFO: + return "SET_PGM_INDEX_INFO"; + case CX2341X_ENC_SET_VBI_CONFIG: + return "SET_VBI_CONFIG"; + case CX2341X_ENC_SET_DMA_BLOCK_SIZE: + return "SET_DMA_BLOCK_SIZE"; + case CX2341X_ENC_GET_PREV_DMA_INFO_MB_10: + return "GET_PREV_DMA_INFO_MB_10"; + case CX2341X_ENC_GET_PREV_DMA_INFO_MB_9: + return "GET_PREV_DMA_INFO_MB_9"; + case CX2341X_ENC_SCHED_DMA_TO_HOST: + return "SCHED_DMA_TO_HOST"; + case CX2341X_ENC_INITIALIZE_INPUT: + return "INITIALIZE_INPUT"; + case CX2341X_ENC_SET_FRAME_DROP_RATE: + return "SET_FRAME_DROP_RATE"; + case CX2341X_ENC_PAUSE_ENCODER: + return "PAUSE_ENCODER"; + case CX2341X_ENC_REFRESH_INPUT: + return "REFRESH_INPUT"; + case CX2341X_ENC_SET_COPYRIGHT: + return "SET_COPYRIGHT"; + case CX2341X_ENC_SET_EVENT_NOTIFICATION: + return "SET_EVENT_NOTIFICATION"; + case CX2341X_ENC_SET_NUM_VSYNC_LINES: + return "SET_NUM_VSYNC_LINES"; + case CX2341X_ENC_SET_PLACEHOLDER: + return "SET_PLACEHOLDER"; + case CX2341X_ENC_MUTE_VIDEO: + return "MUTE_VIDEO"; + case CX2341X_ENC_MUTE_AUDIO: + return "MUTE_AUDIO"; + case CX2341X_ENC_MISC: + return "MISC"; + default: + return "UNKNOWN"; + } +} + +static int cx23885_mbox_func(void *priv, + u32 command, + int in, + int out, + u32 data[CX2341X_MBOX_MAX_DATA]) +{ + struct cx23885_dev *dev = priv; + unsigned long timeout; + u32 value, flag, retval = 0; + int i; + + dprintk(3, "%s: command(0x%X) = %s\n", __func__, command, + cmd_to_str(command)); + + /* this may not be 100% safe if we can't read any memory location + without side effects */ + mc417_memory_read(dev, dev->cx23417_mailbox - 4, &value); + if (value != 0x12345678) { + printk(KERN_ERR + "Firmware and/or mailbox pointer not initialized " + "or corrupted, signature = 0x%x, cmd = %s\n", value, + cmd_to_str(command)); + return -1; + } + + /* This read looks at 32 bits, but flag is only 8 bits. + * Seems we also bail if CMD or TIMEOUT bytes are set??? + */ + mc417_memory_read(dev, dev->cx23417_mailbox, &flag); + if (flag) { + printk(KERN_ERR "ERROR: Mailbox appears to be in use " + "(%x), cmd = %s\n", flag, cmd_to_str(command)); + return -1; + } + + flag |= 1; /* tell 'em we're working on it */ + mc417_memory_write(dev, dev->cx23417_mailbox, flag); + + /* write command + args + fill remaining with zeros */ + /* command code */ + mc417_memory_write(dev, dev->cx23417_mailbox + 1, command); + mc417_memory_write(dev, dev->cx23417_mailbox + 3, + IVTV_API_STD_TIMEOUT); /* timeout */ + for (i = 0; i < in; i++) { + mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, data[i]); + dprintk(3, "API Input %d = %d\n", i, data[i]); + } + for (; i < CX2341X_MBOX_MAX_DATA; i++) + mc417_memory_write(dev, dev->cx23417_mailbox + 4 + i, 0); + + flag |= 3; /* tell 'em we're done writing */ + mc417_memory_write(dev, dev->cx23417_mailbox, flag); + + /* wait for firmware to handle the API command */ + timeout = jiffies + msecs_to_jiffies(10); + for (;;) { + mc417_memory_read(dev, dev->cx23417_mailbox, &flag); + if (0 != (flag & 4)) + break; + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "ERROR: API Mailbox timeout\n"); + return -1; + } + udelay(10); + } + + /* read output values */ + for (i = 0; i < out; i++) { + mc417_memory_read(dev, dev->cx23417_mailbox + 4 + i, data + i); + dprintk(3, "API Output %d = %d\n", i, data[i]); + } + + mc417_memory_read(dev, dev->cx23417_mailbox + 2, &retval); + dprintk(3, "API result = %d\n", retval); + + flag = 0; + mc417_memory_write(dev, dev->cx23417_mailbox, flag); + + return retval; +} + +/* We don't need to call the API often, so using just one + * mailbox will probably suffice + */ +static int cx23885_api_cmd(struct cx23885_dev *dev, + u32 command, + u32 inputcnt, + u32 outputcnt, + ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list vargs; + int i, err; + + dprintk(3, "%s() cmds = 0x%08x\n", __func__, command); + + va_start(vargs, outputcnt); + for (i = 0; i < inputcnt; i++) + data[i] = va_arg(vargs, int); + + err = cx23885_mbox_func(dev, command, inputcnt, outputcnt, data); + for (i = 0; i < outputcnt; i++) { + int *vptr = va_arg(vargs, int *); + *vptr = data[i]; + } + va_end(vargs); + + return err; +} + +static int cx23885_find_mailbox(struct cx23885_dev *dev) +{ + u32 signature[4] = { + 0x12345678, 0x34567812, 0x56781234, 0x78123456 + }; + int signaturecnt = 0; + u32 value; + int i; + + dprintk(2, "%s()\n", __func__); + + for (i = 0; i < CX23885_FIRM_IMAGE_SIZE; i++) { + mc417_memory_read(dev, i, &value); + if (value == signature[signaturecnt]) + signaturecnt++; + else + signaturecnt = 0; + if (4 == signaturecnt) { + dprintk(1, "Mailbox signature found at 0x%x\n", i+1); + return i+1; + } + } + printk(KERN_ERR "Mailbox signature values not found!\n"); + return -1; +} + +static int cx23885_load_firmware(struct cx23885_dev *dev) +{ + static const unsigned char magic[8] = { + 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa + }; + const struct firmware *firmware; + int i, retval = 0; + u32 value = 0; + u32 gpio_output = 0; + u32 checksum = 0; + u32 *dataptr; + + dprintk(2, "%s()\n", __func__); + + /* Save GPIO settings before reset of APU */ + retval |= mc417_memory_read(dev, 0x9020, &gpio_output); + retval |= mc417_memory_read(dev, 0x900C, &value); + + retval = mc417_register_write(dev, + IVTV_REG_VPU, 0xFFFFFFED); + retval |= mc417_register_write(dev, + IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); + retval |= mc417_register_write(dev, + IVTV_REG_ENC_SDRAM_REFRESH, 0x80000800); + retval |= mc417_register_write(dev, + IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A); + retval |= mc417_register_write(dev, + IVTV_REG_APU, 0); + + if (retval != 0) { + printk(KERN_ERR "%s: Error with mc417_register_write\n", + __func__); + return -1; + } + + retval = request_firmware(&firmware, CX23885_FIRM_IMAGE_NAME, + &dev->pci->dev); + + if (retval != 0) { + printk(KERN_ERR + "ERROR: Hotplug firmware request failed (%s).\n", + CX2341X_FIRM_ENC_FILENAME); + printk(KERN_ERR "Please fix your hotplug setup, the board will " + "not work without firmware loaded!\n"); + return -1; + } + + if (firmware->size != CX23885_FIRM_IMAGE_SIZE) { + printk(KERN_ERR "ERROR: Firmware size mismatch " + "(have %zd, expected %d)\n", + firmware->size, CX23885_FIRM_IMAGE_SIZE); + release_firmware(firmware); + return -1; + } + + if (0 != memcmp(firmware->data, magic, 8)) { + printk(KERN_ERR + "ERROR: Firmware magic mismatch, wrong file?\n"); + release_firmware(firmware); + return -1; + } + + /* transfer to the chip */ + dprintk(2, "Loading firmware ...\n"); + dataptr = (u32 *)firmware->data; + for (i = 0; i < (firmware->size >> 2); i++) { + value = *dataptr; + checksum += ~value; + if (mc417_memory_write(dev, i, value) != 0) { + printk(KERN_ERR "ERROR: Loading firmware failed!\n"); + release_firmware(firmware); + return -1; + } + dataptr++; + } + + /* read back to verify with the checksum */ + dprintk(1, "Verifying firmware ...\n"); + for (i--; i >= 0; i--) { + if (mc417_memory_read(dev, i, &value) != 0) { + printk(KERN_ERR "ERROR: Reading firmware failed!\n"); + release_firmware(firmware); + return -1; + } + checksum -= ~value; + } + if (checksum) { + printk(KERN_ERR + "ERROR: Firmware load failed (checksum mismatch).\n"); + release_firmware(firmware); + return -1; + } + release_firmware(firmware); + dprintk(1, "Firmware upload successful.\n"); + + retval |= mc417_register_write(dev, IVTV_REG_HW_BLOCKS, + IVTV_CMD_HW_BLOCKS_RST); + + /* Restore GPIO settings, make sure EIO14 is enabled as an output. */ + dprintk(2, "%s: GPIO output EIO 0-15 was = 0x%x\n", + __func__, gpio_output); + /* Power-up seems to have GPIOs AFU. This was causing digital side + * to fail at power-up. Seems GPIOs should be set to 0x10ff0411 at + * power-up. + * gpio_output |= (1<<14); + */ + /* Note: GPIO14 is specific to the HVR1800 here */ + gpio_output = 0x10ff0411 | (1<<14); + retval |= mc417_register_write(dev, 0x9020, gpio_output | (1<<14)); + dprintk(2, "%s: GPIO output EIO 0-15 now = 0x%x\n", + __func__, gpio_output); + + dprintk(1, "%s: GPIO value EIO 0-15 was = 0x%x\n", + __func__, value); + value |= (1<<14); + dprintk(1, "%s: GPIO value EIO 0-15 now = 0x%x\n", + __func__, value); + retval |= mc417_register_write(dev, 0x900C, value); + + retval |= mc417_register_read(dev, IVTV_REG_VPU, &value); + retval |= mc417_register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8); + + if (retval < 0) + printk(KERN_ERR "%s: Error with mc417_register_write\n", + __func__); + return 0; +} + +void cx23885_417_check_encoder(struct cx23885_dev *dev) +{ + u32 status, seq; + + status = seq = 0; + cx23885_api_cmd(dev, CX2341X_ENC_GET_SEQ_END, 0, 2, &status, &seq); + dprintk(1, "%s() status = %d, seq = %d\n", __func__, status, seq); +} + +static void cx23885_codec_settings(struct cx23885_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + /* assign frame size */ + cx23885_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, + dev->ts1.height, dev->ts1.width); + + dev->mpeg_params.width = dev->ts1.width; + dev->mpeg_params.height = dev->ts1.height; + dev->mpeg_params.is_50hz = + (dev->encodernorm.id & V4L2_STD_625_50) != 0; + + cx2341x_update(dev, cx23885_mbox_func, NULL, &dev->mpeg_params); + + cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 3, 1); + cx23885_api_cmd(dev, CX2341X_ENC_MISC, 2, 0, 4, 1); +} + +static int cx23885_initialize_codec(struct cx23885_dev *dev) +{ + int version; + int retval; + u32 i, data[7]; + + dprintk(1, "%s()\n", __func__); + + retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ + if (retval < 0) { + dprintk(2, "%s() PING OK\n", __func__); + retval = cx23885_load_firmware(dev); + if (retval < 0) { + printk(KERN_ERR "%s() f/w load failed\n", __func__); + return retval; + } + dev->cx23417_mailbox = cx23885_find_mailbox(dev); + if (dev->cx23417_mailbox < 0) { + printk(KERN_ERR "%s() mailbox < 0, error\n", + __func__); + return -1; + } + retval = cx23885_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); + if (retval < 0) { + printk(KERN_ERR + "ERROR: cx23417 firmware ping failed!\n"); + return -1; + } + retval = cx23885_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, + &version); + if (retval < 0) { + printk(KERN_ERR "ERROR: cx23417 firmware get encoder :" + "version failed!\n"); + return -1; + } + dprintk(1, "cx23417 firmware version is 0x%08x\n", version); + msleep(200); + } + + cx23885_codec_settings(dev); + msleep(60); + + cx23885_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0, + CX23885_FIELD1_SAA7115, CX23885_FIELD2_SAA7115); + cx23885_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0, + CX23885_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0); + + /* Setup to capture VBI */ + data[0] = 0x0001BD00; + data[1] = 1; /* frames per interrupt */ + data[2] = 4; /* total bufs */ + data[3] = 0x91559155; /* start codes */ + data[4] = 0x206080C0; /* stop codes */ + data[5] = 6; /* lines */ + data[6] = 64; /* BPL */ + + cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1], + data[2], data[3], data[4], data[5], data[6]); + + for (i = 2; i <= 24; i++) { + int valid; + + valid = ((i >= 19) && (i <= 21)); + cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i, + valid, 0 , 0, 0); + cx23885_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, + i | 0x80000000, valid, 0, 0, 0); + } + + cx23885_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX23885_UNMUTE); + msleep(60); + + /* initialize the video input */ + cx23885_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0); + msleep(60); + + /* Enable VIP style pixel invalidation so we work with scaled mode */ + mc417_memory_write(dev, 2120, 0x00000080); + + /* start capturing to the host interface */ + cx23885_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, + CX23885_MPEG_CAPTURE, CX23885_RAW_BITS_NONE); + msleep(10); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int bb_buf_setup(struct videobuf_queue *q, + unsigned int *count, unsigned int *size) +{ + struct cx23885_fh *fh = q->priv_data; + + fh->dev->ts1.ts_packet_size = mpeglinesize; + fh->dev->ts1.ts_packet_count = mpeglines; + + *size = fh->dev->ts1.ts_packet_size * fh->dev->ts1.ts_packet_count; + *count = mpegbufs; + + return 0; +} + +static int bb_buf_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct cx23885_fh *fh = q->priv_data; + return cx23885_buf_prepare(q, &fh->dev->ts1, + (struct cx23885_buffer *)vb, + field); +} + +static void bb_buf_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct cx23885_fh *fh = q->priv_data; + cx23885_buf_queue(&fh->dev->ts1, (struct cx23885_buffer *)vb); +} + +static void bb_buf_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + cx23885_free_buffer(q, (struct cx23885_buffer *)vb); +} + +static struct videobuf_queue_ops cx23885_qops = { + .buf_setup = bb_buf_setup, + .buf_prepare = bb_buf_prepare, + .buf_queue = bb_buf_queue, + .buf_release = bb_buf_release, +}; + +/* ------------------------------------------------------------------ */ + +static const u32 *ctrl_classes[] = { + cx2341x_mpeg_ctrls, + NULL +}; + +static int cx23885_queryctrl(struct cx23885_dev *dev, + struct v4l2_queryctrl *qctrl) +{ + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); + if (qctrl->id == 0) + return -EINVAL; + + /* MPEG V4L2 controls */ + if (cx2341x_ctrl_query(&dev->mpeg_params, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + + return 0; +} + +static int cx23885_querymenu(struct cx23885_dev *dev, + struct v4l2_querymenu *qmenu) +{ + struct v4l2_queryctrl qctrl; + + qctrl.id = qmenu->id; + cx23885_queryctrl(dev, &qctrl); + return v4l2_ctrl_query_menu(qmenu, &qctrl, + cx2341x_ctrl_get_menu(qmenu->id)); +} + +int cx23885_do_ioctl(struct inode *inode, struct file *file, int radio, + struct cx23885_dev *dev, unsigned int cmd, void *arg, + v4l2_kioctl driver_ioctl) +{ + int err; + + switch (cmd) { + /* ---------- tv norms ---------- */ + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *e = arg; + unsigned int i; + + i = e->index; + if (i >= ARRAY_SIZE(cx23885_tvnorms)) + return -EINVAL; + err = v4l2_video_std_construct(e, + cx23885_tvnorms[e->index].id, + cx23885_tvnorms[e->index].name); + e->index = i; + if (err < 0) + return err; + return 0; + } + case VIDIOC_G_STD: + { + v4l2_std_id *id = arg; + + *id = dev->encodernorm.id; + return 0; + } + case VIDIOC_S_STD: + { + v4l2_std_id *id = arg; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(cx23885_tvnorms); i++) + if (*id & cx23885_tvnorms[i].id) + break; + if (i == ARRAY_SIZE(cx23885_tvnorms)) + return -EINVAL; + dev->encodernorm = cx23885_tvnorms[i]; + + return 0; + } + + /* ------ input switching ---------- */ + case VIDIOC_ENUMINPUT: + { + struct cx23885_input *input; + struct v4l2_input *i = arg; + unsigned int n; + + n = i->index; + if (n >= 4) + return -EINVAL; + input = &cx23885_boards[dev->board].input[n]; + if (input->type == 0) + return -EINVAL; + memset(i, 0, sizeof(*i)); + i->index = n; + /* FIXME + * strcpy(i->name, input->name); */ + strcpy(i->name, "unset"); + if (input->type == CX23885_VMUX_TELEVISION || + input->type == CX23885_VMUX_CABLE) + i->type = V4L2_INPUT_TYPE_TUNER; + else + i->type = V4L2_INPUT_TYPE_CAMERA; + + for (n = 0; n < ARRAY_SIZE(cx23885_tvnorms); n++) + i->std |= cx23885_tvnorms[n].id; + return 0; + } + case VIDIOC_G_INPUT: + { + unsigned int *i = arg; + + *i = dev->input; + return 0; + } + case VIDIOC_S_INPUT: + { + unsigned int *i = arg; + + if (*i >= 4) + return -EINVAL; + + return 0; + } + + /* --- tuner ioctls ------------------------------------------ */ + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *t = arg; + + if (UNSET == dev->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + memset(t, 0, sizeof(*t)); + strcpy(t->name, "Television"); + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_TUNER, t); + cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t); + + dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type); + + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *t = arg; + + if (UNSET == dev->tuner_type) + return -EINVAL; + + /* Update the A/V core */ + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_TUNER, t); + + return 0; + } + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + memset(f, 0, sizeof(*f)); + if (UNSET == dev->tuner_type) + return -EINVAL; + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = dev->freq; + + /* Assumption that tuner is always on bus 1 */ + cx23885_call_i2c_clients(&dev->i2c_bus[1], + VIDIOC_G_FREQUENCY, f); + + return 0; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + dprintk(1, "VIDIOC_S_FREQUENCY: dev type %d, f\n", + dev->tuner_type); + dprintk(1, "VIDIOC_S_FREQUENCY: f tuner %d, f type %d\n", + f->tuner, f->type); + if (UNSET == dev->tuner_type) + return -EINVAL; + if (f->tuner != 0) + return -EINVAL; + if (f->type != V4L2_TUNER_ANALOG_TV) + return -EINVAL; + dev->freq = f->frequency; + + /* Assumption that tuner is always on bus 1 */ + cx23885_call_i2c_clients(&dev->i2c_bus[1], + VIDIOC_S_FREQUENCY, f); + return 0; + } + case VIDIOC_S_CTRL: + { + /* Update the A/V core */ + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_CTRL, arg); + return 0; + } + default: + /* Convert V4L ioctl to V4L2 and call mpeg_do_ioctl + * (driver_ioctl) */ + return v4l_compat_translate_ioctl(inode, file, cmd, arg, + driver_ioctl); + } + + return 0; +} + +static int mpeg_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + struct cx23885_tsport *tsport = &dev->ts1; + + if (v4l_debug > 1) + v4l_print_ioctl(dev->name, cmd); + + switch (cmd) { + + /* --- capabilities ------------------------------------------ */ + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = arg; + + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, dev->name); + strlcpy(cap->card, cx23885_boards[tsport->dev->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cap->version = CX23885_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + 0; + if (UNSET != dev->tuner_type) + cap->capabilities |= V4L2_CAP_TUNER; + + return 0; + } + + /* --- capture ioctls ---------------------------------------- */ + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *f = arg; + int index; + + index = f->index; + if (index != 0) + return -EINVAL; + + memset(f, 0, sizeof(*f)); + f->index = index; + strlcpy(f->description, "MPEG", sizeof(f->description)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->pixelformat = V4L2_PIX_FMT_MPEG; + return 0; + } + case VIDIOC_G_FMT: + { + struct v4l2_format *f = arg; + + memset(f, 0, sizeof(*f)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; + f->fmt.pix.colorspace = 0; + f->fmt.pix.width = dev->ts1.width; + f->fmt.pix.height = dev->ts1.height; + f->fmt.pix.field = fh->mpegq.field; + dprintk(1, "VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", + dev->ts1.width, dev->ts1.height, fh->mpegq.field); + return 0; + } + case VIDIOC_TRY_FMT: + { + struct v4l2_format *f = arg; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; + f->fmt.pix.sizeimage = + f->fmt.pix.colorspace = 0; + dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", + dev->ts1.width, dev->ts1.height, fh->mpegq.field); + return 0; + } + case VIDIOC_S_FMT: + { + struct v4l2_format *f = arg; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = + dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; + f->fmt.pix.colorspace = 0; + dprintk(1, "VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); + return 0; + } + + /* --- streaming capture ------------------------------------- */ + case VIDIOC_REQBUFS: + return videobuf_reqbufs(&fh->mpegq, arg); + + case VIDIOC_QUERYBUF: + return videobuf_querybuf(&fh->mpegq, arg); + + case VIDIOC_QBUF: + return videobuf_qbuf(&fh->mpegq, arg); + + case VIDIOC_DQBUF: + return videobuf_dqbuf(&fh->mpegq, arg, + file->f_flags & O_NONBLOCK); + + case VIDIOC_STREAMON: + return videobuf_streamon(&fh->mpegq); + + case VIDIOC_STREAMOFF: + return videobuf_streamoff(&fh->mpegq); + + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *f = arg; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + return cx2341x_ext_ctrls(&dev->mpeg_params, 0, f, cmd); + } + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *f = arg; + struct cx2341x_mpeg_params p; + int err; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + p = dev->mpeg_params; + err = cx2341x_ext_ctrls(&p, 0, f, cmd); + if (err == 0 && cmd == VIDIOC_S_EXT_CTRLS) { + err = cx2341x_update(dev, cx23885_mbox_func, + &dev->mpeg_params, &p); + dev->mpeg_params = p; + } + return err; + } + case VIDIOC_S_FREQUENCY: + { + cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + CX23885_END_NOW, CX23885_MPEG_CAPTURE, + CX23885_RAW_BITS_NONE); + cx23885_do_ioctl(inode, file, 0, dev, cmd, arg, + mpeg_do_ioctl); + cx23885_initialize_codec(dev); + + return 0; + } + case VIDIOC_LOG_STATUS: + { + char name[32 + 2]; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO + "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx23885_call_i2c_clients(&dev->i2c_bus[0], VIDIOC_LOG_STATUS, + NULL); + cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_LOG_STATUS, + NULL); + cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_LOG_STATUS, + NULL); + cx2341x_log_status(&dev->mpeg_params, name); + printk(KERN_INFO + "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; + } + case VIDIOC_QUERYMENU: + return cx23885_querymenu(dev, arg); + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *c = arg; + + return cx23885_queryctrl(dev, c); + } + + default: + return cx23885_do_ioctl(inode, file, 0, dev, cmd, arg, + mpeg_do_ioctl); + } + return 0; +} + +static int mpeg_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl); +} + +static int mpeg_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct cx23885_dev *h, *dev = NULL; + struct list_head *list; + struct cx23885_fh *fh; + + dprintk(2, "%s()\n", __func__); + + list_for_each(list, &cx23885_devlist) { + h = list_entry(list, struct cx23885_dev, devlist); + if (h->v4l_device->minor == minor) { + dev = h; + break; + } + } + + if (dev == NULL) + return -ENODEV; + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + + file->private_data = fh; + fh->dev = dev; + + videobuf_queue_sg_init(&fh->mpegq, &cx23885_qops, + &dev->pci->dev, &dev->ts1.slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx23885_buffer), + fh); + + return 0; +} + +static int mpeg_release(struct inode *inode, struct file *file) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + dprintk(2, "%s()\n", __func__); + + /* FIXME: Review this crap */ + /* Shut device down on last close */ + if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) { + if (atomic_dec_return(&dev->v4l_reader_count) == 0) { + /* stop mpeg capture */ + cx23885_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + CX23885_END_NOW, CX23885_MPEG_CAPTURE, + CX23885_RAW_BITS_NONE); + + msleep(500); + cx23885_417_check_encoder(dev); + + cx23885_cancel_buffers(&fh->dev->ts1); + } + } + + if (fh->mpegq.streaming) + videobuf_streamoff(&fh->mpegq); + if (fh->mpegq.reading) + videobuf_read_stop(&fh->mpegq); + + videobuf_mmap_free(&fh->mpegq); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static ssize_t mpeg_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + dprintk(2, "%s()\n", __func__); + + /* Deal w/ A/V decoder * and mpeg encoder sync issues. */ + /* Start mpeg encoder on first read. */ + if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) { + if (atomic_inc_return(&dev->v4l_reader_count) == 1) { + if (cx23885_initialize_codec(dev) < 0) + return -EINVAL; + } + } + + return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0, + file->f_flags & O_NONBLOCK); +} + +static unsigned int mpeg_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + dprintk(2, "%s\n", __func__); + + return videobuf_poll_stream(file, &fh->mpegq, wait); +} + +static int mpeg_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cx23885_fh *fh = file->private_data; + struct cx23885_dev *dev = fh->dev; + + dprintk(2, "%s()\n", __func__); + + return videobuf_mmap_mapper(&fh->mpegq, vma); +} + +static struct file_operations mpeg_fops = { + .owner = THIS_MODULE, + .open = mpeg_open, + .release = mpeg_release, + .read = mpeg_read, + .poll = mpeg_poll, + .mmap = mpeg_mmap, + .ioctl = mpeg_ioctl, + .llseek = no_llseek, +}; + +static struct video_device cx23885_mpeg_template = { + .name = "cx23885", + .type = VID_TYPE_CAPTURE | + VID_TYPE_TUNER | + VID_TYPE_SCALES | + VID_TYPE_MPEG_ENCODER, + .fops = &mpeg_fops, + .minor = -1, +}; + +void cx23885_417_unregister(struct cx23885_dev *dev) +{ + dprintk(1, "%s()\n", __func__); + + if (dev->v4l_device) { + if (-1 != dev->v4l_device->minor) + video_unregister_device(dev->v4l_device); + else + video_device_release(dev->v4l_device); + dev->v4l_device = NULL; + } +} + +static struct video_device *cx23885_video_dev_alloc( + struct cx23885_tsport *tsport, + struct pci_dev *pci, + struct video_device *template, + char *type) +{ + struct video_device *vfd; + struct cx23885_dev *dev = tsport->dev; + + dprintk(1, "%s()\n", __func__); + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + *vfd = *template; + vfd->minor = -1; + snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, + type, cx23885_boards[tsport->dev->board].name); + vfd->dev = &pci->dev; + vfd->release = video_device_release; + return vfd; +} + +int cx23885_417_register(struct cx23885_dev *dev) +{ + /* FIXME: Port1 hardcoded here */ + int err = -ENODEV; + struct cx23885_tsport *tsport = &dev->ts1; + + dprintk(1, "%s()\n", __func__); + + if (cx23885_boards[dev->board].portb != CX23885_MPEG_ENCODER) + return err; + + /* Set default TV standard */ + dev->encodernorm = cx23885_tvnorms[0]; + + if (dev->encodernorm.id & V4L2_STD_525_60) + tsport->height = 480; + else + tsport->height = 576; + + tsport->width = 720; + cx2341x_fill_defaults(&dev->mpeg_params); + + dev->mpeg_params.port = CX2341X_PORT_SERIAL; + + /* Allocate and initialize V4L video device */ + dev->v4l_device = cx23885_video_dev_alloc(tsport, + dev->pci, &cx23885_mpeg_template, "mpeg"); + err = video_register_device(dev->v4l_device, + VFL_TYPE_GRABBER, -1); + if (err < 0) { + printk(KERN_INFO "%s: can't register mpeg device\n", dev->name); + return err; + } + + /* Initialize MC417 registers */ + cx23885_mc417_init(dev); + + printk(KERN_INFO "%s: registered device video%d [mpeg]\n", + dev->name, dev->v4l_device->minor & 0x1f); + + return 0; +} diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index dfa269838e0..6ebf58724a0 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -73,6 +73,7 @@ struct cx23885_board cx23885_boards[] = { [CX23885_BOARD_HAUPPAUGE_HVR1800] = { .name = "Hauppauge WinTV-HVR1800", .porta = CX23885_ANALOG_VIDEO, + .portb = CX23885_MPEG_ENCODER, .portc = CX23885_MPEG_DVB, .tuner_type = TUNER_PHILIPS_TDA8290, .tuner_addr = 0x42, /* 0x84 >> 1 */ @@ -130,6 +131,18 @@ struct cx23885_board cx23885_boards[] = { .name = "Hauppauge WinTV-HVR1500", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_HVR1200] = { + .name = "Hauppauge WinTV-HVR1200", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1700] = { + .name = "Hauppauge WinTV-HVR1700", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1400] = { + .name = "Hauppauge WinTV-HVR1400", + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -181,6 +194,18 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x0070, .subdevice = 0x7717, .card = CX23885_BOARD_HAUPPAUGE_HVR1500, + }, { + .subvendor = 0x0070, + .subdevice = 0x71d1, + .card = CX23885_BOARD_HAUPPAUGE_HVR1200, + }, { + .subvendor = 0x0070, + .subdevice = 0x8101, + .card = CX23885_BOARD_HAUPPAUGE_HVR1700, + }, { + .subvendor = 0x0070, + .subdevice = 0x8010, + .card = CX23885_BOARD_HAUPPAUGE_HVR1400, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -235,6 +260,12 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) case 79561: /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ATSC and Basic analog */ case 79571: /* WinTV-HVR1250 (PCIe, OEM, No IR, full height, ATSC and Basic analog */ case 79671: /* WinTV-HVR1250 (PCIe, OEM, No IR, half height, ATSC and Basic analog */ + case 80019: + /* WinTV-HVR1400 (Express Card, Retail, IR, + * DVB-T and Basic analog */ + case 81519: + /* WinTV-HVR1700 (PCIe, Retail, No IR, half height, + * DVB-T and MPEG2 HW Encoder */ break; default: printk("%s: warning: unknown hauppauge model #%d\n", dev->name, tv.model); @@ -264,7 +295,7 @@ int cx23885_tuner_callback(void *priv, int command, int arg) } else { printk(KERN_ERR - "%s(): Unknow command.\n", __FUNCTION__); + "%s(): Unknow command.\n", __func__); return -EINVAL; } break; @@ -306,6 +337,10 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* GPIO-15-18 cx23417 READY, CS, RD, WR */ /* GPIO-19 IR_RX */ + /* CX23417 GPIO's */ + /* EIO15 Zilog Reset */ + /* EIO14 S5H1409/CX24227 Reset */ + /* Force the TDA8295A into reset and back */ cx_set(GP0_IO, 0x00040004); mdelay(20); @@ -314,6 +349,50 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00040004); mdelay(20); break; + case CX23885_BOARD_HAUPPAUGE_HVR1200: + /* GPIO-0 tda10048 demodulator reset */ + /* GPIO-2 tda18271 tuner reset */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x00050000); + mdelay(20); + cx_clear(GP0_IO, 0x00000005); + mdelay(20); + cx_set(GP0_IO, 0x00050005); + break; + case CX23885_BOARD_HAUPPAUGE_HVR1700: + /* GPIO-0 TDA10048 demodulator reset */ + /* GPIO-2 TDA8295A Reset */ + /* GPIO-3-10 cx23417 data0-7 */ + /* GPIO-11-14 cx23417 addr0-3 */ + /* GPIO-15-18 cx23417 READY, CS, RD, WR */ + + /* The following GPIO's are on the interna AVCore (cx25840) */ + /* GPIO-19 IR_RX */ + /* GPIO-20 IR_TX 416/DVBT Select */ + /* GPIO-21 IIS DAT */ + /* GPIO-22 IIS WCLK */ + /* GPIO-23 IIS BCLK */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x00050000); + mdelay(20); + cx_clear(GP0_IO, 0x00000005); + mdelay(20); + cx_set(GP0_IO, 0x00050005); + break; + case CX23885_BOARD_HAUPPAUGE_HVR1400: + /* GPIO-0 Dibcom7000p demodulator reset */ + /* GPIO-2 xc3028L tuner reset */ + /* GPIO-13 LED */ + + /* Put the parts into reset and back */ + cx_set(GP0_IO, 0x00050000); + mdelay(20); + cx_clear(GP0_IO, 0x00000005); + mdelay(20); + cx_set(GP0_IO, 0x00050005); + break; } } @@ -324,6 +403,8 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: case CX23885_BOARD_HAUPPAUGE_HVR1800: + case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1400: /* FIXME: Implement me */ break; } @@ -348,11 +429,14 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: + case CX23885_BOARD_HAUPPAUGE_HVR1400: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0x80); break; case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1800lp: + case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1700: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -364,17 +448,45 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_HAUPPAUGE_HVR1800: + /* Defaults for VID B - Analog encoder */ + /* DREQ_POL, SMODE, PUNC_CLK, MCLK_POL Serial bus + punc clk */ + ts1->gen_ctrl_val = 0x10e; + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + + /* APB_TSVALERR_POL (active low)*/ + ts1->vld_misc_val = 0x2000; + ts1->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4 | 0xc); + + /* Defaults for VID C */ + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: - case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1800lp: + case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1700: + case CX23885_BOARD_HAUPPAUGE_HVR1400: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; } + /* Certain boards support analog, or require the avcore to be + * loaded, ensure this happens. + */ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1800: + case CX23885_BOARD_HAUPPAUGE_HVR1800lp: + case CX23885_BOARD_HAUPPAUGE_HVR1700: + request_module("cx25840"); + break; + } } /* ------------------------------------------------------------------ */ diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index 7f10b273598..f24abcd06de 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -190,25 +190,25 @@ static struct sram_channel cx23887_sram_channels[] = { static int cx23885_risc_decode(u32 risc) { static char *instr[16] = { - [ RISC_SYNC >> 28 ] = "sync", - [ RISC_WRITE >> 28 ] = "write", - [ RISC_WRITEC >> 28 ] = "writec", - [ RISC_READ >> 28 ] = "read", - [ RISC_READC >> 28 ] = "readc", - [ RISC_JUMP >> 28 ] = "jump", - [ RISC_SKIP >> 28 ] = "skip", - [ RISC_WRITERM >> 28 ] = "writerm", - [ RISC_WRITECM >> 28 ] = "writecm", - [ RISC_WRITECR >> 28 ] = "writecr", + [RISC_SYNC >> 28] = "sync", + [RISC_WRITE >> 28] = "write", + [RISC_WRITEC >> 28] = "writec", + [RISC_READ >> 28] = "read", + [RISC_READC >> 28] = "readc", + [RISC_JUMP >> 28] = "jump", + [RISC_SKIP >> 28] = "skip", + [RISC_WRITERM >> 28] = "writerm", + [RISC_WRITECM >> 28] = "writecm", + [RISC_WRITECR >> 28] = "writecr", }; static int incr[16] = { - [ RISC_WRITE >> 28 ] = 3, - [ RISC_JUMP >> 28 ] = 3, - [ RISC_SKIP >> 28 ] = 1, - [ RISC_SYNC >> 28 ] = 1, - [ RISC_WRITERM >> 28 ] = 3, - [ RISC_WRITECM >> 28 ] = 3, - [ RISC_WRITECR >> 28 ] = 4, + [RISC_WRITE >> 28] = 3, + [RISC_JUMP >> 28] = 3, + [RISC_SKIP >> 28] = 1, + [RISC_SYNC >> 28] = 1, + [RISC_WRITERM >> 28] = 3, + [RISC_WRITECM >> 28] = 3, + [RISC_WRITECR >> 28] = 4, }; static char *bits[] = { "12", "13", "14", "resync", @@ -260,7 +260,7 @@ void cx23885_wakeup(struct cx23885_tsport *port, } if (bc != 1) printk("%s: %d buffers handled (should be 1)\n", - __FUNCTION__, bc); + __func__, bc); } int cx23885_sram_channel_setup(struct cx23885_dev *dev, @@ -272,7 +272,7 @@ int cx23885_sram_channel_setup(struct cx23885_dev *dev, if (ch->cmds_start == 0) { - dprintk(1, "%s() Erasing channel [%s]\n", __FUNCTION__, + dprintk(1, "%s() Erasing channel [%s]\n", __func__, ch->name); cx_write(ch->ptr1_reg, 0); cx_write(ch->ptr2_reg, 0); @@ -280,7 +280,7 @@ int cx23885_sram_channel_setup(struct cx23885_dev *dev, cx_write(ch->cnt1_reg, 0); return 0; } else { - dprintk(1, "%s() Configuring channel [%s]\n", __FUNCTION__, + dprintk(1, "%s() Configuring channel [%s]\n", __func__, ch->name); } @@ -297,7 +297,7 @@ int cx23885_sram_channel_setup(struct cx23885_dev *dev, /* write CDT */ for (i = 0; i < lines; i++) { - dprintk(2, "%s() 0x%08x <- 0x%08x\n", __FUNCTION__, cdt + 16*i, + dprintk(2, "%s() 0x%08x <- 0x%08x\n", __func__, cdt + 16*i, ch->fifo_start + bpl*i); cx_write(cdt + 16*i, ch->fifo_start + bpl*i); cx_write(cdt + 16*i + 4, 0); @@ -449,7 +449,7 @@ static void cx23885_shutdown(struct cx23885_dev *dev) static void cx23885_reset(struct cx23885_dev *dev) { - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); cx23885_shutdown(dev); @@ -482,7 +482,7 @@ static void cx23885_reset(struct cx23885_dev *dev) static int cx23885_pci_quirks(struct cx23885_dev *dev) { - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); /* The cx23885 bridge has a weird bug which causes NMI to be asserted * when DMA begins if RDR_TLCTL0 bit4 is not cleared. It does not @@ -513,11 +513,13 @@ int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *port, int portno) { - dprintk(1, "%s(portno=%d)\n", __FUNCTION__, portno); + dprintk(1, "%s(portno=%d)\n", __func__, portno); /* Transport bus init dma queue - Common settings */ port->dma_ctl_val = 0x11; /* Enable RISC controller and Fifo */ port->ts_int_msk_val = 0x1111; /* TS port bits for RISC */ + port->vld_misc_val = 0x0; + port->hw_sop_ctrl_val = (0x47 << 16 | 188 << 4); spin_lock_init(&port->slock); port->dev = dev; @@ -544,7 +546,7 @@ static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *p port->reg_ts_clk_en = VID_B_TS_CLK_EN; port->reg_src_sel = VID_B_SRC_SEL; port->reg_ts_int_msk = VID_B_INT_MSK; - port->reg_ts_int_stat = VID_B_INT_STAT; + port->reg_ts_int_stat = VID_B_INT_STAT; port->sram_chno = SRAM_CH03; /* VID_B */ port->pci_irqmask = 0x02; /* VID_B bit1 */ break; @@ -604,14 +606,14 @@ static void cx23885_dev_checkrevision(struct cx23885_dev *dev) break; default: printk(KERN_ERR "%s() New hardware revision found 0x%x\n", - __FUNCTION__, dev->hwrevision); + __func__, dev->hwrevision); } if (dev->hwrevision) printk(KERN_INFO "%s() Hardware revision = 0x%02x\n", - __FUNCTION__, dev->hwrevision); + __func__, dev->hwrevision); else printk(KERN_ERR "%s() Hardware revision unknown 0x%x\n", - __FUNCTION__, dev->hwrevision); + __func__, dev->hwrevision); } static int cx23885_dev_setup(struct cx23885_dev *dev) @@ -644,7 +646,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) BUG(); dprintk(1, "%s() Memory configured for PCIe bridge type %d\n", - __FUNCTION__, dev->bridge); + __func__, dev->bridge); /* board config */ dev->board = UNSET; @@ -697,10 +699,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dev->i2c_bus[2].reg_wdata = I2C3_WDATA; dev->i2c_bus[2].i2c_period = (0x07 << 24); /* 1.95MHz */ - if(cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) + if ((cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) || + (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER)) cx23885_init_tsport(dev, &dev->ts1, 1); - if(cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) + if ((cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) || + (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER)) cx23885_init_tsport(dev, &dev->ts2, 2); if (get_resources(dev) < 0) { @@ -734,9 +738,9 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dev->radio_addr = cx23885_boards[dev->board].radio_addr; dprintk(1, "%s() tuner_type = 0x%x tuner_addr = 0x%x\n", - __FUNCTION__, dev->tuner_type, dev->tuner_addr); + __func__, dev->tuner_type, dev->tuner_addr); dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n", - __FUNCTION__, dev->radio_type, dev->radio_addr); + __func__, dev->radio_type, dev->radio_addr); /* init hardware */ cx23885_reset(dev); @@ -744,28 +748,43 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_i2c_register(&dev->i2c_bus[0]); cx23885_i2c_register(&dev->i2c_bus[1]); cx23885_i2c_register(&dev->i2c_bus[2]); - cx23885_call_i2c_clients (&dev->i2c_bus[0], TUNER_SET_STANDBY, NULL); cx23885_card_setup(dev); + cx23885_call_i2c_clients (&dev->i2c_bus[0], TUNER_SET_STANDBY, NULL); cx23885_ir_init(dev); if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) { if (cx23885_video_register(dev) < 0) { printk(KERN_ERR "%s() Failed to register analog " - "video adapters on VID_A\n", __FUNCTION__); + "video adapters on VID_A\n", __func__); } } if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) { if (cx23885_dvb_register(&dev->ts1) < 0) { printk(KERN_ERR "%s() Failed to register dvb adapters on VID_B\n", - __FUNCTION__); + __func__); + } + } else + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) { + if (cx23885_417_register(dev) < 0) { + printk(KERN_ERR + "%s() Failed to register 417 on VID_B\n", + __func__); } } if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) { if (cx23885_dvb_register(&dev->ts2) < 0) { - printk(KERN_ERR "%s() Failed to register dvb adapters on VID_C\n", - __FUNCTION__); + printk(KERN_ERR + "%s() Failed to register dvb on VID_C\n", + __func__); + } + } else + if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER) { + if (cx23885_417_register(dev) < 0) { + printk(KERN_ERR + "%s() Failed to register 417 on VID_C\n", + __func__); } } @@ -785,12 +804,18 @@ static void cx23885_dev_unregister(struct cx23885_dev *dev) if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) cx23885_video_unregister(dev); - if(cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) + if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) cx23885_dvb_unregister(&dev->ts1); - if(cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) + cx23885_417_unregister(dev); + + if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) cx23885_dvb_unregister(&dev->ts2); + if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER) + cx23885_417_unregister(dev); + cx23885_i2c_unregister(&dev->i2c_bus[2]); cx23885_i2c_unregister(&dev->i2c_bus[1]); cx23885_i2c_unregister(&dev->i2c_bus[0]); @@ -952,7 +977,7 @@ void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf) videobuf_waiton(&buf->vb, 0, 0); videobuf_dma_unmap(q, dma); videobuf_dma_free(dma); - btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc); + btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); buf->vb.state = VIDEOBUF_NEEDS_INIT; } @@ -960,50 +985,50 @@ static void cx23885_tsport_reg_dump(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; - dprintk(1, "%s() Register Dump\n", __FUNCTION__); - dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __FUNCTION__, + dprintk(1, "%s() Register Dump\n", __func__); + dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __func__, cx_read(DEV_CNTRL2)); - dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __FUNCTION__, + dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __func__, cx_read(PCI_INT_MSK)); - dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __FUNCTION__, + dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __func__, cx_read(AUDIO_INT_INT_MSK)); - dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __FUNCTION__, + dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __func__, cx_read(AUD_INT_DMA_CTL)); - dprintk(1, "%s() AUD_EXT_INT_MSK 0x%08X\n", __FUNCTION__, + dprintk(1, "%s() AUD_EXT_INT_MSK 0x%08X\n", __func__, cx_read(AUDIO_EXT_INT_MSK)); - dprintk(1, "%s() AUD_EXT_DMA_CTL 0x%08X\n", __FUNCTION__, + dprintk(1, "%s() AUD_EXT_DMA_CTL 0x%08X\n", __func__, cx_read(AUD_EXT_DMA_CTL)); - dprintk(1, "%s() PAD_CTRL 0x%08X\n", __FUNCTION__, + dprintk(1, "%s() PAD_CTRL 0x%08X\n", __func__, cx_read(PAD_CTRL)); - dprintk(1, "%s() ALT_PIN_OUT_SEL 0x%08X\n", __FUNCTION__, + dprintk(1, "%s() ALT_PIN_OUT_SEL 0x%08X\n", __func__, cx_read(ALT_PIN_OUT_SEL)); - dprintk(1, "%s() GPIO2 0x%08X\n", __FUNCTION__, + dprintk(1, "%s() GPIO2 0x%08X\n", __func__, cx_read(GPIO2)); - dprintk(1, "%s() gpcnt(0x%08X) 0x%08X\n", __FUNCTION__, + dprintk(1, "%s() gpcnt(0x%08X) 0x%08X\n", __func__, port->reg_gpcnt, cx_read(port->reg_gpcnt)); - dprintk(1, "%s() gpcnt_ctl(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() gpcnt_ctl(0x%08X) 0x%08x\n", __func__, port->reg_gpcnt_ctl, cx_read(port->reg_gpcnt_ctl)); - dprintk(1, "%s() dma_ctl(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() dma_ctl(0x%08X) 0x%08x\n", __func__, port->reg_dma_ctl, cx_read(port->reg_dma_ctl)); - dprintk(1, "%s() src_sel(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() src_sel(0x%08X) 0x%08x\n", __func__, port->reg_src_sel, cx_read(port->reg_src_sel)); - dprintk(1, "%s() lngth(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() lngth(0x%08X) 0x%08x\n", __func__, port->reg_lngth, cx_read(port->reg_lngth)); - dprintk(1, "%s() hw_sop_ctrl(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() hw_sop_ctrl(0x%08X) 0x%08x\n", __func__, port->reg_hw_sop_ctrl, cx_read(port->reg_hw_sop_ctrl)); - dprintk(1, "%s() gen_ctrl(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() gen_ctrl(0x%08X) 0x%08x\n", __func__, port->reg_gen_ctrl, cx_read(port->reg_gen_ctrl)); - dprintk(1, "%s() bd_pkt_status(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() bd_pkt_status(0x%08X) 0x%08x\n", __func__, port->reg_bd_pkt_status, cx_read(port->reg_bd_pkt_status)); - dprintk(1, "%s() sop_status(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() sop_status(0x%08X) 0x%08x\n", __func__, port->reg_sop_status, cx_read(port->reg_sop_status)); - dprintk(1, "%s() fifo_ovfl_stat(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() fifo_ovfl_stat(0x%08X) 0x%08x\n", __func__, port->reg_fifo_ovfl_stat, cx_read(port->reg_fifo_ovfl_stat)); - dprintk(1, "%s() vld_misc(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() vld_misc(0x%08X) 0x%08x\n", __func__, port->reg_vld_misc, cx_read(port->reg_vld_misc)); - dprintk(1, "%s() ts_clk_en(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() ts_clk_en(0x%08X) 0x%08x\n", __func__, port->reg_ts_clk_en, cx_read(port->reg_ts_clk_en)); - dprintk(1, "%s() ts_int_msk(0x%08X) 0x%08x\n", __FUNCTION__, + dprintk(1, "%s() ts_int_msk(0x%08X) 0x%08x\n", __func__, port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk)); } @@ -1012,8 +1037,9 @@ static int cx23885_start_dma(struct cx23885_tsport *port, struct cx23885_buffer *buf) { struct cx23885_dev *dev = port->dev; + u32 reg; - dprintk(1, "%s() w: %d, h: %d, f: %d\n", __FUNCTION__, + dprintk(1, "%s() w: %d, h: %d, f: %d\n", __func__, buf->vb.width, buf->vb.height, buf->vb.field); /* setup fifo + format */ @@ -1031,21 +1057,24 @@ static int cx23885_start_dma(struct cx23885_tsport *port, if ( (!(cx23885_boards[dev->board].portb & CX23885_MPEG_DVB)) && (!(cx23885_boards[dev->board].portc & CX23885_MPEG_DVB)) ) { printk( "%s() Failed. Unsupported value in .portb/c (0x%08x)/(0x%08x)\n", - __FUNCTION__, + __func__, cx23885_boards[dev->board].portb, cx23885_boards[dev->board].portc ); return -EINVAL; } + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) + cx23885_av_clk(dev, 0); + udelay(100); /* If the port supports SRC SELECT, configure it */ if(port->reg_src_sel) cx_write(port->reg_src_sel, port->src_sel_val); - cx_write(port->reg_hw_sop_ctrl, 0x47 << 16 | 188 << 4); + cx_write(port->reg_hw_sop_ctrl, port->hw_sop_ctrl_val); cx_write(port->reg_ts_clk_en, port->ts_clk_en_val); - cx_write(port->reg_vld_misc, 0x00); + cx_write(port->reg_vld_misc, port->vld_misc_val); cx_write(port->reg_gen_ctrl, port->gen_ctrl_val); udelay(100); @@ -1054,11 +1083,26 @@ static int cx23885_start_dma(struct cx23885_tsport *port, cx_write(port->reg_gpcnt_ctl, 3); q->count = 1; + if (cx23885_boards[dev->board].portb & CX23885_MPEG_ENCODER) { + + reg = cx_read(PAD_CTRL); + reg = reg & ~0x1; /* Clear TS1_OE */ + + /* FIXME, bit 2 writing here is questionable */ + /* set TS1_SOP_OE and TS1_OE_HI */ + reg = reg | 0xa; + cx_write(PAD_CTRL, reg); + + /* FIXME and these two registers should be documented. */ + cx_write(CLK_DELAY, cx_read(CLK_DELAY) | 0x80000011); + cx_write(ALT_PIN_OUT_SEL, 0x10100045); + } + switch(dev->bridge) { case CX23885_BRIDGE_885: case CX23885_BRIDGE_887: /* enable irqs */ - dprintk(1, "%s() enabling TS int's and DMA\n", __FUNCTION__ ); + dprintk(1, "%s() enabling TS int's and DMA\n", __func__ ); cx_set(port->reg_ts_int_msk, port->ts_int_msk_val); cx_set(port->reg_dma_ctl, port->dma_ctl_val); cx_set(PCI_INT_MSK, dev->pci_irqmask | port->pci_irqmask); @@ -1069,6 +1113,9 @@ static int cx23885_start_dma(struct cx23885_tsport *port, cx_set(DEV_CNTRL2, (1<<5)); /* Enable RISC controller */ + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) + cx23885_av_clk(dev, 1); + if (debug > 4) cx23885_tsport_reg_dump(port); @@ -1078,12 +1125,32 @@ static int cx23885_start_dma(struct cx23885_tsport *port, static int cx23885_stop_dma(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; - dprintk(1, "%s()\n", __FUNCTION__); + u32 reg; + + dprintk(1, "%s()\n", __func__); /* Stop interrupts and DMA */ cx_clear(port->reg_ts_int_msk, port->ts_int_msk_val); cx_clear(port->reg_dma_ctl, port->dma_ctl_val); + if (cx23885_boards[dev->board].portb & CX23885_MPEG_ENCODER) { + + reg = cx_read(PAD_CTRL); + + /* Set TS1_OE */ + reg = reg | 0x1; + + /* clear TS1_SOP_OE and TS1_OE_HI */ + reg = reg & ~0xa; + cx_write(PAD_CTRL, reg); + cx_write(port->reg_src_sel, 0); + cx_write(port->reg_gen_ctrl, 8); + + } + + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) + cx23885_av_clk(dev, 0); + return 0; } @@ -1093,13 +1160,13 @@ int cx23885_restart_queue(struct cx23885_tsport *port, struct cx23885_dev *dev = port->dev; struct cx23885_buffer *buf; - dprintk(5, "%s()\n", __FUNCTION__); + dprintk(5, "%s()\n", __func__); if (list_empty(&q->active)) { struct cx23885_buffer *prev; prev = NULL; - dprintk(5, "%s() queue is empty\n", __FUNCTION__); + dprintk(5, "%s() queue is empty\n", __func__); for (;;) { if (list_empty(&q->queued)) @@ -1154,7 +1221,7 @@ int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port, int size = port->ts_packet_size * port->ts_packet_count; int rc; - dprintk(1, "%s: %p\n", __FUNCTION__, buf); + dprintk(1, "%s: %p\n", __func__, buf); if (0 != buf->vb.baddr && buf->vb.bsize < size) return -EINVAL; @@ -1197,7 +1264,7 @@ void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf) buf->count = cx88q->count++; mod_timer(&cx88q->timeout, jiffies + BUFFER_TIMEOUT); dprintk(1, "[%p/%d] %s - first active\n", - buf, buf->vb.i, __FUNCTION__); + buf, buf->vb.i, __func__); } else { dprintk( 1, "queue is not empty - append to active\n" ); prev = list_entry(cx88q->active.prev, struct cx23885_buffer, @@ -1208,7 +1275,7 @@ void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf) prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */ dprintk( 1, "[%p/%d] %s - append to active\n", - buf, buf->vb.i, __FUNCTION__); + buf, buf->vb.i, __func__); } } @@ -1239,13 +1306,23 @@ static void do_cancel_buffers(struct cx23885_tsport *port, char *reason, spin_unlock_irqrestore(&port->slock, flags); } +void cx23885_cancel_buffers(struct cx23885_tsport *port) +{ + struct cx23885_dev *dev = port->dev; + struct cx23885_dmaqueue *q = &port->mpegq; + + dprintk(1, "%s()\n", __FUNCTION__); + del_timer_sync(&q->timeout); + cx23885_stop_dma(port); + do_cancel_buffers(port, "cancel", 0); +} static void cx23885_timeout(unsigned long data) { struct cx23885_tsport *port = (struct cx23885_tsport *)data; struct cx23885_dev *dev = port->dev; - dprintk(1, "%s()\n",__FUNCTION__); + dprintk(1, "%s()\n",__func__); if (debug > 5) cx23885_sram_channel_dump(dev, &dev->sram_channels[ port->sram_chno ]); @@ -1254,16 +1331,77 @@ static void cx23885_timeout(unsigned long data) do_cancel_buffers(port, "timeout", 1); } +int cx23885_irq_417(struct cx23885_dev *dev, u32 status) +{ + /* FIXME: port1 assumption here. */ + struct cx23885_tsport *port = &dev->ts1; + int count = 0; + int handled = 0; + + if (status == 0) + return handled; + + count = cx_read(port->reg_gpcnt); + dprintk(7, "status: 0x%08x mask: 0x%08x count: 0x%x\n", + status, cx_read(port->reg_ts_int_msk), count); + + if ((status & VID_B_MSK_BAD_PKT) || + (status & VID_B_MSK_OPC_ERR) || + (status & VID_B_MSK_VBI_OPC_ERR) || + (status & VID_B_MSK_SYNC) || + (status & VID_B_MSK_VBI_SYNC) || + (status & VID_B_MSK_OF) || + (status & VID_B_MSK_VBI_OF)) { + printk(KERN_ERR "%s: V4L mpeg risc op code error, status " + "= 0x%x\n", dev->name, status); + if (status & VID_B_MSK_BAD_PKT) + dprintk(1, " VID_B_MSK_BAD_PKT\n"); + if (status & VID_B_MSK_OPC_ERR) + dprintk(1, " VID_B_MSK_OPC_ERR\n"); + if (status & VID_B_MSK_VBI_OPC_ERR) + dprintk(1, " VID_B_MSK_VBI_OPC_ERR\n"); + if (status & VID_B_MSK_SYNC) + dprintk(1, " VID_B_MSK_SYNC\n"); + if (status & VID_B_MSK_VBI_SYNC) + dprintk(1, " VID_B_MSK_VBI_SYNC\n"); + if (status & VID_B_MSK_OF) + dprintk(1, " VID_B_MSK_OF\n"); + if (status & VID_B_MSK_VBI_OF) + dprintk(1, " VID_B_MSK_VBI_OF\n"); + + cx_clear(port->reg_dma_ctl, port->dma_ctl_val); + cx23885_sram_channel_dump(dev, + &dev->sram_channels[port->sram_chno]); + cx23885_417_check_encoder(dev); + } else if (status & VID_B_MSK_RISCI1) { + dprintk(7, " VID_B_MSK_RISCI1\n"); + spin_lock(&port->slock); + cx23885_wakeup(port, &port->mpegq, count); + spin_unlock(&port->slock); + } else if (status & VID_B_MSK_RISCI2) { + dprintk(7, " VID_B_MSK_RISCI2\n"); + spin_lock(&port->slock); + cx23885_restart_queue(port, &port->mpegq); + spin_unlock(&port->slock); + } + if (status) { + cx_write(port->reg_ts_int_stat, status); + handled = 1; + } + + return handled; +} + static int cx23885_irq_ts(struct cx23885_tsport *port, u32 status) { struct cx23885_dev *dev = port->dev; int handled = 0; u32 count; - if ( (status & VID_BC_MSK_OPC_ERR) || - (status & VID_BC_MSK_BAD_PKT) || - (status & VID_BC_MSK_SYNC) || - (status & VID_BC_MSK_OF)) + if ((status & VID_BC_MSK_OPC_ERR) || + (status & VID_BC_MSK_BAD_PKT) || + (status & VID_BC_MSK_SYNC) || + (status & VID_BC_MSK_OF)) { if (status & VID_BC_MSK_OPC_ERR) dprintk(7, " (VID_BC_MSK_OPC_ERR 0x%08x)\n", VID_BC_MSK_OPC_ERR); @@ -1277,7 +1415,8 @@ static int cx23885_irq_ts(struct cx23885_tsport *port, u32 status) printk(KERN_ERR "%s: mpeg risc op code error\n", dev->name); cx_clear(port->reg_dma_ctl, port->dma_ctl_val); - cx23885_sram_channel_dump(dev, &dev->sram_channels[ port->sram_chno ]); + cx23885_sram_channel_dump(dev, + &dev->sram_channels[port->sram_chno]); } else if (status & VID_BC_MSK_RISCI1) { @@ -1378,11 +1517,17 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) if (ts1_status) { if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) handled += cx23885_irq_ts(ts1, ts1_status); + else + if (cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) + handled += cx23885_irq_417(dev, ts1_status); } if (ts2_status) { if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) handled += cx23885_irq_ts(ts2, ts2_status); + else + if (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER) + handled += cx23885_irq_417(dev, ts2_status); } if (vida_status) @@ -1422,7 +1567,8 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " "latency: %d, mmio: 0x%llx\n", dev->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, - dev->pci_lat, (unsigned long long)pci_resource_start(pci_dev,0)); + dev->pci_lat, + (unsigned long long)pci_resource_start(pci_dev, 0)); pci_set_master(pci_dev); if (!pci_dma_supported(pci_dev, 0xffffffff)) { diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index ed465c007ce..870d6e197d6 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -36,9 +36,12 @@ #include "tda18271.h" #include "lgdt330x.h" #include "xc5000.h" +#include "tda10048.h" #include "dvb-pll.h" #include "tuner-xc2028.h" -#include "tuner-xc2028-types.h" +#include "tuner-simple.h" +#include "dib7000p.h" +#include "dibx000_common.h" static unsigned int debug; @@ -53,6 +56,8 @@ static unsigned int alt_tuner; module_param(alt_tuner, int, 0644); MODULE_PARM_DESC(alt_tuner, "Enable alternate tuner configuration"); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + /* ------------------------------------------------------------------ */ static int dvb_buf_setup(struct videobuf_queue *q, @@ -104,6 +109,13 @@ static struct s5h1409_config hauppauge_generic_config = { .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, }; +static struct tda10048_config hauppauge_hvr1200_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_SERIAL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON +}; + static struct s5h1409_config hauppauge_ezqam_config = { .demod_address = 0x32 >> 1, .output_mode = S5H1409_SERIAL_OUTPUT, @@ -164,8 +176,10 @@ static struct tda829x_config tda829x_no_probe = { }; static struct tda18271_std_map hauppauge_tda18271_std_map = { - .atsc_6 = { .if_freq = 5380, .std_bits = 0x1b }, - .qam_6 = { .if_freq = 4000, .std_bits = 0x18 }, + .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3, + .if_lvl = 6, .rfagc_top = 0x37 }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, + .if_lvl = 6, .rfagc_top = 0x37 }, }; static struct tda18271_config hauppauge_tda18271_config = { @@ -173,6 +187,96 @@ static struct tda18271_config hauppauge_tda18271_config = { .gate = TDA18271_GATE_ANALOG, }; +static struct tda18271_config hauppauge_hvr1200_tuner_config = { + .gate = TDA18271_GATE_ANALOG, +}; + +struct dibx000_agc_config xc3028_agc_config = { + BAND_VHF | BAND_UHF, /* band_caps */ + + /* P_agc_use_sd_mod1=0, P_agc_use_sd_mod2=0, P_agc_freq_pwm_div=0, + * 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) | (0 << 11) | (0 << 10) | (0 << 9) | (0 << 8) | + (3 << 5) | (0 << 4) | (2 << 1) | (0 << 0), /* setup */ + + 712, /* inv_gain */ + 21, /* time_stabiliz */ + + 0, /* alpha_level */ + 118, /* thlock */ + + 0, /* wbd_inv */ + 2867, /* wbd_ref */ + 0, /* wbd_sel */ + 2, /* wbd_alpha */ + + 0, /* agc1_max */ + 0, /* agc1_min */ + 39718, /* agc2_max */ + 9930, /* agc2_min */ + 0, /* agc1_pt1 */ + 0, /* agc1_pt2 */ + 0, /* agc1_pt3 */ + 0, /* agc1_slope1 */ + 0, /* agc1_slope2 */ + 0, /* agc2_pt1 */ + 128, /* agc2_pt2 */ + 29, /* agc2_slope1 */ + 29, /* agc2_slope2 */ + + 17, /* alpha_mant */ + 27, /* alpha_exp */ + 23, /* beta_mant */ + 51, /* beta_exp */ + + 1, /* perform_agc_softsplit */ +}; + +/* PLL Configuration for COFDM BW_MHz = 8.000000 + * With external clock = 30.000000 */ +struct dibx000_bandwidth_config xc3028_bw_config = { + 60000, /* internal */ + 30000, /* sampling */ + 1, /* pll_cfg: prediv */ + 8, /* pll_cfg: ratio */ + 3, /* pll_cfg: range */ + 1, /* pll_cfg: reset */ + 0, /* pll_cfg: bypass */ + 0, /* misc: refdiv */ + 0, /* misc: bypclk_div */ + 1, /* misc: IO_CLK_en_core */ + 1, /* misc: ADClkSrc */ + 0, /* misc: modulo */ + (3 << 14) | (1 << 12) | (524 << 0), /* sad_cfg: refsel, sel, freq_15k */ + (1 << 25) | 5816102, /* ifreq = 5.200000 MHz */ + 20452225, /* timf */ + 30000000 /* xtal_hz */ +}; + +static struct dib7000p_config hauppauge_hvr1400_dib7000_config = { + .output_mpeg2_in_188_bytes = 1, + .hostbus_diversity = 1, + .tuner_is_baseband = 0, + .update_lna = NULL, + + .agc_config_count = 1, + .agc = &xc3028_agc_config, + .bw = &xc3028_bw_config, + + .gpio_dir = DIB7000P_GPIO_DEFAULT_DIRECTIONS, + .gpio_val = DIB7000P_GPIO_DEFAULT_VALUES, + .gpio_pwm_pos = DIB7000P_GPIO_DEFAULT_PWM_POS, + + .pwm_freq_div = 0, + .agc_control = NULL, + .spur_protect = 0, + + .output_mode = OUTMODE_MPEG2_SERIAL, +}; + static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg) { struct cx23885_tsport *port = ptr; @@ -182,7 +286,7 @@ static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg) case XC2028_TUNER_RESET: /* Send the tuner in then out of reset */ /* GPIO-2 xc3028 tuner */ - dprintk(1, "%s: XC2028_TUNER_RESET %d\n", __FUNCTION__, arg); + dprintk(1, "%s: XC2028_TUNER_RESET %d\n", __func__, arg); cx_set(GP0_IO, 0x00040000); cx_clear(GP0_IO, 0x00000004); @@ -192,10 +296,10 @@ static int cx23885_hvr1500_xc3028_callback(void *ptr, int command, int arg) msleep(5); break; case XC2028_RESET_CLK: - dprintk(1, "%s: XC2028_RESET_CLK %d\n", __FUNCTION__, arg); + dprintk(1, "%s: XC2028_RESET_CLK %d\n", __func__, arg); break; default: - dprintk(1, "%s: unknown command %d, arg %d\n", __FUNCTION__, + dprintk(1, "%s: unknown command %d, arg %d\n", __func__, command, arg); return -EINVAL; } @@ -271,8 +375,9 @@ static int dvb_register(struct cx23885_tsport *port) &fusionhdtv_5_express, &i2c_bus->i2c_adap); if (port->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, port->dvb.frontend, 0x61, - &i2c_bus->i2c_adap, DVB_PLL_LG_TDVS_H06XF); + dvb_attach(simple_tuner_attach, port->dvb.frontend, + &i2c_bus->i2c_adap, 0x61, + TUNER_LG_TDVS_H06XF); } break; case CX23885_BOARD_HAUPPAUGE_HVR1500Q: @@ -297,13 +402,52 @@ static int dvb_register(struct cx23885_tsport *port) struct xc2028_config cfg = { .i2c_adap = &i2c_bus->i2c_adap, .i2c_addr = 0x61, - .video_dev = port, .callback = cx23885_hvr1500_xc3028_callback, }; static struct xc2028_ctrl ctl = { .fname = "xc3028-v27.fw", .max_len = 64, - .scode_table = OREN538, + .scode_table = XC3028_FE_OREN538, + }; + + fe = dvb_attach(xc2028_attach, + port->dvb.frontend, &cfg); + if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) + fe->ops.tuner_ops.set_config(fe, &ctl); + } + break; + case CX23885_BOARD_HAUPPAUGE_HVR1200: + case CX23885_BOARD_HAUPPAUGE_HVR1700: + i2c_bus = &dev->i2c_bus[0]; + port->dvb.frontend = dvb_attach(tda10048_attach, + &hauppauge_hvr1200_config, + &i2c_bus->i2c_adap); + if (port->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, port->dvb.frontend, + &dev->i2c_bus[1].i2c_adap, 0x42, + &tda829x_no_probe); + dvb_attach(tda18271_attach, port->dvb.frontend, + 0x60, &dev->i2c_bus[1].i2c_adap, + &hauppauge_hvr1200_tuner_config); + } + break; + case CX23885_BOARD_HAUPPAUGE_HVR1400: + i2c_bus = &dev->i2c_bus[0]; + port->dvb.frontend = dvb_attach(dib7000p_attach, + &i2c_bus->i2c_adap, + 0x12, &hauppauge_hvr1400_dib7000_config); + if (port->dvb.frontend != NULL) { + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_bus[1].i2c_adap, + .i2c_addr = 0x64, + .callback = cx23885_hvr1500_xc3028_callback, + }; + static struct xc2028_ctrl ctl = { + .fname = "xc3028L-v36.fw", + .max_len = 64, + .demod = 5000, + .d2633 = 1 }; fe = dvb_attach(xc2028_attach, @@ -330,7 +474,7 @@ static int dvb_register(struct cx23885_tsport *port) /* register everything */ return videobuf_dvb_register(&port->dvb, THIS_MODULE, port, - &dev->pci->dev); + &dev->pci->dev, adapter_nr); } int cx23885_dvb_register(struct cx23885_tsport *port) @@ -338,7 +482,7 @@ int cx23885_dvb_register(struct cx23885_tsport *port) struct cx23885_dev *dev = port->dev; int err; - dprintk(1, "%s\n", __FUNCTION__); + dprintk(1, "%s\n", __func__); dprintk(1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", dev->board, dev->name, @@ -349,12 +493,12 @@ int cx23885_dvb_register(struct cx23885_tsport *port) /* dvb stuff */ printk("%s: cx23885 based dvb card\n", dev->name); - videobuf_queue_pci_init(&port->dvb.dvbq, &dvb_qops, dev->pci, &port->slock, + videobuf_queue_sg_init(&port->dvb.dvbq, &dvb_qops, &dev->pci->dev, &port->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP, sizeof(struct cx23885_buffer), port); err = dvb_register(port); if (err != 0) - printk("%s() dvb_register failed err = %d\n", __FUNCTION__, err); + printk("%s() dvb_register failed err = %d\n", __func__, err); return err; } diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c index 92fe0bd37c8..c6bb0a05bc1 100644 --- a/drivers/media/video/cx23885/cx23885-i2c.c +++ b/drivers/media/video/cx23885/cx23885-i2c.c @@ -33,7 +33,7 @@ static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); -static unsigned int i2c_scan = 0; +static unsigned int i2c_scan; module_param(i2c_scan, int, 0444); MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); @@ -87,10 +87,10 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap, int retval, cnt; if (joined_rlen) - dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __FUNCTION__, + dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __func__, msg->len, joined_rlen); else - dprintk(1, "%s(msg->len=%d)\n", __FUNCTION__, msg->len); + dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len); /* Deal with i2c probe functions with zero payload */ if (msg->len == 0) { @@ -101,7 +101,7 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap, if (!i2c_slave_did_ack(i2c_adap)) return -EIO; - dprintk(1, "%s() returns 0\n", __FUNCTION__); + dprintk(1, "%s() returns 0\n", __func__); return 0; } @@ -176,7 +176,7 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap, if (i2c_debug && !joined) - dprintk(1, "%s(msg->len=%d)\n", __FUNCTION__, msg->len); + dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len); /* Deal with i2c probe functions with zero payload */ if (msg->len == 0) { @@ -188,7 +188,7 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap, return -EIO; - dprintk(1, "%s() returns 0\n", __FUNCTION__); + dprintk(1, "%s() returns 0\n", __func__); return 0; } @@ -238,11 +238,11 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap, struct cx23885_dev *dev = bus->dev; int i, retval = 0; - dprintk(1, "%s(num = %d)\n", __FUNCTION__, num); + dprintk(1, "%s(num = %d)\n", __func__, num); for (i = 0 ; i < num; i++) { dprintk(1, "%s(num = %d) addr = 0x%02x len = 0x%x\n", - __FUNCTION__, num, msgs[i].addr, msgs[i].len); + __func__, num, msgs[i].addr, msgs[i].len); if (msgs[i].flags & I2C_M_RD) { /* read */ retval = i2c_readbytes(i2c_adap, &msgs[i], 0); @@ -353,6 +353,8 @@ static struct i2c_client cx23885_i2c_client_template = { }; static char *i2c_devs[128] = { + [0x10 >> 1] = "tda10048", + [0x12 >> 1] = "dib7000pc", [ 0x1c >> 1 ] = "lgdt3303", [ 0x86 >> 1 ] = "tda9887", [ 0x32 >> 1 ] = "cx24227", @@ -360,7 +362,8 @@ static char *i2c_devs[128] = { [ 0x84 >> 1 ] = "tda8295", [ 0xa0 >> 1 ] = "eeprom", [ 0xc0 >> 1 ] = "tuner/mt2131/tda8275", - [ 0xc2 >> 1 ] = "tuner/mt2131/tda8275/xc5000", + [0xc2 >> 1] = "tuner/mt2131/tda8275/xc5000/xc3028", + [0xc8 >> 1] = "tuner/xc3028L", }; static void do_i2c_scan(char *name, struct i2c_client *c) @@ -383,7 +386,7 @@ int cx23885_i2c_register(struct cx23885_i2c *bus) { struct cx23885_dev *dev = bus->dev; - dprintk(1, "%s(bus = %d)\n", __FUNCTION__, bus->nr); + dprintk(1, "%s(bus = %d)\n", __func__, bus->nr); memcpy(&bus->i2c_adap, &cx23885_i2c_adap_template, sizeof(bus->i2c_adap)); @@ -420,6 +423,29 @@ int cx23885_i2c_unregister(struct cx23885_i2c *bus) return 0; } +void cx23885_av_clk(struct cx23885_dev *dev, int enable) +{ + /* write 0 to bus 2 addr 0x144 via i2x_xfer() */ + char buffer[3]; + struct i2c_msg msg; + dprintk(1, "%s(enabled = %d)\n", __func__, enable); + + /* Register 0x144 */ + buffer[0] = 0x01; + buffer[1] = 0x44; + if (enable == 1) + buffer[2] = 0x05; + else + buffer[2] = 0x00; + + msg.addr = 0x44; + msg.flags = I2C_M_TEN; + msg.len = 3; + msg.buf = buffer; + + i2c_xfer(&dev->i2c_bus[2].i2c_adap, &msg, 1); +} + /* ----------------------------------------------------------------------- */ /* diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index d3c4d2c5cbe..84652210a28 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -141,7 +141,7 @@ static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc) if (formats[i].fourcc == fourcc) return formats+i; - printk(KERN_ERR "%s(0x%08x) NOT FOUND\n", __FUNCTION__, fourcc); + printk(KERN_ERR "%s(0x%08x) NOT FOUND\n", __func__, fourcc); return NULL; } @@ -292,13 +292,13 @@ void cx23885_video_wakeup(struct cx23885_dev *dev, } if (bc != 1) printk(KERN_ERR "%s: %d buffers handled (should be 1)\n", - __FUNCTION__, bc); + __func__, bc); } int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) { dprintk(1, "%s(norm = 0x%08x) name: [%s]\n", - __FUNCTION__, + __func__, (unsigned int)norm, v4l2_norm_to_name(norm)); @@ -319,7 +319,7 @@ struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, char *type) { struct video_device *vfd; - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); vfd = video_device_alloc(); if (NULL == vfd) @@ -358,7 +358,7 @@ EXPORT_SYMBOL(cx23885_ctrl_query); static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh, unsigned int bit) { - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); if (fh->resources & bit) /* have it already allocated */ return 1; @@ -392,7 +392,7 @@ static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh, unsigned int bits) { BUG_ON((fh->resources & bits) != bits); - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); mutex_lock(&dev->lock); fh->resources &= ~bits; @@ -407,7 +407,7 @@ int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) memset(&route, 0, sizeof(route)); dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n", - __FUNCTION__, + __func__, input, INPUT(input)->vmux, INPUT(input)->gpio0, INPUT(input)->gpio1, INPUT(input)->gpio2, INPUT(input)->gpio3); @@ -427,7 +427,7 @@ EXPORT_SYMBOL(cx23885_video_mux); int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width, unsigned int height, enum v4l2_field field) { - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); return 0; } @@ -435,7 +435,7 @@ static int cx23885_start_video_dma(struct cx23885_dev *dev, struct cx23885_dmaqueue *q, struct cx23885_buffer *buf) { - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); /* setup fifo + format */ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01], @@ -463,7 +463,7 @@ static int cx23885_restart_video_queue(struct cx23885_dev *dev, { struct cx23885_buffer *buf, *prev; struct list_head *item; - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); if (!list_empty(&q->active)) { buf = list_entry(q->active.next, struct cx23885_buffer, @@ -579,13 +579,13 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, if (dev->tvnorm & V4L2_STD_NTSC) { /* cx25840 transmits NTSC bottom field first */ dprintk(1, "%s() Creating NTSC risc\n", - __FUNCTION__); + __func__); line0_offset = buf->bpl; line1_offset = 0; } else { /* All other formats are top field first */ dprintk(1, "%s() Creating PAL/SECAM risc\n", - __FUNCTION__); + __func__); line0_offset = 0; line1_offset = buf->bpl; } @@ -765,8 +765,8 @@ static int video_open(struct inode *inode, struct file *file) fh->height = 240; fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); - videobuf_queue_pci_init(&fh->vidq, &cx23885_video_qops, - dev->pci, &dev->slock, + videobuf_queue_sg_init(&fh->vidq, &cx23885_video_qops, + &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct cx23885_buffer), @@ -885,7 +885,7 @@ static int video_mmap(struct file *file, struct vm_area_struct *vma) int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl) { - dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __FUNCTION__); + dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__); cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_CTRL, ctl); return 0; } @@ -894,7 +894,7 @@ EXPORT_SYMBOL(cx23885_get_control); int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl) { dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)" - " (disabled - no action)\n", __FUNCTION__); + " (disabled - no action)\n", __func__); return 0; } EXPORT_SYMBOL(cx23885_set_control); @@ -990,7 +990,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; int err; - dprintk(2, "%s()\n", __FUNCTION__); + dprintk(2, "%s()\n", __func__); err = vidioc_try_fmt_cap(file, priv, f); if (0 != err) @@ -999,7 +999,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, fh->width = f->fmt.pix.width; fh->height = f->fmt.pix.height; fh->vidq.field = f->fmt.pix.field; - dprintk(2, "%s() width=%d height=%d field=%d\n", __FUNCTION__, + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field); cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_FMT, f); return 0; @@ -1101,7 +1101,7 @@ static int vidioc_streamon(struct file *file, void *priv, { struct cx23885_fh *fh = priv; struct cx23885_dev *dev = fh->dev; - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) return -EINVAL; @@ -1118,7 +1118,7 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) struct cx23885_fh *fh = priv; struct cx23885_dev *dev = fh->dev; int err, res; - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -1136,7 +1136,7 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms) { struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); mutex_lock(&dev->lock); cx23885_set_tvnorm(dev, *tvnorms); @@ -1159,7 +1159,7 @@ int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) [CX23885_VMUX_DEBUG] = "for debug only", }; unsigned int n; - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); n = i->index; if (n >= 4) @@ -1184,7 +1184,7 @@ static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) { struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); return cx23885_enum_input(dev, i); } @@ -1193,7 +1193,7 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; *i = dev->input; - dprintk(1, "%s() returns %d\n", __FUNCTION__, *i); + dprintk(1, "%s() returns %d\n", __func__, *i); return 0; } @@ -1201,10 +1201,10 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev; - dprintk(1, "%s(%d)\n", __FUNCTION__, i); + dprintk(1, "%s(%d)\n", __func__, i); if (i >= 4) { - dprintk(1, "%s() -EINVAL\n", __FUNCTION__); + dprintk(1, "%s() -EINVAL\n", __func__); return -EINVAL; } @@ -1389,7 +1389,7 @@ int cx23885_video_irq(struct cx23885_dev *dev, u32 status) return handled; cx_write(VID_A_INT_STAT, status); - dprintk(2, "%s() status = 0x%08x\n", __FUNCTION__, status); + dprintk(2, "%s() status = 0x%08x\n", __func__, status); /* risc op code error */ if (status & (1 << 16)) { printk(KERN_WARNING "%s/0: video risc op code error\n", @@ -1487,7 +1487,7 @@ static const struct file_operations radio_fops = { void cx23885_video_unregister(struct cx23885_dev *dev) { - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); cx_clear(PCI_INT_MSK, 1); if (dev->video_dev) { @@ -1505,7 +1505,7 @@ int cx23885_video_register(struct cx23885_dev *dev) { int err; - dprintk(1, "%s()\n", __FUNCTION__); + dprintk(1, "%s()\n", __func__); spin_lock_init(&dev->slock); /* Initialize VBI template */ diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 7cb2179f262..32af87f25e7 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -32,6 +32,7 @@ #include "btcx-risc.h" #include "cx23885-reg.h" +#include "media/cx2341x.h" #include <linux/version.h> #include <linux/mutex.h> @@ -59,6 +60,9 @@ #define CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP 4 #define CX23885_BOARD_HAUPPAUGE_HVR1500Q 5 #define CX23885_BOARD_HAUPPAUGE_HVR1500 6 +#define CX23885_BOARD_HAUPPAUGE_HVR1200 7 +#define CX23885_BOARD_HAUPPAUGE_HVR1700 8 +#define CX23885_BOARD_HAUPPAUGE_HVR1400 9 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ @@ -154,6 +158,7 @@ typedef enum { CX23885_MPEG_UNDEFINED = 0, CX23885_MPEG_DVB, CX23885_ANALOG_VIDEO, + CX23885_MPEG_ENCODER, } port_t; struct cx23885_board { @@ -252,6 +257,8 @@ struct cx23885_tsport { u32 gen_ctrl_val; u32 ts_clk_en_val; u32 src_sel_val; + u32 vld_misc_val; + u32 hw_sop_ctrl_val; }; struct cx23885_dev { @@ -312,6 +319,14 @@ struct cx23885_dev { struct cx23885_dmaqueue vidq; struct cx23885_dmaqueue vbiq; spinlock_t slock; + + /* MPEG Encoder ONLY settings */ + u32 cx23417_mailbox; + struct cx2341x_mpeg_params mpeg_params; + struct video_device *v4l_device; + atomic_t v4l_reader_count; + struct cx23885_tvnorm encodernorm; + }; extern struct list_head cx23885_devlist; @@ -431,6 +446,18 @@ extern int cx23885_i2c_register(struct cx23885_i2c *bus); extern int cx23885_i2c_unregister(struct cx23885_i2c *bus); extern void cx23885_call_i2c_clients(struct cx23885_i2c *bus, unsigned int cmd, void *arg); +extern void cx23885_av_clk(struct cx23885_dev *dev, int enable); + +/* ----------------------------------------------------------- */ +/* cx23885-417.c */ +extern int cx23885_417_register(struct cx23885_dev *dev); +extern void cx23885_417_unregister(struct cx23885_dev *dev); +extern int cx23885_irq_417(struct cx23885_dev *dev, u32 status); +extern void cx23885_417_check_encoder(struct cx23885_dev *dev); +extern void cx23885_mc417_init(struct cx23885_dev *dev); +extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value); +extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value); + /* ----------------------------------------------------------- */ /* tv norms */ diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 756a1eeb274..7fde678b2c4 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -352,7 +352,7 @@ static void cx23885_initialize(struct i2c_client *client) static void input_change(struct i2c_client *client) { struct cx25840_state *state = i2c_get_clientdata(client); - v4l2_std_id std = cx25840_get_v4lstd(client); + v4l2_std_id std = state->std; /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */ if (std & V4L2_STD_SECAM) { @@ -523,32 +523,34 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp /* ----------------------------------------------------------------------- */ -static int set_v4lstd(struct i2c_client *client, v4l2_std_id std) +static int set_v4lstd(struct i2c_client *client) { - u8 fmt=0; /* zero is autodetect */ + struct cx25840_state *state = i2c_get_clientdata(client); + u8 fmt = 0; /* zero is autodetect */ + u8 pal_m = 0; /* First tests should be against specific std */ - if (std == V4L2_STD_NTSC_M_JP) { - fmt=0x2; - } else if (std == V4L2_STD_NTSC_443) { - fmt=0x3; - } else if (std == V4L2_STD_PAL_M) { - fmt=0x5; - } else if (std == V4L2_STD_PAL_N) { - fmt=0x6; - } else if (std == V4L2_STD_PAL_Nc) { - fmt=0x7; - } else if (std == V4L2_STD_PAL_60) { - fmt=0x8; + if (state->std == V4L2_STD_NTSC_M_JP) { + fmt = 0x2; + } else if (state->std == V4L2_STD_NTSC_443) { + fmt = 0x3; + } else if (state->std == V4L2_STD_PAL_M) { + pal_m = 1; + fmt = 0x5; + } else if (state->std == V4L2_STD_PAL_N) { + fmt = 0x6; + } else if (state->std == V4L2_STD_PAL_Nc) { + fmt = 0x7; + } else if (state->std == V4L2_STD_PAL_60) { + fmt = 0x8; } else { /* Then, test against generic ones */ - if (std & V4L2_STD_NTSC) { - fmt=0x1; - } else if (std & V4L2_STD_PAL) { - fmt=0x4; - } else if (std & V4L2_STD_SECAM) { - fmt=0xc; - } + if (state->std & V4L2_STD_NTSC) + fmt = 0x1; + else if (state->std & V4L2_STD_PAL) + fmt = 0x4; + else if (state->std & V4L2_STD_SECAM) + fmt = 0xc; } v4l_dbg(1, cx25840_debug, client, "changing video std to fmt %i\n",fmt); @@ -563,42 +565,13 @@ static int set_v4lstd(struct i2c_client *client, v4l2_std_id std) cx25840_and_or(client, 0x47b, ~6, 0); } cx25840_and_or(client, 0x400, ~0xf, fmt); + cx25840_and_or(client, 0x403, ~0x3, pal_m); cx25840_vbi_setup(client); + if (!state->is_cx25836) + input_change(client); return 0; } -v4l2_std_id cx25840_get_v4lstd(struct i2c_client * client) -{ - struct cx25840_state *state = i2c_get_clientdata(client); - /* check VID_FMT_SEL first */ - u8 fmt = cx25840_read(client, 0x400) & 0xf; - - if (!fmt) { - /* check AFD_FMT_STAT if set to autodetect */ - fmt = cx25840_read(client, 0x40d) & 0xf; - } - - switch (fmt) { - case 0x1: - { - /* if the audio std is A2-M, then this is the South Korean - NTSC standard */ - if (!state->is_cx25836 && cx25840_read(client, 0x805) == 2) - return V4L2_STD_NTSC_M_KR; - return V4L2_STD_NTSC_M; - } - case 0x2: return V4L2_STD_NTSC_M_JP; - case 0x3: return V4L2_STD_NTSC_443; - case 0x4: return V4L2_STD_PAL; - case 0x5: return V4L2_STD_PAL_M; - case 0x6: return V4L2_STD_PAL_N; - case 0x7: return V4L2_STD_PAL_Nc; - case 0x8: return V4L2_STD_PAL_60; - case 0xc: return V4L2_STD_SECAM; - default: return V4L2_STD_UNKNOWN; - } -} - /* ----------------------------------------------------------------------- */ static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) @@ -718,9 +691,10 @@ static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) { + struct cx25840_state *state = i2c_get_clientdata(client); struct v4l2_pix_format *pix; int HSC, VSC, Vsrc, Hsrc, filter, Vlines; - int is_50Hz = !(cx25840_get_v4lstd(client) & V4L2_STD_525_60); + int is_50Hz = !(state->std & V4L2_STD_525_60); switch (fmt->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: @@ -1096,12 +1070,15 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, } case VIDIOC_G_STD: - *(v4l2_std_id *)arg = cx25840_get_v4lstd(client); + *(v4l2_std_id *)arg = state->std; break; case VIDIOC_S_STD: + if (state->radio == 0 && state->std == *(v4l2_std_id *)arg) + return 0; state->radio = 0; - return set_v4lstd(client, *(v4l2_std_id *)arg); + state->std = *(v4l2_std_id *)arg; + return set_v4lstd(client); case AUDC_SET_RADIO: state->radio = 1; @@ -1291,6 +1268,12 @@ static int cx25840_probe(struct i2c_client *client) state->id = id; state->rev = device_id; + if (state->is_cx23885) { + /* Drive GPIO2 direction and values */ + cx25840_write(client, 0x160, 0x1d); + cx25840_write(client, 0x164, 0x00); + } + return 0; } diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 95093edc918..8bf797f48b0 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -38,6 +38,7 @@ struct cx25840_state { struct i2c_client *c; int pvr150_workaround; int radio; + v4l2_std_id std; enum cx25840_video_input vid_input; enum cx25840_audio_input aud_input; u32 audclk_freq; @@ -60,7 +61,6 @@ int cx25840_write4(struct i2c_client *client, u16 addr, u32 value); u8 cx25840_read(struct i2c_client *client, u16 addr); u32 cx25840_read4(struct i2c_client *client, u16 addr); int cx25840_and_or(struct i2c_client *client, u16 addr, unsigned mask, u8 value); -v4l2_std_id cx25840_get_v4lstd(struct i2c_client *client); /* ----------------------------------------------------------------------- */ /* cx25850-firmware.c */ diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c index 1ddf724a2c7..620d295947a 100644 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ b/drivers/media/video/cx25840/cx25840-firmware.c @@ -79,11 +79,9 @@ static int check_fw_load(struct i2c_client *client, int size) return 0; } -static int fw_write(struct i2c_client *client, u8 * data, int size) +static int fw_write(struct i2c_client *client, u8 *data, int size) { - int sent; - - if ((sent = i2c_master_send(client, data, size)) < size) { + if (i2c_master_send(client, data, size) < size) { v4l_err(client, "firmware load i2c failure\n"); return -ENOSYS; } @@ -96,7 +94,7 @@ int cx25840_loadfw(struct i2c_client *client) struct cx25840_state *state = i2c_get_clientdata(client); const struct firmware *fw = NULL; u8 buffer[4], *ptr; - int size, send, retval; + int size, retval; if (state->is_cx23885) firmware = FWFILE_CX23885; @@ -124,8 +122,7 @@ int cx25840_loadfw(struct i2c_client *client) while (size > 0) { ptr[0] = 0x08; ptr[1] = 0x02; - send = size > (FWSEND - 2) ? FWSEND : size + 2; - retval = fw_write(client, ptr, send); + retval = fw_write(client, ptr, min(FWSEND, size + 2)); if (retval < 0) { release_firmware(fw); diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c index 6828f59b9d8..c754b9d1336 100644 --- a/drivers/media/video/cx25840/cx25840-vbi.c +++ b/drivers/media/video/cx25840/cx25840-vbi.c @@ -85,7 +85,7 @@ static int decode_vps(u8 * dst, u8 * p) void cx25840_vbi_setup(struct i2c_client *client) { struct cx25840_state *state = i2c_get_clientdata(client); - v4l2_std_id std = cx25840_get_v4lstd(client); + v4l2_std_id std = state->std; int hblank,hactive,burst,vblank,vactive,sc,vblank656,src_decimation; int luma_lpf,uv_lpf, comb; u32 pll_int,pll_frac,pll_post; @@ -242,7 +242,7 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ 0, 0, 0, 0 }; - int is_pal = !(cx25840_get_v4lstd(client) & V4L2_STD_525_60); + int is_pal = !(state->std & V4L2_STD_525_60); int i; fmt = arg; @@ -279,7 +279,7 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) case VIDIOC_S_FMT: { - int is_pal = !(cx25840_get_v4lstd(client) & V4L2_STD_525_60); + int is_pal = !(state->std & V4L2_STD_525_60); int vbi_offset = is_pal ? 1 : 0; int i, x; u8 lcr[24]; diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index 49d3813a9b4..bcf6d9ba063 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -57,6 +57,7 @@ config VIDEO_CX88_DVB select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE + select TUNER_SIMPLE if !DVB_FE_CUSTOMISE ---help--- This adds support for DVB/ATSC cards based on the Conexant 2388x chip. diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 316b106c351..e976fc6bef7 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -283,7 +283,7 @@ static int dsp_buffer_free(snd_cx88_card_t *chip) BUG_ON(!chip->dma_size); dprintk(2,"Freeing buffer\n"); - videobuf_pci_dma_unmap(chip->pci, chip->dma_risc); + videobuf_sg_dma_unmap(&chip->pci->dev, chip->dma_risc); videobuf_dma_free(chip->dma_risc); btcx_riscmem_free(chip->pci,&chip->buf->risc); kfree(chip->buf); @@ -385,7 +385,7 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream, BUG_ON(!chip->dma_size); BUG_ON(chip->num_periods & (chip->num_periods-1)); - buf = videobuf_pci_alloc(sizeof(*buf)); + buf = videobuf_sg_alloc(sizeof(*buf)); if (NULL == buf) return -ENOMEM; @@ -396,14 +396,14 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream, buf->vb.height = chip->num_periods; buf->vb.size = chip->dma_size; - dma=videobuf_to_dma(&buf->vb); + dma = videobuf_to_dma(&buf->vb); videobuf_dma_init(dma); ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, (PAGE_ALIGN(buf->vb.size) >> PAGE_SHIFT)); if (ret < 0) goto error; - ret = videobuf_pci_dma_map(chip->pci,dma); + ret = videobuf_sg_dma_map(&chip->pci->dev, dma); if (ret < 0) goto error; @@ -494,7 +494,7 @@ static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream) count = atomic_read(&chip->count); -// dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __FUNCTION__, +// dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __func__, // count, new, count & (runtime->periods-1), // runtime->period_size * (count & (runtime->periods-1))); return runtime->period_size * (count & (runtime->periods-1)); @@ -690,10 +690,8 @@ MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl); static int snd_cx88_free(snd_cx88_card_t *chip) { - if (chip->irq >= 0){ - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, chip); - } cx88_core_put(chip->core,chip->pci); diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index a99e9d5950a..61c4f72644b 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -45,7 +45,7 @@ static unsigned int mpegbufs = 32; module_param(mpegbufs,int,0644); MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32"); -static unsigned int debug = 0; +static unsigned int debug; module_param(debug,int,0644); MODULE_PARM_DESC(debug,"enable debug messages [blackbird]"); @@ -314,7 +314,7 @@ static int blackbird_mbox_func(void *priv, u32 command, int in, int out, u32 dat u32 value, flag, retval; int i; - dprintk(1,"%s: 0x%X\n", __FUNCTION__, command); + dprintk(1,"%s: 0x%X\n", __func__, command); /* this may not be 100% safe if we can't read any memory location without side effects */ @@ -693,7 +693,7 @@ static int blackbird_queryctrl(struct cx8802_dev *dev, struct v4l2_queryctrl *qc return -EINVAL; /* Standard V4L2 controls */ - if (cx8800_ctrl_query(qctrl) == 0) + if (cx8800_ctrl_query(dev->core, qctrl) == 0) return 0; /* MPEG V4L2 controls */ @@ -933,7 +933,7 @@ static int vidioc_queryctrl (struct file *file, void *priv, qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); if (unlikely(qctrl->id == 0)) return -EINVAL; - return cx8800_ctrl_query(qctrl); + return cx8800_ctrl_query(dev->core, qctrl); } static int vidioc_enum_input (struct file *file, void *priv, @@ -1055,7 +1055,7 @@ static int mpeg_open(struct inode *inode, struct file *file) dev = cx8802_get_device(inode); - dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, "%s\n", __func__); if (dev == NULL) return -ENODEV; @@ -1065,7 +1065,7 @@ static int mpeg_open(struct inode *inode, struct file *file) if (drv) { err = drv->request_acquire(drv); if(err != 0) { - dprintk(1,"%s: Unable to acquire hardware, %d\n", __FUNCTION__, err); + dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err); return err; } } @@ -1087,8 +1087,8 @@ static int mpeg_open(struct inode *inode, struct file *file) file->private_data = fh; fh->dev = dev; - videobuf_queue_pci_init(&fh->mpegq, &blackbird_qops, - dev->pci, &dev->slock, + videobuf_queue_sg_init(&fh->mpegq, &blackbird_qops, + &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct cx88_buffer), @@ -1284,7 +1284,7 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) struct cx8802_dev *dev = core->dvbdev; int err; - dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, "%s\n", __func__); dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", core->boardnr, core->name, diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 8c9a8adf52d..620159d0550 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -44,6 +44,16 @@ static unsigned int latency = UNSET; module_param(latency,int,0444); MODULE_PARM_DESC(latency,"pci latency timer"); +#define info_printk(core, fmt, arg...) \ + printk(KERN_INFO "%s: " fmt, core->name , ## arg) + +#define warn_printk(core, fmt, arg...) \ + printk(KERN_WARNING "%s: " fmt, core->name , ## arg) + +#define err_printk(core, fmt, arg...) \ + printk(KERN_ERR "%s: " fmt, core->name , ## arg) + + /* ------------------------------------------------------------------ */ /* board config info */ @@ -1354,6 +1364,10 @@ static const struct cx88_board cx88_boards[] = { }}, /* fixme: Add radio support */ .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0xe780, + }, }, [CX88_BOARD_ADSTECH_PTV_390] = { .name = "ADS Tech Instant Video PCI", @@ -1401,6 +1415,245 @@ static const struct cx88_board cx88_boards[] = { }}, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO] = { + .name = "DViCO FusionHDTV 5 PCI nano", + /* xc3008 tuner, digital only for now */ + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x000027df, /* Unconfirmed */ + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x000027df, /* Unconfirmed */ + .audioroute = 1, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x000027df, /* Unconfirmed */ + .audioroute = 1, + } }, + .mpeg = CX88_MPEG_DVB, + }, + [CX88_BOARD_PINNACLE_HYBRID_PCTV] = { + .name = "Pinnacle Hybrid PCTV", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x0ff, + }, + }, + [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = { + .name = "Winfast TV2000 XP Global", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x0400, /* pin 2:mute = 0 (off?) */ + .gpio1 = 0x0000, + .gpio2 = 0x0800, /* pin 19:audio = 0 (tv) */ + + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x0400, /* probably? or 0x0404 to turn mute on */ + .gpio1 = 0x0000, + .gpio2 = 0x0808, /* pin 19:audio = 1 (line) */ + + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x0ff, + }, + }, + [CX88_BOARD_POWERCOLOR_REAL_ANGEL] = { + .name = "PowerColor Real Angel 330", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x00ff, + .gpio1 = 0xf35d, + .gpio3 = 0x0000, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x00ff, + .gpio1 = 0xf37d, + .gpio3 = 0x0000, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x000ff, + .gpio1 = 0x0f37d, + .gpio3 = 0x00000, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x000ff, + .gpio1 = 0x0f35d, + .gpio3 = 0x00000, + }, + }, + [CX88_BOARD_GENIATECH_X8000_MT] = { + /* Also PowerColor Real Angel 330 and Geniatech X800 OEM */ + .name = "Geniatech X8000-MT DVBT", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x00000000, + .gpio1 = 0x00e3e341, + .gpio2 = 0x00000000, + .gpio3 = 0x00000000, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x00000000, + .gpio1 = 0x00e3e361, + .gpio2 = 0x00000000, + .gpio3 = 0x00000000, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x00000000, + .gpio1 = 0x00e3e361, + .gpio2 = 0x00000000, + .gpio3 = 0x00000000, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x00000000, + .gpio1 = 0x00e3e341, + .gpio2 = 0x00000000, + .gpio3 = 0x00000000, + }, + .mpeg = CX88_MPEG_DVB, + }, + [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO] = { + .name = "DViCO FusionHDTV DVB-T PRO", + .tuner_type = TUNER_ABSENT, /* XXX: Has XC3028 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = { { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x000067df, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x000067df, + } }, + .mpeg = CX88_MPEG_DVB, + }, + [CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD] = { + .name = "DViCO FusionHDTV 7 Gold", + .tuner_type = TUNER_XC5000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x10df, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x16d9, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x16d9, + }}, + }, + [CX88_BOARD_PROLINK_PV_8000GT] = { + .name = "Prolink Pixelview MPEG 8000GT", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x0ff, + .gpio2 = 0x0cfb, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio2 = 0x0cfb, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio2 = 0x0cfb, + } }, + .radio = { + .type = CX88_RADIO, + .gpio2 = 0x0cfb, + }, + }, + /* Both radio, analog and ATSC work with this board. + However, for analog to work, s5h1409 gate should be open, + otherwise, tuner-xc3028 won't be detected. + A proper fix require using the newer i2c methods to add + tuner-xc3028 without doing an i2c probe. + */ + [CX88_BOARD_KWORLD_ATSC_120] = { + .name = "Kworld PlusTV HD PCI 120 (ATSC 120)", + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x000000ff, + .gpio1 = 0x0000f35d, + .gpio2 = 0x00000000, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x000000ff, + .gpio1 = 0x0000f37e, + .gpio2 = 0x00000000, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x000000ff, + .gpio1 = 0x0000f37e, + .gpio2 = 0x00000000, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x000000ff, + .gpio1 = 0x0000f35d, + .gpio2 = 0x00000000, + }, + .mpeg = CX88_MPEG_DVB, + }, }; /* ------------------------------------------------------------------ */ @@ -1605,7 +1858,11 @@ static const struct cx88_subid cx88_subids[] = { .subdevice = 0xdb11, .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS, /* Re-branded DViCO: UltraView DVB-T Plus */ - },{ + }, { + .subvendor = 0x18ac, + .subdevice = 0xdb30, + .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO, + }, { .subvendor = 0x17de, .subdevice = 0x0840, .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, @@ -1714,6 +1971,38 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x11bd, .subdevice = 0x0051, .card = CX88_BOARD_PINNACLE_PCTV_HD_800i, + }, { + .subvendor = 0x18ac, + .subdevice = 0xd530, + .card = CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO, + }, { + .subvendor = 0x12ab, + .subdevice = 0x1788, + .card = CX88_BOARD_PINNACLE_HYBRID_PCTV, + }, { + .subvendor = 0x14f1, + .subdevice = 0xea3d, + .card = CX88_BOARD_POWERCOLOR_REAL_ANGEL, + }, { + .subvendor = 0x107d, + .subdevice = 0x6f18, + .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, + }, { + .subvendor = 0x14f1, + .subdevice = 0x8852, + .card = CX88_BOARD_GENIATECH_X8000_MT, + }, { + .subvendor = 0x18ac, + .subdevice = 0xd610, + .card = CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD, + }, { + .subvendor = 0x1554, + .subdevice = 0x4935, + .card = CX88_BOARD_PROLINK_PV_8000GT, + }, { + .subvendor = 0x17de, + .subdevice = 0x08c1, + .card = CX88_BOARD_KWORLD_ATSC_120, }, }; @@ -1731,17 +2020,16 @@ static void leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data) if (eeprom_data[4] != 0x7d || eeprom_data[5] != 0x10 || eeprom_data[7] != 0x66) { - printk(KERN_WARNING "%s: Leadtek eeprom invalid.\n", - core->name); + warn_printk(core, "Leadtek eeprom invalid.\n"); return; } core->board.tuner_type = (eeprom_data[6] == 0x13) ? TUNER_PHILIPS_FM1236_MK3 : TUNER_PHILIPS_FM1216ME_MK3; - printk(KERN_INFO "%s: Leadtek Winfast 2000XP Expert config: " - "tuner=%d, eeprom[0]=0x%02x\n", - core->name, core->board.tuner_type, eeprom_data[0]); + info_printk(core, "Leadtek Winfast 2000XP Expert config: " + "tuner=%d, eeprom[0]=0x%02x\n", + core->board.tuner_type, eeprom_data[0]); } static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) @@ -1785,13 +2073,12 @@ static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) /* known */ break; default: - printk("%s: warning: unknown hauppauge model #%d\n", - core->name, tv.model); + warn_printk(core, "warning: unknown hauppauge model #%d\n", + tv.model); break; } - printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", - core->name, tv.model); + info_printk(core, "hauppauge eeprom: model=%d\n", tv.model); } /* ----------------------------------------------------------------------- */ @@ -1837,8 +2124,7 @@ static void gdi_eeprom(struct cx88_core *core, u8 *eeprom_data) char *name = (eeprom_data[0x0d] < ARRAY_SIZE(gdi_tuner)) ? gdi_tuner[eeprom_data[0x0d]].name : NULL; - printk(KERN_INFO "%s: GDI: tuner=%s\n", core->name, - name ? name : "unknown"); + info_printk(core, "GDI: tuner=%s\n", name ? name : "unknown"); if (NULL == name) return; core->board.tuner_type = gdi_tuner[eeprom_data[0x0d]].id; @@ -1846,6 +2132,75 @@ static void gdi_eeprom(struct cx88_core *core, u8 *eeprom_data) CX88_RADIO : 0; } +/* ------------------------------------------------------------------- */ +/* some Divco specific stuff */ +static int cx88_dvico_xc2028_callback(struct cx88_core *core, + int command, int arg) +{ + switch (command) { + case XC2028_TUNER_RESET: + cx_write(MO_GP0_IO, 0x101000); + mdelay(5); + cx_set(MO_GP0_IO, 0x101010); + break; + default: + return -EINVAL; + } + + return 0; +} + + +/* ----------------------------------------------------------------------- */ +/* some Geniatech specific stuff */ + +static int cx88_xc3028_geniatech_tuner_callback(struct cx88_core *core, + int command, int mode) +{ + switch (command) { + case XC2028_TUNER_RESET: + switch (INPUT(core->input).type) { + case CX88_RADIO: + break; + case CX88_VMUX_DVB: + cx_write(MO_GP1_IO, 0x030302); + mdelay(50); + break; + default: + cx_write(MO_GP1_IO, 0x030301); + mdelay(50); + } + cx_write(MO_GP1_IO, 0x101010); + mdelay(50); + cx_write(MO_GP1_IO, 0x101000); + mdelay(50); + cx_write(MO_GP1_IO, 0x101010); + mdelay(50); + return 0; + } + return -EINVAL; +} + +/* ------------------------------------------------------------------- */ +/* some Divco specific stuff */ +static int cx88_pv_8000gt_callback(struct cx88_core *core, + int command, int arg) +{ + switch (command) { + case XC2028_TUNER_RESET: + cx_write(MO_GP2_IO, 0xcf7); + mdelay(50); + cx_write(MO_GP2_IO, 0xef5); + mdelay(50); + cx_write(MO_GP2_IO, 0xcf7); + break; + default: + return -EINVAL; + } + + return 0; +} + /* ----------------------------------------------------------------------- */ /* some DViCO specific stuff */ @@ -1874,32 +2229,85 @@ static void dvico_fusionhdtv_hybrid_init(struct cx88_core *core) msg.len = (i != 12 ? 5 : 2); err = i2c_transfer(&core->i2c_adap, &msg, 1); if (err != 1) { - printk("dvico_fusionhdtv_hybrid_init buf %d failed (err = %d)!\n", i, err); + warn_printk(core, "dvico_fusionhdtv_hybrid_init buf %d " + "failed (err = %d)!\n", i, err); return; } } } +static int cx88_xc2028_tuner_callback(struct cx88_core *core, + int command, int arg) +{ + /* Board-specific callbacks */ + switch (core->boardnr) { + case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: + case CX88_BOARD_POWERCOLOR_REAL_ANGEL: + case CX88_BOARD_GENIATECH_X8000_MT: + case CX88_BOARD_KWORLD_ATSC_120: + return cx88_xc3028_geniatech_tuner_callback(core, + command, arg); + case CX88_BOARD_PROLINK_PV_8000GT: + return cx88_pv_8000gt_callback(core, command, arg); + case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: + return cx88_dvico_xc2028_callback(core, command, arg); + } + + switch (command) { + case XC2028_TUNER_RESET: + switch (INPUT(core->input).type) { + case CX88_RADIO: + info_printk(core, "setting GPIO to radio!\n"); + cx_write(MO_GP0_IO, 0x4ff); + mdelay(250); + cx_write(MO_GP2_IO, 0xff); + mdelay(250); + break; + case CX88_VMUX_DVB: /* Digital TV*/ + default: /* Analog TV */ + info_printk(core, "setting GPIO to TV!\n"); + break; + } + cx_write(MO_GP1_IO, 0x101010); + mdelay(250); + cx_write(MO_GP1_IO, 0x101000); + mdelay(250); + cx_write(MO_GP1_IO, 0x101010); + mdelay(250); + return 0; + } + return -EINVAL; +} + /* ----------------------------------------------------------------------- */ /* Tuner callback function. Currently only needed for the Pinnacle * * PCTV HD 800i with an xc5000 sillicon tuner. This is used for both * * analog tuner attach (tuner-core.c) and dvb tuner attach (cx88-dvb.c) */ -int cx88_tuner_callback(void *priv, int command, int arg) +static int cx88_xc5000_tuner_callback(struct cx88_core *core, + int command, int arg) { - struct i2c_algo_bit_data *i2c_algo = priv; - struct cx88_core *core = i2c_algo->data; - - switch(core->boardnr) { + switch (core->boardnr) { case CX88_BOARD_PINNACLE_PCTV_HD_800i: - if(command == 0) { /* This is the reset command from xc5000 */ + if (command == 0) { /* This is the reset command from xc5000 */ /* Reset XC5000 tuner via SYS_RSTO_pin */ cx_write(MO_SRST_IO, 0); msleep(10); cx_write(MO_SRST_IO, 1); return 0; + } else { + err_printk(core, "xc5000: unknown tuner " + "callback command.\n"); + return -EINVAL; } - else { + break; + case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: + if (command == 0) { /* This is the reset command from xc5000 */ + cx_clear(MO_GP0_IO, 0x00000010); + msleep(10); + cx_set(MO_GP0_IO, 0x00000010); + return 0; + } else { printk(KERN_ERR "xc5000: unknown tuner callback command.\n"); return -EINVAL; @@ -1908,6 +2316,36 @@ int cx88_tuner_callback(void *priv, int command, int arg) } return 0; /* Should never be here */ } + +int cx88_tuner_callback(void *priv, int command, int arg) +{ + struct i2c_algo_bit_data *i2c_algo = priv; + struct cx88_core *core; + + if (!i2c_algo) { + printk(KERN_ERR "cx88: Error - i2c private data undefined.\n"); + return -EINVAL; + } + + core = i2c_algo->data; + + if (!core) { + printk(KERN_ERR "cx88: Error - device struct undefined.\n"); + return -EINVAL; + } + + switch (core->board.tuner_type) { + case TUNER_XC2028: + info_printk(core, "Calling XC2028/3028 callback\n"); + return cx88_xc2028_tuner_callback(core, command, arg); + case TUNER_XC5000: + info_printk(core, "Calling XC5000 callback\n"); + return cx88_xc5000_tuner_callback(core, command, arg); + } + err_printk(core, "Error: Calling callback for tuner %d\n", + core->board.tuner_type); + return -EINVAL; +} EXPORT_SYMBOL(cx88_tuner_callback); /* ----------------------------------------------------------------------- */ @@ -1918,23 +2356,25 @@ static void cx88_card_list(struct cx88_core *core, struct pci_dev *pci) if (0 == pci->subsystem_vendor && 0 == pci->subsystem_device) { - printk("%s: Your board has no valid PCI Subsystem ID and thus can't\n" + printk(KERN_ERR + "%s: Your board has no valid PCI Subsystem ID and thus can't\n" "%s: be autodetected. Please pass card=<n> insmod option to\n" "%s: workaround that. Redirect complaints to the vendor of\n" "%s: the TV card. Best regards,\n" "%s: -- tux\n", core->name,core->name,core->name,core->name,core->name); } else { - printk("%s: Your board isn't known (yet) to the driver. You can\n" + printk(KERN_ERR + "%s: Your board isn't known (yet) to the driver. You can\n" "%s: try to pick one of the existing card configs via\n" "%s: card=<n> insmod option. Updating to the latest\n" "%s: version might help as well.\n", core->name,core->name,core->name,core->name); } - printk("%s: Here is a list of valid choices for the card=<n> insmod option:\n", - core->name); + err_printk(core, "Here is a list of valid choices for the card=<n> " + "insmod option:\n"); for (i = 0; i < ARRAY_SIZE(cx88_boards); i++) - printk("%s: card=%d -> %s\n", + printk(KERN_ERR "%s: card=%d -> %s\n", core->name, i, cx88_boards[i].name); } @@ -1951,9 +2391,57 @@ static void cx88_card_setup_pre_i2c(struct cx88_core *core) cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */ udelay(1000); break; + + case CX88_BOARD_PROLINK_PV_8000GT: + cx_write(MO_GP2_IO, 0xcf7); + mdelay(50); + cx_write(MO_GP2_IO, 0xef5); + mdelay(50); + cx_write(MO_GP2_IO, 0xcf7); + msleep(10); + break; + + case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: + /* Enable the xc5000 tuner */ + cx_set(MO_GP0_IO, 0x00001010); + break; } } +/* + * Sets board-dependent xc3028 configuration + */ +void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) +{ + memset(ctl, 0, sizeof(*ctl)); + + ctl->fname = XC2028_DEFAULT_FIRMWARE; + ctl->max_len = 64; + + switch (core->boardnr) { + case CX88_BOARD_POWERCOLOR_REAL_ANGEL: + /* Doesn't work with firmware version 2.7 */ + ctl->fname = "xc3028-v25.fw"; + break; + case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: + ctl->scode_table = XC3028_FE_ZARLINK456; + break; + case CX88_BOARD_KWORLD_ATSC_120: + case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: + ctl->demod = XC3028_FE_OREN538; + break; + case CX88_BOARD_PROLINK_PV_8000GT: + /* + * This board uses non-MTS firmware + */ + break; + default: + ctl->demod = XC3028_FE_OREN538; + ctl->mts = 1; + } +} +EXPORT_SYMBOL_GPL(cx88_setup_xc3028); + static void cx88_card_setup(struct cx88_core *core) { static u8 eeprom[256]; @@ -1991,6 +2479,13 @@ static void cx88_card_setup(struct cx88_core *core) cx_write(MO_GP0_IO, 0x000007f8); cx_write(MO_GP1_IO, 0x00000001); break; + case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: + /* GPIO0:0 is hooked to demod reset */ + /* GPIO0:4 is hooked to xc3028 reset */ + cx_write(MO_GP0_IO, 0x00111100); + msleep(1); + cx_write(MO_GP0_IO, 0x00111111); + break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: /* GPIO0:6 is hooked to FX2 reset pin */ cx_set(MO_GP0_IO, 0x00004040); @@ -2038,10 +2533,8 @@ static void cx88_card_setup(struct cx88_core *core) for (i = 0; i < ARRAY_SIZE(buffer); i++) if (2 != i2c_master_send(&core->i2c_client, buffer[i],2)) - printk(KERN_WARNING - "%s: Unable to enable " - "tuner(%i).\n", - core->name, i); + warn_printk(core, "Unable to enable " + "tuner(%i).\n", i); } break; case CX88_BOARD_MSI_TVANYWHERE_MASTER: @@ -2062,6 +2555,22 @@ static void cx88_card_setup(struct cx88_core *core) cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tea5767_cfg); } } + + if (core->board.tuner_type == TUNER_XC2028) { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + /* Fills device-dependent initialization parameters */ + cx88_setup_xc3028(core, &ctl); + + /* Sends parameters to xc2028/3028 tuner */ + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + info_printk(core, "Asking xc2028/3028 to load firmware %s\n", + ctl.fname); + cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &xc2028_cfg); + } } /* ------------------------------------------------------------------ */ @@ -2178,9 +2687,8 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) memcpy(&core->board, &cx88_boards[core->boardnr], sizeof(core->board)); - printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", - core->name,pci->subsystem_vendor, - pci->subsystem_device, core->board.name, + info_printk(core, "subsystem: %04x:%04x, board: %s [card=%d,%s]\n", + pci->subsystem_vendor, pci->subsystem_device, core->board.name, core->boardnr, card[core->nr] == core->boardnr ? "insmod option" : "autodetected"); @@ -2189,8 +2697,8 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) if (radio[core->nr] != UNSET) core->board.radio_type = radio[core->nr]; - printk(KERN_INFO "%s: TV tuner type %d, Radio tuner type %d\n", - core->name, core->board.tuner_type, core->board.radio_type); + info_printk(core, "TV tuner type %d, Radio tuner type %d\n", + core->board.tuner_type, core->board.radio_type); /* init hardware */ cx88_reset(core); @@ -2207,12 +2715,3 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) return core; } - -/* ------------------------------------------------------------------ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off - */ diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index 01e2ac98970..c4d1aff1fdb 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -47,15 +47,15 @@ MODULE_LICENSE("GPL"); /* ------------------------------------------------------------------ */ -static unsigned int core_debug = 0; +static unsigned int core_debug; module_param(core_debug,int,0644); MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); -static unsigned int nicam = 0; +static unsigned int nicam; module_param(nicam,int,0644); MODULE_PARM_DESC(nicam,"tv audio is nicam"); -static unsigned int nocomb = 0; +static unsigned int nocomb; module_param(nocomb,int,0644); MODULE_PARM_DESC(nocomb,"disable comb filter"); @@ -219,7 +219,7 @@ cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf) videobuf_waiton(&buf->vb,0,0); videobuf_dma_unmap(q, dma); videobuf_dma_free(dma); - btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc); + btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); buf->vb.state = VIDEOBUF_NEEDS_INIT; } @@ -548,7 +548,7 @@ void cx88_wakeup(struct cx88_core *core, mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); } if (bc != 1) - printk("%s: %d buffers handled (should be 1)\n",__FUNCTION__,bc); + printk("%s: %d buffers handled (should be 1)\n",__func__,bc); } void cx88_shutdown(struct cx88_core *core) @@ -577,7 +577,7 @@ void cx88_shutdown(struct cx88_core *core) int cx88_reset(struct cx88_core *core) { - dprintk(1,"%s\n",__FUNCTION__); + dprintk(1,"%s\n",__func__); cx88_shutdown(core); /* clear irq status */ @@ -929,7 +929,10 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm) dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n", cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f); - cx_andor(MO_INPUT_FORMAT, 0xf, cxiformat); + /* Chroma AGC must be disabled if SECAM is used, we enable it + by default on PAL and NTSC */ + cx_andor(MO_INPUT_FORMAT, 0x40f, + norm & V4L2_STD_SECAM ? cxiformat : cxiformat | 0x400); // FIXME: as-is from DScaler dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n", diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index f7b41eb1bb5..f1251b844e0 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -45,16 +45,20 @@ #include "nxt200x.h" #include "cx24123.h" #include "isl6421.h" +#include "tuner-simple.h" +#include "tda9887.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); -static unsigned int debug = 0; +static unsigned int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"enable debug messages [dvb]"); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + #define dprintk(level,fmt, arg...) if (debug >= level) \ printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg) @@ -235,6 +239,19 @@ static struct zl10353_config dvico_fusionhdtv_hybrid = { .no_tuner = 1, }; +static struct zl10353_config dvico_fusionhdtv_xc3028 = { + .demod_address = 0x0f, + .if2 = 45600, + .no_tuner = 1, +}; + +static struct mt352_config dvico_fusionhdtv_mt352_xc3028 = { + .demod_address = 0x0f, + .if2 = 4560, + .no_tuner = 1, + .demod_init = dvico_fusionhdtv_demod_init, +}; + static struct zl10353_config dvico_fusionhdtv_plus_v1_1 = { .demod_address = 0x0f, }; @@ -266,7 +283,7 @@ static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index) struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; - dprintk(1, "%s: index = %d\n", __FUNCTION__, index); + dprintk(1, "%s: index = %d\n", __func__, index); if (index == 0) cx_clear(MO_GP0_IO, 8); else @@ -357,6 +374,40 @@ static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe, return 0; } +static int cx88_pci_nano_callback(void *ptr, int command, int arg) +{ + struct cx88_core *core = ptr; + + switch (command) { + case XC2028_TUNER_RESET: + /* Send the tuner in then out of reset */ + dprintk(1, "%s: XC2028_TUNER_RESET %d\n", __func__, arg); + + switch (core->boardnr) { + case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: + /* GPIO-4 xc3028 tuner */ + + cx_set(MO_GP0_IO, 0x00001000); + cx_clear(MO_GP0_IO, 0x00000010); + msleep(100); + cx_set(MO_GP0_IO, 0x00000010); + msleep(100); + break; + } + + break; + case XC2028_RESET_CLK: + dprintk(1, "%s: XC2028_RESET_CLK %d\n", __func__, arg); + break; + default: + dprintk(1, "%s: unknown command %d, arg %d\n", __func__, + command, arg); + return -EINVAL; + } + + return 0; +} + static struct cx24123_config geniatech_dvbs_config = { .demod_address = 0x55, .set_ts_params = cx24123_set_ts_param, @@ -383,12 +434,76 @@ static struct s5h1409_config pinnacle_pctv_hd_800i_config = { .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK, }; +static struct s5h1409_config dvico_hdtv5_pci_nano_config = { + .demod_address = 0x32 >> 1, + .output_mode = S5H1409_SERIAL_OUTPUT, + .gpio = S5H1409_GPIO_OFF, + .inversion = S5H1409_INVERSION_OFF, + .status_mode = S5H1409_DEMODLOCKING, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; + +static struct s5h1409_config kworld_atsc_120_config = { + .demod_address = 0x32 >> 1, + .output_mode = S5H1409_SERIAL_OUTPUT, + .gpio = S5H1409_GPIO_OFF, + .inversion = S5H1409_INVERSION_OFF, + .status_mode = S5H1409_DEMODLOCKING, + .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; + static struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = { .i2c_address = 0x64, .if_khz = 5380, .tuner_callback = cx88_tuner_callback, }; +static struct zl10353_config cx88_geniatech_x8000_mt = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, +}; + +static int attach_xc3028(u8 addr, struct cx8802_dev *dev) +{ + struct dvb_frontend *fe; + struct xc2028_ctrl ctl; + struct xc2028_config cfg = { + .i2c_adap = &dev->core->i2c_adap, + .i2c_addr = addr, + .ctrl = &ctl, + .callback = cx88_tuner_callback, + }; + + if (!dev->dvb.frontend) { + printk(KERN_ERR "%s/2: dvb frontend not attached. " + "Can't attach xc3028\n", + dev->core->name); + return -EINVAL; + } + + /* + * Some xc3028 devices may be hidden by an I2C gate. This is known + * to happen with some s5h1409-based devices. + * Now that I2C gate is open, sets up xc3028 configuration + */ + cx88_setup_xc3028(dev->core, &ctl); + + fe = dvb_attach(xc2028_attach, dev->dvb.frontend, &cfg); + if (!fe) { + printk(KERN_ERR "%s/2: xc3028 attach failed\n", + dev->core->name); + dvb_frontend_detach(dev->dvb.frontend); + dvb_unregister_frontend(dev->dvb.frontend); + dev->dvb.frontend = NULL; + return -EINVAL; + } + + printk(KERN_INFO "%s/2: xc3028 attached\n", + dev->core->name); + + return 0; +} + static int dvb_register(struct cx8802_dev *dev) { /* init struct videobuf_dvb */ @@ -429,8 +544,9 @@ static int dvb_register(struct cx8802_dev *dev) &hauppauge_hvr_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); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x61, + TUNER_PHILIPS_FMD1216ME_MK3); } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: @@ -497,8 +613,9 @@ static int dvb_register(struct cx8802_dev *dev) dev->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config, &dev->vp3054->adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, DVB_PLL_FMD1216ME); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x61, + TUNER_PHILIPS_FMD1216ME_MK3); } #else printk(KERN_ERR "%s/2: built without vp3054 support\n", dev->core->name); @@ -509,18 +626,36 @@ static int dvb_register(struct cx8802_dev *dev) &dvico_fusionhdtv_hybrid, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - DVB_PLL_THOMSON_FE6600); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x61, + TUNER_THOMSON_FE6600); } break; + case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: + dev->dvb.frontend = dvb_attach(zl10353_attach, + &dvico_fusionhdtv_xc3028, + &dev->core->i2c_adap); + if (dev->dvb.frontend == NULL) + dev->dvb.frontend = dvb_attach(mt352_attach, + &dvico_fusionhdtv_mt352_xc3028, + &dev->core->i2c_adap); + /* + * On this board, the demod provides the I2C bus pullup. + * We must not permit gate_ctrl to be performed, or + * the xc3028 cannot communicate on the bus. + */ + if (dev->dvb.frontend) + dev->dvb.frontend->ops.i2c_gate_ctrl = NULL; + if (attach_xc3028(0x61, dev) < 0) + return -EINVAL; + break; case CX88_BOARD_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, - &dev->core->i2c_adap, - DVB_PLL_THOMSON_DTT761X); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x61, + TUNER_THOMSON_DTT761X); } break; case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: @@ -540,9 +675,9 @@ static int dvb_register(struct cx8802_dev *dev) &fusionhdtv_3_gold, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - DVB_PLL_MICROTUNE_4042); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x61, + TUNER_MICROTUNE_4042FI5); } } break; @@ -560,9 +695,9 @@ static int dvb_register(struct cx8802_dev *dev) &fusionhdtv_3_gold, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - DVB_PLL_THOMSON_DTT761X); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x61, + TUNER_THOMSON_DTT761X); } } break; @@ -580,9 +715,11 @@ static int dvb_register(struct cx8802_dev *dev) &fusionhdtv_5_gold, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - DVB_PLL_LG_TDVS_H06XF); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x61, + TUNER_LG_TDVS_H06XF); + dvb_attach(tda9887_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x43); } } break; @@ -600,9 +737,11 @@ static int dvb_register(struct cx8802_dev *dev) &pchdtv_hd5500, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, - DVB_PLL_LG_TDVS_H06XF); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x61, + TUNER_LG_TDVS_H06XF); + dvb_attach(tda9887_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x43); } } break; @@ -611,8 +750,9 @@ static int dvb_register(struct cx8802_dev *dev) &ati_hdtvwonder, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, DVB_PLL_TUV1236D); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->core->i2c_adap, 0x61, + TUNER_PHILIPS_TUV1236D); } break; case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: @@ -658,14 +798,62 @@ static int dvb_register(struct cx8802_dev *dev) &pinnacle_pctv_hd_800i_tuner_config); } break; + case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: + dev->dvb.frontend = dvb_attach(s5h1409_attach, + &dvico_hdtv5_pci_nano_config, + &dev->core->i2c_adap); + if (dev->dvb.frontend != NULL) { + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &dev->core->i2c_adap, + .i2c_addr = 0x61, + .callback = cx88_pci_nano_callback, + }; + static struct xc2028_ctrl ctl = { + .fname = "xc3028-v27.fw", + .max_len = 64, + .scode_table = XC3028_FE_OREN538, + }; + + fe = dvb_attach(xc2028_attach, + dev->dvb.frontend, &cfg); + if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) + fe->ops.tuner_ops.set_config(fe, &ctl); + } + break; + case CX88_BOARD_PINNACLE_HYBRID_PCTV: + dev->dvb.frontend = dvb_attach(zl10353_attach, + &cx88_geniatech_x8000_mt, + &dev->core->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) + return -EINVAL; + break; + case CX88_BOARD_GENIATECH_X8000_MT: + dev->ts_gen_cntrl = 0x00; + + dev->dvb.frontend = dvb_attach(zl10353_attach, + &cx88_geniatech_x8000_mt, + &dev->core->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) + return -EINVAL; + break; + case CX88_BOARD_KWORLD_ATSC_120: + dev->dvb.frontend = dvb_attach(s5h1409_attach, + &kworld_atsc_120_config, + &dev->core->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) + return -EINVAL; + break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", dev->core->name); break; } if (NULL == dev->dvb.frontend) { - printk(KERN_ERR "%s/2: frontend initialization failed\n", dev->core->name); - return -1; + printk(KERN_ERR + "%s/2: frontend initialization failed\n", + dev->core->name); + return -EINVAL; } /* Ensure all frontends negotiate bus access */ @@ -675,7 +863,8 @@ static int dvb_register(struct cx8802_dev *dev) cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); /* register everything */ - return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev); + return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, + &dev->pci->dev, adapter_nr); } /* ----------------------------------------------------------- */ @@ -685,7 +874,7 @@ static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv) { struct cx88_core *core = drv->core; int err = 0; - dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, "%s\n", __func__); switch (core->boardnr) { case CX88_BOARD_HAUPPAUGE_HVR1300: @@ -708,7 +897,7 @@ static int cx8802_dvb_advise_release(struct cx8802_driver *drv) { struct cx88_core *core = drv->core; int err = 0; - dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, "%s\n", __func__); switch (core->boardnr) { case CX88_BOARD_HAUPPAUGE_HVR1300: @@ -726,7 +915,7 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv) struct cx8802_dev *dev = drv->core->dvbdev; int err; - dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, "%s\n", __func__); dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", core->boardnr, core->name, @@ -744,8 +933,8 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv) /* dvb stuff */ printk(KERN_INFO "%s/2: cx2388x based DVB/ATSC card\n", core->name); - videobuf_queue_pci_init(&dev->dvb.dvbq, &dvb_qops, - dev->pci, &dev->slock, + videobuf_queue_sg_init(&dev->dvb.dvbq, &dvb_qops, + &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP, sizeof(struct cx88_buffer), @@ -764,7 +953,8 @@ static int cx8802_dvb_remove(struct cx8802_driver *drv) struct cx8802_dev *dev = drv->core->dvbdev; /* dvb */ - videobuf_dvb_unregister(&dev->dvb); + if (dev->dvb.frontend) + videobuf_dvb_unregister(&dev->dvb); vp3054_i2c_remove(dev); diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index 566b26af523..c6b44732a08 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -35,11 +35,11 @@ #include "cx88.h" #include <media/v4l2-common.h> -static unsigned int i2c_debug = 0; +static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]"); -static unsigned int i2c_scan = 0; +static unsigned int i2c_scan; module_param(i2c_scan, int, 0444); MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index bb0911b4d2f..53526d997a4 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -57,7 +57,7 @@ struct cx88_IR { u32 mask_keyup; }; -static int ir_debug = 0; +static int ir_debug; module_param(ir_debug, int, 0644); /* debug level [IR] */ MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); @@ -258,6 +258,13 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->mask_keyup = 0x80; ir->polling = 1; /* ms */ break; + case CX88_BOARD_PROLINK_PV_8000GT: + ir_codes = ir_codes_pixelview_new; + ir->gpio_addr = MO_GP1_IO; + ir->mask_keycode = 0x3f; + ir->mask_keyup = 0x80; + ir->polling = 1; /* ms */ + break; case CX88_BOARD_KWORLD_LTV883: ir_codes = ir_codes_pixelview; ir->gpio_addr = MO_GP1_IO; @@ -310,6 +317,12 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir_type = IR_TYPE_RC5; ir->sampling = 1; break; + case CX88_BOARD_POWERCOLOR_REAL_ANGEL: + ir_codes = ir_codes_powercolor_real_angel; + ir->gpio_addr = MO_GP2_IO; + ir->mask_keycode = 0x7e; + ir->polling = 100; /* ms */ + break; } if (NULL == ir_codes) { diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index e357f415db0..a6b061c2644 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -39,7 +39,7 @@ MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); -static unsigned int debug = 0; +static unsigned int debug; module_param(debug,int,0644); MODULE_PARM_DESC(debug,"enable debug messages [mpeg]"); @@ -146,7 +146,7 @@ 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__, + printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __func__, core->board.mpeg ); return -EINVAL; } @@ -247,7 +247,7 @@ int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev, struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); int rc; - dprintk(1, "%s: %p\n", __FUNCTION__, buf); + dprintk(1, "%s: %p\n", __func__, buf); if (0 != buf->vb.baddr && buf->vb.bsize < size) return -EINVAL; @@ -289,7 +289,7 @@ void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) buf->count = cx88q->count++; mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT); dprintk(1,"[%p/%d] %s - first active\n", - buf, buf->vb.i, __FUNCTION__); + buf, buf->vb.i, __func__); } else { dprintk( 1, "queue is not empty - append to active\n" ); @@ -299,7 +299,7 @@ void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) buf->count = cx88q->count++; prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); dprintk( 1, "[%p/%d] %s - append to active\n", - buf, buf->vb.i, __FUNCTION__); + buf, buf->vb.i, __func__); } } @@ -342,7 +342,7 @@ static void cx8802_timeout(unsigned long data) { struct cx8802_dev *dev = (struct cx8802_dev*)data; - dprintk(1, "%s\n",__FUNCTION__); + dprintk(1, "%s\n",__func__); if (debug) cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); @@ -613,6 +613,8 @@ static int cx8802_request_acquire(struct cx8802_driver *drv) core->active_type_id != drv->type_id) return -EBUSY; + core->input = CX88_VMUX_DVB; + if (drv->advise_acquire) { mutex_lock(&drv->core->lock); @@ -623,7 +625,7 @@ static int cx8802_request_acquire(struct cx8802_driver *drv) } mutex_unlock(&drv->core->lock); - mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __FUNCTION__, cx_read(MO_GP0_IO)); + mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); } return 0; @@ -639,7 +641,7 @@ static int cx8802_request_release(struct cx8802_driver *drv) { 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)); + mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); } mutex_unlock(&drv->core->lock); @@ -813,7 +815,7 @@ static void __devexit cx8802_remove(struct pci_dev *pci_dev) dev = pci_get_drvdata(pci_dev); - dprintk( 1, "%s\n", __FUNCTION__); + dprintk( 1, "%s\n", __func__); if (!list_empty(&dev->drvlist)) { struct cx8802_driver *drv, *tmp; diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c index 76e5c78d8ae..3a1977f41e2 100644 --- a/drivers/media/video/cx88/cx88-tvaudio.c +++ b/drivers/media/video/cx88/cx88-tvaudio.c @@ -53,15 +53,15 @@ #include "cx88.h" -static unsigned int audio_debug = 0; +static unsigned int audio_debug; module_param(audio_debug, int, 0644); MODULE_PARM_DESC(audio_debug, "enable debug messages [audio]"); -static unsigned int always_analog = 0; +static unsigned int always_analog; module_param(always_analog,int,0644); MODULE_PARM_DESC(always_analog,"force analog audio out"); -static unsigned int radio_deemphasis = 0; +static unsigned int radio_deemphasis; module_param(radio_deemphasis,int,0644); MODULE_PARM_DESC(radio_deemphasis, "Radio deemphasis time constant, " "0=None, 1=50us (elsewhere), 2=75us (USA)"); @@ -265,12 +265,12 @@ static void set_audio_standard_BTSC(struct cx88_core *core, unsigned int sap, mode |= EN_FMRADIO_EN_RDS; if (sap) { - dprintk("%s SAP (status: unknown)\n", __FUNCTION__); + dprintk("%s SAP (status: unknown)\n", __func__); set_audio_start(core, SEL_SAP); set_audio_registers(core, btsc_sap); set_audio_finish(core, mode); } else { - dprintk("%s (status: known-good)\n", __FUNCTION__); + dprintk("%s (status: known-good)\n", __func__); set_audio_start(core, SEL_BTSC); set_audio_registers(core, btsc); set_audio_finish(core, mode); @@ -351,16 +351,16 @@ static void set_audio_standard_NICAM(struct cx88_core *core, u32 mode) set_audio_start(core,SEL_NICAM); switch (core->tvaudio) { case WW_L: - dprintk("%s SECAM-L NICAM (status: devel)\n", __FUNCTION__); + dprintk("%s SECAM-L NICAM (status: devel)\n", __func__); set_audio_registers(core, nicam_l); break; case WW_I: - dprintk("%s PAL-I NICAM (status: known-good)\n", __FUNCTION__); + dprintk("%s PAL-I NICAM (status: known-good)\n", __func__); set_audio_registers(core, nicam_bgdki_common); set_audio_registers(core, nicam_i); break; default: - dprintk("%s PAL-BGDK NICAM (status: known-good)\n", __FUNCTION__); + dprintk("%s PAL-BGDK NICAM (status: known-good)\n", __func__); set_audio_registers(core, nicam_bgdki_common); set_audio_registers(core, nicam_default); break; @@ -600,28 +600,28 @@ static void set_audio_standard_A2(struct cx88_core *core, u32 mode) set_audio_start(core, SEL_A2); switch (core->tvaudio) { case WW_BG: - dprintk("%s PAL-BG A1/2 (status: known-good)\n", __FUNCTION__); + dprintk("%s PAL-BG A1/2 (status: known-good)\n", __func__); set_audio_registers(core, a2_bgdk_common); set_audio_registers(core, a2_bg); set_audio_registers(core, a2_deemph50); break; case WW_DK: - dprintk("%s PAL-DK A1/2 (status: known-good)\n", __FUNCTION__); + dprintk("%s PAL-DK A1/2 (status: known-good)\n", __func__); set_audio_registers(core, a2_bgdk_common); set_audio_registers(core, a2_dk); set_audio_registers(core, a2_deemph50); break; case WW_I: - dprintk("%s PAL-I A1 (status: known-good)\n", __FUNCTION__); + dprintk("%s PAL-I A1 (status: known-good)\n", __func__); set_audio_registers(core, a1_i); set_audio_registers(core, a2_deemph50); break; case WW_L: - dprintk("%s AM-L (status: devel)\n", __FUNCTION__); + dprintk("%s AM-L (status: devel)\n", __func__); set_audio_registers(core, am_l); break; default: - dprintk("%s Warning: wrong value\n", __FUNCTION__); + dprintk("%s Warning: wrong value\n", __func__); return; break; }; @@ -637,7 +637,7 @@ static void set_audio_standard_EIAJ(struct cx88_core *core) { /* end of list */ }, }; - dprintk("%s (status: unknown)\n", __FUNCTION__); + dprintk("%s (status: unknown)\n", __func__); set_audio_start(core, SEL_EIAJ); set_audio_registers(core, eiaj); @@ -691,7 +691,7 @@ static void set_audio_standard_FM(struct cx88_core *core, { /* end of list */ }, }; - dprintk("%s (status: unknown)\n", __FUNCTION__); + dprintk("%s (status: unknown)\n", __func__); set_audio_start(core, SEL_FMRADIO); switch (deemph) { diff --git a/drivers/media/video/cx88/cx88-vbi.c b/drivers/media/video/cx88/cx88-vbi.c index d96ecfcf393..0943060682b 100644 --- a/drivers/media/video/cx88/cx88-vbi.c +++ b/drivers/media/video/cx88/cx88-vbi.c @@ -11,7 +11,7 @@ static unsigned int vbibufs = 4; module_param(vbibufs,int,0644); MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32"); -static unsigned int vbi_debug = 0; +static unsigned int vbi_debug; module_param(vbi_debug,int,0644); MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]"); diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 227179620d1..eea23f95edb 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -63,11 +63,11 @@ MODULE_PARM_DESC(video_nr,"video device numbers"); MODULE_PARM_DESC(vbi_nr,"vbi device numbers"); MODULE_PARM_DESC(radio_nr,"radio device numbers"); -static unsigned int video_debug = 0; +static unsigned int video_debug; module_param(video_debug,int,0644); MODULE_PARM_DESC(video_debug,"enable debug messages [video]"); -static unsigned int irq_debug = 0; +static unsigned int irq_debug; module_param(irq_debug,int,0644); MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]"); @@ -228,6 +228,30 @@ static struct cx88_ctrl cx8800_ctls[] = { .mask = 0x00ff, .shift = 0, },{ + .v = { + .id = V4L2_CID_CHROMA_AGC, + .name = "Chroma AGC", + .minimum = 0, + .maximum = 1, + .default_value = 0x1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, + .reg = MO_INPUT_FORMAT, + .mask = 1 << 10, + .shift = 10, + }, { + .v = { + .id = V4L2_CID_COLOR_KILLER, + .name = "Color killer", + .minimum = 0, + .maximum = 1, + .default_value = 0x1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, + .reg = MO_INPUT_FORMAT, + .mask = 1 << 9, + .shift = 9, + }, { /* --- audio --- */ .v = { .id = V4L2_CID_AUDIO_MUTE, @@ -282,6 +306,8 @@ const u32 cx88_user_ctrls[] = { V4L2_CID_AUDIO_VOLUME, V4L2_CID_AUDIO_BALANCE, V4L2_CID_AUDIO_MUTE, + V4L2_CID_CHROMA_AGC, + V4L2_CID_COLOR_KILLER, 0 }; EXPORT_SYMBOL(cx88_user_ctrls); @@ -291,7 +317,7 @@ static const u32 *ctrl_classes[] = { NULL }; -int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl) +int cx8800_ctrl_query(struct cx88_core *core, struct v4l2_queryctrl *qctrl) { int i; @@ -306,6 +332,11 @@ int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl) return 0; } *qctrl = cx8800_ctls[i].v; + /* Report chroma AGC as inactive when SECAM is selected */ + if (cx8800_ctls[i].v.id == V4L2_CID_CHROMA_AGC && + core->tvnorm & V4L2_STD_SECAM) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; } EXPORT_SYMBOL(cx8800_ctrl_query); @@ -776,14 +807,14 @@ static int video_open(struct inode *inode, struct file *file) fh->height = 240; fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); - videobuf_queue_pci_init(&fh->vidq, &cx8800_video_qops, - dev->pci, &dev->slock, + videobuf_queue_sg_init(&fh->vidq, &cx8800_video_qops, + &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct cx88_buffer), fh); - videobuf_queue_pci_init(&fh->vbiq, &cx8800_vbi_qops, - dev->pci, &dev->slock, + videobuf_queue_sg_init(&fh->vbiq, &cx8800_vbi_qops, + &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_FIELD_SEQ_TB, sizeof(struct cx88_buffer), @@ -976,6 +1007,12 @@ int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl) } mask=0xffff; break; + case V4L2_CID_CHROMA_AGC: + /* Do not allow chroma AGC to be enabled for SECAM */ + value = ((ctl->value - c->off) << c->shift) & c->mask; + if (core->tvnorm & V4L2_STD_SECAM && value) + return -EINVAL; + break; default: value = ((ctl->value - c->off) << c->shift) & c->mask; break; @@ -1268,10 +1305,12 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i) static int vidioc_queryctrl (struct file *file, void *priv, struct v4l2_queryctrl *qctrl) { + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); if (unlikely(qctrl->id == 0)) return -EINVAL; - return cx8800_ctrl_query(qctrl); + return cx8800_ctrl_query(core, qctrl); } static int vidioc_g_ctrl (struct file *file, void *priv, @@ -1832,8 +1871,11 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, switch (core->boardnr) { case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: - request_module("ir-kbd-i2c"); + case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: request_module("rtc-isl1208"); + /* break intentionally omitted */ + case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: + request_module("ir-kbd-i2c"); } /* register v4l devices */ @@ -1917,6 +1959,9 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev) core->kthread = NULL; } + if (core->ir) + cx88_ir_stop(core, core->ir); + cx88_shutdown(core); /* FIXME */ pci_disable_device(pci_dev); diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 37e6d2e4002..14ac173f407 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -37,6 +37,7 @@ #include "btcx-risc.h" #include "cx88-reg.h" +#include "tuner-xc2028.h" #include <linux/version.h> #include <linux/mutex.h> @@ -211,6 +212,15 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_HAUPPAUGE_HVR1300 56 #define CX88_BOARD_ADSTECH_PTV_390 57 #define CX88_BOARD_PINNACLE_PCTV_HD_800i 58 +#define CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO 59 +#define CX88_BOARD_PINNACLE_HYBRID_PCTV 60 +#define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL 61 +#define CX88_BOARD_POWERCOLOR_REAL_ANGEL 62 +#define CX88_BOARD_GENIATECH_X8000_MT 63 +#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO 64 +#define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65 +#define CX88_BOARD_PROLINK_PV_8000GT 66 +#define CX88_BOARD_KWORLD_ATSC_120 67 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, @@ -595,6 +605,7 @@ extern int cx88_tuner_callback(void *dev, int command, int arg); extern int cx88_get_resources(const struct cx88_core *core, struct pci_dev *pci); extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr); +extern void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl); /* ----------------------------------------------------------- */ /* cx88-tvaudio.c */ @@ -640,7 +651,8 @@ void cx8802_cancel_buffers(struct cx8802_dev *dev); /* ----------------------------------------------------------- */ /* cx88-video.c*/ extern const u32 cx88_user_ctrls[]; -extern int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl); +extern int cx8800_ctrl_query(struct cx88_core *core, + struct v4l2_queryctrl *qctrl); int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i); int cx88_set_freq (struct cx88_core *core,struct v4l2_frequency *f); int cx88_get_control(struct cx88_core *core, struct v4l2_control *ctl); diff --git a/drivers/media/video/dabfirmware.h b/drivers/media/video/dabfirmware.h index d14d803566a..cbd92635993 100644 --- a/drivers/media/video/dabfirmware.h +++ b/drivers/media/video/dabfirmware.h @@ -1,5 +1,12 @@ /* * dabdata.h - dab usb firmware and bitstream data + * + * Copyright (C) 1999 BayCom GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that redistributions of source + * code retain the above copyright notice and this comment without + * modification. */ static INTEL_HEX_RECORD firmware[] = { diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c index a5731f90be0..8d1f8ee2a53 100644 --- a/drivers/media/video/dabusb.c +++ b/drivers/media/video/dabusb.c @@ -205,7 +205,7 @@ static void dabusb_iso_complete (struct urb *purb) /*-------------------------------------------------------------------*/ static int dabusb_alloc_buffers (pdabusb_t s) { - int buffers = 0; + int transfer_len = 0; pbuff_t b; unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE); int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe)); @@ -216,7 +216,7 @@ static int dabusb_alloc_buffers (pdabusb_t s) dbg("dabusb_alloc_buffers pipesize:%d packets:%d transfer_buffer_len:%d", pipesize, packets, transfer_buffer_length); - while (buffers < (s->total_buffer_size << 10)) { + while (transfer_len < (s->total_buffer_size << 10)) { b = kzalloc(sizeof (buff_t), GFP_KERNEL); if (!b) { err("kzalloc(sizeof(buff_t))==NULL"); @@ -251,10 +251,10 @@ static int dabusb_alloc_buffers (pdabusb_t s) b->purb->iso_frame_desc[i].length = pipesize; } - buffers += transfer_buffer_length; + transfer_len += transfer_buffer_length; list_add_tail (&b->buff_list, &s->free_buff_list); } - s->got_mem = buffers; + s->got_mem = transfer_len; return 0; diff --git a/drivers/media/video/dpc7146.c b/drivers/media/video/dpc7146.c index 9ceb6b2f394..88d6df71d05 100644 --- a/drivers/media/video/dpc7146.c +++ b/drivers/media/video/dpc7146.c @@ -54,11 +54,11 @@ #define DPC_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "debug verbosity"); -static int dpc_num = 0; +static int dpc_num; #define DPC_INPUTS 2 static struct v4l2_input dpc_inputs[DPC_INPUTS] = { diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index 0f7a0bd86ff..9caffed2b6b 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -1,11 +1,13 @@ config VIDEO_EM28XX - tristate "Empia EM2800/2820/2840 USB video capture support" + tristate "Empia EM28xx USB video capture support" depends on VIDEO_DEV && I2C && INPUT select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_IR + select VIDEOBUF_VMALLOC select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO ---help--- This is a video4linux driver for Empia 28xx based TV cards. @@ -27,3 +29,13 @@ config VIDEO_EM28XX_ALSA To compile this driver as a module, choose M here: the module will be called em28xx-alsa +config VIDEO_EM28XX_DVB + tristate "DVB/ATSC Support for em28xx based TV cards" + depends on VIDEO_EM28XX && DVB_CORE + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select VIDEOBUF_DVB + select FW_LOADER + ---help--- + This adds support for DVB cards based on the + Empiatech em28xx chips. diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile index 0924550992d..3d1c3cc337f 100644 --- a/drivers/media/video/em28xx/Makefile +++ b/drivers/media/video/em28xx/Makefile @@ -5,6 +5,7 @@ em28xx-alsa-objs := em28xx-audio.o obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o +obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index 8c67f678266..92b2a6db4fd 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -51,7 +51,7 @@ MODULE_PARM_DESC(debug, "activates debug info"); #define dprintk(fmt, arg...) do { \ if (debug) \ printk(KERN_INFO "em28xx-audio %s: " fmt, \ - __FUNCTION__, ##arg); \ + __func__, ##arg); \ } while (0) static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index aae7753fef1..50ccf377120 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -36,7 +36,6 @@ #include <media/v4l2-common.h> #include "em28xx.h" -#include "tuner-xc2028.h" static int tuner = -1; module_param(tuner, int, 0444); @@ -52,26 +51,6 @@ struct em28xx_hash_table { unsigned int tuner; }; -/* Boards supported by driver */ - -#define EM2800_BOARD_UNKNOWN 0 -#define EM2820_BOARD_UNKNOWN 1 -#define EM2820_BOARD_TERRATEC_CINERGY_250 2 -#define EM2820_BOARD_PINNACLE_USB_2 3 -#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4 -#define EM2820_BOARD_MSI_VOX_USB_2 5 -#define EM2800_BOARD_TERRATEC_CINERGY_200 6 -#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7 -#define EM2800_BOARD_KWORLD_USB2800 8 -#define EM2820_BOARD_PINNACLE_DVC_90 9 -#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10 -#define EM2880_BOARD_TERRATEC_HYBRID_XS 11 -#define EM2820_BOARD_KWORLD_PVRTV2800RF 12 -#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13 -#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 -#define EM2800_BOARD_VGEAR_POCKETTV 15 -#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 16 - struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_UNKNOWN] = { .name = "Unknown EM2800 video grabber", @@ -200,6 +179,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .mts_firmware = 1, .has_12mhz_i2s = 1, + .has_dvb = 1, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -214,9 +194,6 @@ struct em28xx_board em28xx_boards[] = { .vmux = TVP5150_SVIDEO, .amux = 1, } }, - - /* gpio's 4, 1, 0 */ - .analog_gpio = 0x003d2d, }, [EM2880_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS", @@ -331,7 +308,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Kworld USB2800", .is_em2800 = 1, .vchannels = 3, - .tuner_type = TUNER_PHILIPS_ATSC, + .tuner_type = TUNER_PHILIPS_FCV1236D, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA7113, .input = { { @@ -453,7 +430,36 @@ struct usb_device_id em28xx_id_table [] = { }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); -/* EEPROM hash table for devices with generic USB IDs */ +/* + * Reset sequences for analog/digital modes + */ + +/* Board Hauppauge WinTV HVR 900 analog */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = { + {EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10}, + {0x05, 0xff, 0x10, 10}, + { -1, -1, -1, -1}, +}; + +/* Board Hauppauge WinTV HVR 900 digital */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = { + {EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x04, 0x0f, 10}, + {EM2880_R04_GPO, 0x0c, 0x0f, 10}, + { -1, -1, -1, -1}, +}; + +/* Board Hauppauge WinTV HVR 900 tuner_callback */ +static struct em28xx_reg_seq hauppauge_wintv_hvr_900_tuner_callback[] = { + {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, + {EM28XX_R08_GPIO, 0, EM_GPIO_4, 10}, + {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, + { -1, -1, -1, -1}, +}; + +/* + * EEPROM hash table for devices with generic USB IDs + */ static struct em28xx_hash_table em28xx_eeprom_hash [] = { /* P/N: SA 60002070465 Tuner: TVF7533-MF */ {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, @@ -465,79 +471,113 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, }; +int em28xx_tuner_callback(void *ptr, int command, int arg) +{ + int rc = 0; + struct em28xx *dev = ptr; + + if (dev->tuner_type != TUNER_XC2028) + return 0; + + if (command != XC2028_TUNER_RESET) + return 0; + + if (dev->mode == EM28XX_ANALOG_MODE) + rc = em28xx_gpio_set(dev, dev->tun_analog_gpio); + else + rc = em28xx_gpio_set(dev, dev->tun_digital_gpio); + + return rc; +} +EXPORT_SYMBOL_GPL(em28xx_tuner_callback); + +static void em28xx_set_model(struct em28xx *dev) +{ + dev->is_em2800 = em28xx_boards[dev->model].is_em2800; + dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx; + dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; + dev->decoder = em28xx_boards[dev->model].decoder; + dev->video_inputs = em28xx_boards[dev->model].vchannels; + dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s; + dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; + dev->has_dvb = em28xx_boards[dev->model].has_dvb; +} + /* Since em28xx_pre_card_setup() requires a proper dev->model, * this won't work for boards with generic PCI IDs */ void em28xx_pre_card_setup(struct em28xx *dev) { + int rc; + + rc = em28xx_read_reg(dev, EM2880_R04_GPO); + if (rc >= 0) + dev->reg_gpo = rc; + + dev->wait_after_write = 5; + rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); + if (rc > 0) { + switch (rc) { + case CHIP_ID_EM2883: + em28xx_info("chip ID is em2882/em2883\n"); + dev->wait_after_write = 0; + break; + default: + em28xx_info("em28xx chip ID = %d\n", rc); + } + } + em28xx_set_model(dev); + /* request some modules */ switch (dev->model) { case EM2880_BOARD_TERRATEC_PRODIGY_XS: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: - case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2880_BOARD_TERRATEC_HYBRID_XS: - em28xx_write_regs(dev, XCLK_REG, "\x27", 1); - em28xx_write_regs(dev, I2C_CLK_REG, "\x40", 1); - em28xx_write_regs(dev, 0x08, "\xff", 1); - em28xx_write_regs(dev, 0x04, "\x00", 1); - msleep(100); - em28xx_write_regs(dev, 0x04, "\x08", 1); - msleep(100); - em28xx_write_regs(dev, 0x08, "\xff", 1); - msleep(50); - em28xx_write_regs(dev, 0x08, "\x2d", 1); + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); msleep(50); - em28xx_write_regs(dev, 0x08, "\x3d", 1); + + /* Sets GPO/GPIO sequences for this device */ + dev->analog_gpio = hauppauge_wintv_hvr_900_analog; + dev->digital_gpio = hauppauge_wintv_hvr_900_digital; + dev->tun_analog_gpio = hauppauge_wintv_hvr_900_tuner_callback; + dev->tun_digital_gpio = hauppauge_wintv_hvr_900_tuner_callback; + break; } + + em28xx_gpio_set(dev, dev->tun_analog_gpio); + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); + + /* Unlock device */ + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); } -static int em28xx_tuner_callback(void *ptr, int command, int arg) +static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) { - int rc = 0; - struct em28xx *dev = ptr; + memset(ctl, 0, sizeof(*ctl)); - if (dev->tuner_type != TUNER_XC2028) - return 0; - - switch (command) { - case XC2028_TUNER_RESET: - { - /* GPIO and initialization codes for analog TV and radio - This code should be complemented for DTV, since reset - codes are different. - */ - - dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); - dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1); - - if (dev->analog_gpio) { - char gpio0 = dev->analog_gpio & 0xff; - char gpio1 = (dev->analog_gpio >> 8) & 0xff; - char gpio4 = dev->analog_gpio >> 24; - - if (gpio4) { - dev->em28xx_write_regs(dev, 0x04, &gpio4, 1); - msleep(140); - } - - msleep(6); - dev->em28xx_write_regs(dev, 0x08, &gpio0, 1); - msleep(10); - dev->em28xx_write_regs(dev, 0x08, &gpio1, 1); - msleep(5); - } + ctl->fname = XC2028_DEFAULT_FIRMWARE; + ctl->max_len = 64; + ctl->mts = em28xx_boards[dev->model].mts_firmware; + switch (dev->model) { + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + ctl->demod = XC3028_FE_ZARLINK456; break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + /* FIXME: Better to specify the needed IF */ + ctl->demod = XC3028_FE_DEFAULT; + break; + default: + ctl->demod = XC3028_FE_OREN538; } - } - return rc; } static void em28xx_config_tuner(struct em28xx *dev) { struct v4l2_priv_tun_config xc2028_cfg; - struct xc2028_ctrl ctl; struct tuner_setup tun_setup; struct v4l2_frequency f; @@ -552,11 +592,9 @@ static void em28xx_config_tuner(struct em28xx *dev) em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); if (dev->tuner_type == TUNER_XC2028) { - memset(&ctl, 0, sizeof(ctl)); + struct xc2028_ctrl ctl; - ctl.fname = XC2028_DEFAULT_FIRMWARE; - ctl.max_len = 64; - ctl.mts = em28xx_boards[dev->model].mts_firmware; + em28xx_setup_xc3028(dev, &ctl); xc2028_cfg.tuner = TUNER_XC2028; xc2028_cfg.priv = &ctl; @@ -654,19 +692,6 @@ static int em28xx_hint_board(struct em28xx *dev) return -1; } - -static void em28xx_set_model(struct em28xx *dev) -{ - dev->is_em2800 = em28xx_boards[dev->model].is_em2800; - dev->has_msp34xx = em28xx_boards[dev->model].has_msp34xx; - dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; - dev->decoder = em28xx_boards[dev->model].decoder; - dev->video_inputs = em28xx_boards[dev->model].vchannels; - dev->analog_gpio = em28xx_boards[dev->model].analog_gpio; - dev->has_12mhz_i2s = em28xx_boards[dev->model].has_12mhz_i2s; - dev->max_range_640_480 = em28xx_boards[dev->model].max_range_640_480; -} - /* ----------------------------------------------------------------------- */ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir) { diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index c1caaa855b9..f8c41d8c74c 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -31,104 +31,33 @@ /* #define ENABLE_DEBUG_ISOC_FRAMES */ -static unsigned int core_debug = 0; +static unsigned int core_debug; module_param(core_debug,int,0644); MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); #define em28xx_coredbg(fmt, arg...) do {\ if (core_debug) \ printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __FUNCTION__ , ##arg); } while (0) + dev->name, __func__ , ##arg); } while (0) -static unsigned int reg_debug = 0; +static unsigned int reg_debug; module_param(reg_debug,int,0644); MODULE_PARM_DESC(reg_debug,"enable debug messages [URB reg]"); #define em28xx_regdbg(fmt, arg...) do {\ if (reg_debug) \ printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __FUNCTION__ , ##arg); } while (0) - -static unsigned int isoc_debug = 0; -module_param(isoc_debug,int,0644); -MODULE_PARM_DESC(isoc_debug,"enable debug messages [isoc transfers]"); - -#define em28xx_isocdbg(fmt, arg...) do {\ - if (isoc_debug) \ - printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __FUNCTION__ , ##arg); } while (0) + dev->name, __func__ , ##arg); } while (0) static int alt = EM28XX_PINOUT; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); - -/* - * em28xx_request_buffers() - * allocate a number of buffers - */ -u32 em28xx_request_buffers(struct em28xx *dev, u32 count) -{ - const size_t imagesize = PAGE_ALIGN(dev->frame_size); /*needs to be page aligned cause the buffers can be mapped individually! */ - void *buff = NULL; - u32 i; - em28xx_coredbg("requested %i buffers with size %zi\n", - count, imagesize); - if (count > EM28XX_NUM_FRAMES) - count = EM28XX_NUM_FRAMES; - - dev->num_frames = count; - while (dev->num_frames > 0) { - if ((buff = vmalloc_32(dev->num_frames * imagesize))) { - memset(buff, 0, dev->num_frames * imagesize); - break; - } - dev->num_frames--; - } - - for (i = 0; i < dev->num_frames; i++) { - dev->frame[i].bufmem = buff + i * imagesize; - dev->frame[i].buf.index = i; - dev->frame[i].buf.m.offset = i * imagesize; - dev->frame[i].buf.length = dev->frame_size; - dev->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dev->frame[i].buf.sequence = 0; - dev->frame[i].buf.field = V4L2_FIELD_NONE; - dev->frame[i].buf.memory = V4L2_MEMORY_MMAP; - dev->frame[i].buf.flags = 0; - } - return dev->num_frames; -} - -/* - * em28xx_queue_unusedframes() - * add all frames that are not currently in use to the inbuffer queue - */ -void em28xx_queue_unusedframes(struct em28xx *dev) -{ - unsigned long lock_flags; - u32 i; - - for (i = 0; i < dev->num_frames; i++) - if (dev->frame[i].state == F_UNUSED) { - dev->frame[i].state = F_QUEUED; - spin_lock_irqsave(&dev->queue_lock, lock_flags); - list_add_tail(&dev->frame[i].frame, &dev->inqueue); - spin_unlock_irqrestore(&dev->queue_lock, lock_flags); - } -} - -/* - * em28xx_release_buffers() - * free frame buffers - */ -void em28xx_release_buffers(struct em28xx *dev) -{ - if (dev->num_frames) { - vfree(dev->frame[0].bufmem); - dev->num_frames = 0; - } -} +/* FIXME */ +#define em28xx_isocdbg(fmt, arg...) do {\ + if (core_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) /* * em28xx_read_reg_req() @@ -148,11 +77,11 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, buf, len, HZ); - if (reg_debug){ + if (reg_debug) { printk(ret < 0 ? " failed!\n" : "%02x values: ", ret); - for (byte = 0; byte < len; byte++) { + for (byte = 0; byte < len; byte++) printk(" %02x", (unsigned char)buf[byte]); - } + printk("\n"); } @@ -205,7 +134,10 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, unsigned char *bufs; if (dev->state & DEV_DISCONNECTED) - return(-ENODEV); + return -ENODEV; + + if (len < 1) + return -EINVAL; bufs = kmalloc(len, GFP_KERNEL); @@ -214,8 +146,8 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, if (reg_debug) { int i; for (i = 0; i < len; ++i) - printk (" %02x", (unsigned char)buf[i]); - printk ("\n"); + printk(" %02x", (unsigned char)buf[i]); + printk("\n"); } if (!bufs) @@ -224,14 +156,32 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0x0000, reg, bufs, len, HZ); - msleep(5); /* FIXME: magic number */ + if (dev->wait_after_write) + msleep(dev->wait_after_write); + kfree(bufs); return ret; } int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) { - return em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len); + int rc; + + rc = em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len); + + /* Stores GPO/GPIO values at the cache, if changed + Only write values should be stored, since input on a GPIO + register will return the input bits. + Not sure what happens on reading GPO register. + */ + if (rc >= 0) { + if (reg == EM2880_R04_GPO) + dev->reg_gpo = buf[0]; + else if (reg == EM28XX_R08_GPIO) + dev->reg_gpio = buf[0]; + } + + return rc; } /* @@ -244,9 +194,20 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, { int oldval; u8 newval; - if ((oldval = em28xx_read_reg(dev, reg)) < 0) + + /* Uses cache for gpo/gpio registers */ + if (reg == EM2880_R04_GPO) + oldval = dev->reg_gpo; + else if (reg == EM28XX_R08_GPIO) + oldval = dev->reg_gpio; + else + oldval = em28xx_read_reg(dev, reg); + + if (oldval < 0) return oldval; + newval = (((u8) oldval) & ~bitmask) | (val & bitmask); + return em28xx_write_regs(dev, reg, &newval, 1); } @@ -258,20 +219,26 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) { int ret, i; u8 addr = reg & 0x7f; - if ((ret = em28xx_write_regs(dev, AC97LSB_REG, val, 2)) < 0) + + ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, val, 2); + if (ret < 0) return ret; - if ((ret = em28xx_write_regs(dev, AC97ADDR_REG, &addr, 1)) < 0) + + ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1); + if (ret < 0) return ret; /* Wait up to 50 ms for AC97 command to complete */ for (i = 0; i < 10; i++) { - if ((ret = em28xx_read_reg(dev, AC97BUSY_REG)) < 0) + ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY); + if (ret < 0) return ret; + if (!(ret & 0x01)) return 0; msleep(5); } - em28xx_warn ("AC97 command still being executed: not handled properly!\n"); + em28xx_warn("AC97 command still being executed: not handled properly!\n"); return 0; } @@ -289,7 +256,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) else input = EM2800_AUDIO_SRC_TUNER; - ret = em28xx_write_regs(dev, EM2800_AUDIOSRC_REG, &input, 1); + ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1); if (ret < 0) return ret; } @@ -315,7 +282,7 @@ static int em28xx_set_audio_source(struct em28xx *dev) } } - ret = em28xx_write_reg_bits(dev, AUDIOSRC_REG, input, 0xc0); + ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0); if (ret < 0) return ret; msleep(5); @@ -323,11 +290,11 @@ static int em28xx_set_audio_source(struct em28xx *dev) /* Sets AC97 mixer registers This is seems to be needed, even for non-ac97 configs */ - ret = em28xx_write_ac97(dev, VIDEO_AC97, video); + ret = em28xx_write_ac97(dev, EM28XX_R14_VIDEO_AC97, video); if (ret < 0) return ret; - ret = em28xx_write_ac97(dev, LINE_IN_AC97, line); + ret = em28xx_write_ac97(dev, EM28XX_R10_LINE_IN_AC97, line); return ret; } @@ -343,7 +310,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Mute */ s[1] |= 0x80; - ret = em28xx_write_ac97(dev, MASTER_AC97, s); + ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s); if (ret < 0) return ret; @@ -354,7 +321,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) if (!dev->mute) xclk |= 0x80; - ret = em28xx_write_reg_bits(dev, XCLK_REG, xclk, 0xa7); + ret = em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, xclk, 0xa7); if (ret < 0) return ret; msleep(10); @@ -365,7 +332,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) /* Unmute device */ if (!dev->mute) s[1] &= ~0x80; - ret = em28xx_write_ac97(dev, MASTER_AC97, s); + ret = em28xx_write_ac97(dev, EM28XX_R02_MASTER_AC97, s); return ret; } @@ -373,50 +340,68 @@ EXPORT_SYMBOL_GPL(em28xx_audio_analog_set); int em28xx_colorlevels_set_default(struct em28xx *dev) { - em28xx_write_regs(dev, YGAIN_REG, "\x10", 1); /* contrast */ - em28xx_write_regs(dev, YOFFSET_REG, "\x00", 1); /* brightness */ - em28xx_write_regs(dev, UVGAIN_REG, "\x10", 1); /* saturation */ - em28xx_write_regs(dev, UOFFSET_REG, "\x00", 1); - em28xx_write_regs(dev, VOFFSET_REG, "\x00", 1); - em28xx_write_regs(dev, SHARPNESS_REG, "\x00", 1); - - em28xx_write_regs(dev, GAMMA_REG, "\x20", 1); - em28xx_write_regs(dev, RGAIN_REG, "\x20", 1); - em28xx_write_regs(dev, GGAIN_REG, "\x20", 1); - em28xx_write_regs(dev, BGAIN_REG, "\x20", 1); - em28xx_write_regs(dev, ROFFSET_REG, "\x00", 1); - em28xx_write_regs(dev, GOFFSET_REG, "\x00", 1); - return em28xx_write_regs(dev, BOFFSET_REG, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R20_YGAIN, "\x10", 1); /* contrast */ + em28xx_write_regs(dev, EM28XX_R21_YOFFSET, "\x00", 1); /* brightness */ + em28xx_write_regs(dev, EM28XX_R22_UVGAIN, "\x10", 1); /* saturation */ + em28xx_write_regs(dev, EM28XX_R23_UOFFSET, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R24_VOFFSET, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R25_SHARPNESS, "\x00", 1); + + em28xx_write_regs(dev, EM28XX_R14_GAMMA, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R15_RGAIN, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R16_GGAIN, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R17_BGAIN, "\x20", 1); + em28xx_write_regs(dev, EM28XX_R18_ROFFSET, "\x00", 1); + em28xx_write_regs(dev, EM28XX_R19_GOFFSET, "\x00", 1); + return em28xx_write_regs(dev, EM28XX_R1A_BOFFSET, "\x00", 1); } int em28xx_capture_start(struct em28xx *dev, int start) { - int ret; + int rc; /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ - if ((ret = em28xx_write_reg_bits(dev, USBSUSP_REG, start ? 0x10 : 0x00, - 0x10)) < 0) - return ret; + rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP, + start ? 0x10 : 0x00, 0x10); + if (rc < 0) + return rc; + + if (!start) { + /* disable video capture */ + rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x27", 1); + return rc; + } + /* enable video capture */ - return em28xx_write_regs(dev, VINENABLE_REG, start ? "\x67" : "\x27", 1); + rc = em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); + + if (dev->mode == EM28XX_ANALOG_MODE) + rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x67", 1); + else + rc = em28xx_write_regs(dev, EM28XX_R12_VINENABLE, "\x37", 1); + + msleep(6); + + return rc; } int em28xx_outfmt_set_yuv422(struct em28xx *dev) { - em28xx_write_regs(dev, OUTFMT_REG, "\x34", 1); - em28xx_write_regs(dev, VINMODE_REG, "\x10", 1); - return em28xx_write_regs(dev, VINCTRL_REG, "\x11", 1); + em28xx_write_regs(dev, EM28XX_R27_OUTFMT, "\x34", 1); + em28xx_write_regs(dev, EM28XX_R10_VINMODE, "\x10", 1); + return em28xx_write_regs(dev, EM28XX_R11_VINCTRL, "\x11", 1); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, u8 ymin, u8 ymax) { - em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n", xmin, ymin, xmax, ymax); + em28xx_coredbg("em28xx Scale: (%d,%d)-(%d,%d)\n", + xmin, ymin, xmax, ymax); - em28xx_write_regs(dev, XMIN_REG, &xmin, 1); - em28xx_write_regs(dev, XMAX_REG, &xmax, 1); - em28xx_write_regs(dev, YMIN_REG, &ymin, 1); - return em28xx_write_regs(dev, YMAX_REG, &ymax, 1); + em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1); + em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1); + em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1); + return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1); } static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, @@ -426,34 +411,36 @@ static int em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, u8 cheight = height; u8 overflow = (height >> 7 & 0x02) | (width >> 8 & 0x01); - em28xx_coredbg("em28xx Area Set: (%d,%d)\n", (width | (overflow & 2) << 7), + em28xx_coredbg("em28xx Area Set: (%d,%d)\n", + (width | (overflow & 2) << 7), (height | (overflow & 1) << 8)); - em28xx_write_regs(dev, HSTART_REG, &hstart, 1); - em28xx_write_regs(dev, VSTART_REG, &vstart, 1); - em28xx_write_regs(dev, CWIDTH_REG, &cwidth, 1); - em28xx_write_regs(dev, CHEIGHT_REG, &cheight, 1); - return em28xx_write_regs(dev, OFLOW_REG, &overflow, 1); + em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1); + em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1); + em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1); + em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1); + return em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); } static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) { u8 mode; /* the em2800 scaler only supports scaling down to 50% */ - if(dev->is_em2800) + if (dev->is_em2800) mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); else { u8 buf[2]; buf[0] = h; buf[1] = h >> 8; - em28xx_write_regs(dev, HSCALELOW_REG, (char *)buf, 2); + em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2); buf[0] = v; buf[1] = v >> 8; - em28xx_write_regs(dev, VSCALELOW_REG, (char *)buf, 2); - /* it seems that both H and V scalers must be active to work correctly */ + em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2); + /* it seems that both H and V scalers must be active + to work correctly */ mode = (h || v)? 0x30: 0x00; } - return em28xx_write_reg_bits(dev, COMPR_REG, mode, 0x30); + return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30); } /* FIXME: this only function read values from dev */ @@ -469,376 +456,271 @@ int em28xx_resolution_set(struct em28xx *dev) return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } - -/******************* isoc transfer handling ****************************/ - -#ifdef ENABLE_DEBUG_ISOC_FRAMES -static void em28xx_isoc_dump(struct urb *urb) +int em28xx_set_alternate(struct em28xx *dev) { - int len = 0; - int ntrans = 0; + int errCode, prev_alt = dev->alt; int i; + unsigned int min_pkt_size = dev->width * 2 + 4; - printk(KERN_DEBUG "isocIrq: sf=%d np=%d ec=%x\n", - urb->start_frame, urb->number_of_packets, - urb->error_count); - for (i = 0; i < urb->number_of_packets; i++) { - unsigned char *buf = - urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - int alen = urb->iso_frame_desc[i].actual_length; - if (alen > 0) { - if (buf[0] == 0x88) { - ntrans++; - len += alen; - } else if (buf[0] == 0x22) { - printk(KERN_DEBUG - "= l=%d nt=%d bpp=%d\n", - len - 4 * ntrans, ntrans, - ntrans == 0 ? 0 : len / ntrans); - ntrans = 1; - len = alen; - } else - printk(KERN_DEBUG "!\n"); + /* When image size is bigger than a certain value, + the frame size should be increased, otherwise, only + green screen will be received. + */ + if (dev->width * 2 * dev->height > 720 * 240 * 2) + min_pkt_size *= 2; + + for (i = 0; i < dev->num_alt; i++) { + /* stop when the selected alt setting offers enough bandwidth */ + if (dev->alt_max_pkt_size[i] >= min_pkt_size) { + dev->alt = i; + break; + /* otherwise make sure that we end up with the maximum bandwidth + because the min_pkt_size equation might be wrong... + */ + } else if (dev->alt_max_pkt_size[i] > + dev->alt_max_pkt_size[dev->alt]) + dev->alt = i; + } + + if (dev->alt != prev_alt) { + em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); + dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; + em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", + dev->alt, dev->max_pkt_size); + errCode = usb_set_interface(dev->udev, 0, dev->alt); + if (errCode < 0) { + em28xx_errdev("cannot change alternate number to %d (error=%i)\n", + dev->alt, errCode); + return errCode; } - printk(KERN_DEBUG " n=%d s=%d al=%d %x\n", i, - urb->iso_frame_desc[i].status, - urb->iso_frame_desc[i].actual_length, - (unsigned int) - *((unsigned char *)(urb->transfer_buffer + - urb->iso_frame_desc[i]. - offset))); } + return 0; } -#endif -static inline int em28xx_isoc_video(struct em28xx *dev,struct em28xx_frame_t **f, - unsigned long *lock_flags, unsigned char buf) +int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio) { - if (!(buf & 0x01)) { - if ((*f)->state == F_GRABBING) { - /*previous frame is incomplete */ - if ((*f)->fieldbytesused < dev->field_size) { - (*f)->state = F_ERROR; - em28xx_isocdbg ("dropping incomplete bottom field (%i missing bytes)", - dev->field_size-(*f)->fieldbytesused); - } else { - (*f)->state = F_DONE; - (*f)->buf.bytesused = dev->frame_size; - } - } - if ((*f)->state == F_DONE || (*f)->state == F_ERROR) { - /* move current frame to outqueue and get next free buffer from inqueue */ - spin_lock_irqsave(&dev-> queue_lock, *lock_flags); - list_move_tail(&(*f)->frame, &dev->outqueue); - if (!list_empty(&dev->inqueue)) - (*f) = list_entry(dev-> inqueue.next, - struct em28xx_frame_t,frame); - else - (*f) = NULL; - spin_unlock_irqrestore(&dev->queue_lock,*lock_flags); - } - if (!(*f)) { - em28xx_isocdbg ("new frame but no buffer is free"); - return -1; - } - do_gettimeofday(&(*f)->buf.timestamp); - (*f)->buf.sequence = ++dev->frame_count; - (*f)->buf.field = V4L2_FIELD_INTERLACED; - (*f)->state = F_GRABBING; - (*f)->buf.bytesused = 0; - (*f)->top_field = 1; - (*f)->fieldbytesused = 0; - } else { - /* acquiring bottom field */ - if ((*f)->state == F_GRABBING) { - if (!(*f)->top_field) { - (*f)->state = F_ERROR; - em28xx_isocdbg ("unexpected begin of bottom field; discarding it"); - } else if ((*f)-> fieldbytesused < dev->field_size - 172) { - (*f)->state = F_ERROR; - em28xx_isocdbg ("dropping incomplete top field (%i missing bytes)", - dev->field_size-(*f)->fieldbytesused); - } else { - (*f)->top_field = 0; - (*f)->fieldbytesused = 0; - } + int rc = 0; + + if (!gpio) + return rc; + + dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1); + if (dev->mode == EM28XX_ANALOG_MODE) + dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1); + else + dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x37", 1); + msleep(6); + + /* Send GPIO reset sequences specified at board entry */ + while (gpio->sleep >= 0) { + if (gpio->reg >= 0) { + rc = em28xx_write_reg_bits(dev, + gpio->reg, + gpio->val, + gpio->mask); + if (rc < 0) + return rc; } + if (gpio->sleep > 0) + msleep(gpio->sleep); + + gpio++; } - return (0); + return rc; } -static inline void em28xx_isoc_video_copy(struct em28xx *dev, - struct em28xx_frame_t **f, unsigned char *buf, int len) +int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode) { - void *fieldstart, *startwrite, *startread; - int linesdone, currlinedone, offset, lencopy,remain; + if (dev->mode == set_mode) + return 0; - if(dev->frame_size != (*f)->buf.length){ - em28xx_err("frame_size %i and buf.length %i are different!!!\n",dev->frame_size,(*f)->buf.length); - return; + if (set_mode == EM28XX_MODE_UNDEFINED) { + dev->mode = set_mode; + return 0; } - if ((*f)->fieldbytesused + len > dev->field_size) - len =dev->field_size - (*f)->fieldbytesused; - - if (buf[0] != 0x88 && buf[0] != 0x22) { - em28xx_isocdbg("frame is not complete\n"); - startread = buf; - len+=4; - } else - startread = buf + 4; - - remain = len; + dev->mode = set_mode; - if ((*f)->top_field) - fieldstart = (*f)->bufmem; + if (dev->mode == EM28XX_DIGITAL_MODE) + return em28xx_gpio_set(dev, dev->digital_gpio); else - fieldstart = (*f)->bufmem + dev->bytesperline; - - linesdone = (*f)->fieldbytesused / dev->bytesperline; - currlinedone = (*f)->fieldbytesused % dev->bytesperline; - offset = linesdone * dev->bytesperline * 2 + currlinedone; - startwrite = fieldstart + offset; - lencopy = dev->bytesperline - currlinedone; - lencopy = lencopy > remain ? remain : lencopy; - - memcpy(startwrite, startread, lencopy); - remain -= lencopy; - - while (remain > 0) { - startwrite += lencopy + dev->bytesperline; - startread += lencopy; - if (dev->bytesperline > remain) - lencopy = remain; - else - lencopy = dev->bytesperline; - - memcpy(startwrite, startread, lencopy); - remain -= lencopy; - } - - (*f)->fieldbytesused += len; + return em28xx_gpio_set(dev, dev->analog_gpio); } +EXPORT_SYMBOL_GPL(em28xx_set_mode); + +/* ------------------------------------------------------------------ + URB control + ------------------------------------------------------------------*/ /* - * em28xx_isoIrq() - * handles the incoming isoc urbs and fills the frames from our inqueue + * IRQ callback, called by URB callback */ -static void em28xx_isocIrq(struct urb *urb) +static void em28xx_irq_callback(struct urb *urb) { - struct em28xx *dev = urb->context; - int i, status; - struct em28xx_frame_t **f; - unsigned long lock_flags; - - if (!dev) - return; -#ifdef ENABLE_DEBUG_ISOC_FRAMES - if (isoc_debug>1) - em28xx_isoc_dump(urb); -#endif - - if (urb->status == -ENOENT) - return; - - f = &dev->frame_current; - - if (dev->stream == STREAM_INTERRUPT) { - dev->stream = STREAM_OFF; - if ((*f)) - (*f)->state = F_QUEUED; - em28xx_isocdbg("stream interrupted"); - wake_up_interruptible(&dev->wait_stream); - } - - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) - return; - - if (dev->stream == STREAM_ON && !list_empty(&dev->inqueue)) { - if (!(*f)) - (*f) = list_entry(dev->inqueue.next, - struct em28xx_frame_t, frame); - - for (i = 0; i < urb->number_of_packets; i++) { - unsigned char *buf = urb->transfer_buffer + - urb->iso_frame_desc[i].offset; - int len = urb->iso_frame_desc[i].actual_length - 4; - - if (urb->iso_frame_desc[i].status) { - em28xx_isocdbg("data error: [%d] len=%d, status=%d", i, - urb->iso_frame_desc[i].actual_length, - urb->iso_frame_desc[i].status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; - } - if (urb->iso_frame_desc[i].actual_length <= 0) { - em28xx_isocdbg("packet %d is empty",i); - continue; - } - if (urb->iso_frame_desc[i].actual_length > - urb->iso_frame_desc[i].length) { - em28xx_isocdbg("packet bigger than packet size"); - continue; - } - /*new frame */ - if (buf[0] == 0x22 && buf[1] == 0x5a) { - em28xx_isocdbg("Video frame, length=%i!",len); - - if (em28xx_isoc_video(dev,f,&lock_flags,buf[2])) - break; - } else if (buf[0]==0x33 && buf[1]==0x95 && buf[2]==0x00) { - em28xx_isocdbg("VBI HEADER!!!"); - } + struct em28xx_dmaqueue *dma_q = urb->context; + struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + int rc, i; - /* actual copying */ - if ((*f)->state == F_GRABBING) { - em28xx_isoc_video_copy(dev,f,buf, len); - } - } - } + /* Copy data from URB */ + spin_lock(&dev->slock); + rc = dev->isoc_ctl.isoc_copy(dev, urb); + spin_unlock(&dev->slock); + /* Reset urb buffers */ for (i = 0; i < urb->number_of_packets; i++) { urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } - urb->status = 0; - if ((status = usb_submit_urb(urb, GFP_ATOMIC))) { - em28xx_errdev("resubmit of urb failed (error=%i)\n", status); - dev->state |= DEV_MISCONFIGURED; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + em28xx_isocdbg("urb resubmit failed (error=%i)\n", + urb->status); } - wake_up_interruptible(&dev->wait_frame); - return; } /* - * em28xx_uninit_isoc() - * deallocates the buffers and urbs allocated during em28xx_init_iosc() + * Stop and Deallocate URBs */ void em28xx_uninit_isoc(struct em28xx *dev) { + struct urb *urb; int i; - for (i = 0; i < EM28XX_NUM_BUFS; i++) { - if (dev->urb[i]) { - usb_kill_urb(dev->urb[i]); - if (dev->transfer_buffer[i]) { + em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n"); + + dev->isoc_ctl.nfields = -1; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = dev->isoc_ctl.urb[i]; + if (urb) { + usb_kill_urb(urb); + usb_unlink_urb(urb); + if (dev->isoc_ctl.transfer_buffer[i]) { usb_buffer_free(dev->udev, - dev->urb[i]->transfer_buffer_length, - dev->transfer_buffer[i], - dev->urb[i]->transfer_dma); + urb->transfer_buffer_length, + dev->isoc_ctl.transfer_buffer[i], + urb->transfer_dma); } - usb_free_urb(dev->urb[i]); + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; } - dev->urb[i] = NULL; - dev->transfer_buffer[i] = NULL; + dev->isoc_ctl.transfer_buffer[i] = NULL; } + + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb = NULL; + dev->isoc_ctl.transfer_buffer = NULL; + dev->isoc_ctl.num_bufs = 0; + em28xx_capture_start(dev, 0); } +EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); /* - * em28xx_init_isoc() - * allocates transfer buffers and submits the urbs for isoc transfer + * Allocate URBs and start IRQ */ -int em28xx_init_isoc(struct em28xx *dev) +int em28xx_init_isoc(struct em28xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) { - /* change interface to 3 which allows the biggest packet sizes */ - int i, errCode; - int sb_size; - - em28xx_set_alternate(dev); - sb_size = EM28XX_NUM_PACKETS * dev->max_pkt_size; - - /* reset streaming vars */ - dev->frame_current = NULL; - dev->frame_count = 0; - - /* allocate urbs */ - for (i = 0; i < EM28XX_NUM_BUFS; i++) { - struct urb *urb; - int j; - /* allocate transfer buffer */ - urb = usb_alloc_urb(EM28XX_NUM_PACKETS, GFP_KERNEL); - if (!urb){ - em28xx_errdev("cannot alloc urb %i\n", i); + struct em28xx_dmaqueue *dma_q = &dev->vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int j, k; + int rc; + + em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n"); + + /* De-allocates all pending stuff */ + em28xx_uninit_isoc(dev); + + dev->isoc_ctl.isoc_copy = isoc_copy; + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + em28xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + em28xx_errdev("cannot allocate memory for usbtransfer\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dev->isoc_ctl.max_pkt_size = max_pkt_size; + dev->isoc_ctl.buf = NULL; + + sb_size = max_packets * dev->isoc_ctl.max_pkt_size; + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + em28xx_err("cannot alloc isoc_ctl.urb %i\n", i); em28xx_uninit_isoc(dev); return -ENOMEM; } - dev->transfer_buffer[i] = usb_buffer_alloc(dev->udev, sb_size, - GFP_KERNEL, - &urb->transfer_dma); - if (!dev->transfer_buffer[i]) { - em28xx_errdev - ("unable to allocate %i bytes for transfer buffer %i\n", - sb_size, i); + dev->isoc_ctl.urb[i] = urb; + + dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!dev->isoc_ctl.transfer_buffer[i]) { + em28xx_err("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt()?" while in int":""); em28xx_uninit_isoc(dev); - usb_free_urb(urb); return -ENOMEM; } - memset(dev->transfer_buffer[i], 0, sb_size); - urb->dev = dev->udev; - urb->context = dev; - urb->pipe = usb_rcvisocpipe(dev->udev, 0x82); - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->interval = 1; - urb->transfer_buffer = dev->transfer_buffer[i]; - urb->complete = em28xx_isocIrq; - urb->number_of_packets = EM28XX_NUM_PACKETS; - urb->transfer_buffer_length = sb_size; - for (j = 0; j < EM28XX_NUM_PACKETS; j++) { - urb->iso_frame_desc[j].offset = j * dev->max_pkt_size; - urb->iso_frame_desc[j].length = dev->max_pkt_size; + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + + /* FIXME: this is a hack - should be + 'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK' + should also be using 'desc.bInterval' + */ + pipe = usb_rcvisocpipe(dev->udev, + dev->mode == EM28XX_ANALOG_MODE ? 0x82 : 0x84); + + usb_fill_int_urb(urb, dev->udev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + em28xx_irq_callback, dma_q, 1); + + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP; + + k = 0; + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + dev->isoc_ctl.max_pkt_size; + k += dev->isoc_ctl.max_pkt_size; } - dev->urb[i] = urb; } - /* submit urbs */ - em28xx_coredbg("Submitting %d urbs of %d packets (%d each)\n", - EM28XX_NUM_BUFS, EM28XX_NUM_PACKETS, dev->max_pkt_size); - for (i = 0; i < EM28XX_NUM_BUFS; i++) { - errCode = usb_submit_urb(dev->urb[i], GFP_KERNEL); - if (errCode) { - em28xx_errdev("submit of urb %i failed (error=%i)\n", i, - errCode); - em28xx_uninit_isoc(dev); - return errCode; - } - } - - return 0; -} - -int em28xx_set_alternate(struct em28xx *dev) -{ - int errCode, prev_alt = dev->alt; - int i; - unsigned int min_pkt_size = dev->bytesperline+4; - - /* When image size is bigger than a ceirtain value, - the frame size should be increased, otherwise, only - green screen will be received. - */ - if (dev->frame_size > 720*240*2) - min_pkt_size *= 2; + init_waitqueue_head(&dma_q->wq); - for (i = 0; i < dev->num_alt; i++) - if (dev->alt_max_pkt_size[i] >= min_pkt_size) - break; - dev->alt = i; + em28xx_capture_start(dev, 1); - if (dev->alt != prev_alt) { - em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", - min_pkt_size, dev->alt); - dev->max_pkt_size = dev->alt_max_pkt_size[dev->alt]; - em28xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", - dev->alt, dev->max_pkt_size); - errCode = usb_set_interface(dev->udev, 0, dev->alt); - if (errCode < 0) { - em28xx_errdev ("cannot change alternate number to %d (error=%i)\n", - dev->alt, errCode); - return errCode; + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + em28xx_err("submit of urb %i failed (error=%i)\n", i, + rc); + em28xx_uninit_isoc(dev); + return rc; } } + return 0; } +EXPORT_SYMBOL_GPL(em28xx_init_isoc); diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c new file mode 100644 index 00000000000..7df81575b7f --- /dev/null +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -0,0 +1,474 @@ +/* + DVB device driver for em28xx + + (c) 2008 Mauro Carvalho Chehab <mchehab@infradead.org> + + (c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com> + - Fixes for the driver to properly work with HVR-950 + + (c) 2008 Aidan Thornton <makosoft@googlemail.com> + + Based on cx88-dvb, saa7134-dvb and videobuf-dvb originally written by: + (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> + (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] + + 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. + */ + +#include <linux/kernel.h> +#include <linux/usb.h> + +#include "em28xx.h" +#include <media/v4l2-common.h> +#include <media/videobuf-vmalloc.h> + +#include "lgdt330x.h" +#include "zl10353.h" + +MODULE_DESCRIPTION("driver for em28xx based DVB cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages [dvb]"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk(level, fmt, arg...) do { \ +if (debug >= level) \ + printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ +} while (0) + +#define EM28XX_DVB_NUM_BUFS 5 +#define EM28XX_DVB_MAX_PACKETSIZE 564 +#define EM28XX_DVB_MAX_PACKETS 64 + +struct em28xx_dvb { + struct dvb_frontend *frontend; + + /* feed count management */ + struct mutex lock; + int nfeeds; + + /* general boilerplate stuff */ + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; +}; + + +static inline void print_err_status(struct em28xx *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(1, "URB status %d [%s].\n", status, errmsg); + } else { + dprintk(1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static inline int dvb_isoc_copy(struct em28xx *dev, struct urb *urb) +{ + int i; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } + + return 0; +} + +static int start_streaming(struct em28xx_dvb *dvb) +{ + int rc; + struct em28xx *dev = dvb->adapter.priv; + + usb_set_interface(dev->udev, 0, 1); + rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + if (rc < 0) + return rc; + + return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS, + EM28XX_DVB_NUM_BUFS, EM28XX_DVB_MAX_PACKETSIZE, + dvb_isoc_copy); +} + +static int stop_streaming(struct em28xx_dvb *dvb) +{ + struct em28xx *dev = dvb->adapter.priv; + + em28xx_uninit_isoc(dev); + + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + + return 0; +} + +static int start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct em28xx_dvb *dvb = demux->priv; + int rc, ret; + + if (!demux->dmx.frontend) + return -EINVAL; + + mutex_lock(&dvb->lock); + dvb->nfeeds++; + rc = dvb->nfeeds; + + if (dvb->nfeeds == 1) { + ret = start_streaming(dvb); + if (ret < 0) + rc = ret; + } + + mutex_unlock(&dvb->lock); + return rc; +} + +static int stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct em28xx_dvb *dvb = demux->priv; + int err = 0; + + mutex_lock(&dvb->lock); + dvb->nfeeds--; + + if (0 == dvb->nfeeds) + err = stop_streaming(dvb); + + mutex_unlock(&dvb->lock); + return err; +} + + + +/* ------------------------------------------------------------------ */ +static int em28xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct em28xx *dev = fe->dvb->priv; + + if (acquire) + return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + else + return em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); +} + +/* ------------------------------------------------------------------ */ + +static struct lgdt330x_config em2880_lgdt3303_dev = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, +}; + +static struct zl10353_config em28xx_zl10353_with_xc3028 = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .parallel_ts = 1, + .if2 = 45600, +}; + +/* ------------------------------------------------------------------ */ + +static int attach_xc3028(u8 addr, struct em28xx *dev) +{ + struct dvb_frontend *fe; + struct xc2028_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.i2c_adap = &dev->i2c_adap; + cfg.i2c_addr = addr; + cfg.callback = em28xx_tuner_callback; + + if (!dev->dvb->frontend) { + printk(KERN_ERR "%s/2: dvb frontend not attached. " + "Can't attach xc3028\n", + dev->name); + return -EINVAL; + } + + fe = dvb_attach(xc2028_attach, dev->dvb->frontend, &cfg); + if (!fe) { + printk(KERN_ERR "%s/2: xc3028 attach failed\n", + dev->name); + dvb_frontend_detach(dev->dvb->frontend); + dvb_unregister_frontend(dev->dvb->frontend); + dev->dvb->frontend = NULL; + return -EINVAL; + } + + printk(KERN_INFO "%s/2: xc3028 attached\n", dev->name); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +int register_dvb(struct em28xx_dvb *dvb, + struct module *module, + struct em28xx *dev, + struct device *device) +{ + int result; + + mutex_init(&dvb->lock); + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, dev->name, module, device, + adapter_nr); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n", + dev->name, result); + goto fail_adapter; + } + + /* Ensure all frontends negotiate bus access */ + dvb->frontend->ops.ts_bus_ctrl = em28xx_dvb_bus_ctrl; + + dvb->adapter.priv = dev; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dvb; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = start_feed; + dvb->demux.stop_feed = stop_feed; + + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + dev->name, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", + dev->name, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_fe_conn; + } + + /* register network adapter */ + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +fail_adapter: + return result; +} + +static void unregister_dvb(struct em28xx_dvb *dvb) +{ + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +} + + +static int dvb_init(struct em28xx *dev) +{ + int result = 0; + struct em28xx_dvb *dvb; + + dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); + + if (dvb == NULL) { + printk(KERN_INFO "em28xx_dvb: memory allocation failed\n"); + return -ENOMEM; + } + dev->dvb = dvb; + + em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); + /* init frontend */ + switch (dev->model) { + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950: + dvb->frontend = dvb_attach(lgdt330x_attach, + &em2880_lgdt3303_dev, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + dvb->frontend = dvb_attach(zl10353_attach, + &em28xx_zl10353_with_xc3028, + &dev->i2c_adap); + if (attach_xc3028(0x61, dev) < 0) { + result = -EINVAL; + goto out_free; + } + break; + default: + printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" + " isn't supported yet\n", + dev->name); + break; + } + if (NULL == dvb->frontend) { + printk(KERN_ERR + "%s/2: frontend initialization failed\n", + dev->name); + result = -EINVAL; + goto out_free; + } + + /* register everything */ + result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); + + if (result < 0) + goto out_free; + + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + printk(KERN_INFO "Successfully loaded em28xx-dvb\n"); + return 0; + +out_free: + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + kfree(dvb); + dev->dvb = NULL; + return result; +} + +static int dvb_fini(struct em28xx *dev) +{ + if (dev->dvb) { + unregister_dvb(dev->dvb); + dev->dvb = NULL; + } + + return 0; +} + +static struct em28xx_ops dvb_ops = { + .id = EM28XX_DVB, + .name = "Em28xx dvb Extension", + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init em28xx_dvb_register(void) +{ + return em28xx_register_extension(&dvb_ops); +} + +static void __exit em28xx_dvb_unregister(void) +{ + em28xx_unregister_extension(&dvb_ops); +} + +module_init(em28xx_dvb_register); +module_exit(em28xx_dvb_unregister); diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index cacd04d46e9..6a78fd294ca 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -33,19 +33,29 @@ /* ----------------------------------------------------------- */ -static unsigned int i2c_scan = 0; +static unsigned int i2c_scan; module_param(i2c_scan, int, 0444); MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); -static unsigned int i2c_debug = 0; +static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); -#define dprintk1(lvl,fmt, args...) if (i2c_debug>=lvl) do {\ - printk(fmt, ##args); } while (0) -#define dprintk2(lvl,fmt, args...) if (i2c_debug>=lvl) do{ \ - printk(KERN_DEBUG "%s at %s: " fmt, \ - dev->name, __FUNCTION__ , ##args); } while (0) + +#define dprintk1(lvl, fmt, args...) \ +do { \ + if (i2c_debug >= lvl) { \ + printk(fmt, ##args); \ + } \ +} while (0) + +#define dprintk2(lvl, fmt, args...) \ +do { \ + if (i2c_debug >= lvl) { \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __func__ , ##args); \ + } \ +} while (0) /* * em2800_i2c_send_max4() @@ -235,16 +245,16 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, return 0; for (i = 0; i < num; i++) { addr = msgs[i].addr << 1; - dprintk2(2,"%s %s addr=%x len=%d:", + dprintk2(2, "%s %s addr=%x len=%d:", (msgs[i].flags & I2C_M_RD) ? "read" : "write", i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); - if (!msgs[i].len) { /* no len: check only for device presence */ + if (!msgs[i].len) { /* no len: check only for device presence */ if (dev->is_em2800) rc = em2800_i2c_check_for_device(dev, addr); else rc = em28xx_i2c_check_for_device(dev, addr); if (rc < 0) { - dprintk2(2," no device\n"); + dprintk2(2, " no device\n"); return rc; } @@ -258,14 +268,13 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, rc = em28xx_i2c_recv_bytes(dev, addr, msgs[i].buf, msgs[i].len); - if (i2c_debug>=2) { - for (byte = 0; byte < msgs[i].len; byte++) { + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) printk(" %02x", msgs[i].buf[byte]); - } } } else { /* write bytes */ - if (i2c_debug>=2) { + if (i2c_debug >= 2) { for (byte = 0; byte < msgs[i].len; byte++) printk(" %02x", msgs[i].buf[byte]); } @@ -281,13 +290,13 @@ static int em28xx_i2c_xfer(struct i2c_adapter *i2c_adap, } if (rc < 0) goto err; - if (i2c_debug>=2) + if (i2c_debug >= 2) printk("\n"); } return num; - err: - dprintk2(2," ERROR: %i\n", rc); +err: + dprintk2(2, " ERROR: %i\n", rc); return rc; } @@ -330,7 +339,9 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) return -1; buf = 0; - if (1 != (err = i2c_master_send(&dev->i2c_client, &buf, 1))) { + + err = i2c_master_send(&dev->i2c_client, &buf, 1); + if (err != 1) { printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", dev->name, err); return -1; @@ -403,8 +414,10 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) break; } printk(KERN_INFO "Table at 0x%02x, strings=0x%04x, 0x%04x, 0x%04x\n", - em_eeprom->string_idx_table,em_eeprom->string1, - em_eeprom->string2,em_eeprom->string3); + em_eeprom->string_idx_table, + em_eeprom->string1, + em_eeprom->string2, + em_eeprom->string3); return 0; } @@ -430,58 +443,61 @@ static int attach_inform(struct i2c_client *client) struct em28xx *dev = client->adapter->algo_data; switch (client->addr << 1) { - case 0x86: - case 0x84: - case 0x96: - case 0x94: - { - struct v4l2_priv_tun_config tda9887_cfg; - - struct tuner_setup tun_setup; - - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = TUNER_TDA9887; - tun_setup.addr = client->addr; - - em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &dev->tda9887_conf; - em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, - &tda9887_cfg); - break; - } - case 0x42: - dprintk1(1,"attach_inform: saa7114 detected.\n"); - break; - case 0x4a: - dprintk1(1,"attach_inform: saa7113 detected.\n"); - break; - case 0xa0: - dprintk1(1,"attach_inform: eeprom detected.\n"); - break; - case 0x60: - case 0x8e: - { - struct IR_i2c *ir = i2c_get_clientdata(client); - dprintk1(1,"attach_inform: IR detected (%s).\n",ir->phys); - em28xx_set_ir(dev,ir); - break; - } - case 0x80: - case 0x88: - dprintk1(1,"attach_inform: msp34xx detected.\n"); - break; - case 0xb8: - case 0xba: - dprintk1(1,"attach_inform: tvp5150 detected.\n"); - break; - - default: - if (!dev->tuner_addr) - dev->tuner_addr = client->addr; - - dprintk1(1,"attach inform: detected I2C address %x\n", client->addr << 1); + case 0x86: + case 0x84: + case 0x96: + case 0x94: + { + struct v4l2_priv_tun_config tda9887_cfg; + + struct tuner_setup tun_setup; + + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = TUNER_TDA9887; + tun_setup.addr = client->addr; + + em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, + &tun_setup); + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &dev->tda9887_conf; + em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, + &tda9887_cfg); + break; + } + case 0x42: + dprintk1(1, "attach_inform: saa7114 detected.\n"); + break; + case 0x4a: + dprintk1(1, "attach_inform: saa7113 detected.\n"); + break; + case 0xa0: + dprintk1(1, "attach_inform: eeprom detected.\n"); + break; + case 0x60: + case 0x8e: + { + struct IR_i2c *ir = i2c_get_clientdata(client); + dprintk1(1, "attach_inform: IR detected (%s).\n", + ir->phys); + em28xx_set_ir(dev, ir); + break; + } + case 0x80: + case 0x88: + dprintk1(1, "attach_inform: msp34xx detected.\n"); + break; + case 0xb8: + case 0xba: + dprintk1(1, "attach_inform: tvp5150 detected.\n"); + break; + + default: + if (!dev->tuner_addr) + dev->tuner_addr = client->addr; + + dprintk1(1, "attach inform: detected I2C address %x\n", + client->addr << 1); } diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 10da2fd8d98..bb5807159b8 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -32,10 +32,12 @@ static unsigned int ir_debug; module_param(ir_debug, int, 0644); -MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]"); +MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); -#define dprintk(fmt, arg...) if (ir_debug) \ - printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg) +#define dprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \ + } /* ----------------------------------------------------------------------- */ @@ -44,7 +46,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(&ir->c,&b,1)) { + if (1 != i2c_master_recv(&ir->c, &b, 1)) { dprintk("read error\n"); return -EIO; } @@ -74,24 +76,25 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char code; /* poll IR chip */ - if (2 != i2c_master_recv(&ir->c,buf,2)) + if (2 != i2c_master_recv(&ir->c, buf, 2)) return -EIO; /* Does eliminate repeated parity code */ - if (buf[1]==0xff) + if (buf[1] == 0xff) return 0; - ir->old=buf[1]; + ir->old = buf[1]; /* Rearranges bits to the right order */ - code= ((buf[0]&0x01)<<5) | /* 0010 0000 */ + code = ((buf[0]&0x01)<<5) | /* 0010 0000 */ ((buf[0]&0x02)<<3) | /* 0001 0000 */ ((buf[0]&0x04)<<1) | /* 0000 1000 */ ((buf[0]&0x08)>>1) | /* 0000 0100 */ ((buf[0]&0x10)>>3) | /* 0000 0010 */ ((buf[0]&0x20)>>5); /* 0000 0001 */ - dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n",code,buf[0]); + dprintk("ir hauppauge (em2840): code=0x%02x (rcv=0x%02x)\n", + code, buf[0]); /* return key */ *ir_key = code; @@ -106,15 +109,14 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, /* poll IR chip */ - if (3 != i2c_master_recv(&ir->c,buf,3)) { + if (3 != i2c_master_recv(&ir->c, buf, 3)) { dprintk("read error\n"); return -EIO; } dprintk("key %02x\n", buf[2]&0x3f); - if (buf[0]!=0x00){ + if (buf[0] != 0x00) return 0; - } *ir_key = buf[2]&0x3f; *ir_raw = buf[2]&0x3f; diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h new file mode 100644 index 00000000000..9058bed0795 --- /dev/null +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -0,0 +1,88 @@ +#define EM_GPIO_0 (1 << 0) +#define EM_GPIO_1 (1 << 1) +#define EM_GPIO_2 (1 << 2) +#define EM_GPIO_3 (1 << 3) +#define EM_GPIO_4 (1 << 4) +#define EM_GPIO_5 (1 << 5) +#define EM_GPIO_6 (1 << 6) +#define EM_GPIO_7 (1 << 7) + +#define EM_GPO_0 (1 << 0) +#define EM_GPO_1 (1 << 1) +#define EM_GPO_2 (1 << 2) +#define EM_GPO_3 (1 << 3) + +/* em2800 registers */ +#define EM2800_R08_AUDIOSRC 0x08 + +/* em28xx registers */ + + /* GPIO/GPO registers */ +#define EM2880_R04_GPO 0x04 /* em2880-em2883 only */ +#define EM28XX_R08_GPIO 0x08 /* em2820 or upper */ + +#define EM28XX_R06_I2C_CLK 0x06 +#define EM28XX_R0A_CHIPID 0x0a +#define EM28XX_R0C_USBSUSP 0x0c /* */ + +#define EM28XX_R0E_AUDIOSRC 0x0e +#define EM28XX_R0F_XCLK 0x0f + +#define EM28XX_R10_VINMODE 0x10 +#define EM28XX_R11_VINCTRL 0x11 +#define EM28XX_R12_VINENABLE 0x12 /* */ + +#define EM28XX_R14_GAMMA 0x14 +#define EM28XX_R15_RGAIN 0x15 +#define EM28XX_R16_GGAIN 0x16 +#define EM28XX_R17_BGAIN 0x17 +#define EM28XX_R18_ROFFSET 0x18 +#define EM28XX_R19_GOFFSET 0x19 +#define EM28XX_R1A_BOFFSET 0x1a + +#define EM28XX_R1B_OFLOW 0x1b +#define EM28XX_R1C_HSTART 0x1c +#define EM28XX_R1D_VSTART 0x1d +#define EM28XX_R1E_CWIDTH 0x1e +#define EM28XX_R1F_CHEIGHT 0x1f + +#define EM28XX_R20_YGAIN 0x20 +#define EM28XX_R21_YOFFSET 0x21 +#define EM28XX_R22_UVGAIN 0x22 +#define EM28XX_R23_UOFFSET 0x23 +#define EM28XX_R24_VOFFSET 0x24 +#define EM28XX_R25_SHARPNESS 0x25 + +#define EM28XX_R26_COMPR 0x26 +#define EM28XX_R27_OUTFMT 0x27 + +#define EM28XX_R28_XMIN 0x28 +#define EM28XX_R29_XMAX 0x29 +#define EM28XX_R2A_YMIN 0x2a +#define EM28XX_R2B_YMAX 0x2b + +#define EM28XX_R30_HSCALELOW 0x30 +#define EM28XX_R31_HSCALEHIGH 0x31 +#define EM28XX_R32_VSCALELOW 0x32 +#define EM28XX_R33_VSCALEHIGH 0x33 + +#define EM28XX_R40_AC97LSB 0x40 +#define EM28XX_R41_AC97MSB 0x41 +#define EM28XX_R42_AC97ADDR 0x42 +#define EM28XX_R43_AC97BUSY 0x43 + +/* em202 registers */ +#define EM28XX_R02_MASTER_AC97 0x02 +#define EM28XX_R10_LINE_IN_AC97 0x10 +#define EM28XX_R14_VIDEO_AC97 0x14 + +/* register settings */ +#define EM2800_AUDIO_SRC_TUNER 0x0d +#define EM2800_AUDIO_SRC_LINE 0x0c +#define EM28XX_AUDIO_SRC_TUNER 0xc0 +#define EM28XX_AUDIO_SRC_LINE 0x80 + +/* FIXME: Need to be populated with the other chip ID's */ +enum em28xx_chip_id { + CHIP_ID_EM2883 = 36, +}; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 4abe6701a77..8996175cc95 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1,5 +1,6 @@ /* - em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB video capture devices + em28xx-video.c - driver for Empia EM2800/EM2820/2840 USB + video capture devices Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> Markus Rechberger <mrechberger@gmail.com> @@ -52,7 +53,19 @@ #define em28xx_videodbg(fmt, arg...) do {\ if (video_debug) \ printk(KERN_INFO "%s %s :"fmt, \ - dev->name, __FUNCTION__ , ##arg); } while (0) + dev->name, __func__ , ##arg); } while (0) + +static unsigned int isoc_debug; +module_param(isoc_debug, int, 0644); +MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); + +#define em28xx_isocdbg(fmt, arg...) \ +do {\ + if (isoc_debug) { \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); \ + } \ + } while (0) MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); @@ -74,9 +87,9 @@ MODULE_PARM_DESC(video_nr, "video device numbers"); MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); MODULE_PARM_DESC(radio_nr, "radio device numbers"); -static unsigned int video_debug = 0; -module_param(video_debug,int,0644); -MODULE_PARM_DESC(video_debug,"enable debug messages [video]"); +static unsigned int video_debug; +module_param(video_debug, int, 0644); +MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); /* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */ static unsigned long em28xx_devused; @@ -93,7 +106,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { .step = 0x1, .default_value = 0x1f, .flags = 0, - },{ + }, { .id = V4L2_CID_AUDIO_MUTE, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "Mute", @@ -107,8 +120,391 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { static struct usb_driver em28xx_usb_driver; +/* ------------------------------------------------------------------ + DMA and thread functions + ------------------------------------------------------------------*/ + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf) +{ + /* Advice that buffer was filled */ + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); -/********************* v4l2 interface ******************************************/ + dev->isoc_ctl.buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +/* + * Identify the buffer header type and properly handles + */ +static void em28xx_copy_video(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + void *fieldstart, *startwrite, *startread; + int linesdone, currlinedone, offset, lencopy, remain; + int bytesperline = dev->width << 1; + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + if (p[0] != 0x88 && p[0] != 0x22) { + em28xx_isocdbg("frame is not complete\n"); + len += 4; + } else + p += 4; + + startread = p; + remain = len; + + /* Interlaces frame */ + if (buf->top_field) + fieldstart = outp; + else + fieldstart = outp + bytesperline; + + linesdone = dma_q->pos / bytesperline; + currlinedone = dma_q->pos % bytesperline; + offset = linesdone * bytesperline * 2 + currlinedone; + startwrite = fieldstart + offset; + lencopy = bytesperline - currlinedone; + lencopy = lencopy > remain ? remain : lencopy; + + if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", + ((char *)startwrite + lencopy) - + ((char *)outp + buf->vb.size)); + lencopy = remain = (char *)outp + buf->vb.size - (char *)startwrite; + } + if (lencopy <= 0) + return; + memcpy(startwrite, startread, lencopy); + + remain -= lencopy; + + while (remain > 0) { + startwrite += lencopy + bytesperline; + startread += lencopy; + if (bytesperline > remain) + lencopy = remain; + else + lencopy = bytesperline; + + if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + em28xx_isocdbg("Overflow of %zi bytes past buffer end (2)\n", + ((char *)startwrite + lencopy) - + ((char *)outp + buf->vb.size)); + lencopy = remain = (char *)outp + buf->vb.size - + (char *)startwrite; + } + if (lencopy <= 0) + break; + + memcpy(startwrite, startread, lencopy); + + remain -= lencopy; + } + + dma_q->pos += len; +} + +static inline void print_err_status(struct em28xx *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + em28xx_isocdbg("URB status %d [%s].\n", status, errmsg); + } else { + em28xx_isocdbg("URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer **buf) +{ + struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + char *outp; + + if (list_empty(&dma_q->active)) { + em28xx_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); + + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0, (*buf)->vb.size); + + dev->isoc_ctl.buf = *buf; + + return; +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) +{ + struct em28xx_buffer *buf; + struct em28xx_dmaqueue *dma_q = urb->context; + unsigned char *outp = NULL; + int i, len = 0, rc = 1; + unsigned char *p; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->isoc_ctl.buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + len = urb->iso_frame_desc[i].actual_length - 4; + + if (urb->iso_frame_desc[i].actual_length <= 0) { + /* em28xx_isocdbg("packet %d is empty",i); - spammy */ + continue; + } + if (urb->iso_frame_desc[i].actual_length > + dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); + continue; + } + + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* FIXME: incomplete buffer checks where removed to make + logic simpler. Impacts of those changes should be evaluated + */ + if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) { + em28xx_isocdbg("VBI HEADER!!!\n"); + /* FIXME: Should add vbi copy */ + continue; + } + if (p[0] == 0x22 && p[1] == 0x5a) { + em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], + len, (p[2] & 1)? "odd" : "even"); + + if (!(p[2] & 1)) { + if (buf != NULL) + buffer_filled(dev, dma_q, buf); + get_next_buf(dma_q, &buf); + if (buf == NULL) + outp = NULL; + else + outp = videobuf_to_vmalloc(&buf->vb); + } + + if (buf != NULL) { + if (p[2] & 1) + buf->top_field = 0; + else + buf->top_field = 1; + } + + dma_q->pos = 0; + } + if (buf != NULL) + em28xx_copy_video(dev, dma_q, buf, p, outp, len); + } + return rc; +} + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct v4l2_frequency f; + + *size = 16 * fh->dev->width * fh->dev->height >> 3; + if (0 == *count) + *count = EM28XX_DEF_BUF; + + if (*count < EM28XX_MIN_BUF) + *count = EM28XX_MIN_BUF; + + /* Ask tuner to go to analog mode */ + memset(&f, 0, sizeof(f)); + f.frequency = dev->ctl_freq; + + em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f); + + return 0; +} + +/* This is called *without* dev->slock held; please keep it that way */ +static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.buf == buf) + dev->isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx *dev = fh->dev; + int rc = 0, urb_init = 0; + + /* FIXME: It assumes depth = 16 */ + /* The only currently supported format is 16 bits/pixel */ + buf->vb.size = 16 * dev->width * dev->height >> 3; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->width; + buf->vb.height = dev->height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + if (!dev->isoc_ctl.num_bufs) + urb_init = 1; + + if (urb_init) { + rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, dev->max_pkt_size, + em28xx_isoc_copy); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct em28xx_dmaqueue *vidq = &dev->vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); + +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = (struct em28xx *)fh->dev; + + em28xx_isocdbg("em28xx: called buffer_release\n"); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops em28xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************* v4l2 interface **************************************/ /* * em28xx_config() @@ -123,9 +519,9 @@ static int em28xx_config(struct em28xx *dev) /* enable vbi capturing */ -/* em28xx_write_regs_req(dev,0x00,0x0e,"\xC0",1); audio register */ -/* em28xx_write_regs_req(dev,0x00,0x0f,"\x80",1); clk register */ - em28xx_write_regs_req(dev,0x00,0x11,"\x51",1); +/* em28xx_write_regs_req(dev, 0x00, 0x0e, "\xC0", 1); audio register */ +/* em28xx_write_regs_req(dev, 0x00, 0x0f, "\x80", 1); clk register */ + em28xx_write_regs_req(dev, 0x00, 0x11, "\x51", 1); dev->mute = 1; /* maybe not the right place... */ dev->volume = 0x1f; @@ -152,23 +548,6 @@ static void em28xx_config_i2c(struct em28xx *dev) em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL); } -/* - * em28xx_empty_framequeues() - * prepare queues for incoming and outgoing frames - */ -static void em28xx_empty_framequeues(struct em28xx *dev) -{ - u32 i; - - INIT_LIST_HEAD(&dev->inqueue); - INIT_LIST_HEAD(&dev->outqueue); - - for (i = 0; i < EM28XX_NUM_FRAMES; i++) { - dev->frame[i].state = F_UNUSED; - dev->frame[i].buf.bytesused = 0; - } -} - static void video_mux(struct em28xx *dev, int index) { struct v4l2_routing route; @@ -181,12 +560,15 @@ static void video_mux(struct em28xx *dev, int index) em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); if (dev->has_msp34xx) { - if (dev->i2s_speed) - em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); + if (dev->i2s_speed) { + em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, + &dev->i2s_speed); + } route.input = dev->ctl_ainput; route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); /* Note: this is msp3400 specific */ - em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route); + em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, + &route); } em28xx_audio_analog_set(dev); @@ -202,15 +584,12 @@ static int res_get(struct em28xx_fh *fh) if (fh->stream_on) return rc; - mutex_lock(&dev->lock); - if (dev->stream_on) - rc = -EINVAL; - else { - dev->stream_on = 1; - fh->stream_on = 1; - } + return -EINVAL; + mutex_lock(&dev->lock); + dev->stream_on = 1; + fh->stream_on = 1; mutex_unlock(&dev->lock); return rc; } @@ -231,33 +610,6 @@ static void res_free(struct em28xx_fh *fh) } /* - * em28xx_vm_open() - */ -static void em28xx_vm_open(struct vm_area_struct *vma) -{ - struct em28xx_frame_t *f = vma->vm_private_data; - f->vma_use_count++; -} - -/* - * em28xx_vm_close() - */ -static void em28xx_vm_close(struct vm_area_struct *vma) -{ - /* NOTE: buffers are not freed here */ - struct em28xx_frame_t *f = vma->vm_private_data; - - if (f->vma_use_count) - f->vma_use_count--; -} - -static struct vm_operations_struct em28xx_vm_ops = { - .open = em28xx_vm_open, - .close = em28xx_vm_close, -}; - - -/* * em28xx_get_ctrl() * return the current saturation, brightness or contrast, mute state */ @@ -296,34 +648,6 @@ static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) } } -/* - * em28xx_stream_interrupt() - * stops streaming - */ -static int em28xx_stream_interrupt(struct em28xx *dev) -{ - int rc = 0; - - /* stop reading from the device */ - - dev->stream = STREAM_INTERRUPT; - rc = wait_event_timeout(dev->wait_stream, - (dev->stream == STREAM_OFF) || - (dev->state & DEV_DISCONNECTED), - EM28XX_URB_TIMEOUT); - - if (rc) { - dev->state |= DEV_MISCONFIGURED; - em28xx_videodbg("device is misconfigured; close and " - "open /dev/video%d again\n", - dev->vdev->minor-MINOR_VFL_TYPE_GRABBER_MIN); - return rc; - } - - return 0; -} - - static int check_dev(struct em28xx *dev) { if (dev->state & DEV_DISCONNECTED) { @@ -370,8 +694,8 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv, f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - f->fmt.pix.bytesperline = dev->bytesperline; - f->fmt.pix.sizeimage = dev->frame_size; + f->fmt.pix.bytesperline = dev->width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */ @@ -447,7 +771,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc, i; + int rc; rc = check_dev(dev); if (rc < 0) @@ -457,49 +781,34 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, mutex_lock(&dev->lock); - for (i = 0; i < dev->num_frames; i++) - if (dev->frame[i].vma_use_count) { - em28xx_videodbg("VIDIOC_S_FMT failed. " - "Unmap the buffers first.\n"); - rc = -EINVAL; - goto err; - } - - /* stop io in case it is already in progress */ - if (dev->stream == STREAM_ON) { - em28xx_videodbg("VIDIOC_SET_FMT: interrupting stream\n"); - rc = em28xx_stream_interrupt(dev); - if (rc < 0) - goto err; + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + em28xx_errdev("%s queue busy\n", __func__); + rc = -EBUSY; + goto out; } - em28xx_release_buffers(dev); - dev->io = IO_NONE; + if (dev->stream_on && !fh->stream_on) { + em28xx_errdev("%s device in use by another fh\n", __func__); + rc = -EBUSY; + goto out; + } /* set new image size */ dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; - dev->frame_size = dev->width * dev->height * 2; - dev->field_size = dev->frame_size >> 1; - dev->bytesperline = dev->width * 2; get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); - /* FIXME: This is really weird! Why capture is starting with - this ioctl ??? - */ - em28xx_uninit_isoc(dev); em28xx_set_alternate(dev); - em28xx_capture_start(dev, 1); em28xx_resolution_set(dev); - em28xx_init_isoc(dev); + rc = 0; -err: +out: mutex_unlock(&dev->lock); return rc; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; @@ -524,9 +833,6 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) /* set new image size */ dev->width = f.fmt.pix.width; dev->height = f.fmt.pix.height; - dev->frame_size = dev->width * dev->height * 2; - dev->field_size = dev->frame_size >> 1; - dev->bytesperline = dev->width * 2; get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); em28xx_resolution_set(dev); @@ -619,11 +925,11 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) index = dev->ctl_ainput; - if (index == 0) { + if (index == 0) strcpy(a->name, "Television"); - } else { + else strcpy(a->name, "Line In"); - } + a->capability = V4L2_AUDCAP_STEREO; a->index = index; @@ -834,9 +1140,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int em28xx_reg_len(int reg) { switch (reg) { - case AC97LSB_REG: - case HSCALELOW_REG: - case VSCALELOW_REG: + case EM28XX_R40_AC97LSB: + case EM28XX_R30_HSCALELOW: + case EM28XX_R32_VSCALELOW: return 2; default: return 1; @@ -918,23 +1224,11 @@ static int vidioc_streamon(struct file *file, void *priv, if (rc < 0) return rc; - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP) - return -EINVAL; - - if (list_empty(&dev->inqueue)) - return -EINVAL; - - mutex_lock(&dev->lock); - if (unlikely(res_get(fh) < 0)) { - mutex_unlock(&dev->lock); + if (unlikely(res_get(fh) < 0)) return -EBUSY; - } - dev->stream = STREAM_ON; /* FIXME: Start video capture here? */ - - mutex_unlock(&dev->lock); - return 0; + return (videobuf_streamon(&fh->vb_vidq)); } static int vidioc_streamoff(struct file *file, void *priv, @@ -948,23 +1242,14 @@ static int vidioc_streamoff(struct file *file, void *priv, if (rc < 0) return rc; - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP) + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (type != fh->type) return -EINVAL; - mutex_lock(&dev->lock); - - if (dev->stream == STREAM_ON) { - em28xx_videodbg("VIDIOC_STREAMOFF: interrupting stream\n"); - rc = em28xx_stream_interrupt(dev); - if (rc < 0) { - mutex_unlock(&dev->lock); - return rc; - } - } - - em28xx_empty_framequeues(dev); + videobuf_streamoff(&fh->vb_vidq); + res_free(fh); - mutex_unlock(&dev->lock); return 0; } @@ -1058,53 +1343,13 @@ static int vidioc_reqbufs(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - u32 i; int rc; rc = check_dev(dev); if (rc < 0) return rc; - if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - rb->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (dev->io == IO_READ) { - em28xx_videodbg("method is set to read;" - " close and open the device again to" - " choose the mmap I/O method\n"); - return -EINVAL; - } - - for (i = 0; i < dev->num_frames; i++) - if (dev->frame[i].vma_use_count) { - em28xx_videodbg("VIDIOC_REQBUFS failed; " - "previous buffers are still mapped\n"); - return -EINVAL; - } - - mutex_lock(&dev->lock); - - if (dev->stream == STREAM_ON) { - em28xx_videodbg("VIDIOC_REQBUFS: interrupting stream\n"); - rc = em28xx_stream_interrupt(dev); - if (rc < 0) { - mutex_unlock(&dev->lock); - return rc; - } - } - - em28xx_empty_framequeues(dev); - - em28xx_release_buffers(dev); - if (rb->count) - rb->count = em28xx_request_buffers(dev, rb->count); - - dev->frame_current = NULL; - dev->io = rb->count ? IO_MMAP : IO_NONE; - - mutex_unlock(&dev->lock); - return 0; + return (videobuf_reqbufs(&fh->vb_vidq, rb)); } static int vidioc_querybuf(struct file *file, void *priv, @@ -1118,52 +1363,20 @@ static int vidioc_querybuf(struct file *file, void *priv, if (rc < 0) return rc; - if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b->index >= dev->num_frames || dev->io != IO_MMAP) - return -EINVAL; - - mutex_lock(&dev->lock); - - memcpy(b, &dev->frame[b->index].buf, sizeof(*b)); - - if (dev->frame[b->index].vma_use_count) - b->flags |= V4L2_BUF_FLAG_MAPPED; - - if (dev->frame[b->index].state == F_DONE) - b->flags |= V4L2_BUF_FLAG_DONE; - else if (dev->frame[b->index].state != F_UNUSED) - b->flags |= V4L2_BUF_FLAG_QUEUED; - - mutex_unlock(&dev->lock); - return 0; + return (videobuf_querybuf(&fh->vb_vidq, b)); } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - unsigned long lock_flags; int rc; rc = check_dev(dev); if (rc < 0) return rc; - if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP || - b->index >= dev->num_frames) - return -EINVAL; - - if (dev->frame[b->index].state != F_UNUSED) - return -EAGAIN; - - dev->frame[b->index].state = F_QUEUED; - - /* add frame to fifo */ - spin_lock_irqsave(&dev->queue_lock, lock_flags); - list_add_tail(&dev->frame[b->index].frame, &dev->inqueue); - spin_unlock_irqrestore(&dev->queue_lock, lock_flags); - - return 0; + return (videobuf_qbuf(&fh->vb_vidq, b)); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) @@ -1171,46 +1384,24 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; int rc; - struct em28xx_frame_t *f; - unsigned long lock_flags; rc = check_dev(dev); if (rc < 0) return rc; - if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || dev->io != IO_MMAP) - return -EINVAL; - - if (list_empty(&dev->outqueue)) { - if (dev->stream == STREAM_OFF) - return -EINVAL; - - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - - rc = wait_event_interruptible(dev->wait_frame, - (!list_empty(&dev->outqueue)) || - (dev->state & DEV_DISCONNECTED)); - if (rc) - return rc; - - if (dev->state & DEV_DISCONNECTED) - return -ENODEV; - } - - spin_lock_irqsave(&dev->queue_lock, lock_flags); - f = list_entry(dev->outqueue.next, struct em28xx_frame_t, frame); - list_del(dev->outqueue.next); - spin_unlock_irqrestore(&dev->queue_lock, lock_flags); - - f->state = F_UNUSED; - memcpy(b, &f->buf, sizeof(*b)); + return (videobuf_dqbuf(&fh->vb_vidq, b, + file->f_flags & O_NONBLOCK)); +} - if (f->vma_use_count) - b->flags |= V4L2_BUF_FLAG_MAPPED; +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) +{ + struct em28xx_fh *fh = priv; - return 0; + return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); } +#endif + /* ----------------------------------------------------------- */ /* RADIO ESPECIFIC IOCTLS */ @@ -1316,17 +1507,18 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) { int minor = iminor(inode); int errCode = 0, radio = 0; - struct em28xx *h,*dev = NULL; + struct em28xx *h, *dev = NULL; struct em28xx_fh *fh; + enum v4l2_buf_type fh_type = 0; list_for_each_entry(h, &em28xx_devlist, devlist) { if (h->vdev->minor == minor) { dev = h; - dev->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; } if (h->vbi_dev->minor == minor) { dev = h; - dev->type = V4L2_BUF_TYPE_VBI_CAPTURE; + fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; } if (h->radio_dev && h->radio_dev->minor == minor) { @@ -1338,10 +1530,10 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) return -ENODEV; em28xx_videodbg("open minor=%d type=%s users=%d\n", - minor,v4l2_type_names[dev->type],dev->users); + minor, v4l2_type_names[fh_type], dev->users); - fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); + fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); if (!fh) { em28xx_errdev("em28xx-video.c: Out of memory?!\n"); return -ENOMEM; @@ -1349,28 +1541,24 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) mutex_lock(&dev->lock); fh->dev = dev; fh->radio = radio; + fh->type = fh_type; filp->private_data = fh; - if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { dev->width = norm_maxw(dev); dev->height = norm_maxh(dev); - dev->frame_size = dev->width * dev->height * 2; - dev->field_size = dev->frame_size >> 1; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */ - dev->bytesperline = dev->width * 2; dev->hscale = 0; dev->vscale = 0; + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); em28xx_set_alternate(dev); - em28xx_capture_start(dev, 1); em28xx_resolution_set(dev); + /* Needed, since GPIO might have disabled power of + some i2c device + */ + em28xx_config_i2c(dev); - /* start the transfer */ - errCode = em28xx_init_isoc(dev); - if (errCode) - goto err; - - em28xx_empty_framequeues(dev); } if (fh->radio) { em28xx_videodbg("video_open: setting radio device\n"); @@ -1379,8 +1567,12 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp) dev->users++; -err: + videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, + NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, + sizeof(struct em28xx_buffer), fh); + mutex_unlock(&dev->lock); + return errCode; } @@ -1423,12 +1615,13 @@ static void em28xx_release_resources(struct em28xx *dev) usb_put_dev(dev->udev); /* Mark device as unused */ - em28xx_devused&=~(1<<dev->devno); + em28xx_devused &= ~(1<<dev->devno); } /* * em28xx_v4l2_close() - * stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls + * stops streaming and deallocates all resources allocated by the v4l2 + * calls and ioctls */ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) { @@ -1445,9 +1638,8 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) mutex_lock(&dev->lock); if (dev->users == 1) { - em28xx_uninit_isoc(dev); - em28xx_release_buffers(dev); - dev->io = IO_NONE; + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); /* the device is already disconnect, free the remaining resources */ @@ -1458,6 +1650,10 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) return 0; } + /* do this before setting alternate! */ + em28xx_uninit_isoc(dev); + em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED); + /* set alternate 0 */ dev->alt = 0; em28xx_videodbg("setting alternate 0\n"); @@ -1479,135 +1675,29 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp) * will allocate buffers when called for the first time */ static ssize_t -em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count, - loff_t * f_pos) +em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, + loff_t *pos) { - struct em28xx_frame_t *f, *i; - unsigned long lock_flags; - int ret = 0; struct em28xx_fh *fh = filp->private_data; struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; /* FIXME: read() is not prepared to allow changing the video resolution while streaming. Seems a bug at em28xx_set_fmt */ - if (unlikely(res_get(fh) < 0)) - return -EBUSY; - - mutex_lock(&dev->lock); - - if (dev->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - em28xx_videodbg("V4l2_Buf_type_videocapture is set\n"); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (unlikely(res_get(fh))) + return -EBUSY; - if (dev->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - em28xx_videodbg("V4L2_BUF_TYPE_VBI_CAPTURE is set\n"); - em28xx_videodbg("not supported yet! ...\n"); - if (copy_to_user(buf, "", 1)) { - mutex_unlock(&dev->lock); - return -EFAULT; - } - mutex_unlock(&dev->lock); - return (1); - } - if (dev->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - em28xx_videodbg("V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set\n"); - em28xx_videodbg("not supported yet! ...\n"); - if (copy_to_user(buf, "", 1)) { - mutex_unlock(&dev->lock); - return -EFAULT; - } - mutex_unlock(&dev->lock); - return (1); + return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); } - - if (dev->state & DEV_DISCONNECTED) { - em28xx_videodbg("device not present\n"); - mutex_unlock(&dev->lock); - return -ENODEV; - } - - if (dev->state & DEV_MISCONFIGURED) { - em28xx_videodbg("device misconfigured; close and open it again\n"); - mutex_unlock(&dev->lock); - return -EIO; - } - - if (dev->io == IO_MMAP) { - em28xx_videodbg ("IO method is set to mmap; close and open" - " the device again to choose the read method\n"); - mutex_unlock(&dev->lock); - return -EINVAL; - } - - if (dev->io == IO_NONE) { - if (!em28xx_request_buffers(dev, EM28XX_NUM_READ_FRAMES)) { - em28xx_errdev("read failed, not enough memory\n"); - mutex_unlock(&dev->lock); - return -ENOMEM; - } - dev->io = IO_READ; - dev->stream = STREAM_ON; - em28xx_queue_unusedframes(dev); - } - - if (!count) { - mutex_unlock(&dev->lock); - return 0; - } - - if (list_empty(&dev->outqueue)) { - if (filp->f_flags & O_NONBLOCK) { - mutex_unlock(&dev->lock); - return -EAGAIN; - } - ret = wait_event_interruptible - (dev->wait_frame, - (!list_empty(&dev->outqueue)) || - (dev->state & DEV_DISCONNECTED)); - if (ret) { - mutex_unlock(&dev->lock); - return ret; - } - if (dev->state & DEV_DISCONNECTED) { - mutex_unlock(&dev->lock); - return -ENODEV; - } - dev->video_bytesread = 0; - } - - f = list_entry(dev->outqueue.prev, struct em28xx_frame_t, frame); - - em28xx_queue_unusedframes(dev); - - if (count > f->buf.length) - count = f->buf.length; - - if ((dev->video_bytesread + count) > dev->frame_size) - count = dev->frame_size - dev->video_bytesread; - - if (copy_to_user(buf, f->bufmem+dev->video_bytesread, count)) { - em28xx_err("Error while copying to user\n"); - return -EFAULT; - } - dev->video_bytesread += count; - - if (dev->video_bytesread == dev->frame_size) { - spin_lock_irqsave(&dev->queue_lock, lock_flags); - list_for_each_entry(i, &dev->outqueue, frame) - i->state = F_UNUSED; - INIT_LIST_HEAD(&dev->outqueue); - spin_unlock_irqrestore(&dev->queue_lock, lock_flags); - - em28xx_queue_unusedframes(dev); - dev->video_bytesread = 0; - } - - *f_pos += count; - - mutex_unlock(&dev->lock); - - return count; + return 0; } /* @@ -1616,46 +1706,21 @@ em28xx_v4l2_read(struct file *filp, char __user * buf, size_t count, */ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait) { - unsigned int mask = 0; struct em28xx_fh *fh = filp->private_data; struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; if (unlikely(res_get(fh) < 0)) return POLLERR; - mutex_lock(&dev->lock); - - if (dev->state & DEV_DISCONNECTED) { - em28xx_videodbg("device not present\n"); - } else if (dev->state & DEV_MISCONFIGURED) { - em28xx_videodbg("device is misconfigured; close and open it again\n"); - } else { - if (dev->io == IO_NONE) { - if (!em28xx_request_buffers - (dev, EM28XX_NUM_READ_FRAMES)) { - em28xx_warn - ("poll() failed, not enough memory\n"); - } else { - dev->io = IO_READ; - dev->stream = STREAM_ON; - } - } - - if (dev->io == IO_READ) { - em28xx_queue_unusedframes(dev); - poll_wait(filp, &dev->wait_frame, wait); - - if (!list_empty(&dev->outqueue)) - mask |= POLLIN | POLLRDNORM; - - mutex_unlock(&dev->lock); - - return mask; - } - } + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; - mutex_unlock(&dev->lock); - return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); } /* @@ -1665,69 +1730,23 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) { struct em28xx_fh *fh = filp->private_data; struct em28xx *dev = fh->dev; - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long start = vma->vm_start; - void *pos; - u32 i; + int rc; if (unlikely(res_get(fh) < 0)) return -EBUSY; - mutex_lock(&dev->lock); - - if (dev->state & DEV_DISCONNECTED) { - em28xx_videodbg("mmap: device not present\n"); - mutex_unlock(&dev->lock); - return -ENODEV; - } - - if (dev->state & DEV_MISCONFIGURED) { - em28xx_videodbg ("mmap: Device is misconfigured; close and " - "open it again\n"); - mutex_unlock(&dev->lock); - return -EIO; - } - - if (dev->io != IO_MMAP || !(vma->vm_flags & VM_WRITE)) { - mutex_unlock(&dev->lock); - return -EINVAL; - } - - if (size > PAGE_ALIGN(dev->frame[0].buf.length)) - size = PAGE_ALIGN(dev->frame[0].buf.length); - - for (i = 0; i < dev->num_frames; i++) { - if ((dev->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) - break; - } - if (i == dev->num_frames) { - em28xx_videodbg("mmap: user supplied mapping address is out of range\n"); - mutex_unlock(&dev->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 */ + rc = check_dev(dev); + if (rc < 0) + return rc; - pos = dev->frame[i].bufmem; - while (size > 0) { /* size is page-aligned */ - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - em28xx_videodbg("mmap: vm_insert_page failed\n"); - mutex_unlock(&dev->lock); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); - vma->vm_ops = &em28xx_vm_ops; - vma->vm_private_data = &dev->frame[i]; + em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, + rc); - em28xx_vm_open(vma); - mutex_unlock(&dev->lock); - return 0; + return rc; } static const struct file_operations em28xx_v4l_fops = { @@ -1790,6 +1809,9 @@ static const struct video_device em28xx_video_template = { .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, #endif +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif .tvnorms = V4L2_STD_ALL, .current_norm = V4L2_STD_PAL, @@ -1818,7 +1840,7 @@ static struct video_device em28xx_radio_template = { #endif }; -/******************************** usb interface *****************************************/ +/******************************** usb interface ******************************/ static LIST_HEAD(em28xx_extension_devlist); @@ -1875,6 +1897,7 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, vfd->dev = &dev->udev->dev; vfd->release = video_device_release; vfd->type = type; + vfd->debug = video_debug; snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); @@ -1898,7 +1921,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->udev = udev; mutex_init(&dev->lock); - spin_lock_init(&dev->queue_lock); + spin_lock_init(&dev->slock); init_waitqueue_head(&dev->open); init_waitqueue_head(&dev->wait_frame); init_waitqueue_head(&dev->wait_stream); @@ -1910,10 +1933,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->em28xx_read_reg_req = em28xx_read_reg_req; dev->is_em2800 = em28xx_boards[dev->model].is_em2800; - errCode = em28xx_read_reg(dev, CHIPID_REG); - if (errCode >= 0) - em28xx_info("em28xx chip ID = %d\n", errCode); - em28xx_pre_card_setup(dev); errCode = em28xx_config(dev); @@ -1946,10 +1965,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->width = maxw; dev->height = maxh; dev->interlaced = EM28XX_INTERLACED_DEFAULT; - dev->field_size = dev->width * dev->height; - dev->frame_size = - dev->interlaced ? dev->field_size << 1 : dev->field_size; - dev->bytesperline = dev->width * 2; dev->hscale = 0; dev->vscale = 0; dev->ctl_input = 2; @@ -2005,6 +2020,10 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->radio_dev->minor & 0x1f); } + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + if (dev->has_msp34xx) { /* Send a reset to other chips via gpio */ @@ -2048,6 +2067,9 @@ static void request_module_async(struct work_struct *work) request_module("snd-usb-audio"); else request_module("em28xx-alsa"); + + if (dev->has_dvb) + request_module("em28xx-dvb"); } static void request_modules(struct em28xx *dev) @@ -2077,22 +2099,24 @@ static int em28xx_usb_probe(struct usb_interface *interface, ifnum = interface->altsetting[0].desc.bInterfaceNumber; /* Check to see next free device and mark as used */ - nr=find_first_zero_bit(&em28xx_devused,EM28XX_MAXBOARDS); - em28xx_devused|=1<<nr; + nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS); + em28xx_devused |= 1<<nr; /* Don't register audio interfaces */ if (interface->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) { em28xx_err(DRIVER_NAME " audio device (%04x:%04x): interface %i, class %i\n", - udev->descriptor.idVendor,udev->descriptor.idProduct, + udev->descriptor.idVendor, + udev->descriptor.idProduct, ifnum, interface->altsetting[0].desc.bInterfaceClass); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENODEV; } em28xx_err(DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i\n", - udev->descriptor.idVendor,udev->descriptor.idProduct, + udev->descriptor.idVendor, + udev->descriptor.idProduct, ifnum, interface->altsetting[0].desc.bInterfaceClass); @@ -2102,18 +2126,19 @@ static int em28xx_usb_probe(struct usb_interface *interface, if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) { em28xx_err(DRIVER_NAME " probing error: endpoint is non-ISO endpoint!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENODEV; } if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { em28xx_err(DRIVER_NAME " probing error: endpoint is ISO OUT endpoint!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENODEV; } if (nr >= EM28XX_MAXBOARDS) { - printk (DRIVER_NAME ": Supports only %i em28xx boards.\n",EM28XX_MAXBOARDS); - em28xx_devused&=~(1<<nr); + printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", + EM28XX_MAXBOARDS); + em28xx_devused &= ~(1<<nr); return -ENOMEM; } @@ -2121,7 +2146,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { em28xx_err(DRIVER_NAME ": out of memory!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); return -ENOMEM; } @@ -2145,14 +2170,14 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* compute alternate max packet sizes */ uif = udev->actconfig->interface[0]; - dev->num_alt=uif->num_altsetting; - em28xx_info("Alternate settings: %i\n",dev->num_alt); -// dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* - dev->alt_max_pkt_size = kmalloc(32* - dev->num_alt,GFP_KERNEL); + dev->num_alt = uif->num_altsetting; + em28xx_info("Alternate settings: %i\n", dev->num_alt); +/* dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)* */ + dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL); + if (dev->alt_max_pkt_size == NULL) { em28xx_errdev("out of memory!\n"); - em28xx_devused&=~(1<<nr); + em28xx_devused &= ~(1<<nr); kfree(dev); return -ENOMEM; } @@ -2162,11 +2187,11 @@ static int em28xx_usb_probe(struct usb_interface *interface, wMaxPacketSize); dev->alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); - em28xx_info("Alternate setting %i, max size= %i\n",i, - dev->alt_max_pkt_size[i]); + em28xx_info("Alternate setting %i, max size= %i\n", i, + dev->alt_max_pkt_size[i]); } - if ((card[nr]>=0)&&(card[nr]<em28xx_bcount)) + if ((card[nr] >= 0) && (card[nr] < em28xx_bcount)) dev->model = card[nr]; /* allocate device struct */ @@ -2202,7 +2227,8 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_info("disconnecting %s\n", dev->vdev->name); - /* wait until all current v4l2 io is finished then deallocate resources */ + /* wait until all current v4l2 io is finished then deallocate + resources */ mutex_lock(&dev->lock); wake_up_interruptible_all(&dev->open); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 04e0e48ecab..002f170b211 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -26,11 +26,39 @@ #define _EM28XX_H #include <linux/videodev2.h> +#include <media/videobuf-vmalloc.h> + #include <linux/i2c.h> #include <linux/mutex.h> #include <media/ir-kbd-i2c.h> - -#define UNSET -1 +#if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) +#include <media/videobuf-dvb.h> +#endif +#include "tuner-xc2028.h" +#include "em28xx-reg.h" + +/* Boards supported by driver */ +#define EM2800_BOARD_UNKNOWN 0 +#define EM2820_BOARD_UNKNOWN 1 +#define EM2820_BOARD_TERRATEC_CINERGY_250 2 +#define EM2820_BOARD_PINNACLE_USB_2 3 +#define EM2820_BOARD_HAUPPAUGE_WINTV_USB_2 4 +#define EM2820_BOARD_MSI_VOX_USB_2 5 +#define EM2800_BOARD_TERRATEC_CINERGY_200 6 +#define EM2800_BOARD_LEADTEK_WINFAST_USBII 7 +#define EM2800_BOARD_KWORLD_USB2800 8 +#define EM2820_BOARD_PINNACLE_DVC_90 9 +#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 10 +#define EM2880_BOARD_TERRATEC_HYBRID_XS 11 +#define EM2820_BOARD_KWORLD_PVRTV2800RF 12 +#define EM2880_BOARD_TERRATEC_PRODIGY_XS 13 +#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14 +#define EM2800_BOARD_VGEAR_POCKETTV 15 +#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 16 + +/* Limits minimum and default number of buffers */ +#define EM28XX_MIN_BUF 4 +#define EM28XX_DEF_BUF 8 /* maximum number of em28xx boards */ #define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */ @@ -81,31 +109,78 @@ /* time in msecs to wait for i2c writes to finish */ #define EM2800_I2C_WRITE_TIMEOUT 20 -/* the various frame states */ -enum em28xx_frame_state { - F_UNUSED = 0, - F_QUEUED, - F_GRABBING, - F_DONE, - F_ERROR, +enum em28xx_mode { + EM28XX_MODE_UNDEFINED, + EM28XX_ANALOG_MODE, + EM28XX_DIGITAL_MODE, }; -/* stream states */ enum em28xx_stream_state { STREAM_OFF, STREAM_INTERRUPT, STREAM_ON, }; -/* frames */ -struct em28xx_frame_t { - void *bufmem; - struct v4l2_buffer buf; - enum em28xx_frame_state state; +struct em28xx; + +struct em28xx_usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct em28xx_buffer *buf; + + /* Stores the number of received fields */ + int nfields; + + /* isoc urb callback */ + int (*isoc_copy) (struct em28xx *dev, struct urb *urb); + +}; + +struct em28xx_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ +}; + +/* buffer for one video frame */ +struct em28xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + struct list_head frame; - unsigned long vma_use_count; int top_field; - int fieldbytesused; + int receiving; +}; + +struct em28xx_dmaqueue { + struct list_head active; + struct list_head queued; + + wait_queue_head_t wq; + + /* Counters to control buffer fill */ + int pos; }; /* io methods */ @@ -152,6 +227,12 @@ enum em28xx_decoder { EM28XX_SAA7114 }; +struct em28xx_reg_seq { + int reg; + unsigned char val, mask; + int sleep; +}; + struct em28xx_board { char *name; int vchannels; @@ -165,8 +246,7 @@ struct em28xx_board { unsigned int mts_firmware:1; unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; - - unsigned int analog_gpio; + unsigned int has_dvb:1; enum em28xx_decoder decoder; @@ -199,7 +279,10 @@ enum em28xx_dev_state { #define EM28XX_NUM_AUDIO_PACKETS 64 #define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */ #define EM28XX_CAPTURE_STREAM_EN 1 + +/* em28xx extensions */ #define EM28XX_AUDIO 0x10 +#define EM28XX_DVB 0x20 struct em28xx_audio { char name[50]; @@ -217,13 +300,24 @@ struct em28xx_audio { spinlock_t slock; }; +struct em28xx; + +struct em28xx_fh { + struct em28xx *dev; + unsigned int stream_on:1; /* Locks streams */ + int radio; + + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + /* main device struct */ struct em28xx { /* generic device properties */ char name[30]; /* name (including minor) of the device */ int model; /* index in the device_data struct */ int devno; /* marks the number of this device */ - unsigned int analog_gpio; unsigned int is_em2800:1; unsigned int has_msp34xx:1; unsigned int has_tda9887:1; @@ -231,6 +325,16 @@ struct em28xx { unsigned int has_audio_class:1; unsigned int has_12mhz_i2s:1; unsigned int max_range_640_480:1; + unsigned int has_dvb:1; + + /* Some older em28xx chips needs a waiting time after writing */ + unsigned int wait_after_write; + + /* GPIO sequences for analog and digital mode */ + struct em28xx_reg_seq *analog_gpio, *digital_gpio; + + /* GPIO sequences for tuner callbacks */ + struct em28xx_reg_seq *tun_analog_gpio, *tun_digital_gpio; int video_inputs; /* number of video inputs */ struct list_head devlist; @@ -255,36 +359,28 @@ struct em28xx { int mute; int volume; /* frame properties */ - struct em28xx_frame_t frame[EM28XX_NUM_FRAMES]; /* list of frames */ - int num_frames; /* number of frames currently in use */ - unsigned int frame_count; /* total number of transfered frames */ - struct em28xx_frame_t *frame_current; /* the frame that is being filled */ int width; /* current frame width */ int height; /* current frame height */ - int frame_size; /* current frame size */ - int field_size; /* current field size */ - int bytesperline; int hscale; /* horizontal scale factor (see datasheet) */ int vscale; /* vertical scale factor (see datasheet) */ int interlaced; /* 1=interlace fileds, 0=just top fileds */ - int type; unsigned int video_bytesread; /* Number of bytes read */ unsigned long hash; /* eeprom hash - for boards with generic ID */ - unsigned long i2c_hash; /* i2c devicelist hash - for boards with generic ID */ + unsigned long i2c_hash; /* i2c devicelist hash - + for boards with generic ID */ struct em28xx_audio *adev; /* states */ enum em28xx_dev_state state; - enum em28xx_stream_state stream; enum em28xx_io_method io; struct work_struct request_module_wk; /* locks */ struct mutex lock; - spinlock_t queue_lock; + /* spinlock_t queue_lock; */ struct list_head inqueue, outqueue; wait_queue_head_t open, wait_frame, wait_stream; struct video_device *vbi_dev; @@ -292,6 +388,11 @@ struct em28xx { unsigned char eedata[256]; + /* Isoc control struct */ + struct em28xx_dmaqueue vidq; + struct em28xx_usb_isoc_ctl isoc_ctl; + spinlock_t slock; + /* usb transfer */ struct usb_device *udev; /* the usb device */ int alt; /* alternate */ @@ -301,20 +402,21 @@ struct em28xx { struct urb *urb[EM28XX_NUM_BUFS]; /* urb for isoc transfers */ char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc transfer */ /* helper funcs that call usb_control_msg */ - int (*em28xx_write_regs) (struct em28xx * dev, u16 reg, char *buf, - int len); - int (*em28xx_read_reg) (struct em28xx * dev, u16 reg); - int (*em28xx_read_reg_req_len) (struct em28xx * dev, u8 req, u16 reg, + int (*em28xx_write_regs) (struct em28xx *dev, u16 reg, char *buf, int len); - int (*em28xx_write_regs_req) (struct em28xx * dev, u8 req, u16 reg, + int (*em28xx_read_reg) (struct em28xx *dev, u16 reg); + int (*em28xx_read_reg_req_len) (struct em28xx *dev, u8 req, u16 reg, + char *buf, int len); + int (*em28xx_write_regs_req) (struct em28xx *dev, u8 req, u16 reg, char *buf, int len); - int (*em28xx_read_reg_req) (struct em28xx * dev, u8 req, u16 reg); -}; + int (*em28xx_read_reg_req) (struct em28xx *dev, u8 req, u16 reg); -struct em28xx_fh { - struct em28xx *dev; - unsigned int stream_on:1; /* Locks streams */ - int radio; + enum em28xx_mode mode; + + /* Caches GPO and GPIO registers */ + unsigned char reg_gpo, reg_gpio; + + struct em28xx_dvb *dvb; }; struct em28xx_ops { @@ -351,22 +453,27 @@ int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); int em28xx_outfmt_set_yuv422(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); -int em28xx_init_isoc(struct em28xx *dev); -void em28xx_uninit_isoc(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); +int em28xx_init_isoc(struct em28xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); +void em28xx_uninit_isoc(struct em28xx *dev); +int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); +int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); /* Provided by em28xx-video.c */ int em28xx_register_extension(struct em28xx_ops *dev); void em28xx_unregister_extension(struct em28xx_ops *dev); /* Provided by em28xx-cards.c */ -extern int em2800_variant_detect(struct usb_device* udev,int model); +extern int em2800_variant_detect(struct usb_device *udev, int model); extern void em28xx_pre_card_setup(struct em28xx *dev); extern void em28xx_card_setup(struct em28xx *dev); extern struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; extern const unsigned int em28xx_bcount; void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir); +int em28xx_tuner_callback(void *ptr, int command, int arg); /* Provided by em28xx-input.c */ /* TODO: Check if the standard get_key handlers on ir-common can be used */ @@ -375,71 +482,6 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -/* em2800 registers */ -#define EM2800_AUDIOSRC_REG 0x08 - -/* em28xx registers */ -#define I2C_CLK_REG 0x06 -#define CHIPID_REG 0x0a -#define USBSUSP_REG 0x0c /* */ - -#define AUDIOSRC_REG 0x0e -#define XCLK_REG 0x0f - -#define VINMODE_REG 0x10 -#define VINCTRL_REG 0x11 -#define VINENABLE_REG 0x12 /* */ - -#define GAMMA_REG 0x14 -#define RGAIN_REG 0x15 -#define GGAIN_REG 0x16 -#define BGAIN_REG 0x17 -#define ROFFSET_REG 0x18 -#define GOFFSET_REG 0x19 -#define BOFFSET_REG 0x1a - -#define OFLOW_REG 0x1b -#define HSTART_REG 0x1c -#define VSTART_REG 0x1d -#define CWIDTH_REG 0x1e -#define CHEIGHT_REG 0x1f - -#define YGAIN_REG 0x20 -#define YOFFSET_REG 0x21 -#define UVGAIN_REG 0x22 -#define UOFFSET_REG 0x23 -#define VOFFSET_REG 0x24 -#define SHARPNESS_REG 0x25 - -#define COMPR_REG 0x26 -#define OUTFMT_REG 0x27 - -#define XMIN_REG 0x28 -#define XMAX_REG 0x29 -#define YMIN_REG 0x2a -#define YMAX_REG 0x2b - -#define HSCALELOW_REG 0x30 -#define HSCALEHIGH_REG 0x31 -#define VSCALELOW_REG 0x32 -#define VSCALEHIGH_REG 0x33 - -#define AC97LSB_REG 0x40 -#define AC97MSB_REG 0x41 -#define AC97ADDR_REG 0x42 -#define AC97BUSY_REG 0x43 - -/* em202 registers */ -#define MASTER_AC97 0x02 -#define LINE_IN_AC97 0x10 -#define VIDEO_AC97 0x14 - -/* register settings */ -#define EM2800_AUDIO_SRC_TUNER 0x0d -#define EM2800_AUDIO_SRC_LINE 0x0c -#define EM28XX_AUDIO_SRC_TUNER 0xc0 -#define EM28XX_AUDIO_SRC_LINE 0x80 - /* printk macros */ #define em28xx_err(fmt, arg...) do {\ @@ -456,80 +498,80 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, printk(KERN_WARNING "%s: "fmt,\ dev->name , ##arg); } while (0) -inline static int em28xx_compression_disable(struct em28xx *dev) +static inline int em28xx_compression_disable(struct em28xx *dev) { /* side effect of disabling scaler and mixer */ - return em28xx_write_regs(dev, COMPR_REG, "\x00", 1); + return em28xx_write_regs(dev, EM28XX_R26_COMPR, "\x00", 1); } -inline static int em28xx_contrast_get(struct em28xx *dev) +static inline int em28xx_contrast_get(struct em28xx *dev) { - return em28xx_read_reg(dev, YGAIN_REG) & 0x1f; + return em28xx_read_reg(dev, EM28XX_R20_YGAIN) & 0x1f; } -inline static int em28xx_brightness_get(struct em28xx *dev) +static inline int em28xx_brightness_get(struct em28xx *dev) { - return em28xx_read_reg(dev, YOFFSET_REG); + return em28xx_read_reg(dev, EM28XX_R21_YOFFSET); } -inline static int em28xx_saturation_get(struct em28xx *dev) +static inline int em28xx_saturation_get(struct em28xx *dev) { - return em28xx_read_reg(dev, UVGAIN_REG) & 0x1f; + return em28xx_read_reg(dev, EM28XX_R22_UVGAIN) & 0x1f; } -inline static int em28xx_u_balance_get(struct em28xx *dev) +static inline int em28xx_u_balance_get(struct em28xx *dev) { - return em28xx_read_reg(dev, UOFFSET_REG); + return em28xx_read_reg(dev, EM28XX_R23_UOFFSET); } -inline static int em28xx_v_balance_get(struct em28xx *dev) +static inline int em28xx_v_balance_get(struct em28xx *dev) { - return em28xx_read_reg(dev, VOFFSET_REG); + return em28xx_read_reg(dev, EM28XX_R24_VOFFSET); } -inline static int em28xx_gamma_get(struct em28xx *dev) +static inline int em28xx_gamma_get(struct em28xx *dev) { - return em28xx_read_reg(dev, GAMMA_REG) & 0x3f; + return em28xx_read_reg(dev, EM28XX_R14_GAMMA) & 0x3f; } -inline static int em28xx_contrast_set(struct em28xx *dev, s32 val) +static inline int em28xx_contrast_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, YGAIN_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R20_YGAIN, &tmp, 1); } -inline static int em28xx_brightness_set(struct em28xx *dev, s32 val) +static inline int em28xx_brightness_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, YOFFSET_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R21_YOFFSET, &tmp, 1); } -inline static int em28xx_saturation_set(struct em28xx *dev, s32 val) +static inline int em28xx_saturation_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, UVGAIN_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R22_UVGAIN, &tmp, 1); } -inline static int em28xx_u_balance_set(struct em28xx *dev, s32 val) +static inline int em28xx_u_balance_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, UOFFSET_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R23_UOFFSET, &tmp, 1); } -inline static int em28xx_v_balance_set(struct em28xx *dev, s32 val) +static inline int em28xx_v_balance_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, VOFFSET_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R24_VOFFSET, &tmp, 1); } -inline static int em28xx_gamma_set(struct em28xx *dev, s32 val) +static inline int em28xx_gamma_set(struct em28xx *dev, s32 val) { u8 tmp = (u8) val; - return em28xx_write_regs(dev, GAMMA_REG, &tmp, 1); + return em28xx_write_regs(dev, EM28XX_R14_GAMMA, &tmp, 1); } /*FIXME: maxw should be dependent of alt mode */ -inline static unsigned int norm_maxw(struct em28xx *dev) +static inline unsigned int norm_maxw(struct em28xx *dev) { if (dev->max_range_640_480) return 640; @@ -537,7 +579,7 @@ inline static unsigned int norm_maxw(struct em28xx *dev) return 720; } -inline static unsigned int norm_maxh(struct em28xx *dev) +static inline unsigned int norm_maxh(struct em28xx *dev) { if (dev->max_range_640_480) return 480; diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h index 02c741d8f85..cc77d144df3 100644 --- a/drivers/media/video/et61x251/et61x251.h +++ b/drivers/media/video/et61x251/et61x251.h @@ -199,7 +199,7 @@ do { \ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ else if ((level) >= 3) \ dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ - __FILE__, __FUNCTION__, __LINE__ , ## args); \ + __FILE__, __func__, __LINE__ , ## args); \ } \ } while (0) # define KDBG(level, fmt, args...) \ @@ -209,7 +209,7 @@ do { \ pr_info("et61x251: " fmt "\n", ## args); \ else if ((level) == 3) \ pr_debug("sn9c102: [%s:%s:%d] " fmt "\n", __FILE__, \ - __FUNCTION__, __LINE__ , ## args); \ + __func__, __LINE__ , ## args); \ } \ } while (0) # define V4LDBG(level, name, cmd) \ @@ -225,7 +225,7 @@ do { \ #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __FUNCTION__, \ +dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ __LINE__ , ## args) #undef PDBGG diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index 06b6a3ae06c..5e749c528a6 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -2523,7 +2523,9 @@ static const struct file_operations et61x251_fops = { .open = et61x251_open, .release = et61x251_release, .ioctl = et61x251_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .read = et61x251_read, .poll = et61x251_poll, .mmap = et61x251_mmap, @@ -2538,7 +2540,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) { struct usb_device *udev = interface_to_usbdev(intf); struct et61x251_device* cam; - static unsigned int dev_nr = 0; + static unsigned int dev_nr; unsigned int i; int err = 0; diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index c7fed340565..352f84d440f 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -25,12 +25,12 @@ #include <media/saa7146_vv.h> -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "debug verbosity"); /* global variables */ -static int hexium_num = 0; +static int hexium_num; #define HEXIUM_GEMINI 4 #define HEXIUM_GEMINI_DUAL 5 diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 137c4736da0..8d3c1482e7e 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -25,12 +25,12 @@ #include <media/saa7146_vv.h> -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "debug verbosity"); /* global variables */ -static int hexium_num = 0; +static int hexium_num; #define HEXIUM_HV_PCI6_ORION 1 #define HEXIUM_ORION_1SVHS_3BNC 2 diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 9851987b95f..11c5fdedc23 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -40,7 +40,6 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/workqueue.h> -#include <asm/semaphore.h> #include <media/ir-common.h> #include <media/ir-kbd-i2c.h> @@ -51,7 +50,7 @@ static int debug; module_param(debug, int, 0644); /* debug level (0,1,2) */ -static int hauppauge = 0; +static int hauppauge; module_param(hauppauge, int, 0644); /* Choose Hauppauge remote */ MODULE_PARM_DESC(hauppauge, "Specify Hauppauge remote: 0=black, 1=grey (defaults to 0)"); @@ -154,7 +153,7 @@ static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) } if(buf[0] !=0 || buf[1] !=0 || buf[2] !=0 || buf[3] != 0) - dprintk(2, "%s: 0x%2x 0x%2x 0x%2x 0x%2x\n", __FUNCTION__, + dprintk(2, "%s: 0x%2x 0x%2x 0x%2x 0x%2x\n", __func__, buf[0], buf[1], buf[2], buf[3]); /* no key pressed or signal from other ir remote */ @@ -509,10 +508,10 @@ static int ir_probe(struct i2c_adapter *adap) static const int probe_em28XX[] = { 0x30, 0x47, -1 }; static const int probe_cx88[] = { 0x18, 0x6b, 0x71, -1 }; static const int probe_cx23885[] = { 0x6b, -1 }; - const int *probe = NULL; - struct i2c_client c; + const int *probe; + struct i2c_client *c; unsigned char buf; - int i,rc; + int i, rc; switch (adap->id) { case I2C_HW_B_BT848: @@ -533,23 +532,27 @@ static int ir_probe(struct i2c_adapter *adap) case I2C_HW_B_CX23885: probe = probe_cx23885; break; - } - if (NULL == probe) + default: return 0; + } + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return -ENOMEM; - memset(&c,0,sizeof(c)); - c.adapter = adap; + c->adapter = adap; for (i = 0; -1 != probe[i]; i++) { - c.addr = probe[i]; - rc = i2c_master_recv(&c,&buf,0); + c->addr = probe[i]; + rc = i2c_master_recv(c, &buf, 0); dprintk(1,"probe 0x%02x @ %s: %s\n", probe[i], adap->name, (0 == rc) ? "yes" : "no"); if (0 == rc) { - ir_attach(adap,probe[i],0,0); + ir_attach(adap, probe[i], 0, 0); break; } } + kfree(c); return 0; } diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig index 270906fc314..b6171702c4d 100644 --- a/drivers/media/video/ivtv/Kconfig +++ b/drivers/media/video/ivtv/Kconfig @@ -10,6 +10,7 @@ config VIDEO_IVTV select VIDEO_CX25840 select VIDEO_MSP3400 select VIDEO_SAA711X + select VIDEO_SAA717X select VIDEO_SAA7127 select VIDEO_TVAUDIO select VIDEO_CS53L32A diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c index f23c6b8d691..e908649ea37 100644 --- a/drivers/media/video/ivtv/ivtv-cards.c +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -416,11 +416,10 @@ static const struct ivtv_card ivtv_card_avc2410 = { on the country/region setting of the user to decide which tuner is available. */ .tuners = { - /* This tuner has been verified for the AVC2410 */ { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, - /* This is a good guess, but I'm not totally sure this is - the correct tuner for NTSC. */ - { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + { .std = V4L2_STD_ALL - V4L2_STD_NTSC_M_JP, + .tuner = TUNER_PHILIPS_FM1236_MK3 }, + { .std = V4L2_STD_NTSC_M_JP, .tuner = TUNER_PHILIPS_FQ1286 }, }, .pci_list = ivtv_pci_avc2410, .i2c = &ivtv_i2c_std, diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h index 191aafdd996..9186fa2ee5f 100644 --- a/drivers/media/video/ivtv/ivtv-cards.h +++ b/drivers/media/video/ivtv/ivtv-cards.h @@ -119,7 +119,7 @@ #define IVTV_CARD_MAX_VIDEO_INPUTS 6 #define IVTV_CARD_MAX_AUDIO_INPUTS 3 -#define IVTV_CARD_MAX_TUNERS 2 +#define IVTV_CARD_MAX_TUNERS 3 /* SAA71XX HW inputs */ #define IVTV_SAA71XX_COMPOSITE0 0 diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 948ca35e7ee..065df53f80f 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -101,7 +101,7 @@ static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, static unsigned int cardtype_c = 1; static unsigned int tuner_c = 1; static unsigned int radio_c = 1; -static char pal[] = "--"; +static char pal[] = "---"; static char secam[] = "--"; static char ntsc[] = "-"; @@ -126,12 +126,13 @@ static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS; static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS; static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS; -static int ivtv_yuv_mode = 0; -static int ivtv_yuv_threshold=-1; +static int ivtv_yuv_mode; +static int ivtv_yuv_threshold = -1; static int ivtv_pci_latency = 1; -int ivtv_debug = 0; +int ivtv_debug; +static int tunertype = -1; static int newi2c = -1; module_param_array(tuner, int, &tuner_c, 0644); @@ -154,6 +155,7 @@ module_param(dec_mpg_buffers, int, 0644); module_param(dec_yuv_buffers, int, 0644); module_param(dec_vbi_buffers, int, 0644); +module_param(tunertype, int, 0644); module_param(newi2c, int, 0644); MODULE_PARM_DESC(tuner, "Tuner type selection,\n" @@ -190,9 +192,14 @@ MODULE_PARM_DESC(cardtype, "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n" "\t\t\t 0 = Autodetect (default)\n" "\t\t\t-1 = Ignore this card\n\t\t"); -MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); -MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC"); -MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K"); +MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60"); +MODULE_PARM_DESC(secam, "Set SECAM standard: BGH, DK, L, LC"); +MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J (Japan), K (South Korea)"); +MODULE_PARM_DESC(tunertype, + "Specify tuner type:\n" + "\t\t\t 0 = tuner for PAL-B/G/H/D/K/I, SECAM-B/G/H/D/K/L/Lc\n" + "\t\t\t 1 = tuner for NTSC-M/J/K, PAL-M/N/Nc\n" + "\t\t\t-1 = Autodetect (default)\n"); MODULE_PARM_DESC(debug, "Debug level (bitmask). Default: 0\n" "\t\t\t 1/0x0001: warning\n" @@ -490,30 +497,35 @@ static v4l2_std_id ivtv_parse_std(struct ivtv *itv) { switch (pal[0]) { case '6': + tunertype = 0; return V4L2_STD_PAL_60; case 'b': case 'B': case 'g': case 'G': - return V4L2_STD_PAL_BG; case 'h': case 'H': - return V4L2_STD_PAL_H; + tunertype = 0; + return V4L2_STD_PAL_BG | V4L2_STD_PAL_H; case 'n': case 'N': + tunertype = 1; if (pal[1] == 'c' || pal[1] == 'C') return V4L2_STD_PAL_Nc; return V4L2_STD_PAL_N; case 'i': case 'I': + tunertype = 0; return V4L2_STD_PAL_I; case 'd': case 'D': case 'k': case 'K': + tunertype = 0; return V4L2_STD_PAL_DK; case 'M': case 'm': + tunertype = 1; return V4L2_STD_PAL_M; case '-': break; @@ -529,14 +541,17 @@ static v4l2_std_id ivtv_parse_std(struct ivtv *itv) case 'G': case 'h': case 'H': + tunertype = 0; return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; case 'd': case 'D': case 'k': case 'K': + tunertype = 0; return V4L2_STD_SECAM_DK; case 'l': case 'L': + tunertype = 0; if (secam[1] == 'C' || secam[1] == 'c') return V4L2_STD_SECAM_LC; return V4L2_STD_SECAM_L; @@ -550,12 +565,15 @@ static v4l2_std_id ivtv_parse_std(struct ivtv *itv) switch (ntsc[0]) { case 'm': case 'M': + tunertype = 1; return V4L2_STD_NTSC_M; case 'j': case 'J': + tunertype = 1; return V4L2_STD_NTSC_M_JP; case 'k': case 'K': + tunertype = 1; return V4L2_STD_NTSC_M_KR; case '-': break; @@ -584,8 +602,13 @@ static void ivtv_process_options(struct ivtv *itv) itv->options.tuner = tuner[itv->num]; itv->options.radio = radio[itv->num]; itv->options.newi2c = newi2c; - + if (tunertype < -1 || tunertype > 1) { + IVTV_WARN("Invalid tunertype argument, will autodetect instead\n"); + tunertype = -1; + } itv->std = ivtv_parse_std(itv); + if (itv->std == 0 && tunertype >= 0) + itv->std = tunertype ? V4L2_STD_MN : (V4L2_STD_ALL & ~V4L2_STD_MN); itv->has_cx23415 = (itv->dev->device == PCI_DEVICE_ID_IVTV15); chipname = itv->has_cx23415 ? "cx23415" : "cx23416"; if (itv->options.cardtype == -1) { @@ -711,6 +734,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) itv->yuv_info.lace_mode = ivtv_yuv_mode; itv->yuv_info.lace_threshold = ivtv_yuv_threshold; itv->yuv_info.max_frames_buffered = 3; + itv->yuv_info.track_osd = 1; return 0; } @@ -859,7 +883,9 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) #ifndef CONFIG_VIDEO_SAA7127 hw = ivtv_request_module(itv, hw, "saa7127", IVTV_HW_SAA7127); #endif +#ifndef CONFIG_VIDEO_SAA717X hw = ivtv_request_module(itv, hw, "saa717x", IVTV_HW_SAA717X); +#endif #ifndef CONFIG_VIDEO_UPD64031A hw = ivtv_request_module(itv, hw, "upd64031a", IVTV_HW_UPD64031A); #endif diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 536140f0c19..ba06e813c58 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -456,6 +456,8 @@ struct yuv_playback_info int v_filter_2; int h_filter; + u8 track_osd; /* Should yuv output track the OSD size & position */ + u32 osd_x_offset; u32 osd_y_offset; diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 6fb96f19a86..a7640c49f1d 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -219,7 +219,9 @@ static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, /* Process pending program info updates and pending VBI data */ ivtv_update_pgm_info(itv); - if (jiffies - itv->dualwatch_jiffies > msecs_to_jiffies(1000)) { + if (time_after(jiffies, + itv->dualwatch_jiffies + + msecs_to_jiffies(1000))) { itv->dualwatch_jiffies = jiffies; ivtv_dualwatch(itv); } @@ -753,7 +755,7 @@ unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) IVTV_DEBUG_HI_FILE("Encoder poll\n"); poll_wait(filp, &s->waitq, wait); - if (eof || s->q_full.length) + if (eof || s->q_full.length || s->q_io.length) return POLLIN | POLLRDNORM; return 0; } diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index fa5ab1eb180..9824eafee02 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -177,10 +177,16 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) } if (id != I2C_DRIVERID_TUNER) { - c = i2c_new_device(&itv->i2c_adap, &info); - if (c->driver == NULL) + if (id == I2C_DRIVERID_UPD64031A || + id == I2C_DRIVERID_UPD64083) { + unsigned short addrs[2] = { info.addr, I2C_CLIENT_END }; + + c = i2c_new_probed_device(&itv->i2c_adap, &info, addrs); + } else + c = i2c_new_device(&itv->i2c_adap, &info); + if (c && c->driver == NULL) i2c_unregister_device(c); - else + else if (c) itv->i2c_clients[i] = c; return itv->i2c_clients[i] ? 0 : -ENODEV; } diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index edef2a57961..15cac181212 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -712,6 +712,7 @@ static int ivtv_debug_ioctls(struct file *filp, unsigned int cmd, void *arg) int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg) { struct ivtv_open_id *id = NULL; + struct yuv_playback_info *yi = &itv->yuv_info; u32 data[CX2341X_MBOX_MAX_DATA]; int streamtype = 0; @@ -741,7 +742,8 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void memset(vcap, 0, sizeof(*vcap)); strcpy(vcap->driver, IVTV_DRIVER_NAME); /* driver name */ - strcpy(vcap->card, itv->card_name); /* card type */ + strncpy(vcap->card, itv->card_name, + sizeof(vcap->card)-1); /* card type */ strcpy(vcap->bus_info, pci_name(itv->dev)); /* bus info... */ vcap->version = IVTV_DRIVER_VERSION; /* version */ vcap->capabilities = itv->v4l2_cap; /* capabilities */ @@ -827,8 +829,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void case VIDIOC_CROPCAP: { struct v4l2_cropcap *cropcap = arg; - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) return -EINVAL; cropcap->bounds.top = cropcap->bounds.left = 0; cropcap->bounds.width = 720; @@ -837,8 +838,14 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void cropcap->pixelaspect.numerator = itv->is_50hz ? 59 : 10; cropcap->pixelaspect.denominator = itv->is_50hz ? 54 : 11; } else if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { - cropcap->bounds.width = itv->yuv_info.osd_full_w; - cropcap->bounds.height = itv->yuv_info.osd_full_h; + if (yi->track_osd) { + cropcap->bounds.width = yi->osd_full_w; + cropcap->bounds.height = yi->osd_full_h; + } else { + cropcap->bounds.width = 720; + cropcap->bounds.height = + itv->is_out_50hz ? 576 : 480; + } cropcap->pixelaspect.numerator = itv->is_out_50hz ? 59 : 10; cropcap->pixelaspect.denominator = itv->is_out_50hz ? 54 : 11; } else { @@ -856,7 +863,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { - itv->yuv_info.main_rect = crop->c; + yi->main_rect = crop->c; return 0; } else { if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, @@ -867,9 +874,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void } return -EINVAL; } - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - return itv->video_dec_func(itv, VIDIOC_S_CROP, arg); + return -EINVAL; } case VIDIOC_G_CROP: { @@ -878,14 +883,12 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) - crop->c = itv->yuv_info.main_rect; + crop->c = yi->main_rect; else crop->c = itv->main_rect; return 0; } - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - return itv->video_dec_func(itv, VIDIOC_G_CROP, arg); + return -EINVAL; } case VIDIOC_ENUM_FMT: { @@ -1070,11 +1073,10 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void itv->main_rect.height = itv->params.height; ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, 720, itv->main_rect.height, 0, 0); - itv->yuv_info.main_rect = itv->main_rect; + yi->main_rect = itv->main_rect; if (!itv->osd_info) { - itv->yuv_info.osd_full_w = 720; - itv->yuv_info.osd_full_h = - itv->is_out_50hz ? 576 : 480; + yi->osd_full_w = 720; + yi->osd_full_h = itv->is_out_50hz ? 576 : 480; } } break; @@ -1272,6 +1274,8 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void else fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; } + if (yi->track_osd) + fb->flags |= V4L2_FBUF_FLAG_OVERLAY; break; } @@ -1285,6 +1289,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void (fb->flags & (V4L2_FBUF_FLAG_LOCAL_ALPHA|V4L2_FBUF_FLAG_LOCAL_INV_ALPHA)) != 0; itv->osd_chroma_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0; ivtv_set_osd_alpha(itv); + yi->track_osd = (fb->flags & V4L2_FBUF_FLAG_OVERLAY) != 0; break; } @@ -1628,6 +1633,7 @@ static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp, if (ivtv_debug & IVTV_DBGFLG_IOCTL) { printk(KERN_INFO "ivtv%d ioctl: ", itv->num); v4l_printk_ioctl(cmd); + printk("\n"); } return ivtv_debug_ioctls(filp, cmd, arg); @@ -1671,6 +1677,7 @@ static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp, if (ivtv_debug & IVTV_DBGFLG_IOCTL) { printk(KERN_INFO "ivtv%d ioctl: ", itv->num); v4l_printk_ioctl(cmd); + printk("\n"); } return ivtv_v4l2_ioctls(itv, filp, cmd, arg); @@ -1684,6 +1691,7 @@ static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp, if (ivtv_debug & IVTV_DBGFLG_IOCTL) { printk(KERN_INFO "ivtv%d ioctl: ", itv->num); v4l_printk_ioctl(cmd); + printk("\n"); } return ivtv_control_ioctls(itv, cmd, arg); diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c index 65604dde972..a329c4689db 100644 --- a/drivers/media/video/ivtv/ivtv-irq.c +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -384,6 +384,8 @@ static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s) ivtv_stream_sync_for_device(s); write_reg(s->sg_handle, IVTV_REG_ENCDMAADDR); write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER); + itv->dma_timer.expires = jiffies + msecs_to_jiffies(100); + add_timer(&itv->dma_timer); } static void ivtv_dma_dec_start_xfer(struct ivtv_stream *s) @@ -398,6 +400,8 @@ static void ivtv_dma_dec_start_xfer(struct ivtv_stream *s) ivtv_stream_sync_for_device(s); write_reg(s->sg_handle, IVTV_REG_DECDMAADDR); write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); + itv->dma_timer.expires = jiffies + msecs_to_jiffies(100); + add_timer(&itv->dma_timer); } /* start the encoder DMA */ @@ -459,8 +463,6 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s) ivtv_dma_enc_start_xfer(s); set_bit(IVTV_F_I_DMA, &itv->i_flags); itv->cur_dma_stream = s->type; - itv->dma_timer.expires = jiffies + msecs_to_jiffies(100); - add_timer(&itv->dma_timer); } } @@ -481,8 +483,6 @@ static void ivtv_dma_dec_start(struct ivtv_stream *s) ivtv_dma_dec_start_xfer(s); set_bit(IVTV_F_I_DMA, &itv->i_flags); itv->cur_dma_stream = s->type; - itv->dma_timer.expires = jiffies + msecs_to_jiffies(100); - add_timer(&itv->dma_timer); } static void ivtv_irq_dma_read(struct ivtv *itv) @@ -492,10 +492,11 @@ static void ivtv_irq_dma_read(struct ivtv *itv) int hw_stream_type = 0; IVTV_DEBUG_HI_IRQ("DEC DMA READ\n"); - if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && itv->cur_dma_stream < 0) { - del_timer(&itv->dma_timer); + + del_timer(&itv->dma_timer); + + if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && itv->cur_dma_stream < 0) return; - } if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { s = &itv->streams[itv->cur_dma_stream]; @@ -543,7 +544,6 @@ static void ivtv_irq_dma_read(struct ivtv *itv) } wake_up(&s->waitq); } - del_timer(&itv->dma_timer); clear_bit(IVTV_F_I_UDMA, &itv->i_flags); clear_bit(IVTV_F_I_DMA, &itv->i_flags); itv->cur_dma_stream = -1; @@ -557,10 +557,12 @@ static void ivtv_irq_enc_dma_complete(struct ivtv *itv) ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data); IVTV_DEBUG_HI_IRQ("ENC DMA COMPLETE %x %d (%d)\n", data[0], data[1], itv->cur_dma_stream); - if (itv->cur_dma_stream < 0) { - del_timer(&itv->dma_timer); + + del_timer(&itv->dma_timer); + + if (itv->cur_dma_stream < 0) return; - } + s = &itv->streams[itv->cur_dma_stream]; ivtv_stream_sync_for_cpu(s); @@ -585,7 +587,6 @@ static void ivtv_irq_enc_dma_complete(struct ivtv *itv) ivtv_dma_enc_start_xfer(s); return; } - del_timer(&itv->dma_timer); clear_bit(IVTV_F_I_DMA, &itv->i_flags); itv->cur_dma_stream = -1; dma_post(s); diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c index 13a6c374d2d..1b5c0ac09a8 100644 --- a/drivers/media/video/ivtv/ivtv-mailbox.c +++ b/drivers/media/video/ivtv/ivtv-mailbox.c @@ -177,7 +177,8 @@ static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int f /* Sleep before a retry, if not atomic */ if (!(flags & API_NO_WAIT_MB)) { - if (jiffies - then > msecs_to_jiffies(10*retries)) + if (time_after(jiffies, + then + msecs_to_jiffies(10*retries))) break; ivtv_msleep_timeout(10, 0); } @@ -244,7 +245,9 @@ static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) data, then just return 0 as there is no need to issue this command again. Just an optimization to prevent unnecessary use of mailboxes. */ if (itv->api_cache[cmd].last_jiffies && - jiffies - itv->api_cache[cmd].last_jiffies < msecs_to_jiffies(1800000) && + time_before(jiffies, + itv->api_cache[cmd].last_jiffies + + msecs_to_jiffies(1800000)) && !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) { itv->api_cache[cmd].last_jiffies = jiffies; return 0; @@ -299,7 +302,7 @@ static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) } } while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) { - if (jiffies - then > api_timeout) { + if (time_after(jiffies, then + api_timeout)) { IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name); /* reset the mailbox, but it is likely too late already */ write_sync(0, &mbox->flags); @@ -311,7 +314,7 @@ static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) else ivtv_msleep_timeout(1, 0); } - if (jiffies - then > msecs_to_jiffies(100)) + if (time_after(jiffies, then + msecs_to_jiffies(100))) IVTV_DEBUG_WARN("%s took %u jiffies\n", api_info[cmd].name, jiffies_to_msecs(jiffies - then)); diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c index 39a21671324..3e1deec67a5 100644 --- a/drivers/media/video/ivtv/ivtv-queue.c +++ b/drivers/media/video/ivtv/ivtv-queue.c @@ -51,7 +51,7 @@ void ivtv_queue_init(struct ivtv_queue *q) void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q) { - unsigned long flags = 0; + unsigned long flags; /* clear the buffer if it is going to be enqueued to the free queue */ if (q == &s->q_free) { @@ -71,7 +71,7 @@ void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_qu struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q) { struct ivtv_buffer *buf = NULL; - unsigned long flags = 0; + unsigned long flags; spin_lock_irqsave(&s->qlock, flags); if (!list_empty(&q->list)) { diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 24d98ecf35a..4ab8d36831b 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -768,7 +768,8 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) /* wait 2s for EOS interrupt */ while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) && - jiffies < then + msecs_to_jiffies (2000)) { + time_before(jiffies, + then + msecs_to_jiffies(2000))) { schedule_timeout(msecs_to_jiffies(10)); } diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c index 85183480a22..393d917cd67 100644 --- a/drivers/media/video/ivtv/ivtv-yuv.c +++ b/drivers/media/video/ivtv/ivtv-yuv.c @@ -718,9 +718,11 @@ static u32 ivtv_yuv_window_setup(struct ivtv *itv, struct yuv_frame_info *f) f->src_w -= (osd_scale * osd_crop) >> 16; } - /* The OSD can be moved. Track to it */ - f->dst_x += itv->yuv_info.osd_x_offset; - f->dst_y += itv->yuv_info.osd_y_offset; + if (itv->yuv_info.track_osd) { + /* The OSD can be moved. Track to it */ + f->dst_x += itv->yuv_info.osd_x_offset; + f->dst_y += itv->yuv_info.osd_y_offset; + } /* Width & height for both src & dst must be even. Same for coordinates. */ @@ -792,11 +794,19 @@ void ivtv_yuv_work_handler(struct ivtv *itv) IVTV_DEBUG_YUV("Update yuv registers for frame %d\n", frame); f = yi->new_frame_info[frame]; - /* Update the osd pan info */ - f.pan_x = yi->osd_x_pan; - f.pan_y = yi->osd_y_pan; - f.vis_w = yi->osd_vis_w; - f.vis_h = yi->osd_vis_h; + if (yi->track_osd) { + /* Snapshot the osd pan info */ + f.pan_x = yi->osd_x_pan; + f.pan_y = yi->osd_y_pan; + f.vis_w = yi->osd_vis_w; + f.vis_h = yi->osd_vis_h; + } else { + /* Not tracking the osd, so assume full screen */ + f.pan_x = 0; + f.pan_y = 0; + f.vis_w = 720; + f.vis_h = yi->decode_height; + } /* Calculate the display window coordinates. Exit if nothing left */ if (!(yuv_update = ivtv_yuv_window_setup(itv, &f))) @@ -914,7 +924,7 @@ static void ivtv_yuv_init(struct ivtv *itv) } /* Get next available yuv buffer on PVR350 */ -void ivtv_yuv_next_free(struct ivtv *itv) +static void ivtv_yuv_next_free(struct ivtv *itv) { int draw, display; struct yuv_playback_info *yi = &itv->yuv_info; @@ -937,7 +947,7 @@ void ivtv_yuv_next_free(struct ivtv *itv) } /* Set up frame according to ivtv_dma_frame parameters */ -void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args) +static void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args) { struct yuv_playback_info *yi = &itv->yuv_info; u8 frame = yi->draw_frame; @@ -965,12 +975,6 @@ void ivtv_yuv_setup_frame(struct ivtv *itv, struct ivtv_dma_frame *args) /* Are we going to offset the Y plane */ nf->offset_y = (nf->tru_h + nf->src_x < 512 - 16) ? 1 : 0; - /* Snapshot the osd pan info */ - nf->pan_x = yi->osd_x_pan; - nf->pan_y = yi->osd_y_pan; - nf->vis_w = yi->osd_vis_w; - nf->vis_h = yi->osd_vis_h; - nf->update = 0; nf->interlaced_y = 0; nf->interlaced_uv = 0; @@ -1042,7 +1046,7 @@ void ivtv_yuv_frame_complete(struct ivtv *itv) (itv->yuv_info.draw_frame + 1) % IVTV_YUV_BUFFERS); } -int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args) +static int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args) { DEFINE_WAIT(wait); int rc = 0; diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 3d51fa0a52b..e7ccbc895d7 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -42,15 +42,10 @@ #include <linux/meye.h> MODULE_AUTHOR("Stelian Pop <stelian@popies.net>"); -MODULE_DESCRIPTION("v4l/v4l2 driver for the MotionEye camera"); +MODULE_DESCRIPTION("v4l2 driver for the MotionEye camera"); MODULE_LICENSE("GPL"); MODULE_VERSION(MEYE_DRIVER_VERSION); -/* force usage of V4L1 API */ -static int forcev4l1; /* = 0 */ -module_param(forcev4l1, int, 0644); -MODULE_PARM_DESC(forcev4l1, "force use of V4L1 instead of V4L2"); - /* number of grab buffers */ static unsigned int gbuffers = 2; module_param(gbuffers, int, 0444); @@ -789,7 +784,7 @@ static irqreturn_t meye_irq(int irq, void *dev_id) { u32 v; int reqnr; - static int sequence = 0; + static int sequence; v = mchip_read(MCHIP_MM_INTA); @@ -876,795 +871,735 @@ static int meye_release(struct inode *inode, struct file *file) return 0; } -static int meye_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int meyeioc_g_params(struct meye_params *p) { - switch (cmd) { + *p = meye.params; + return 0; +} - case VIDIOCGCAP: { - struct video_capability *b = arg; - strcpy(b->name,meye.video_dev->name); - b->type = VID_TYPE_CAPTURE; - b->channels = 1; - b->audios = 0; - b->maxwidth = 640; - b->maxheight = 480; - b->minwidth = 320; - b->minheight = 240; - break; - } +static int meyeioc_s_params(struct meye_params *jp) +{ + if (jp->subsample > 1) + return -EINVAL; - case VIDIOCGCHAN: { - struct video_channel *v = arg; - v->flags = 0; - v->tuners = 0; - v->type = VIDEO_TYPE_CAMERA; - if (v->channel != 0) - return -EINVAL; - strcpy(v->name,"Camera"); - break; - } + if (jp->quality > 10) + return -EINVAL; - case VIDIOCSCHAN: { - struct video_channel *v = arg; - if (v->channel != 0) - return -EINVAL; - break; - } + if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63) + return -EINVAL; - case VIDIOCGPICT: { - struct video_picture *p = arg; - *p = meye.picture; - break; - } + if (jp->framerate > 31) + return -EINVAL; - case VIDIOCSPICT: { - struct video_picture *p = arg; - if (p->depth != 16) - return -EINVAL; - if (p->palette != VIDEO_PALETTE_YUV422 && p->palette != VIDEO_PALETTE_YUYV) - return -EINVAL; - mutex_lock(&meye.lock); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, - p->brightness >> 10); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, - p->hue >> 10); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, - p->colour >> 10); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, - p->contrast >> 10); - meye.picture = *p; - mutex_unlock(&meye.lock); - break; - } + mutex_lock(&meye.lock); - case VIDIOCSYNC: { - int *i = arg; - int unused; + if (meye.params.subsample != jp->subsample || + meye.params.quality != jp->quality) + mchip_hic_stop(); /* need restart */ + + meye.params = *jp; + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, + meye.params.sharpness); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, + meye.params.agc); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, + meye.params.picture); + mutex_unlock(&meye.lock); - if (*i < 0 || *i >= gbuffers) - return -EINVAL; + return 0; +} - mutex_lock(&meye.lock); +static int meyeioc_qbuf_capt(int *nb) +{ + if (!meye.grab_fbuffer) + return -EINVAL; - switch (meye.grab_buffer[*i].state) { + if (*nb >= gbuffers) + return -EINVAL; - case MEYE_BUF_UNUSED: - mutex_unlock(&meye.lock); - return -EINVAL; - case MEYE_BUF_USING: - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&meye.lock); - return -EAGAIN; - } - if (wait_event_interruptible(meye.proc_list, - (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { - mutex_unlock(&meye.lock); - return -EINTR; - } - /* fall through */ - case MEYE_BUF_DONE: - meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; - kfifo_get(meye.doneq, (unsigned char *)&unused, sizeof(int)); - } - mutex_unlock(&meye.lock); - break; + if (*nb < 0) { + /* stop capture */ + mchip_hic_stop(); + return 0; } - case VIDIOCMCAPTURE: { - struct video_mmap *vm = arg; - int restart = 0; - - if (vm->frame >= gbuffers || vm->frame < 0) - return -EINVAL; - if (vm->format != VIDEO_PALETTE_YUV422 && vm->format != VIDEO_PALETTE_YUYV) - return -EINVAL; - if (vm->height * vm->width * 2 > gbufsize) - return -EINVAL; - if (!meye.grab_fbuffer) - return -EINVAL; - if (meye.grab_buffer[vm->frame].state != MEYE_BUF_UNUSED) - return -EBUSY; - - mutex_lock(&meye.lock); - if (vm->width == 640 && vm->height == 480) { - if (meye.params.subsample) { - meye.params.subsample = 0; - restart = 1; - } - } else if (vm->width == 320 && vm->height == 240) { - if (!meye.params.subsample) { - meye.params.subsample = 1; - restart = 1; - } - } else { - mutex_unlock(&meye.lock); - return -EINVAL; - } + if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED) + return -EBUSY; - if (restart || meye.mchip_mode != MCHIP_HIC_MODE_CONT_OUT) - mchip_continuous_start(); - meye.grab_buffer[vm->frame].state = MEYE_BUF_USING; - kfifo_put(meye.grabq, (unsigned char *)&vm->frame, sizeof(int)); - mutex_unlock(&meye.lock); - break; - } + mutex_lock(&meye.lock); - case VIDIOCGMBUF: { - struct video_mbuf *vm = arg; - int i; + if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) + mchip_cont_compression_start(); - memset(vm, 0 , sizeof(*vm)); - vm->size = gbufsize * gbuffers; - vm->frames = gbuffers; - for (i = 0; i < gbuffers; i++) - vm->offsets[i] = i * gbufsize; - break; - } + meye.grab_buffer[*nb].state = MEYE_BUF_USING; + kfifo_put(meye.grabq, (unsigned char *)nb, sizeof(int)); + mutex_unlock(&meye.lock); - case MEYEIOC_G_PARAMS: { - struct meye_params *p = arg; - *p = meye.params; - break; - } + return 0; +} - case MEYEIOC_S_PARAMS: { - struct meye_params *jp = arg; - if (jp->subsample > 1) - return -EINVAL; - if (jp->quality > 10) - return -EINVAL; - if (jp->sharpness > 63 || jp->agc > 63 || jp->picture > 63) - return -EINVAL; - if (jp->framerate > 31) - return -EINVAL; - mutex_lock(&meye.lock); - if (meye.params.subsample != jp->subsample || - meye.params.quality != jp->quality) - mchip_hic_stop(); /* need restart */ - meye.params = *jp; - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, - meye.params.sharpness); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, - meye.params.agc); - sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, - meye.params.picture); - mutex_unlock(&meye.lock); - break; - } +static int meyeioc_sync(struct file *file, void *fh, int *i) +{ + int unused; - case MEYEIOC_QBUF_CAPT: { - int *nb = arg; - - if (!meye.grab_fbuffer) - return -EINVAL; - if (*nb >= gbuffers) - return -EINVAL; - if (*nb < 0) { - /* stop capture */ - mchip_hic_stop(); - return 0; - } - if (meye.grab_buffer[*nb].state != MEYE_BUF_UNUSED) - return -EBUSY; - mutex_lock(&meye.lock); - if (meye.mchip_mode != MCHIP_HIC_MODE_CONT_COMP) - mchip_cont_compression_start(); - meye.grab_buffer[*nb].state = MEYE_BUF_USING; - kfifo_put(meye.grabq, (unsigned char *)nb, sizeof(int)); + if (*i < 0 || *i >= gbuffers) + return -EINVAL; + + mutex_lock(&meye.lock); + switch (meye.grab_buffer[*i].state) { + + case MEYE_BUF_UNUSED: mutex_unlock(&meye.lock); - break; + return -EINVAL; + case MEYE_BUF_USING: + if (file->f_flags & O_NONBLOCK) { + mutex_unlock(&meye.lock); + return -EAGAIN; + } + if (wait_event_interruptible(meye.proc_list, + (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { + mutex_unlock(&meye.lock); + return -EINTR; + } + /* fall through */ + case MEYE_BUF_DONE: + meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; + kfifo_get(meye.doneq, (unsigned char *)&unused, sizeof(int)); } + *i = meye.grab_buffer[*i].size; + mutex_unlock(&meye.lock); + return 0; +} - case MEYEIOC_SYNC: { - int *i = arg; - int unused; +static int meyeioc_stillcapt(void) +{ + if (!meye.grab_fbuffer) + return -EINVAL; - if (*i < 0 || *i >= gbuffers) - return -EINVAL; + if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) + return -EBUSY; - mutex_lock(&meye.lock); - switch (meye.grab_buffer[*i].state) { + mutex_lock(&meye.lock); + meye.grab_buffer[0].state = MEYE_BUF_USING; + mchip_take_picture(); - case MEYE_BUF_UNUSED: - mutex_unlock(&meye.lock); - return -EINVAL; - case MEYE_BUF_USING: - if (file->f_flags & O_NONBLOCK) { - mutex_unlock(&meye.lock); - return -EAGAIN; - } - if (wait_event_interruptible(meye.proc_list, - (meye.grab_buffer[*i].state != MEYE_BUF_USING))) { - mutex_unlock(&meye.lock); - return -EINTR; - } - /* fall through */ - case MEYE_BUF_DONE: - meye.grab_buffer[*i].state = MEYE_BUF_UNUSED; - kfifo_get(meye.doneq, (unsigned char *)&unused, sizeof(int)); - } - *i = meye.grab_buffer[*i].size; - mutex_unlock(&meye.lock); - break; - } + mchip_get_picture(meye.grab_fbuffer, + mchip_hsize() * mchip_vsize() * 2); - case MEYEIOC_STILLCAPT: { + meye.grab_buffer[0].state = MEYE_BUF_DONE; + mutex_unlock(&meye.lock); - if (!meye.grab_fbuffer) - return -EINVAL; - if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) - return -EBUSY; - mutex_lock(&meye.lock); - meye.grab_buffer[0].state = MEYE_BUF_USING; + return 0; +} + +static int meyeioc_stilljcapt(int *len) +{ + if (!meye.grab_fbuffer) + return -EINVAL; + + if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) + return -EBUSY; + + mutex_lock(&meye.lock); + meye.grab_buffer[0].state = MEYE_BUF_USING; + *len = -1; + + while (*len == -1) { mchip_take_picture(); - mchip_get_picture( - meye.grab_fbuffer, - mchip_hsize() * mchip_vsize() * 2); - meye.grab_buffer[0].state = MEYE_BUF_DONE; - mutex_unlock(&meye.lock); - break; + *len = mchip_compress_frame(meye.grab_fbuffer, gbufsize); } - case MEYEIOC_STILLJCAPT: { - int *len = arg; - - if (!meye.grab_fbuffer) - return -EINVAL; - if (meye.grab_buffer[0].state != MEYE_BUF_UNUSED) - return -EBUSY; - mutex_lock(&meye.lock); - meye.grab_buffer[0].state = MEYE_BUF_USING; - *len = -1; - while (*len == -1) { - mchip_take_picture(); - *len = mchip_compress_frame(meye.grab_fbuffer, gbufsize); - } - meye.grab_buffer[0].state = MEYE_BUF_DONE; - mutex_unlock(&meye.lock); - break; - } + meye.grab_buffer[0].state = MEYE_BUF_DONE; + mutex_unlock(&meye.lock); + return 0; +} - case VIDIOC_QUERYCAP: { - struct v4l2_capability *cap = arg; +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, "meye"); + strcpy(cap->card, "meye"); + sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev)); - if (forcev4l1) - return -EINVAL; + cap->version = (MEYE_DRIVER_MAJORVERSION << 8) + + MEYE_DRIVER_MINORVERSION; - memset(cap, 0, sizeof(*cap)); - strcpy(cap->driver, "meye"); - strcpy(cap->card, "meye"); - sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev)); - cap->version = (MEYE_DRIVER_MAJORVERSION << 8) + - MEYE_DRIVER_MINORVERSION; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING; - break; - } + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING; + + return 0; +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; - case VIDIOC_ENUMINPUT: { - struct v4l2_input *i = arg; + memset(i, 0, sizeof(*i)); + i->index = 0; + strcpy(i->name, "Camera"); + i->type = V4L2_INPUT_TYPE_CAMERA; - if (i->index != 0) - return -EINVAL; - memset(i, 0, sizeof(*i)); - i->index = 0; - strcpy(i->name, "Camera"); - i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int i) +{ + if (i != 0) + return -EINVAL; + + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *c) +{ + switch (c->id) { + + case V4L2_CID_BRIGHTNESS: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Brightness"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_HUE: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Hue"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_CONTRAST: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Contrast"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_SATURATION: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Saturation"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + c->flags = 0; + break; + case V4L2_CID_AGC: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Agc"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 48; + c->flags = 0; break; + case V4L2_CID_MEYE_SHARPNESS: + case V4L2_CID_SHARPNESS: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Sharpness"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 32; + + /* Continue to report legacy private SHARPNESS ctrl but + * say it is disabled in preference to ctrl in the spec + */ + c->flags = (c->id == V4L2_CID_SHARPNESS) ? 0 : + V4L2_CTRL_FLAG_DISABLED; + break; + case V4L2_CID_PICTURE: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Picture"); + c->minimum = 0; + c->maximum = 63; + c->step = 1; + c->default_value = 0; + c->flags = 0; + break; + case V4L2_CID_JPEGQUAL: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "JPEG quality"); + c->minimum = 0; + c->maximum = 10; + c->step = 1; + c->default_value = 8; + c->flags = 0; + break; + case V4L2_CID_FRAMERATE: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Framerate"); + c->minimum = 0; + c->maximum = 31; + c->step = 1; + c->default_value = 0; + c->flags = 0; + break; + default: + return -EINVAL; } - case VIDIOC_G_INPUT: { - int *i = arg; + return 0; +} - *i = 0; +static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) +{ + mutex_lock(&meye.lock); + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); + meye.picture.brightness = c->value << 10; + break; + case V4L2_CID_HUE: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAHUE, c->value); + meye.picture.hue = c->value << 10; + break; + case V4L2_CID_CONTRAST: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); + meye.picture.contrast = c->value << 10; + break; + case V4L2_CID_SATURATION: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); + meye.picture.colour = c->value << 10; + break; + case V4L2_CID_AGC: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAAGC, c->value); + meye.params.agc = c->value; + break; + case V4L2_CID_SHARPNESS: + case V4L2_CID_MEYE_SHARPNESS: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value); + meye.params.sharpness = c->value; + break; + case V4L2_CID_PICTURE: + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value); + meye.params.picture = c->value; + break; + case V4L2_CID_JPEGQUAL: + meye.params.quality = c->value; + break; + case V4L2_CID_FRAMERATE: + meye.params.framerate = c->value; break; + default: + mutex_unlock(&meye.lock); + return -EINVAL; } + mutex_unlock(&meye.lock); - case VIDIOC_S_INPUT: { - int *i = arg; + return 0; +} - if (*i != 0) - return -EINVAL; +static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) +{ + mutex_lock(&meye.lock); + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = meye.picture.brightness >> 10; + break; + case V4L2_CID_HUE: + c->value = meye.picture.hue >> 10; + break; + case V4L2_CID_CONTRAST: + c->value = meye.picture.contrast >> 10; + break; + case V4L2_CID_SATURATION: + c->value = meye.picture.colour >> 10; + break; + case V4L2_CID_AGC: + c->value = meye.params.agc; + break; + case V4L2_CID_SHARPNESS: + case V4L2_CID_MEYE_SHARPNESS: + c->value = meye.params.sharpness; break; + case V4L2_CID_PICTURE: + c->value = meye.params.picture; + break; + case V4L2_CID_JPEGQUAL: + c->value = meye.params.quality; + break; + case V4L2_CID_FRAMERATE: + c->value = meye.params.framerate; + break; + default: + mutex_unlock(&meye.lock); + return -EINVAL; } + mutex_unlock(&meye.lock); - case VIDIOC_QUERYCTRL: { - struct v4l2_queryctrl *c = arg; + return 0; +} - switch (c->id) { +static int vidioc_enum_fmt_cap(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (f->index > 1) + return -EINVAL; - case V4L2_CID_BRIGHTNESS: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Brightness"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_HUE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Hue"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_CONTRAST: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Contrast"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_SATURATION: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Saturation"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_AGC: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Agc"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 48; - c->flags = 0; - break; - case V4L2_CID_SHARPNESS: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Sharpness"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 32; - c->flags = 0; - break; - case V4L2_CID_PICTURE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Picture"); - c->minimum = 0; - c->maximum = 63; - c->step = 1; - c->default_value = 0; - c->flags = 0; - break; - case V4L2_CID_JPEGQUAL: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "JPEG quality"); - c->minimum = 0; - c->maximum = 10; - c->step = 1; - c->default_value = 8; - c->flags = 0; - break; - case V4L2_CID_FRAMERATE: - c->type = V4L2_CTRL_TYPE_INTEGER; - strcpy(c->name, "Framerate"); - c->minimum = 0; - c->maximum = 31; - c->step = 1; - c->default_value = 0; - c->flags = 0; - break; - default: - return -EINVAL; - } - break; + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (f->index == 0) { + /* standard YUV 422 capture */ + memset(f, 0, sizeof(*f)); + f->index = 0; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = 0; + strcpy(f->description, "YUV422"); + f->pixelformat = V4L2_PIX_FMT_YUYV; + } else { + /* compressed MJPEG capture */ + memset(f, 0, sizeof(*f)); + f->index = 1; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strcpy(f->description, "MJPEG"); + f->pixelformat = V4L2_PIX_FMT_MJPEG; } - case VIDIOC_S_CTRL: { - struct v4l2_control *c = arg; + return 0; +} - mutex_lock(&meye.lock); - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); - meye.picture.brightness = c->value << 10; - break; - case V4L2_CID_HUE: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAHUE, c->value); - meye.picture.hue = c->value << 10; - break; - case V4L2_CID_CONTRAST: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); - meye.picture.contrast = c->value << 10; - break; - case V4L2_CID_SATURATION: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); - meye.picture.colour = c->value << 10; - break; - case V4L2_CID_AGC: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAAGC, c->value); - meye.params.agc = c->value; - break; - case V4L2_CID_SHARPNESS: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value); - meye.params.sharpness = c->value; - break; - case V4L2_CID_PICTURE: - sony_pic_camera_command( - SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value); - meye.params.picture = c->value; - break; - case V4L2_CID_JPEGQUAL: - meye.params.quality = c->value; - break; - case V4L2_CID_FRAMERATE: - meye.params.framerate = c->value; - break; - default: - mutex_unlock(&meye.lock); - return -EINVAL; - } - mutex_unlock(&meye.lock); - break; +static int vidioc_try_fmt_cap(struct file *file, void *fh, + struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + + if (f->fmt.pix.width <= 320) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } else { + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; } - case VIDIOC_G_CTRL: { - struct v4l2_control *c = arg; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; - mutex_lock(&meye.lock); - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = meye.picture.brightness >> 10; - break; - case V4L2_CID_HUE: - c->value = meye.picture.hue >> 10; - break; - case V4L2_CID_CONTRAST: - c->value = meye.picture.contrast >> 10; - break; - case V4L2_CID_SATURATION: - c->value = meye.picture.colour >> 10; - break; - case V4L2_CID_AGC: - c->value = meye.params.agc; - break; - case V4L2_CID_SHARPNESS: - c->value = meye.params.sharpness; - break; - case V4L2_CID_PICTURE: - c->value = meye.params.picture; - break; - case V4L2_CID_JPEGQUAL: - c->value = meye.params.quality; - break; - case V4L2_CID_FRAMERATE: - c->value = meye.params.framerate; - break; - default: - mutex_unlock(&meye.lock); - return -EINVAL; - } - mutex_unlock(&meye.lock); + return 0; +} + +static int vidioc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + switch (meye.mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + default: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + break; + case MCHIP_HIC_MODE_CONT_COMP: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; break; } - case VIDIOC_ENUM_FMT: { - struct v4l2_fmtdesc *f = arg; - - if (f->index > 1) - return -EINVAL; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->index == 0) { - /* standard YUV 422 capture */ - memset(f, 0, sizeof(*f)); - f->index = 0; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->flags = 0; - strcpy(f->description, "YUV422"); - f->pixelformat = V4L2_PIX_FMT_YUYV; - } else { - /* compressed MJPEG capture */ - memset(f, 0, sizeof(*f)); - f->index = 1; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->flags = V4L2_FMT_FLAG_COMPRESSED; - strcpy(f->description, "MJPEG"); - f->pixelformat = V4L2_PIX_FMT_MJPEG; - } - break; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = mchip_hsize(); + f->fmt.pix.height = mchip_vsize(); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + + return 0; +} + +static int vidioc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && + f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + + f->fmt.pix.field = V4L2_FIELD_NONE; + mutex_lock(&meye.lock); + + if (f->fmt.pix.width <= 320) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + meye.params.subsample = 1; + } else { + f->fmt.pix.width = 640; + f->fmt.pix.height = 480; + meye.params.subsample = 0; } - case VIDIOC_TRY_FMT: { - struct v4l2_format *f = arg; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && - f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - if (f->fmt.pix.field != V4L2_FIELD_ANY && - f->fmt.pix.field != V4L2_FIELD_NONE) - return -EINVAL; - f->fmt.pix.field = V4L2_FIELD_NONE; - if (f->fmt.pix.width <= 320) { - f->fmt.pix.width = 320; - f->fmt.pix.height = 240; - } else { - f->fmt.pix.width = 640; - f->fmt.pix.height = 480; - } - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; + switch (f->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_YUYV: + meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; + break; + case V4L2_PIX_FMT_MJPEG: + meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; break; } - case VIDIOC_G_FMT: { - struct v4l2_format *f = arg; + mutex_unlock(&meye.lock); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * + f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - switch (meye.mchip_mode) { - case MCHIP_HIC_MODE_CONT_OUT: - default: - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; - break; - case MCHIP_HIC_MODE_CONT_COMP: - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; - break; - } - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = mchip_hsize(); - f->fmt.pix.height = mchip_vsize(); - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; - break; - } + return 0; +} - case VIDIOC_S_FMT: { - struct v4l2_format *f = arg; - - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && - f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - if (f->fmt.pix.field != V4L2_FIELD_ANY && - f->fmt.pix.field != V4L2_FIELD_NONE) - return -EINVAL; - f->fmt.pix.field = V4L2_FIELD_NONE; - mutex_lock(&meye.lock); - if (f->fmt.pix.width <= 320) { - f->fmt.pix.width = 320; - f->fmt.pix.height = 240; - meye.params.subsample = 1; - } else { - f->fmt.pix.width = 640; - f->fmt.pix.height = 480; - meye.params.subsample = 0; - } - switch (f->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_YUYV: - meye.mchip_mode = MCHIP_HIC_MODE_CONT_OUT; - break; - case V4L2_PIX_FMT_MJPEG: - meye.mchip_mode = MCHIP_HIC_MODE_CONT_COMP; - break; - } - mutex_unlock(&meye.lock); - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *req) +{ + int i; - break; - } + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; - case VIDIOC_REQBUFS: { - struct v4l2_requestbuffers *req = arg; - int i; + if (req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; - if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (req->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - if (meye.grab_fbuffer && req->count == gbuffers) { - /* already allocated, no modifications */ - break; - } - mutex_lock(&meye.lock); - if (meye.grab_fbuffer) { - for (i = 0; i < gbuffers; i++) - if (meye.vma_use_count[i]) { - mutex_unlock(&meye.lock); - return -EINVAL; - } - rvfree(meye.grab_fbuffer, gbuffers * gbufsize); - meye.grab_fbuffer = NULL; - } - gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS)); - req->count = gbuffers; - meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize); - if (!meye.grab_fbuffer) { - printk(KERN_ERR "meye: v4l framebuffer allocation" - " failed\n"); - mutex_unlock(&meye.lock); - return -ENOMEM; - } - for (i = 0; i < gbuffers; i++) - meye.vma_use_count[i] = 0; - mutex_unlock(&meye.lock); - break; + if (meye.grab_fbuffer && req->count == gbuffers) { + /* already allocated, no modifications */ + return 0; } - case VIDIOC_QUERYBUF: { - struct v4l2_buffer *buf = arg; - int index = buf->index; - - if (index < 0 || index >= gbuffers) - return -EINVAL; - memset(buf, 0, sizeof(*buf)); - buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->index = index; - buf->bytesused = meye.grab_buffer[index].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; - if (meye.grab_buffer[index].state == MEYE_BUF_USING) - buf->flags |= V4L2_BUF_FLAG_QUEUED; - if (meye.grab_buffer[index].state == MEYE_BUF_DONE) - buf->flags |= V4L2_BUF_FLAG_DONE; - buf->field = V4L2_FIELD_NONE; - buf->timestamp = meye.grab_buffer[index].timestamp; - buf->sequence = meye.grab_buffer[index].sequence; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = index * gbufsize; - buf->length = gbufsize; - break; + mutex_lock(&meye.lock); + if (meye.grab_fbuffer) { + for (i = 0; i < gbuffers; i++) + if (meye.vma_use_count[i]) { + mutex_unlock(&meye.lock); + return -EINVAL; + } + rvfree(meye.grab_fbuffer, gbuffers * gbufsize); + meye.grab_fbuffer = NULL; } - case VIDIOC_QBUF: { - struct v4l2_buffer *buf = arg; - - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - if (buf->index < 0 || buf->index >= gbuffers) - return -EINVAL; - if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED) - return -EINVAL; - mutex_lock(&meye.lock); - buf->flags |= V4L2_BUF_FLAG_QUEUED; - buf->flags &= ~V4L2_BUF_FLAG_DONE; - meye.grab_buffer[buf->index].state = MEYE_BUF_USING; - kfifo_put(meye.grabq, (unsigned char *)&buf->index, sizeof(int)); + gbuffers = max(2, min((int)req->count, MEYE_MAX_BUFNBRS)); + req->count = gbuffers; + meye.grab_fbuffer = rvmalloc(gbuffers * gbufsize); + + if (!meye.grab_fbuffer) { + printk(KERN_ERR "meye: v4l framebuffer allocation" + " failed\n"); mutex_unlock(&meye.lock); - break; + return -ENOMEM; } - case VIDIOC_DQBUF: { - struct v4l2_buffer *buf = arg; - int reqnr; + for (i = 0; i < gbuffers; i++) + meye.vma_use_count[i] = 0; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; + mutex_unlock(&meye.lock); - mutex_lock(&meye.lock); - if (kfifo_len(meye.doneq) == 0 && file->f_flags & O_NONBLOCK) { - mutex_unlock(&meye.lock); - return -EAGAIN; - } - if (wait_event_interruptible(meye.proc_list, - kfifo_len(meye.doneq) != 0) < 0) { - mutex_unlock(&meye.lock); - return -EINTR; - } - if (!kfifo_get(meye.doneq, (unsigned char *)&reqnr, - sizeof(int))) { - mutex_unlock(&meye.lock); - return -EBUSY; - } - if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) { - mutex_unlock(&meye.lock); - return -EINVAL; - } - buf->index = reqnr; - buf->bytesused = meye.grab_buffer[reqnr].size; - buf->flags = V4L2_BUF_FLAG_MAPPED; - buf->field = V4L2_FIELD_NONE; - buf->timestamp = meye.grab_buffer[reqnr].timestamp; - buf->sequence = meye.grab_buffer[reqnr].sequence; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = reqnr * gbufsize; - buf->length = gbufsize; - meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED; + return 0; +} + +static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + int index = buf->index; + + if (index < 0 || index >= gbuffers) + return -EINVAL; + + memset(buf, 0, sizeof(*buf)); + + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->index = index; + buf->bytesused = meye.grab_buffer[index].size; + buf->flags = V4L2_BUF_FLAG_MAPPED; + + if (meye.grab_buffer[index].state == MEYE_BUF_USING) + buf->flags |= V4L2_BUF_FLAG_QUEUED; + + if (meye.grab_buffer[index].state == MEYE_BUF_DONE) + buf->flags |= V4L2_BUF_FLAG_DONE; + + buf->field = V4L2_FIELD_NONE; + buf->timestamp = meye.grab_buffer[index].timestamp; + buf->sequence = meye.grab_buffer[index].sequence; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = index * gbufsize; + buf->length = gbufsize; + + return 0; +} + +static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (buf->index < 0 || buf->index >= gbuffers) + return -EINVAL; + + if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED) + return -EINVAL; + + mutex_lock(&meye.lock); + buf->flags |= V4L2_BUF_FLAG_QUEUED; + buf->flags &= ~V4L2_BUF_FLAG_DONE; + meye.grab_buffer[buf->index].state = MEYE_BUF_USING; + kfifo_put(meye.grabq, (unsigned char *)&buf->index, sizeof(int)); + mutex_unlock(&meye.lock); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) +{ + int reqnr; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + mutex_lock(&meye.lock); + + if (kfifo_len(meye.doneq) == 0 && file->f_flags & O_NONBLOCK) { mutex_unlock(&meye.lock); - break; + return -EAGAIN; } - case VIDIOC_STREAMON: { - mutex_lock(&meye.lock); - switch (meye.mchip_mode) { - case MCHIP_HIC_MODE_CONT_OUT: - mchip_continuous_start(); - break; - case MCHIP_HIC_MODE_CONT_COMP: - mchip_cont_compression_start(); - break; - default: - mutex_unlock(&meye.lock); - return -EINVAL; - } + if (wait_event_interruptible(meye.proc_list, + kfifo_len(meye.doneq) != 0) < 0) { mutex_unlock(&meye.lock); - break; + return -EINTR; } - case VIDIOC_STREAMOFF: { - int i; + if (!kfifo_get(meye.doneq, (unsigned char *)&reqnr, + sizeof(int))) { + mutex_unlock(&meye.lock); + return -EBUSY; + } - mutex_lock(&meye.lock); - mchip_hic_stop(); - kfifo_reset(meye.grabq); - kfifo_reset(meye.doneq); - for (i = 0; i < MEYE_MAX_BUFNBRS; i++) - meye.grab_buffer[i].state = MEYE_BUF_UNUSED; + if (meye.grab_buffer[reqnr].state != MEYE_BUF_DONE) { mutex_unlock(&meye.lock); - break; + return -EINVAL; } - /* - * XXX what about private snapshot ioctls ? - * Do they need to be converted to V4L2 ? - */ + buf->index = reqnr; + buf->bytesused = meye.grab_buffer[reqnr].size; + buf->flags = V4L2_BUF_FLAG_MAPPED; + buf->field = V4L2_FIELD_NONE; + buf->timestamp = meye.grab_buffer[reqnr].timestamp; + buf->sequence = meye.grab_buffer[reqnr].sequence; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = reqnr * gbufsize; + buf->length = gbufsize; + meye.grab_buffer[reqnr].state = MEYE_BUF_UNUSED; + mutex_unlock(&meye.lock); + + return 0; +} +static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) +{ + mutex_lock(&meye.lock); + + switch (meye.mchip_mode) { + case MCHIP_HIC_MODE_CONT_OUT: + mchip_continuous_start(); + break; + case MCHIP_HIC_MODE_CONT_COMP: + mchip_cont_compression_start(); + break; default: - return -ENOIOCTLCMD; + mutex_unlock(&meye.lock); + return -EINVAL; } + mutex_unlock(&meye.lock); + return 0; } -static int meye_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) { - return video_usercopy(inode, file, cmd, arg, meye_do_ioctl); + mutex_lock(&meye.lock); + mchip_hic_stop(); + kfifo_reset(meye.grabq); + kfifo_reset(meye.doneq); + + for (i = 0; i < MEYE_MAX_BUFNBRS; i++) + meye.grab_buffer[i].state = MEYE_BUF_UNUSED; + + mutex_unlock(&meye.lock); + return 0; +} + +static int vidioc_default(struct file *file, void *fh, int cmd, void *arg) +{ + switch (cmd) { + case MEYEIOC_G_PARAMS: + return meyeioc_g_params((struct meye_params *) arg); + + case MEYEIOC_S_PARAMS: + return meyeioc_s_params((struct meye_params *) arg); + + case MEYEIOC_QBUF_CAPT: + return meyeioc_qbuf_capt((int *) arg); + + case MEYEIOC_SYNC: + return meyeioc_sync(file, fh, (int *) arg); + + case MEYEIOC_STILLCAPT: + return meyeioc_stillcapt(); + + case MEYEIOC_STILLJCAPT: + return meyeioc_stilljcapt((int *) arg); + + default: + return -EINVAL; + } + } static unsigned int meye_poll(struct file *file, poll_table *wait) @@ -1752,8 +1687,10 @@ static const struct file_operations meye_fops = { .open = meye_open, .release = meye_release, .mmap = meye_mmap, - .ioctl = meye_ioctl, + .ioctl = video_ioctl2, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .poll = meye_poll, .llseek = no_llseek, }; @@ -1765,6 +1702,24 @@ static struct video_device meye_template = { .fops = &meye_fops, .release = video_device_release, .minor = -1, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, + .vidioc_try_fmt_cap = vidioc_try_fmt_cap, + .vidioc_g_fmt_cap = vidioc_g_fmt_cap, + .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_default = vidioc_default, }; #ifdef CONFIG_PM diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 7a11f3159e3..b73c740f7fb 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -366,7 +366,7 @@ int msp_sleep(struct msp_state *state, int timeout) } /* ------------------------------------------------------------------------ */ -#ifdef CONFIG_VIDEO_V4L1 +#ifdef CONFIG_VIDEO_ALLOW_V4L1 static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode) { if (rxsubchans == V4L2_TUNER_SUB_MONO) @@ -514,7 +514,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ -#ifdef CONFIG_VIDEO_V4L1 +#ifdef CONFIG_VIDEO_ALLOW_V4L1 case VIDIOCGAUDIO: { struct video_audio *va = arg; diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c index 61ec794a737..7f556859279 100644 --- a/drivers/media/video/msp3400-kthreads.c +++ b/drivers/media/video/msp3400-kthreads.c @@ -833,11 +833,6 @@ static int msp34xxg_modus(struct i2c_client *client) v4l_dbg(1, msp_debug, client, "selected radio modus\n"); return 0x0001; } - - if (state->v4l2_std & V4L2_STD_PAL) { - v4l_dbg(1, msp_debug, client, "selected PAL modus\n"); - return 0x7001; - } if (state->v4l2_std == V4L2_STD_NTSC_M_JP) { v4l_dbg(1, msp_debug, client, "selected M (EIA-J) modus\n"); return 0x4001; @@ -846,15 +841,15 @@ static int msp34xxg_modus(struct i2c_client *client) v4l_dbg(1, msp_debug, client, "selected M (A2) modus\n"); return 0x0001; } + if (state->v4l2_std == V4L2_STD_SECAM_L) { + v4l_dbg(1, msp_debug, client, "selected SECAM-L modus\n"); + return 0x6001; + } if (state->v4l2_std & V4L2_STD_MN) { v4l_dbg(1, msp_debug, client, "selected M (BTSC) modus\n"); return 0x2001; } - if (state->v4l2_std & V4L2_STD_SECAM) { - v4l_dbg(1, msp_debug, client, "selected SECAM modus\n"); - return 0x6001; - } - return 0x0001; + return 0x7001; } static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) diff --git a/drivers/media/video/mt20xx.c b/drivers/media/video/mt20xx.c index 74fd6a01d4c..fbcb2823373 100644 --- a/drivers/media/video/mt20xx.c +++ b/drivers/media/video/mt20xx.c @@ -10,12 +10,10 @@ #include "tuner-i2c.h" #include "mt20xx.h" -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); -#define PREFIX "mt20xx" - /* ---------------------------------------------------------------------- */ static unsigned int optimize_vco = 1; @@ -24,7 +22,7 @@ module_param(optimize_vco, int, 0644); static unsigned int tv_antenna = 1; module_param(tv_antenna, int, 0644); -static unsigned int radio_antenna = 0; +static unsigned int radio_antenna; module_param(radio_antenna, int, 0644); /* ---------------------------------------------------------------------- */ @@ -611,6 +609,7 @@ struct dvb_frontend *microtune_attach(struct dvb_frontend *fe, priv->i2c_props.addr = i2c_addr; priv->i2c_props.adap = i2c_adap; + priv->i2c_props.name = "mt20xx"; //priv->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */ diff --git a/drivers/media/video/mt20xx.h b/drivers/media/video/mt20xx.h index 5e9c825d2e9..aa848e14ce5 100644 --- a/drivers/media/video/mt20xx.h +++ b/drivers/media/video/mt20xx.h @@ -29,7 +29,7 @@ static inline struct dvb_frontend *microtune_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap, u8 i2c_addr) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c new file mode 100644 index 00000000000..3fb5f63df1e --- /dev/null +++ b/drivers/media/video/mt9m001.c @@ -0,0 +1,722 @@ +/* + * Driver for MT9M001 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/videodev2.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/log2.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +#ifdef CONFIG_MT9M001_PCA9536_SWITCH +#include <asm/gpio.h> +#endif + +/* mt9m001 i2c address 0x5d + * The platform has to define i2c_board_info + * and call i2c_register_board_info() */ + +/* mt9m001 selected register addresses */ +#define MT9M001_CHIP_VERSION 0x00 +#define MT9M001_ROW_START 0x01 +#define MT9M001_COLUMN_START 0x02 +#define MT9M001_WINDOW_HEIGHT 0x03 +#define MT9M001_WINDOW_WIDTH 0x04 +#define MT9M001_HORIZONTAL_BLANKING 0x05 +#define MT9M001_VERTICAL_BLANKING 0x06 +#define MT9M001_OUTPUT_CONTROL 0x07 +#define MT9M001_SHUTTER_WIDTH 0x09 +#define MT9M001_FRAME_RESTART 0x0b +#define MT9M001_SHUTTER_DELAY 0x0c +#define MT9M001_RESET 0x0d +#define MT9M001_READ_OPTIONS1 0x1e +#define MT9M001_READ_OPTIONS2 0x20 +#define MT9M001_GLOBAL_GAIN 0x35 +#define MT9M001_CHIP_ENABLE 0xF1 + +static const struct soc_camera_data_format mt9m001_colour_formats[] = { + /* Order important: first natively supported, + * second supported with a GPIO extender */ + { + .name = "Bayer (sRGB) 10 bit", + .depth = 10, + .fourcc = V4L2_PIX_FMT_SBGGR16, + .colorspace = V4L2_COLORSPACE_SRGB, + }, { + .name = "Bayer (sRGB) 8 bit", + .depth = 8, + .fourcc = V4L2_PIX_FMT_SBGGR8, + .colorspace = V4L2_COLORSPACE_SRGB, + } +}; + +static const struct soc_camera_data_format mt9m001_monochrome_formats[] = { + /* Order important - see above */ + { + .name = "Monochrome 10 bit", + .depth = 10, + .fourcc = V4L2_PIX_FMT_Y16, + }, { + .name = "Monochrome 8 bit", + .depth = 8, + .fourcc = V4L2_PIX_FMT_GREY, + }, +}; + +struct mt9m001 { + struct i2c_client *client; + struct soc_camera_device icd; + int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ + int switch_gpio; + unsigned char autoexposure; + unsigned char datawidth; +}; + +static int reg_read(struct soc_camera_device *icd, const u8 reg) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = mt9m001->client; + s32 data = i2c_smbus_read_word_data(client, reg); + return data < 0 ? data : swab16(data); +} + +static int reg_write(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + return i2c_smbus_write_word_data(mt9m001->client, reg, swab16(data)); +} + +static int reg_set(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret | data); +} + +static int reg_clear(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret & ~data); +} + +static int mt9m001_init(struct soc_camera_device *icd) +{ + int ret; + + /* Disable chip, synchronous option update */ + dev_dbg(icd->vdev->dev, "%s\n", __func__); + + ret = reg_write(icd, MT9M001_RESET, 1); + if (ret >= 0) + ret = reg_write(icd, MT9M001_RESET, 0); + if (ret >= 0) + ret = reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); + + return ret >= 0 ? 0 : -EIO; +} + +static int mt9m001_release(struct soc_camera_device *icd) +{ + /* Disable the chip */ + reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); + return 0; +} + +static int mt9m001_start_capture(struct soc_camera_device *icd) +{ + /* Switch to master "normal" mode */ + if (reg_write(icd, MT9M001_OUTPUT_CONTROL, 2) < 0) + return -EIO; + return 0; +} + +static int mt9m001_stop_capture(struct soc_camera_device *icd) +{ + /* Stop sensor readout */ + if (reg_write(icd, MT9M001_OUTPUT_CONTROL, 0) < 0) + return -EIO; + return 0; +} + +static int bus_switch_request(struct mt9m001 *mt9m001, + struct soc_camera_link *icl) +{ +#ifdef CONFIG_MT9M001_PCA9536_SWITCH + int ret; + unsigned int gpio = icl->gpio; + + if (gpio_is_valid(gpio)) { + /* We have a data bus switch. */ + ret = gpio_request(gpio, "mt9m001"); + if (ret < 0) { + dev_err(&mt9m001->client->dev, "Cannot get GPIO %u\n", + gpio); + return ret; + } + + ret = gpio_direction_output(gpio, 0); + if (ret < 0) { + dev_err(&mt9m001->client->dev, + "Cannot set GPIO %u to output\n", gpio); + gpio_free(gpio); + return ret; + } + } + + mt9m001->switch_gpio = gpio; +#else + mt9m001->switch_gpio = -EINVAL; +#endif + return 0; +} + +static void bus_switch_release(struct mt9m001 *mt9m001) +{ +#ifdef CONFIG_MT9M001_PCA9536_SWITCH + if (gpio_is_valid(mt9m001->switch_gpio)) + gpio_free(mt9m001->switch_gpio); +#endif +} + +static int bus_switch_act(struct mt9m001 *mt9m001, int go8bit) +{ +#ifdef CONFIG_MT9M001_PCA9536_SWITCH + if (!gpio_is_valid(mt9m001->switch_gpio)) + return -ENODEV; + + gpio_set_value_cansleep(mt9m001->switch_gpio, go8bit); + return 0; +#else + return -ENODEV; +#endif +} + +static int bus_switch_possible(struct mt9m001 *mt9m001) +{ +#ifdef CONFIG_MT9M001_PCA9536_SWITCH + return gpio_is_valid(mt9m001->switch_gpio); +#else + return 0; +#endif +} + +static int mt9m001_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; + int ret; + + /* Flags validity verified in test_bus_param */ + + if ((mt9m001->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) || + (mt9m001->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) || + (mt9m001->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) { + /* Well, we actually only can do 10 or 8 bits... */ + if (width_flag == SOCAM_DATAWIDTH_9) + return -EINVAL; + ret = bus_switch_act(mt9m001, + width_flag == SOCAM_DATAWIDTH_8); + if (ret < 0) + return ret; + + mt9m001->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; + } + + return 0; +} + +static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + unsigned int width_flag = SOCAM_DATAWIDTH_10; + + if (bus_switch_possible(mt9m001)) + width_flag |= SOCAM_DATAWIDTH_8; + + /* MT9M001 has all capture_format parameters fixed */ + return SOCAM_PCLK_SAMPLE_RISING | + SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_MASTER | + width_flag; +} + +static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + int ret; + const u16 hblank = 9, vblank = 25; + + /* Blanking and start values - default... */ + ret = reg_write(icd, MT9M001_HORIZONTAL_BLANKING, hblank); + if (ret >= 0) + ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank); + + /* The caller provides a supported format, as verified per + * call to icd->try_fmt_cap() */ + if (ret >= 0) + ret = reg_write(icd, MT9M001_COLUMN_START, rect->left); + if (ret >= 0) + ret = reg_write(icd, MT9M001_ROW_START, rect->top); + if (ret >= 0) + ret = reg_write(icd, MT9M001_WINDOW_WIDTH, rect->width - 1); + if (ret >= 0) + ret = reg_write(icd, MT9M001_WINDOW_HEIGHT, + rect->height + icd->y_skip_top - 1); + if (ret >= 0 && mt9m001->autoexposure) { + ret = reg_write(icd, MT9M001_SHUTTER_WIDTH, + rect->height + icd->y_skip_top + vblank); + if (ret >= 0) { + const struct v4l2_queryctrl *qctrl = + soc_camera_find_qctrl(icd->ops, + V4L2_CID_EXPOSURE); + icd->exposure = (524 + (rect->height + icd->y_skip_top + + vblank - 1) * + (qctrl->maximum - qctrl->minimum)) / + 1048 + qctrl->minimum; + } + } + + return ret < 0 ? ret : 0; +} + +static int mt9m001_try_fmt_cap(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + if (f->fmt.pix.height < 32 + icd->y_skip_top) + f->fmt.pix.height = 32 + icd->y_skip_top; + if (f->fmt.pix.height > 1024 + icd->y_skip_top) + f->fmt.pix.height = 1024 + icd->y_skip_top; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > 1280) + f->fmt.pix.width = 1280; + f->fmt.pix.width &= ~0x01; /* has to be even, unsure why was ~3 */ + + return 0; +} + +static int mt9m001_get_chip_id(struct soc_camera_device *icd, + struct v4l2_chip_ident *id) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + + if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match_chip != mt9m001->client->addr) + return -ENODEV; + + id->ident = mt9m001->model; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9m001_get_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9m001->client->addr) + return -ENODEV; + + reg->val = reg_read(icd, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9m001_set_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9m001->client->addr) + return -ENODEV; + + if (reg_write(icd, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +const struct v4l2_queryctrl mt9m001_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 64, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 1, + .maximum = 255, + .step = 1, + .default_value = 255, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + } +}; + +static int mt9m001_video_probe(struct soc_camera_device *); +static void mt9m001_video_remove(struct soc_camera_device *); +static int mt9m001_get_control(struct soc_camera_device *, struct v4l2_control *); +static int mt9m001_set_control(struct soc_camera_device *, struct v4l2_control *); + +static struct soc_camera_ops mt9m001_ops = { + .owner = THIS_MODULE, + .probe = mt9m001_video_probe, + .remove = mt9m001_video_remove, + .init = mt9m001_init, + .release = mt9m001_release, + .start_capture = mt9m001_start_capture, + .stop_capture = mt9m001_stop_capture, + .set_fmt_cap = mt9m001_set_fmt_cap, + .try_fmt_cap = mt9m001_try_fmt_cap, + .set_bus_param = mt9m001_set_bus_param, + .query_bus_param = mt9m001_query_bus_param, + .controls = mt9m001_controls, + .num_controls = ARRAY_SIZE(mt9m001_controls), + .get_control = mt9m001_get_control, + .set_control = mt9m001_set_control, + .get_chip_id = mt9m001_get_chip_id, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .get_register = mt9m001_get_register, + .set_register = mt9m001_set_register, +#endif +}; + +static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + data = reg_read(icd, MT9M001_READ_OPTIONS2); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x8000); + break; + case V4L2_CID_EXPOSURE_AUTO: + ctrl->value = mt9m001->autoexposure; + break; + } + return 0; +} + +static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + const struct v4l2_queryctrl *qctrl; + int data; + + qctrl = soc_camera_find_qctrl(&mt9m001_ops, ctrl->id); + + if (!qctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->value) + data = reg_set(icd, MT9M001_READ_OPTIONS2, 0x8000); + else + data = reg_clear(icd, MT9M001_READ_OPTIONS2, 0x8000); + if (data < 0) + return -EIO; + break; + case V4L2_CID_GAIN: + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + /* See Datasheet Table 7, Gain settings. */ + if (ctrl->value <= qctrl->default_value) { + /* Pack it into 0..1 step 0.125, register values 0..8 */ + unsigned long range = qctrl->default_value - qctrl->minimum; + data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range; + + dev_dbg(&icd->dev, "Setting gain %d\n", data); + data = reg_write(icd, MT9M001_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } else { + /* Pack it into 1.125..15 variable step, register values 9..67 */ + /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ + unsigned long range = qctrl->maximum - qctrl->default_value - 1; + unsigned long gain = ((ctrl->value - qctrl->default_value - 1) * + 111 + range / 2) / range + 9; + + if (gain <= 32) + data = gain; + else if (gain <= 64) + data = ((gain - 32) * 16 + 16) / 32 + 80; + else + data = ((gain - 64) * 7 + 28) / 56 + 96; + + dev_dbg(&icd->dev, "Setting gain from %d to %d\n", + reg_read(icd, MT9M001_GLOBAL_GAIN), data); + data = reg_write(icd, MT9M001_GLOBAL_GAIN, data); + if (data < 0) + return -EIO; + } + + /* Success */ + icd->gain = ctrl->value; + break; + case V4L2_CID_EXPOSURE: + /* mt9m001 has maximum == default */ + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + else { + unsigned long range = qctrl->maximum - qctrl->minimum; + unsigned long shutter = ((ctrl->value - qctrl->minimum) * 1048 + + range / 2) / range + 1; + + dev_dbg(&icd->dev, "Setting shutter width from %d to %lu\n", + reg_read(icd, MT9M001_SHUTTER_WIDTH), shutter); + if (reg_write(icd, MT9M001_SHUTTER_WIDTH, shutter) < 0) + return -EIO; + icd->exposure = ctrl->value; + mt9m001->autoexposure = 0; + } + break; + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->value) { + const u16 vblank = 25; + if (reg_write(icd, MT9M001_SHUTTER_WIDTH, icd->height + + icd->y_skip_top + vblank) < 0) + return -EIO; + qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); + icd->exposure = (524 + (icd->height + icd->y_skip_top + vblank - 1) * + (qctrl->maximum - qctrl->minimum)) / + 1048 + qctrl->minimum; + mt9m001->autoexposure = 1; + } else + mt9m001->autoexposure = 0; + break; + } + return 0; +} + +/* Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one */ +static int mt9m001_video_probe(struct soc_camera_device *icd) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + s32 data; + int ret; + + /* We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* Enable the chip */ + data = reg_write(&mt9m001->icd, MT9M001_CHIP_ENABLE, 1); + dev_dbg(&icd->dev, "write: %d\n", data); + + /* Read out the chip version register */ + data = reg_read(icd, MT9M001_CHIP_VERSION); + + /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ + switch (data) { + case 0x8411: + case 0x8421: + mt9m001->model = V4L2_IDENT_MT9M001C12ST; + icd->formats = mt9m001_colour_formats; + if (mt9m001->client->dev.platform_data) + icd->num_formats = ARRAY_SIZE(mt9m001_colour_formats); + else + icd->num_formats = 1; + break; + case 0x8431: + mt9m001->model = V4L2_IDENT_MT9M001C12STM; + icd->formats = mt9m001_monochrome_formats; + if (mt9m001->client->dev.platform_data) + icd->num_formats = ARRAY_SIZE(mt9m001_monochrome_formats); + else + icd->num_formats = 1; + break; + default: + ret = -ENODEV; + dev_err(&icd->dev, + "No MT9M001 chip detected, register read %x\n", data); + goto ei2c; + } + + dev_info(&icd->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, + data == 0x8431 ? "C12STM" : "C12ST"); + + /* Now that we know the model, we can start video */ + ret = soc_camera_video_start(icd); + if (ret) + goto eisis; + + return 0; + +eisis: +ei2c: + return ret; +} + +static void mt9m001_video_remove(struct soc_camera_device *icd) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + + dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr, + mt9m001->icd.dev.parent, mt9m001->icd.vdev); + soc_camera_video_stop(&mt9m001->icd); +} + +static int mt9m001_probe(struct i2c_client *client) +{ + struct mt9m001 *mt9m001; + struct soc_camera_device *icd; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl = client->dev.platform_data; + int ret; + + if (!icl) { + dev_err(&client->dev, "MT9M001 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9m001 = kzalloc(sizeof(struct mt9m001), GFP_KERNEL); + if (!mt9m001) + return -ENOMEM; + + mt9m001->client = client; + i2c_set_clientdata(client, mt9m001); + + /* Second stage probe - when a capture adapter is there */ + icd = &mt9m001->icd; + icd->ops = &mt9m001_ops; + icd->control = &client->dev; + icd->x_min = 20; + icd->y_min = 12; + icd->x_current = 20; + icd->y_current = 12; + icd->width_min = 48; + icd->width_max = 1280; + icd->height_min = 32; + icd->height_max = 1024; + icd->y_skip_top = 1; + icd->iface = icl->bus_id; + /* Default datawidth - this is the only width this camera (normally) + * supports. It is only with extra logic that it can support + * other widths. Therefore it seems to be a sensible default. */ + mt9m001->datawidth = 10; + /* Simulated autoexposure. If enabled, we calculate shutter width + * ourselves in the driver based on vertical blanking and frame width */ + mt9m001->autoexposure = 1; + + ret = bus_switch_request(mt9m001, icl); + if (ret) + goto eswinit; + + ret = soc_camera_device_register(icd); + if (ret) + goto eisdr; + + return 0; + +eisdr: + bus_switch_release(mt9m001); +eswinit: + kfree(mt9m001); + return ret; +} + +static int mt9m001_remove(struct i2c_client *client) +{ + struct mt9m001 *mt9m001 = i2c_get_clientdata(client); + + soc_camera_device_unregister(&mt9m001->icd); + bus_switch_release(mt9m001); + kfree(mt9m001); + + return 0; +} + +static struct i2c_driver mt9m001_i2c_driver = { + .driver = { + .name = "mt9m001", + }, + .probe = mt9m001_probe, + .remove = mt9m001_remove, +}; + +static int __init mt9m001_mod_init(void) +{ + return i2c_add_driver(&mt9m001_i2c_driver); +} + +static void __exit mt9m001_mod_exit(void) +{ + i2c_del_driver(&mt9m001_i2c_driver); +} + +module_init(mt9m001_mod_init); +module_exit(mt9m001_mod_exit); + +MODULE_DESCRIPTION("Micron MT9M001 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c new file mode 100644 index 00000000000..d4b9e274434 --- /dev/null +++ b/drivers/media/video/mt9v022.c @@ -0,0 +1,844 @@ +/* + * Driver for MT9V022 CMOS Image Sensor from Micron + * + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/videodev2.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/log2.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +#ifdef CONFIG_MT9M001_PCA9536_SWITCH +#include <asm/gpio.h> +#endif + +/* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c + * The platform has to define i2c_board_info + * and call i2c_register_board_info() */ + +static char *sensor_type; +module_param(sensor_type, charp, S_IRUGO); +MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"\n"); + +/* mt9v022 selected register addresses */ +#define MT9V022_CHIP_VERSION 0x00 +#define MT9V022_COLUMN_START 0x01 +#define MT9V022_ROW_START 0x02 +#define MT9V022_WINDOW_HEIGHT 0x03 +#define MT9V022_WINDOW_WIDTH 0x04 +#define MT9V022_HORIZONTAL_BLANKING 0x05 +#define MT9V022_VERTICAL_BLANKING 0x06 +#define MT9V022_CHIP_CONTROL 0x07 +#define MT9V022_SHUTTER_WIDTH1 0x08 +#define MT9V022_SHUTTER_WIDTH2 0x09 +#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a +#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b +#define MT9V022_RESET 0x0c +#define MT9V022_READ_MODE 0x0d +#define MT9V022_MONITOR_MODE 0x0e +#define MT9V022_PIXEL_OPERATION_MODE 0x0f +#define MT9V022_LED_OUT_CONTROL 0x1b +#define MT9V022_ADC_MODE_CONTROL 0x1c +#define MT9V022_ANALOG_GAIN 0x34 +#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47 +#define MT9V022_PIXCLK_FV_LV 0x74 +#define MT9V022_DIGITAL_TEST_PATTERN 0x7f +#define MT9V022_AEC_AGC_ENABLE 0xAF +#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD + +/* Progressive scan, master, defaults */ +#define MT9V022_CHIP_CONTROL_DEFAULT 0x188 + +static const struct soc_camera_data_format mt9v022_colour_formats[] = { + /* Order important: first natively supported, + * second supported with a GPIO extender */ + { + .name = "Bayer (sRGB) 10 bit", + .depth = 10, + .fourcc = V4L2_PIX_FMT_SBGGR16, + .colorspace = V4L2_COLORSPACE_SRGB, + }, { + .name = "Bayer (sRGB) 8 bit", + .depth = 8, + .fourcc = V4L2_PIX_FMT_SBGGR8, + .colorspace = V4L2_COLORSPACE_SRGB, + } +}; + +static const struct soc_camera_data_format mt9v022_monochrome_formats[] = { + /* Order important - see above */ + { + .name = "Monochrome 10 bit", + .depth = 10, + .fourcc = V4L2_PIX_FMT_Y16, + }, { + .name = "Monochrome 8 bit", + .depth = 8, + .fourcc = V4L2_PIX_FMT_GREY, + }, +}; + +struct mt9v022 { + struct i2c_client *client; + struct soc_camera_device icd; + int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ + int switch_gpio; + u16 chip_control; + unsigned char datawidth; +}; + +static int reg_read(struct soc_camera_device *icd, const u8 reg) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct i2c_client *client = mt9v022->client; + s32 data = i2c_smbus_read_word_data(client, reg); + return data < 0 ? data : swab16(data); +} + +static int reg_write(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + return i2c_smbus_write_word_data(mt9v022->client, reg, swab16(data)); +} + +static int reg_set(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret | data); +} + +static int reg_clear(struct soc_camera_device *icd, const u8 reg, + const u16 data) +{ + int ret; + + ret = reg_read(icd, reg); + if (ret < 0) + return ret; + return reg_write(icd, reg, ret & ~data); +} + +static int mt9v022_init(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + int ret; + + /* Almost the default mode: master, parallel, simultaneous, and an + * undocumented bit 0x200, which is present in table 7, but not in 8, + * plus snapshot mode to disable scan for now */ + mt9v022->chip_control |= 0x10; + ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + if (ret >= 0) + reg_write(icd, MT9V022_READ_MODE, 0x300); + + /* All defaults */ + if (ret >= 0) + /* AEC, AGC on */ + ret = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x3); + if (ret >= 0) + ret = reg_write(icd, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480); + if (ret >= 0) + /* default - auto */ + ret = reg_clear(icd, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); + if (ret >= 0) + ret = reg_write(icd, MT9V022_DIGITAL_TEST_PATTERN, 0); + + return ret >= 0 ? 0 : -EIO; +} + +static int mt9v022_release(struct soc_camera_device *icd) +{ + /* Nothing? */ + return 0; +} + +static int mt9v022_start_capture(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + /* Switch to master "normal" mode */ + mt9v022->chip_control &= ~0x10; + if (reg_write(icd, MT9V022_CHIP_CONTROL, + mt9v022->chip_control) < 0) + return -EIO; + return 0; +} + +static int mt9v022_stop_capture(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + /* Switch to snapshot mode */ + mt9v022->chip_control |= 0x10; + if (reg_write(icd, MT9V022_CHIP_CONTROL, + mt9v022->chip_control) < 0) + return -EIO; + return 0; +} + +static int bus_switch_request(struct mt9v022 *mt9v022, struct soc_camera_link *icl) +{ +#ifdef CONFIG_MT9V022_PCA9536_SWITCH + int ret; + unsigned int gpio = icl->gpio; + + if (gpio_is_valid(gpio)) { + /* We have a data bus switch. */ + ret = gpio_request(gpio, "mt9v022"); + if (ret < 0) { + dev_err(&mt9v022->client->dev, "Cannot get GPIO %u\n", gpio); + return ret; + } + + ret = gpio_direction_output(gpio, 0); + if (ret < 0) { + dev_err(&mt9v022->client->dev, + "Cannot set GPIO %u to output\n", gpio); + gpio_free(gpio); + return ret; + } + } + + mt9v022->switch_gpio = gpio; +#else + mt9v022->switch_gpio = -EINVAL; +#endif + return 0; +} + +static void bus_switch_release(struct mt9v022 *mt9v022) +{ +#ifdef CONFIG_MT9V022_PCA9536_SWITCH + if (gpio_is_valid(mt9v022->switch_gpio)) + gpio_free(mt9v022->switch_gpio); +#endif +} + +static int bus_switch_act(struct mt9v022 *mt9v022, int go8bit) +{ +#ifdef CONFIG_MT9V022_PCA9536_SWITCH + if (!gpio_is_valid(mt9v022->switch_gpio)) + return -ENODEV; + + gpio_set_value_cansleep(mt9v022->switch_gpio, go8bit); + return 0; +#else + return -ENODEV; +#endif +} + +static int bus_switch_possible(struct mt9v022 *mt9v022) +{ +#ifdef CONFIG_MT9V022_PCA9536_SWITCH + return gpio_is_valid(mt9v022->switch_gpio); +#else + return 0; +#endif +} + +static int mt9v022_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; + int ret; + u16 pixclk = 0; + + /* Only one width bit may be set */ + if (!is_power_of_2(width_flag)) + return -EINVAL; + + if ((mt9v022->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) || + (mt9v022->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) || + (mt9v022->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) { + /* Well, we actually only can do 10 or 8 bits... */ + if (width_flag == SOCAM_DATAWIDTH_9) + return -EINVAL; + + ret = bus_switch_act(mt9v022, + width_flag == SOCAM_DATAWIDTH_8); + if (ret < 0) + return ret; + + mt9v022->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; + } + + if (flags & SOCAM_PCLK_SAMPLE_RISING) + pixclk |= 0x10; + + if (!(flags & SOCAM_HSYNC_ACTIVE_HIGH)) + pixclk |= 0x1; + + if (!(flags & SOCAM_VSYNC_ACTIVE_HIGH)) + pixclk |= 0x2; + + ret = reg_write(icd, MT9V022_PIXCLK_FV_LV, pixclk); + if (ret < 0) + return ret; + + if (!(flags & SOCAM_MASTER)) + mt9v022->chip_control &= ~0x8; + + ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + if (ret < 0) + return ret; + + dev_dbg(&icd->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", + pixclk, mt9v022->chip_control); + + return 0; +} + +static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + unsigned int width_flag = SOCAM_DATAWIDTH_10; + + if (bus_switch_possible(mt9v022)) + width_flag |= SOCAM_DATAWIDTH_8; + + return SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | + SOCAM_MASTER | SOCAM_SLAVE | + width_flag; +} + +static int mt9v022_set_fmt_cap(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + int ret; + + /* The caller provides a supported format, as verified per call to + * icd->try_fmt_cap(), datawidth is from our supported format list */ + switch (pixfmt) { + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y16: + if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM) + return -EINVAL; + break; + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SBGGR16: + if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC) + return -EINVAL; + break; + case 0: + /* No format change, only geometry */ + break; + default: + return -EINVAL; + } + + /* Like in example app. Contradicts the datasheet though */ + ret = reg_read(icd, MT9V022_AEC_AGC_ENABLE); + if (ret >= 0) { + if (ret & 1) /* Autoexposure */ + ret = reg_write(icd, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, + rect->height + icd->y_skip_top + 43); + else + ret = reg_write(icd, MT9V022_TOTAL_SHUTTER_WIDTH, + rect->height + icd->y_skip_top + 43); + } + /* Setup frame format: defaults apart from width and height */ + if (ret >= 0) + ret = reg_write(icd, MT9V022_COLUMN_START, rect->left); + if (ret >= 0) + ret = reg_write(icd, MT9V022_ROW_START, rect->top); + if (ret >= 0) + /* Default 94, Phytec driver says: + * "width + horizontal blank >= 660" */ + ret = reg_write(icd, MT9V022_HORIZONTAL_BLANKING, + rect->width > 660 - 43 ? 43 : + 660 - rect->width); + if (ret >= 0) + ret = reg_write(icd, MT9V022_VERTICAL_BLANKING, 45); + if (ret >= 0) + ret = reg_write(icd, MT9V022_WINDOW_WIDTH, rect->width); + if (ret >= 0) + ret = reg_write(icd, MT9V022_WINDOW_HEIGHT, + rect->height + icd->y_skip_top); + + if (ret < 0) + return ret; + + dev_dbg(&icd->dev, "Frame %ux%u pixel\n", rect->width, rect->height); + + return 0; +} + +static int mt9v022_try_fmt_cap(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + if (f->fmt.pix.height < 32 + icd->y_skip_top) + f->fmt.pix.height = 32 + icd->y_skip_top; + if (f->fmt.pix.height > 480 + icd->y_skip_top) + f->fmt.pix.height = 480 + icd->y_skip_top; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > 752) + f->fmt.pix.width = 752; + f->fmt.pix.width &= ~0x03; /* ? */ + + return 0; +} + +static int mt9v022_get_chip_id(struct soc_camera_device *icd, + struct v4l2_chip_ident *id) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + + if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match_chip != mt9v022->client->addr) + return -ENODEV; + + id->ident = mt9v022->model; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int mt9v022_get_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9v022->client->addr) + return -ENODEV; + + reg->val = reg_read(icd, reg->reg); + + if (reg->val > 0xffff) + return -EIO; + + return 0; +} + +static int mt9v022_set_register(struct soc_camera_device *icd, + struct v4l2_register *reg) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + + if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) + return -EINVAL; + + if (reg->match_chip != mt9v022->client->addr) + return -ENODEV; + + if (reg_write(icd, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +const struct v4l2_queryctrl mt9v022_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Analog Gain", + .minimum = 64, + .maximum = 127, + .step = 1, + .default_value = 64, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 1, + .maximum = 255, + .step = 1, + .default_value = 255, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Gain", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + } +}; + +static int mt9v022_video_probe(struct soc_camera_device *); +static void mt9v022_video_remove(struct soc_camera_device *); +static int mt9v022_get_control(struct soc_camera_device *, struct v4l2_control *); +static int mt9v022_set_control(struct soc_camera_device *, struct v4l2_control *); + +static struct soc_camera_ops mt9v022_ops = { + .owner = THIS_MODULE, + .probe = mt9v022_video_probe, + .remove = mt9v022_video_remove, + .init = mt9v022_init, + .release = mt9v022_release, + .start_capture = mt9v022_start_capture, + .stop_capture = mt9v022_stop_capture, + .set_fmt_cap = mt9v022_set_fmt_cap, + .try_fmt_cap = mt9v022_try_fmt_cap, + .set_bus_param = mt9v022_set_bus_param, + .query_bus_param = mt9v022_query_bus_param, + .controls = mt9v022_controls, + .num_controls = ARRAY_SIZE(mt9v022_controls), + .get_control = mt9v022_get_control, + .set_control = mt9v022_set_control, + .get_chip_id = mt9v022_get_chip_id, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .get_register = mt9v022_get_register, + .set_register = mt9v022_set_register, +#endif +}; + +static int mt9v022_get_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + data = reg_read(icd, MT9V022_READ_MODE); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x10); + break; + case V4L2_CID_HFLIP: + data = reg_read(icd, MT9V022_READ_MODE); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x20); + break; + case V4L2_CID_EXPOSURE_AUTO: + data = reg_read(icd, MT9V022_AEC_AGC_ENABLE); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x1); + break; + case V4L2_CID_AUTOGAIN: + data = reg_read(icd, MT9V022_AEC_AGC_ENABLE); + if (data < 0) + return -EIO; + ctrl->value = !!(data & 0x2); + break; + } + return 0; +} + +static int mt9v022_set_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + int data; + const struct v4l2_queryctrl *qctrl; + + qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id); + + if (!qctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->value) + data = reg_set(icd, MT9V022_READ_MODE, 0x10); + else + data = reg_clear(icd, MT9V022_READ_MODE, 0x10); + if (data < 0) + return -EIO; + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + data = reg_set(icd, MT9V022_READ_MODE, 0x20); + else + data = reg_clear(icd, MT9V022_READ_MODE, 0x20); + if (data < 0) + return -EIO; + break; + case V4L2_CID_GAIN: + /* mt9v022 has minimum == default */ + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + else { + unsigned long range = qctrl->maximum - qctrl->minimum; + /* Datasheet says 16 to 64. autogain only works properly + * after setting gain to maximum 14. Larger values + * produce "white fly" noise effect. On the whole, + * manually setting analog gain does no good. */ + unsigned long gain = ((ctrl->value - qctrl->minimum) * + 10 + range / 2) / range + 4; + if (gain >= 32) + gain &= ~1; + /* The user wants to set gain manually, hope, she + * knows, what she's doing... Switch AGC off. */ + + if (reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) + return -EIO; + + dev_info(&icd->dev, "Setting gain from %d to %lu\n", + reg_read(icd, MT9V022_ANALOG_GAIN), gain); + if (reg_write(icd, MT9V022_ANALOG_GAIN, gain) < 0) + return -EIO; + icd->gain = ctrl->value; + } + break; + case V4L2_CID_EXPOSURE: + /* mt9v022 has maximum == default */ + if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) + return -EINVAL; + else { + unsigned long range = qctrl->maximum - qctrl->minimum; + unsigned long shutter = ((ctrl->value - qctrl->minimum) * + 479 + range / 2) / range + 1; + /* The user wants to set shutter width manually, hope, + * she knows, what she's doing... Switch AEC off. */ + + if (reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x1) < 0) + return -EIO; + + dev_dbg(&icd->dev, "Shutter width from %d to %lu\n", + reg_read(icd, MT9V022_TOTAL_SHUTTER_WIDTH), + shutter); + if (reg_write(icd, MT9V022_TOTAL_SHUTTER_WIDTH, + shutter) < 0) + return -EIO; + icd->exposure = ctrl->value; + } + break; + case V4L2_CID_AUTOGAIN: + if (ctrl->value) + data = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x2); + else + data = reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x2); + if (data < 0) + return -EIO; + break; + case V4L2_CID_EXPOSURE_AUTO: + if (ctrl->value) + data = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x1); + else + data = reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x1); + if (data < 0) + return -EIO; + break; + } + return 0; +} + +/* Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one */ +static int mt9v022_video_probe(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + s32 data; + int ret; + + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* Read out the chip version register */ + data = reg_read(icd, MT9V022_CHIP_VERSION); + + /* must be 0x1311 or 0x1313 */ + if (data != 0x1311 && data != 0x1313) { + ret = -ENODEV; + dev_info(&icd->dev, "No MT9V022 detected, ID register 0x%x\n", + data); + goto ei2c; + } + + /* Soft reset */ + ret = reg_write(icd, MT9V022_RESET, 1); + if (ret < 0) + goto ei2c; + /* 15 clock cycles */ + udelay(200); + if (reg_read(icd, MT9V022_RESET)) { + dev_err(&icd->dev, "Resetting MT9V022 failed!\n"); + goto ei2c; + } + + /* Set monochrome or colour sensor type */ + if (sensor_type && (!strcmp("colour", sensor_type) || + !strcmp("color", sensor_type))) { + ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); + mt9v022->model = V4L2_IDENT_MT9V022IX7ATC; + icd->formats = mt9v022_colour_formats; + if (mt9v022->client->dev.platform_data) + icd->num_formats = ARRAY_SIZE(mt9v022_colour_formats); + else + icd->num_formats = 1; + } else { + ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 0x11); + mt9v022->model = V4L2_IDENT_MT9V022IX7ATM; + icd->formats = mt9v022_monochrome_formats; + if (mt9v022->client->dev.platform_data) + icd->num_formats = ARRAY_SIZE(mt9v022_monochrome_formats); + else + icd->num_formats = 1; + } + + if (ret >= 0) + ret = soc_camera_video_start(icd); + if (ret < 0) + goto eisis; + + dev_info(&icd->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", + data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ? + "monochrome" : "colour"); + + return 0; + +eisis: +ei2c: + return ret; +} + +static void mt9v022_video_remove(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + + dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9v022->client->addr, + mt9v022->icd.dev.parent, mt9v022->icd.vdev); + soc_camera_video_stop(&mt9v022->icd); +} + +static int mt9v022_probe(struct i2c_client *client) +{ + struct mt9v022 *mt9v022; + struct soc_camera_device *icd; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl = client->dev.platform_data; + int ret; + + if (!icl) { + dev_err(&client->dev, "MT9V022 driver needs platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); + return -EIO; + } + + mt9v022 = kzalloc(sizeof(struct mt9v022), GFP_KERNEL); + if (!mt9v022) + return -ENOMEM; + + mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; + mt9v022->client = client; + i2c_set_clientdata(client, mt9v022); + + icd = &mt9v022->icd; + icd->ops = &mt9v022_ops; + icd->control = &client->dev; + icd->x_min = 1; + icd->y_min = 4; + icd->x_current = 1; + icd->y_current = 4; + icd->width_min = 48; + icd->width_max = 752; + icd->height_min = 32; + icd->height_max = 480; + icd->y_skip_top = 1; + icd->iface = icl->bus_id; + /* Default datawidth - this is the only width this camera (normally) + * supports. It is only with extra logic that it can support + * other widths. Therefore it seems to be a sensible default. */ + mt9v022->datawidth = 10; + + ret = bus_switch_request(mt9v022, icl); + if (ret) + goto eswinit; + + ret = soc_camera_device_register(icd); + if (ret) + goto eisdr; + + return 0; + +eisdr: + bus_switch_release(mt9v022); +eswinit: + kfree(mt9v022); + return ret; +} + +static int mt9v022_remove(struct i2c_client *client) +{ + struct mt9v022 *mt9v022 = i2c_get_clientdata(client); + + soc_camera_device_unregister(&mt9v022->icd); + bus_switch_release(mt9v022); + kfree(mt9v022); + + return 0; +} + +static struct i2c_driver mt9v022_i2c_driver = { + .driver = { + .name = "mt9v022", + }, + .probe = mt9v022_probe, + .remove = mt9v022_remove, +}; + +static int __init mt9v022_mod_init(void) +{ + return i2c_add_driver(&mt9v022_i2c_driver); +} + +static void __exit mt9v022_mod_exit(void) +{ + i2c_del_driver(&mt9v022_i2c_driver); +} + +module_init(mt9v022_mod_init); +module_exit(mt9v022_mod_exit); + +MODULE_DESCRIPTION("Micron MT9V022 Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index cb5a510f925..f68e91fbe7f 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -38,7 +38,7 @@ #define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) /* global variable */ -static int mxb_num = 0; +static int mxb_num; /* initial frequence the tuner will be tuned to. in verden (lower saxony, germany) 4148 is a @@ -47,7 +47,7 @@ static int freq = 4148; module_param(freq, int, 0644); MODULE_PARM_DESC(freq, "initial frequency the tuner will be tuned to while setup"); -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c index d55d5800efb..eafb0c7736e 100644 --- a/drivers/media/video/ov511.c +++ b/drivers/media/video/ov511.c @@ -41,7 +41,6 @@ #include <linux/slab.h> #include <linux/ctype.h> #include <linux/pagemap.h> -#include <asm/semaphore.h> #include <asm/processor.h> #include <linux/mm.h> #include <linux/device.h> @@ -4660,7 +4659,9 @@ static const struct file_operations ov511_fops = { .read = ov51x_v4l1_read, .mmap = ov51x_v4l1_mmap, .ioctl = ov51x_v4l1_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, }; @@ -5832,7 +5833,7 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id) goto error; memcpy(ov->vdev, &vdev_template, sizeof(*ov->vdev)); - ov->vdev->dev = &dev->dev; + ov->vdev->dev = &intf->dev; video_set_drvdata(ov->vdev, ov); for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) { diff --git a/drivers/media/video/ov511.h b/drivers/media/video/ov511.h index 18c64222dd1..1010e51189b 100644 --- a/drivers/media/video/ov511.h +++ b/drivers/media/video/ov511.h @@ -12,7 +12,7 @@ #ifdef OV511_DEBUG #define PDEBUG(level, fmt, args...) \ if (debug >= (level)) info("[%s:%d] " fmt, \ - __FUNCTION__, __LINE__ , ## args) + __func__, __LINE__ , ## args) #else #define PDEBUG(level, fmt, args...) do {} while(0) #endif diff --git a/drivers/media/video/ovcamchip/ovcamchip_priv.h b/drivers/media/video/ovcamchip/ovcamchip_priv.h index 50c7763d44b..9afa4fe4772 100644 --- a/drivers/media/video/ovcamchip/ovcamchip_priv.h +++ b/drivers/media/video/ovcamchip/ovcamchip_priv.h @@ -24,11 +24,11 @@ extern int ovcamchip_debug; #define PDEBUG(level, fmt, args...) \ if (ovcamchip_debug >= (level)) pr_debug("[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args) + __func__, __LINE__ , ## args) #define DDEBUG(level, dev, fmt, args...) \ if (ovcamchip_debug >= (level)) dev_dbg(dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args) + __func__, __LINE__ , ## args) /* Number of times to retry chip detection. Increase this if you are getting * "Failed to init camera chip" */ diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index 6820c2aabd3..51b1461d8fb 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -57,11 +57,11 @@ struct i2c_info u8 hits; }; -static int i2c_count = 0; +static int i2c_count; static struct i2c_info i2cinfo[64]; static int decoder = PHILIPS2; -static int standard = 0; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ +static int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ /* * I/O ports and Shared Memory @@ -885,7 +885,9 @@ static const struct file_operations pms_fops = { .open = video_exclusive_open, .release = video_exclusive_release, .ioctl = pms_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .read = pms_read, .llseek = no_llseek, }; diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig index 6fc1b8be1a1..a8da90f69dd 100644 --- a/drivers/media/video/pvrusb2/Kconfig +++ b/drivers/media/video/pvrusb2/Kconfig @@ -58,6 +58,30 @@ config VIDEO_PVRUSB2_SYSFS Note: This feature is experimental and subject to change. +config VIDEO_PVRUSB2_DVB + bool "pvrusb2 DVB support (EXPERIMENTAL)" + default n + depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL + select DVB_LGDT330X if !DVB_FE_CUSTOMISE + select DVB_S5H1409 if !DVB_FE_CUSTOMISE + select DVB_TDA10048 if !DVB_FE_CUSTOMIZE + select DVB_TDA18271 if !DVB_FE_CUSTOMIZE + select TUNER_SIMPLE if !DVB_FE_CUSTOMISE + select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE + ---help--- + + This option enables compilation of a DVB interface for the + pvrusb2 driver. Currently this is very very experimental. + It is also limiting - the DVB interface can only access the + digital side of hybrid devices, and there are going to be + issues if you attempt to mess with the V4L side at the same + time. Don't turn this on unless you know what you are + doing. + + If you are in doubt, say N. + + Note: This feature is very experimental and might break + config VIDEO_PVRUSB2_DEBUGIFC bool "pvrusb2 debug interface" depends on VIDEO_PVRUSB2_SYSFS diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile index 47284e55864..5b3083c89aa 100644 --- a/drivers/media/video/pvrusb2/Makefile +++ b/drivers/media/video/pvrusb2/Makefile @@ -1,5 +1,6 @@ obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o +obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \ @@ -9,6 +10,11 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ + $(obj-pvrusb2-dvb-y) \ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o + +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c index 9d94aed2e12..b5db6a5bab3 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-context.c +++ b/drivers/media/video/pvrusb2/pvrusb2-context.c @@ -23,40 +23,193 @@ #include "pvrusb2-ioread.h" #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" +#include <linux/wait.h> +#include <linux/kthread.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/slab.h> -#include <asm/semaphore.h> + +static struct pvr2_context *pvr2_context_exist_first; +static struct pvr2_context *pvr2_context_exist_last; +static struct pvr2_context *pvr2_context_notify_first; +static struct pvr2_context *pvr2_context_notify_last; +static DEFINE_MUTEX(pvr2_context_mutex); +static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data); +static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data); +static int pvr2_context_cleanup_flag; +static int pvr2_context_cleaned_flag; +static struct task_struct *pvr2_context_thread_ptr; + + +static void pvr2_context_set_notify(struct pvr2_context *mp, int fl) +{ + int signal_flag = 0; + mutex_lock(&pvr2_context_mutex); + if (fl) { + if (!mp->notify_flag) { + signal_flag = (pvr2_context_notify_first == NULL); + mp->notify_prev = pvr2_context_notify_last; + mp->notify_next = NULL; + pvr2_context_notify_last = mp; + if (mp->notify_prev) { + mp->notify_prev->notify_next = mp; + } else { + pvr2_context_notify_first = mp; + } + mp->notify_flag = !0; + } + } else { + if (mp->notify_flag) { + mp->notify_flag = 0; + if (mp->notify_next) { + mp->notify_next->notify_prev = mp->notify_prev; + } else { + pvr2_context_notify_last = mp->notify_prev; + } + if (mp->notify_prev) { + mp->notify_prev->notify_next = mp->notify_next; + } else { + pvr2_context_notify_first = mp->notify_next; + } + } + } + mutex_unlock(&pvr2_context_mutex); + if (signal_flag) wake_up(&pvr2_context_sync_data); +} static void pvr2_context_destroy(struct pvr2_context *mp) { - pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp); + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp); if (mp->hdw) pvr2_hdw_destroy(mp->hdw); + pvr2_context_set_notify(mp, 0); + mutex_lock(&pvr2_context_mutex); + if (mp->exist_next) { + mp->exist_next->exist_prev = mp->exist_prev; + } else { + pvr2_context_exist_last = mp->exist_prev; + } + if (mp->exist_prev) { + mp->exist_prev->exist_next = mp->exist_next; + } else { + pvr2_context_exist_first = mp->exist_next; + } + if (!pvr2_context_exist_first) { + /* Trigger wakeup on control thread in case it is waiting + for an exit condition. */ + wake_up(&pvr2_context_sync_data); + } + mutex_unlock(&pvr2_context_mutex); kfree(mp); } -static void pvr2_context_state_check(struct pvr2_context *mp) +static void pvr2_context_notify(struct pvr2_context *mp) { - if (mp->init_flag) return; + pvr2_context_set_notify(mp,!0); +} + - switch (pvr2_hdw_get_state(mp->hdw)) { - case PVR2_STATE_WARM: break; - case PVR2_STATE_ERROR: break; - case PVR2_STATE_READY: break; - case PVR2_STATE_RUN: break; - default: return; +static void pvr2_context_check(struct pvr2_context *mp) +{ + struct pvr2_channel *ch1, *ch2; + pvr2_trace(PVR2_TRACE_CTXT, + "pvr2_context %p (notify)", mp); + if (!mp->initialized_flag && !mp->disconnect_flag) { + mp->initialized_flag = !0; + pvr2_trace(PVR2_TRACE_CTXT, + "pvr2_context %p (initialize)", mp); + /* Finish hardware initialization */ + if (pvr2_hdw_initialize(mp->hdw, + (void (*)(void *))pvr2_context_notify, + mp)) { + mp->video_stream.stream = + pvr2_hdw_get_video_stream(mp->hdw); + /* Trigger interface initialization. By doing this + here initialization runs in our own safe and + cozy thread context. */ + if (mp->setup_func) mp->setup_func(mp); + } else { + pvr2_trace(PVR2_TRACE_CTXT, + "pvr2_context %p (thread skipping setup)", + mp); + /* Even though initialization did not succeed, + we're still going to continue anyway. We need + to do this in order to await the expected + disconnect (which we will detect in the normal + course of operation). */ + } } - pvr2_context_enter(mp); do { - mp->init_flag = !0; - mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw); - if (mp->setup_func) { - mp->setup_func(mp); + for (ch1 = mp->mc_first; ch1; ch1 = ch2) { + ch2 = ch1->mc_next; + if (ch1->check_func) ch1->check_func(ch1); + } + + if (mp->disconnect_flag && !mp->mc_first) { + /* Go away... */ + pvr2_context_destroy(mp); + return; + } +} + + +static int pvr2_context_shutok(void) +{ + return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL); +} + + +static int pvr2_context_thread_func(void *foo) +{ + struct pvr2_context *mp; + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start"); + + do { + while ((mp = pvr2_context_notify_first) != NULL) { + pvr2_context_set_notify(mp, 0); + pvr2_context_check(mp); } - } while (0); pvr2_context_exit(mp); - } + wait_event_interruptible( + pvr2_context_sync_data, + ((pvr2_context_notify_first != NULL) || + pvr2_context_shutok())); + } while (!pvr2_context_shutok()); + + pvr2_context_cleaned_flag = !0; + wake_up(&pvr2_context_cleanup_data); + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up"); + + wait_event_interruptible( + pvr2_context_sync_data, + kthread_should_stop()); + + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end"); + + return 0; +} + + +int pvr2_context_global_init(void) +{ + pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func, + 0, + "pvrusb2-context"); + return (pvr2_context_thread_ptr ? 0 : -ENOMEM); +} + + +void pvr2_context_global_done(void) +{ + pvr2_context_cleanup_flag = !0; + wake_up(&pvr2_context_sync_data); + wait_event_interruptible( + pvr2_context_cleanup_data, + pvr2_context_cleaned_flag); + kthread_stop(pvr2_context_thread_ptr); +} struct pvr2_context *pvr2_context_create( @@ -67,67 +220,75 @@ struct pvr2_context *pvr2_context_create( struct pvr2_context *mp = NULL; mp = kzalloc(sizeof(*mp),GFP_KERNEL); if (!mp) goto done; - pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_main id=%p",mp); + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp); mp->setup_func = setup_func; mutex_init(&mp->mutex); + mutex_lock(&pvr2_context_mutex); + mp->exist_prev = pvr2_context_exist_last; + mp->exist_next = NULL; + pvr2_context_exist_last = mp; + if (mp->exist_prev) { + mp->exist_prev->exist_next = mp; + } else { + pvr2_context_exist_first = mp; + } + mutex_unlock(&pvr2_context_mutex); mp->hdw = pvr2_hdw_create(intf,devid); if (!mp->hdw) { pvr2_context_destroy(mp); mp = NULL; goto done; } - pvr2_hdw_set_state_callback(mp->hdw, - (void (*)(void *))pvr2_context_state_check, - mp); - pvr2_context_state_check(mp); + pvr2_context_set_notify(mp, !0); done: return mp; } -void pvr2_context_enter(struct pvr2_context *mp) +static void pvr2_context_reset_input_limits(struct pvr2_context *mp) +{ + unsigned int tmsk,mmsk; + struct pvr2_channel *cp; + struct pvr2_hdw *hdw = mp->hdw; + mmsk = pvr2_hdw_get_input_available(hdw); + tmsk = mmsk; + for (cp = mp->mc_first; cp; cp = cp->mc_next) { + if (!cp->input_mask) continue; + tmsk &= cp->input_mask; + } + pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk); + pvr2_hdw_commit_ctl(hdw); +} + + +static void pvr2_context_enter(struct pvr2_context *mp) { mutex_lock(&mp->mutex); - pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_enter(id=%p)",mp); } -void pvr2_context_exit(struct pvr2_context *mp) +static void pvr2_context_exit(struct pvr2_context *mp) { int destroy_flag = 0; if (!(mp->mc_first || !mp->disconnect_flag)) { destroy_flag = !0; } - pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_exit(id=%p) outside",mp); mutex_unlock(&mp->mutex); - if (destroy_flag) pvr2_context_destroy(mp); -} - - -static void pvr2_context_run_checks(struct pvr2_context *mp) -{ - struct pvr2_channel *ch1,*ch2; - for (ch1 = mp->mc_first; ch1; ch1 = ch2) { - ch2 = ch1->mc_next; - if (ch1->check_func) { - ch1->check_func(ch1); - } - } + if (destroy_flag) pvr2_context_notify(mp); } void pvr2_context_disconnect(struct pvr2_context *mp) { - pvr2_context_enter(mp); do { - pvr2_hdw_disconnect(mp->hdw); - mp->disconnect_flag = !0; - pvr2_context_run_checks(mp); - } while (0); pvr2_context_exit(mp); + pvr2_hdw_disconnect(mp->hdw); + mp->disconnect_flag = !0; + pvr2_context_notify(mp); } void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp) { + pvr2_context_enter(mp); cp->hdw = mp->hdw; cp->mc_head = mp; cp->mc_next = NULL; @@ -138,6 +299,7 @@ void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp) mp->mc_first = cp; } mp->mc_last = cp; + pvr2_context_exit(mp); } @@ -153,7 +315,10 @@ static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp) void pvr2_channel_done(struct pvr2_channel *cp) { struct pvr2_context *mp = cp->mc_head; + pvr2_context_enter(mp); + cp->input_mask = 0; pvr2_channel_disclaim_stream(cp); + pvr2_context_reset_input_limits(mp); if (cp->mc_next) { cp->mc_next->mc_prev = cp->mc_prev; } else { @@ -165,6 +330,58 @@ void pvr2_channel_done(struct pvr2_channel *cp) mp->mc_first = cp->mc_next; } cp->hdw = NULL; + pvr2_context_exit(mp); +} + + +int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk) +{ + unsigned int tmsk,mmsk; + int ret = 0; + struct pvr2_channel *p2; + struct pvr2_hdw *hdw = cp->hdw; + + mmsk = pvr2_hdw_get_input_available(hdw); + cmsk &= mmsk; + if (cmsk == cp->input_mask) { + /* No change; nothing to do */ + return 0; + } + + pvr2_context_enter(cp->mc_head); + do { + if (!cmsk) { + cp->input_mask = 0; + pvr2_context_reset_input_limits(cp->mc_head); + break; + } + tmsk = mmsk; + for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) { + if (p2 == cp) continue; + if (!p2->input_mask) continue; + tmsk &= p2->input_mask; + } + if (!(tmsk & cmsk)) { + ret = -EPERM; + break; + } + tmsk &= cmsk; + if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) { + /* Internal failure changing allowed list; probably + should not happen, but react if it does. */ + break; + } + cp->input_mask = cmsk; + pvr2_hdw_commit_ctl(hdw); + } while (0); + pvr2_context_exit(cp->mc_head); + return ret; +} + + +unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp) +{ + return cp->input_mask; } @@ -174,7 +391,7 @@ int pvr2_channel_claim_stream(struct pvr2_channel *cp, int code = 0; pvr2_context_enter(cp->mc_head); do { if (sp == cp->stream) break; - if (sp->user) { + if (sp && sp->user) { code = -EBUSY; break; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h index a04187a9322..745e270233c 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-context.h +++ b/drivers/media/video/pvrusb2/pvrusb2-context.h @@ -30,7 +30,6 @@ struct pvr2_stream; /* stream interface - defined elsewhere */ struct pvr2_context; /* All central state */ struct pvr2_channel; /* One I/O pathway to a user */ struct pvr2_context_stream; /* Wrapper for a stream */ -struct pvr2_crit_reg; /* Critical region pointer */ struct pvr2_ioread; /* Low level stream structure */ struct pvr2_context_stream { @@ -41,11 +40,16 @@ struct pvr2_context_stream { struct pvr2_context { struct pvr2_channel *mc_first; struct pvr2_channel *mc_last; + struct pvr2_context *exist_next; + struct pvr2_context *exist_prev; + struct pvr2_context *notify_next; + struct pvr2_context *notify_prev; struct pvr2_hdw *hdw; struct pvr2_context_stream video_stream; struct mutex mutex; + int notify_flag; + int initialized_flag; int disconnect_flag; - int init_flag; /* Called after pvr2_context initialization is complete */ void (*setup_func)(struct pvr2_context *); @@ -58,12 +62,10 @@ struct pvr2_channel { struct pvr2_channel *mc_prev; struct pvr2_context_stream *stream; struct pvr2_hdw *hdw; + unsigned int input_mask; void (*check_func)(struct pvr2_channel *); }; -void pvr2_context_enter(struct pvr2_context *); -void pvr2_context_exit(struct pvr2_context *); - struct pvr2_context *pvr2_context_create(struct usb_interface *intf, const struct usb_device_id *devid, void (*setup_func)(struct pvr2_context *)); @@ -71,11 +73,15 @@ void pvr2_context_disconnect(struct pvr2_context *); void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *); void pvr2_channel_done(struct pvr2_channel *); +int pvr2_channel_limit_inputs(struct pvr2_channel *,unsigned int); +unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *); int pvr2_channel_claim_stream(struct pvr2_channel *, struct pvr2_context_stream *); struct pvr2_ioread *pvr2_channel_create_mpeg_stream( struct pvr2_context_stream *); +int pvr2_context_global_init(void); +void pvr2_context_global_done(void); #endif /* __PVRUSB2_CONTEXT_H */ /* diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index 5a3e8d21a38..91a42f2473a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -30,6 +30,9 @@ static int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val) { if (cptr->info->check_value) { if (!cptr->info->check_value(cptr,val)) return -ERANGE; + } else if (cptr->info->type == pvr2_ctl_enum) { + if (val < 0) return -ERANGE; + if (val >= cptr->info->def.type_enum.count) return -ERANGE; } else { int lim; lim = cptr->info->def.type_int.min_value; @@ -63,13 +66,10 @@ int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val) if (cptr->info->set_value) { if (cptr->info->type == pvr2_ctl_bitmask) { mask &= cptr->info->def.type_bitmask.valid_bits; - } else if (cptr->info->type == pvr2_ctl_int) { + } else if ((cptr->info->type == pvr2_ctl_int)|| + (cptr->info->type == pvr2_ctl_enum)) { ret = pvr2_ctrl_range_check(cptr,val); if (ret < 0) break; - } else if (cptr->info->type == pvr2_ctl_enum) { - if (val >= cptr->info->def.type_enum.count) { - break; - } } else if (cptr->info->type != pvr2_ctl_bool) { break; } @@ -204,8 +204,7 @@ int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val, if (cptr->info->type == pvr2_ctl_enum) { const char **names; names = cptr->info->def.type_enum.value_names; - if ((val >= 0) && - (val < cptr->info->def.type_enum.count)) { + if (pvr2_ctrl_range_check(cptr,val) == 0) { if (names[val]) { *blen = scnprintf( bptr,bmax,"%s", @@ -528,10 +527,8 @@ int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr, ptr,len,valptr, cptr->info->def.type_enum.value_names, cptr->info->def.type_enum.count); - if ((ret >= 0) && - ((*valptr < 0) || - (*valptr >= cptr->info->def.type_enum.count))) { - ret = -ERANGE; + if (ret >= 0) { + ret = pvr2_ctrl_range_check(cptr,*valptr); } if (maskptr) *maskptr = ~0; } else if (cptr->info->type == pvr2_ctl_bitmask) { diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index ffdc45c324e..97350b048b8 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -84,7 +84,9 @@ static const struct routing_scheme_item routing_schemegv[] = { .vid = CX25840_COMPOSITE2, .aud = CX25840_AUDIO5, }, - [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */ + [PVR2_CVAL_INPUT_RADIO] = { + /* line-in is used for radio and composite. A GPIO is + used to switch between the two choices. */ .vid = CX25840_COMPOSITE1, .aud = CX25840_AUDIO_SERIAL, }, diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h index fca49d8a931..11537ddf8aa 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debug.h +++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h @@ -39,7 +39,7 @@ extern int pvrusb2_debug; #define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */ #define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */ #define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */ -#define PVR2_TRACE_CREG (1 << 13) /* Main critical region entry / exit */ +#define PVR2_TRACE_CTXT (1 << 13) /* Main context tracking */ #define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */ #define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */ #define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index b0687430fdd..b53121c78ff 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -164,6 +164,8 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, int ccnt; int ret; u32 gpio_dir,gpio_in,gpio_out; + struct pvr2_stream_stats stats; + struct pvr2_stream *sp; ret = pvr2_hdw_is_hsm(hdw); ccnt = scnprintf(buf,acnt,"USB link speed: %s\n", @@ -182,6 +184,24 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, pvr2_hdw_get_streaming(hdw) ? "on" : "off"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; + + sp = pvr2_hdw_get_video_stream(hdw); + if (sp) { + pvr2_stream_get_stats(sp, &stats, 0); + ccnt = scnprintf( + buf,acnt, + "Bytes streamed=%u" + " URBs: queued=%u idle=%u ready=%u" + " processed=%u failed=%u\n", + stats.bytes_processed, + stats.buffers_in_queue, + stats.buffers_in_idle, + stats.buffers_in_ready, + stats.buffers_processed, + stats.buffers_failed); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + } + return bcnt; } @@ -220,6 +240,10 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, return pvr2_hdw_cmd_decoder_reset(hdw); } else if (debugifc_match_keyword(wptr,wlen,"worker")) { return pvr2_hdw_untrip(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"usbstats")) { + pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw), + NULL, !0); + return 0; } return -EINVAL; } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) { diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index fe9991c10cf..2dd06a90adc 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -32,7 +32,15 @@ pvr2_device_desc structures. /* This is needed in order to pull in tuner type ids... */ #include <linux/i2c.h> #include <media/tuner.h> - +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +#include "pvrusb2-hdw-internal.h" +#include "lgdt330x.h" +#include "s5h1409.h" +#include "tda10048.h" +#include "tda18271.h" +#include "tda8290.h" +#include "tuner-simple.h" +#endif /*------------------------------------------------------------------------*/ @@ -49,14 +57,19 @@ static const char *pvr2_fw1_names_29xxx[] = { }; static const struct pvr2_device_desc pvr2_device_29xxx = { - .description = "WinTV PVR USB2 Model Category 29xxxx", + .description = "WinTV PVR USB2 Model Category 29xxx", .shortname = "29xxx", .client_modules.lst = pvr2_client_29xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx), .fx2_firmware.lst = pvr2_fw1_names_29xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx), .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_fmradio = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, }; @@ -75,7 +88,7 @@ static const char *pvr2_fw1_names_24xxx[] = { }; static const struct pvr2_device_desc pvr2_device_24xxx = { - .description = "WinTV PVR USB2 Model Category 24xxxx", + .description = "WinTV PVR USB2 Model Category 24xxx", .shortname = "24xxx", .client_modules.lst = pvr2_client_24xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx), @@ -85,7 +98,12 @@ static const struct pvr2_device_desc pvr2_device_24xxx = { .flag_has_wm8775 = !0, .flag_has_hauppauge_rom = !0, .flag_has_hauppauge_custom_ir = !0, + .flag_has_analogtuner = !0, + .flag_has_fmradio = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, }; @@ -105,6 +123,30 @@ static const struct pvr2_device_desc pvr2_device_gotview_2 = { .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2), .flag_has_cx25840 = !0, .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .flag_has_analogtuner = !0, + .flag_has_fmradio = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, +}; + + + +/*------------------------------------------------------------------------*/ +/* GOTVIEW USB2.0 DVD Deluxe */ + +/* (same module list as gotview_2) */ + +static const struct pvr2_device_desc pvr2_device_gotview_2d = { + .description = "Gotview USB 2.0 DVD Deluxe", + .shortname = "gv2d", + .client_modules.lst = pvr2_client_gotview_2, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2), + .flag_has_cx25840 = !0, + .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, }; @@ -114,6 +156,38 @@ static const struct pvr2_device_desc pvr2_device_gotview_2 = { /*------------------------------------------------------------------------*/ /* OnAir Creator */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct lgdt330x_config pvr2_lgdt3303_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3303, + .clock_polarity_flip = 1, +}; + +static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x61, + TUNER_LG_TDVS_H06XF); + + return 0; +} + +struct pvr2_dvb_props pvr2_onair_creator_fe_props = { + .frontend_attach = pvr2_lgdt3303_attach, + .tuner_attach = pvr2_lgh06xf_attach, +}; +#endif + static const char *pvr2_client_onair_creator[] = { "saa7115", "tuner", @@ -126,7 +200,16 @@ static const struct pvr2_device_desc pvr2_device_onair_creator = { .client_modules.lst = pvr2_client_onair_creator, .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_creator), .default_tuner_type = TUNER_LG_TDVS_H06XF, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .flag_digital_requires_cx23416 = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, + .default_std_mask = V4L2_STD_NTSC_M, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_onair_creator_fe_props, +#endif }; #endif @@ -136,6 +219,37 @@ static const struct pvr2_device_desc pvr2_device_onair_creator = { /*------------------------------------------------------------------------*/ /* OnAir USB 2.0 */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct lgdt330x_config pvr2_lgdt3302_config = { + .demod_address = 0x0e, + .demod_chip = LGDT3302, +}; + +static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x61, + TUNER_PHILIPS_FCV1236D); + + return 0; +} + +struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { + .frontend_attach = pvr2_lgdt3302_attach, + .tuner_attach = pvr2_fcv1236d_attach, +}; +#endif + static const char *pvr2_client_onair_usb2[] = { "saa7115", "tuner", @@ -147,8 +261,17 @@ static const struct pvr2_device_desc pvr2_device_onair_usb2 = { .shortname = "oa2", .client_modules.lst = pvr2_client_onair_usb2, .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_usb2), - .default_tuner_type = TUNER_PHILIPS_ATSC, + .default_tuner_type = TUNER_PHILIPS_FCV1236D, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .flag_digital_requires_cx23416 = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, + .default_std_mask = V4L2_STD_NTSC_M, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_onair_usb2_fe_props, +#endif }; #endif @@ -157,6 +280,50 @@ static const struct pvr2_device_desc pvr2_device_onair_usb2 = { /*------------------------------------------------------------------------*/ /* Hauppauge PVR-USB2 Model 73xxx */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct tda10048_config hauppauge_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_50, + .inversion = TDA10048_INVERSION_ON, +}; + +static struct tda829x_config tda829x_no_probe = { + .probe_tuner = TDA829X_DONT_PROBE, +}; + +static struct tda18271_config hauppauge_tda18271_dvb_config = { + .gate = TDA18271_GATE_ANALOG, +}; + +static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(tda829x_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x42, + &tda829x_no_probe); + dvb_attach(tda18271_attach, adap->fe, 0x60, + &adap->channel.hdw->i2c_adap, + &hauppauge_tda18271_dvb_config); + + return 0; +} + +struct pvr2_dvb_props pvr2_73xxx_dvb_props = { + .frontend_attach = pvr2_tda10048_attach, + .tuner_attach = pvr2_73xxx_tda18271_8295_attach, +}; +#endif + static const char *pvr2_client_73xxx[] = { "cx25840", "tuner", @@ -167,7 +334,7 @@ static const char *pvr2_fw1_names_73xxx[] = { }; static const struct pvr2_device_desc pvr2_device_73xxx = { - .description = "WinTV PVR USB2 Model Category 73xxxx", + .description = "WinTV PVR USB2 Model Category 73xxx", .shortname = "73xxx", .client_modules.lst = pvr2_client_73xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_73xxx), @@ -175,15 +342,14 @@ static const struct pvr2_device_desc pvr2_device_73xxx = { .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx), .flag_has_cx25840 = !0, .flag_has_hauppauge_rom = !0, -#if 0 .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, -#else - .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_73xxx_dvb_props, #endif }; @@ -192,6 +358,56 @@ static const struct pvr2_device_desc pvr2_device_73xxx = { /*------------------------------------------------------------------------*/ /* Hauppauge PVR-USB2 Model 75xxx */ +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +static struct s5h1409_config pvr2_s5h1409_config = { + .demod_address = 0x32 >> 1, + .output_mode = S5H1409_PARALLEL_OUTPUT, + .gpio = S5H1409_GPIO_OFF, + .qam_if = 4000, + .inversion = S5H1409_INVERSION_ON, + .status_mode = S5H1409_DEMODLOCKING, +}; + +static struct tda18271_std_map hauppauge_tda18271_std_map = { + .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3, + .if_lvl = 6, .rfagc_top = 0x37, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, + .if_lvl = 6, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config hauppauge_tda18271_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, +}; + +static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap) +{ + adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config, + &adap->channel.hdw->i2c_adap); + if (adap->fe) + return 0; + + return -EIO; +} + +static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) +{ + dvb_attach(tda829x_attach, adap->fe, + &adap->channel.hdw->i2c_adap, 0x42, + &tda829x_no_probe); + dvb_attach(tda18271_attach, adap->fe, 0x60, + &adap->channel.hdw->i2c_adap, + &hauppauge_tda18271_config); + + return 0; +} + +struct pvr2_dvb_props pvr2_750xx_dvb_props = { + .frontend_attach = pvr2_s5h1409_attach, + .tuner_attach = pvr2_tda18271_8295_attach, +}; +#endif + static const char *pvr2_client_75xxx[] = { "cx25840", "tuner", @@ -201,17 +417,43 @@ static const char *pvr2_fw1_names_75xxx[] = { "v4l-pvrusb2-73xxx-01.fw", }; -static const struct pvr2_device_desc pvr2_device_75xxx = { - .description = "WinTV PVR USB2 Model Category 75xxxx", - .shortname = "75xxx", +static const struct pvr2_device_desc pvr2_device_750xx = { + .description = "WinTV PVR USB2 Model Category 750xx", + .shortname = "750xx", + .client_modules.lst = pvr2_client_75xxx, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), + .fx2_firmware.lst = pvr2_fw1_names_75xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), + .flag_has_cx25840 = !0, + .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, + .default_std_mask = V4L2_STD_NTSC_M, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + .dvb_props = &pvr2_750xx_dvb_props, +#endif +}; + +static const struct pvr2_device_desc pvr2_device_751xx = { + .description = "WinTV PVR USB2 Model Category 751xx", + .shortname = "751xx", .client_modules.lst = pvr2_client_75xxx, .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), .fx2_firmware.lst = pvr2_fw1_names_75xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), .flag_has_cx25840 = !0, .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .default_std_mask = V4L2_STD_NTSC_M, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, }; @@ -225,6 +467,8 @@ struct usb_device_id pvr2_device_table[] = { .driver_info = (kernel_ulong_t)&pvr2_device_24xxx}, { USB_DEVICE(0x1164, 0x0622), .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2}, + { USB_DEVICE(0x1164, 0x0602), + .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d}, #ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR { USB_DEVICE(0x11ba, 0x1003), .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator}, @@ -236,9 +480,9 @@ struct usb_device_id pvr2_device_table[] = { { USB_DEVICE(0x2040, 0x7300), .driver_info = (kernel_ulong_t)&pvr2_device_73xxx}, { USB_DEVICE(0x2040, 0x7500), - .driver_info = (kernel_ulong_t)&pvr2_device_75xxx}, + .driver_info = (kernel_ulong_t)&pvr2_device_750xx}, { USB_DEVICE(0x2040, 0x7501), - .driver_info = (kernel_ulong_t)&pvr2_device_75xxx}, + .driver_info = (kernel_ulong_t)&pvr2_device_751xx}, { } }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h index 64b467f0637..c2e2b06fe2e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -23,6 +23,9 @@ #include <linux/mod_devicetable.h> #include <linux/videodev2.h> +#ifdef CONFIG_VIDEO_PVRUSB2_DVB +#include "pvrusb2-dvb.h" +#endif /* @@ -39,6 +42,13 @@ struct pvr2_string_table { #define PVR2_ROUTING_SCHEME_HAUPPAUGE 0 #define PVR2_ROUTING_SCHEME_GOTVIEW 1 +#define PVR2_DIGITAL_SCHEME_NONE 0 +#define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1 +#define PVR2_DIGITAL_SCHEME_ONAIR 2 + +#define PVR2_LED_SCHEME_NONE 0 +#define PVR2_LED_SCHEME_HAUPPAUGE 1 + /* This describes a particular hardware type (except for the USB device ID which must live in a separate structure due to environmental constraints). See the top of pvrusb2-hdw.c for where this is @@ -58,40 +68,64 @@ struct pvr2_device_desc { was initialized from internal ROM. */ struct pvr2_string_table fx2_firmware; +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + /* callback functions to handle attachment of digital tuner & demod */ + struct pvr2_dvb_props *dvb_props; + +#endif + /* Initial standard bits to use for this device, if not zero. + Anything set here is also implied as an available standard. + Note: This is ignored if overridden on the module load line via + the video_std module option. */ + v4l2_std_id default_std_mask; + + /* V4L tuner type ID to use with this device (only used if the + driver could not discover the type any other way). */ + int default_tuner_type; + /* Signal routing scheme used by device, contains one of PVR2_ROUTING_SCHEME_XXX. Schemes have to be defined as we encounter them. This is an arbitrary integer scheme id; its meaning is contained entirely within the driver and is interpreted by logic which must send commands to the chip-level drivers (search for things which touch this field). */ - unsigned int signal_routing_scheme; + unsigned char signal_routing_scheme; - /* V4L tuner type ID to use with this device (only used if the - driver could not discover the type any other way). */ - int default_tuner_type; + /* Indicates scheme for controlling device's LED (if any). The + driver will turn on the LED when streaming is underway. This + contains one of PVR2_LED_SCHEME_XXX. */ + unsigned char led_scheme; - /* Initial standard bits to use for this device, if not zero. - Anything set here is also implied as an available standard. - Note: This is ignored if overridden on the module load line via - the video_std module option. */ - v4l2_std_id default_std_mask; + /* Control scheme to use if there is a digital tuner. This + contains one of PVR2_DIGITAL_SCHEME_XXX. This is an arbitrary + integer scheme id; its meaning is contained entirely within the + driver and is interpreted by logic which must control the + streaming pathway (search for things which touch this field). */ + unsigned char digital_control_scheme; /* If set, we don't bother trying to load cx23416 firmware. */ - char flag_skip_cx23416_firmware; + int flag_skip_cx23416_firmware:1; + + /* If set, the encoder must be healthy in order for digital mode to + work (otherwise we assume that digital streaming will work even + if we fail to locate firmware for the encoder). If the device + doesn't support digital streaming then this flag has no + effect. */ + int flag_digital_requires_cx23416:1; /* Device has a hauppauge eeprom which we can interrogate. */ - char flag_has_hauppauge_rom; + int flag_has_hauppauge_rom:1; /* Device does not require a powerup command to be issued. */ - char flag_no_powerup; + int flag_no_powerup:1; /* Device has a cx25840 - this enables special additional logic to handle it. */ - char flag_has_cx25840; + int flag_has_cx25840:1; /* Device has a wm8775 - this enables special additional logic to ensure that it is found. */ - char flag_has_wm8775; + int flag_has_wm8775:1; /* Device has IR hardware that can be faked into looking like a normal Hauppauge i2c IR receiver. This is currently very @@ -101,7 +135,15 @@ struct pvr2_device_desc { to virtualize the presence of the non-existant IR receiver chip and implement the virtual receiver in terms of appropriate FX2 commands. */ - char flag_has_hauppauge_custom_ir; + int flag_has_hauppauge_custom_ir:1; + + /* These bits define which kinds of sources the device can handle. + Note: Digital tuner presence is inferred by the + digital_control_scheme enumeration. */ + int flag_has_fmradio:1; /* Has FM radio receiver */ + int flag_has_analogtuner:1; /* Has analog tuner */ + int flag_has_composite:1; /* Has composite input */ + int flag_has_svideo:1; /* Has s-video input */ }; extern struct usb_device_id pvr2_device_table[]; diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c new file mode 100644 index 00000000000..2e64f98d124 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c @@ -0,0 +1,425 @@ +/* + * pvrusb2-dvb.c - linux-dvb api interface to the pvrusb2 driver. + * + * Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kthread.h> +#include <linux/freezer.h> +#include "dvbdev.h" +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-hdw.h" +#include "pvrusb2-io.h" +#include "pvrusb2-dvb.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap) +{ + int ret; + unsigned int count; + struct pvr2_buffer *bp; + struct pvr2_stream *stream; + + printk(KERN_DEBUG "dvb thread started\n"); + set_freezable(); + + stream = adap->channel.stream->stream; + + for (;;) { + if (kthread_should_stop()) break; + + /* Not sure about this... */ + try_to_freeze(); + + bp = pvr2_stream_get_ready_buffer(stream); + if (bp != NULL) { + count = pvr2_buffer_get_count(bp); + if (count) { + dvb_dmx_swfilter( + &adap->demux, + adap->buffer_storage[ + pvr2_buffer_get_id(bp)], + count); + } else { + ret = pvr2_buffer_get_status(bp); + if (ret < 0) break; + } + ret = pvr2_buffer_queue(bp); + if (ret < 0) break; + + /* Since we know we did something to a buffer, + just go back and try again. No point in + blocking unless we really ran out of + buffers to process. */ + continue; + } + + + /* Wait until more buffers become available or we're + told not to wait any longer. */ + ret = wait_event_interruptible( + adap->buffer_wait_data, + (pvr2_stream_get_ready_count(stream) > 0) || + kthread_should_stop()); + if (ret < 0) break; + } + + /* If we get here and ret is < 0, then an error has occurred. + Probably would be a good idea to communicate that to DVB core... */ + + printk(KERN_DEBUG "dvb thread stopped\n"); + + return 0; +} + +static int pvr2_dvb_feed_thread(void *data) +{ + int stat = pvr2_dvb_feed_func(data); + /* from videobuf-dvb.c: */ + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + return stat; +} + +static void pvr2_dvb_notify(struct pvr2_dvb_adapter *adap) +{ + wake_up(&adap->buffer_wait_data); +} + +static void pvr2_dvb_stream_end(struct pvr2_dvb_adapter *adap) +{ + unsigned int idx; + struct pvr2_stream *stream; + + if (adap->thread) { + kthread_stop(adap->thread); + adap->thread = NULL; + } + + if (adap->channel.stream) { + stream = adap->channel.stream->stream; + } else { + stream = NULL; + } + if (stream) { + pvr2_hdw_set_streaming(adap->channel.hdw, 0); + pvr2_stream_set_callback(stream, NULL, NULL); + pvr2_stream_kill(stream); + pvr2_stream_set_buffer_count(stream, 0); + pvr2_channel_claim_stream(&adap->channel, NULL); + } + + if (adap->stream_run) { + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + if (!(adap->buffer_storage[idx])) continue; + kfree(adap->buffer_storage[idx]); + adap->buffer_storage[idx] = 0; + } + adap->stream_run = 0; + } +} + +static int pvr2_dvb_stream_do_start(struct pvr2_dvb_adapter *adap) +{ + struct pvr2_context *pvr = adap->channel.mc_head; + unsigned int idx; + int ret; + struct pvr2_buffer *bp; + struct pvr2_stream *stream = 0; + + if (adap->stream_run) return -EIO; + + ret = pvr2_channel_claim_stream(&adap->channel, &pvr->video_stream); + /* somebody else already has the stream */ + if (ret < 0) return ret; + + stream = adap->channel.stream->stream; + + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + adap->buffer_storage[idx] = kmalloc(PVR2_DVB_BUFFER_SIZE, + GFP_KERNEL); + if (!(adap->buffer_storage[idx])) return -ENOMEM; + } + + pvr2_stream_set_callback(pvr->video_stream.stream, + (pvr2_stream_callback) pvr2_dvb_notify, adap); + + ret = pvr2_stream_set_buffer_count(stream, PVR2_DVB_BUFFER_COUNT); + if (ret < 0) return ret; + + for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) { + bp = pvr2_stream_get_buffer(stream, idx); + pvr2_buffer_set_buffer(bp, + adap->buffer_storage[idx], + PVR2_DVB_BUFFER_SIZE); + } + + ret = pvr2_hdw_set_streaming(adap->channel.hdw, 1); + if (ret < 0) return ret; + + while ((bp = pvr2_stream_get_idle_buffer(stream)) != 0) { + ret = pvr2_buffer_queue(bp); + if (ret < 0) return ret; + } + + adap->thread = kthread_run(pvr2_dvb_feed_thread, adap, "pvrusb2-dvb"); + + if (IS_ERR(adap->thread)) { + ret = PTR_ERR(adap->thread); + adap->thread = NULL; + return ret; + } + + adap->stream_run = !0; + + return 0; +} + +static int pvr2_dvb_stream_start(struct pvr2_dvb_adapter *adap) +{ + int ret = pvr2_dvb_stream_do_start(adap); + if (ret < 0) pvr2_dvb_stream_end(adap); + return ret; +} + +static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff) +{ + struct pvr2_dvb_adapter *adap = dvbdmxfeed->demux->priv; + int ret = 0; + + if (adap == NULL) return -ENODEV; + + mutex_lock(&adap->lock); + do { + if (onoff) { + if (!adap->feedcount) { + printk(KERN_DEBUG "start feeding\n"); + ret = pvr2_dvb_stream_start(adap); + if (ret < 0) break; + } + (adap->feedcount)++; + } else if (adap->feedcount > 0) { + (adap->feedcount)--; + if (!adap->feedcount) { + printk(KERN_DEBUG "stop feeding\n"); + pvr2_dvb_stream_end(adap); + } + } + } while (0); + mutex_unlock(&adap->lock); + + return ret; +} + +static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + printk(KERN_DEBUG "start pid: 0x%04x, feedtype: %d\n", + dvbdmxfeed->pid, dvbdmxfeed->type); + return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1); +} + +static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + printk(KERN_DEBUG "stop pid: 0x%04x, feedtype: %d\n", + dvbdmxfeed->pid, dvbdmxfeed->type); + return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0); +} + +static int pvr2_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct pvr2_dvb_adapter *adap = fe->dvb->priv; + return pvr2_channel_limit_inputs( + &adap->channel, + (acquire ? (1 << PVR2_CVAL_INPUT_DTV) : 0)); +} + +static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap) +{ + int ret; + + ret = dvb_register_adapter(&adap->dvb_adap, "pvrusb2-dvb", + THIS_MODULE/*&hdw->usb_dev->owner*/, + &adap->channel.hdw->usb_dev->dev, + adapter_nr); + if (ret < 0) { + err("dvb_register_adapter failed: error %d", ret); + goto err; + } + adap->dvb_adap.priv = adap; + + adap->demux.dmx.capabilities = DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + adap->demux.priv = adap; + adap->demux.filternum = 256; + adap->demux.feednum = 256; + adap->demux.start_feed = pvr2_dvb_start_feed; + adap->demux.stop_feed = pvr2_dvb_stop_feed; + adap->demux.write_to_decoder = NULL; + + ret = dvb_dmx_init(&adap->demux); + if (ret < 0) { + err("dvb_dmx_init failed: error %d", ret); + goto err_dmx; + } + + adap->dmxdev.filternum = adap->demux.filternum; + adap->dmxdev.demux = &adap->demux.dmx; + adap->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap); + if (ret < 0) { + err("dvb_dmxdev_init failed: error %d", ret); + goto err_dmx_dev; + } + + dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx); + + return 0; + +err_dmx_dev: + dvb_dmx_release(&adap->demux); +err_dmx: + dvb_unregister_adapter(&adap->dvb_adap); +err: + return ret; +} + +static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap) +{ + printk(KERN_DEBUG "unregistering DVB devices\n"); + dvb_net_release(&adap->dvb_net); + adap->demux.dmx.close(&adap->demux.dmx); + dvb_dmxdev_release(&adap->dmxdev); + dvb_dmx_release(&adap->demux); + dvb_unregister_adapter(&adap->dvb_adap); + return 0; +} + +static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap) +{ + struct pvr2_hdw *hdw = adap->channel.hdw; + struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props; + int ret = 0; + + if (dvb_props == NULL) { + err("fe_props not defined!"); + return -EINVAL; + } + + ret = pvr2_channel_limit_inputs( + &adap->channel, + (1 << PVR2_CVAL_INPUT_DTV)); + if (ret) { + err("failed to grab control of dtv input (code=%d)", + ret); + return ret; + } + + if (dvb_props->frontend_attach == NULL) { + err("frontend_attach not defined!"); + ret = -EINVAL; + goto done; + } + + if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) { + + if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) { + err("frontend registration failed!"); + dvb_frontend_detach(adap->fe); + adap->fe = NULL; + ret = -ENODEV; + goto done; + } + + if (dvb_props->tuner_attach) + dvb_props->tuner_attach(adap); + + if (adap->fe->ops.analog_ops.standby) + adap->fe->ops.analog_ops.standby(adap->fe); + + /* Ensure all frontends negotiate bus access */ + adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl; + + } else { + err("no frontend was attached!"); + ret = -ENODEV; + return ret; + } + + done: + pvr2_channel_limit_inputs(&adap->channel, 0); + return ret; +} + +static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap) +{ + if (adap->fe != NULL) { + dvb_unregister_frontend(adap->fe); + dvb_frontend_detach(adap->fe); + } + return 0; +} + +static void pvr2_dvb_destroy(struct pvr2_dvb_adapter *adap) +{ + pvr2_dvb_stream_end(adap); + pvr2_dvb_frontend_exit(adap); + pvr2_dvb_adapter_exit(adap); + pvr2_channel_done(&adap->channel); + kfree(adap); +} + +static void pvr2_dvb_internal_check(struct pvr2_channel *chp) +{ + struct pvr2_dvb_adapter *adap; + adap = container_of(chp, struct pvr2_dvb_adapter, channel); + if (!adap->channel.mc_head->disconnect_flag) return; + pvr2_dvb_destroy(adap); +} + +struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr) +{ + int ret = 0; + struct pvr2_dvb_adapter *adap; + if (!pvr->hdw->hdw_desc->dvb_props) { + /* Device lacks a digital interface so don't set up + the DVB side of the driver either. For now. */ + return NULL; + } + adap = kzalloc(sizeof(*adap), GFP_KERNEL); + if (!adap) return adap; + pvr2_channel_init(&adap->channel, pvr); + adap->channel.check_func = pvr2_dvb_internal_check; + init_waitqueue_head(&adap->buffer_wait_data); + mutex_init(&adap->lock); + ret = pvr2_dvb_adapter_init(adap); + if (ret < 0) goto fail1; + ret = pvr2_dvb_frontend_init(adap); + if (ret < 0) goto fail2; + return adap; + +fail2: + pvr2_dvb_adapter_exit(adap); +fail1: + pvr2_channel_done(&adap->channel); + return NULL; +} + diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.h b/drivers/media/video/pvrusb2/pvrusb2-dvb.h new file mode 100644 index 00000000000..884ff916a35 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.h @@ -0,0 +1,41 @@ +#ifndef __PVRUSB2_DVB_H__ +#define __PVRUSB2_DVB_H__ + +#include "dvb_frontend.h" +#include "dvb_demux.h" +#include "dvb_net.h" +#include "dmxdev.h" +#include "pvrusb2-context.h" + +#define PVR2_DVB_BUFFER_COUNT 32 +#define PVR2_DVB_BUFFER_SIZE PAGE_ALIGN(0x4000) + +struct pvr2_dvb_adapter { + struct pvr2_channel channel; + + struct dvb_adapter dvb_adap; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dvb_net dvb_net; + struct dvb_frontend *fe; + + int feedcount; + int max_feed_count; + + struct task_struct *thread; + struct mutex lock; + + unsigned int stream_run:1; + + wait_queue_head_t buffer_wait_data; + char *buffer_storage[PVR2_DVB_BUFFER_COUNT]; +}; + +struct pvr2_dvb_props { + int (*frontend_attach) (struct pvr2_dvb_adapter *); + int (*tuner_attach) (struct pvr2_dvb_adapter *); +}; + +struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr); + +#endif /* __PVRUSB2_DVB_H__ */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 64062879981..c46d367f747 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -278,11 +278,20 @@ static int pvr2_encoder_cmd(void *ctxt, ret = -EBUSY; } if (ret) { + del_timer_sync(&hdw->encoder_run_timer); hdw->state_encoder_ok = 0; pvr2_trace(PVR2_TRACE_STBITS, "State bit %s <-- %s", "state_encoder_ok", (hdw->state_encoder_ok ? "true" : "false")); + if (hdw->state_encoder_runok) { + hdw->state_encoder_runok = 0; + pvr2_trace(PVR2_TRACE_STBITS, + "State bit %s <-- %s", + "state_encoder_runok", + (hdw->state_encoder_runok ? + "true" : "false")); + } pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Giving up on command." @@ -480,10 +489,6 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw) /* unmask some interrupts */ pvr2_write_register(hdw, 0x0048, 0xbfffffff); - /* change some GPIO data */ - pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000481); - pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000); - pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1, hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); @@ -526,12 +531,6 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw) break; } - /* change some GPIO data */ - /* Note: Bit d7 of dir appears to control the LED. So we shut it - off here. */ - pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401); - pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000); - return status; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h index ffbc6d09610..abaada31e66 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h +++ b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h @@ -22,32 +22,41 @@ #ifndef _PVRUSB2_FX2_CMD_H_ #define _PVRUSB2_FX2_CMD_H_ -#define FX2CMD_MEM_WRITE_DWORD 0x01 -#define FX2CMD_MEM_READ_DWORD 0x02 +#define FX2CMD_MEM_WRITE_DWORD 0x01u +#define FX2CMD_MEM_READ_DWORD 0x02u -#define FX2CMD_MEM_READ_64BYTES 0x28 +#define FX2CMD_MEM_READ_64BYTES 0x28u -#define FX2CMD_REG_WRITE 0x04 -#define FX2CMD_REG_READ 0x05 -#define FX2CMD_MEMSEL 0x06 +#define FX2CMD_REG_WRITE 0x04u +#define FX2CMD_REG_READ 0x05u +#define FX2CMD_MEMSEL 0x06u -#define FX2CMD_I2C_WRITE 0x08 -#define FX2CMD_I2C_READ 0x09 +#define FX2CMD_I2C_WRITE 0x08u +#define FX2CMD_I2C_READ 0x09u -#define FX2CMD_GET_USB_SPEED 0x0b +#define FX2CMD_GET_USB_SPEED 0x0bu -#define FX2CMD_STREAMING_ON 0x36 -#define FX2CMD_STREAMING_OFF 0x37 +#define FX2CMD_STREAMING_ON 0x36u +#define FX2CMD_STREAMING_OFF 0x37u -#define FX2CMD_FWPOST1 0x52 +#define FX2CMD_FWPOST1 0x52u -#define FX2CMD_POWER_OFF 0xdc -#define FX2CMD_POWER_ON 0xde +#define FX2CMD_POWER_OFF 0xdcu +#define FX2CMD_POWER_ON 0xdeu -#define FX2CMD_DEEP_RESET 0xdd +#define FX2CMD_DEEP_RESET 0xddu -#define FX2CMD_GET_EEPROM_ADDR 0xeb -#define FX2CMD_GET_IR_CODE 0xec +#define FX2CMD_GET_EEPROM_ADDR 0xebu +#define FX2CMD_GET_IR_CODE 0xecu + +#define FX2CMD_HCW_DEMOD_RESETIN 0xf0u +#define FX2CMD_HCW_DTV_STREAMING_ON 0xf1u +#define FX2CMD_HCW_DTV_STREAMING_OFF 0xf2u + +#define FX2CMD_ONAIR_DTV_STREAMING_ON 0xa0u +#define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1u +#define FX2CMD_ONAIR_DTV_POWER_ON 0xa2u +#define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3u #endif /* _PVRUSB2_FX2_CMD_H_ */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index d7a216b41b7..a3fe251d6fd 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -163,6 +163,11 @@ struct pvr2_decoder_ctrl { #define FW1_STATE_RELOAD 3 #define FW1_STATE_OK 4 +/* What state the device is in if it is a hybrid */ +#define PVR2_PATHWAY_UNKNOWN 0 +#define PVR2_PATHWAY_ANALOG 1 +#define PVR2_PATHWAY_DIGITAL 2 + typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16); #define PVR2_I2C_FUNC_CNT 128 @@ -182,7 +187,6 @@ struct pvr2_hdw { struct workqueue_struct *workqueue; struct work_struct workpoll; /* Update driver state */ struct work_struct worki2csync; /* Update i2c clients */ - struct work_struct workinit; /* Driver initialization sequence */ /* Video spigot */ struct pvr2_stream *vid_stream; @@ -229,17 +233,19 @@ struct pvr2_hdw { /* Bits of state that describe what is going on with various parts of the driver. */ + int state_pathway_ok; /* Pathway config is ok */ int state_encoder_ok; /* Encoder is operational */ int state_encoder_run; /* Encoder is running */ int state_encoder_config; /* Encoder is configured */ int state_encoder_waitok; /* Encoder pre-wait done */ + int state_encoder_runok; /* Encoder has run for >= .25 sec */ int state_decoder_run; /* Decoder is running */ int state_usbstream_run; /* FX2 is streaming */ int state_decoder_quiescent; /* Decoder idle for > 50msec */ int state_pipeline_config; /* Pipeline is configured */ - int state_pipeline_req; /* Somebody wants to stream */ - int state_pipeline_pause; /* Pipeline must be paused */ - int state_pipeline_idle; /* Pipeline not running */ + int state_pipeline_req; /* Somebody wants to stream */ + int state_pipeline_pause; /* Pipeline must be paused */ + int state_pipeline_idle; /* Pipeline not running */ /* This is the master state of the driver. It is the combined result of other bits of state. Examining this will indicate the @@ -247,6 +253,9 @@ struct pvr2_hdw { PVR2_STATE_xxxx */ unsigned int master_state; + /* True if device led is currently on */ + int led_on; + /* True if states must be re-evaluated */ int state_stale; @@ -259,6 +268,9 @@ struct pvr2_hdw { /* Timer for measuring encoder pre-wait time */ struct timer_list encoder_wait_timer; + /* Timer for measuring encoder minimum run time */ + struct timer_list encoder_run_timer; + /* Place to block while waiting for state changes */ wait_queue_head_t state_wait_data; @@ -267,6 +279,7 @@ struct pvr2_hdw { int flag_disconnected; /* flag_ok == 0 due to disconnect */ int flag_init_ok; /* true if structure is fully initialized */ int fw1_state; /* current situation with fw1 */ + int pathway_state; /* one of PVR2_PATHWAY_xxx */ int flag_decoder_missed;/* We've noticed missing decoder */ int flag_tripped; /* Indicates overall failure to start */ @@ -323,6 +336,11 @@ struct pvr2_hdw { int v4l_minor_number_vbi; int v4l_minor_number_radio; + /* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */ + unsigned int input_avail_mask; + /* Bit mask of PVR2_CVAL_INPUT choices which are currenly allowed */ + unsigned int input_allowed_mask; + /* Location of eeprom or a negative number if none */ int eeprom_addr; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index d6955fa3959..0a868888f38 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -25,7 +25,6 @@ #include <linux/firmware.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> -#include <asm/semaphore.h> #include "pvrusb2.h" #include "pvrusb2-std.h" #include "pvrusb2-util.h" @@ -44,13 +43,13 @@ static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL}; static DEFINE_MUTEX(pvr2_unit_mtx); -static int ctlchg = 0; +static int ctlchg; static int initusbreset = 1; -static int procreload = 0; +static int procreload; static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; -static int init_pause_msec = 0; +static int init_pause_msec; module_param(ctlchg, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value"); @@ -183,6 +182,7 @@ static const char *control_values_srate[] = { static const char *control_values_input[] = { [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/ + [PVR2_CVAL_INPUT_DTV] = "dtv", [PVR2_CVAL_INPUT_RADIO] = "radio", [PVR2_CVAL_INPUT_SVIDEO] = "s-video", [PVR2_CVAL_INPUT_COMPOSITE] = "composite", @@ -216,12 +216,45 @@ static const char *pvr2_state_names[] = { }; +struct pvr2_fx2cmd_descdef { + unsigned char id; + unsigned char *desc; +}; + +static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = { + {FX2CMD_MEM_WRITE_DWORD, "write encoder dword"}, + {FX2CMD_MEM_READ_DWORD, "read encoder dword"}, + {FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"}, + {FX2CMD_REG_WRITE, "write encoder register"}, + {FX2CMD_REG_READ, "read encoder register"}, + {FX2CMD_MEMSEL, "encoder memsel"}, + {FX2CMD_I2C_WRITE, "i2c write"}, + {FX2CMD_I2C_READ, "i2c read"}, + {FX2CMD_GET_USB_SPEED, "get USB speed"}, + {FX2CMD_STREAMING_ON, "stream on"}, + {FX2CMD_STREAMING_OFF, "stream off"}, + {FX2CMD_FWPOST1, "fwpost1"}, + {FX2CMD_POWER_OFF, "power off"}, + {FX2CMD_POWER_ON, "power on"}, + {FX2CMD_DEEP_RESET, "deep reset"}, + {FX2CMD_GET_EEPROM_ADDR, "get rom addr"}, + {FX2CMD_GET_IR_CODE, "get IR code"}, + {FX2CMD_HCW_DEMOD_RESETIN, "hcw demod resetin"}, + {FX2CMD_HCW_DTV_STREAMING_ON, "hcw dtv stream on"}, + {FX2CMD_HCW_DTV_STREAMING_OFF, "hcw dtv stream off"}, + {FX2CMD_ONAIR_DTV_STREAMING_ON, "onair dtv stream on"}, + {FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"}, + {FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"}, + {FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"}, +}; + + +static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v); static void pvr2_hdw_state_sched(struct pvr2_hdw *); static int pvr2_hdw_state_eval(struct pvr2_hdw *); static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); static void pvr2_hdw_worker_i2c(struct work_struct *work); static void pvr2_hdw_worker_poll(struct work_struct *work); -static void pvr2_hdw_worker_init(struct work_struct *work); static int pvr2_hdw_wait(struct pvr2_hdw *,int state); static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *); static void pvr2_hdw_state_log_state(struct pvr2_hdw *); @@ -232,6 +265,8 @@ static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); static void pvr2_hdw_quiescent_timeout(unsigned long); static void pvr2_hdw_encoder_wait_timeout(unsigned long); +static void pvr2_hdw_encoder_run_timeout(unsigned long); +static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32); static int pvr2_send_request_ex(struct pvr2_hdw *hdw, unsigned int timeout,int probe_fl, void *write_data,unsigned int write_len, @@ -368,26 +403,14 @@ static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) return 0; } -static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) +static int ctrl_check_input(struct pvr2_ctrl *cptr,int v) { - struct pvr2_hdw *hdw = cptr->hdw; - - if (hdw->input_val != v) { - hdw->input_val = v; - hdw->input_dirty = !0; - } + return ((1 << v) & cptr->hdw->input_allowed_mask) != 0; +} - /* Handle side effects - if we switch to a mode that needs the RF - tuner, then select the right frequency choice as well and mark - it dirty. */ - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - hdw->freqSelector = 0; - hdw->freqDirty = !0; - } else if (hdw->input_val == PVR2_CVAL_INPUT_TV) { - hdw->freqSelector = 1; - hdw->freqDirty = !0; - } - return 0; +static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) +{ + return pvr2_hdw_set_input(cptr->hdw,v); } static int ctrl_isdirty_input(struct pvr2_ctrl *cptr) @@ -804,6 +827,7 @@ static const struct pvr2_ctl_info control_defs[] = { .name = "input", .internal_id = PVR2_CID_INPUT, .default_value = PVR2_CVAL_INPUT_TV, + .check_value = ctrl_check_input, DEFREF(input), DEFENUM(control_values_input), },{ @@ -983,7 +1007,7 @@ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) /* Set the currently tuned frequency and account for all possible driver-core side effects of this action. */ -void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) +static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) { if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { if (hdw->freqSelector) { @@ -1196,6 +1220,14 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) time we configure the encoder, then we'll fully configure it. */ hdw->enc_cur_valid = 0; + /* Encoder is about to be reset so note that as far as we're + concerned now, the encoder has never been run. */ + del_timer_sync(&hdw->encoder_run_timer); + if (hdw->state_encoder_runok) { + hdw->state_encoder_runok = 0; + trace_stbit("state_encoder_runok",hdw->state_encoder_runok); + } + /* First prepare firmware loading */ ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ @@ -1213,19 +1245,14 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/ - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_FWPOST1; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - hdw->cmd_buffer[0] = FX2CMD_MEMSEL; - hdw->cmd_buffer[1] = 0; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_FWPOST1); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload prep failed, ret=%d",ret); release_firmware(fw_entry); - return ret; + goto done; } /* Now send firmware */ @@ -1238,7 +1265,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) " must be a multiple of %zu bytes", fw_files[fwidx],sizeof(u32)); release_firmware(fw_entry); - return -1; + ret = -EINVAL; + goto done; } fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL); @@ -1246,7 +1274,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) release_firmware(fw_entry); pvr2_trace(PVR2_TRACE_ERROR_LEGS, "failed to allocate memory for firmware2 upload"); - return -ENOMEM; + ret = -ENOMEM; + goto done; } pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT); @@ -1277,23 +1306,27 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload transfer failure"); - return ret; + goto done; } /* Finish upload */ ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/ - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_MEMSEL; - hdw->cmd_buffer[1] = 0; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload post-proc failure"); } + + done: + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + /* Ensure that GPIO 11 is set to output for GOTVIEW + hardware. */ + pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); + } return ret; } @@ -1365,11 +1398,6 @@ int pvr2_hdw_untrip(struct pvr2_hdw *hdw) } -const char *pvr2_hdw_get_state_name(unsigned int id) -{ - if (id >= ARRAY_SIZE(pvr2_state_names)) return NULL; - return pvr2_state_names[id]; -} int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw) @@ -1496,7 +1524,7 @@ struct pvr2_std_hack { default - which can always be overridden explicitly - and if the user has otherwise named a default then that default will always be used in place of this table. */ -const static struct pvr2_std_hack std_eeprom_maps[] = { +static const struct pvr2_std_hack std_eeprom_maps[] = { { /* PAL(B/G) */ .pat = V4L2_STD_B|V4L2_STD_GH, .std = V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_PAL_G, @@ -1713,6 +1741,13 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + /* Ensure that GPIO 11 is set to output for GOTVIEW + hardware. */ + pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); + } + pvr2_hdw_commit_setup(hdw); hdw->vid_stream = pvr2_stream_create(); @@ -1806,12 +1841,37 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw) } -/* Create and return a structure for interacting with the underlying - hardware */ +/* Perform second stage initialization. Set callback pointer first so that + we can avoid a possible initialization race (if the kernel thread runs + before the callback has been set). */ +int pvr2_hdw_initialize(struct pvr2_hdw *hdw, + void (*callback_func)(void *), + void *callback_data) +{ + LOCK_TAKE(hdw->big_lock); do { + if (hdw->flag_disconnected) { + /* Handle a race here: If we're already + disconnected by this point, then give up. If we + get past this then we'll remain connected for + the duration of initialization since the entire + initialization sequence is now protected by the + big_lock. */ + break; + } + hdw->state_data = callback_data; + hdw->state_func = callback_func; + pvr2_hdw_setup(hdw); + } while (0); LOCK_GIVE(hdw->big_lock); + return hdw->flag_init_ok; +} + + +/* Create, set up, and return a structure for interacting with the + underlying hardware. */ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, const struct usb_device_id *devid) { - unsigned int idx,cnt1,cnt2; + unsigned int idx,cnt1,cnt2,m; struct pvr2_hdw *hdw; int valid_std_mask; struct pvr2_ctrl *cptr; @@ -1835,6 +1895,10 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->encoder_wait_timer.data = (unsigned long)hdw; hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout; + init_timer(&hdw->encoder_run_timer); + hdw->encoder_run_timer.data = (unsigned long)hdw; + hdw->encoder_run_timer.function = pvr2_hdw_encoder_run_timeout; + hdw->master_state = PVR2_STATE_DEAD; init_waitqueue_head(&hdw->state_wait_data); @@ -1842,6 +1906,26 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->tuner_signal_stale = !0; cx2341x_fill_defaults(&hdw->enc_ctl_state); + /* Calculate which inputs are OK */ + m = 0; + if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV; + if (hdw_desc->digital_control_scheme != PVR2_DIGITAL_SCHEME_NONE) { + m |= 1 << PVR2_CVAL_INPUT_DTV; + } + if (hdw_desc->flag_has_svideo) m |= 1 << PVR2_CVAL_INPUT_SVIDEO; + if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE; + if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO; + hdw->input_avail_mask = m; + hdw->input_allowed_mask = hdw->input_avail_mask; + + /* If not a hybrid device, pathway_state never changes. So + initialize it here to what it should forever be. */ + if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_DTV))) { + hdw->pathway_state = PVR2_PATHWAY_ANALOG; + } else if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_TV))) { + hdw->pathway_state = PVR2_PATHWAY_DIGITAL; + } + hdw->control_cnt = CTRLDEF_COUNT; hdw->control_cnt += MPEGDEF_COUNT; hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, @@ -1859,6 +1943,15 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, cptr = hdw->controls + idx; cptr->info = control_defs+idx; } + + /* Ensure that default input choice is a valid one. */ + m = hdw->input_avail_mask; + if (m) for (idx = 0; idx < (sizeof(m) << 3); idx++) { + if (!((1 << idx) & m)) continue; + hdw->input_val = idx; + break; + } + /* Define and configure additional controls from cx2341x module. */ hdw->mpeg_ctrl_info = kzalloc( sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT, GFP_KERNEL); @@ -1982,7 +2075,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->workqueue = create_singlethread_workqueue(hdw->name); INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll); INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c); - INIT_WORK(&hdw->workinit,pvr2_hdw_worker_init); pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s", hdw->unit_number,hdw->name); @@ -2004,11 +2096,11 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, mutex_init(&hdw->ctl_lock_mutex); mutex_init(&hdw->big_lock_mutex); - queue_work(hdw->workqueue,&hdw->workinit); return hdw; fail: if (hdw) { del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_run_timer); del_timer_sync(&hdw->encoder_wait_timer); if (hdw->workqueue) { flush_workqueue(hdw->workqueue); @@ -2065,13 +2157,14 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) { if (!hdw) return; pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw); - del_timer_sync(&hdw->quiescent_timer); - del_timer_sync(&hdw->encoder_wait_timer); if (hdw->workqueue) { flush_workqueue(hdw->workqueue); destroy_workqueue(hdw->workqueue); hdw->workqueue = NULL; } + del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_run_timer); + del_timer_sync(&hdw->encoder_wait_timer); if (hdw->fw_buffer) { kfree(hdw->fw_buffer); hdw->fw_buffer = NULL; @@ -2353,6 +2446,18 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) } } + if (hdw->input_dirty && hdw->state_pathway_ok && + (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ? + PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != + hdw->pathway_state)) { + /* Change of mode being asked for... */ + hdw->state_pathway_ok = 0; + trace_stbit("state_pathway_ok",hdw->state_pathway_ok); + } + if (!hdw->state_pathway_ok) { + /* Can't commit anything until pathway is ok. */ + return 0; + } /* If any of the below has changed, then we can't do the update while the pipeline is running. Pipeline must be paused first and decoder -> encoder connection be made quiescent before we @@ -2406,12 +2511,28 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) hdw->active_stream_type = hdw->desired_stream_type; } + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + u32 b; + /* Handle GOTVIEW audio switching */ + pvr2_hdw_gpio_get_out(hdw,&b); + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + /* Set GPIO 11 */ + pvr2_hdw_gpio_chg_out(hdw,(1 << 11),~0); + } else { + /* Clear GPIO 11 */ + pvr2_hdw_gpio_chg_out(hdw,(1 << 11),0); + } + } + /* Now execute i2c core update */ pvr2_i2c_core_sync(hdw); - if (hdw->state_encoder_run) { - /* If encoder isn't running, then this will get worked out - later when we start the encoder. */ + if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) && + hdw->state_encoder_run) { + /* If encoder isn't running or it can't be touched, then + this will get worked out later when we start the + encoder. */ if (pvr2_encoder_adjust(hdw) < 0) return !0; } @@ -2454,15 +2575,6 @@ static void pvr2_hdw_worker_poll(struct work_struct *work) } -static void pvr2_hdw_worker_init(struct work_struct *work) -{ - struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workinit); - LOCK_TAKE(hdw->big_lock); do { - pvr2_hdw_setup(hdw); - } while (0); LOCK_GIVE(hdw->big_lock); -} - - static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state) { return wait_event_interruptible( @@ -2472,17 +2584,6 @@ static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state) } -void pvr2_hdw_set_state_callback(struct pvr2_hdw *hdw, - void (*callback_func)(void *), - void *callback_data) -{ - LOCK_TAKE(hdw->big_lock); do { - hdw->state_data = callback_data; - hdw->state_func = callback_func; - } while (0); LOCK_GIVE(hdw->big_lock); -} - - /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) { @@ -3051,6 +3152,67 @@ int pvr2_send_request(struct pvr2_hdw *hdw, read_data,read_len); } + +static int pvr2_issue_simple_cmd(struct pvr2_hdw *hdw,u32 cmdcode) +{ + int ret; + unsigned int cnt = 1; + unsigned int args = 0; + LOCK_TAKE(hdw->ctl_lock); + hdw->cmd_buffer[0] = cmdcode & 0xffu; + args = (cmdcode >> 8) & 0xffu; + args = (args > 2) ? 2 : args; + if (args) { + cnt += args; + hdw->cmd_buffer[1] = (cmdcode >> 16) & 0xffu; + if (args > 1) { + hdw->cmd_buffer[2] = (cmdcode >> 24) & 0xffu; + } + } + if (pvrusb2_debug & PVR2_TRACE_INIT) { + unsigned int idx; + unsigned int ccnt,bcnt; + char tbuf[50]; + cmdcode &= 0xffu; + bcnt = 0; + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + "Sending FX2 command 0x%x",cmdcode); + bcnt += ccnt; + for (idx = 0; idx < ARRAY_SIZE(pvr2_fx2cmd_desc); idx++) { + if (pvr2_fx2cmd_desc[idx].id == cmdcode) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + " \"%s\"", + pvr2_fx2cmd_desc[idx].desc); + bcnt += ccnt; + break; + } + } + if (args) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + " (%u",hdw->cmd_buffer[1]); + bcnt += ccnt; + if (args > 1) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + ",%u",hdw->cmd_buffer[2]); + bcnt += ccnt; + } + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + ")"); + bcnt += ccnt; + } + pvr2_trace(PVR2_TRACE_INIT,"%.*s",bcnt,tbuf); + } + ret = pvr2_send_request(hdw,hdw->cmd_buffer,cnt,NULL,0); + LOCK_GIVE(hdw->ctl_lock); + return ret; +} + + int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data) { int ret; @@ -3158,25 +3320,19 @@ void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val) int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) { - int status; - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset"); - hdw->cmd_buffer[0] = FX2CMD_DEEP_RESET; - status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET); } int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) { - int status; - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT,"Requesting powerup"); - hdw->cmd_buffer[0] = FX2CMD_POWER_ON; - status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON); +} + + +int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw) +{ + return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_OFF); } @@ -3201,16 +3357,173 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) } +static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) +{ + hdw->flag_ok = !0; + return pvr2_issue_simple_cmd(hdw, + FX2CMD_HCW_DEMOD_RESETIN | + (1 << 8) | + ((onoff ? 1 : 0) << 16)); +} + + +static int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff) +{ + hdw->flag_ok = !0; + return pvr2_issue_simple_cmd(hdw,(onoff ? + FX2CMD_ONAIR_DTV_POWER_ON : + FX2CMD_ONAIR_DTV_POWER_OFF)); +} + + +static int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw, + int onoff) +{ + return pvr2_issue_simple_cmd(hdw,(onoff ? + FX2CMD_ONAIR_DTV_STREAMING_ON : + FX2CMD_ONAIR_DTV_STREAMING_OFF)); +} + + +static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) +{ + int cmode; + /* Compare digital/analog desired setting with current setting. If + they don't match, fix it... */ + cmode = (digitalFl ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG); + if (cmode == hdw->pathway_state) { + /* They match; nothing to do */ + return; + } + + switch (hdw->hdw_desc->digital_control_scheme) { + case PVR2_DIGITAL_SCHEME_HAUPPAUGE: + pvr2_hdw_cmd_hcw_demod_reset(hdw,digitalFl); + if (cmode == PVR2_PATHWAY_ANALOG) { + /* If moving to analog mode, also force the decoder + to reset. If no decoder is attached, then it's + ok to ignore this because if/when the decoder + attaches, it will reset itself at that time. */ + pvr2_hdw_cmd_decoder_reset(hdw); + } + break; + case PVR2_DIGITAL_SCHEME_ONAIR: + /* Supposedly we should always have the power on whether in + digital or analog mode. But for now do what appears to + work... */ + pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl); + break; + default: break; + } + + pvr2_hdw_untrip_unlocked(hdw); + hdw->pathway_state = cmode; +} + + +void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff) +{ + /* change some GPIO data + * + * note: bit d7 of dir appears to control the LED, + * so we shut it off here. + * + */ + if (onoff) { + pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000481); + } else { + pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000401); + } + pvr2_hdw_gpio_chg_out(hdw, 0xffffffff, 0x00000000); +} + + +typedef void (*led_method_func)(struct pvr2_hdw *,int); + +static led_method_func led_methods[] = { + [PVR2_LED_SCHEME_HAUPPAUGE] = pvr2_led_ctrl_hauppauge, +}; + + +/* Toggle LED */ +static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff) +{ + unsigned int scheme_id; + led_method_func fp; + + if ((!onoff) == (!hdw->led_on)) return; + + hdw->led_on = onoff != 0; + + scheme_id = hdw->hdw_desc->led_scheme; + if (scheme_id < ARRAY_SIZE(led_methods)) { + fp = led_methods[scheme_id]; + } else { + fp = NULL; + } + + if (fp) (*fp)(hdw,onoff); +} + + /* Stop / start video stream transport */ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { - int status; - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = - (runFl ? FX2CMD_STREAMING_ON : FX2CMD_STREAMING_OFF); - status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + int ret; + + /* If we're in analog mode, then just issue the usual analog + command. */ + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + return pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF)); + /*Note: Not reached */ + } + + if (hdw->pathway_state != PVR2_PATHWAY_DIGITAL) { + /* Whoops, we don't know what mode we're in... */ + return -EINVAL; + } + + /* To get here we have to be in digital mode. The mechanism here + is unfortunately different for different vendors. So we switch + on the device's digital scheme attribute in order to figure out + what to do. */ + switch (hdw->hdw_desc->digital_control_scheme) { + case PVR2_DIGITAL_SCHEME_HAUPPAUGE: + return pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_HCW_DTV_STREAMING_ON : + FX2CMD_HCW_DTV_STREAMING_OFF)); + case PVR2_DIGITAL_SCHEME_ONAIR: + ret = pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF)); + if (ret) return ret; + return pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,runFl); + default: + return -EINVAL; + } +} + + +/* Evaluate whether or not state_pathway_ok can change */ +static int state_eval_pathway_ok(struct pvr2_hdw *hdw) +{ + if (hdw->state_pathway_ok) { + /* Nothing to do if pathway is already ok */ + return 0; + } + if (!hdw->state_pipeline_idle) { + /* Not allowed to change anything if pipeline is not idle */ + return 0; + } + pvr2_hdw_cmd_modeswitch(hdw,hdw->input_val == PVR2_CVAL_INPUT_DTV); + hdw->state_pathway_ok = !0; + trace_stbit("state_pathway_ok",hdw->state_pathway_ok); + return !0; } @@ -3223,6 +3536,12 @@ static int state_eval_encoder_ok(struct pvr2_hdw *hdw) if (hdw->state_encoder_config) return 0; if (hdw->state_decoder_run) return 0; if (hdw->state_usbstream_run) return 0; + if (hdw->pathway_state == PVR2_PATHWAY_DIGITAL) { + if (!hdw->hdw_desc->flag_digital_requires_cx23416) return 0; + } else if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) { + return 0; + } + if (pvr2_upload_firmware2(hdw) < 0) { hdw->flag_tripped = !0; trace_stbit("flag_tripped",hdw->flag_tripped); @@ -3248,7 +3567,9 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw) /* paranoia - solve race if timer just completed */ del_timer_sync(&hdw->encoder_wait_timer); } else { - if (!hdw->state_encoder_ok || + if (!hdw->state_pathway_ok || + (hdw->pathway_state != PVR2_PATHWAY_ANALOG) || + !hdw->state_encoder_ok || !hdw->state_pipeline_idle || hdw->state_pipeline_pause || !hdw->state_pipeline_req || @@ -3297,20 +3618,116 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw) } +/* Return true if the encoder should not be running. */ +static int state_check_disable_encoder_run(struct pvr2_hdw *hdw) +{ + if (!hdw->state_encoder_ok) { + /* Encoder isn't healthy at the moment, so stop it. */ + return !0; + } + if (!hdw->state_pathway_ok) { + /* Mode is not understood at the moment (i.e. it wants to + change), so encoder must be stopped. */ + return !0; + } + + switch (hdw->pathway_state) { + case PVR2_PATHWAY_ANALOG: + if (!hdw->state_decoder_run) { + /* We're in analog mode and the decoder is not + running; thus the encoder should be stopped as + well. */ + return !0; + } + break; + case PVR2_PATHWAY_DIGITAL: + if (hdw->state_encoder_runok) { + /* This is a funny case. We're in digital mode so + really the encoder should be stopped. However + if it really is running, only kill it after + runok has been set. This gives a chance for the + onair quirk to function (encoder must run + briefly first, at least once, before onair + digital streaming can work). */ + return !0; + } + break; + default: + /* Unknown mode; so encoder should be stopped. */ + return !0; + } + + /* If we get here, we haven't found a reason to stop the + encoder. */ + return 0; +} + + +/* Return true if the encoder should be running. */ +static int state_check_enable_encoder_run(struct pvr2_hdw *hdw) +{ + if (!hdw->state_encoder_ok) { + /* Don't run the encoder if it isn't healthy... */ + return 0; + } + if (!hdw->state_pathway_ok) { + /* Don't run the encoder if we don't (yet) know what mode + we need to be in... */ + return 0; + } + + switch (hdw->pathway_state) { + case PVR2_PATHWAY_ANALOG: + if (hdw->state_decoder_run) { + /* In analog mode, if the decoder is running, then + run the encoder. */ + return !0; + } + break; + case PVR2_PATHWAY_DIGITAL: + if ((hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_ONAIR) && + !hdw->state_encoder_runok) { + /* This is a quirk. OnAir hardware won't stream + digital until the encoder has been run at least + once, for a minimal period of time (empiricially + measured to be 1/4 second). So if we're on + OnAir hardware and the encoder has never been + run at all, then start the encoder. Normal + state machine logic in the driver will + automatically handle the remaining bits. */ + return !0; + } + break; + default: + /* For completeness (unknown mode; encoder won't run ever) */ + break; + } + /* If we get here, then we haven't found any reason to run the + encoder, so don't run it. */ + return 0; +} + + /* Evaluate whether or not state_encoder_run can change */ static int state_eval_encoder_run(struct pvr2_hdw *hdw) { if (hdw->state_encoder_run) { + if (!state_check_disable_encoder_run(hdw)) return 0; if (hdw->state_encoder_ok) { - if (hdw->state_decoder_run) return 0; + del_timer_sync(&hdw->encoder_run_timer); if (pvr2_encoder_stop(hdw) < 0) return !0; } hdw->state_encoder_run = 0; } else { - if (!hdw->state_encoder_ok) return 0; - if (!hdw->state_decoder_run) return 0; + if (!state_check_enable_encoder_run(hdw)) return 0; if (pvr2_encoder_start(hdw) < 0) return !0; hdw->state_encoder_run = !0; + if (!hdw->state_encoder_runok) { + hdw->encoder_run_timer.expires = + jiffies + (HZ*250/1000); + add_timer(&hdw->encoder_run_timer); + } } trace_stbit("state_encoder_run",hdw->state_encoder_run); return !0; @@ -3339,13 +3756,27 @@ static void pvr2_hdw_encoder_wait_timeout(unsigned long data) } +/* Timeout function for encoder run timer. */ +static void pvr2_hdw_encoder_run_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + if (!hdw->state_encoder_runok) { + hdw->state_encoder_runok = !0; + trace_stbit("state_encoder_runok",hdw->state_encoder_runok); + hdw->state_stale = !0; + queue_work(hdw->workqueue,&hdw->workpoll); + } +} + + /* Evaluate whether or not state_decoder_run can change */ static int state_eval_decoder_run(struct pvr2_hdw *hdw) { if (hdw->state_decoder_run) { if (hdw->state_encoder_ok) { if (hdw->state_pipeline_req && - !hdw->state_pipeline_pause) return 0; + !hdw->state_pipeline_pause && + hdw->state_pathway_ok) return 0; } if (!hdw->flag_decoder_missed) { pvr2_decoder_enable(hdw,0); @@ -3378,7 +3809,9 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) hopefully further stabilize the encoder. */ return 0; } - if (!hdw->state_pipeline_req || + if (!hdw->state_pathway_ok || + (hdw->pathway_state != PVR2_PATHWAY_ANALOG) || + !hdw->state_pipeline_req || hdw->state_pipeline_pause || !hdw->state_pipeline_config || !hdw->state_encoder_config || @@ -3399,16 +3832,43 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) static int state_eval_usbstream_run(struct pvr2_hdw *hdw) { if (hdw->state_usbstream_run) { - if (hdw->state_encoder_ok) { - if (hdw->state_encoder_run) return 0; + int fl = !0; + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + fl = (hdw->state_encoder_ok && + hdw->state_encoder_run); + } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + (hdw->hdw_desc->flag_digital_requires_cx23416)) { + fl = hdw->state_encoder_ok; + } + if (fl && + hdw->state_pipeline_req && + !hdw->state_pipeline_pause && + hdw->state_pathway_ok) { + return 0; } pvr2_hdw_cmd_usbstream(hdw,0); hdw->state_usbstream_run = 0; } else { - if (!hdw->state_encoder_ok || - !hdw->state_encoder_run || - !hdw->state_pipeline_req || - hdw->state_pipeline_pause) return 0; + if (!hdw->state_pipeline_req || + hdw->state_pipeline_pause || + !hdw->state_pathway_ok) return 0; + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + if (!hdw->state_encoder_ok || + !hdw->state_encoder_run) return 0; + } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + (hdw->hdw_desc->flag_digital_requires_cx23416)) { + if (!hdw->state_encoder_ok) return 0; + if (hdw->state_encoder_run) return 0; + if (hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_ONAIR) { + /* OnAir digital receivers won't stream + unless the analog encoder has run first. + Why? I have no idea. But don't even + try until we know the analog side is + known to have run. */ + if (!hdw->state_encoder_runok) return 0; + } + } if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0; hdw->state_usbstream_run = !0; } @@ -3454,7 +3914,8 @@ static int state_update_pipeline_state(struct pvr2_hdw *hdw) typedef int (*state_eval_func)(struct pvr2_hdw *); /* Set of functions to be run to evaluate various states in the driver. */ -const static state_eval_func eval_funcs[] = { +static const state_eval_func eval_funcs[] = { + state_eval_pathway_ok, state_eval_pipeline_config, state_eval_encoder_ok, state_eval_encoder_config, @@ -3502,6 +3963,34 @@ static int pvr2_hdw_state_update(struct pvr2_hdw *hdw) } +static unsigned int print_input_mask(unsigned int msk, + char *buf,unsigned int acnt) +{ + unsigned int idx,ccnt; + unsigned int tcnt = 0; + for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) { + if (!((1 << idx) & msk)) continue; + ccnt = scnprintf(buf+tcnt, + acnt-tcnt, + "%s%s", + (tcnt ? ", " : ""), + control_values_input[idx]); + tcnt += ccnt; + } + return tcnt; +} + + +static const char *pvr2_pathway_state_name(int id) +{ + switch (id) { + case PVR2_PATHWAY_ANALOG: return "analog"; + case PVR2_PATHWAY_DIGITAL: return "digital"; + default: return "unknown"; + } +} + + static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, char *buf,unsigned int acnt) { @@ -3509,13 +3998,15 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, case 0: return scnprintf( buf,acnt, - "driver:%s%s%s%s%s", + "driver:%s%s%s%s%s <mode=%s>", (hdw->flag_ok ? " <ok>" : " <fail>"), (hdw->flag_init_ok ? " <init>" : " <uninitialized>"), (hdw->flag_disconnected ? " <disconnected>" : " <connected>"), (hdw->flag_tripped ? " <tripped>" : ""), - (hdw->flag_decoder_missed ? " <no decoder>" : "")); + (hdw->flag_decoder_missed ? " <no decoder>" : ""), + pvr2_pathway_state_name(hdw->pathway_state)); + case 1: return scnprintf( buf,acnt, @@ -3528,7 +4019,7 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, case 2: return scnprintf( buf,acnt, - "worker:%s%s%s%s%s%s", + "worker:%s%s%s%s%s%s%s", (hdw->state_decoder_run ? " <decode:run>" : (hdw->state_decoder_quiescent ? @@ -3538,20 +4029,65 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, (hdw->state_encoder_ok ? "" : " <encode:init>"), (hdw->state_encoder_run ? - " <encode:run>" : " <encode:stop>"), + (hdw->state_encoder_runok ? + " <encode:run>" : + " <encode:firstrun>") : + (hdw->state_encoder_runok ? + " <encode:stop>" : + " <encode:virgin>")), (hdw->state_encoder_config ? " <encode:configok>" : (hdw->state_encoder_waitok ? - "" : " <encode:wait>")), + "" : " <encode:waitok>")), (hdw->state_usbstream_run ? - " <usb:run>" : " <usb:stop>")); - break; + " <usb:run>" : " <usb:stop>"), + (hdw->state_pathway_ok ? + " <pathway:ok>" : "")); case 3: return scnprintf( buf,acnt, "state: %s", pvr2_get_state_name(hdw->master_state)); - break; + case 4: { + unsigned int tcnt = 0; + unsigned int ccnt; + + ccnt = scnprintf(buf, + acnt, + "Hardware supported inputs: "); + tcnt += ccnt; + tcnt += print_input_mask(hdw->input_avail_mask, + buf+tcnt, + acnt-tcnt); + if (hdw->input_avail_mask != hdw->input_allowed_mask) { + ccnt = scnprintf(buf+tcnt, + acnt-tcnt, + "; allowed inputs: "); + tcnt += ccnt; + tcnt += print_input_mask(hdw->input_allowed_mask, + buf+tcnt, + acnt-tcnt); + } + return tcnt; + } + case 5: { + struct pvr2_stream_stats stats; + if (!hdw->vid_stream) break; + pvr2_stream_get_stats(hdw->vid_stream, + &stats, + 0); + return scnprintf( + buf,acnt, + "Bytes streamed=%u" + " URBs: queued=%u idle=%u ready=%u" + " processed=%u failed=%u", + stats.bytes_processed, + stats.buffers_in_queue, + stats.buffers_in_idle, + stats.buffers_in_ready, + stats.buffers_processed, + stats.buffers_failed); + } default: break; } return 0; @@ -3597,6 +4133,7 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) unsigned int st; int state_updated = 0; int callback_flag = 0; + int analog_mode; pvr2_trace(PVR2_TRACE_STBITS, "Drive state check START"); @@ -3607,18 +4144,23 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) /* Process all state and get back over disposition */ state_updated = pvr2_hdw_state_update(hdw); + analog_mode = (hdw->pathway_state != PVR2_PATHWAY_DIGITAL); + /* Update master state based upon all other states. */ if (!hdw->flag_ok) { st = PVR2_STATE_DEAD; } else if (hdw->fw1_state != FW1_STATE_OK) { st = PVR2_STATE_COLD; - } else if (!hdw->state_encoder_ok) { + } else if ((analog_mode || + hdw->hdw_desc->flag_digital_requires_cx23416) && + !hdw->state_encoder_ok) { st = PVR2_STATE_WARM; - } else if (hdw->flag_tripped || hdw->flag_decoder_missed) { + } else if (hdw->flag_tripped || + (analog_mode && hdw->flag_decoder_missed)) { st = PVR2_STATE_ERROR; - } else if (hdw->state_encoder_run && - hdw->state_decoder_run && - hdw->state_usbstream_run) { + } else if (hdw->state_usbstream_run && + (!analog_mode || + (hdw->state_encoder_run && hdw->state_decoder_run))) { st = PVR2_STATE_RUN; } else { st = PVR2_STATE_READY; @@ -3628,6 +4170,7 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) "Device state change from %s to %s", pvr2_get_state_name(hdw->master_state), pvr2_get_state_name(st)); + pvr2_led_ctrl(hdw,st == PVR2_STATE_RUN); hdw->master_state = st; state_updated = !0; callback_flag = !0; @@ -3657,47 +4200,6 @@ static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw) } -void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw, - struct pvr2_hdw_debug_info *ptr) -{ - ptr->big_lock_held = hdw->big_lock_held; - ptr->ctl_lock_held = hdw->ctl_lock_held; - ptr->flag_disconnected = hdw->flag_disconnected; - ptr->flag_init_ok = hdw->flag_init_ok; - ptr->flag_ok = hdw->flag_ok; - ptr->fw1_state = hdw->fw1_state; - ptr->flag_decoder_missed = hdw->flag_decoder_missed; - ptr->flag_tripped = hdw->flag_tripped; - ptr->state_encoder_ok = hdw->state_encoder_ok; - ptr->state_encoder_run = hdw->state_encoder_run; - ptr->state_decoder_run = hdw->state_decoder_run; - ptr->state_usbstream_run = hdw->state_usbstream_run; - ptr->state_decoder_quiescent = hdw->state_decoder_quiescent; - ptr->state_pipeline_config = hdw->state_pipeline_config; - ptr->state_pipeline_req = hdw->state_pipeline_req; - ptr->state_pipeline_pause = hdw->state_pipeline_pause; - ptr->state_pipeline_idle = hdw->state_pipeline_idle; - ptr->cmd_debug_state = hdw->cmd_debug_state; - ptr->cmd_code = hdw->cmd_debug_code; - ptr->cmd_debug_write_len = hdw->cmd_debug_write_len; - ptr->cmd_debug_read_len = hdw->cmd_debug_read_len; - ptr->cmd_debug_timeout = hdw->ctl_timeout_flag; - ptr->cmd_debug_write_pend = hdw->ctl_write_pend_flag; - ptr->cmd_debug_read_pend = hdw->ctl_read_pend_flag; - ptr->cmd_debug_rstatus = hdw->ctl_read_urb->status; - ptr->cmd_debug_wstatus = hdw->ctl_read_urb->status; -} - - -void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw, - struct pvr2_hdw_debug_info *ptr) -{ - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_hdw_get_debug_info_unlocked(hdw,ptr); - } while(0); LOCK_GIVE(hdw->ctl_lock); -} - - int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp) { return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp); @@ -3757,6 +4259,80 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val) } +unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw) +{ + return hdw->input_avail_mask; +} + + +unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw) +{ + return hdw->input_allowed_mask; +} + + +static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v) +{ + if (hdw->input_val != v) { + hdw->input_val = v; + hdw->input_dirty = !0; + } + + /* Handle side effects - if we switch to a mode that needs the RF + tuner, then select the right frequency choice as well and mark + it dirty. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + hdw->freqSelector = 0; + hdw->freqDirty = !0; + } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) || + (hdw->input_val == PVR2_CVAL_INPUT_DTV)) { + hdw->freqSelector = 1; + hdw->freqDirty = !0; + } + return 0; +} + + +int pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw, + unsigned int change_mask, + unsigned int change_val) +{ + int ret = 0; + unsigned int nv,m,idx; + LOCK_TAKE(hdw->big_lock); + do { + nv = hdw->input_allowed_mask & ~change_mask; + nv |= (change_val & change_mask); + nv &= hdw->input_avail_mask; + if (!nv) { + /* No legal modes left; return error instead. */ + ret = -EPERM; + break; + } + hdw->input_allowed_mask = nv; + if ((1 << hdw->input_val) & hdw->input_allowed_mask) { + /* Current mode is still in the allowed mask, so + we're done. */ + break; + } + /* Select and switch to a mode that is still in the allowed + mask */ + if (!hdw->input_allowed_mask) { + /* Nothing legal; give up */ + break; + } + m = hdw->input_allowed_mask; + for (idx = 0; idx < (sizeof(m) << 3); idx++) { + if (!((1 << idx) & m)) continue; + pvr2_hdw_set_input(hdw,idx); + break; + } + } while (0); + LOCK_GIVE(hdw->big_lock); + return ret; +} + + /* Find I2C address of eeprom */ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) { diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 3ad7a13d6c3..20295e0c199 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -40,9 +40,10 @@ /* Legal values for the INPUT state variable */ #define PVR2_CVAL_INPUT_TV 0 -#define PVR2_CVAL_INPUT_SVIDEO 1 +#define PVR2_CVAL_INPUT_DTV 1 #define PVR2_CVAL_INPUT_COMPOSITE 2 -#define PVR2_CVAL_INPUT_RADIO 3 +#define PVR2_CVAL_INPUT_SVIDEO 3 +#define PVR2_CVAL_INPUT_RADIO 4 enum pvr2_config { pvr2_config_empty, /* No configuration */ @@ -90,9 +91,6 @@ enum pvr2_v4l_type { /* Translate configuration enum to a string label */ const char *pvr2_config_get_name(enum pvr2_config); -/* Translate a master state enum to a string label */ -const char *pvr2_hdw_get_state_name(unsigned int); - struct pvr2_hdw; /* Create and return a structure for interacting with the underlying @@ -100,14 +98,15 @@ struct pvr2_hdw; struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, const struct usb_device_id *devid); +/* Perform second stage initialization, passing in a notification callback + for when the master state changes. */ +int pvr2_hdw_initialize(struct pvr2_hdw *, + void (*callback_func)(void *), + void *callback_data); + /* Destroy hardware interaction structure */ void pvr2_hdw_destroy(struct pvr2_hdw *); -/* Register a function to be called whenever the master state changes. */ -void pvr2_hdw_set_state_callback(struct pvr2_hdw *, - void (*callback_func)(void *), - void *callback_data); - /* Return true if in the ready (normal) state */ int pvr2_hdw_dev_ok(struct pvr2_hdw *); @@ -146,6 +145,23 @@ struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *, /* Commit all control changes made up to this point */ int pvr2_hdw_commit_ctl(struct pvr2_hdw *); +/* Return a bit mask of valid input selections for this device. Mask bits + * will be according to PVR_CVAL_INPUT_xxxx definitions. */ +unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *); + +/* Return a bit mask of allowed input selections for this device. Mask bits + * will be according to PVR_CVAL_INPUT_xxxx definitions. */ +unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *); + +/* Change the set of allowed input selections for this device. Both + change_mask and change_valu are mask bits according to + PVR_CVAL_INPUT_xxxx definitions. The change_mask parameter indicate + which settings are being changed and the change_val parameter indicates + whether corresponding settings are being set or cleared. */ +int pvr2_hdw_set_input_allowed(struct pvr2_hdw *, + unsigned int change_mask, + unsigned int change_val); + /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); @@ -250,6 +266,9 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *); /* Execute simple reset command */ int pvr2_hdw_cmd_powerup(struct pvr2_hdw *); +/* suspend */ +int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *); + /* Order decoder to reset */ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *); diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 62867fa3517..793c89a8d67 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -35,7 +35,7 @@ */ -static unsigned int i2c_scan = 0; +static unsigned int i2c_scan; module_param(i2c_scan, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.c b/drivers/media/video/pvrusb2/pvrusb2-io.c index a9889ff96ec..7aff8b72006 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-io.c +++ b/drivers/media/video/pvrusb2/pvrusb2-io.c @@ -80,6 +80,10 @@ struct pvr2_stream { /* Tracking state for tolerating errors */ unsigned int fail_count; unsigned int fail_tolerance; + + unsigned int buffers_processed; + unsigned int buffers_failed; + unsigned int bytes_processed; }; struct pvr2_buffer { @@ -446,6 +450,8 @@ static void buffer_complete(struct urb *urb) (urb->status == -ENOENT) || (urb->status == -ECONNRESET) || (urb->status == -ESHUTDOWN)) { + (sp->buffers_processed)++; + sp->bytes_processed += urb->actual_length; bp->used_count = urb->actual_length; if (sp->fail_count) { pvr2_trace(PVR2_TRACE_TOLERANCE, @@ -457,11 +463,13 @@ static void buffer_complete(struct urb *urb) // We can tolerate this error, because we're below the // threshold... (sp->fail_count)++; + (sp->buffers_failed)++; pvr2_trace(PVR2_TRACE_TOLERANCE, "stream %p ignoring error %d" " - fail count increased to %u", sp,urb->status,sp->fail_count); } else { + (sp->buffers_failed)++; bp->status = urb->status; } spin_unlock_irqrestore(&sp->list_lock,irq_flags); @@ -515,6 +523,28 @@ void pvr2_stream_set_callback(struct pvr2_stream *sp, } while(0); mutex_unlock(&sp->mutex); } +void pvr2_stream_get_stats(struct pvr2_stream *sp, + struct pvr2_stream_stats *stats, + int zero_counts) +{ + unsigned long irq_flags; + spin_lock_irqsave(&sp->list_lock,irq_flags); + if (stats) { + stats->buffers_in_queue = sp->q_count; + stats->buffers_in_idle = sp->i_count; + stats->buffers_in_ready = sp->r_count; + stats->buffers_processed = sp->buffers_processed; + stats->buffers_failed = sp->buffers_failed; + stats->bytes_processed = sp->bytes_processed; + } + if (zero_counts) { + sp->buffers_processed = 0; + sp->buffers_failed = 0; + sp->bytes_processed = 0; + } + spin_unlock_irqrestore(&sp->list_lock,irq_flags); +} + /* Query / set the nominal buffer count */ int pvr2_stream_get_buffer_count(struct pvr2_stream *sp) { diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.h b/drivers/media/video/pvrusb2/pvrusb2-io.h index 93279cc2a35..42fcf8281a8 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-io.h +++ b/drivers/media/video/pvrusb2/pvrusb2-io.h @@ -36,6 +36,15 @@ enum pvr2_buffer_state { struct pvr2_stream; struct pvr2_buffer; +struct pvr2_stream_stats { + unsigned int buffers_in_queue; + unsigned int buffers_in_idle; + unsigned int buffers_in_ready; + unsigned int buffers_processed; + unsigned int buffers_failed; + unsigned int bytes_processed; +}; + /* Initialize / tear down stream structure */ struct pvr2_stream *pvr2_stream_create(void); void pvr2_stream_destroy(struct pvr2_stream *); @@ -45,6 +54,9 @@ void pvr2_stream_setup(struct pvr2_stream *, void pvr2_stream_set_callback(struct pvr2_stream *, pvr2_stream_callback func, void *data); +void pvr2_stream_get_stats(struct pvr2_stream *, + struct pvr2_stream_stats *, + int zero_counts); /* Query / set the nominal buffer count */ int pvr2_stream_get_buffer_count(struct pvr2_stream *); diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c index b63b2265503..332aced8a5a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -60,6 +60,10 @@ static void pvr_setup_attach(struct pvr2_context *pvr) { /* Create association with v4l layer */ pvr2_v4l2_create(pvr); +#ifdef CONFIG_VIDEO_PVRUSB2_DVB + /* Create association with dvb layer */ + pvr2_dvb_create(pvr); +#endif #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS pvr2_sysfs_create(pvr,class_ptr); #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ @@ -121,6 +125,12 @@ static int __init pvr_init(void) pvr2_trace(PVR2_TRACE_INIT,"pvr_init"); + ret = pvr2_context_global_init(); + if (ret != 0) { + pvr2_trace(PVR2_TRACE_INIT,"pvr_init failure code=%d",ret); + return ret; + } + #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS class_ptr = pvr2_sysfs_class_create(); #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ @@ -132,6 +142,8 @@ static int __init pvr_init(void) if (pvrusb2_debug) info("Debug mask is %d (0x%x)", pvrusb2_debug,pvrusb2_debug); + pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete"); + return ret; } @@ -144,6 +156,10 @@ static void __exit pvr_exit(void) #ifdef CONFIG_VIDEO_PVRUSB2_SYSFS pvr2_sysfs_class_destroy(class_ptr); #endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ + + pvr2_context_global_done(); + + pvr2_trace(PVR2_TRACE_INIT,"pvr_exit complete"); } module_init(pvr_init); diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c index da309288daa..fdc5a2b49ca 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-std.c +++ b/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -79,7 +79,7 @@ struct std_name { #define TSTD_Nc (V4L2_STD_PAL_Nc) #define TSTD_60 (V4L2_STD_PAL_60) -#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_SECAM) +#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_ATSC|CSTD_SECAM) /* Mapping of standard bits to color system */ static const struct std_name std_groups[] = { @@ -328,7 +328,7 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, struct v4l2_standard *stddefs; if (pvrusb2_debug & PVR2_TRACE_STD) { - char buf[50]; + char buf[100]; bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id); pvr2_trace( PVR2_TRACE_STD,"Mapping standards mask=0x%x (%.*s)", @@ -352,8 +352,11 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; } + /* Don't complain about ATSC standard values */ + fmsk &= ~CSTD_ATSC; + if (fmsk) { - char buf[50]; + char buf[100]; bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); pvr2_trace( PVR2_TRACE_ERROR_LEGS, diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 7a1cd878e31..0ff7a836a8a 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -21,7 +21,6 @@ #include <linux/string.h> #include <linux/slab.h> -#include <asm/semaphore.h> #include "pvrusb2-sysfs.h" #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" @@ -288,6 +287,8 @@ static ssize_t store_val_norm(int id,struct device *class_dev, struct pvr2_sysfs *sfp; int ret; sfp = (struct pvr2_sysfs *)class_dev->driver_data; + pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"", + sfp,id,(int)count,buf); ret = store_val_any(id,0,sfp,buf,count); if (!ret) ret = count; return ret; @@ -299,6 +300,8 @@ static ssize_t store_val_custom(int id,struct device *class_dev, struct pvr2_sysfs *sfp; int ret; sfp = (struct pvr2_sysfs *)class_dev->driver_data; + pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"", + sfp,id,(int)count,buf); ret = store_val_any(id,1,sfp,buf,count); if (!ret) ret = count; return ret; @@ -605,8 +608,9 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) ret = sysfs_create_group(&sfp->class_dev->kobj,&cip->grp); if (ret) { - printk(KERN_WARNING "%s: sysfs_create_group error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "sysfs_create_group error: %d", + ret); return; } cip->created_ok = !0; @@ -637,15 +641,17 @@ static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp) sfp->debugifc = dip; ret = device_create_file(sfp->class_dev,&dip->attr_debugcmd); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { dip->debugcmd_created_ok = !0; } ret = device_create_file(sfp->class_dev,&dip->attr_debuginfo); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { dip->debuginfo_created_ok = !0; } @@ -848,8 +854,8 @@ static void class_dev_create(struct pvr2_sysfs *sfp, class_dev->driver_data = sfp; ret = device_register(class_dev); if (ret) { - printk(KERN_ERR "%s: device_register failed\n", - __FUNCTION__); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_register failed"); kfree(class_dev); return; } @@ -861,8 +867,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_v4l_minor_number); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->v4l_minor_number_created_ok = !0; } @@ -874,8 +881,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_v4l_radio_minor_number); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->v4l_radio_minor_number_created_ok = !0; } @@ -886,8 +894,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, sfp->attr_unit_number.store = NULL; ret = device_create_file(sfp->class_dev,&sfp->attr_unit_number); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->unit_number_created_ok = !0; } @@ -899,8 +908,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_bus_info); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->bus_info_created_ok = !0; } @@ -912,8 +922,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_hdw_name); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->hdw_name_created_ok = !0; } @@ -925,8 +936,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, ret = device_create_file(sfp->class_dev, &sfp->attr_hdw_desc); if (ret < 0) { - printk(KERN_WARNING "%s: device_create_file error: %d\n", - __FUNCTION__, ret); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "device_create_file error: %d", + ret); } else { sfp->hdw_desc_created_ok = !0; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 8f0587ebd4b..087a1824556 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -57,7 +57,9 @@ struct pvr2_v4l2_fh { struct pvr2_v4l2_fh *vprev; wait_queue_head_t wait_data; int fw_mode_flag; - int prev_input_val; + /* Map contiguous ordinal value to input id */ + unsigned char *input_map; + unsigned int input_cnt; }; struct pvr2_v4l2 { @@ -259,14 +261,21 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, struct v4l2_input *vi = (struct v4l2_input *)arg; struct v4l2_input tmp; unsigned int cnt; + int val; cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); memset(&tmp,0,sizeof(tmp)); tmp.index = vi->index; ret = 0; - switch (vi->index) { + if ((vi->index < 0) || (vi->index >= fh->input_cnt)) { + ret = -EINVAL; + break; + } + val = fh->input_map[vi->index]; + switch (val) { case PVR2_CVAL_INPUT_TV: + case PVR2_CVAL_INPUT_DTV: case PVR2_CVAL_INPUT_RADIO: tmp.type = V4L2_INPUT_TYPE_TUNER; break; @@ -281,7 +290,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, if (ret < 0) break; cnt = 0; - pvr2_ctrl_get_valname(cptr,vi->index, + pvr2_ctrl_get_valname(cptr,val, tmp.name,sizeof(tmp.name)-1,&cnt); tmp.name[cnt] = 0; @@ -303,22 +312,33 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_INPUT: { + unsigned int idx; struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; int val; cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); val = 0; ret = pvr2_ctrl_get_value(cptr,&val); - vi->index = val; + vi->index = 0; + for (idx = 0; idx < fh->input_cnt; idx++) { + if (fh->input_map[idx] == val) { + vi->index = idx; + break; + } + } break; } case VIDIOC_S_INPUT: { struct v4l2_input *vi = (struct v4l2_input *)arg; + if ((vi->index < 0) || (vi->index >= fh->input_cnt)) { + ret = -ERANGE; + break; + } ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), - vi->index); + fh->input_map[vi->index]); break; } @@ -858,7 +878,6 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file) { struct pvr2_v4l2_fh *fhp = file->private_data; struct pvr2_v4l2 *vp = fhp->vhead; - struct pvr2_context *mp = fhp->vhead->channel.mc_head; struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw; pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release"); @@ -875,42 +894,30 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file) v4l2_prio_close(&vp->prio, &fhp->prio); file->private_data = NULL; - pvr2_context_enter(mp); do { - /* Restore the previous input selection, if it makes sense - to do so. */ - if (fhp->dev_info->v4l_type == VFL_TYPE_RADIO) { - struct pvr2_ctrl *cp; - int pval; - cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - pvr2_ctrl_get_value(cp,&pval); - /* Only restore if we're still selecting the radio */ - if (pval == PVR2_CVAL_INPUT_RADIO) { - pvr2_ctrl_set_value(cp,fhp->prev_input_val); - pvr2_hdw_commit_ctl(hdw); - } - } - - if (fhp->vnext) { - fhp->vnext->vprev = fhp->vprev; - } else { - vp->vlast = fhp->vprev; - } - if (fhp->vprev) { - fhp->vprev->vnext = fhp->vnext; - } else { - vp->vfirst = fhp->vnext; - } - fhp->vnext = NULL; - fhp->vprev = NULL; - fhp->vhead = NULL; - pvr2_channel_done(&fhp->channel); - pvr2_trace(PVR2_TRACE_STRUCT, - "Destroying pvr_v4l2_fh id=%p",fhp); - kfree(fhp); - if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) { - pvr2_v4l2_destroy_no_lock(vp); - } - } while (0); pvr2_context_exit(mp); + if (fhp->vnext) { + fhp->vnext->vprev = fhp->vprev; + } else { + vp->vlast = fhp->vprev; + } + if (fhp->vprev) { + fhp->vprev->vnext = fhp->vnext; + } else { + vp->vfirst = fhp->vnext; + } + fhp->vnext = NULL; + fhp->vprev = NULL; + fhp->vhead = NULL; + pvr2_channel_done(&fhp->channel); + pvr2_trace(PVR2_TRACE_STRUCT, + "Destroying pvr_v4l2_fh id=%p",fhp); + if (fhp->input_map) { + kfree(fhp->input_map); + fhp->input_map = NULL; + } + kfree(fhp); + if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) { + pvr2_v4l2_destroy_no_lock(vp); + } return 0; } @@ -921,6 +928,9 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) struct pvr2_v4l2_fh *fhp; struct pvr2_v4l2 *vp; struct pvr2_hdw *hdw; + unsigned int input_mask = 0; + unsigned int input_cnt,idx; + int ret = 0; dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase); @@ -943,32 +953,62 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) init_waitqueue_head(&fhp->wait_data); fhp->dev_info = dip; - pvr2_context_enter(vp->channel.mc_head); do { - pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); - pvr2_channel_init(&fhp->channel,vp->channel.mc_head); + pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); + pvr2_channel_init(&fhp->channel,vp->channel.mc_head); - fhp->vnext = NULL; - fhp->vprev = vp->vlast; - if (vp->vlast) { - vp->vlast->vnext = fhp; - } else { - vp->vfirst = fhp; - } - vp->vlast = fhp; - fhp->vhead = vp; - - /* Opening the /dev/radioX device implies a mode switch. - So execute that here. Note that you can get the - IDENTICAL effect merely by opening the normal video - device and setting the input appropriately. */ - if (dip->v4l_type == VFL_TYPE_RADIO) { - struct pvr2_ctrl *cp; - cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - pvr2_ctrl_get_value(cp,&fhp->prev_input_val); - pvr2_ctrl_set_value(cp,PVR2_CVAL_INPUT_RADIO); - pvr2_hdw_commit_ctl(hdw); - } - } while (0); pvr2_context_exit(vp->channel.mc_head); + if (dip->v4l_type == VFL_TYPE_RADIO) { + /* Opening device as a radio, legal input selection subset + is just the radio. */ + input_mask = (1 << PVR2_CVAL_INPUT_RADIO); + } else { + /* Opening the main V4L device, legal input selection + subset includes all analog inputs. */ + input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) | + (1 << PVR2_CVAL_INPUT_TV) | + (1 << PVR2_CVAL_INPUT_COMPOSITE) | + (1 << PVR2_CVAL_INPUT_SVIDEO)); + } + ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask); + if (ret) { + pvr2_channel_done(&fhp->channel); + pvr2_trace(PVR2_TRACE_STRUCT, + "Destroying pvr_v4l2_fh id=%p (input mask error)", + fhp); + + kfree(fhp); + return ret; + } + + input_mask &= pvr2_hdw_get_input_available(hdw); + input_cnt = 0; + for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { + if (input_mask & (1 << idx)) input_cnt++; + } + fhp->input_cnt = input_cnt; + fhp->input_map = kzalloc(input_cnt,GFP_KERNEL); + if (!fhp->input_map) { + pvr2_channel_done(&fhp->channel); + pvr2_trace(PVR2_TRACE_STRUCT, + "Destroying pvr_v4l2_fh id=%p (input map failure)", + fhp); + kfree(fhp); + return -ENOMEM; + } + input_cnt = 0; + for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { + if (!(input_mask & (1 << idx))) continue; + fhp->input_map[input_cnt++] = idx; + } + + fhp->vnext = NULL; + fhp->vprev = vp->vlast; + if (vp->vlast) { + vp->vlast->vnext = fhp; + } else { + vp->vfirst = fhp; + } + vp->vlast = fhp; + fhp->vhead = vp; fhp->file = file; file->private_data = fhp; @@ -1201,24 +1241,27 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) vp = kzalloc(sizeof(*vp),GFP_KERNEL); if (!vp) return vp; - vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); - vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); - if (!(vp->dev_video && vp->dev_radio)) { - kfree(vp->dev_video); - kfree(vp->dev_radio); - kfree(vp); - return NULL; - } pvr2_channel_init(&vp->channel,mnp); pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp); vp->channel.check_func = pvr2_v4l2_internal_check; /* register streams */ + vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); + if (!vp->dev_video) goto fail; pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER); - pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO); + if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) & + (1 << PVR2_CVAL_INPUT_RADIO)) { + vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); + if (!vp->dev_radio) goto fail; + pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO); + } return vp; + fail: + pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp); + pvr2_v4l2_destroy_no_lock(vp); + return 0; } /* diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index e0a453a6543..423fa7c2d0c 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -130,8 +130,8 @@ static int default_fbufs = 3; /* Default number of frame buffers */ #ifdef CONFIG_USB_PWC_DEBUG int pwc_trace = PWC_DEBUG_LEVEL; #endif -static int power_save = 0; -static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */ +static int power_save; +static int led_on = 100, led_off; /* defaults to LED that is on while in use */ static int pwc_preferred_compression = 1; /* 0..3 = uncompressed..high */ static struct { int type; @@ -159,7 +159,9 @@ static const struct file_operations pwc_fops = { .poll = pwc_video_poll, .mmap = pwc_video_mmap, .ioctl = pwc_video_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, }; static struct video_device pwc_template = { @@ -487,7 +489,7 @@ static void pwc_reset_buffers(struct pwc_device *pdev) int i; unsigned long flags; - PWC_DEBUG_MEMORY(">> %s __enter__\n", __FUNCTION__); + PWC_DEBUG_MEMORY(">> %s __enter__\n", __func__); spin_lock_irqsave(&pdev->ptrlock, flags); pdev->full_frames = NULL; @@ -509,7 +511,7 @@ static void pwc_reset_buffers(struct pwc_device *pdev) pdev->fill_image = 0; spin_unlock_irqrestore(&pdev->ptrlock, flags); - PWC_DEBUG_MEMORY("<< %s __leaving__\n", __FUNCTION__); + PWC_DEBUG_MEMORY("<< %s __leaving__\n", __func__); } @@ -786,8 +788,8 @@ static void pwc_isoc_handler(struct urb *urb) } /* ..status == 0 */ else { /* This is normally not interesting to the user, unless - * you are really debugging something */ - static int iso_error = 0; + * you are really debugging something, default = 0 */ + static int iso_error; iso_error++; if (iso_error < 20) PWC_DEBUG_FLOW("Iso frame %d of USB has error %d\n", i, fst); @@ -1426,7 +1428,7 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) unsigned long page, pos = 0; int index; - PWC_DEBUG_MEMORY(">> %s\n", __FUNCTION__); + PWC_DEBUG_MEMORY(">> %s\n", __func__); pdev = vdev->priv; size = vma->vm_end - vma->vm_start; start = vma->vm_start; diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 32fbe1ae625..1742889874d 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -351,8 +351,10 @@ int pwc_video_do_ioctl(struct inode *inode, struct file *file, return -EFAULT; #ifdef CONFIG_USB_PWC_DEBUG - if (PWC_DEBUG_LEVEL_IOCTL & pwc_trace) + if (PWC_DEBUG_LEVEL_IOCTL & pwc_trace) { v4l_printk_ioctl(cmd); + printk("\n"); + } #endif diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c new file mode 100644 index 00000000000..7cc8e9b19fb --- /dev/null +++ b/drivers/media/video/pxa_camera.c @@ -0,0 +1,1206 @@ +/* + * V4L2 Driver for PXA camera host + * + * Copyright (C) 2006, Sascha Hauer, Pengutronix + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/moduleparam.h> +#include <linux/time.h> +#include <linux/version.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/clk.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/soc_camera.h> + +#include <linux/videodev2.h> + +#include <asm/dma.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/camera.h> + +#define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) +#define PXA_CAM_DRV_NAME "pxa27x-camera" + +#define CICR0_SIM_MP (0 << 24) +#define CICR0_SIM_SP (1 << 24) +#define CICR0_SIM_MS (2 << 24) +#define CICR0_SIM_EP (3 << 24) +#define CICR0_SIM_ES (4 << 24) + +#define CICR1_DW_VAL(x) ((x) & CICR1_DW) /* Data bus width */ +#define CICR1_PPL_VAL(x) (((x) << 15) & CICR1_PPL) /* Pixels per line */ +#define CICR1_COLOR_SP_VAL(x) (((x) << 3) & CICR1_COLOR_SP) /* color space */ +#define CICR1_RGB_BPP_VAL(x) (((x) << 7) & CICR1_RGB_BPP) /* bpp for rgb */ +#define CICR1_RGBT_CONV_VAL(x) (((x) << 29) & CICR1_RGBT_CONV) /* rgbt conv */ + +#define CICR2_BLW_VAL(x) (((x) << 24) & CICR2_BLW) /* Beginning-of-line pixel clock wait count */ +#define CICR2_ELW_VAL(x) (((x) << 16) & CICR2_ELW) /* End-of-line pixel clock wait count */ +#define CICR2_HSW_VAL(x) (((x) << 10) & CICR2_HSW) /* Horizontal sync pulse width */ +#define CICR2_BFPW_VAL(x) (((x) << 3) & CICR2_BFPW) /* Beginning-of-frame pixel clock wait count */ +#define CICR2_FSW_VAL(x) (((x) << 0) & CICR2_FSW) /* Frame stabilization wait count */ + +#define CICR3_BFW_VAL(x) (((x) << 24) & CICR3_BFW) /* Beginning-of-frame line clock wait count */ +#define CICR3_EFW_VAL(x) (((x) << 16) & CICR3_EFW) /* End-of-frame line clock wait count */ +#define CICR3_VSW_VAL(x) (((x) << 11) & CICR3_VSW) /* Vertical sync pulse width */ +#define CICR3_LPF_VAL(x) (((x) << 0) & CICR3_LPF) /* Lines per frame */ + +#define CICR0_IRQ_MASK (CICR0_TOM | CICR0_RDAVM | CICR0_FEM | CICR0_EOLM | \ + CICR0_PERRM | CICR0_QDM | CICR0_CDM | CICR0_SOFM | \ + CICR0_EOFM | CICR0_FOM) + +static DEFINE_MUTEX(camera_lock); + +/* + * Structures + */ +enum pxa_camera_active_dma { + DMA_Y = 0x1, + DMA_U = 0x2, + DMA_V = 0x4, +}; + +/* descriptor needed for the PXA DMA engine */ +struct pxa_cam_dma { + dma_addr_t sg_dma; + struct pxa_dma_desc *sg_cpu; + size_t sg_size; + int sglen; +}; + +/* buffer for one video frame */ +struct pxa_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + const struct soc_camera_data_format *fmt; + + /* our descriptor lists for Y, U and V channels */ + struct pxa_cam_dma dmas[3]; + + int inwork; + + enum pxa_camera_active_dma active_dma; +}; + +struct pxa_camera_dev { + struct device *dev; + /* PXA27x is only supposed to handle one camera on its Quick Capture + * interface. If anyone ever builds hardware to enable more than + * one camera, they will have to modify this driver too */ + struct soc_camera_device *icd; + struct clk *clk; + + unsigned int irq; + void __iomem *base; + + int channels; + unsigned int dma_chans[3]; + + struct pxacamera_platform_data *pdata; + struct resource *res; + unsigned long platform_flags; + unsigned long platform_mclk_10khz; + + struct list_head capture; + + spinlock_t lock; + + struct pxa_buffer *active; + struct pxa_dma_desc *sg_tail[3]; +}; + +static const char *pxa_cam_driver_description = "PXA_Camera"; + +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ + +/* + * Videobuf operations + */ +static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + + dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + + /* planar capture requires Y, U and V buffers to be page aligned */ + if (pcdev->channels == 3) { + *size = PAGE_ALIGN(icd->width * icd->height); /* Y pages */ + *size += PAGE_ALIGN(icd->width * icd->height / 2); /* U pages */ + *size += PAGE_ALIGN(icd->width * icd->height / 2); /* V pages */ + } else { + *size = icd->width * icd->height * + ((icd->current_fmt->depth + 7) >> 3); + } + + if (0 == *count) + *count = 32; + while (*size * *count > vid_limit * 1024 * 1024) + (*count)--; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + int i; + + BUG_ON(in_interrupt()); + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + &buf->vb, buf->vb.baddr, buf->vb.bsize); + + /* This waits until this buffer is out of danger, i.e., until it is no + * longer in STATE_QUEUED or STATE_ACTIVE */ + videobuf_waiton(&buf->vb, 0, 0); + videobuf_dma_unmap(vq, dma); + videobuf_dma_free(dma); + + for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) { + if (buf->dmas[i].sg_cpu) + dma_free_coherent(pcdev->dev, buf->dmas[i].sg_size, + buf->dmas[i].sg_cpu, + buf->dmas[i].sg_dma); + buf->dmas[i].sg_cpu = NULL; + } + + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, + struct pxa_buffer *buf, + struct videobuf_dmabuf *dma, int channel, + int sglen, int sg_start, int cibr, + unsigned int size) +{ + struct pxa_cam_dma *pxa_dma = &buf->dmas[channel]; + int i; + + if (pxa_dma->sg_cpu) + dma_free_coherent(pcdev->dev, pxa_dma->sg_size, + pxa_dma->sg_cpu, pxa_dma->sg_dma); + + pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc); + pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->dev, pxa_dma->sg_size, + &pxa_dma->sg_dma, GFP_KERNEL); + if (!pxa_dma->sg_cpu) + return -ENOMEM; + + pxa_dma->sglen = sglen; + + for (i = 0; i < sglen; i++) { + int sg_i = sg_start + i; + struct scatterlist *sg = dma->sglist; + unsigned int dma_len = sg_dma_len(&sg[sg_i]), xfer_len; + + pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr; + pxa_dma->sg_cpu[i].dtadr = sg_dma_address(&sg[sg_i]); + + /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ + xfer_len = (min(dma_len, size) + 7) & ~7; + + pxa_dma->sg_cpu[i].dcmd = + DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len; + size -= dma_len; + pxa_dma->sg_cpu[i].ddadr = + pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); + } + + pxa_dma->sg_cpu[sglen - 1].ddadr = DDADR_STOP; + pxa_dma->sg_cpu[sglen - 1].dcmd |= DCMD_ENDIRQEN; + + return 0; +} + +static int pxa_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); + int ret; + int sglen_y, sglen_yu = 0, sglen_u = 0, sglen_v = 0; + int size_y, size_u = 0, size_v = 0; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + /* Added list head initialization on alloc */ + WARN_ON(!list_empty(&vb->queue)); + +#ifdef DEBUG + /* This can be useful if you want to see if we actually fill + * the buffer with something */ + memset((void *)vb->baddr, 0xaa, vb->bsize); +#endif + + BUG_ON(NULL == icd->current_fmt); + + /* I think, in buf_prepare you only have to protect global data, + * the actual buffer is yours */ + buf->inwork = 1; + + if (buf->fmt != icd->current_fmt || + vb->width != icd->width || + vb->height != icd->height || + vb->field != field) { + buf->fmt = icd->current_fmt; + vb->width = icd->width; + vb->height = icd->height; + vb->field = field; + vb->state = VIDEOBUF_NEEDS_INIT; + } + + vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3); + if (0 != vb->baddr && vb->bsize < vb->size) { + ret = -EINVAL; + goto out; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + unsigned int size = vb->size; + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + + ret = videobuf_iolock(vq, vb, NULL); + if (ret) + goto fail; + + if (pcdev->channels == 3) { + /* FIXME the calculations should be more precise */ + sglen_y = dma->sglen / 2; + sglen_u = sglen_v = dma->sglen / 4 + 1; + sglen_yu = sglen_y + sglen_u; + size_y = size / 2; + size_u = size_v = size / 4; + } else { + sglen_y = dma->sglen; + size_y = size; + } + + /* init DMA for Y channel */ + ret = pxa_init_dma_channel(pcdev, buf, dma, 0, sglen_y, + 0, 0x28, size_y); + + if (ret) { + dev_err(pcdev->dev, + "DMA initialization for Y/RGB failed\n"); + goto fail; + } + + if (pcdev->channels == 3) { + /* init DMA for U channel */ + ret = pxa_init_dma_channel(pcdev, buf, dma, 1, sglen_u, + sglen_y, 0x30, size_u); + if (ret) { + dev_err(pcdev->dev, + "DMA initialization for U failed\n"); + goto fail_u; + } + + /* init DMA for V channel */ + ret = pxa_init_dma_channel(pcdev, buf, dma, 2, sglen_v, + sglen_yu, 0x38, size_v); + if (ret) { + dev_err(pcdev->dev, + "DMA initialization for V failed\n"); + goto fail_v; + } + } + + vb->state = VIDEOBUF_PREPARED; + } + + buf->inwork = 0; + buf->active_dma = DMA_Y; + if (pcdev->channels == 3) + buf->active_dma |= DMA_U | DMA_V; + + return 0; + +fail_v: + dma_free_coherent(pcdev->dev, buf->dmas[1].sg_size, + buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma); +fail_u: + dma_free_coherent(pcdev->dev, buf->dmas[0].sg_size, + buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma); +fail: + free_buffer(vq, buf); +out: + buf->inwork = 0; + return ret; +} + +static void pxa_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); + struct pxa_buffer *active; + unsigned long flags; + int i; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + spin_lock_irqsave(&pcdev->lock, flags); + + list_add_tail(&vb->queue, &pcdev->capture); + + vb->state = VIDEOBUF_ACTIVE; + active = pcdev->active; + + if (!active) { + CIFR |= CIFR_RESET_F; + + for (i = 0; i < pcdev->channels; i++) { + DDADR(pcdev->dma_chans[i]) = buf->dmas[i].sg_dma; + DCSR(pcdev->dma_chans[i]) = DCSR_RUN; + pcdev->sg_tail[i] = buf->dmas[i].sg_cpu + buf->dmas[i].sglen - 1; + } + + pcdev->active = buf; + CICR0 |= CICR0_ENB; + } else { + struct pxa_cam_dma *buf_dma; + struct pxa_cam_dma *act_dma; + int nents; + + for (i = 0; i < pcdev->channels; i++) { + buf_dma = &buf->dmas[i]; + act_dma = &active->dmas[i]; + nents = buf_dma->sglen; + + /* Stop DMA engine */ + DCSR(pcdev->dma_chans[i]) = 0; + + /* Add the descriptors we just initialized to + the currently running chain */ + pcdev->sg_tail[i]->ddadr = buf_dma->sg_dma; + pcdev->sg_tail[i] = buf_dma->sg_cpu + buf_dma->sglen - 1; + + /* Setup a dummy descriptor with the DMA engines current + * state + */ + buf_dma->sg_cpu[nents].dsadr = + pcdev->res->start + 0x28 + i*8; /* CIBRx */ + buf_dma->sg_cpu[nents].dtadr = + DTADR(pcdev->dma_chans[i]); + buf_dma->sg_cpu[nents].dcmd = + DCMD(pcdev->dma_chans[i]); + + if (DDADR(pcdev->dma_chans[i]) == DDADR_STOP) { + /* The DMA engine is on the last + descriptor, set the next descriptors + address to the descriptors we just + initialized */ + buf_dma->sg_cpu[nents].ddadr = buf_dma->sg_dma; + } else { + buf_dma->sg_cpu[nents].ddadr = + DDADR(pcdev->dma_chans[i]); + } + + /* The next descriptor is the dummy descriptor */ + DDADR(pcdev->dma_chans[i]) = buf_dma->sg_dma + nents * + sizeof(struct pxa_dma_desc); + + DCSR(pcdev->dma_chans[i]) = DCSR_RUN; + } + } + + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static void pxa_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); +#ifdef DEBUG + struct soc_camera_device *icd = vq->priv_data; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + switch (vb->state) { + case VIDEOBUF_ACTIVE: + dev_dbg(&icd->dev, "%s (active)\n", __func__); + break; + case VIDEOBUF_QUEUED: + dev_dbg(&icd->dev, "%s (queued)\n", __func__); + break; + case VIDEOBUF_PREPARED: + dev_dbg(&icd->dev, "%s (prepared)\n", __func__); + break; + default: + dev_dbg(&icd->dev, "%s (unknown)\n", __func__); + break; + } +#endif + + free_buffer(vq, buf); +} + +static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, + struct videobuf_buffer *vb, + struct pxa_buffer *buf) +{ + /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ + list_del_init(&vb->queue); + vb->state = VIDEOBUF_DONE; + do_gettimeofday(&vb->ts); + vb->field_count++; + wake_up(&vb->done); + + if (list_empty(&pcdev->capture)) { + pcdev->active = NULL; + DCSR(pcdev->dma_chans[0]) = 0; + DCSR(pcdev->dma_chans[1]) = 0; + DCSR(pcdev->dma_chans[2]) = 0; + CICR0 &= ~CICR0_ENB; + return; + } + + pcdev->active = list_entry(pcdev->capture.next, + struct pxa_buffer, vb.queue); +} + +static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, + enum pxa_camera_active_dma act_dma) +{ + struct pxa_buffer *buf; + unsigned long flags; + u32 status, camera_status, overrun; + struct videobuf_buffer *vb; + + spin_lock_irqsave(&pcdev->lock, flags); + + status = DCSR(channel); + DCSR(channel) = status | DCSR_ENDINTR; + + if (status & DCSR_BUSERR) { + dev_err(pcdev->dev, "DMA Bus Error IRQ!\n"); + goto out; + } + + if (!(status & DCSR_ENDINTR)) { + dev_err(pcdev->dev, "Unknown DMA IRQ source, " + "status: 0x%08x\n", status); + goto out; + } + + if (!pcdev->active) { + dev_err(pcdev->dev, "DMA End IRQ with no active buffer!\n"); + goto out; + } + + camera_status = CISR; + overrun = CISR_IFO_0; + if (pcdev->channels == 3) + overrun |= CISR_IFO_1 | CISR_IFO_2; + if (camera_status & overrun) { + dev_dbg(pcdev->dev, "FIFO overrun! CISR: %x\n", camera_status); + /* Stop the Capture Interface */ + CICR0 &= ~CICR0_ENB; + /* Stop DMA */ + DCSR(channel) = 0; + /* Reset the FIFOs */ + CIFR |= CIFR_RESET_F; + /* Enable End-Of-Frame Interrupt */ + CICR0 &= ~CICR0_EOFM; + /* Restart the Capture Interface */ + CICR0 |= CICR0_ENB; + goto out; + } + + vb = &pcdev->active->vb; + buf = container_of(vb, struct pxa_buffer, vb); + WARN_ON(buf->inwork || list_empty(&vb->queue)); + dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + buf->active_dma &= ~act_dma; + if (!buf->active_dma) + pxa_camera_wakeup(pcdev, vb, buf); + +out: + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static void pxa_camera_dma_irq_y(int channel, void *data) +{ + struct pxa_camera_dev *pcdev = data; + pxa_camera_dma_irq(channel, pcdev, DMA_Y); +} + +static void pxa_camera_dma_irq_u(int channel, void *data) +{ + struct pxa_camera_dev *pcdev = data; + pxa_camera_dma_irq(channel, pcdev, DMA_U); +} + +static void pxa_camera_dma_irq_v(int channel, void *data) +{ + struct pxa_camera_dev *pcdev = data; + pxa_camera_dma_irq(channel, pcdev, DMA_V); +} + +static struct videobuf_queue_ops pxa_videobuf_ops = { + .buf_setup = pxa_videobuf_setup, + .buf_prepare = pxa_videobuf_prepare, + .buf_queue = pxa_videobuf_queue, + .buf_release = pxa_videobuf_release, +}; + +static int mclk_get_divisor(struct pxa_camera_dev *pcdev) +{ + unsigned int mclk_10khz = pcdev->platform_mclk_10khz; + unsigned long div; + unsigned long lcdclk; + + lcdclk = clk_get_rate(pcdev->clk) / 10000; + + /* We verify platform_mclk_10khz != 0, so if anyone breaks it, here + * they get a nice Oops */ + div = (lcdclk + 2 * mclk_10khz - 1) / (2 * mclk_10khz) - 1; + + dev_dbg(pcdev->dev, "LCD clock %lukHz, target freq %dkHz, " + "divisor %lu\n", lcdclk * 10, mclk_10khz * 10, div); + + return div; +} + +static void pxa_camera_activate(struct pxa_camera_dev *pcdev) +{ + struct pxacamera_platform_data *pdata = pcdev->pdata; + u32 cicr4 = 0; + + dev_dbg(pcdev->dev, "Registered platform device at %p data %p\n", + pcdev, pdata); + + if (pdata && pdata->init) { + dev_dbg(pcdev->dev, "%s: Init gpios\n", __func__); + pdata->init(pcdev->dev); + } + + if (pdata && pdata->power) { + dev_dbg(pcdev->dev, "%s: Power on camera\n", __func__); + pdata->power(pcdev->dev, 1); + } + + if (pdata && pdata->reset) { + dev_dbg(pcdev->dev, "%s: Releasing camera reset\n", + __func__); + pdata->reset(pcdev->dev, 1); + } + + CICR0 = 0x3FF; /* disable all interrupts */ + + if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) + cicr4 |= CICR4_PCLK_EN; + if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) + cicr4 |= CICR4_MCLK_EN; + if (pcdev->platform_flags & PXA_CAMERA_PCP) + cicr4 |= CICR4_PCP; + if (pcdev->platform_flags & PXA_CAMERA_HSP) + cicr4 |= CICR4_HSP; + if (pcdev->platform_flags & PXA_CAMERA_VSP) + cicr4 |= CICR4_VSP; + + CICR4 = mclk_get_divisor(pcdev) | cicr4; + + clk_enable(pcdev->clk); +} + +static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev) +{ + struct pxacamera_platform_data *board = pcdev->pdata; + + clk_disable(pcdev->clk); + + if (board && board->reset) { + dev_dbg(pcdev->dev, "%s: Asserting camera reset\n", + __func__); + board->reset(pcdev->dev, 0); + } + + if (board && board->power) { + dev_dbg(pcdev->dev, "%s: Power off camera\n", __func__); + board->power(pcdev->dev, 0); + } +} + +static irqreturn_t pxa_camera_irq(int irq, void *data) +{ + struct pxa_camera_dev *pcdev = data; + unsigned int status = CISR; + + dev_dbg(pcdev->dev, "Camera interrupt status 0x%x\n", status); + + if (!status) + return IRQ_NONE; + + CISR = status; + + if (status & CISR_EOF) { + int i; + for (i = 0; i < pcdev->channels; i++) { + DDADR(pcdev->dma_chans[i]) = + pcdev->active->dmas[i].sg_dma; + DCSR(pcdev->dma_chans[i]) = DCSR_RUN; + } + CICR0 |= CICR0_EOFM; + } + + return IRQ_HANDLED; +} + +/* The following two functions absolutely depend on the fact, that + * there can be only one camera on PXA quick capture interface */ +static int pxa_camera_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + int ret; + + mutex_lock(&camera_lock); + + if (pcdev->icd) { + ret = -EBUSY; + goto ebusy; + } + + dev_info(&icd->dev, "PXA Camera driver attached to camera %d\n", + icd->devnum); + + pxa_camera_activate(pcdev); + ret = icd->ops->init(icd); + + if (!ret) + pcdev->icd = icd; + +ebusy: + mutex_unlock(&camera_lock); + + return ret; +} + +static void pxa_camera_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + + BUG_ON(icd != pcdev->icd); + + dev_info(&icd->dev, "PXA Camera driver detached from camera %d\n", + icd->devnum); + + /* disable capture, disable interrupts */ + CICR0 = 0x3ff; + + /* Stop DMA engine */ + DCSR(pcdev->dma_chans[0]) = 0; + DCSR(pcdev->dma_chans[1]) = 0; + DCSR(pcdev->dma_chans[2]) = 0; + + icd->ops->release(icd); + + pxa_camera_deactivate(pcdev); + + pcdev->icd = NULL; +} + +static int test_platform_param(struct pxa_camera_dev *pcdev, + unsigned char buswidth, unsigned long *flags) +{ + /* + * Platform specified synchronization and pixel clock polarities are + * only a recommendation and are only used during probing. The PXA270 + * quick capture interface supports both. + */ + *flags = (pcdev->platform_flags & PXA_CAMERA_MASTER ? + SOCAM_MASTER : SOCAM_SLAVE) | + SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_HSYNC_ACTIVE_LOW | + SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_VSYNC_ACTIVE_LOW | + SOCAM_PCLK_SAMPLE_RISING | + SOCAM_PCLK_SAMPLE_FALLING; + + /* If requested data width is supported by the platform, use it */ + switch (buswidth) { + case 10: + if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_10; + break; + case 9: + if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_9; + break; + case 8: + if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_8; + } + + return 0; +} + +static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +{ + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + unsigned long dw, bpp, bus_flags, camera_flags, common_flags; + u32 cicr0, cicr1, cicr4 = 0; + int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); + + if (ret < 0) + return ret; + + camera_flags = icd->ops->query_bus_param(icd); + + common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); + if (!common_flags) + return -EINVAL; + + pcdev->channels = 1; + + /* Make choises, based on platform preferences */ + if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if (pcdev->platform_flags & PXA_CAMERA_HSP) + common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; + else + common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; + } + + if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { + if (pcdev->platform_flags & PXA_CAMERA_VSP) + common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; + else + common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; + } + + if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && + (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if (pcdev->platform_flags & PXA_CAMERA_PCP) + common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + else + common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + } + + ret = icd->ops->set_bus_param(icd, common_flags); + if (ret < 0) + return ret; + + /* Datawidth is now guaranteed to be equal to one of the three values. + * We fix bit-per-pixel equal to data-width... */ + switch (common_flags & SOCAM_DATAWIDTH_MASK) { + case SOCAM_DATAWIDTH_10: + icd->buswidth = 10; + dw = 4; + bpp = 0x40; + break; + case SOCAM_DATAWIDTH_9: + icd->buswidth = 9; + dw = 3; + bpp = 0x20; + break; + default: + /* Actually it can only be 8 now, + * default is just to silence compiler warnings */ + case SOCAM_DATAWIDTH_8: + icd->buswidth = 8; + dw = 2; + bpp = 0; + } + + if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) + cicr4 |= CICR4_PCLK_EN; + if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) + cicr4 |= CICR4_MCLK_EN; + if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) + cicr4 |= CICR4_PCP; + if (common_flags & SOCAM_HSYNC_ACTIVE_LOW) + cicr4 |= CICR4_HSP; + if (common_flags & SOCAM_VSYNC_ACTIVE_LOW) + cicr4 |= CICR4_VSP; + + cicr0 = CICR0; + if (cicr0 & CICR0_ENB) + CICR0 = cicr0 & ~CICR0_ENB; + + cicr1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw; + + switch (pixfmt) { + case V4L2_PIX_FMT_YUV422P: + pcdev->channels = 3; + cicr1 |= CICR1_YCBCR_F; + case V4L2_PIX_FMT_YUYV: + cicr1 |= CICR1_COLOR_SP_VAL(2); + break; + case V4L2_PIX_FMT_RGB555: + cicr1 |= CICR1_RGB_BPP_VAL(1) | CICR1_RGBT_CONV_VAL(2) | + CICR1_TBIT | CICR1_COLOR_SP_VAL(1); + break; + case V4L2_PIX_FMT_RGB565: + cicr1 |= CICR1_COLOR_SP_VAL(1) | CICR1_RGB_BPP_VAL(2); + break; + } + + CICR1 = cicr1; + CICR2 = 0; + CICR3 = CICR3_LPF_VAL(icd->height - 1) | + CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top)); + CICR4 = mclk_get_divisor(pcdev) | cicr4; + + /* CIF interrupts are not used, only DMA */ + CICR0 = (pcdev->platform_flags & PXA_CAMERA_MASTER ? + CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP)) | + CICR0_DMAEN | CICR0_IRQ_MASK | (cicr0 & CICR0_ENB); + + return 0; +} + +static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +{ + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + unsigned long bus_flags, camera_flags; + int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); + + if (ret < 0) + return ret; + + camera_flags = icd->ops->query_bus_param(icd); + + return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL; +} + +static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + return icd->ops->set_fmt_cap(icd, pixfmt, rect); +} + +static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + /* limit to pxa hardware capabilities */ + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.height > 2048) + f->fmt.pix.height = 2048; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > 2048) + f->fmt.pix.width = 2048; + f->fmt.pix.width &= ~0x01; + + /* limit to sensor capabilities */ + return icd->ops->try_fmt_cap(icd, f); +} + +static int pxa_camera_reqbufs(struct soc_camera_file *icf, + struct v4l2_requestbuffers *p) +{ + int i; + + /* This is for locking debugging only. I removed spinlocks and now I + * check whether .prepare is ever called on a linked buffer, or whether + * a dma IRQ can occur for an in-work or unlinked buffer. Until now + * it hadn't triggered */ + for (i = 0; i < p->count; i++) { + struct pxa_buffer *buf = container_of(icf->vb_vidq.bufs[i], + struct pxa_buffer, vb); + buf->inwork = 0; + INIT_LIST_HEAD(&buf->vb.queue); + } + + return 0; +} + +static unsigned int pxa_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_file *icf = file->private_data; + struct pxa_buffer *buf; + + buf = list_entry(icf->vb_vidq.stream.next, struct pxa_buffer, + vb.stream); + + poll_wait(file, &buf->vb.done, pt); + + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return POLLIN|POLLRDNORM; + + return 0; +} + +static int pxa_camera_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + /* cap->name is set by the firendly caller:-> */ + strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card)); + cap->version = PXA_CAM_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +static spinlock_t *pxa_camera_spinlock_alloc(struct soc_camera_file *icf) +{ + struct soc_camera_host *ici = + to_soc_camera_host(icf->icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + + return &pcdev->lock; +} + +static struct soc_camera_host_ops pxa_soc_camera_host_ops = { + .owner = THIS_MODULE, + .add = pxa_camera_add_device, + .remove = pxa_camera_remove_device, + .set_fmt_cap = pxa_camera_set_fmt_cap, + .try_fmt_cap = pxa_camera_try_fmt_cap, + .reqbufs = pxa_camera_reqbufs, + .poll = pxa_camera_poll, + .querycap = pxa_camera_querycap, + .try_bus_param = pxa_camera_try_bus_param, + .set_bus_param = pxa_camera_set_bus_param, + .spinlock_alloc = pxa_camera_spinlock_alloc, +}; + +/* Should be allocated dynamically too, but we have only one. */ +static struct soc_camera_host pxa_soc_camera_host = { + .drv_name = PXA_CAM_DRV_NAME, + .vbq_ops = &pxa_videobuf_ops, + .msize = sizeof(struct pxa_buffer), + .ops = &pxa_soc_camera_host_ops, +}; + +static int pxa_camera_probe(struct platform_device *pdev) +{ + struct pxa_camera_dev *pcdev; + struct resource *res; + void __iomem *base; + unsigned int irq; + int err = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!res || !irq) { + err = -ENODEV; + goto exit; + } + + pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + if (!pcdev) { + dev_err(&pdev->dev, "Could not allocate pcdev\n"); + err = -ENOMEM; + goto exit; + } + + pcdev->clk = clk_get(&pdev->dev, "CAMCLK"); + if (IS_ERR(pcdev->clk)) { + err = PTR_ERR(pcdev->clk); + goto exit_kfree; + } + + dev_set_drvdata(&pdev->dev, pcdev); + pcdev->res = res; + + pcdev->pdata = pdev->dev.platform_data; + pcdev->platform_flags = pcdev->pdata->flags; + if (!(pcdev->platform_flags & (PXA_CAMERA_DATAWIDTH_8 | + PXA_CAMERA_DATAWIDTH_9 | PXA_CAMERA_DATAWIDTH_10))) { + /* Platform hasn't set available data widths. This is bad. + * Warn and use a default. */ + dev_warn(&pdev->dev, "WARNING! Platform hasn't set available " + "data widths, using default 10 bit\n"); + pcdev->platform_flags |= PXA_CAMERA_DATAWIDTH_10; + } + pcdev->platform_mclk_10khz = pcdev->pdata->mclk_10khz; + if (!pcdev->platform_mclk_10khz) { + dev_warn(&pdev->dev, + "mclk_10khz == 0! Please, fix your platform data. " + "Using default 20MHz\n"); + pcdev->platform_mclk_10khz = 2000; + } + + INIT_LIST_HEAD(&pcdev->capture); + spin_lock_init(&pcdev->lock); + + /* + * Request the regions. + */ + if (!request_mem_region(res->start, res->end - res->start + 1, + PXA_CAM_DRV_NAME)) { + err = -EBUSY; + goto exit_clk; + } + + base = ioremap(res->start, res->end - res->start + 1); + if (!base) { + err = -ENOMEM; + goto exit_release; + } + pcdev->irq = irq; + pcdev->base = base; + pcdev->dev = &pdev->dev; + + /* request dma */ + pcdev->dma_chans[0] = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, + pxa_camera_dma_irq_y, pcdev); + if (pcdev->dma_chans[0] < 0) { + dev_err(pcdev->dev, "Can't request DMA for Y\n"); + err = -ENOMEM; + goto exit_iounmap; + } + dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]); + + pcdev->dma_chans[1] = pxa_request_dma("CI_U", DMA_PRIO_HIGH, + pxa_camera_dma_irq_u, pcdev); + if (pcdev->dma_chans[1] < 0) { + dev_err(pcdev->dev, "Can't request DMA for U\n"); + err = -ENOMEM; + goto exit_free_dma_y; + } + dev_dbg(pcdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]); + + pcdev->dma_chans[2] = pxa_request_dma("CI_V", DMA_PRIO_HIGH, + pxa_camera_dma_irq_v, pcdev); + if (pcdev->dma_chans[0] < 0) { + dev_err(pcdev->dev, "Can't request DMA for V\n"); + err = -ENOMEM; + goto exit_free_dma_u; + } + dev_dbg(pcdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]); + + DRCMR68 = pcdev->dma_chans[0] | DRCMR_MAPVLD; + DRCMR69 = pcdev->dma_chans[1] | DRCMR_MAPVLD; + DRCMR70 = pcdev->dma_chans[2] | DRCMR_MAPVLD; + + /* request irq */ + err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME, + pcdev); + if (err) { + dev_err(pcdev->dev, "Camera interrupt register failed \n"); + goto exit_free_dma; + } + + pxa_soc_camera_host.priv = pcdev; + pxa_soc_camera_host.dev.parent = &pdev->dev; + pxa_soc_camera_host.nr = pdev->id; + err = soc_camera_host_register(&pxa_soc_camera_host); + if (err) + goto exit_free_irq; + + return 0; + +exit_free_irq: + free_irq(pcdev->irq, pcdev); +exit_free_dma: + pxa_free_dma(pcdev->dma_chans[2]); +exit_free_dma_u: + pxa_free_dma(pcdev->dma_chans[1]); +exit_free_dma_y: + pxa_free_dma(pcdev->dma_chans[0]); +exit_iounmap: + iounmap(base); +exit_release: + release_mem_region(res->start, res->end - res->start + 1); +exit_clk: + clk_put(pcdev->clk); +exit_kfree: + kfree(pcdev); +exit: + return err; +} + +static int __devexit pxa_camera_remove(struct platform_device *pdev) +{ + struct pxa_camera_dev *pcdev = platform_get_drvdata(pdev); + struct resource *res; + + clk_put(pcdev->clk); + + pxa_free_dma(pcdev->dma_chans[0]); + pxa_free_dma(pcdev->dma_chans[1]); + pxa_free_dma(pcdev->dma_chans[2]); + free_irq(pcdev->irq, pcdev); + + soc_camera_host_unregister(&pxa_soc_camera_host); + + iounmap(pcdev->base); + + res = pcdev->res; + release_mem_region(res->start, res->end - res->start + 1); + + kfree(pcdev); + + dev_info(&pdev->dev, "PXA Camera driver unloaded\n"); + + return 0; +} + +static struct platform_driver pxa_camera_driver = { + .driver = { + .name = PXA_CAM_DRV_NAME, + }, + .probe = pxa_camera_probe, + .remove = __exit_p(pxa_camera_remove), +}; + + +static int __devinit pxa_camera_init(void) +{ + return platform_driver_register(&pxa_camera_driver); +} + +static void __exit pxa_camera_exit(void) +{ + return platform_driver_unregister(&pxa_camera_driver); +} + +module_init(pxa_camera_init); +module_exit(pxa_camera_exit); + +MODULE_DESCRIPTION("PXA27x SoC Camera Host driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index f55d6e85f20..ec8c65dc840 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -701,7 +701,9 @@ static const struct file_operations saa_fops = { .open = saa5249_open, .release = saa5249_release, .ioctl = saa5249_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, }; diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c index 72e344a12c7..716ee7f64df 100644 --- a/drivers/media/video/saa6588.c +++ b/drivers/media/video/saa6588.c @@ -44,10 +44,10 @@ static unsigned short normal_i2c[] = { I2C_CLIENT_INSMOD; /* insmod options */ -static unsigned int debug = 0; -static unsigned int xtal = 0; -static unsigned int rbds = 0; -static unsigned int plvl = 0; +static unsigned int debug; +static unsigned int xtal; +static unsigned int rbds; +static unsigned int plvl; static unsigned int bufblocks = 100; module_param(debug, int, 0644); diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c index 1df2602cd18..4aa82b31070 100644 --- a/drivers/media/video/saa7110.c +++ b/drivers/media/video/saa7110.c @@ -46,7 +46,7 @@ MODULE_LICENSE("GPL"); #include <media/v4l2-common.h> #include <linux/video_decoder.h> -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); diff --git a/drivers/media/video/saa7111.c b/drivers/media/video/saa7111.c index a0772c53bb1..96c3d435772 100644 --- a/drivers/media/video/saa7111.c +++ b/drivers/media/video/saa7111.c @@ -55,7 +55,7 @@ MODULE_LICENSE("GPL"); #define I2C_NAME(s) (s)->name -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); diff --git a/drivers/media/video/saa7114.c b/drivers/media/video/saa7114.c index bf91a4faa70..e79075533be 100644 --- a/drivers/media/video/saa7114.c +++ b/drivers/media/video/saa7114.c @@ -56,7 +56,7 @@ MODULE_LICENSE("GPL"); #define I2C_NAME(x) (x)->name -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 41e5e518a47..416d05d4a96 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -57,7 +57,7 @@ MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, " "Hans Verkuil, Mauro Carvalho Chehab"); MODULE_LICENSE("GPL"); -static int debug = 0; +static int debug; module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); @@ -957,7 +957,7 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) if (std == V4L2_STD_PAL_M) { reg |= 0x30; - } else if (std == V4L2_STD_PAL_N) { + } else if (std == V4L2_STD_PAL_Nc) { reg |= 0x20; } else if (std == V4L2_STD_PAL_60) { reg |= 0x10; diff --git a/drivers/media/video/saa711x.c b/drivers/media/video/saa711x.c index 80bf9118785..cedb988574b 100644 --- a/drivers/media/video/saa711x.c +++ b/drivers/media/video/saa711x.c @@ -48,7 +48,7 @@ MODULE_LICENSE("GPL"); #include <linux/video_decoder.h> -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, " Set the default Debug level. Default: 0 (Off) - (0-1)"); diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 96bc3b1298a..e086f14d566 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -37,6 +37,7 @@ config VIDEO_SAA7134_DVB select DVB_TDA826X if !DVB_FE_CUSTOMISE select DVB_TDA827X if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE + select TUNER_SIMPLE if !DVB_FE_CUSTOMISE ---help--- This adds support for DVB cards based on the Philips saa7134 chip. diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index 047add8f301..ba3082422a0 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -31,7 +31,7 @@ #include "saa7134.h" #include "saa7134-reg.h" -static unsigned int debug = 0; +static unsigned int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"enable debug messages [alsa]"); @@ -503,7 +503,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, /* release the old buffer */ if (substream->runtime->dma_area) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_pci_dma_unmap(dev->pci, &dev->dmasound.dma); + videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); substream->runtime->dma_area = NULL; } @@ -519,12 +519,12 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, return err; } - if (0 != (err = videobuf_pci_dma_map(dev->pci, &dev->dmasound.dma))) { + if (0 != (err = videobuf_sg_dma_map(&dev->pci->dev, &dev->dmasound.dma))) { dsp_buffer_free(dev); return err; } if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) { - videobuf_pci_dma_unmap(dev->pci, &dev->dmasound.dma); + videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); return err; } @@ -533,7 +533,7 @@ static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, dev->dmasound.dma.sglen, 0))) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_pci_dma_unmap(dev->pci, &dev->dmasound.dma); + videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); return err; } @@ -569,7 +569,7 @@ static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) if (substream->runtime->dma_area) { saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); - videobuf_pci_dma_unmap(dev->pci, &dev->dmasound.dma); + videobuf_sg_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); dsp_buffer_free(dev); substream->runtime->dma_area = NULL; } @@ -954,10 +954,8 @@ static void snd_saa7134_free(struct snd_card * card) if (chip->dev->dmasound.priv_data == NULL) return; - if (chip->irq >= 0) { - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, &chip->dev->dmasound); - } chip->dev->dmasound.priv_data = NULL; diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 6f5744286e8..98375955a84 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -22,11 +22,15 @@ #include <linux/init.h> #include <linux/module.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> #include "saa7134-reg.h" #include "saa7134.h" +#include "tuner-xc2028.h" #include <media/v4l2-common.h> #include <media/tveeprom.h> +#include "tea5767.h" /* commly used strings */ static char name_mute[] = "mute"; @@ -1046,7 +1050,7 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_MANLI_MTV002] = { /* Ognjen Nastic <ognjen@logosoft.ba> */ - .name = "Manli MuchTV M-TV002/Behold TV 403 FM", + .name = "Manli MuchTV M-TV002", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_PAL, .radio_type = UNSET, @@ -1073,7 +1077,7 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_MANLI_MTV001] = { /* Ognjen Nastic <ognjen@logosoft.ba> UNTESTED */ - .name = "Manli MuchTV M-TV001/Behold TV 401", + .name = "Manli MuchTV M-TV001", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_PAL, .radio_type = UNSET, @@ -2195,6 +2199,8 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_BEHOLD_409FM] = { /* <http://tuner.beholder.ru>, Sergey <skiv@orel.ru> */ + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 409 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -2202,6 +2208,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -2908,15 +2915,13 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_MD7134_BRIDGE_2] = { - /* This card has two saa7134 chips on it, - but only one of them is currently working. - The programming for the primary decoder is - in SAA7134_BOARD_MD7134 */ + /* The second saa7134 on this card only serves as DVB-S host bridge */ .name = "Medion 7134 Bridge #2", .audio_clock = 0x00187de7, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, }, [SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS] = { .name = "LifeView FlyDVB-T Hybrid Cardbus/MSI TV @nywhere A/D NB", @@ -3330,7 +3335,7 @@ struct saa7134_board saa7134_boards[] = { /* Juan Pablo Sormani <sorman@gmail.com> */ .name = "Encore ENLTV-FM", .audio_clock = 0x00200000, - .tuner_type = TUNER_PHILIPS_ATSC, + .tuner_type = TUNER_PHILIPS_FCV1236D, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -3575,12 +3580,15 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_401] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 401", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FQ1216ME, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3601,12 +3609,15 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_403] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 403", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FQ1216ME, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3623,12 +3634,15 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_403FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 403 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FQ1216ME, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3649,6 +3663,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_405] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 405", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3656,6 +3672,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3673,6 +3690,8 @@ struct saa7134_board saa7134_boards[] = { }, [SAA7134_BOARD_BEHOLD_405FM] = { /* Sergey <skiv@orel.ru> */ + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 405 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3680,6 +3699,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3700,6 +3720,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_407] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 407", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3707,7 +3729,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .gpiomask = 0xc0c000, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3727,6 +3749,8 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_407FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 407 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3734,7 +3758,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, - .gpiomask = 0xc0c000, + .gpiomask = 0x00008000, .inputs = {{ .name = name_svideo, .vmux = 8, @@ -3759,6 +3783,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_409] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 409", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3766,6 +3792,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -3782,6 +3809,8 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_BEHOLD_505FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 505 FM/RDS", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3789,6 +3818,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -3813,6 +3843,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_507_9FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, @@ -3820,6 +3852,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, .inputs = {{ .name = name_tv, .vmux = 3, @@ -3840,6 +3873,8 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV Columbus TVFM", .audio_clock = 0x00187de7, .tuner_type = TUNER_ALPS_TSBE5_PAL, @@ -3847,23 +3882,28 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x000A8004, .inputs = {{ .name = name_tv, .vmux = 3, .amux = TV, .tv = 1, - },{ + .gpio = 0x000A8004, + }, { .name = name_comp1, .vmux = 1, .amux = LINE1, - },{ + .gpio = 0x000A8000, + }, { .name = name_svideo, .vmux = 8, .amux = LINE1, - }}, + .gpio = 0x000A8000, + } }, .radio = { .name = name_radio, .amux = LINE2, + .gpio = 0x000A8000, }, }, [SAA7134_BOARD_BEHOLD_607_9FM] = { @@ -3992,6 +4032,221 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x6000, }, }, + [SAA7134_BOARD_PHILIPS_SNAKE] = { + .name = "NXP Snake DVB-S reference design", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + }, + [SAA7134_BOARD_CREATIX_CTX953] = { + .name = "Medion/Creatix CTX953 Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 0, + .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 = 8, + .amux = LINE1, + } }, + }, + [SAA7134_BOARD_MSI_TVANYWHERE_AD11] = { + .name = "MSI TV@nywhere A/D v1.1", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_AVERMEDIA_CARDBUS_506] = { + .name = "AVerMedia Cardbus TV/Radio (E506R)", + .audio_clock = 0x187de7, + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + /* + TODO: + .mpeg = SAA7134_MPEG_DVB, + */ + + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, + [SAA7134_BOARD_AVERMEDIA_A16D] = { + .name = "AVerMedia Hybrid TV/Radio (A16D)", + .audio_clock = 0x187de7, + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_AVERMEDIA_M115] = { + .name = "Avermedia M115", + .audio_clock = 0x187de7, + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + }, + [SAA7134_BOARD_VIDEOMATE_T750] = { + /* John Newbigin <jn@it.swin.edu.au> */ + .name = "Compro VideoMate T750", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .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, + } }, + .radio = { + .name = name_radio, + .amux = TV, + } + }, + [SAA7134_BOARD_AVERMEDIA_A700_PRO] = { + /* Matthias Schwarzott <zzam@gentoo.org> */ + .name = "Avermedia DVB-S Pro A700", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + /* no DVB support for now */ + /* .mpeg = SAA7134_MPEG_DVB, */ + .inputs = { { + .name = name_comp, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + } }, + }, + [SAA7134_BOARD_AVERMEDIA_A700_HYBRID] = { + /* Matthias Schwarzott <zzam@gentoo.org> */ + .name = "Avermedia DVB-S Hybrid+FM A700", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, /* TUNER_XC2028 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + /* no DVB support for now */ + /* .mpeg = SAA7134_MPEG_DVB, */ + .inputs = { { + .name = name_comp, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + } }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -4224,6 +4479,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .driver_data = SAA7134_BOARD_MD2819, },{ .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa7a1, + .driver_data = SAA7134_BOARD_AVERMEDIA_A700_PRO, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa7a2, + .driver_data = SAA7134_BOARD_AVERMEDIA_A700_HYBRID, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, .subvendor = 0x1461, /* Avermedia Technologies Inc */ .subdevice = 0x2115, @@ -4942,7 +5209,43 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x1822, /*Twinhan Technology Co. Ltd*/ .subdevice = 0x0022, .driver_data = SAA7134_BOARD_TWINHAN_DTV_DVB_3056, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x16be, + .subdevice = 0x0010, /* Medion version CTX953_V.1.4.3 */ + .driver_data = SAA7134_BOARD_CREATIX_CTX953, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1462, /* MSI */ + .subdevice = 0x8625, /* TV@nywhere A/D v1.1 */ + .driver_data = SAA7134_BOARD_MSI_TVANYWHERE_AD11, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf436, + .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_506, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf936, + .driver_data = SAA7134_BOARD_AVERMEDIA_A16D, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa836, + .driver_data = SAA7134_BOARD_AVERMEDIA_M115, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x185b, + .subdevice = 0xc900, + .driver_data = SAA7134_BOARD_VIDEOMATE_T750, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -4998,6 +5301,77 @@ static void board_flyvideo(struct saa7134_dev *dev) dev->name, dev->name, dev->name); } +static int saa7134_xc2028_callback(struct saa7134_dev *dev, + int command, int arg) +{ + switch (command) { + case XC2028_TUNER_RESET: + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); + mdelay(250); + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0); + mdelay(250); + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); + mdelay(250); + saa_andorl(SAA7133_ANALOG_IO_SELECT >> 2, 0x02, 0x02); + saa_andorl(SAA7134_ANALOG_IN_CTRL1 >> 2, 0x81, 0x81); + saa_andorl(SAA7134_AUDIO_CLOCK0 >> 2, 0x03187de7, 0x03187de7); + saa_andorl(SAA7134_AUDIO_PLL_CTRL >> 2, 0x03, 0x03); + saa_andorl(SAA7134_AUDIO_CLOCKS_PER_FIELD0 >> 2, + 0x0001e000, 0x0001e000); + return 0; + } + return -EINVAL; +} + + +static int saa7134_tda8290_callback(struct saa7134_dev *dev, + int command, int arg) +{ + u8 sync_control; + + switch (command) { + case 0: /* switch LNA gain through GPIO 22*/ + saa7134_set_gpio(dev, 22, arg) ; + break; + case 1: /* vsync output at GPIO22. 50 / 60Hz */ + saa_andorb(SAA7134_VIDEO_PORT_CTRL3, 0x80, 0x80); + saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x03); + if (arg == 1) + sync_control = 11; + else + sync_control = 17; + saa_writeb(SAA7134_VGATE_START, sync_control); + saa_writeb(SAA7134_VGATE_STOP, sync_control + 1); + saa_andorb(SAA7134_MISC_VGATE_MSB, 0x03, 0x00); + break; + default: + return -EINVAL; + } + + return 0; +} + +int saa7134_tuner_callback(void *priv, int command, int arg) +{ + struct saa7134_dev *dev = priv; + if (dev != NULL) { + switch (dev->tuner_type) { + case TUNER_PHILIPS_TDA8290: + return saa7134_tda8290_callback(dev, command, arg); + case TUNER_XC2028: + return saa7134_xc2028_callback(dev, command, arg); + } + } else { + printk(KERN_ERR "saa7134: Error - device struct undefined.\n"); + return -EINVAL; + } + return -EINVAL; +} +EXPORT_SYMBOL(saa7134_tuner_callback); + /* ----------------------------------------------------------- */ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) @@ -5067,6 +5441,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_VIDEOMATE_DVBT_200: case SAA7134_BOARD_VIDEOMATE_DVBT_200A: + case SAA7134_BOARD_VIDEOMATE_T750: case SAA7134_BOARD_MANLI_MTV001: case SAA7134_BOARD_MANLI_MTV002: case SAA7134_BOARD_BEHOLD_409FM: @@ -5133,11 +5508,29 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000); break; case SAA7134_BOARD_AVERMEDIA_CARDBUS: - case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + case SAA7134_BOARD_AVERMEDIA_M115: + case SAA7134_BOARD_AVERMEDIA_A16D: + /* power-down tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0); + msleep(10); /* power-up tuner chip */ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0xffffffff); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff); - msleep(1); + msleep(10); + break; + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + /* power-down tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0); + msleep(10); + /* power-up tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0x000A8004); + msleep(10); + /* remote via GPIO */ + dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_RTD_VFG7350: @@ -5160,7 +5553,6 @@ int saa7134_board_init1(struct saa7134_dev *dev) dev->has_remote = SAA7134_REMOTE_I2C; break; case SAA7134_BOARD_AVERMEDIA_A169_B: - case SAA7134_BOARD_MD7134_BRIDGE_2: printk("%s: %s: dual saa713x broadcast decoders\n" "%s: Sorry, none of the inputs to this chip are supported yet.\n" "%s: Dual decoder functionality is disabled for now, use the other chip.\n", @@ -5172,6 +5564,15 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x8c040007, 0x8c040007); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd); break; + case SAA7134_BOARD_AVERMEDIA_A700_PRO: + case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: + /* write windows gpio values */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100); + printk("%s: %s: hybrid analog/dvb card\n" + "%s: Sorry, only the analog inputs are supported for now.\n", + dev->name, card(dev).name, dev->name); + break; } return 0; } @@ -5200,11 +5601,16 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->tuner_type = saa7134_boards[dev->board].tuner_type; if (TUNER_ABSENT != dev->tuner_type) { - tun_setup.mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV; - tun_setup.type = dev->tuner_type; - tun_setup.addr = ADDR_UNSET; + tun_setup.mode_mask = T_RADIO | + T_ANALOG_TV | + T_DIGITAL_TV; + tun_setup.type = dev->tuner_type; + tun_setup.addr = ADDR_UNSET; + tun_setup.tuner_callback = saa7134_tuner_callback; - saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR, &tun_setup); + saa7134_i2c_call_clients(dev, + TUNER_SET_TYPE_ADDR, + &tun_setup); } break; case SAA7134_BOARD_MD7134: @@ -5275,14 +5681,25 @@ int saa7134_board_init2(struct saa7134_dev *dev) &tda9887_cfg); } - tun_setup.mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV; + tun_setup.mode_mask = T_RADIO | + T_ANALOG_TV | + T_DIGITAL_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = ADDR_UNSET; - saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup); + saa7134_i2c_call_clients(dev, + TUNER_SET_TYPE_ADDR, &tun_setup); } break; case SAA7134_BOARD_PHILIPS_EUROPA: + if (dev->autodetected && (dev->eedata[0x41] == 0x1c)) { + /* Reconfigure board as Snake reference design */ + dev->board = SAA7134_BOARD_PHILIPS_SNAKE; + dev->tuner_type = saa7134_boards[dev->board].tuner_type; + printk(KERN_INFO "%s: Reconfigured board as %s\n", + dev->name, saa7134_boards[dev->board].name); + break; + } case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: /* The Philips EUROPA based hybrid boards have the tuner connected through @@ -5333,6 +5750,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) case SAA7134_BOARD_MEDION_MD8800_QUADRO: case SAA7134_BOARD_AVERMEDIA_SUPER_007: case SAA7134_BOARD_TWINHAN_DTV_DVB_3056: + case SAA7134_BOARD_CREATIX_CTX953: /* this is a hybrid board, initialize to analog mode * and configure firmware eeprom address */ @@ -5402,13 +5820,46 @@ int saa7134_board_init2(struct saa7134_dev *dev) break; } break; + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + { + struct v4l2_priv_tun_config tea5767_cfg; + struct tea5767_ctrl ctl; + + dev->i2c_client.addr = 0xC0; + /* set TEA5767(analog FM) defines */ + memset(&ctl, 0, sizeof(ctl)); + ctl.xtal_freq = TEA5767_HIGH_LO_13MHz; + tea5767_cfg.tuner = TUNER_TEA5767; + tea5767_cfg.priv = &ctl; + saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tea5767_cfg); + } + break; } + + if (dev->tuner_type == TUNER_XC2028) { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(ctl)); + memset(&ctl, 0, sizeof(ctl)); + + ctl.fname = XC2028_DEFAULT_FIRMWARE; + ctl.max_len = 64; + + switch (dev->board) { + case SAA7134_BOARD_AVERMEDIA_A16D: + ctl.demod = XC3028_FE_ZARLINK456; + break; + default: + ctl.demod = XC3028_FE_OREN538; + ctl.mts = 1; + } + + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg); + } + return 0; } - -/* ----------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 58ab163fdbd..eec127864fe 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -42,23 +42,23 @@ MODULE_LICENSE("GPL"); /* ------------------------------------------------------------------ */ -static unsigned int irq_debug = 0; +static unsigned int irq_debug; module_param(irq_debug, int, 0644); MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]"); -static unsigned int core_debug = 0; +static unsigned int core_debug; module_param(core_debug, int, 0644); MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); -static unsigned int gpio_tracking = 0; +static unsigned int gpio_tracking; module_param(gpio_tracking, int, 0644); MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]"); -static unsigned int alsa = 0; +static unsigned int alsa; module_param(alsa, int, 0644); MODULE_PARM_DESC(alsa,"enable ALSA DMA sound [dmasound]"); -static unsigned int oss = 0; +static unsigned int oss; module_param(oss, int, 0644); MODULE_PARM_DESC(oss,"enable OSS DMA sound [dmasound]"); @@ -142,39 +142,6 @@ void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value) } } -int saa7134_tuner_callback(void *ptr, int command, int arg) -{ - u8 sync_control; - struct saa7134_dev *dev = ptr; - - switch (dev->tuner_type) { - case TUNER_PHILIPS_TDA8290: - switch (command) { - case 0: /* switch LNA gain through GPIO 22*/ - saa7134_set_gpio(dev, 22, arg) ; - break; - case 1: /* vsync output at GPIO22. 50 / 60Hz */ - dprintk("setting GPIO22 to vsync %d\n", arg); - saa_andorb(SAA7134_VIDEO_PORT_CTRL3, 0x80, 0x80); - saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x03); - if (arg == 1) - sync_control = 11; - else - sync_control = 17; - saa_writeb(SAA7134_VGATE_START, sync_control); - saa_writeb(SAA7134_VGATE_STOP, sync_control + 1); - saa_andorb(SAA7134_MISC_VGATE_MSB, 0x03, 0x00); - break; - default: - return -EINVAL; - } - break; - default: - return -ENODEV; - } - return 0; -} - /* ------------------------------------------------------------------ */ @@ -897,6 +864,10 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, struct saa7134_dev *dev; struct saa7134_mpeg_ops *mops; int err; + int mask; + + if (saa7134_devcount == SAA7134_MAXBOARDS) + return -ENOMEM; dev = kzalloc(sizeof(*dev),GFP_KERNEL); if (NULL == dev) @@ -1094,6 +1065,11 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, if (TUNER_ABSENT != dev->tuner_type) saa7134_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + if (card(dev).gpiomask != 0) { + mask = card(dev).gpiomask; + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, 0); + } return 0; fail4: diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index ea2be9eceeb..2d16be2259d 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -33,33 +33,40 @@ #include "saa7134.h" #include <media/v4l2-common.h> #include "dvb-pll.h" +#include <dvb_frontend.h> #include "mt352.h" #include "mt352_priv.h" /* FIXME */ #include "tda1004x.h" #include "nxt200x.h" +#include "tuner-xc2028.h" #include "tda10086.h" #include "tda826x.h" #include "tda827x.h" #include "isl6421.h" +#include "isl6405.h" +#include "lnbp21.h" +#include "tuner-simple.h" MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); -static unsigned int antenna_pwr = 0; +static unsigned int antenna_pwr; module_param(antenna_pwr, int, 0444); MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)"); -static int use_frontend = 0; +static int use_frontend; module_param(use_frontend, int, 0644); MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)"); -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off)."); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + #define dprintk(fmt, arg...) do { if (debug) \ printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0) @@ -91,7 +98,7 @@ static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on) saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28)); udelay(10); ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27); - dprintk("%s %s\n", __FUNCTION__, ok ? "on" : "off"); + dprintk("%s %s\n", __func__, ok ? "on" : "off"); if (!ok) saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26)); @@ -111,7 +118,7 @@ static int mt352_pinnacle_init(struct dvb_frontend* fe) static u8 irq_cfg [] = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 }; struct saa7134_dev *dev= fe->dvb->priv; - dprintk("%s called\n", __FUNCTION__); + dprintk("%s called\n", __func__); mt352_write(fe, clock_config, sizeof(clock_config)); udelay(200); @@ -146,6 +153,26 @@ static int mt352_aver777_init(struct dvb_frontend* fe) return 0; } +static int mt352_aver_a16d_init(struct dvb_frontend *fe) +{ + static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 }; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + + return 0; +} + + + static int mt352_pinnacle_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters* params) { @@ -188,6 +215,16 @@ static struct mt352_config avermedia_777 = { .demod_init = mt352_aver777_init, }; +static struct mt352_config avermedia_16d = { + .demod_address = 0xf, + .demod_init = mt352_aver_a16d_init, +}; + +static struct mt352_config avermedia_e506r_mt352_dev = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, +}; + /* ================================================================== * tda1004x based DVB-T cards, helper functions */ @@ -430,8 +467,6 @@ static struct tda1004x_config philips_europa_config = { .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - static struct tda1004x_config medion_cardbus = { .demod_address = 0x08, .invert = 1, @@ -447,47 +482,6 @@ static struct tda1004x_config medion_cardbus = { * tda 1004x based cards with philips silicon tuner */ -static void philips_tda827x_lna_gain(struct dvb_frontend *fe, int high) -{ - struct saa7134_dev *dev = fe->dvb->priv; - struct tda1004x_state *state = fe->demodulator_priv; - u8 addr = state->config->i2c_gate; - u8 config = state->config->tuner_config; - u8 GP00_CF[] = {0x20, 0x01}; - u8 GP00_LEV[] = {0x22, 0x00}; - - struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = GP00_CF, .len = 2}; - if (config) { - if (high) { - dprintk("setting LNA to high gain\n"); - } else { - dprintk("setting LNA to low gain\n"); - } - } - switch (config) { - case 0: /* no LNA */ - break; - case 1: /* switch is GPIO 0 of tda8290 */ - case 2: - /* turn Vsync off */ - saa7134_set_gpio(dev, 22, 0); - GP00_LEV[1] = high ? 0 : 1; - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { - wprintk("could not access tda8290 at addr: 0x%02x\n", - addr << 1); - return; - } - msg.buf = GP00_LEV; - if (config == 2) - GP00_LEV[1] = high ? 1 : 0; - i2c_transfer(&dev->i2c_adap, &msg, 1); - break; - case 3: /* switch with GPIO of saa713x */ - saa7134_set_gpio(dev, 22, high); - break; - } -} - static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable) { struct tda1004x_state *state = fe->demodulator_priv; @@ -510,8 +504,6 @@ static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable) return 0; } -/* ------------------------------------------------------------------ */ - static int philips_tda827x_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; @@ -546,28 +538,57 @@ static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) return 0; } -static struct tda827x_config tda827x_cfg = { - .lna_gain = philips_tda827x_lna_gain, - .init = philips_tda827x_tuner_init, - .sleep = philips_tda827x_tuner_sleep -}; - -static void configure_tda827x_fe(struct saa7134_dev *dev, struct tda1004x_config *tda_conf) +static void configure_tda827x_fe(struct saa7134_dev *dev, struct tda1004x_config *cdec_conf, + struct tda827x_config *tuner_conf) { - dev->dvb.frontend = dvb_attach(tda10046_attach, tda_conf, &dev->i2c_adap); + dev->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap); if (dev->dvb.frontend) { - if (tda_conf->i2c_gate) + if (cdec_conf->i2c_gate) dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - if (dvb_attach(tda827x_attach, dev->dvb.frontend, tda_conf->tuner_address, - &dev->i2c_adap,&tda827x_cfg) == NULL) { + if (dvb_attach(tda827x_attach, dev->dvb.frontend, cdec_conf->tuner_address, + &dev->i2c_adap, tuner_conf) == NULL) { wprintk("no tda827x tuner found at addr: %02x\n", - tda_conf->tuner_address); + cdec_conf->tuner_address); } } } /* ------------------------------------------------------------------ */ +static struct tda827x_config tda827x_cfg_0 = { + .tuner_callback = saa7134_tuner_callback, + .init = philips_tda827x_tuner_init, + .sleep = philips_tda827x_tuner_sleep, + .config = 0, + .switch_addr = 0 +}; + +static struct tda827x_config tda827x_cfg_1 = { + .tuner_callback = saa7134_tuner_callback, + .init = philips_tda827x_tuner_init, + .sleep = philips_tda827x_tuner_sleep, + .config = 1, + .switch_addr = 0x4b +}; + +static struct tda827x_config tda827x_cfg_2 = { + .tuner_callback = saa7134_tuner_callback, + .init = philips_tda827x_tuner_init, + .sleep = philips_tda827x_tuner_sleep, + .config = 2, + .switch_addr = 0x4b +}; + +static struct tda827x_config tda827x_cfg_2_sw42 = { + .tuner_callback = saa7134_tuner_callback, + .init = philips_tda827x_tuner_init, + .sleep = philips_tda827x_tuner_sleep, + .config = 2, + .switch_addr = 0x42 +}; + +/* ------------------------------------------------------------------ */ + static struct tda1004x_config tda827x_lifeview_config = { .demod_address = 0x08, .invert = 1, @@ -590,7 +611,6 @@ static struct tda1004x_config philips_tiger_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x61, - .tuner_config = 0, .antenna_switch= 1, .request_firmware = philips_tda1004x_request_firmware }; @@ -605,7 +625,6 @@ static struct tda1004x_config cinergy_ht_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x61, - .tuner_config = 0, .request_firmware = philips_tda1004x_request_firmware }; @@ -619,7 +638,6 @@ static struct tda1004x_config cinergy_ht_pci_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x60, - .tuner_config = 0, .request_firmware = philips_tda1004x_request_firmware }; @@ -633,7 +651,6 @@ static struct tda1004x_config philips_tiger_s_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x61, - .tuner_config = 2, .antenna_switch= 1, .request_firmware = philips_tda1004x_request_firmware }; @@ -648,7 +665,6 @@ static struct tda1004x_config pinnacle_pctv_310i_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x61, - .tuner_config = 1, .request_firmware = philips_tda1004x_request_firmware }; @@ -662,7 +678,6 @@ static struct tda1004x_config hauppauge_hvr_1110_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x61, - .tuner_config = 1, .request_firmware = philips_tda1004x_request_firmware }; @@ -676,7 +691,6 @@ static struct tda1004x_config asus_p7131_dual_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x61, - .tuner_config = 0, .antenna_switch= 2, .request_firmware = philips_tda1004x_request_firmware }; @@ -715,7 +729,6 @@ static struct tda1004x_config md8800_dvbt_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x60, - .tuner_config = 0, .request_firmware = philips_tda1004x_request_firmware }; @@ -729,7 +742,6 @@ static struct tda1004x_config asus_p7131_4871_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x61, - .tuner_config = 2, .antenna_switch= 2, .request_firmware = philips_tda1004x_request_firmware }; @@ -744,7 +756,6 @@ static struct tda1004x_config asus_p7131_hybrid_lna_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x61, - .tuner_config = 2, .antenna_switch= 2, .request_firmware = philips_tda1004x_request_firmware }; @@ -759,7 +770,6 @@ static struct tda1004x_config kworld_dvb_t_210_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x61, - .tuner_config = 2, .antenna_switch= 1, .request_firmware = philips_tda1004x_request_firmware }; @@ -774,7 +784,6 @@ static struct tda1004x_config avermedia_super_007_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x4b, .tuner_address = 0x60, - .tuner_config = 0, .antenna_switch= 1, .request_firmware = philips_tda1004x_request_firmware }; @@ -789,7 +798,6 @@ static struct tda1004x_config twinhan_dtv_dvb_3056_config = { .if_freq = TDA10046_FREQ_045, .i2c_gate = 0x42, .tuner_address = 0x61, - .tuner_config = 2, .antenna_switch = 1, .request_firmware = philips_tda1004x_request_firmware }; @@ -817,9 +825,10 @@ static int ads_duo_tuner_sleep(struct dvb_frontend *fe) } static struct tda827x_config ads_duo_cfg = { - .lna_gain = philips_tda827x_lna_gain, + .tuner_callback = saa7134_tuner_callback, .init = ads_duo_tuner_init, - .sleep = ads_duo_tuner_sleep + .sleep = ads_duo_tuner_sleep, + .config = 0 }; static struct tda1004x_config ads_tech_duo_config = { @@ -842,8 +851,73 @@ static struct tda10086_config flydvbs = { .demod_address = 0x0e, .invert = 0, .diseqc_tone = 0, + .xtal_freq = TDA10086_XTAL_16M, }; +static struct tda10086_config sd1878_4m = { + .demod_address = 0x0e, + .invert = 0, + .diseqc_tone = 0, + .xtal_freq = TDA10086_XTAL_4M, +}; + +/* ------------------------------------------------------------------ + * special case: lnb supply is connected to the gated i2c + */ + +static int md8800_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + int res = -EIO; + struct saa7134_dev *dev = fe->dvb->priv; + if (fe->ops.i2c_gate_ctrl) { + fe->ops.i2c_gate_ctrl(fe, 1); + if (dev->original_set_voltage) + res = dev->original_set_voltage(fe, voltage); + fe->ops.i2c_gate_ctrl(fe, 0); + } + return res; +}; + +static int md8800_set_high_voltage(struct dvb_frontend *fe, long arg) +{ + int res = -EIO; + struct saa7134_dev *dev = fe->dvb->priv; + if (fe->ops.i2c_gate_ctrl) { + fe->ops.i2c_gate_ctrl(fe, 1); + if (dev->original_set_high_voltage) + res = dev->original_set_high_voltage(fe, arg); + fe->ops.i2c_gate_ctrl(fe, 0); + } + return res; +}; + +static int md8800_set_voltage2(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct saa7134_dev *dev = fe->dvb->priv; + u8 wbuf[2] = { 0x1f, 00 }; + u8 rbuf; + struct i2c_msg msg[] = { { .addr = 0x08, .flags = 0, .buf = wbuf, .len = 1 }, + { .addr = 0x08, .flags = I2C_M_RD, .buf = &rbuf, .len = 1 } }; + + if (i2c_transfer(&dev->i2c_adap, msg, 2) != 2) + return -EIO; + /* NOTE: this assumes that gpo1 is used, it might be bit 5 (gpo2) */ + if (voltage == SEC_VOLTAGE_18) + wbuf[1] = rbuf | 0x10; + else + wbuf[1] = rbuf & 0xef; + msg[0].len = 2; + i2c_transfer(&dev->i2c_adap, msg, 1); + return 0; +} + +static int md8800_set_high_voltage2(struct dvb_frontend *fe, long arg) +{ + struct saa7134_dev *dev = fe->dvb->priv; + wprintk("%s: sorry can't set high LNB supply voltage from here\n", __func__); + return -EIO; +} + /* ================================================================== * nxt200x based ATSC cards, helper functions */ @@ -863,12 +937,14 @@ static struct nxt200x_config kworldatsc110 = { static int dvb_init(struct saa7134_dev *dev) { int ret; + int attach_xc3028 = 0; + /* init struct videobuf_dvb */ dev->ts.nr_bufs = 32; dev->ts.nr_packets = 32*4; dev->dvb.name = dev->name; - videobuf_queue_pci_init(&dev->dvb.dvbq, &saa7134_ts_qops, - dev->pci, &dev->slock, + videobuf_queue_sg_init(&dev->dvb.dvbq, &saa7134_ts_qops, + &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_ALTERNATE, sizeof(struct saa7134_buf), @@ -889,17 +965,25 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777, &dev->i2c_adap); if (dev->dvb.frontend) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, DVB_PLL_PHILIPS_TD1316); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->i2c_adap, 0x61, + TUNER_PHILIPS_TD1316); } break; + case SAA7134_BOARD_AVERMEDIA_A16D: + dprintk("avertv A16D dvb setup\n"); + dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_16d, + &dev->i2c_adap); + attach_xc3028 = 1; + break; case SAA7134_BOARD_MD7134: dev->dvb.frontend = dvb_attach(tda10046_attach, &medion_cardbus, &dev->i2c_adap); if (dev->dvb.frontend) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, medion_cardbus.tuner_address, - &dev->i2c_adap, DVB_PLL_FMD1216ME); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->i2c_adap, medion_cardbus.tuner_address, + TUNER_PHILIPS_FMD1216ME_MK3); } break; case SAA7134_BOARD_PHILIPS_TOUGH: @@ -913,7 +997,7 @@ static int dvb_init(struct saa7134_dev *dev) break; case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: - configure_tda827x_fe(dev, &tda827x_lifeview_config); + configure_tda827x_fe(dev, &tda827x_lifeview_config, &tda827x_cfg_0); break; case SAA7134_BOARD_PHILIPS_EUROPA: case SAA7134_BOARD_VIDEOMATE_DVBT_300: @@ -938,36 +1022,36 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_KWORLD_DVBT_210: - configure_tda827x_fe(dev, &kworld_dvb_t_210_config); + configure_tda827x_fe(dev, &kworld_dvb_t_210_config, &tda827x_cfg_2); break; case SAA7134_BOARD_PHILIPS_TIGER: - configure_tda827x_fe(dev, &philips_tiger_config); + configure_tda827x_fe(dev, &philips_tiger_config, &tda827x_cfg_0); break; case SAA7134_BOARD_PINNACLE_PCTV_310i: - configure_tda827x_fe(dev, &pinnacle_pctv_310i_config); + configure_tda827x_fe(dev, &pinnacle_pctv_310i_config, &tda827x_cfg_1); break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: - configure_tda827x_fe(dev, &hauppauge_hvr_1110_config); + configure_tda827x_fe(dev, &hauppauge_hvr_1110_config, &tda827x_cfg_1); break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: - configure_tda827x_fe(dev, &asus_p7131_dual_config); + configure_tda827x_fe(dev, &asus_p7131_dual_config, &tda827x_cfg_0); break; case SAA7134_BOARD_FLYDVBT_LR301: - configure_tda827x_fe(dev, &tda827x_lifeview_config); + configure_tda827x_fe(dev, &tda827x_lifeview_config, &tda827x_cfg_0); break; case SAA7134_BOARD_FLYDVB_TRIO: if(! use_frontend) { /* terrestrial */ - configure_tda827x_fe(dev, &lifeview_trio_config); - } else { /* satellite */ + configure_tda827x_fe(dev, &lifeview_trio_config, &tda827x_cfg_0); + } 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) { - wprintk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__); + wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__); } if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0) == NULL) { - wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__); + wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__); } } } @@ -979,18 +1063,56 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap); if (dev->dvb.frontend) { if (dvb_attach(tda827x_attach,dev->dvb.frontend, - ads_tech_duo_config.tuner_address, - &dev->i2c_adap,&ads_duo_cfg) == NULL) { + ads_tech_duo_config.tuner_address, &dev->i2c_adap, + &ads_duo_cfg) == NULL) { wprintk("no tda827x tuner found at addr: %02x\n", ads_tech_duo_config.tuner_address); } } break; case SAA7134_BOARD_TEVION_DVBT_220RF: - configure_tda827x_fe(dev, &tevion_dvbt220rf_config); + configure_tda827x_fe(dev, &tevion_dvbt220rf_config, &tda827x_cfg_0); break; case SAA7134_BOARD_MEDION_MD8800_QUADRO: - configure_tda827x_fe(dev, &md8800_dvbt_config); + if (!use_frontend) { /* terrestrial */ + configure_tda827x_fe(dev, &md8800_dvbt_config, &tda827x_cfg_0); + } else { /* satellite */ + dev->dvb.frontend = dvb_attach(tda10086_attach, + &flydvbs, &dev->i2c_adap); + if (dev->dvb.frontend) { + struct dvb_frontend *fe = dev->dvb.frontend; + u8 dev_id = dev->eedata[2]; + u8 data = 0xc4; + struct i2c_msg msg = {.addr = 0x08, .flags = 0, .len = 1}; + + if (dvb_attach(tda826x_attach, dev->dvb.frontend, + 0x60, &dev->i2c_adap, 0) == NULL) + wprintk("%s: Medion Quadro, no tda826x " + "found !\n", __func__); + if (dev_id != 0x08) { + /* we need to open the i2c gate (we know it exists) */ + fe->ops.i2c_gate_ctrl(fe, 1); + if (dvb_attach(isl6405_attach, fe, + &dev->i2c_adap, 0x08, 0, 0) == NULL) + wprintk("%s: Medion Quadro, no ISL6405 " + "found !\n", __func__); + if (dev_id == 0x07) { + /* fire up the 2nd section of the LNB supply since + we can't do this from the other section */ + msg.buf = &data; + i2c_transfer(&dev->i2c_adap, &msg, 1); + } + fe->ops.i2c_gate_ctrl(fe, 0); + dev->original_set_voltage = fe->ops.set_voltage; + fe->ops.set_voltage = md8800_set_voltage; + dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage; + fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage; + } else { + fe->ops.set_voltage = md8800_set_voltage2; + fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage2; + } + } + } break; case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: dev->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180, @@ -1004,8 +1126,9 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110, &dev->i2c_adap); if (dev->dvb.frontend) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, DVB_PLL_TUV1236D); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->i2c_adap, 0x61, + TUNER_PHILIPS_TUV1236D); } break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -1014,11 +1137,11 @@ static int dvb_init(struct saa7134_dev *dev) if (dev->dvb.frontend) { if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x60, &dev->i2c_adap, 0) == NULL) { - wprintk("%s: No tda826x found!\n", __FUNCTION__); + wprintk("%s: No tda826x found!\n", __func__); } if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0) == NULL) { - wprintk("%s: No ISL6421 found!\n", __FUNCTION__); + wprintk("%s: No ISL6421 found!\n", __func__); } } break; @@ -1030,8 +1153,9 @@ static int dvb_init(struct saa7134_dev *dev) dev->original_demod_sleep = dev->dvb.frontend->ops.sleep; dev->dvb.frontend->ops.sleep = philips_europa_demod_sleep; - dvb_attach(dvb_pll_attach, dev->dvb.frontend, medion_cardbus.tuner_address, - &dev->i2c_adap, DVB_PLL_FMD1216ME); + dvb_attach(simple_tuner_attach, dev->dvb.frontend, + &dev->i2c_adap, medion_cardbus.tuner_address, + TUNER_PHILIPS_FMD1216ME_MK3); } break; case SAA7134_BOARD_VIDEOMATE_DVBT_200A: @@ -1044,38 +1168,107 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_CINERGY_HT_PCMCIA: - configure_tda827x_fe(dev, &cinergy_ht_config); + configure_tda827x_fe(dev, &cinergy_ht_config, &tda827x_cfg_0); break; case SAA7134_BOARD_CINERGY_HT_PCI: - configure_tda827x_fe(dev, &cinergy_ht_pci_config); + configure_tda827x_fe(dev, &cinergy_ht_pci_config, &tda827x_cfg_0); break; case SAA7134_BOARD_PHILIPS_TIGER_S: - configure_tda827x_fe(dev, &philips_tiger_s_config); + configure_tda827x_fe(dev, &philips_tiger_s_config, &tda827x_cfg_2); break; case SAA7134_BOARD_ASUS_P7131_4871: - configure_tda827x_fe(dev, &asus_p7131_4871_config); + configure_tda827x_fe(dev, &asus_p7131_4871_config, &tda827x_cfg_2); break; case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: - configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config); + configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config, &tda827x_cfg_2); break; case SAA7134_BOARD_AVERMEDIA_SUPER_007: - configure_tda827x_fe(dev, &avermedia_super_007_config); + configure_tda827x_fe(dev, &avermedia_super_007_config, &tda827x_cfg_0); break; case SAA7134_BOARD_TWINHAN_DTV_DVB_3056: - configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config); + configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config, &tda827x_cfg_2_sw42); + break; + case SAA7134_BOARD_PHILIPS_SNAKE: + dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, + &dev->i2c_adap); + if (dev->dvb.frontend) { + if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x60, + &dev->i2c_adap, 0) == NULL) + wprintk("%s: No tda826x found!\n", __func__); + if (dvb_attach(lnbp21_attach, dev->dvb.frontend, + &dev->i2c_adap, 0, 0) == NULL) + wprintk("%s: No lnbp21 found!\n", __func__); + } + break; + case SAA7134_BOARD_CREATIX_CTX953: + configure_tda827x_fe(dev, &md8800_dvbt_config, &tda827x_cfg_0); + break; + case SAA7134_BOARD_MSI_TVANYWHERE_AD11: + configure_tda827x_fe(dev, &philips_tiger_s_config, &tda827x_cfg_2); + break; + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + dev->dvb.frontend = dvb_attach(mt352_attach, + &avermedia_e506r_mt352_dev, + &dev->i2c_adap); + attach_xc3028 = 1; + break; + case SAA7134_BOARD_MD7134_BRIDGE_2: + dev->dvb.frontend = dvb_attach(tda10086_attach, + &sd1878_4m, &dev->i2c_adap); + if (dev->dvb.frontend) { + struct dvb_frontend *fe; + if (dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60, + &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) + wprintk("%s: MD7134 DVB-S, no SD1878 " + "found !\n", __func__); + /* we need to open the i2c gate (we know it exists) */ + fe = dev->dvb.frontend; + fe->ops.i2c_gate_ctrl(fe, 1); + if (dvb_attach(isl6405_attach, fe, + &dev->i2c_adap, 0x08, 0, 0) == NULL) + wprintk("%s: MD7134 DVB-S, no ISL6405 " + "found !\n", __func__); + fe->ops.i2c_gate_ctrl(fe, 0); + dev->original_set_voltage = fe->ops.set_voltage; + fe->ops.set_voltage = md8800_set_voltage; + dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage; + fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage; + } break; default: wprintk("Huh? unknown DVB card?\n"); break; } + if (attach_xc3028) { + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_adap, + .i2c_addr = 0x61, + }; + + if (!dev->dvb.frontend) + return -1; + + fe = dvb_attach(xc2028_attach, dev->dvb.frontend, &cfg); + if (!fe) { + printk(KERN_ERR "%s/2: xc3028 attach failed\n", + dev->name); + dvb_frontend_detach(dev->dvb.frontend); + dvb_unregister_frontend(dev->dvb.frontend); + dev->dvb.frontend = NULL; + return -1; + } + } + if (NULL == dev->dvb.frontend) { printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name); return -1; } /* register everything else */ - ret = videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev); + ret = videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev, + adapter_nr); /* this sequence is necessary to make the tda1004x load its firmware * and to enter analog mode of hybrid boards @@ -1106,9 +1299,22 @@ static int dvb_fini(struct saa7134_dev *dev) /* otherwise we don't detect the tuner on next insmod */ saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tda9887_cfg); + } else if (dev->board == SAA7134_BOARD_MEDION_MD8800_QUADRO) { + if ((dev->eedata[2] == 0x07) && use_frontend) { + /* turn off the 2nd lnb supply */ + u8 data = 0x80; + struct i2c_msg msg = {.addr = 0x08, .buf = &data, .flags = 0, .len = 1}; + struct dvb_frontend *fe; + fe = dev->dvb.frontend; + if (fe->ops.i2c_gate_ctrl) { + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&dev->i2c_adap, &msg, 1); + fe->ops.i2c_gate_ctrl(fe, 0); + } + } } - - videobuf_dvb_unregister(&dev->dvb); + if (dev->dvb.frontend) + videobuf_dvb_unregister(&dev->dvb); return 0; } diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index 3d2ec30de22..1314522a813 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -40,7 +40,7 @@ static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; module_param_array(empress_nr, int, NULL, 0444); MODULE_PARM_DESC(empress_nr,"ts device number"); -static unsigned int debug = 0; +static unsigned int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"enable debug messages"); @@ -402,7 +402,7 @@ static int empress_init(struct saa7134_dev *dev) { int err; - dprintk("%s: %s\n",dev->name,__FUNCTION__); + dprintk("%s: %s\n",dev->name,__func__); dev->empress_dev = video_device_alloc(); if (NULL == dev->empress_dev) return -ENOMEM; @@ -427,8 +427,8 @@ static int empress_init(struct saa7134_dev *dev) printk(KERN_INFO "%s: registered device video%d [mpeg]\n", dev->name,dev->empress_dev->minor & 0x1f); - videobuf_queue_pci_init(&dev->empress_tsq, &saa7134_ts_qops, - dev->pci, &dev->slock, + videobuf_queue_sg_init(&dev->empress_tsq, &saa7134_ts_qops, + &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_ALTERNATE, sizeof(struct saa7134_buf), @@ -440,7 +440,7 @@ static int empress_init(struct saa7134_dev *dev) static int empress_fini(struct saa7134_dev *dev) { - dprintk("%s: %s\n",dev->name,__FUNCTION__); + dprintk("%s: %s\n",dev->name,__func__); if (NULL == dev->empress_dev) return 0; diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index d3322c3018f..2ccfaba0c49 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -33,11 +33,11 @@ /* ----------------------------------------------------------- */ -static unsigned int i2c_debug = 0; +static unsigned int i2c_debug; module_param(i2c_debug, int, 0644); MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]"); -static unsigned int i2c_scan = 0; +static unsigned int i2c_scan; module_param(i2c_scan, int, 0444); MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); @@ -140,6 +140,8 @@ static inline int i2c_is_busy(enum i2c_status status) { switch (status) { case BUSY: + case TO_SCL: + case TO_ARB: return true; default: return false; diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index b4188819782..767ff30832f 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -27,15 +27,15 @@ #include "saa7134-reg.h" #include "saa7134.h" -static unsigned int disable_ir = 0; +static unsigned int disable_ir; module_param(disable_ir, int, 0444); MODULE_PARM_DESC(disable_ir,"disable infrared remote support"); -static unsigned int ir_debug = 0; +static unsigned int ir_debug; module_param(ir_debug, int, 0644); MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]"); -static int pinnacle_remote = 0; +static int pinnacle_remote; module_param(pinnacle_remote, int, 0644); /* Choose Pinnacle PCTV remote */ MODULE_PARM_DESC(pinnacle_remote, "Specify Pinnacle PCTV remote: 0=coloured, 1=grey (defaults to 0)"); @@ -331,6 +331,11 @@ int saa7134_input_init1(struct saa7134_dev *dev) break; case SAA7134_BOARD_MANLI_MTV001: case SAA7134_BOARD_MANLI_MTV002: + ir_codes = ir_codes_manli; + mask_keycode = 0x001f00; + mask_keyup = 0x004000; + polling = 50; /* ms */ + break; case SAA7134_BOARD_BEHOLD_409FM: case SAA7134_BOARD_BEHOLD_401: case SAA7134_BOARD_BEHOLD_403: @@ -343,7 +348,13 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_505FM: case SAA7134_BOARD_BEHOLD_507_9FM: ir_codes = ir_codes_manli; - mask_keycode = 0x001f00; + mask_keycode = 0x003f00; + mask_keyup = 0x004000; + polling = 50; /* ms */ + break; + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + ir_codes = ir_codes_behold_columbus; + mask_keycode = 0x003f00; mask_keyup = 0x004000; polling = 50; // ms break; diff --git a/drivers/media/video/saa7134/saa7134-reg.h b/drivers/media/video/saa7134/saa7134-reg.h index ac6431ba4fc..86f5eefdb0f 100644 --- a/drivers/media/video/saa7134/saa7134-reg.h +++ b/drivers/media/video/saa7134/saa7134-reg.h @@ -365,6 +365,9 @@ #define SAA7135_DSP_RWSTATE_RDB (1 << 1) #define SAA7135_DSP_RWSTATE_WRR (1 << 0) +#define SAA7135_DSP_RWCLEAR 0x586 +#define SAA7135_DSP_RWCLEAR_RERR 1 + /* ------------------------------------------------------------------ */ /* * Local variables: diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/video/saa7134/saa7134-ts.c index f1b8fcaeb43..eae72fd60ce 100644 --- a/drivers/media/video/saa7134/saa7134-ts.c +++ b/drivers/media/video/saa7134/saa7134-ts.c @@ -32,7 +32,7 @@ /* ------------------------------------------------------------------ */ -static unsigned int ts_debug = 0; +static unsigned int ts_debug; module_param(ts_debug, int, 0644); MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]"); diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c index 4e9810469ae..232af598d94 100644 --- a/drivers/media/video/saa7134/saa7134-tvaudio.c +++ b/drivers/media/video/saa7134/saa7134-tvaudio.c @@ -35,18 +35,18 @@ /* ------------------------------------------------------------------ */ -static unsigned int audio_debug = 0; +static unsigned int audio_debug; module_param(audio_debug, int, 0644); MODULE_PARM_DESC(audio_debug,"enable debug messages [tv audio]"); -static unsigned int audio_ddep = 0; +static unsigned int audio_ddep; module_param(audio_ddep, int, 0644); MODULE_PARM_DESC(audio_ddep,"audio ddep overwrite"); static int audio_clock_override = UNSET; module_param(audio_clock_override, int, 0644); -static int audio_clock_tweak = 0; +static int audio_clock_tweak; module_param(audio_clock_tweak, int, 0644); MODULE_PARM_DESC(audio_clock_tweak, "Audio clock tick fine tuning for cards with audio crystal that's slightly off (range [-1024 .. 1024])"); @@ -653,6 +653,17 @@ static char *stdres[0x20] = { #define DSP_RETRY 32 #define DSP_DELAY 16 +#define SAA7135_DSP_RWCLEAR_RERR 1 + +static inline int saa_dsp_reset_error_bit(struct saa7134_dev *dev) +{ + int state = saa_readb(SAA7135_DSP_RWSTATE); + if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) { + d2printk("%s: resetting error bit\n", dev->name); + saa_writeb(SAA7135_DSP_RWCLEAR, SAA7135_DSP_RWCLEAR_RERR); + } + return 0; +} static inline int saa_dsp_wait_bit(struct saa7134_dev *dev, int bit) { @@ -660,8 +671,8 @@ static inline int saa_dsp_wait_bit(struct saa7134_dev *dev, int bit) state = saa_readb(SAA7135_DSP_RWSTATE); if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) { - printk("%s: dsp access error\n",dev->name); - /* FIXME: send ack ... */ + printk(KERN_WARNING "%s: dsp access error\n", dev->name); + saa_dsp_reset_error_bit(dev); return -EIO; } while (0 == (state & bit)) { diff --git a/drivers/media/video/saa7134/saa7134-vbi.c b/drivers/media/video/saa7134/saa7134-vbi.c index f0d5ed9c2b0..cb0304298a9 100644 --- a/drivers/media/video/saa7134/saa7134-vbi.c +++ b/drivers/media/video/saa7134/saa7134-vbi.c @@ -31,7 +31,7 @@ /* ------------------------------------------------------------------ */ -static unsigned int vbi_debug = 0; +static unsigned int vbi_debug; module_param(vbi_debug, int, 0644); MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]"); diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 39c41ad97d0..a0baf2d0ba7 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -40,7 +40,7 @@ unsigned int video_debug; static unsigned int gbuffers = 8; -static unsigned int noninterlaced = 0; +static unsigned int noninterlaced; /* 0 */ static unsigned int gbufsize = 720*576*4; static unsigned int gbufsize_max = 720*576*4; static char secam[] = "--"; @@ -626,13 +626,8 @@ void saa7134_set_tvnorm_hw(struct saa7134_dev *dev) { saa7134_set_decoder(dev); - if (card_in(dev, dev->ctl_input).tv) { - if ((card(dev).tuner_type == TUNER_PHILIPS_TDA8290) - && ((card(dev).tuner_config == 1) - || (card(dev).tuner_config == 2))) - saa7134_set_gpio(dev, 22, 5); + if (card_in(dev, dev->ctl_input).tv) saa7134_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id); - } } static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale) @@ -1350,14 +1345,14 @@ static int video_open(struct inode *inode, struct file *file) fh->height = 576; v4l2_prio_open(&dev->prio,&fh->prio); - videobuf_queue_pci_init(&fh->cap, &video_qops, - dev->pci, &dev->slock, + videobuf_queue_sg_init(&fh->cap, &video_qops, + &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_INTERLACED, sizeof(struct saa7134_buf), fh); - videobuf_queue_pci_init(&fh->vbi, &saa7134_vbi_qops, - dev->pci, &dev->slock, + videobuf_queue_sg_init(&fh->vbi, &saa7134_vbi_qops, + &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_FIELD_SEQ_TB, sizeof(struct saa7134_buf), diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index f940d025479..924ffd13637 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -253,7 +253,17 @@ struct saa7134_format { #define SAA7134_BOARD_BEHOLD_607_9FM 129 #define SAA7134_BOARD_BEHOLD_M6 130 #define SAA7134_BOARD_TWINHAN_DTV_DVB_3056 131 -#define SAA7134_BOARD_GENIUS_TVGO_A11MCE 132 +#define SAA7134_BOARD_GENIUS_TVGO_A11MCE 132 +#define SAA7134_BOARD_PHILIPS_SNAKE 133 +#define SAA7134_BOARD_CREATIX_CTX953 134 +#define SAA7134_BOARD_MSI_TVANYWHERE_AD11 135 +#define SAA7134_BOARD_AVERMEDIA_CARDBUS_506 136 +#define SAA7134_BOARD_AVERMEDIA_A16D 137 +#define SAA7134_BOARD_AVERMEDIA_M115 138 +#define SAA7134_BOARD_VIDEOMATE_T750 139 +#define SAA7134_BOARD_AVERMEDIA_A700_PRO 140 +#define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141 + #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 @@ -380,9 +390,7 @@ struct saa7134_fh { unsigned int radio; enum v4l2_buf_type type; unsigned int resources; -#ifdef VIDIOC_G_PRIORITY enum v4l2_priority prio; -#endif /* video overlay */ struct v4l2_window win; @@ -454,9 +462,7 @@ struct saa7134_dev { struct list_head devlist; struct mutex lock; spinlock_t slock; -#ifdef VIDIOC_G_PRIORITY struct v4l2_prio_state prio; -#endif /* workstruct for loading modules */ struct work_struct request_module_wk; @@ -556,7 +562,9 @@ struct saa7134_dev { #if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) /* SAA7134_MPEG_DVB only */ struct videobuf_dvb dvb; - int (*original_demod_sleep)(struct dvb_frontend* fe); + int (*original_demod_sleep)(struct dvb_frontend *fe); + int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); + int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg); #endif }; @@ -594,7 +602,6 @@ extern int saa7134_no_overlay; void saa7134_track_gpio(struct saa7134_dev *dev, char *msg); void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value); -int saa7134_tuner_callback(void *ptr, int command, int arg); #define SAA7134_PGTABLE_SIZE 4096 @@ -631,6 +638,7 @@ extern struct pci_device_id __devinitdata saa7134_pci_tbl[]; extern int saa7134_board_init1(struct saa7134_dev *dev); extern int saa7134_board_init2(struct saa7134_dev *dev); +int saa7134_tuner_callback(void *priv, int command, int arg); /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c new file mode 100644 index 00000000000..53c5edbcf7e --- /dev/null +++ b/drivers/media/video/saa717x.c @@ -0,0 +1,1516 @@ +/* + * saa717x - Philips SAA717xHL video decoder driver + * + * Based on the saa7115 driver + * + * Changes by Ohta Kyuma <alpha292@bremen.or.jp> + * - Apply to SAA717x,NEC uPD64031,uPD64083. (1/31/2004) + * + * Changes by T.Adachi (tadachi@tadachi-net.com) + * - support audio, video scaler etc, and checked the initialize sequence. + * + * Cleaned up by Hans Verkuil <hverkuil@xs4all.nl> + * + * Note: this is a reversed engineered driver based on captures from + * the I2C bus under Windows. This chip is very similar to the saa7134, + * though. Unfortunately, this driver is currently only working for NTSC. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> + +#include <linux/videodev.h> +#include <linux/videodev2.h> +#include <linux/i2c.h> +#include <media/v4l2-common.h> +#include <media/v4l2-i2c-drv.h> + +MODULE_DESCRIPTION("Philips SAA717x audio/video decoder driver"); +MODULE_AUTHOR("K. Ohta, T. Adachi, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +struct saa717x_state { + v4l2_std_id std; + int input; + int enable; + int radio; + int bright; + int contrast; + int hue; + int sat; + int playback; + int audio; + int tuner_audio_mode; + int audio_main_mute; + int audio_main_vol_r; + int audio_main_vol_l; + u16 audio_main_bass; + u16 audio_main_treble; + u16 audio_main_volume; + u16 audio_main_balance; + int audio_input; +}; + +/* ----------------------------------------------------------------------- */ + +/* for audio mode */ +#define TUNER_AUDIO_MONO 0 /* LL */ +#define TUNER_AUDIO_STEREO 1 /* LR */ +#define TUNER_AUDIO_LANG1 2 /* LL */ +#define TUNER_AUDIO_LANG2 3 /* RR */ + +#define SAA717X_NTSC_WIDTH (704) +#define SAA717X_NTSC_HEIGHT (480) + +/* ----------------------------------------------------------------------- */ + +static int saa717x_write(struct i2c_client *client, u32 reg, u32 value) +{ + struct i2c_adapter *adap = client->adapter; + int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488; + unsigned char mm1[6]; + struct i2c_msg msg; + + msg.flags = 0; + msg.addr = client->addr; + mm1[0] = (reg >> 8) & 0xff; + mm1[1] = reg & 0xff; + + if (fw_addr) { + mm1[4] = (value >> 16) & 0xff; + mm1[3] = (value >> 8) & 0xff; + mm1[2] = value & 0xff; + } else { + mm1[2] = value & 0xff; + } + msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */ + msg.buf = mm1; + v4l_dbg(2, debug, client, "wrote: reg 0x%03x=%08x\n", reg, value); + return i2c_transfer(adap, &msg, 1) == 1; +} + +static void saa717x_write_regs(struct i2c_client *client, u32 *data) +{ + while (data[0] || data[1]) { + saa717x_write(client, data[0], data[1]); + data += 2; + } +} + +static u32 saa717x_read(struct i2c_client *client, u32 reg) +{ + struct i2c_adapter *adap = client->adapter; + int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528; + unsigned char mm1[2]; + unsigned char mm2[4] = { 0, 0, 0, 0 }; + struct i2c_msg msgs[2]; + u32 value; + + msgs[0].flags = 0; + msgs[1].flags = I2C_M_RD; + msgs[0].addr = msgs[1].addr = client->addr; + mm1[0] = (reg >> 8) & 0xff; + mm1[1] = reg & 0xff; + msgs[0].len = 2; + msgs[0].buf = mm1; + msgs[1].len = fw_addr ? 3 : 1; /* Multibyte Registers contains *only* 3 bytes */ + msgs[1].buf = mm2; + i2c_transfer(adap, msgs, 2); + + if (fw_addr) + value = (mm2[2] & 0xff) | ((mm2[1] & 0xff) >> 8) | ((mm2[0] & 0xff) >> 16); + else + value = mm2[0] & 0xff; + + v4l_dbg(2, debug, client, "read: reg 0x%03x=0x%08x\n", reg, value); + return value; +} + +/* ----------------------------------------------------------------------- */ + +static u32 reg_init_initialize[] = +{ + /* from linux driver */ + 0x101, 0x008, /* Increment delay */ + + 0x103, 0x000, /* Analog input control 2 */ + 0x104, 0x090, /* Analog input control 3 */ + 0x105, 0x090, /* Analog input control 4 */ + 0x106, 0x0eb, /* Horizontal sync start */ + 0x107, 0x0e0, /* Horizontal sync stop */ + 0x109, 0x055, /* Luminance control */ + + 0x10f, 0x02a, /* Chroma gain control */ + 0x110, 0x000, /* Chroma control 2 */ + + 0x114, 0x045, /* analog/ADC */ + + 0x118, 0x040, /* RAW data gain */ + 0x119, 0x080, /* RAW data offset */ + + 0x044, 0x000, /* VBI horizontal input window start (L) TASK A */ + 0x045, 0x000, /* VBI horizontal input window start (H) TASK A */ + 0x046, 0x0cf, /* VBI horizontal input window stop (L) TASK A */ + 0x047, 0x002, /* VBI horizontal input window stop (H) TASK A */ + + 0x049, 0x000, /* VBI vertical input window start (H) TASK A */ + + 0x04c, 0x0d0, /* VBI horizontal output length (L) TASK A */ + 0x04d, 0x002, /* VBI horizontal output length (H) TASK A */ + + 0x064, 0x080, /* Lumina brightness TASK A */ + 0x065, 0x040, /* Luminance contrast TASK A */ + 0x066, 0x040, /* Chroma saturation TASK A */ + /* 067H: Reserved */ + 0x068, 0x000, /* VBI horizontal scaling increment (L) TASK A */ + 0x069, 0x004, /* VBI horizontal scaling increment (H) TASK A */ + 0x06a, 0x000, /* VBI phase offset TASK A */ + + 0x06e, 0x000, /* Horizontal phase offset Luma TASK A */ + 0x06f, 0x000, /* Horizontal phase offset Chroma TASK A */ + + 0x072, 0x000, /* Vertical filter mode TASK A */ + + 0x084, 0x000, /* VBI horizontal input window start (L) TAKS B */ + 0x085, 0x000, /* VBI horizontal input window start (H) TAKS B */ + 0x086, 0x0cf, /* VBI horizontal input window stop (L) TAKS B */ + 0x087, 0x002, /* VBI horizontal input window stop (H) TAKS B */ + + 0x089, 0x000, /* VBI vertical input window start (H) TAKS B */ + + 0x08c, 0x0d0, /* VBI horizontal output length (L) TASK B */ + 0x08d, 0x002, /* VBI horizontal output length (H) TASK B */ + + 0x0a4, 0x080, /* Lumina brightness TASK B */ + 0x0a5, 0x040, /* Luminance contrast TASK B */ + 0x0a6, 0x040, /* Chroma saturation TASK B */ + /* 0A7H reserved */ + 0x0a8, 0x000, /* VBI horizontal scaling increment (L) TASK B */ + 0x0a9, 0x004, /* VBI horizontal scaling increment (H) TASK B */ + 0x0aa, 0x000, /* VBI phase offset TASK B */ + + 0x0ae, 0x000, /* Horizontal phase offset Luma TASK B */ + 0x0af, 0x000, /*Horizontal phase offset Chroma TASK B */ + + 0x0b2, 0x000, /* Vertical filter mode TASK B */ + + 0x00c, 0x000, /* Start point GREEN path */ + 0x00d, 0x000, /* Start point BLUE path */ + 0x00e, 0x000, /* Start point RED path */ + + 0x010, 0x010, /* GREEN path gamma curve --- */ + 0x011, 0x020, + 0x012, 0x030, + 0x013, 0x040, + 0x014, 0x050, + 0x015, 0x060, + 0x016, 0x070, + 0x017, 0x080, + 0x018, 0x090, + 0x019, 0x0a0, + 0x01a, 0x0b0, + 0x01b, 0x0c0, + 0x01c, 0x0d0, + 0x01d, 0x0e0, + 0x01e, 0x0f0, + 0x01f, 0x0ff, /* --- GREEN path gamma curve */ + + 0x020, 0x010, /* BLUE path gamma curve --- */ + 0x021, 0x020, + 0x022, 0x030, + 0x023, 0x040, + 0x024, 0x050, + 0x025, 0x060, + 0x026, 0x070, + 0x027, 0x080, + 0x028, 0x090, + 0x029, 0x0a0, + 0x02a, 0x0b0, + 0x02b, 0x0c0, + 0x02c, 0x0d0, + 0x02d, 0x0e0, + 0x02e, 0x0f0, + 0x02f, 0x0ff, /* --- BLUE path gamma curve */ + + 0x030, 0x010, /* RED path gamma curve --- */ + 0x031, 0x020, + 0x032, 0x030, + 0x033, 0x040, + 0x034, 0x050, + 0x035, 0x060, + 0x036, 0x070, + 0x037, 0x080, + 0x038, 0x090, + 0x039, 0x0a0, + 0x03a, 0x0b0, + 0x03b, 0x0c0, + 0x03c, 0x0d0, + 0x03d, 0x0e0, + 0x03e, 0x0f0, + 0x03f, 0x0ff, /* --- RED path gamma curve */ + + 0x109, 0x085, /* Luminance control */ + + /**** from app start ****/ + 0x584, 0x000, /* AGC gain control */ + 0x585, 0x000, /* Program count */ + 0x586, 0x003, /* Status reset */ + 0x588, 0x0ff, /* Number of audio samples (L) */ + 0x589, 0x00f, /* Number of audio samples (M) */ + 0x58a, 0x000, /* Number of audio samples (H) */ + 0x58b, 0x000, /* Audio select */ + 0x58c, 0x010, /* Audio channel assign1 */ + 0x58d, 0x032, /* Audio channel assign2 */ + 0x58e, 0x054, /* Audio channel assign3 */ + 0x58f, 0x023, /* Audio format */ + 0x590, 0x000, /* SIF control */ + + 0x595, 0x000, /* ?? */ + 0x596, 0x000, /* ?? */ + 0x597, 0x000, /* ?? */ + + 0x464, 0x00, /* Digital input crossbar1 */ + + 0x46c, 0xbbbb10, /* Digital output selection1-3 */ + 0x470, 0x101010, /* Digital output selection4-6 */ + + 0x478, 0x00, /* Sound feature control */ + + 0x474, 0x18, /* Softmute control */ + + 0x454, 0x0425b9, /* Sound Easy programming(reset) */ + 0x454, 0x042539, /* Sound Easy programming(reset) */ + + + /**** common setting( of DVD play, including scaler commands) ****/ + 0x042, 0x003, /* Data path configuration for VBI (TASK A) */ + + 0x082, 0x003, /* Data path configuration for VBI (TASK B) */ + + 0x108, 0x0f8, /* Sync control */ + 0x2a9, 0x0fd, /* ??? */ + 0x102, 0x089, /* select video input "mode 9" */ + 0x111, 0x000, /* Mode/delay control */ + + 0x10e, 0x00a, /* Chroma control 1 */ + + 0x594, 0x002, /* SIF, analog I/O select */ + + 0x454, 0x0425b9, /* Sound */ + 0x454, 0x042539, + + 0x111, 0x000, + 0x10e, 0x00a, + 0x464, 0x000, + 0x300, 0x000, + 0x301, 0x006, + 0x302, 0x000, + 0x303, 0x006, + 0x308, 0x040, + 0x309, 0x000, + 0x30a, 0x000, + 0x30b, 0x000, + 0x000, 0x002, + 0x001, 0x000, + 0x002, 0x000, + 0x003, 0x000, + 0x004, 0x033, + 0x040, 0x01d, + 0x041, 0x001, + 0x042, 0x004, + 0x043, 0x000, + 0x080, 0x01e, + 0x081, 0x001, + 0x082, 0x004, + 0x083, 0x000, + 0x190, 0x018, + 0x115, 0x000, + 0x116, 0x012, + 0x117, 0x018, + 0x04a, 0x011, + 0x08a, 0x011, + 0x04b, 0x000, + 0x08b, 0x000, + 0x048, 0x000, + 0x088, 0x000, + 0x04e, 0x012, + 0x08e, 0x012, + 0x058, 0x012, + 0x098, 0x012, + 0x059, 0x000, + 0x099, 0x000, + 0x05a, 0x003, + 0x09a, 0x003, + 0x05b, 0x001, + 0x09b, 0x001, + 0x054, 0x008, + 0x094, 0x008, + 0x055, 0x000, + 0x095, 0x000, + 0x056, 0x0c7, + 0x096, 0x0c7, + 0x057, 0x002, + 0x097, 0x002, + 0x0ff, 0x0ff, + 0x060, 0x001, + 0x0a0, 0x001, + 0x061, 0x000, + 0x0a1, 0x000, + 0x062, 0x000, + 0x0a2, 0x000, + 0x063, 0x000, + 0x0a3, 0x000, + 0x070, 0x000, + 0x0b0, 0x000, + 0x071, 0x004, + 0x0b1, 0x004, + 0x06c, 0x0e9, + 0x0ac, 0x0e9, + 0x06d, 0x003, + 0x0ad, 0x003, + 0x05c, 0x0d0, + 0x09c, 0x0d0, + 0x05d, 0x002, + 0x09d, 0x002, + 0x05e, 0x0f2, + 0x09e, 0x0f2, + 0x05f, 0x000, + 0x09f, 0x000, + 0x074, 0x000, + 0x0b4, 0x000, + 0x075, 0x000, + 0x0b5, 0x000, + 0x076, 0x000, + 0x0b6, 0x000, + 0x077, 0x000, + 0x0b7, 0x000, + 0x195, 0x008, + 0x0ff, 0x0ff, + 0x108, 0x0f8, + 0x111, 0x000, + 0x10e, 0x00a, + 0x2a9, 0x0fd, + 0x464, 0x001, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + + + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x193, 0x000, + 0x300, 0x000, + 0x301, 0x006, + 0x302, 0x000, + 0x303, 0x006, + 0x308, 0x040, + 0x309, 0x000, + 0x30a, 0x000, + 0x30b, 0x000, + 0x000, 0x002, + 0x001, 0x000, + 0x002, 0x000, + 0x003, 0x000, + 0x004, 0x033, + 0x040, 0x01d, + 0x041, 0x001, + 0x042, 0x004, + 0x043, 0x000, + 0x080, 0x01e, + 0x081, 0x001, + 0x082, 0x004, + 0x083, 0x000, + 0x190, 0x018, + 0x115, 0x000, + 0x116, 0x012, + 0x117, 0x018, + 0x04a, 0x011, + 0x08a, 0x011, + 0x04b, 0x000, + 0x08b, 0x000, + 0x048, 0x000, + 0x088, 0x000, + 0x04e, 0x012, + 0x08e, 0x012, + 0x058, 0x012, + 0x098, 0x012, + 0x059, 0x000, + 0x099, 0x000, + 0x05a, 0x003, + 0x09a, 0x003, + 0x05b, 0x001, + 0x09b, 0x001, + 0x054, 0x008, + 0x094, 0x008, + 0x055, 0x000, + 0x095, 0x000, + 0x056, 0x0c7, + 0x096, 0x0c7, + 0x057, 0x002, + 0x097, 0x002, + 0x060, 0x001, + 0x0a0, 0x001, + 0x061, 0x000, + 0x0a1, 0x000, + 0x062, 0x000, + 0x0a2, 0x000, + 0x063, 0x000, + 0x0a3, 0x000, + 0x070, 0x000, + 0x0b0, 0x000, + 0x071, 0x004, + 0x0b1, 0x004, + 0x06c, 0x0e9, + 0x0ac, 0x0e9, + 0x06d, 0x003, + 0x0ad, 0x003, + 0x05c, 0x0d0, + 0x09c, 0x0d0, + 0x05d, 0x002, + 0x09d, 0x002, + 0x05e, 0x0f2, + 0x09e, 0x0f2, + 0x05f, 0x000, + 0x09f, 0x000, + 0x074, 0x000, + 0x0b4, 0x000, + 0x075, 0x000, + 0x0b5, 0x000, + 0x076, 0x000, + 0x0b6, 0x000, + 0x077, 0x000, + 0x0b7, 0x000, + 0x195, 0x008, + 0x598, 0x0e7, + 0x599, 0x07d, + 0x59a, 0x018, + 0x59c, 0x066, + 0x59d, 0x090, + 0x59e, 0x001, + 0x584, 0x000, + 0x585, 0x000, + 0x586, 0x003, + 0x588, 0x0ff, + 0x589, 0x00f, + 0x58a, 0x000, + 0x58b, 0x000, + 0x58c, 0x010, + 0x58d, 0x032, + 0x58e, 0x054, + 0x58f, 0x023, + 0x590, 0x000, + 0x595, 0x000, + 0x596, 0x000, + 0x597, 0x000, + 0x464, 0x000, + 0x46c, 0xbbbb10, + 0x470, 0x101010, + 0x478, 0x000, + 0x474, 0x018, + 0x454, 0x042135, + 0x193, 0x0a6, + 0x108, 0x0f8, + 0x042, 0x003, + 0x082, 0x003, + 0x454, 0x0425b9, + 0x454, 0x042539, + 0x193, 0x000, + 0x193, 0x0a6, + 0x464, 0x000, + + 0, 0 +}; + +/* Tuner */ +static u32 reg_init_tuner_input[] = { + 0x108, 0x0f8, /* Sync control */ + 0x111, 0x000, /* Mode/delay control */ + 0x10e, 0x00a, /* Chroma control 1 */ + 0, 0 +}; + +/* Composite */ +static u32 reg_init_composite_input[] = { + 0x108, 0x0e8, /* Sync control */ + 0x111, 0x000, /* Mode/delay control */ + 0x10e, 0x04a, /* Chroma control 1 */ + 0, 0 +}; + +/* S-Video */ +static u32 reg_init_svideo_input[] = { + 0x108, 0x0e8, /* Sync control */ + 0x111, 0x000, /* Mode/delay control */ + 0x10e, 0x04a, /* Chroma control 1 */ + 0, 0 +}; + +static u32 reg_set_audio_template[4][2] = +{ + { /* for MONO + tadachi 6/29 DMA audio output select? + Register 0x46c + 7-4: DMA2, 3-0: DMA1 ch. DMA4, DMA3 DMA2, DMA1 + 0: MAIN left, 1: MAIN right + 2: AUX1 left, 3: AUX1 right + 4: AUX2 left, 5: AUX2 right + 6: DPL left, 7: DPL right + 8: DPL center, 9: DPL surround + A: monitor output, B: digital sense */ + 0xbbbb00, + + /* tadachi 6/29 DAC and I2S output select? + Register 0x470 + 7-4:DAC right ch. 3-0:DAC left ch. + I2S1 right,left I2S2 right,left */ + 0x00, + }, + { /* for STEREO */ + 0xbbbb10, 0x101010, + }, + { /* for LANG1 */ + 0xbbbb00, 0x00, + }, + { /* for LANG2/SAP */ + 0xbbbb11, 0x111111, + } +}; + + +/* Get detected audio flags (from saa7134 driver) */ +static void get_inf_dev_status(struct i2c_client *client, + int *dual_flag, int *stereo_flag) +{ + u32 reg_data3; + + static char *stdres[0x20] = { + [0x00] = "no standard detected", + [0x01] = "B/G (in progress)", + [0x02] = "D/K (in progress)", + [0x03] = "M (in progress)", + + [0x04] = "B/G A2", + [0x05] = "B/G NICAM", + [0x06] = "D/K A2 (1)", + [0x07] = "D/K A2 (2)", + [0x08] = "D/K A2 (3)", + [0x09] = "D/K NICAM", + [0x0a] = "L NICAM", + [0x0b] = "I NICAM", + + [0x0c] = "M Korea", + [0x0d] = "M BTSC ", + [0x0e] = "M EIAJ", + + [0x0f] = "FM radio / IF 10.7 / 50 deemp", + [0x10] = "FM radio / IF 10.7 / 75 deemp", + [0x11] = "FM radio / IF sel / 50 deemp", + [0x12] = "FM radio / IF sel / 75 deemp", + + [0x13 ... 0x1e] = "unknown", + [0x1f] = "??? [in progress]", + }; + + + *dual_flag = *stereo_flag = 0; + + /* (demdec status: 0x528) */ + + /* read current status */ + reg_data3 = saa717x_read(client, 0x0528); + + v4l_dbg(1, debug, client, "tvaudio thread status: 0x%x [%s%s%s]\n", + reg_data3, stdres[reg_data3 & 0x1f], + (reg_data3 & 0x000020) ? ",stereo" : "", + (reg_data3 & 0x000040) ? ",dual" : ""); + v4l_dbg(1, debug, client, "detailed status: " + "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", + (reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "", + (reg_data3 & 0x000100) ? " A2/EIAJ dual " : "", + (reg_data3 & 0x000200) ? " A2/EIAJ stereo " : "", + (reg_data3 & 0x000400) ? " A2/EIAJ noise mute " : "", + + (reg_data3 & 0x000800) ? " BTSC/FM radio pilot " : "", + (reg_data3 & 0x001000) ? " SAP carrier " : "", + (reg_data3 & 0x002000) ? " BTSC stereo noise mute " : "", + (reg_data3 & 0x004000) ? " SAP noise mute " : "", + (reg_data3 & 0x008000) ? " VDSP " : "", + + (reg_data3 & 0x010000) ? " NICST " : "", + (reg_data3 & 0x020000) ? " NICDU " : "", + (reg_data3 & 0x040000) ? " NICAM muted " : "", + (reg_data3 & 0x080000) ? " NICAM reserve sound " : "", + + (reg_data3 & 0x100000) ? " init done " : ""); + + if (reg_data3 & 0x000220) { + v4l_dbg(1, debug, client, "ST!!!\n"); + *stereo_flag = 1; + } + + if (reg_data3 & 0x000140) { + v4l_dbg(1, debug, client, "DUAL!!!\n"); + *dual_flag = 1; + } +} + +/* regs write to set audio mode */ +static void set_audio_mode(struct i2c_client *client, int audio_mode) +{ + v4l_dbg(1, debug, client, "writing registers to set audio mode by set %d\n", + audio_mode); + + saa717x_write(client, 0x46c, reg_set_audio_template[audio_mode][0]); + saa717x_write(client, 0x470, reg_set_audio_template[audio_mode][1]); +} + +/* write regs to video output level (bright,contrast,hue,sat) */ +static void set_video_output_level_regs(struct i2c_client *client, + struct saa717x_state *decoder) +{ + /* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */ + saa717x_write(client, 0x10a, decoder->bright); + + /* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) - + 0h (luminance off) 40: i2c dump + c0h (-1.0 inverse chrominance) + 80h (-2.0 inverse chrominance) */ + saa717x_write(client, 0x10b, decoder->contrast); + + /* saturation? 7fh(max)-40h(ITU)-0h(color off) + c0h (-1.0 inverse chrominance) + 80h (-2.0 inverse chrominance) */ + saa717x_write(client, 0x10c, decoder->sat); + + /* color hue (phase) control + 7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */ + saa717x_write(client, 0x10d, decoder->hue); +} + +/* write regs to set audio volume, bass and treble */ +static int set_audio_regs(struct i2c_client *client, + struct saa717x_state *decoder) +{ + u8 mute = 0xac; /* -84 dB */ + u32 val; + unsigned int work_l, work_r; + + /* set SIF analog I/O select */ + saa717x_write(client, 0x0594, decoder->audio_input); + v4l_dbg(1, debug, client, "set audio input %d\n", + decoder->audio_input); + + /* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */ + work_l = (min(65536 - decoder->audio_main_balance, 32768) * decoder->audio_main_volume) / 32768; + work_r = (min(decoder->audio_main_balance, (u16)32768) * decoder->audio_main_volume) / 32768; + decoder->audio_main_vol_l = (long)work_l * (24 - (-40)) / 65535 - 40; + decoder->audio_main_vol_r = (long)work_r * (24 - (-40)) / 65535 - 40; + + /* set main volume */ + /* main volume L[7-0],R[7-0],0x00 24=24dB,-83dB, -84(mute) */ + /* def:0dB->6dB(MPG600GR) */ + /* if mute is on, set mute */ + if (decoder->audio_main_mute) { + val = mute | (mute << 8); + } else { + val = (u8)decoder->audio_main_vol_l | + ((u8)decoder->audio_main_vol_r << 8); + } + + saa717x_write(client, 0x480, val); + + /* bass and treble; go to another function */ + /* set bass and treble */ + val = decoder->audio_main_bass | (decoder->audio_main_treble << 8); + saa717x_write(client, 0x488, val); + return 0; +} + +/********** scaling staff ***********/ +static void set_h_prescale(struct i2c_client *client, + int task, int prescale) +{ + static const struct { + int xpsc; + int xacl; + int xc2_1; + int xdcg; + int vpfy; + } vals[] = { + /* XPSC XACL XC2_1 XDCG VPFY */ + { 1, 0, 0, 0, 0 }, + { 2, 2, 1, 2, 2 }, + { 3, 4, 1, 3, 2 }, + { 4, 8, 1, 4, 2 }, + { 5, 8, 1, 4, 2 }, + { 6, 8, 1, 4, 3 }, + { 7, 8, 1, 4, 3 }, + { 8, 15, 0, 4, 3 }, + { 9, 15, 0, 4, 3 }, + { 10, 16, 1, 5, 3 }, + }; + static const int count = ARRAY_SIZE(vals); + int i, task_shift; + + task_shift = task * 0x40; + for (i = 0; i < count; i++) + if (vals[i].xpsc == prescale) + break; + if (i == count) + return; + + /* horizonal prescaling */ + saa717x_write(client, 0x60 + task_shift, vals[i].xpsc); + /* accumulation length */ + saa717x_write(client, 0x61 + task_shift, vals[i].xacl); + /* level control */ + saa717x_write(client, 0x62 + task_shift, + (vals[i].xc2_1 << 3) | vals[i].xdcg); + /*FIR prefilter control */ + saa717x_write(client, 0x63 + task_shift, + (vals[i].vpfy << 2) | vals[i].vpfy); +} + +/********** scaling staff ***********/ +static void set_v_scale(struct i2c_client *client, int task, int yscale) +{ + int task_shift; + + task_shift = task * 0x40; + /* Vertical scaling ratio (LOW) */ + saa717x_write(client, 0x70 + task_shift, yscale & 0xff); + /* Vertical scaling ratio (HI) */ + saa717x_write(client, 0x71 + task_shift, yscale >> 8); +} + +static int saa717x_set_audio_clock_freq(struct i2c_client *client, u32 freq) +{ + /* not yet implament, so saa717x_cfg_??hz_??_audio is not defined. */ + return 0; +} + +static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct saa717x_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value < 0 || ctrl->value > 255) { + v4l_err(client, "invalid brightness setting %d\n", ctrl->value); + return -ERANGE; + } + + state->bright = ctrl->value; + v4l_dbg(1, debug, client, "bright:%d\n", state->bright); + saa717x_write(client, 0x10a, state->bright); + break; + + case V4L2_CID_CONTRAST: + if (ctrl->value < 0 || ctrl->value > 127) { + v4l_err(client, "invalid contrast setting %d\n", ctrl->value); + return -ERANGE; + } + + state->contrast = ctrl->value; + v4l_dbg(1, debug, client, "contrast:%d\n", state->contrast); + saa717x_write(client, 0x10b, state->contrast); + break; + + case V4L2_CID_SATURATION: + if (ctrl->value < 0 || ctrl->value > 127) { + v4l_err(client, "invalid saturation setting %d\n", ctrl->value); + return -ERANGE; + } + + state->sat = ctrl->value; + v4l_dbg(1, debug, client, "sat:%d\n", state->sat); + saa717x_write(client, 0x10c, state->sat); + break; + + case V4L2_CID_HUE: + if (ctrl->value < -127 || ctrl->value > 127) { + v4l_err(client, "invalid hue setting %d\n", ctrl->value); + return -ERANGE; + } + + state->hue = ctrl->value; + v4l_dbg(1, debug, client, "hue:%d\n", state->hue); + saa717x_write(client, 0x10d, state->hue); + break; + + case V4L2_CID_AUDIO_MUTE: + state->audio_main_mute = ctrl->value; + set_audio_regs(client, state); + break; + + case V4L2_CID_AUDIO_VOLUME: + state->audio_main_volume = ctrl->value; + set_audio_regs(client, state); + break; + + case V4L2_CID_AUDIO_BALANCE: + state->audio_main_balance = ctrl->value; + set_audio_regs(client, state); + break; + + case V4L2_CID_AUDIO_TREBLE: + state->audio_main_treble = ctrl->value; + set_audio_regs(client, state); + break; + + case V4L2_CID_AUDIO_BASS: + state->audio_main_bass = ctrl->value; + set_audio_regs(client, state); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int saa717x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct saa717x_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = state->bright; + break; + + case V4L2_CID_CONTRAST: + ctrl->value = state->contrast; + break; + + case V4L2_CID_SATURATION: + ctrl->value = state->sat; + break; + + case V4L2_CID_HUE: + ctrl->value = state->hue; + break; + + case V4L2_CID_AUDIO_MUTE: + ctrl->value = state->audio_main_mute; + break; + + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = state->audio_main_volume; + break; + + case V4L2_CID_AUDIO_BALANCE: + ctrl->value = state->audio_main_balance; + break; + + case V4L2_CID_AUDIO_TREBLE: + ctrl->value = state->audio_main_treble; + break; + + case V4L2_CID_AUDIO_BASS: + ctrl->value = state->audio_main_bass; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static struct v4l2_queryctrl saa717x_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 128, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64, + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64, + .flags = 0, + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_VOLUME, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535 / 100, + .default_value = 58880, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Balance", + .minimum = 0, + .maximum = 65535, + .step = 65535 / 100, + .default_value = 32768, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_MUTE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, + }, { + .id = V4L2_CID_AUDIO_BASS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Bass", + .minimum = 0, + .maximum = 65535, + .step = 65535 / 100, + .default_value = 32768, + }, { + .id = V4L2_CID_AUDIO_TREBLE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Treble", + .minimum = 0, + .maximum = 65535, + .step = 65535 / 100, + .default_value = 32768, + }, +}; + +static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_state *decoder, int inp) +{ + int is_tuner = inp & 0x80; /* tuner input flag */ + + inp &= 0x7f; + + v4l_dbg(1, debug, client, "decoder set input (%d)\n", inp); + /* inputs from 0-9 are available*/ + /* saa717x have mode0-mode9 but mode5 is reserved. */ + if (inp < 0 || inp > 9 || inp == 5) + return -EINVAL; + + if (decoder->input != inp) { + int input_line = inp; + + decoder->input = input_line; + v4l_dbg(1, debug, client, "now setting %s input %d\n", + input_line >= 6 ? "S-Video" : "Composite", + input_line); + + /* select mode */ + saa717x_write(client, 0x102, + (saa717x_read(client, 0x102) & 0xf0) | + input_line); + + /* bypass chrominance trap for modes 6..9 */ + saa717x_write(client, 0x109, + (saa717x_read(client, 0x109) & 0x7f) | + (input_line < 6 ? 0x0 : 0x80)); + + /* change audio_mode */ + if (is_tuner) { + /* tuner */ + set_audio_mode(client, decoder->tuner_audio_mode); + } else { + /* Force to STEREO mode if Composite or + * S-Video were chosen */ + set_audio_mode(client, TUNER_AUDIO_STEREO); + } + /* change initialize procedure (Composite/S-Video) */ + if (is_tuner) + saa717x_write_regs(client, reg_init_tuner_input); + else if (input_line >= 6) + saa717x_write_regs(client, reg_init_svideo_input); + else + saa717x_write_regs(client, reg_init_composite_input); + } + + return 0; +} + +static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + struct saa717x_state *decoder = i2c_get_clientdata(client); + + v4l_dbg(1, debug, client, "IOCTL: %08x\n", cmd); + + switch (cmd) { + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + return saa717x_set_audio_clock_freq(client, *(u32 *)arg); + + case VIDIOC_G_CTRL: + return saa717x_get_v4lctrl(client, (struct v4l2_control *)arg); + + case VIDIOC_S_CTRL: + return saa717x_set_v4lctrl(client, (struct v4l2_control *)arg); + + case VIDIOC_QUERYCTRL: { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++) + if (qc->id && qc->id == saa717x_qctrl[i].id) { + memcpy(qc, &saa717x_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; + } + +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: { + struct v4l2_register *reg = arg; + + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa717x_read(client, reg->reg); + break; + } + + case VIDIOC_DBG_S_REGISTER: { + struct v4l2_register *reg = arg; + u16 addr = reg->reg & 0xffff; + u8 val = reg->val & 0xff; + + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa717x_write(client, addr, val); + break; + } +#endif + + case VIDIOC_S_FMT: { + struct v4l2_format *fmt = (struct v4l2_format *)arg; + struct v4l2_pix_format *pix; + int prescale, h_scale, v_scale; + + pix = &fmt->fmt.pix; + v4l_dbg(1, debug, client, "decoder set size\n"); + + /* FIXME need better bounds checking here */ + if (pix->width < 1 || pix->width > 1440) + return -EINVAL; + if (pix->height < 1 || pix->height > 960) + return -EINVAL; + + /* scaling setting */ + /* NTSC and interlace only */ + prescale = SAA717X_NTSC_WIDTH / pix->width; + if (prescale == 0) + prescale = 1; + h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width; + /* interlace */ + v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height; + + /* Horizontal prescaling etc */ + set_h_prescale(client, 0, prescale); + set_h_prescale(client, 1, prescale); + + /* Horizontal scaling increment */ + /* TASK A */ + saa717x_write(client, 0x6C, (u8)(h_scale & 0xFF)); + saa717x_write(client, 0x6D, (u8)((h_scale >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(client, 0xAC, (u8)(h_scale & 0xFF)); + saa717x_write(client, 0xAD, (u8)((h_scale >> 8) & 0xFF)); + + /* Vertical prescaling etc */ + set_v_scale(client, 0, v_scale); + set_v_scale(client, 1, v_scale); + + /* set video output size */ + /* video number of pixels at output */ + /* TASK A */ + saa717x_write(client, 0x5C, (u8)(pix->width & 0xFF)); + saa717x_write(client, 0x5D, (u8)((pix->width >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(client, 0x9C, (u8)(pix->width & 0xFF)); + saa717x_write(client, 0x9D, (u8)((pix->width >> 8) & 0xFF)); + + /* video number of lines at output */ + /* TASK A */ + saa717x_write(client, 0x5E, (u8)(pix->height & 0xFF)); + saa717x_write(client, 0x5F, (u8)((pix->height >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(client, 0x9E, (u8)(pix->height & 0xFF)); + saa717x_write(client, 0x9F, (u8)((pix->height >> 8) & 0xFF)); + break; + } + + case AUDC_SET_RADIO: + decoder->radio = 1; + break; + + case VIDIOC_S_STD: { + v4l2_std_id std = *(v4l2_std_id *) arg; + + v4l_dbg(1, debug, client, "decoder set norm "); + v4l_dbg(1, debug, client, "(not yet implementd)\n"); + + decoder->radio = 0; + decoder->std = std; + break; + } + + case VIDIOC_INT_G_AUDIO_ROUTING: { + struct v4l2_routing *route = arg; + + route->input = decoder->audio_input; + route->output = 0; + break; + } + + case VIDIOC_INT_S_AUDIO_ROUTING: { + struct v4l2_routing *route = arg; + + if (route->input < 3) { /* FIXME! --tadachi */ + decoder->audio_input = route->input; + v4l_dbg(1, debug, client, + "set decoder audio input to %d\n", + decoder->audio_input); + set_audio_regs(client, decoder); + break; + } + return -ERANGE; + } + + case VIDIOC_INT_S_VIDEO_ROUTING: { + struct v4l2_routing *route = arg; + int inp = route->input; + + return saa717x_set_video_input(client, decoder, inp); + } + + case VIDIOC_STREAMON: { + v4l_dbg(1, debug, client, "decoder enable output\n"); + decoder->enable = 1; + saa717x_write(client, 0x193, 0xa6); + break; + } + + case VIDIOC_STREAMOFF: { + v4l_dbg(1, debug, client, "decoder disable output\n"); + decoder->enable = 0; + saa717x_write(client, 0x193, 0x26); /* right? FIXME!--tadachi */ + break; + } + + /* change audio mode */ + case VIDIOC_S_TUNER: { + struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; + int audio_mode; + char *mes[4] = { + "MONO", "STEREO", "LANG1", "LANG2/SAP" + }; + + audio_mode = V4L2_TUNER_MODE_STEREO; + + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + audio_mode = TUNER_AUDIO_MONO; + break; + case V4L2_TUNER_MODE_STEREO: + audio_mode = TUNER_AUDIO_STEREO; + break; + case V4L2_TUNER_MODE_LANG2: + audio_mode = TUNER_AUDIO_LANG2; + break; + case V4L2_TUNER_MODE_LANG1: + audio_mode = TUNER_AUDIO_LANG1; + break; + } + + v4l_dbg(1, debug, client, "change audio mode to %s\n", + mes[audio_mode]); + decoder->tuner_audio_mode = audio_mode; + /* The registers are not changed here. */ + /* See DECODER_ENABLE_OUTPUT section. */ + set_audio_mode(client, decoder->tuner_audio_mode); + break; + } + + case VIDIOC_G_TUNER: { + struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; + int dual_f, stereo_f; + + if (decoder->radio) + break; + get_inf_dev_status(client, &dual_f, &stereo_f); + + v4l_dbg(1, debug, client, "DETECT==st:%d dual:%d\n", + stereo_f, dual_f); + + /* mono */ + if ((dual_f == 0) && (stereo_f == 0)) { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l_dbg(1, debug, client, "DETECT==MONO\n"); + } + + /* stereo */ + if (stereo_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_STEREO || + vt->audmode == V4L2_TUNER_MODE_LANG1) { + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + v4l_dbg(1, debug, client, "DETECT==ST(ST)\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l_dbg(1, debug, client, "DETECT==ST(MONO)\n"); + } + } + + /* dual */ + if (dual_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_LANG2) { + vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; + v4l_dbg(1, debug, client, "DETECT==DUAL1\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; + v4l_dbg(1, debug, client, "DETECT==DUAL2\n"); + } + } + break; + } + + case VIDIOC_LOG_STATUS: + /* not yet implemented */ + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + + +/* i2c implementation */ + +/* ----------------------------------------------------------------------- */ +static int saa717x_probe(struct i2c_client *client) +{ + struct saa717x_state *decoder; + u8 id = 0; + char *p = ""; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + snprintf(client->name, sizeof(client->name) - 1, "saa717x"); + + if (saa717x_write(client, 0x5a4, 0xfe) && + saa717x_write(client, 0x5a5, 0x0f) && + saa717x_write(client, 0x5a6, 0x00) && + saa717x_write(client, 0x5a7, 0x01)) + id = saa717x_read(client, 0x5a0); + if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) { + v4l_dbg(1, debug, client, "saa717x not found (id=%02x)\n", id); + return -ENODEV; + } + if (id == 0xc2) + p = "saa7173"; + else if (id == 0x32) + p = "saa7174A"; + else if (id == 0x6c) + p = "saa7174HL"; + else + p = "saa7171"; + v4l_info(client, "%s found @ 0x%x (%s)\n", p, + client->addr << 1, client->adapter->name); + + decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); + i2c_set_clientdata(client, decoder); + + if (decoder == NULL) + return -ENOMEM; + decoder->std = V4L2_STD_NTSC; + decoder->input = -1; + decoder->enable = 1; + + /* tune these parameters */ + decoder->bright = 0x80; + decoder->contrast = 0x44; + decoder->sat = 0x40; + decoder->hue = 0x00; + + /* FIXME!! */ + decoder->playback = 0; /* initially capture mode used */ + decoder->audio = 1; /* DECODER_AUDIO_48_KHZ */ + + decoder->audio_input = 2; /* FIXME!! */ + + decoder->tuner_audio_mode = TUNER_AUDIO_STEREO; + /* set volume, bass and treble */ + decoder->audio_main_vol_l = 6; + decoder->audio_main_vol_r = 6; + decoder->audio_main_bass = 0; + decoder->audio_main_treble = 0; + decoder->audio_main_mute = 0; + decoder->audio_main_balance = 32768; + /* normalize (24 to -40 (not -84) -> 65535 to 0) */ + decoder->audio_main_volume = + (decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40)); + + v4l_dbg(1, debug, client, "writing init values\n"); + + /* FIXME!! */ + saa717x_write_regs(client, reg_init_initialize); + set_video_output_level_regs(client, decoder); + /* set bass,treble to 0db 20041101 K.Ohta */ + decoder->audio_main_bass = 0; + decoder->audio_main_treble = 0; + set_audio_regs(client, decoder); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(2*HZ); + return 0; +} + +static int saa717x_remove(struct i2c_client *client) +{ + kfree(i2c_get_clientdata(client)); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "saa717x", + .driverid = I2C_DRIVERID_SAA717X, + .command = saa717x_command, + .probe = saa717x_probe, + .remove = saa717x_remove, + .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, +}; diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c index 41f70440fd3..02fda4eecea 100644 --- a/drivers/media/video/saa7185.c +++ b/drivers/media/video/saa7185.c @@ -52,7 +52,7 @@ MODULE_LICENSE("GPL"); #define I2C_NAME(s) (s)->name -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c index d5d7d6cf734..1cd629380f7 100644 --- a/drivers/media/video/se401.c +++ b/drivers/media/video/se401.c @@ -35,7 +35,7 @@ static const char version[] = "0.24"; #include <linux/usb.h> #include "se401.h" -static int flickerless=0; +static int flickerless; static int video_nr = -1; static struct usb_device_id device_table [] = { @@ -300,10 +300,10 @@ static void se401_button_irq(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + dbg("%s - urb shutting down with status: %d", __func__, urb->status); return; default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + dbg("%s - nonzero urb status received: %d", __func__, urb->status); goto exit; } @@ -315,7 +315,7 @@ exit: status = usb_submit_urb (urb, GFP_ATOMIC); if (status) err ("%s - usb_submit_urb failed with result %d", - __FUNCTION__, status); + __func__, status); } static void se401_video_irq(struct urb *urb) @@ -1224,7 +1224,9 @@ static const struct file_operations se401_fops = { .read = se401_read, .mmap = se401_mmap, .ioctl = se401_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, }; static struct video_device se401_template = { @@ -1279,7 +1281,7 @@ static int se401_init(struct usb_se401 *se401, int button) rc=se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp)); se401->cheight=cp[0]+cp[1]*256; - if (!cp[2] && SE401_FORMAT_BAYER) { + if (!(cp[2] & SE401_FORMAT_BAYER)) { err("Bayer format not supported!"); return 1; } diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h index 2e3c3de793a..0c8d87d8d18 100644 --- a/drivers/media/video/sn9c102/sn9c102.h +++ b/drivers/media/video/sn9c102/sn9c102.h @@ -176,7 +176,7 @@ do { \ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ else if ((level) >= 3) \ dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args); \ + __func__, __LINE__ , ## args); \ } \ } while (0) # define V4LDBG(level, name, cmd) \ @@ -191,7 +191,7 @@ do { \ pr_info("sn9c102: " fmt "\n", ## args); \ else if ((level) == 3) \ pr_debug("sn9c102: [%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args); \ + __func__, __LINE__ , ## args); \ } \ } while (0) #else @@ -202,7 +202,7 @@ do { \ #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __FUNCTION__, \ +dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ __LINE__ , ## args) #undef PDBGG diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index c40ba3adab2..5748b1e1a12 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -464,9 +464,9 @@ sn9c102_i2c_try_read(struct sn9c102_device* cam, } -int -sn9c102_i2c_try_write(struct sn9c102_device* cam, - const struct sn9c102_sensor* sensor, u8 address, u8 value) +static int sn9c102_i2c_try_write(struct sn9c102_device* cam, + const struct sn9c102_sensor* sensor, + u8 address, u8 value) { return sn9c102_i2c_try_raw_write(cam, sensor, 3, sensor->i2c_slave_id, address, @@ -528,7 +528,7 @@ sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) /* Search for the SOF marker (fixed part) in the header */ for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) { - if (unlikely(i+j) == len) + if (unlikely(i+j == len)) return NULL; if (*(m+i+j) == marker[cam->sof.bytesread]) { cam->sof.header[cam->sof.bytesread] = *(m+i+j); @@ -3224,7 +3224,9 @@ static const struct file_operations sn9c102_fops = { .open = sn9c102_open, .release = sn9c102_release, .ioctl = sn9c102_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .read = sn9c102_read, .poll = sn9c102_poll, .mmap = sn9c102_mmap, @@ -3239,7 +3241,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) { struct usb_device *udev = interface_to_usbdev(intf); struct sn9c102_device* cam; - static unsigned int dev_nr = 0; + static unsigned int dev_nr; unsigned int i; int err = 0, r; diff --git a/drivers/media/video/sn9c102/sn9c102_sensor.h b/drivers/media/video/sn9c102/sn9c102_sensor.h index 2d7d786b843..4af7382da5c 100644 --- a/drivers/media/video/sn9c102/sn9c102_sensor.h +++ b/drivers/media/video/sn9c102/sn9c102_sensor.h @@ -85,9 +85,6 @@ sn9c102_attach_sensor(struct sn9c102_device* cam, */ /* The "try" I2C I/O versions are used when probing the sensor */ -extern int sn9c102_i2c_try_write(struct sn9c102_device*, - const struct sn9c102_sensor*, u8 address, - u8 value); extern int sn9c102_i2c_try_read(struct sn9c102_device*, const struct sn9c102_sensor*, u8 address); @@ -126,7 +123,7 @@ extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2], Register adresses must be < 256. */ #define sn9c102_write_const_regs(sn9c102_device, data...) \ - ({ const static u8 _valreg[][2] = {data}; \ + ({ static const u8 _valreg[][2] = {data}; \ sn9c102_write_regs(sn9c102_device, _valreg, ARRAY_SIZE(_valreg)); }) /*****************************************************************************/ diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c new file mode 100644 index 00000000000..a1b92446c8b --- /dev/null +++ b/drivers/media/video/soc_camera.c @@ -0,0 +1,1031 @@ +/* + * camera image capture (abstract) bus driver + * + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This driver provides an interface between platform-specific camera + * busses and camera devices. It should be used if the camera is + * connected not over a "proper" bus like PCI or USB, but over a + * special bus, like, for example, the Quick Capture interface on PXA270 + * SoCs. Later it should also be used for i.MX31 SoCs from Freescale. + * It can handle multiple cameras and / or multiple busses, which can + * be used, e.g., in stereo-vision applications. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/list.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/vmalloc.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/soc_camera.h> + +static LIST_HEAD(hosts); +static LIST_HEAD(devices); +static DEFINE_MUTEX(list_lock); +static DEFINE_MUTEX(video_lock); + +const static struct soc_camera_data_format* +format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < icd->num_formats; i++) + if (icd->formats[i].fourcc == fourcc) + return icd->formats + i; + return NULL; +} + +static int soc_camera_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + enum v4l2_field field; + const struct soc_camera_data_format *fmt; + int ret; + + WARN_ON(priv != file->private_data); + + fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat); + if (!fmt) { + dev_dbg(&icd->dev, "invalid format 0x%08x\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + dev_dbg(&icd->dev, "fmt: 0x%08x\n", fmt->fourcc); + + field = f->fmt.pix.field; + + if (field == V4L2_FIELD_ANY) { + field = V4L2_FIELD_NONE; + } else if (V4L2_FIELD_NONE != field) { + dev_err(&icd->dev, "Field type invalid.\n"); + return -EINVAL; + } + + /* test physical bus parameters */ + ret = ici->ops->try_bus_param(icd, f->fmt.pix.pixelformat); + if (ret) + return ret; + + /* limit format to hardware capabilities */ + ret = ici->ops->try_fmt_cap(icd, f); + + /* calculate missing fields */ + f->fmt.pix.field = field; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return ret; +} + +static int soc_camera_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_UNKNOWN; + strcpy(inp->name, "Camera"); + + return 0; +} + +static int soc_camera_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int soc_camera_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + + return 0; +} + +static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a) +{ + return 0; +} + +static int soc_camera_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + int ret; + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + + WARN_ON(priv != file->private_data); + + dev_dbg(&icd->dev, "%s: %d\n", __func__, p->memory); + + ret = videobuf_reqbufs(&icf->vb_vidq, p); + if (ret < 0) + return ret; + + return ici->ops->reqbufs(icf, p); +} + +static int soc_camera_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_file *icf = file->private_data; + + WARN_ON(priv != file->private_data); + + return videobuf_querybuf(&icf->vb_vidq, p); +} + +static int soc_camera_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_file *icf = file->private_data; + + WARN_ON(priv != file->private_data); + + return videobuf_qbuf(&icf->vb_vidq, p); +} + +static int soc_camera_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct soc_camera_file *icf = file->private_data; + + WARN_ON(priv != file->private_data); + + return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK); +} + +static int soc_camera_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev; + struct soc_camera_device *icd; + struct soc_camera_host *ici; + struct soc_camera_file *icf; + spinlock_t *lock; + int ret; + + icf = vmalloc(sizeof(*icf)); + if (!icf) + return -ENOMEM; + + /* Protect against icd->remove() until we module_get() both drivers. */ + mutex_lock(&video_lock); + + vdev = video_devdata(file); + icd = container_of(vdev->dev, struct soc_camera_device, dev); + ici = to_soc_camera_host(icd->dev.parent); + + if (!try_module_get(icd->ops->owner)) { + dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); + ret = -EINVAL; + goto emgd; + } + + if (!try_module_get(ici->ops->owner)) { + dev_err(&icd->dev, "Couldn't lock capture bus driver.\n"); + ret = -EINVAL; + goto emgi; + } + + icf->icd = icd; + + icf->lock = ici->ops->spinlock_alloc(icf); + if (!icf->lock) { + ret = -ENOMEM; + goto esla; + } + + icd->use_count++; + + /* Now we really have to activate the camera */ + if (icd->use_count == 1) { + ret = ici->ops->add(icd); + if (ret < 0) { + dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); + icd->use_count--; + goto eiciadd; + } + } + + mutex_unlock(&video_lock); + + file->private_data = icf; + dev_dbg(&icd->dev, "camera device open\n"); + + /* We must pass NULL as dev pointer, then all pci_* dma operations + * transform to normal dma_* ones. */ + videobuf_queue_sg_init(&icf->vb_vidq, ici->vbq_ops, NULL, icf->lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, + ici->msize, icd); + + return 0; + + /* All errors are entered with the video_lock held */ +eiciadd: + lock = icf->lock; + icf->lock = NULL; + if (ici->ops->spinlock_free) + ici->ops->spinlock_free(lock); +esla: + module_put(ici->ops->owner); +emgi: + module_put(icd->ops->owner); +emgd: + mutex_unlock(&video_lock); + vfree(icf); + return ret; +} + +static int soc_camera_close(struct inode *inode, struct file *file) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct video_device *vdev = icd->vdev; + spinlock_t *lock = icf->lock; + + mutex_lock(&video_lock); + icd->use_count--; + if (!icd->use_count) + ici->ops->remove(icd); + icf->lock = NULL; + if (ici->ops->spinlock_free) + ici->ops->spinlock_free(lock); + module_put(icd->ops->owner); + module_put(ici->ops->owner); + mutex_unlock(&video_lock); + + vfree(icf); + + dev_dbg(vdev->dev, "camera device close\n"); + + return 0; +} + +static ssize_t soc_camera_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct video_device *vdev = icd->vdev; + int err = -EINVAL; + + dev_err(vdev->dev, "camera device read not implemented\n"); + + return err; +} + +static int soc_camera_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + int err; + + dev_dbg(&icd->dev, "mmap called, vma=0x%08lx\n", (unsigned long)vma); + + err = videobuf_mmap_mapper(&icf->vb_vidq, vma); + + dev_dbg(&icd->dev, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, + err); + + return err; +} + +static unsigned int soc_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + + if (list_empty(&icf->vb_vidq.stream)) { + dev_err(&icd->dev, "Trying to poll with no queued buffers!\n"); + return POLLERR; + } + + return ici->ops->poll(file, pt); +} + + +static struct file_operations soc_camera_fops = { + .owner = THIS_MODULE, + .open = soc_camera_open, + .release = soc_camera_close, + .ioctl = video_ioctl2, + .read = soc_camera_read, + .mmap = soc_camera_mmap, + .poll = soc_camera_poll, + .llseek = no_llseek, +}; + + +static int soc_camera_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + int ret; + struct v4l2_rect rect; + const static struct soc_camera_data_format *data_fmt; + + WARN_ON(priv != file->private_data); + + data_fmt = format_by_fourcc(icd, f->fmt.pix.pixelformat); + if (!data_fmt) + return -EINVAL; + + /* buswidth may be further adjusted by the ici */ + icd->buswidth = data_fmt->depth; + + ret = soc_camera_try_fmt_cap(file, icf, f); + if (ret < 0) + return ret; + + rect.left = icd->x_current; + rect.top = icd->y_current; + rect.width = f->fmt.pix.width; + rect.height = f->fmt.pix.height; + ret = ici->ops->set_fmt_cap(icd, f->fmt.pix.pixelformat, &rect); + if (ret < 0) + return ret; + + icd->current_fmt = data_fmt; + icd->width = rect.width; + icd->height = rect.height; + icf->vb_vidq.field = f->fmt.pix.field; + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) + dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", + f->type); + + dev_dbg(&icd->dev, "set width: %d height: %d\n", + icd->width, icd->height); + + /* set physical bus parameters */ + return ici->ops->set_bus_param(icd, f->fmt.pix.pixelformat); +} + +static int soc_camera_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + const struct soc_camera_data_format *format; + + WARN_ON(priv != file->private_data); + + if (f->index >= icd->num_formats) + return -EINVAL; + + format = &icd->formats[f->index]; + + strlcpy(f->description, format->name, sizeof(f->description)); + f->pixelformat = format->fourcc; + return 0; +} + +static int soc_camera_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + WARN_ON(priv != file->private_data); + + f->fmt.pix.width = icd->width; + f->fmt.pix.height = icd->height; + f->fmt.pix.field = icf->vb_vidq.field; + f->fmt.pix.pixelformat = icd->current_fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * icd->current_fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + dev_dbg(&icd->dev, "current_fmt->fourcc: 0x%08x\n", + icd->current_fmt->fourcc); + return 0; +} + +static int soc_camera_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + + WARN_ON(priv != file->private_data); + + strlcpy(cap->driver, ici->drv_name, sizeof(cap->driver)); + return ici->ops->querycap(ici, cap); +} + +static int soc_camera_streamon(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + WARN_ON(priv != file->private_data); + + dev_dbg(&icd->dev, "%s\n", __func__); + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + icd->ops->start_capture(icd); + + /* This calls buf_queue from host driver's videobuf_queue_ops */ + return videobuf_streamon(&icf->vb_vidq); +} + +static int soc_camera_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + WARN_ON(priv != file->private_data); + + dev_dbg(&icd->dev, "%s\n", __func__); + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* This calls buf_release from host driver's videobuf_queue_ops for all + * remaining buffers. When the last buffer is freed, stop capture */ + videobuf_streamoff(&icf->vb_vidq); + + icd->ops->stop_capture(icd); + + return 0; +} + +static int soc_camera_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + int i; + + WARN_ON(priv != file->private_data); + + if (!qc->id) + return -EINVAL; + + for (i = 0; i < icd->ops->num_controls; i++) + if (qc->id == icd->ops->controls[i].id) { + memcpy(qc, &(icd->ops->controls[i]), + sizeof(*qc)); + return 0; + } + + return -EINVAL; +} + +static int soc_camera_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + WARN_ON(priv != file->private_data); + + switch (ctrl->id) { + case V4L2_CID_GAIN: + if (icd->gain == (unsigned short)~0) + return -EINVAL; + ctrl->value = icd->gain; + return 0; + case V4L2_CID_EXPOSURE: + if (icd->exposure == (unsigned short)~0) + return -EINVAL; + ctrl->value = icd->exposure; + return 0; + } + + if (icd->ops->get_control) + return icd->ops->get_control(icd, ctrl); + return -EINVAL; +} + +static int soc_camera_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + WARN_ON(priv != file->private_data); + + if (icd->ops->set_control) + return icd->ops->set_control(icd, ctrl); + return -EINVAL; +} + +static int soc_camera_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *a) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->bounds.left = icd->x_min; + a->bounds.top = icd->y_min; + a->bounds.width = icd->width_max; + a->bounds.height = icd->height_max; + a->defrect.left = icd->x_min; + a->defrect.top = icd->y_min; + a->defrect.width = 640; + a->defrect.height = 480; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int soc_camera_g_crop(struct file *file, void *fh, + struct v4l2_crop *a) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->c.left = icd->x_current; + a->c.top = icd->y_current; + a->c.width = icd->width; + a->c.height = icd->height; + + return 0; +} + +static int soc_camera_s_crop(struct file *file, void *fh, + struct v4l2_crop *a) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + int ret; + + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + ret = ici->ops->set_fmt_cap(icd, 0, &a->c); + if (!ret) { + icd->width = a->c.width; + icd->height = a->c.height; + icd->x_current = a->c.left; + icd->y_current = a->c.top; + } + + return ret; +} + +static int soc_camera_g_chip_ident(struct file *file, void *fh, + struct v4l2_chip_ident *id) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + if (!icd->ops->get_chip_id) + return -EINVAL; + + return icd->ops->get_chip_id(icd, id); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int soc_camera_g_register(struct file *file, void *fh, + struct v4l2_register *reg) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + if (!icd->ops->get_register) + return -EINVAL; + + return icd->ops->get_register(icd, reg); +} + +static int soc_camera_s_register(struct file *file, void *fh, + struct v4l2_register *reg) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + + if (!icd->ops->set_register) + return -EINVAL; + + return icd->ops->set_register(icd, reg); +} +#endif + +static int device_register_link(struct soc_camera_device *icd) +{ + int ret = device_register(&icd->dev); + + if (ret < 0) { + /* Prevent calling device_unregister() */ + icd->dev.parent = NULL; + dev_err(&icd->dev, "Cannot register device: %d\n", ret); + /* Even if probe() was unsuccessful for all registered drivers, + * device_register() returns 0, and we add the link, just to + * document this camera's control device */ + } else if (icd->control) + /* Have to sysfs_remove_link() before device_unregister()? */ + if (sysfs_create_link(&icd->dev.kobj, &icd->control->kobj, + "control")) + dev_warn(&icd->dev, + "Failed creating the control symlink\n"); + return ret; +} + +/* So far this function cannot fail */ +static void scan_add_host(struct soc_camera_host *ici) +{ + struct soc_camera_device *icd; + + mutex_lock(&list_lock); + + list_for_each_entry(icd, &devices, list) { + if (icd->iface == ici->nr) { + icd->dev.parent = &ici->dev; + device_register_link(icd); + } + } + + mutex_unlock(&list_lock); +} + +/* return: 0 if no match found or a match found and + * device_register() successful, error code otherwise */ +static int scan_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici; + int ret = 0; + + mutex_lock(&list_lock); + + list_add_tail(&icd->list, &devices); + + /* Watch out for class_for_each_device / class_find_device API by + * Dave Young <hidave.darkstar@gmail.com> */ + list_for_each_entry(ici, &hosts, list) { + if (icd->iface == ici->nr) { + ret = 1; + icd->dev.parent = &ici->dev; + break; + } + } + + mutex_unlock(&list_lock); + + if (ret) + ret = device_register_link(icd); + + return ret; +} + +static int soc_camera_probe(struct device *dev) +{ + struct soc_camera_device *icd = to_soc_camera_dev(dev); + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + int ret; + + if (!icd->ops->probe) + return -ENODEV; + + /* We only call ->add() here to activate and probe the camera. + * We shall ->remove() and deactivate it immediately afterwards. */ + ret = ici->ops->add(icd); + if (ret < 0) + return ret; + + ret = icd->ops->probe(icd); + if (ret >= 0) { + const struct v4l2_queryctrl *qctrl; + + qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_GAIN); + icd->gain = qctrl ? qctrl->default_value : (unsigned short)~0; + qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); + icd->exposure = qctrl ? qctrl->default_value : + (unsigned short)~0; + } + ici->ops->remove(icd); + + return ret; +} + +/* This is called on device_unregister, which only means we have to disconnect + * from the host, but not remove ourselves from the device list */ +static int soc_camera_remove(struct device *dev) +{ + struct soc_camera_device *icd = to_soc_camera_dev(dev); + + if (icd->ops->remove) + icd->ops->remove(icd); + + return 0; +} + +static struct bus_type soc_camera_bus_type = { + .name = "soc-camera", + .probe = soc_camera_probe, + .remove = soc_camera_remove, +}; + +static struct device_driver ic_drv = { + .name = "camera", + .bus = &soc_camera_bus_type, + .owner = THIS_MODULE, +}; + +/* + * Image capture host - this is a host device, not a bus device, so, + * no bus reference, no probing. + */ +static struct class soc_camera_host_class = { + .owner = THIS_MODULE, + .name = "camera_host", +}; + +static void dummy_release(struct device *dev) +{ +} + +static spinlock_t *spinlock_alloc(struct soc_camera_file *icf) +{ + spinlock_t *lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL); + + if (lock) + spin_lock_init(lock); + + return lock; +} + +static void spinlock_free(spinlock_t *lock) +{ + kfree(lock); +} + +int soc_camera_host_register(struct soc_camera_host *ici) +{ + int ret; + struct soc_camera_host *ix; + + if (!ici->vbq_ops || !ici->ops->add || !ici->ops->remove) + return -EINVAL; + + /* Number might be equal to the platform device ID */ + sprintf(ici->dev.bus_id, "camera_host%d", ici->nr); + ici->dev.class = &soc_camera_host_class; + + mutex_lock(&list_lock); + list_for_each_entry(ix, &hosts, list) { + if (ix->nr == ici->nr) { + mutex_unlock(&list_lock); + return -EBUSY; + } + } + + list_add_tail(&ici->list, &hosts); + mutex_unlock(&list_lock); + + ici->dev.release = dummy_release; + + ret = device_register(&ici->dev); + + if (ret) + goto edevr; + + if (!ici->ops->spinlock_alloc) { + ici->ops->spinlock_alloc = spinlock_alloc; + ici->ops->spinlock_free = spinlock_free; + } + + scan_add_host(ici); + + return 0; + +edevr: + mutex_lock(&list_lock); + list_del(&ici->list); + mutex_unlock(&list_lock); + + return ret; +} +EXPORT_SYMBOL(soc_camera_host_register); + +/* Unregister all clients! */ +void soc_camera_host_unregister(struct soc_camera_host *ici) +{ + struct soc_camera_device *icd; + + mutex_lock(&list_lock); + + list_del(&ici->list); + + list_for_each_entry(icd, &devices, list) { + if (icd->dev.parent == &ici->dev) { + device_unregister(&icd->dev); + /* Not before device_unregister(), .remove + * needs parent to call ici->ops->remove() */ + icd->dev.parent = NULL; + memset(&icd->dev.kobj, 0, sizeof(icd->dev.kobj)); + } + } + + mutex_unlock(&list_lock); + + device_unregister(&ici->dev); +} +EXPORT_SYMBOL(soc_camera_host_unregister); + +/* Image capture device */ +int soc_camera_device_register(struct soc_camera_device *icd) +{ + struct soc_camera_device *ix; + int num = -1, i; + + if (!icd) + return -EINVAL; + + for (i = 0; i < 256 && num < 0; i++) { + num = i; + list_for_each_entry(ix, &devices, list) { + if (ix->iface == icd->iface && ix->devnum == i) { + num = -1; + break; + } + } + } + + if (num < 0) + /* ok, we have 256 cameras on this host... + * man, stay reasonable... */ + return -ENOMEM; + + icd->devnum = num; + icd->dev.bus = &soc_camera_bus_type; + snprintf(icd->dev.bus_id, sizeof(icd->dev.bus_id), + "%u-%u", icd->iface, icd->devnum); + + icd->dev.release = dummy_release; + + return scan_add_device(icd); +} +EXPORT_SYMBOL(soc_camera_device_register); + +void soc_camera_device_unregister(struct soc_camera_device *icd) +{ + mutex_lock(&list_lock); + list_del(&icd->list); + + /* The bus->remove will be eventually called */ + if (icd->dev.parent) + device_unregister(&icd->dev); + mutex_unlock(&list_lock); +} +EXPORT_SYMBOL(soc_camera_device_unregister); + +int soc_camera_video_start(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + int err = -ENOMEM; + struct video_device *vdev; + + if (!icd->dev.parent) + return -ENODEV; + + vdev = video_device_alloc(); + if (!vdev) + goto evidallocd; + dev_dbg(&ici->dev, "Allocated video_device %p\n", vdev); + + strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); + /* Maybe better &ici->dev */ + vdev->dev = &icd->dev; + vdev->type = VID_TYPE_CAPTURE; + vdev->current_norm = V4L2_STD_UNKNOWN; + vdev->fops = &soc_camera_fops; + vdev->release = video_device_release; + vdev->minor = -1; + vdev->tvnorms = V4L2_STD_UNKNOWN, + vdev->vidioc_querycap = soc_camera_querycap; + vdev->vidioc_g_fmt_cap = soc_camera_g_fmt_cap; + vdev->vidioc_enum_fmt_cap = soc_camera_enum_fmt_cap; + vdev->vidioc_s_fmt_cap = soc_camera_s_fmt_cap; + vdev->vidioc_enum_input = soc_camera_enum_input; + vdev->vidioc_g_input = soc_camera_g_input; + vdev->vidioc_s_input = soc_camera_s_input; + vdev->vidioc_s_std = soc_camera_s_std; + vdev->vidioc_reqbufs = soc_camera_reqbufs; + vdev->vidioc_try_fmt_cap = soc_camera_try_fmt_cap; + vdev->vidioc_querybuf = soc_camera_querybuf; + vdev->vidioc_qbuf = soc_camera_qbuf; + vdev->vidioc_dqbuf = soc_camera_dqbuf; + vdev->vidioc_streamon = soc_camera_streamon; + vdev->vidioc_streamoff = soc_camera_streamoff; + vdev->vidioc_queryctrl = soc_camera_queryctrl; + vdev->vidioc_g_ctrl = soc_camera_g_ctrl; + vdev->vidioc_s_ctrl = soc_camera_s_ctrl; + vdev->vidioc_cropcap = soc_camera_cropcap; + vdev->vidioc_g_crop = soc_camera_g_crop; + vdev->vidioc_s_crop = soc_camera_s_crop; + vdev->vidioc_g_chip_ident = soc_camera_g_chip_ident; +#ifdef CONFIG_VIDEO_ADV_DEBUG + vdev->vidioc_g_register = soc_camera_g_register; + vdev->vidioc_s_register = soc_camera_s_register; +#endif + + icd->current_fmt = &icd->formats[0]; + + err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor); + if (err < 0) { + dev_err(vdev->dev, "video_register_device failed\n"); + goto evidregd; + } + icd->vdev = vdev; + + return 0; + +evidregd: + video_device_release(vdev); +evidallocd: + return err; +} +EXPORT_SYMBOL(soc_camera_video_start); + +void soc_camera_video_stop(struct soc_camera_device *icd) +{ + struct video_device *vdev = icd->vdev; + + dev_dbg(&icd->dev, "%s\n", __func__); + + if (!icd->dev.parent || !vdev) + return; + + mutex_lock(&video_lock); + video_unregister_device(vdev); + icd->vdev = NULL; + mutex_unlock(&video_lock); +} +EXPORT_SYMBOL(soc_camera_video_stop); + +static int __init soc_camera_init(void) +{ + int ret = bus_register(&soc_camera_bus_type); + if (ret) + return ret; + ret = driver_register(&ic_drv); + if (ret) + goto edrvr; + ret = class_register(&soc_camera_host_class); + if (ret) + goto eclr; + + return 0; + +eclr: + driver_unregister(&ic_drv); +edrvr: + bus_unregister(&soc_camera_bus_type); + return ret; +} + +static void __exit soc_camera_exit(void) +{ + class_unregister(&soc_camera_host_class); + driver_unregister(&ic_drv); + bus_unregister(&soc_camera_bus_type); +} + +module_init(soc_camera_init); +module_exit(soc_camera_exit); + +MODULE_DESCRIPTION("Image capture bus driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index ceba45ad029..9276ed99738 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -1100,7 +1100,7 @@ static int stk_setup_format(struct stk_camera *dev) && i < ARRAY_SIZE(stk_sizes)) i++; if (i == ARRAY_SIZE(stk_sizes)) { - STK_ERROR("Something is broken in %s\n", __FUNCTION__); + STK_ERROR("Something is broken in %s\n", __func__); return -EFAULT; } /* This registers controls some timings, not sure of what. */ @@ -1465,7 +1465,7 @@ static void stk_camera_disconnect(struct usb_interface *interface) } #ifdef CONFIG_PM -int stk_camera_suspend(struct usb_interface *intf, pm_message_t message) +static int stk_camera_suspend(struct usb_interface *intf, pm_message_t message) { struct stk_camera *dev = usb_get_intfdata(intf); if (is_streaming(dev)) { @@ -1476,7 +1476,7 @@ int stk_camera_suspend(struct usb_interface *intf, pm_message_t message) return 0; } -int stk_camera_resume(struct usb_interface *intf) +static int stk_camera_resume(struct usb_interface *intf) { struct stk_camera *dev = usb_get_intfdata(intf); if (!is_initialised(dev)) diff --git a/drivers/media/video/stradis.c b/drivers/media/video/stradis.c index 3fb85af5d1f..c109511f21e 100644 --- a/drivers/media/video/stradis.c +++ b/drivers/media/video/stradis.c @@ -58,7 +58,7 @@ static struct saa7146 saa7146s[SAA7146_MAX]; -static int saa_num = 0; /* number of SAA7146s in use */ +static int saa_num; /* number of SAA7146s in use */ static int video_nr = -1; module_param(video_nr, int, 0); @@ -248,7 +248,7 @@ static void I2CBusScan(struct saa7146 *saa) attach_inform(saa, i); } -static int debiwait_maxwait = 0; +static int debiwait_maxwait; static int wait_for_debi_done(struct saa7146 *saa) { @@ -1906,7 +1906,9 @@ static const struct file_operations saa_fops = { .open = saa_open, .release = saa_release, .ioctl = saa_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .read = saa_read, .llseek = no_llseek, .write = saa_write, diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c index afc32aa56fd..d7f130bedb5 100644 --- a/drivers/media/video/stv680.c +++ b/drivers/media/video/stv680.c @@ -72,15 +72,18 @@ #include "stv680.h" static int video_nr = -1; -static int swapRGB = 0; /* default for auto sleect */ -static int swapRGB_on = 0; /* default to allow auto select; -1=swap never, +1= swap always */ -static unsigned int debug = 0; +static int swapRGB; /* 0 = default for auto select */ + +/* 0 = default to allow auto select; -1 = swap never, +1 = swap always */ +static int swapRGB_on; + +static unsigned int debug; #define PDEBUG(level, fmt, args...) \ do { \ if (debug >= level) \ - info("[%s:%d] " fmt, __FUNCTION__, __LINE__ , ## args); \ + info("[%s:%d] " fmt, __func__, __LINE__ , ## args); \ } while (0) @@ -1391,7 +1394,9 @@ static const struct file_operations stv680_fops = { .read = stv680_read, .mmap = stv680_mmap, .ioctl = stv680_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, }; static struct video_device stv680_template = { diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c index fb895f6684a..6943b447a1b 100644 --- a/drivers/media/video/tcm825x.c +++ b/drivers/media/video/tcm825x.c @@ -906,7 +906,7 @@ static int __init tcm825x_init(void) rval = i2c_add_driver(&tcm825x_i2c_driver); if (rval) printk(KERN_INFO "%s: failed registering " TCM825X_NAME "\n", - __FUNCTION__); + __func__); return rval; } diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c index 55bc89a6f06..0ebb5b525e5 100644 --- a/drivers/media/video/tda8290.c +++ b/drivers/media/video/tda8290.c @@ -32,8 +32,6 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); -#define PREFIX "tda8290" - /* ---------------------------------------------------------------------- */ struct tda8290_priv { @@ -174,7 +172,7 @@ static void tda8290_set_params(struct dvb_frontend *fe, set_audio(fe, params); if (priv->cfg.config) - tuner_dbg("tda827xa config is 0x%02x\n", *priv->cfg.config); + tuner_dbg("tda827xa config is 0x%02x\n", priv->cfg.config); tuner_i2c_xfer_send(&priv->i2c_props, easy_mode, 2); tuner_i2c_xfer_send(&priv->i2c_props, agc_out_on, 2); tuner_i2c_xfer_send(&priv->i2c_props, soft_reset, 2); @@ -365,7 +363,7 @@ static void tda8295_set_params(struct dvb_frontend *fe, set_audio(fe, params); - tuner_dbg("%s: freq = %d\n", __FUNCTION__, params->frequency); + tuner_dbg("%s: freq = %d\n", __func__, params->frequency); tda8295_power(fe, 1); tda8295_agc1_out(fe, 1); @@ -444,8 +442,7 @@ static void tda8290_init_if(struct dvb_frontend *fe) unsigned char set_GP00_CF[] = { 0x20, 0x01 }; unsigned char set_GP01_CF[] = { 0x20, 0x0B }; - if ((priv->cfg.config) && - ((*priv->cfg.config == 1) || (*priv->cfg.config == 2))) + if ((priv->cfg.config == 1) || (priv->cfg.config == 2)) tuner_i2c_xfer_send(&priv->i2c_props, set_GP00_CF, 2); else tuner_i2c_xfer_send(&priv->i2c_props, set_GP01_CF, 2); @@ -590,8 +587,8 @@ static int tda829x_find_tuner(struct dvb_frontend *fe) else priv->ver |= TDA8275A; - tda827x_attach(fe, priv->tda827x_addr, - priv->i2c_props.adap, &priv->cfg); + tda827x_attach(fe, priv->tda827x_addr, priv->i2c_props.adap, &priv->cfg); + priv->cfg.switch_addr = priv->i2c_props.addr; } if (fe->ops.tuner_ops.init) fe->ops.tuner_ops.init(fe); @@ -616,7 +613,7 @@ static int tda8290_probe(struct tuner_i2c_props *i2c_props) if (tda8290_id[1] == TDA8290_ID) { if (debug) printk(KERN_DEBUG "%s: tda8290 detected @ %d-%04x\n", - __FUNCTION__, i2c_adapter_id(i2c_props->adap), + __func__, i2c_adapter_id(i2c_props->adap), i2c_props->addr); return 0; } @@ -636,7 +633,7 @@ static int tda8295_probe(struct tuner_i2c_props *i2c_props) if (tda8295_id[1] == TDA8295_ID) { if (debug) printk(KERN_DEBUG "%s: tda8295 detected @ %d-%04x\n", - __FUNCTION__, i2c_adapter_id(i2c_props->adap), + __func__, i2c_adapter_id(i2c_props->adap), i2c_props->addr); return 0; } @@ -674,6 +671,7 @@ struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, priv->i2c_props.addr = i2c_addr; priv->i2c_props.adap = i2c_adap; + priv->i2c_props.name = "tda829x"; if (cfg) { priv->cfg.config = cfg->lna_cfg; priv->cfg.tuner_callback = cfg->tuner_callback; diff --git a/drivers/media/video/tda8290.h b/drivers/media/video/tda8290.h index dc8ef310b7b..d3bbf276a46 100644 --- a/drivers/media/video/tda8290.h +++ b/drivers/media/video/tda8290.h @@ -21,7 +21,7 @@ #include "dvb_frontend.h" struct tda829x_config { - unsigned int *lna_cfg; + unsigned int lna_cfg; int (*tuner_callback) (void *dev, int command, int arg); unsigned int probe_tuner:1; @@ -39,7 +39,7 @@ extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, #else static inline int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -EINVAL; } @@ -49,7 +49,7 @@ static inline struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, struct tda829x_config *cfg) { printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", - __FUNCTION__); + __func__); return NULL; } #endif diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index bdca5d27897..0cee0024278 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -31,11 +31,11 @@ #include "tda9840.h" -static int debug = 0; /* insmod parameter */ +static int debug; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); #define dprintk(args...) \ - do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __FUNCTION__, __LINE__); printk(args); } } while (0) + do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) #define SWITCH 0x00 #define LEVEL_ADJUST 0x02 diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index 106c93b8203..a0545ba957b 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -25,10 +25,12 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); -#define PREFIX "tda9887" +static DEFINE_MUTEX(tda9887_list_mutex); +static LIST_HEAD(hybrid_tuner_instance_list); struct tda9887_priv { struct tuner_i2c_props i2c_props; + struct list_head hybrid_tuner_instance_list; unsigned char data[4]; unsigned int config; @@ -644,7 +646,15 @@ static int tda9887_set_config(struct dvb_frontend *fe, void *priv_cfg) static void tda9887_release(struct dvb_frontend *fe) { - kfree(fe->analog_demod_priv); + struct tda9887_priv *priv = fe->analog_demod_priv; + + mutex_lock(&tda9887_list_mutex); + + if (priv) + hybrid_tuner_release_state(priv); + + mutex_unlock(&tda9887_list_mutex); + fe->analog_demod_priv = NULL; } @@ -665,17 +675,29 @@ struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, u8 i2c_addr) { struct tda9887_priv *priv = NULL; + int instance; - priv = kzalloc(sizeof(struct tda9887_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - fe->analog_demod_priv = priv; + mutex_lock(&tda9887_list_mutex); - priv->i2c_props.addr = i2c_addr; - priv->i2c_props.adap = i2c_adap; - priv->mode = T_STANDBY; + instance = hybrid_tuner_request_state(struct tda9887_priv, priv, + hybrid_tuner_instance_list, + i2c_adap, i2c_addr, "tda9887"); + switch (instance) { + case 0: + mutex_unlock(&tda9887_list_mutex); + return NULL; + break; + case 1: + fe->analog_demod_priv = priv; + priv->mode = T_STANDBY; + tuner_info("tda988[5/6/7] found\n"); + break; + default: + fe->analog_demod_priv = priv; + break; + } - tuner_info("tda988[5/6/7] found\n"); + mutex_unlock(&tda9887_list_mutex); memcpy(&fe->ops.analog_ops, &tda9887_ops, sizeof(struct analog_demod_ops)); diff --git a/drivers/media/video/tda9887.h b/drivers/media/video/tda9887.h index 8f873a8e6ed..be49dcbfc70 100644 --- a/drivers/media/video/tda9887.h +++ b/drivers/media/video/tda9887.h @@ -30,7 +30,7 @@ static inline struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap, u8 i2c_addr) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif diff --git a/drivers/media/video/tea5761.c b/drivers/media/video/tea5761.c index 5326eeceaac..b93cdef9ac7 100644 --- a/drivers/media/video/tea5761.c +++ b/drivers/media/video/tea5761.c @@ -14,12 +14,10 @@ #include "tuner-i2c.h" #include "tea5761.h" -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); -#define PREFIX "tea5761" - struct tea5761_priv { struct tuner_i2c_props i2c_props; @@ -131,7 +129,7 @@ static void tea5761_status_dump(unsigned char *buffer) frq = 1000 * (div * 32768 / 1000 + FREQ_OFFSET + 225) / 4; /* Freq in KHz */ - printk(PREFIX "Frequency %d.%03d KHz (divider = 0x%04x)\n", + printk(KERN_INFO "tea5761: Frequency %d.%03d KHz (divider = 0x%04x)\n", frq / 1000, frq % 1000, div); } @@ -249,14 +247,19 @@ int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) if (16 != (rc = tuner_i2c_xfer_recv(&i2c, buffer, 16))) { printk(KERN_WARNING "it is not a TEA5761. Received %i chars.\n", rc); - return EINVAL; + return -EINVAL; } - if (!((buffer[13] != 0x2b) || (buffer[14] != 0x57) || (buffer[15] != 0x061))) { - printk(KERN_WARNING "Manufacturer ID= 0x%02x, Chip ID = %02x%02x. It is not a TEA5761\n",buffer[13],buffer[14],buffer[15]); - return EINVAL; + if ((buffer[13] != 0x2b) || (buffer[14] != 0x57) || (buffer[15] != 0x061)) { + printk(KERN_WARNING "Manufacturer ID= 0x%02x, Chip ID = %02x%02x." + " It is not a TEA5761\n", + buffer[13], buffer[14], buffer[15]); + return -EINVAL; } - printk(KERN_WARNING "TEA5761 detected.\n"); + printk(KERN_WARNING "tea5761: TEA%02x%02x detected. " + "Manufacturer ID= 0x%02x\n", + buffer[14], buffer[15], buffer[13]); + return 0; } @@ -302,6 +305,7 @@ struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, priv->i2c_props.addr = i2c_addr; priv->i2c_props.adap = i2c_adap; + priv->i2c_props.name = "tea5761"; memcpy(&fe->ops.tuner_ops, &tea5761_tuner_ops, sizeof(struct dvb_tuner_ops)); diff --git a/drivers/media/video/tea5761.h b/drivers/media/video/tea5761.h index 73a03b42784..8eb62722b98 100644 --- a/drivers/media/video/tea5761.h +++ b/drivers/media/video/tea5761.h @@ -31,7 +31,7 @@ static inline int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) { printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", - __FUNCTION__); + __func__); return -EINVAL; } @@ -39,7 +39,7 @@ static inline struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap, u8 i2c_addr) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif diff --git a/drivers/media/video/tea5767.c b/drivers/media/video/tea5767.c index e1b48d87e7b..f6e7d7ad842 100644 --- a/drivers/media/video/tea5767.c +++ b/drivers/media/video/tea5767.c @@ -16,12 +16,10 @@ #include "tuner-i2c.h" #include "tea5767.h" -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); -#define PREFIX "tea5767" - /*****************************************************************************/ struct tea5767_priv { @@ -137,14 +135,14 @@ static void tea5767_status_dump(struct tea5767_priv *priv, unsigned int div, frq; if (TEA5767_READY_FLAG_MASK & buffer[0]) - printk(PREFIX "Ready Flag ON\n"); + tuner_info("Ready Flag ON\n"); else - printk(PREFIX "Ready Flag OFF\n"); + tuner_info("Ready Flag OFF\n"); if (TEA5767_BAND_LIMIT_MASK & buffer[0]) - printk(PREFIX "Tuner at band limit\n"); + tuner_info("Tuner at band limit\n"); else - printk(PREFIX "Tuner not at band limit\n"); + tuner_info("Tuner not at band limit\n"); div = ((buffer[0] & 0x3f) << 8) | buffer[1]; @@ -166,23 +164,23 @@ static void tea5767_status_dump(struct tea5767_priv *priv, buffer[0] = (div >> 8) & 0x3f; buffer[1] = div & 0xff; - printk(PREFIX "Frequency %d.%03d KHz (divider = 0x%04x)\n", - frq / 1000, frq % 1000, div); + tuner_info("Frequency %d.%03d KHz (divider = 0x%04x)\n", + frq / 1000, frq % 1000, div); if (TEA5767_STEREO_MASK & buffer[2]) - printk(PREFIX "Stereo\n"); + tuner_info("Stereo\n"); else - printk(PREFIX "Mono\n"); + tuner_info("Mono\n"); - printk(PREFIX "IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK); + tuner_info("IF Counter = %d\n", buffer[2] & TEA5767_IF_CNTR_MASK); - printk(PREFIX "ADC Level = %d\n", - (buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4); + tuner_info("ADC Level = %d\n", + (buffer[3] & TEA5767_ADC_LEVEL_MASK) >> 4); - printk(PREFIX "Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK)); + tuner_info("Chip ID = %d\n", (buffer[3] & TEA5767_CHIP_ID_MASK)); - printk(PREFIX "Reserved = 0x%02x\n", - (buffer[4] & TEA5767_RESERVED_MASK)); + tuner_info("Reserved = 0x%02x\n", + (buffer[4] & TEA5767_RESERVED_MASK)); } /* Freq should be specifyed at 62.5 Hz */ @@ -395,11 +393,6 @@ int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) return EINVAL; } - /* It seems that tea5767 returns 0xff after the 5th byte */ - if ((buffer[5] != 0xff) || (buffer[6] != 0xff)) { - printk(KERN_WARNING "Returned more than 5 bytes. It is not a TEA5767\n"); - return EINVAL; - } return 0; } @@ -456,6 +449,8 @@ struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, priv->i2c_props.addr = i2c_addr; priv->i2c_props.adap = i2c_adap; + priv->i2c_props.name = "tea5767"; + priv->ctrl.xtal_freq = TEA5767_HIGH_LO_32768; priv->ctrl.port1 = 1; priv->ctrl.port2 = 1; diff --git a/drivers/media/video/tea5767.h b/drivers/media/video/tea5767.h index a44451f6114..7b547c092e2 100644 --- a/drivers/media/video/tea5767.h +++ b/drivers/media/video/tea5767.h @@ -50,7 +50,7 @@ static inline int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr) { printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", - __FUNCTION__); + __func__); return -EINVAL; } @@ -58,7 +58,7 @@ static inline struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, struct i2c_adapter* i2c_adap, u8 i2c_addr) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c index df2fad9f391..9513d8611e8 100644 --- a/drivers/media/video/tea6415c.c +++ b/drivers/media/video/tea6415c.c @@ -33,11 +33,11 @@ #include "tea6415c.h" -static int debug = 0; /* insmod parameter */ +static int debug; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); #define dprintk(args...) \ - do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __FUNCTION__, __LINE__); printk(args); } } while (0) + do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) #define TEA6415C_NUM_INPUTS 8 #define TEA6415C_NUM_OUTPUTS 6 diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c index 4ff6c63f723..7fd53367c07 100644 --- a/drivers/media/video/tea6420.c +++ b/drivers/media/video/tea6420.c @@ -33,11 +33,11 @@ #include "tea6420.h" -static int debug = 0; /* insmod parameter */ +static int debug; /* insmod parameter */ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); #define dprintk(args...) \ - do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __FUNCTION__, __LINE__); printk(args); } } while (0) + do { if (debug) { printk("%s: %s()[%d]: ", KBUILD_MODNAME, __func__, __LINE__); printk(args); } } while (0) /* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */ static unsigned short normal_i2c[] = { I2C_ADDR_TEA6420_1, I2C_ADDR_TEA6420_2, I2C_CLIENT_END }; diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 78a09a2a485..529e00952a8 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -68,9 +68,9 @@ static unsigned short normal_i2c[] = { I2C_CLIENT_INSMOD; /* insmod options used at init time => read/only */ -static unsigned int addr = 0; -static unsigned int no_autodetect = 0; -static unsigned int show_i2c = 0; +static unsigned int addr; +static unsigned int no_autodetect; +static unsigned int show_i2c; /* insmod options used at runtime => read/write */ static int tuner_debug; @@ -313,24 +313,14 @@ static void tuner_i2c_address_check(struct tuner *t) tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n"); tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n"); tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n", - t->i2c->adapter->name, t->i2c->addr, t->type, - tuners[t->type].name); + t->i2c->adapter->name, t->i2c->addr, t->type, t->i2c->name); tuner_warn("====================== WARNING! ======================\n"); } -static void attach_simple_tuner(struct tuner *t) -{ - struct simple_tuner_config cfg = { - .type = t->type, - .tun = &tuners[t->type] - }; - simple_tuner_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg); -} - static void attach_tda829x(struct tuner *t) { struct tda829x_config cfg = { - .lna_cfg = &t->config, + .lna_cfg = t->config, .tuner_callback = t->tuner_callback, }; tda829x_attach(&t->fe, t->i2c->adapter, t->i2c->addr, &cfg); @@ -352,11 +342,6 @@ static void set_type(struct i2c_client *c, unsigned int type, return; } - if (type >= tuner_count) { - tuner_warn ("tuner 0x%02x: Tuner count greater than %d\n",c->addr,tuner_count); - return; - } - t->type = type; t->config = new_config; if (tuner_callback != NULL) { @@ -409,7 +394,12 @@ static void set_type(struct i2c_client *c, unsigned int type, buffer[2] = 0x86; buffer[3] = 0x54; i2c_master_send(c, buffer, 4); - attach_simple_tuner(t); + if (simple_tuner_attach(&t->fe, t->i2c->adapter, t->i2c->addr, + t->type) == NULL) { + t->type = TUNER_ABSENT; + t->mode_mask = T_UNINITIALIZED; + return; + } break; case TUNER_PHILIPS_TD1316: buffer[0] = 0x0b; @@ -417,14 +407,18 @@ static void set_type(struct i2c_client *c, unsigned int type, buffer[2] = 0x86; buffer[3] = 0xa4; i2c_master_send(c,buffer,4); - attach_simple_tuner(t); + if (simple_tuner_attach(&t->fe, t->i2c->adapter, + t->i2c->addr, t->type) == NULL) { + t->type = TUNER_ABSENT; + t->mode_mask = T_UNINITIALIZED; + return; + } break; case TUNER_XC2028: { struct xc2028_config cfg = { .i2c_adap = t->i2c->adapter, .i2c_addr = t->i2c->addr, - .video_dev = c->adapter->algo_data, .callback = t->tuner_callback, }; if (!xc2028_attach(&t->fe, &cfg)) { @@ -455,7 +449,12 @@ static void set_type(struct i2c_client *c, unsigned int type, } break; default: - attach_simple_tuner(t); + if (simple_tuner_attach(&t->fe, t->i2c->adapter, + t->i2c->addr, t->type) == NULL) { + t->type = TUNER_ABSENT; + t->mode_mask = T_UNINITIALIZED; + return; + } break; } @@ -759,7 +758,7 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (analog_ops->standby) analog_ops->standby(&t->fe); break; -#ifdef CONFIG_VIDEO_V4L1 +#ifdef CONFIG_VIDEO_ALLOW_V4L1 case VIDIOCSAUDIO: if (check_mode(t, "VIDIOCSAUDIO") == EINVAL) return 0; @@ -1112,8 +1111,8 @@ static int tuner_probe(struct i2c_client *client) if (!no_autodetect) { switch (client->addr) { case 0x10: - if (tea5761_autodetection(t->i2c->adapter, t->i2c->addr) - != EINVAL) { + if (tea5761_autodetection(t->i2c->adapter, + t->i2c->addr) >= 0) { t->type = TUNER_TEA5761; t->mode_mask = T_RADIO; t->mode = T_STANDBY; @@ -1125,7 +1124,7 @@ static int tuner_probe(struct i2c_client *client) goto register_client; } - break; + return -ENODEV; case 0x42: case 0x43: case 0x4a: diff --git a/drivers/media/video/tuner-i2c.h b/drivers/media/video/tuner-i2c.h index de52e8ffd34..3ad6c8e0b04 100644 --- a/drivers/media/video/tuner-i2c.h +++ b/drivers/media/video/tuner-i2c.h @@ -26,6 +26,10 @@ struct tuner_i2c_props { u8 addr; struct i2c_adapter *adap; + + /* used for tuner instance management */ + int count; + char *name; }; static inline int tuner_i2c_xfer_send(struct tuner_i2c_props *props, char *buf, int len) @@ -59,29 +63,111 @@ static inline int tuner_i2c_xfer_send_recv(struct tuner_i2c_props *props, return (ret == 2) ? ilen : ret; } -#define tuner_warn(fmt, arg...) do { \ - printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(priv->i2c_props.adap), \ - priv->i2c_props.addr, ##arg); \ +/* Callers must declare as a global for the module: + * + * static LIST_HEAD(hybrid_tuner_instance_list); + * + * hybrid_tuner_instance_list should be the third argument + * passed into hybrid_tuner_request_state(). + * + * state structure must contain the following: + * + * struct list_head hybrid_tuner_instance_list; + * struct tuner_i2c_props i2c_props; + * + * hybrid_tuner_instance_list (both within state structure and globally) + * is only required if the driver is using hybrid_tuner_request_state + * and hybrid_tuner_release_state to manage state sharing between + * multiple instances of hybrid tuners. + */ + +#define tuner_printk(kernlvl, i2cprops, fmt, arg...) do { \ + printk(kernlvl "%s %d-%04x: " fmt, i2cprops.name, \ + i2cprops.adap ? \ + i2c_adapter_id(i2cprops.adap) : -1, \ + i2cprops.addr, ##arg); \ } while (0) -#define tuner_info(fmt, arg...) do { \ - printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(priv->i2c_props.adap), \ - priv->i2c_props.addr , ##arg); \ +/* TO DO: convert all callers of these macros to pass in + * struct tuner_i2c_props, then remove the macro wrappers */ + +#define __tuner_warn(i2cprops, fmt, arg...) do { \ + tuner_printk(KERN_WARNING, i2cprops, fmt, ##arg); \ } while (0) -#define tuner_err(fmt, arg...) do { \ - printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(priv->i2c_props.adap), \ - priv->i2c_props.addr , ##arg); \ +#define __tuner_info(i2cprops, fmt, arg...) do { \ + tuner_printk(KERN_INFO, i2cprops, fmt, ##arg); \ } while (0) -#define tuner_dbg(fmt, arg...) do { \ +#define __tuner_err(i2cprops, fmt, arg...) do { \ + tuner_printk(KERN_ERR, i2cprops, fmt, ##arg); \ + } while (0) + +#define __tuner_dbg(i2cprops, fmt, arg...) do { \ if ((debug)) \ - printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(priv->i2c_props.adap), \ - priv->i2c_props.addr , ##arg); \ + tuner_printk(KERN_DEBUG, i2cprops, fmt, ##arg); \ } while (0) +#define tuner_warn(fmt, arg...) __tuner_warn(priv->i2c_props, fmt, ##arg) +#define tuner_info(fmt, arg...) __tuner_info(priv->i2c_props, fmt, ##arg) +#define tuner_err(fmt, arg...) __tuner_err(priv->i2c_props, fmt, ##arg) +#define tuner_dbg(fmt, arg...) __tuner_dbg(priv->i2c_props, fmt, ##arg) + +/****************************************************************************/ + +/* The return value of hybrid_tuner_request_state indicates the number of + * instances using this tuner object. + * + * 0 - no instances, indicates an error - kzalloc must have failed + * + * 1 - one instance, indicates that the tuner object was created successfully + * + * 2 (or more) instances, indicates that an existing tuner object was found + */ + +#define hybrid_tuner_request_state(type, state, list, i2cadap, i2caddr, devname)\ +({ \ + int __ret = 0; \ + list_for_each_entry(state, &list, hybrid_tuner_instance_list) { \ + if (((i2cadap) && (state->i2c_props.adap)) && \ + ((i2c_adapter_id(state->i2c_props.adap) == \ + i2c_adapter_id(i2cadap)) && \ + (i2caddr == state->i2c_props.addr))) { \ + __tuner_info(state->i2c_props, \ + "attaching existing instance\n"); \ + state->i2c_props.count++; \ + __ret = state->i2c_props.count; \ + break; \ + } \ + } \ + if (0 == __ret) { \ + state = kzalloc(sizeof(type), GFP_KERNEL); \ + if (NULL == state) \ + goto __fail; \ + state->i2c_props.addr = i2caddr; \ + state->i2c_props.adap = i2cadap; \ + state->i2c_props.name = devname; \ + __tuner_info(state->i2c_props, \ + "creating new instance\n"); \ + list_add_tail(&state->hybrid_tuner_instance_list, &list);\ + state->i2c_props.count++; \ + __ret = state->i2c_props.count; \ + } \ +__fail: \ + __ret; \ +}) + +#define hybrid_tuner_release_state(state) \ +({ \ + int __ret; \ + state->i2c_props.count--; \ + __ret = state->i2c_props.count; \ + if (!state->i2c_props.count) { \ + __tuner_info(state->i2c_props, "destroying instance\n");\ + list_del(&state->hybrid_tuner_instance_list); \ + kfree(state); \ + } \ + __ret; \ +}) + #endif /* __TUNER_I2C_H__ */ diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c index c1db576696c..be8d903171b 100644 --- a/drivers/media/video/tuner-simple.c +++ b/drivers/media/video/tuner-simple.c @@ -13,15 +13,25 @@ #include "tuner-i2c.h" #include "tuner-simple.h" -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); -#define PREFIX "tuner-simple" +#define TUNER_SIMPLE_MAX 64 +static unsigned int simple_devcount; -static int offset = 0; +static int offset; module_param(offset, int, 0664); -MODULE_PARM_DESC(offset,"Allows to specify an offset for tuner"); +MODULE_PARM_DESC(offset, "Allows to specify an offset for tuner"); + +static unsigned int atv_input[TUNER_SIMPLE_MAX] = \ + { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 }; +static unsigned int dtv_input[TUNER_SIMPLE_MAX] = \ + { [0 ... (TUNER_SIMPLE_MAX-1)] = 0 }; +module_param_array(atv_input, int, NULL, 0644); +module_param_array(dtv_input, int, NULL, 0644); +MODULE_PARM_DESC(atv_input, "specify atv rf input, 0 for autoselect"); +MODULE_PARM_DESC(dtv_input, "specify dtv rf input, 0 for autoselect"); /* ---------------------------------------------------------------------- */ @@ -36,8 +46,8 @@ MODULE_PARM_DESC(offset,"Allows to specify an offset for tuner"); */ #define TEMIC_SET_PAL_I 0x05 #define TEMIC_SET_PAL_DK 0x09 -#define TEMIC_SET_PAL_L 0x0a // SECAM ? -#define TEMIC_SET_PAL_L2 0x0b // change IF ! +#define TEMIC_SET_PAL_L 0x0a /* SECAM ? */ +#define TEMIC_SET_PAL_L2 0x0b /* change IF ! */ #define TEMIC_SET_PAL_BG 0x0c /* tv tuner system standard selection for Philips FQ1216ME @@ -90,14 +100,21 @@ MODULE_PARM_DESC(offset,"Allows to specify an offset for tuner"); #define TUNER_PLL_LOCKED 0x40 #define TUNER_STEREO_MK3 0x04 +static DEFINE_MUTEX(tuner_simple_list_mutex); +static LIST_HEAD(hybrid_tuner_instance_list); + struct tuner_simple_priv { + unsigned int nr; u16 last_div; + struct tuner_i2c_props i2c_props; + struct list_head hybrid_tuner_instance_list; unsigned int type; struct tunertype *tun; u32 frequency; + u32 bandwidth; }; /* ---------------------------------------------------------------------- */ @@ -107,7 +124,7 @@ static int tuner_read_status(struct dvb_frontend *fe) struct tuner_simple_priv *priv = fe->tuner_priv; unsigned char byte; - if (1 != tuner_i2c_xfer_recv(&priv->i2c_props,&byte,1)) + if (1 != tuner_i2c_xfer_recv(&priv->i2c_props, &byte, 1)) return 0; return byte; @@ -121,13 +138,13 @@ static inline int tuner_signal(const int status) static inline int tuner_stereo(const int type, const int status) { switch (type) { - case TUNER_PHILIPS_FM1216ME_MK3: - case TUNER_PHILIPS_FM1236_MK3: - case TUNER_PHILIPS_FM1256_IH3: - case TUNER_LG_NTSC_TAPE: - return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3); - default: - return status & TUNER_STEREO; + case TUNER_PHILIPS_FM1216ME_MK3: + case TUNER_PHILIPS_FM1236_MK3: + case TUNER_PHILIPS_FM1256_IH3: + case TUNER_LG_NTSC_TAPE: + return ((status & TUNER_SIGNAL) == TUNER_STEREO_MK3); + default: + return status & TUNER_STEREO; } } @@ -145,7 +162,12 @@ static inline int tuner_afcstatus(const int status) static int simple_get_status(struct dvb_frontend *fe, u32 *status) { struct tuner_simple_priv *priv = fe->tuner_priv; - int tuner_status = tuner_read_status(fe); + int tuner_status; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + tuner_status = tuner_read_status(fe); *status = 0; @@ -162,7 +184,12 @@ static int simple_get_status(struct dvb_frontend *fe, u32 *status) static int simple_get_rf_strength(struct dvb_frontend *fe, u16 *strength) { struct tuner_simple_priv *priv = fe->tuner_priv; - int signal = tuner_signal(tuner_read_status(fe)); + int signal; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + signal = tuner_signal(tuner_read_status(fe)); *strength = signal; @@ -173,174 +200,378 @@ static int simple_get_rf_strength(struct dvb_frontend *fe, u16 *strength) /* ---------------------------------------------------------------------- */ -static int simple_set_tv_freq(struct dvb_frontend *fe, - struct analog_parameters *params) +static inline char *tuner_param_name(enum param_type type) { - struct tuner_simple_priv *priv = fe->tuner_priv; - u8 config, cb, tuneraddr; - u16 div; - struct tunertype *tun; - u8 buffer[4]; - int rc, IFPCoff, i, j; - enum param_type desired_type; - struct tuner_params *t_params; + char *name; - tun = priv->tun; + switch (type) { + case TUNER_PARAM_TYPE_RADIO: + name = "radio"; + break; + case TUNER_PARAM_TYPE_PAL: + name = "pal"; + break; + case TUNER_PARAM_TYPE_SECAM: + name = "secam"; + break; + case TUNER_PARAM_TYPE_NTSC: + name = "ntsc"; + break; + case TUNER_PARAM_TYPE_DIGITAL: + name = "digital"; + break; + default: + name = "unknown"; + break; + } + return name; +} - /* IFPCoff = Video Intermediate Frequency - Vif: - 940 =16*58.75 NTSC/J (Japan) - 732 =16*45.75 M/N STD - 704 =16*44 ATSC (at DVB code) - 632 =16*39.50 I U.K. - 622.4=16*38.90 B/G D/K I, L STD - 592 =16*37.00 D China - 590 =16.36.875 B Australia - 543.2=16*33.95 L' STD - 171.2=16*10.70 FM Radio (at set_radio_freq) - */ +static struct tuner_params *simple_tuner_params(struct dvb_frontend *fe, + enum param_type desired_type) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + struct tunertype *tun = priv->tun; + int i; - if (params->std == V4L2_STD_NTSC_M_JP) { - IFPCoff = 940; - desired_type = TUNER_PARAM_TYPE_NTSC; - } else if ((params->std & V4L2_STD_MN) && - !(params->std & ~V4L2_STD_MN)) { - IFPCoff = 732; - desired_type = TUNER_PARAM_TYPE_NTSC; - } else if (params->std == V4L2_STD_SECAM_LC) { - IFPCoff = 543; - desired_type = TUNER_PARAM_TYPE_SECAM; - } else { - IFPCoff = 623; - desired_type = TUNER_PARAM_TYPE_PAL; - } + for (i = 0; i < tun->count; i++) + if (desired_type == tun->params[i].type) + break; - for (j = 0; j < tun->count-1; j++) { - if (desired_type != tun->params[j].type) - continue; - break; - } - /* use default tuner_t_params if desired_type not available */ - if (desired_type != tun->params[j].type) { - tuner_dbg("IFPCoff = %d: tuner_t_params undefined for tuner %d\n", - IFPCoff, priv->type); - j = 0; + /* use default tuner params if desired_type not available */ + if (i == tun->count) { + tuner_dbg("desired params (%s) undefined for tuner %d\n", + tuner_param_name(desired_type), priv->type); + i = 0; } - t_params = &tun->params[j]; + + tuner_dbg("using tuner params #%d (%s)\n", i, + tuner_param_name(tun->params[i].type)); + + return &tun->params[i]; +} + +static int simple_config_lookup(struct dvb_frontend *fe, + struct tuner_params *t_params, + int *frequency, u8 *config, u8 *cb) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + int i; for (i = 0; i < t_params->count; i++) { - if (params->frequency > t_params->ranges[i].limit) + if (*frequency > t_params->ranges[i].limit) continue; break; } if (i == t_params->count) { - tuner_dbg("TV frequency out of range (%d > %d)", - params->frequency, t_params->ranges[i - 1].limit); - params->frequency = t_params->ranges[--i].limit; + tuner_dbg("frequency out of range (%d > %d)\n", + *frequency, t_params->ranges[i - 1].limit); + *frequency = t_params->ranges[--i].limit; } - config = t_params->ranges[i].config; - cb = t_params->ranges[i].cb; - /* i == 0 -> VHF_LO - * i == 1 -> VHF_HI - * i == 2 -> UHF */ - tuner_dbg("tv: param %d, range %d\n",j,i); + *config = t_params->ranges[i].config; + *cb = t_params->ranges[i].cb; + + tuner_dbg("freq = %d.%02d (%d), range = %d, " + "config = 0x%02x, cb = 0x%02x\n", + *frequency / 16, *frequency % 16 * 100 / 16, *frequency, + i, *config, *cb); + + return i; +} + +/* ---------------------------------------------------------------------- */ + +static void simple_set_rf_input(struct dvb_frontend *fe, + u8 *config, u8 *cb, unsigned int rf) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; - div=params->frequency + IFPCoff + offset; + switch (priv->type) { + case TUNER_PHILIPS_TUV1236D: + switch (rf) { + case 1: + *cb |= 0x08; + break; + default: + *cb &= ~0x08; + break; + } + break; + case TUNER_PHILIPS_FCV1236D: + switch (rf) { + case 1: + *cb |= 0x01; + break; + default: + *cb &= ~0x01; + break; + } + break; + default: + break; + } +} - tuner_dbg("Freq= %d.%02d MHz, V_IF=%d.%02d MHz, Offset=%d.%02d MHz, div=%0d\n", - params->frequency / 16, params->frequency % 16 * 100 / 16, - IFPCoff / 16, IFPCoff % 16 * 100 / 16, - offset / 16, offset % 16 * 100 / 16, - div); +static int simple_std_setup(struct dvb_frontend *fe, + struct analog_parameters *params, + u8 *config, u8 *cb) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + u8 tuneraddr; + int rc; /* tv norm specific stuff for multi-norm tuners */ switch (priv->type) { - case TUNER_PHILIPS_SECAM: // FI1216MF + case TUNER_PHILIPS_SECAM: /* FI1216MF */ /* 0x01 -> ??? no change ??? */ /* 0x02 -> PAL BDGHI / SECAM L */ /* 0x04 -> ??? PAL others / SECAM others ??? */ - cb &= ~0x03; - if (params->std & V4L2_STD_SECAM_L) //also valid for V4L2_STD_SECAM - cb |= PHILIPS_MF_SET_STD_L; + *cb &= ~0x03; + if (params->std & V4L2_STD_SECAM_L) + /* also valid for V4L2_STD_SECAM */ + *cb |= PHILIPS_MF_SET_STD_L; else if (params->std & V4L2_STD_SECAM_LC) - cb |= PHILIPS_MF_SET_STD_LC; + *cb |= PHILIPS_MF_SET_STD_LC; else /* V4L2_STD_B|V4L2_STD_GH */ - cb |= PHILIPS_MF_SET_STD_BG; + *cb |= PHILIPS_MF_SET_STD_BG; break; case TUNER_TEMIC_4046FM5: - cb &= ~0x0f; + *cb &= ~0x0f; if (params->std & V4L2_STD_PAL_BG) { - cb |= TEMIC_SET_PAL_BG; + *cb |= TEMIC_SET_PAL_BG; } else if (params->std & V4L2_STD_PAL_I) { - cb |= TEMIC_SET_PAL_I; + *cb |= TEMIC_SET_PAL_I; } else if (params->std & V4L2_STD_PAL_DK) { - cb |= TEMIC_SET_PAL_DK; + *cb |= TEMIC_SET_PAL_DK; } else if (params->std & V4L2_STD_SECAM_L) { - cb |= TEMIC_SET_PAL_L; + *cb |= TEMIC_SET_PAL_L; } break; case TUNER_PHILIPS_FQ1216ME: - cb &= ~0x0f; + *cb &= ~0x0f; if (params->std & (V4L2_STD_PAL_BG|V4L2_STD_PAL_DK)) { - cb |= PHILIPS_SET_PAL_BGDK; + *cb |= PHILIPS_SET_PAL_BGDK; } else if (params->std & V4L2_STD_PAL_I) { - cb |= PHILIPS_SET_PAL_I; + *cb |= PHILIPS_SET_PAL_I; } else if (params->std & V4L2_STD_SECAM_L) { - cb |= PHILIPS_SET_PAL_L; + *cb |= PHILIPS_SET_PAL_L; } break; - case TUNER_PHILIPS_ATSC: + case TUNER_PHILIPS_FCV1236D: /* 0x00 -> ATSC antenna input 1 */ /* 0x01 -> ATSC antenna input 2 */ /* 0x02 -> NTSC antenna input 1 */ /* 0x03 -> NTSC antenna input 2 */ - cb &= ~0x03; + *cb &= ~0x03; if (!(params->std & V4L2_STD_ATSC)) - cb |= 2; - /* FIXME: input */ + *cb |= 2; break; case TUNER_MICROTUNE_4042FI5: /* Set the charge pump for fast tuning */ - config |= TUNER_CHARGE_PUMP; + *config |= TUNER_CHARGE_PUMP; break; case TUNER_PHILIPS_TUV1236D: + { /* 0x40 -> ATSC antenna input 1 */ /* 0x48 -> ATSC antenna input 2 */ /* 0x00 -> NTSC antenna input 1 */ /* 0x08 -> NTSC antenna input 2 */ - buffer[0] = 0x14; - buffer[1] = 0x00; - buffer[2] = 0x17; - buffer[3] = 0x00; - cb &= ~0x40; + u8 buffer[4] = { 0x14, 0x00, 0x17, 0x00}; + *cb &= ~0x40; if (params->std & V4L2_STD_ATSC) { - cb |= 0x40; + *cb |= 0x40; buffer[1] = 0x04; } /* set to the correct mode (analog or digital) */ tuneraddr = priv->i2c_props.addr; priv->i2c_props.addr = 0x0a; - if (2 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,&buffer[0],2))) - tuner_warn("i2c i/o error: rc == %d (should be 2)\n",rc); - if (2 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,&buffer[2],2))) - tuner_warn("i2c i/o error: rc == %d (should be 2)\n",rc); + rc = tuner_i2c_xfer_send(&priv->i2c_props, &buffer[0], 2); + if (2 != rc) + tuner_warn("i2c i/o error: rc == %d " + "(should be 2)\n", rc); + rc = tuner_i2c_xfer_send(&priv->i2c_props, &buffer[2], 2); + if (2 != rc) + tuner_warn("i2c i/o error: rc == %d " + "(should be 2)\n", rc); priv->i2c_props.addr = tuneraddr; - /* FIXME: input */ break; } + } + if (atv_input[priv->nr]) + simple_set_rf_input(fe, config, cb, atv_input[priv->nr]); + + return 0; +} + +static int simple_post_tune(struct dvb_frontend *fe, u8 *buffer, + u16 div, u8 config, u8 cb) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + int rc; + + switch (priv->type) { + case TUNER_LG_TDVS_H06XF: + /* Set the Auxiliary Byte. */ + buffer[0] = buffer[2]; + buffer[0] &= ~0x20; + buffer[0] |= 0x18; + buffer[1] = 0x20; + tuner_dbg("tv 0x%02x 0x%02x\n", buffer[0], buffer[1]); + + rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 2); + if (2 != rc) + tuner_warn("i2c i/o error: rc == %d " + "(should be 2)\n", rc); + break; + case TUNER_MICROTUNE_4042FI5: + { + /* FIXME - this may also work for other tuners */ + unsigned long timeout = jiffies + msecs_to_jiffies(1); + u8 status_byte = 0; + + /* Wait until the PLL locks */ + for (;;) { + if (time_after(jiffies, timeout)) + return 0; + rc = tuner_i2c_xfer_recv(&priv->i2c_props, + &status_byte, 1); + if (1 != rc) { + tuner_warn("i2c i/o read error: rc == %d " + "(should be 1)\n", rc); + break; + } + if (status_byte & TUNER_PLL_LOCKED) + break; + udelay(10); + } + + /* Set the charge pump for optimized phase noise figure */ + config &= ~TUNER_CHARGE_PUMP; + buffer[0] = (div>>8) & 0x7f; + buffer[1] = div & 0xff; + buffer[2] = config; + buffer[3] = cb; + tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", + buffer[0], buffer[1], buffer[2], buffer[3]); + + rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); + if (4 != rc) + tuner_warn("i2c i/o error: rc == %d " + "(should be 4)\n", rc); + break; + } + } + + return 0; +} + +static int simple_radio_bandswitch(struct dvb_frontend *fe, u8 *buffer) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + + switch (priv->type) { + case TUNER_TENA_9533_DI: + case TUNER_YMEC_TVF_5533MF: + tuner_dbg("This tuner doesn't have FM. " + "Most cards have a TEA5767 for FM\n"); + return 0; + case TUNER_PHILIPS_FM1216ME_MK3: + case TUNER_PHILIPS_FM1236_MK3: + case TUNER_PHILIPS_FMD1216ME_MK3: + case TUNER_LG_NTSC_TAPE: + case TUNER_PHILIPS_FM1256_IH3: + buffer[3] = 0x19; + break; + case TUNER_TNF_5335MF: + buffer[3] = 0x11; + break; + case TUNER_LG_PAL_FM: + buffer[3] = 0xa5; + break; + case TUNER_THOMSON_DTT761X: + buffer[3] = 0x39; + break; + case TUNER_MICROTUNE_4049FM5: + default: + buffer[3] = 0xa4; + break; + } + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static int simple_set_tv_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + u8 config, cb; + u16 div; + struct tunertype *tun; + u8 buffer[4]; + int rc, IFPCoff, i; + enum param_type desired_type; + struct tuner_params *t_params; + + tun = priv->tun; + + /* IFPCoff = Video Intermediate Frequency - Vif: + 940 =16*58.75 NTSC/J (Japan) + 732 =16*45.75 M/N STD + 704 =16*44 ATSC (at DVB code) + 632 =16*39.50 I U.K. + 622.4=16*38.90 B/G D/K I, L STD + 592 =16*37.00 D China + 590 =16.36.875 B Australia + 543.2=16*33.95 L' STD + 171.2=16*10.70 FM Radio (at set_radio_freq) + */ + + if (params->std == V4L2_STD_NTSC_M_JP) { + IFPCoff = 940; + desired_type = TUNER_PARAM_TYPE_NTSC; + } else if ((params->std & V4L2_STD_MN) && + !(params->std & ~V4L2_STD_MN)) { + IFPCoff = 732; + desired_type = TUNER_PARAM_TYPE_NTSC; + } else if (params->std == V4L2_STD_SECAM_LC) { + IFPCoff = 543; + desired_type = TUNER_PARAM_TYPE_SECAM; + } else { + IFPCoff = 623; + desired_type = TUNER_PARAM_TYPE_PAL; + } + + t_params = simple_tuner_params(fe, desired_type); + + i = simple_config_lookup(fe, t_params, ¶ms->frequency, + &config, &cb); + + div = params->frequency + IFPCoff + offset; + + tuner_dbg("Freq= %d.%02d MHz, V_IF=%d.%02d MHz, " + "Offset=%d.%02d MHz, div=%0d\n", + params->frequency / 16, params->frequency % 16 * 100 / 16, + IFPCoff / 16, IFPCoff % 16 * 100 / 16, + offset / 16, offset % 16 * 100 / 16, div); + + /* tv norm specific stuff for multi-norm tuners */ + simple_std_setup(fe, params, &config, &cb); if (t_params->cb_first_if_lower_freq && div < priv->last_div) { buffer[0] = config; @@ -357,8 +588,10 @@ static int simple_set_tv_freq(struct dvb_frontend *fe, if (t_params->has_tda9887) { struct v4l2_priv_tun_config tda9887_cfg; int config = 0; - int is_secam_l = (params->std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) && - !(params->std & ~(V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)); + int is_secam_l = (params->std & (V4L2_STD_SECAM_L | + V4L2_STD_SECAM_LC)) && + !(params->std & ~(V4L2_STD_SECAM_L | + V4L2_STD_SECAM_LC)); tda9887_cfg.tuner = TUNER_TDA9887; tda9887_cfg.priv = &config; @@ -368,8 +601,7 @@ static int simple_set_tv_freq(struct dvb_frontend *fe, config |= TDA9887_PORT1_ACTIVE; if (t_params->port2_active ^ t_params->port2_invert_for_secam_lc) config |= TDA9887_PORT2_ACTIVE; - } - else { + } else { if (t_params->port1_active) config |= TDA9887_PORT1_ACTIVE; if (t_params->port2_active) @@ -384,8 +616,7 @@ static int simple_set_tv_freq(struct dvb_frontend *fe, config |= TDA9887_TOP(t_params->default_top_secam_mid); else if (t_params->default_top_secam_high) config |= TDA9887_TOP(t_params->default_top_secam_high); - } - else { + } else { if (i == 0 && t_params->default_top_low) config |= TDA9887_TOP(t_params->default_top_low); else if (i == 1 && t_params->default_top_mid) @@ -399,56 +630,14 @@ static int simple_set_tv_freq(struct dvb_frontend *fe, &tda9887_cfg); } tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", - buffer[0],buffer[1],buffer[2],buffer[3]); - - if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,buffer,4))) - tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc); - - switch (priv->type) { - case TUNER_LG_TDVS_H06XF: - /* Set the Auxiliary Byte. */ - buffer[0] = buffer[2]; - buffer[0] &= ~0x20; - buffer[0] |= 0x18; - buffer[1] = 0x20; - tuner_dbg("tv 0x%02x 0x%02x\n",buffer[0],buffer[1]); - - if (2 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,buffer,2))) - tuner_warn("i2c i/o error: rc == %d (should be 2)\n",rc); - break; - case TUNER_MICROTUNE_4042FI5: - { - // FIXME - this may also work for other tuners - unsigned long timeout = jiffies + msecs_to_jiffies(1); - u8 status_byte = 0; + buffer[0], buffer[1], buffer[2], buffer[3]); - /* Wait until the PLL locks */ - for (;;) { - if (time_after(jiffies,timeout)) - return 0; - if (1 != (rc = tuner_i2c_xfer_recv(&priv->i2c_props,&status_byte,1))) { - tuner_warn("i2c i/o read error: rc == %d (should be 1)\n",rc); - break; - } - if (status_byte & TUNER_PLL_LOCKED) - break; - udelay(10); - } + rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); + if (4 != rc) + tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc); - /* Set the charge pump for optimized phase noise figure */ - config &= ~TUNER_CHARGE_PUMP; - buffer[0] = (div>>8) & 0x7f; - buffer[1] = div & 0xff; - buffer[2] = config; - buffer[3] = cb; - tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n", - buffer[0],buffer[1],buffer[2],buffer[3]); + simple_post_tune(fe, &buffer[0], div, config, cb); - if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,buffer,4))) - tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc); - break; - } - } return 0; } @@ -483,37 +672,13 @@ static int simple_set_radio_freq(struct dvb_frontend *fe, freq += (unsigned int)(41.3*16000); break; default: - tuner_warn("Unsupported radio_if value %d\n", t_params->radio_if); + tuner_warn("Unsupported radio_if value %d\n", + t_params->radio_if); return 0; } /* Bandswitch byte */ - switch (priv->type) { - case TUNER_TENA_9533_DI: - case TUNER_YMEC_TVF_5533MF: - tuner_dbg("This tuner doesn't have FM. Most cards have a TEA5767 for FM\n"); - return 0; - case TUNER_PHILIPS_FM1216ME_MK3: - case TUNER_PHILIPS_FM1236_MK3: - case TUNER_PHILIPS_FMD1216ME_MK3: - case TUNER_LG_NTSC_TAPE: - case TUNER_PHILIPS_FM1256_IH3: - buffer[3] = 0x19; - break; - case TUNER_TNF_5335MF: - buffer[3] = 0x11; - break; - case TUNER_LG_PAL_FM: - buffer[3] = 0xa5; - break; - case TUNER_THOMSON_DTT761X: - buffer[3] = 0x39; - break; - case TUNER_MICROTUNE_4049FM5: - default: - buffer[3] = 0xa4; - break; - } + simple_radio_bandswitch(fe, &buffer[0]); buffer[2] = (t_params->ranges[0].config & ~TUNER_RATIO_MASK) | TUNER_RATIO_SELECT_50; /* 50 kHz step */ @@ -534,7 +699,7 @@ static int simple_set_radio_freq(struct dvb_frontend *fe, } tuner_dbg("radio 0x%02x 0x%02x 0x%02x 0x%02x\n", - buffer[0],buffer[1],buffer[2],buffer[3]); + buffer[0], buffer[1], buffer[2], buffer[3]); priv->last_div = div; if (t_params->has_tda9887) { @@ -544,9 +709,11 @@ static int simple_set_radio_freq(struct dvb_frontend *fe, tda9887_cfg.tuner = TUNER_TDA9887; tda9887_cfg.priv = &config; - if (t_params->port1_active && !t_params->port1_fm_high_sensitivity) + if (t_params->port1_active && + !t_params->port1_fm_high_sensitivity) config |= TDA9887_PORT1_ACTIVE; - if (t_params->port2_active && !t_params->port2_fm_high_sensitivity) + if (t_params->port2_active && + !t_params->port2_fm_high_sensitivity) config |= TDA9887_PORT2_ACTIVE; if (t_params->intercarrier_mode) config |= TDA9887_INTERCARRIER; @@ -557,10 +724,11 @@ static int simple_set_radio_freq(struct dvb_frontend *fe, if (t_params->radio_if == 2) config |= TDA9887_RIF_41_3; i2c_clients_command(priv->i2c_props.adap, TUNER_SET_CONFIG, - &tda9887_cfg); + &tda9887_cfg); } - if (4 != (rc = tuner_i2c_xfer_send(&priv->i2c_props,buffer,4))) - tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc); + rc = tuner_i2c_xfer_send(&priv->i2c_props, buffer, 4); + if (4 != rc) + tuner_warn("i2c i/o error: rc == %d (should be 4)\n", rc); return 0; } @@ -571,6 +739,9 @@ static int simple_set_params(struct dvb_frontend *fe, struct tuner_simple_priv *priv = fe->tuner_priv; int ret = -EINVAL; + if (priv->i2c_props.adap == NULL) + return -EINVAL; + switch (params->mode) { case V4L2_TUNER_RADIO: ret = simple_set_radio_freq(fe, params); @@ -582,14 +753,210 @@ static int simple_set_params(struct dvb_frontend *fe, priv->frequency = params->frequency * 62500; break; } + priv->bandwidth = 0; return ret; } +static void simple_set_dvb(struct dvb_frontend *fe, u8 *buf, + const struct dvb_frontend_parameters *params) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + + switch (priv->type) { + case TUNER_PHILIPS_FMD1216ME_MK3: + if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ && + params->frequency >= 158870000) + buf[3] |= 0x08; + break; + case TUNER_PHILIPS_TD1316: + /* determine band */ + buf[3] |= (params->frequency < 161000000) ? 1 : + (params->frequency < 444000000) ? 2 : 4; + + /* setup PLL filter */ + if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) + buf[3] |= 1 << 3; + break; + case TUNER_PHILIPS_TUV1236D: + case TUNER_PHILIPS_FCV1236D: + { + unsigned int new_rf; + + if (dtv_input[priv->nr]) + new_rf = dtv_input[priv->nr]; + else + switch (params->u.vsb.modulation) { + case QAM_64: + case QAM_256: + new_rf = 1; + break; + case VSB_8: + default: + new_rf = 0; + break; + } + simple_set_rf_input(fe, &buf[2], &buf[3], new_rf); + break; + } + default: + break; + } +} + +static u32 simple_dvb_configure(struct dvb_frontend *fe, u8 *buf, + const struct dvb_frontend_parameters *params) +{ + /* This function returns the tuned frequency on success, 0 on error */ + struct tuner_simple_priv *priv = fe->tuner_priv; + struct tunertype *tun = priv->tun; + static struct tuner_params *t_params; + u8 config, cb; + u32 div; + int ret, frequency = params->frequency / 62500; + + t_params = simple_tuner_params(fe, TUNER_PARAM_TYPE_DIGITAL); + ret = simple_config_lookup(fe, t_params, &frequency, &config, &cb); + if (ret < 0) + return 0; /* failure */ + + div = ((frequency + t_params->iffreq) * 62500 + offset + + tun->stepsize/2) / tun->stepsize; + + buf[0] = div >> 8; + buf[1] = div & 0xff; + buf[2] = config; + buf[3] = cb; + + simple_set_dvb(fe, buf, params); + + tuner_dbg("%s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n", + tun->name, div, buf[0], buf[1], buf[2], buf[3]); + + /* calculate the frequency we set it to */ + return (div * tun->stepsize) - t_params->iffreq; +} + +static int simple_dvb_calc_regs(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params, + u8 *buf, int buf_len) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + u32 frequency; + + if (buf_len < 5) + return -EINVAL; + + frequency = simple_dvb_configure(fe, buf+1, params); + if (frequency == 0) + return -EINVAL; + + buf[0] = priv->i2c_props.addr; + + priv->frequency = frequency; + priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? + params->u.ofdm.bandwidth : 0; + + return 5; +} + +static int simple_dvb_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + u32 prev_freq, prev_bw; + int ret; + u8 buf[5]; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + prev_freq = priv->frequency; + prev_bw = priv->bandwidth; + + ret = simple_dvb_calc_regs(fe, params, buf, 5); + if (ret != 5) + goto fail; + + /* put analog demod in standby when tuning digital */ + if (fe->ops.analog_ops.standby) + fe->ops.analog_ops.standby(fe); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* buf[0] contains the i2c address, but * + * we already have it in i2c_props.addr */ + ret = tuner_i2c_xfer_send(&priv->i2c_props, buf+1, 4); + if (ret != 4) + goto fail; + + return 0; +fail: + /* calc_regs sets frequency and bandwidth. if we failed, unset them */ + priv->frequency = prev_freq; + priv->bandwidth = prev_bw; + + return ret; +} + +static int simple_init(struct dvb_frontend *fe) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + if (priv->tun->initdata) { + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = tuner_i2c_xfer_send(&priv->i2c_props, + priv->tun->initdata + 1, + priv->tun->initdata[0]); + if (ret != priv->tun->initdata[0]) + return ret; + } + + return 0; +} + +static int simple_sleep(struct dvb_frontend *fe) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + if (priv->tun->sleepdata) { + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + ret = tuner_i2c_xfer_send(&priv->i2c_props, + priv->tun->sleepdata + 1, + priv->tun->sleepdata[0]); + if (ret != priv->tun->sleepdata[0]) + return ret; + } + + return 0; +} static int simple_release(struct dvb_frontend *fe) { - kfree(fe->tuner_priv); + struct tuner_simple_priv *priv = fe->tuner_priv; + + mutex_lock(&tuner_simple_list_mutex); + + if (priv) + hybrid_tuner_release_state(priv); + + mutex_unlock(&tuner_simple_list_mutex); + fe->tuner_priv = NULL; return 0; @@ -602,10 +969,22 @@ static int simple_get_frequency(struct dvb_frontend *fe, u32 *frequency) return 0; } +static int simple_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct tuner_simple_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + static struct dvb_tuner_ops simple_tuner_ops = { + .init = simple_init, + .sleep = simple_sleep, .set_analog_params = simple_set_params, + .set_params = simple_dvb_set_params, + .calc_regs = simple_dvb_calc_regs, .release = simple_release, .get_frequency = simple_get_frequency, + .get_bandwidth = simple_get_bandwidth, .get_status = simple_get_status, .get_rf_strength = simple_get_rf_strength, }; @@ -613,30 +992,92 @@ static struct dvb_tuner_ops simple_tuner_ops = { struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap, u8 i2c_addr, - struct simple_tuner_config *cfg) + unsigned int type) { struct tuner_simple_priv *priv = NULL; + int instance; - priv = kzalloc(sizeof(struct tuner_simple_priv), GFP_KERNEL); - if (priv == NULL) + if (type >= tuner_count) { + printk(KERN_WARNING "%s: invalid tuner type: %d (max: %d)\n", + __func__, type, tuner_count-1); return NULL; - fe->tuner_priv = priv; + } - priv->i2c_props.addr = i2c_addr; - priv->i2c_props.adap = i2c_adap; - priv->type = cfg->type; - priv->tun = cfg->tun; + /* If i2c_adap is set, check that the tuner is at the correct address. + * Otherwise, if i2c_adap is NULL, the tuner will be programmed directly + * by the digital demod via calc_regs. + */ + if (i2c_adap != NULL) { + u8 b[1]; + struct i2c_msg msg = { + .addr = i2c_addr, .flags = I2C_M_RD, + .buf = b, .len = 1, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (1 != i2c_transfer(i2c_adap, &msg, 1)) + tuner_warn("unable to probe %s, proceeding anyway.", + tuners[type].name); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } - memcpy(&fe->ops.tuner_ops, &simple_tuner_ops, sizeof(struct dvb_tuner_ops)); + mutex_lock(&tuner_simple_list_mutex); - tuner_info("type set to %d (%s)\n", cfg->type, cfg->tun->name); + instance = hybrid_tuner_request_state(struct tuner_simple_priv, priv, + hybrid_tuner_instance_list, + i2c_adap, i2c_addr, + "tuner-simple"); + switch (instance) { + case 0: + mutex_unlock(&tuner_simple_list_mutex); + return NULL; + break; + case 1: + fe->tuner_priv = priv; - strlcpy(fe->ops.tuner_ops.info.name, cfg->tun->name, sizeof(fe->ops.tuner_ops.info.name)); + priv->type = type; + priv->tun = &tuners[type]; + priv->nr = simple_devcount++; + break; + default: + fe->tuner_priv = priv; + break; + } - return fe; -} + mutex_unlock(&tuner_simple_list_mutex); + + memcpy(&fe->ops.tuner_ops, &simple_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + tuner_info("type set to %d (%s)\n", type, priv->tun->name); + + if ((debug) || ((atv_input[priv->nr] > 0) || + (dtv_input[priv->nr] > 0))) { + if (0 == atv_input[priv->nr]) + tuner_info("tuner %d atv rf input will be " + "autoselected\n", priv->nr); + else + tuner_info("tuner %d atv rf input will be " + "set to input %d (insmod option)\n", + priv->nr, atv_input[priv->nr]); + if (0 == dtv_input[priv->nr]) + tuner_info("tuner %d dtv rf input will be " + "autoselected\n", priv->nr); + else + tuner_info("tuner %d dtv rf input will be " + "set to input %d (insmod option)\n", + priv->nr, dtv_input[priv->nr]); + } + strlcpy(fe->ops.tuner_ops.info.name, priv->tun->name, + sizeof(fe->ops.tuner_ops.info.name)); + return fe; +} EXPORT_SYMBOL_GPL(simple_tuner_attach); MODULE_DESCRIPTION("Simple 4-control-bytes style tuner driver"); diff --git a/drivers/media/video/tuner-simple.h b/drivers/media/video/tuner-simple.h index 9089939a8c0..e46cf0121e0 100644 --- a/drivers/media/video/tuner-simple.h +++ b/drivers/media/video/tuner-simple.h @@ -20,25 +20,18 @@ #include <linux/i2c.h> #include "dvb_frontend.h" -struct simple_tuner_config -{ - /* chip type */ - unsigned int type; - struct tunertype *tun; -}; - #if defined(CONFIG_TUNER_SIMPLE) || (defined(CONFIG_TUNER_SIMPLE_MODULE) && defined(MODULE)) extern struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap, u8 i2c_addr, - struct simple_tuner_config *cfg); + unsigned int type); #else static inline struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c_adap, u8 i2c_addr, - struct simple_tuner_config *cfg) + unsigned int type) { - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return NULL; } #endif diff --git a/drivers/media/video/tuner-types.c b/drivers/media/video/tuner-types.c index 883047f9c28..10dddca8b5d 100644 --- a/drivers/media/video/tuner-types.c +++ b/drivers/media/video/tuner-types.c @@ -35,6 +35,27 @@ * based on the video standard in use. */ +/* The following was taken from dvb-pll.c: */ + +/* Set AGC TOP value to 103 dBuV: + * 0x80 = Control Byte + * 0x40 = 250 uA charge pump (irrelevant) + * 0x18 = Aux Byte to follow + * 0x06 = 64.5 kHz divider (irrelevant) + * 0x01 = Disable Vt (aka sleep) + * + * 0x00 = AGC Time constant 2s Iagc = 300 nA (vs 0x80 = 9 nA) + * 0x50 = AGC Take over point = 103 dBuV + */ +static u8 tua603x_agc103[] = { 2, 0x80|0x40|0x18|0x06|0x01, 0x00|0x50 }; + +/* 0x04 = 166.67 kHz divider + * + * 0x80 = AGC Time constant 50ms Iagc = 9 uA + * 0x20 = AGC Take over point = 112 dBuV + */ +static u8 tua603x_agc112[] = { 2, 0x80|0x40|0x18|0x04|0x01, 0x80|0x20 }; + /* 0-9 */ /* ------------ TUNER_TEMIC_PAL - TEMIC PAL ------------ */ @@ -594,19 +615,31 @@ static struct tuner_params tuner_philips_pal_mk_params[] = { }, }; -/* ---- TUNER_PHILIPS_ATSC - Philips FCV1236D (ATSC/NTSC) ---- */ +/* ---- TUNER_PHILIPS_FCV1236D - Philips FCV1236D (ATSC/NTSC) ---- */ -static struct tuner_range tuner_philips_fcv1236d_ranges[] = { - { 16 * 157.25 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 451.25 /*MHz*/, 0x8e, 0x90, }, +static struct tuner_range tuner_philips_fcv1236d_ntsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0x8e, 0xa2, }, + { 16 * 451.25 /*MHz*/, 0x8e, 0x92, }, + { 16 * 999.99 , 0x8e, 0x32, }, +}; + +static struct tuner_range tuner_philips_fcv1236d_atsc_ranges[] = { + { 16 * 159.00 /*MHz*/, 0x8e, 0xa0, }, + { 16 * 453.00 /*MHz*/, 0x8e, 0x90, }, { 16 * 999.99 , 0x8e, 0x30, }, }; static struct tuner_params tuner_philips_fcv1236d_params[] = { { .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_philips_fcv1236d_ranges, - .count = ARRAY_SIZE(tuner_philips_fcv1236d_ranges), + .ranges = tuner_philips_fcv1236d_ntsc_ranges, + .count = ARRAY_SIZE(tuner_philips_fcv1236d_ntsc_ranges), + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_philips_fcv1236d_atsc_ranges, + .count = ARRAY_SIZE(tuner_philips_fcv1236d_atsc_ranges), + .iffreq = 16 * 44.00, }, }; @@ -701,12 +734,24 @@ static struct tuner_range tuner_microtune_4042fi5_ntsc_ranges[] = { { 16 * 999.99 , 0x8e, 0x31, }, }; +static struct tuner_range tuner_microtune_4042fi5_atsc_ranges[] = { + { 16 * 162.00 /*MHz*/, 0x8e, 0xa1, }, + { 16 * 457.00 /*MHz*/, 0x8e, 0x91, }, + { 16 * 999.99 , 0x8e, 0x31, }, +}; + static struct tuner_params tuner_microtune_4042fi5_params[] = { { .type = TUNER_PARAM_TYPE_NTSC, .ranges = tuner_microtune_4042fi5_ntsc_ranges, .count = ARRAY_SIZE(tuner_microtune_4042fi5_ntsc_ranges), }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_microtune_4042fi5_atsc_ranges, + .count = ARRAY_SIZE(tuner_microtune_4042fi5_atsc_ranges), + .iffreq = 16 * 44.00 /*MHz*/, + }, }; /* 50-59 */ @@ -740,6 +785,7 @@ static struct tuner_params tuner_philips_fm1256_ih3_params[] = { /* ------------ TUNER_THOMSON_DTT7610 - THOMSON ATSC ------------ */ +/* single range used for both ntsc and atsc */ static struct tuner_range tuner_thomson_dtt7610_ntsc_ranges[] = { { 16 * 157.25 /*MHz*/, 0x8e, 0x39, }, { 16 * 454.00 /*MHz*/, 0x8e, 0x3a, }, @@ -752,6 +798,12 @@ static struct tuner_params tuner_thomson_dtt7610_params[] = { .ranges = tuner_thomson_dtt7610_ntsc_ranges, .count = ARRAY_SIZE(tuner_thomson_dtt7610_ntsc_ranges), }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_thomson_dtt7610_ntsc_ranges, + .count = ARRAY_SIZE(tuner_thomson_dtt7610_ntsc_ranges), + .iffreq = 16 * 44.00 /*MHz*/, + }, }; /* ------------ TUNER_PHILIPS_FQ1286 - Philips NTSC ------------ */ @@ -855,6 +907,11 @@ static struct tuner_range tuner_thomson_dtt761x_ntsc_ranges[] = { { 16 * 999.99 , 0x8e, 0x3c, }, }; +static struct tuner_range tuner_thomson_dtt761x_atsc_ranges[] = { + { 16 * 147.00 /*MHz*/, 0x8e, 0x39, }, + { 16 * 417.00 /*MHz*/, 0x8e, 0x3a, }, + { 16 * 999.99 , 0x8e, 0x3c, }, +}; static struct tuner_params tuner_thomson_dtt761x_params[] = { { @@ -865,6 +922,12 @@ static struct tuner_params tuner_thomson_dtt761x_params[] = { .fm_gain_normal = 1, .radio_if = 2, /* 41.3 MHz */ }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_thomson_dtt761x_atsc_ranges, + .count = ARRAY_SIZE(tuner_thomson_dtt761x_atsc_ranges), + .iffreq = 16 * 44.00, /*MHz*/ + }, }; /* ------------ TUNER_TENA_9533_DI - Philips PAL ------------ */ @@ -891,6 +954,15 @@ static struct tuner_range tuner_philips_fmd1216me_mk3_pal_ranges[] = { { 16 * 999.99 , 0x86, 0x54, }, }; +static struct tuner_range tuner_philips_fmd1216me_mk3_dvb_ranges[] = { + { 16 * 143.87 /*MHz*/, 0xbc, 0x41 }, + { 16 * 158.87 /*MHz*/, 0xf4, 0x41 }, + { 16 * 329.87 /*MHz*/, 0xbc, 0x42 }, + { 16 * 441.87 /*MHz*/, 0xf4, 0x42 }, + { 16 * 625.87 /*MHz*/, 0xbc, 0x44 }, + { 16 * 803.87 /*MHz*/, 0xf4, 0x44 }, + { 16 * 999.99 , 0xfc, 0x44 }, +}; static struct tuner_params tuner_philips_fmd1216me_mk3_params[] = { { @@ -904,6 +976,12 @@ static struct tuner_params tuner_philips_fmd1216me_mk3_params[] = { .port2_invert_for_secam_lc = 1, .port1_set_for_fm_mono = 1, }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_philips_fmd1216me_mk3_dvb_ranges, + .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_dvb_ranges), + .iffreq = 16 * 36.125, /*MHz*/ + }, }; @@ -915,6 +993,11 @@ static struct tuner_range tuner_tua6034_ntsc_ranges[] = { { 16 * 999.99 , 0x8e, 0x04 }, }; +static struct tuner_range tuner_tua6034_atsc_ranges[] = { + { 16 * 165.00 /*MHz*/, 0xce, 0x01 }, + { 16 * 450.00 /*MHz*/, 0xce, 0x02 }, + { 16 * 999.99 , 0xce, 0x04 }, +}; static struct tuner_params tuner_lg_tdvs_h06xf_params[] = { { @@ -922,6 +1005,12 @@ static struct tuner_params tuner_lg_tdvs_h06xf_params[] = { .ranges = tuner_tua6034_ntsc_ranges, .count = ARRAY_SIZE(tuner_tua6034_ntsc_ranges), }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_tua6034_atsc_ranges, + .count = ARRAY_SIZE(tuner_tua6034_atsc_ranges), + .iffreq = 16 * 44.00, + }, }; /* ------------ TUNER_YMEC_TVF66T5_B_DFF - Philips PAL ------------ */ @@ -974,12 +1063,30 @@ static struct tuner_range tuner_philips_td1316_pal_ranges[] = { { 16 * 999.99 , 0xc8, 0xa4, }, }; +static struct tuner_range tuner_philips_td1316_dvb_ranges[] = { + { 16 * 93.834 /*MHz*/, 0xca, 0x60, }, + { 16 * 123.834 /*MHz*/, 0xca, 0xa0, }, + { 16 * 163.834 /*MHz*/, 0xca, 0xc0, }, + { 16 * 253.834 /*MHz*/, 0xca, 0x60, }, + { 16 * 383.834 /*MHz*/, 0xca, 0xa0, }, + { 16 * 443.834 /*MHz*/, 0xca, 0xc0, }, + { 16 * 583.834 /*MHz*/, 0xca, 0x60, }, + { 16 * 793.834 /*MHz*/, 0xca, 0xa0, }, + { 16 * 999.999 , 0xca, 0xe0, }, +}; + static struct tuner_params tuner_philips_td1316_params[] = { { .type = TUNER_PARAM_TYPE_PAL, .ranges = tuner_philips_td1316_pal_ranges, .count = ARRAY_SIZE(tuner_philips_td1316_pal_ranges), }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_philips_td1316_dvb_ranges, + .count = ARRAY_SIZE(tuner_philips_td1316_dvb_ranges), + .iffreq = 16 * 36.166667 /*MHz*/, + }, }; /* ------------ TUNER_PHILIPS_TUV1236D - Philips ATSC ------------ */ @@ -990,6 +1097,11 @@ static struct tuner_range tuner_tuv1236d_ntsc_ranges[] = { { 16 * 999.99 , 0xce, 0x04, }, }; +static struct tuner_range tuner_tuv1236d_atsc_ranges[] = { + { 16 * 157.25 /*MHz*/, 0xc6, 0x41, }, + { 16 * 454.00 /*MHz*/, 0xc6, 0x42, }, + { 16 * 999.99 , 0xc6, 0x44, }, +}; static struct tuner_params tuner_tuv1236d_params[] = { { @@ -997,6 +1109,12 @@ static struct tuner_params tuner_tuv1236d_params[] = { .ranges = tuner_tuv1236d_ntsc_ranges, .count = ARRAY_SIZE(tuner_tuv1236d_ntsc_ranges), }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_tuv1236d_atsc_ranges, + .count = ARRAY_SIZE(tuner_tuv1236d_atsc_ranges), + .iffreq = 16 * 44.00, + }, }; /* ------------ TUNER_TNF_xxx5 - Texas Instruments--------- */ @@ -1050,17 +1168,30 @@ static struct tuner_params tuner_samsung_tcpn_2121p30a_params[] = { /* ------------ TUNER_THOMSON_FE6600 - DViCO Hybrid PAL ------------ */ -static struct tuner_range tuner_thomson_fe6600_ranges[] = { +static struct tuner_range tuner_thomson_fe6600_pal_ranges[] = { { 16 * 160.00 /*MHz*/, 0xfe, 0x11, }, { 16 * 442.00 /*MHz*/, 0xf6, 0x12, }, { 16 * 999.99 , 0xf6, 0x18, }, }; +static struct tuner_range tuner_thomson_fe6600_dvb_ranges[] = { + { 16 * 250.00 /*MHz*/, 0xb4, 0x12, }, + { 16 * 455.00 /*MHz*/, 0xfe, 0x11, }, + { 16 * 775.50 /*MHz*/, 0xbc, 0x18, }, + { 16 * 999.99 , 0xf4, 0x18, }, +}; + static struct tuner_params tuner_thomson_fe6600_params[] = { { .type = TUNER_PARAM_TYPE_PAL, - .ranges = tuner_thomson_fe6600_ranges, - .count = ARRAY_SIZE(tuner_thomson_fe6600_ranges), + .ranges = tuner_thomson_fe6600_pal_ranges, + .count = ARRAY_SIZE(tuner_thomson_fe6600_pal_ranges), + }, + { + .type = TUNER_PARAM_TYPE_DIGITAL, + .ranges = tuner_thomson_fe6600_dvb_ranges, + .count = ARRAY_SIZE(tuner_thomson_fe6600_dvb_ranges), + .iffreq = 16 * 36.125 /*MHz*/, }, }; @@ -1303,10 +1434,13 @@ struct tunertype tuners[] = { .params = tuner_philips_pal_mk_params, .count = ARRAY_SIZE(tuner_philips_pal_mk_params), }, - [TUNER_PHILIPS_ATSC] = { /* Philips ATSC */ + [TUNER_PHILIPS_FCV1236D] = { /* Philips ATSC */ .name = "Philips FCV1236D ATSC/NTSC dual in", .params = tuner_philips_fcv1236d_params, .count = ARRAY_SIZE(tuner_philips_fcv1236d_params), + .min = 16 * 53.00, + .max = 16 * 803.00, + .stepsize = 62500, }, [TUNER_PHILIPS_FM1236_MK3] = { /* Philips NTSC */ .name = "Philips NTSC MK3 (FM1236MK3 or FM1236/F)", @@ -1342,6 +1476,9 @@ struct tunertype tuners[] = { .name = "Microtune 4042 FI5 ATSC/NTSC dual in", .params = tuner_microtune_4042fi5_params, .count = ARRAY_SIZE(tuner_microtune_4042fi5_params), + .min = 16 * 57.00, + .max = 16 * 858.00, + .stepsize = 62500, }, /* 50-59 */ @@ -1359,6 +1496,9 @@ struct tunertype tuners[] = { .name = "Thomson DTT 7610 (ATSC/NTSC)", .params = tuner_thomson_dtt7610_params, .count = ARRAY_SIZE(tuner_thomson_dtt7610_params), + .min = 16 * 44.00, + .max = 16 * 958.00, + .stepsize = 62500, }, [TUNER_PHILIPS_FQ1286] = { /* Philips NTSC */ .name = "Philips FQ1286", @@ -1400,6 +1540,10 @@ struct tunertype tuners[] = { .name = "Thomson DTT 761X (ATSC/NTSC)", .params = tuner_thomson_dtt761x_params, .count = ARRAY_SIZE(tuner_thomson_dtt761x_params), + .min = 16 * 57.00, + .max = 16 * 863.00, + .stepsize = 62500, + .initdata = tua603x_agc103, }, [TUNER_TENA_9533_DI] = { /* Philips PAL */ .name = "Tena TNF9533-D/IF/TNF9533-B/DF", @@ -1414,11 +1558,20 @@ struct tunertype tuners[] = { .name = "Philips FMD1216ME MK3 Hybrid Tuner", .params = tuner_philips_fmd1216me_mk3_params, .count = ARRAY_SIZE(tuner_philips_fmd1216me_mk3_params), + .min = 16 * 50.87, + .max = 16 * 858.00, + .stepsize = 166667, + .initdata = tua603x_agc112, + .sleepdata = (u8[]){ 4, 0x9c, 0x60, 0x85, 0x54 }, }, [TUNER_LG_TDVS_H06XF] = { /* LGINNOTEK ATSC */ .name = "LG TDVS-H06xF", /* H061F, H062F & H064F */ .params = tuner_lg_tdvs_h06xf_params, .count = ARRAY_SIZE(tuner_lg_tdvs_h06xf_params), + .min = 16 * 54.00, + .max = 16 * 863.00, + .stepsize = 62500, + .initdata = tua603x_agc103, }, [TUNER_YMEC_TVF66T5_B_DFF] = { /* Philips PAL */ .name = "Ymec TVF66T5-B/DFF", @@ -1434,11 +1587,17 @@ struct tunertype tuners[] = { .name = "Philips TD1316 Hybrid Tuner", .params = tuner_philips_td1316_params, .count = ARRAY_SIZE(tuner_philips_td1316_params), + .min = 16 * 87.00, + .max = 16 * 895.00, + .stepsize = 166667, }, [TUNER_PHILIPS_TUV1236D] = { /* Philips ATSC */ .name = "Philips TUV1236D ATSC/NTSC dual in", .params = tuner_tuv1236d_params, .count = ARRAY_SIZE(tuner_tuv1236d_params), + .min = 16 * 54.00, + .max = 16 * 864.00, + .stepsize = 62500, }, [TUNER_TNF_5335MF] = { /* Tenna PAL/NTSC */ .name = "Tena TNF 5335 and similar models", @@ -1460,6 +1619,9 @@ struct tunertype tuners[] = { .name = "Thomson FE6600", .params = tuner_thomson_fe6600_params, .count = ARRAY_SIZE(tuner_thomson_fe6600_params), + .min = 16 * 44.25, + .max = 16 * 858.00, + .stepsize = 166667, }, [TUNER_SAMSUNG_TCPG_6121P30A] = { /* Samsung PAL */ .name = "Samsung TCPG 6121P30A", @@ -1480,5 +1642,11 @@ struct tunertype tuners[] = { /* see xc5000.c for details */ }, }; +EXPORT_SYMBOL(tuners); unsigned const int tuner_count = ARRAY_SIZE(tuners); +EXPORT_SYMBOL(tuner_count); + +MODULE_DESCRIPTION("Simple tuner device type database"); +MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tuner-xc2028-types.h b/drivers/media/video/tuner-xc2028-types.h index d0057fbf0ec..74dc46a71f6 100644 --- a/drivers/media/video/tuner-xc2028-types.h +++ b/drivers/media/video/tuner-xc2028-types.h @@ -1,6 +1,9 @@ /* tuner-xc2028_types * - * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) + * This file includes internal tipes to be used inside tuner-xc2028. + * Shouldn't be included outside tuner-xc2028 + * + * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) * This code is placed under the terms of the GNU General Public License v2 */ @@ -54,11 +57,13 @@ /* LCD firmwares exist only for MTS STD/MN (PAL or NTSC/M) and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr) There are variants both with and without NOGD + Those firmwares produce better result with LCD displays */ #define LCD (1<<12) /* NOGD firmwares exist only for MTS STD/MN (PAL or NTSC/M) and for non-MTS STD/MN (PAL, NTSC/M or NTSC/Kr) + The NOGD firmwares don't have group delay compensation filter */ #define NOGD (1<<13) @@ -85,11 +90,19 @@ /* This flag identifies that the scode table has a new format */ #define HAS_IF (1 << 30) -#define SCODE_TYPES (MTS|DTV6|QAM|DTV7|DTV78|DTV8|LCD|NOGD|MONO|ATSC|IF| \ - LG60|ATI638|OREN538|OREN36|TOYOTA388|TOYOTA794| \ - DIBCOM52|ZARLINK456|CHINA|F6MHZ|SCODE) +/* There are different scode tables for MTS and non-MTS. + The MTS firmwares support mono only + */ +#define SCODE_TYPES (SCODE | MTS) + -/* Newer types to be moved to videodev2.h */ +/* Newer types not defined on videodev2.h. + The original idea were to move all those types to videodev2.h, but + it seemed overkill, since, with the exception of SECAM/K3, the other + types seem to be autodetected. + It is not clear where secam/k3 is used, nor we have a feedback of this + working or being autodetected by the standard secam firmware. + */ #define V4L2_STD_SECAM_K3 (0x04000000) diff --git a/drivers/media/video/tuner-xc2028.c b/drivers/media/video/tuner-xc2028.c index 50cf876f020..cc3db7d79a0 100644 --- a/drivers/media/video/tuner-xc2028.c +++ b/drivers/media/video/tuner-xc2028.c @@ -1,6 +1,6 @@ /* tuner-xc2028 * - * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) + * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) * * Copyright (c) 2007 Michel Ludwig (michel.ludwig@gmail.com) * - frontend interface @@ -23,8 +23,6 @@ #include "dvb_frontend.h" -#define PREFIX "xc2028" - static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); @@ -43,6 +41,11 @@ MODULE_PARM_DESC(audio_std, "NICAM/A\n" "NICAM/B\n"); +static char firmware_name[FIRMWARE_NAME_MAX]; +module_param_string(firmware_name, firmware_name, sizeof(firmware_name), 0); +MODULE_PARM_DESC(firmware_name, "Firmware file name. Allows overriding the " + "default firmware name\n"); + static LIST_HEAD(xc2028_list); static DEFINE_MUTEX(xc2028_list_mutex); @@ -127,12 +130,12 @@ struct xc2028_data { _rc; \ }) -static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) +static int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) { unsigned char buf[2]; unsigned char ibuf[2]; - tuner_dbg("%s %04x called\n", __FUNCTION__, reg); + tuner_dbg("%s %04x called\n", __func__, reg); buf[0] = reg >> 8; buf[1] = (unsigned char) reg; @@ -145,7 +148,7 @@ static unsigned int xc2028_get_reg(struct xc2028_data *priv, u16 reg, u16 *val) } #define dump_firm_type(t) dump_firm_type_and_int_freq(t, 0) -void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq) +static void dump_firm_type_and_int_freq(unsigned int type, u16 int_freq) { if (type & BASE) printk("BASE "); @@ -232,6 +235,7 @@ static v4l2_std_id parse_audio_std_option(void) static void free_firmware(struct xc2028_data *priv) { int i; + tuner_dbg("%s called\n", __func__); if (!priv->firm) return; @@ -255,19 +259,24 @@ static int load_all_firmwares(struct dvb_frontend *fe) int rc = 0; int n, n_array; char name[33]; + char *fname; + + tuner_dbg("%s called\n", __func__); - tuner_dbg("%s called\n", __FUNCTION__); + if (!firmware_name[0]) + fname = priv->ctrl.fname; + else + fname = firmware_name; - tuner_dbg("Reading firmware %s\n", priv->ctrl.fname); - rc = request_firmware(&fw, priv->ctrl.fname, - &priv->i2c_props.adap->dev); + tuner_dbg("Reading firmware %s\n", fname); + rc = request_firmware(&fw, fname, &priv->i2c_props.adap->dev); if (rc < 0) { if (rc == -ENOENT) tuner_err("Error: firmware %s not found.\n", - priv->ctrl.fname); + fname); else tuner_err("Error %d while requesting firmware %s \n", - rc, priv->ctrl.fname); + rc, fname); return rc; } @@ -276,7 +285,7 @@ static int load_all_firmwares(struct dvb_frontend *fe) if (fw->size < sizeof(name) - 1 + 2 + 2) { tuner_err("Error: firmware file %s has invalid size!\n", - priv->ctrl.fname); + fname); goto corrupt; } @@ -291,7 +300,7 @@ static int load_all_firmwares(struct dvb_frontend *fe) p += 2; tuner_info("Loading %d firmware images from %s, type: %s, ver %d.%d\n", - n_array, priv->ctrl.fname, name, + n_array, fname, name, priv->firm_version >> 8, priv->firm_version & 0xff); priv->firm = kzalloc(sizeof(*priv->firm) * n_array, GFP_KERNEL); @@ -395,9 +404,9 @@ static int seek_firmware(struct dvb_frontend *fe, unsigned int type, { struct xc2028_data *priv = fe->tuner_priv; int i, best_i = -1, best_nr_matches = 0; - unsigned int ign_firm_type_mask = 0; + unsigned int type_mask = 0; - tuner_dbg("%s called, want type=", __FUNCTION__); + tuner_dbg("%s called, want type=", __func__); if (debug) { dump_firm_type(type); printk("(%x), id %016llx.\n", type, (unsigned long long)*id); @@ -412,18 +421,23 @@ static int seek_firmware(struct dvb_frontend *fe, unsigned int type, *id = V4L2_STD_PAL; if (type & BASE) - type &= BASE_TYPES; + type_mask = BASE_TYPES; else if (type & SCODE) { type &= SCODE_TYPES; - ign_firm_type_mask = HAS_IF; + type_mask = SCODE_TYPES & ~HAS_IF; } else if (type & DTV_TYPES) - type &= DTV_TYPES; + type_mask = DTV_TYPES; else if (type & STD_SPECIFIC_TYPES) - type &= STD_SPECIFIC_TYPES; + type_mask = STD_SPECIFIC_TYPES; + + type &= type_mask; + + if (!type & SCODE) + type_mask = ~0; /* Seek for exact match */ for (i = 0; i < priv->firm_size; i++) { - if ((type == (priv->firm[i].type & ~ign_firm_type_mask)) && + if ((type == (priv->firm[i].type & type_mask)) && (*id == priv->firm[i].id)) goto found; } @@ -433,7 +447,7 @@ static int seek_firmware(struct dvb_frontend *fe, unsigned int type, v4l2_std_id match_mask; int nr_matches; - if (type != (priv->firm[i].type & ~ign_firm_type_mask)) + if (type != (priv->firm[i].type & type_mask)) continue; match_mask = *id & priv->firm[i].id; @@ -483,7 +497,7 @@ static int load_firmware(struct dvb_frontend *fe, unsigned int type, int pos, rc; unsigned char *p, *endp, buf[priv->ctrl.max_len]; - tuner_dbg("%s called\n", __FUNCTION__); + tuner_dbg("%s called\n", __func__); pos = seek_firmware(fe, type, id); if (pos < 0) @@ -586,7 +600,7 @@ static int load_scode(struct dvb_frontend *fe, unsigned int type, int pos, rc; unsigned char *p; - tuner_dbg("%s called\n", __FUNCTION__); + tuner_dbg("%s called\n", __func__); if (!int_freq) { pos = seek_firmware(fe, type, id); @@ -650,7 +664,7 @@ static int check_firmware(struct dvb_frontend *fe, unsigned int type, u16 version, hwmodel; v4l2_std_id std0; - tuner_dbg("%s called\n", __FUNCTION__); + tuner_dbg("%s called\n", __func__); if (!priv->firm) { if (!priv->ctrl.fname) { @@ -770,10 +784,10 @@ check_device: goto fail; } - tuner_info("Device is Xceive %d version %d.%d, " - "firmware version %d.%d\n", - hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, - (version & 0xf0) >> 4, version & 0xf); + tuner_dbg("Device is Xceive %d version %d.%d, " + "firmware version %d.%d\n", + hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, + (version & 0xf0) >> 4, version & 0xf); /* Check firmware version against what we downloaded. */ if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) { @@ -824,27 +838,34 @@ static int xc2028_signal(struct dvb_frontend *fe, u16 *strength) u16 frq_lock, signal = 0; int rc; - tuner_dbg("%s called\n", __FUNCTION__); + tuner_dbg("%s called\n", __func__); mutex_lock(&priv->lock); /* Sync Lock Indicator */ rc = xc2028_get_reg(priv, 0x0002, &frq_lock); - if (rc < 0 || frq_lock == 0) + if (rc < 0) goto ret; - /* Frequency is locked. Return signal quality */ + /* Frequency is locked */ + if (frq_lock == 1) + signal = 32768; /* Get SNR of the video signal */ rc = xc2028_get_reg(priv, 0x0040, &signal); if (rc < 0) - signal = -frq_lock; + goto ret; + + /* Use both frq_lock and signal to generate the result */ + signal = signal || ((signal & 0x07) << 12); ret: mutex_unlock(&priv->lock); *strength = signal; + tuner_dbg("signal strength is %d\n", signal); + return rc; } @@ -861,7 +882,7 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, unsigned char buf[4]; u32 div, offset = 0; - tuner_dbg("%s called\n", __FUNCTION__); + tuner_dbg("%s called\n", __func__); mutex_lock(&priv->lock); @@ -906,9 +927,11 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, if (rc < 0) goto ret; - rc = priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1); - if (rc < 0) - goto ret; + /* Return code shouldn't be checked. + The reset CLK is needed only with tm6000. + Driver should work fine even if this fails. + */ + priv->tuner_callback(priv->video_dev, XC2028_RESET_CLK, 1); msleep(10); @@ -942,7 +965,7 @@ static int xc2028_set_analog_freq(struct dvb_frontend *fe, struct xc2028_data *priv = fe->tuner_priv; unsigned int type=0; - tuner_dbg("%s called\n", __FUNCTION__); + tuner_dbg("%s called\n", __func__); if (p->mode == V4L2_TUNER_RADIO) { type |= FM; @@ -975,7 +998,7 @@ static int xc2028_set_params(struct dvb_frontend *fe, fe_bandwidth_t bw = BANDWIDTH_8_MHZ; u16 demod = 0; - tuner_dbg("%s called\n", __FUNCTION__); + tuner_dbg("%s called\n", __func__); if (priv->ctrl.d2633) type |= D2633; @@ -1040,33 +1063,12 @@ static int xc2028_set_params(struct dvb_frontend *fe, T_DIGITAL_TV, type, 0, demod); } -static int xc2028_sleep(struct dvb_frontend *fe) -{ - struct xc2028_data *priv = fe->tuner_priv; - int rc = 0; - - tuner_dbg("%s called\n", __FUNCTION__); - - mutex_lock(&priv->lock); - - if (priv->firm_version < 0x0202) - rc = send_seq(priv, {0x00, 0x08, 0x00, 0x00}); - else - rc = send_seq(priv, {0x80, 0x08, 0x00, 0x00}); - - priv->cur_fw.type = 0; /* need firmware reload */ - - mutex_unlock(&priv->lock); - - return rc; -} - static int xc2028_dvb_release(struct dvb_frontend *fe) { struct xc2028_data *priv = fe->tuner_priv; - tuner_dbg("%s called\n", __FUNCTION__); + tuner_dbg("%s called\n", __func__); mutex_lock(&xc2028_list_mutex); @@ -1091,7 +1093,7 @@ static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency) { struct xc2028_data *priv = fe->tuner_priv; - tuner_dbg("%s called\n", __FUNCTION__); + tuner_dbg("%s called\n", __func__); *frequency = priv->frequency; @@ -1104,25 +1106,25 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) struct xc2028_ctrl *p = priv_cfg; int rc = 0; - tuner_dbg("%s called\n", __FUNCTION__); + tuner_dbg("%s called\n", __func__); mutex_lock(&priv->lock); - kfree(priv->ctrl.fname); - free_firmware(priv); - memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); - priv->ctrl.fname = NULL; + if (priv->ctrl.max_len < 9) + priv->ctrl.max_len = 13; if (p->fname) { + if (priv->ctrl.fname && strcmp(p->fname, priv->ctrl.fname)) { + kfree(priv->ctrl.fname); + free_firmware(priv); + } + priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); if (priv->ctrl.fname == NULL) rc = -ENOMEM; } - if (priv->ctrl.max_len < 9) - priv->ctrl.max_len = 13; - mutex_unlock(&priv->lock); return rc; @@ -1142,8 +1144,6 @@ static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = { .get_frequency = xc2028_get_frequency, .get_rf_strength = xc2028_signal, .set_params = xc2028_set_params, - .sleep = xc2028_sleep, - }; struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, @@ -1153,23 +1153,29 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, void *video_dev; if (debug) - printk(KERN_DEBUG PREFIX ": Xcv2028/3028 init called!\n"); + printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n"); - if (NULL == cfg || NULL == cfg->video_dev) + if (NULL == cfg) return NULL; if (!fe) { - printk(KERN_ERR PREFIX ": No frontend!\n"); + printk(KERN_ERR "xc2028: No frontend!\n"); return NULL; } - video_dev = cfg->video_dev; + video_dev = cfg->i2c_adap->algo_data; + + if (debug) + printk(KERN_DEBUG "xc2028: video_dev =%p\n", video_dev); mutex_lock(&xc2028_list_mutex); list_for_each_entry(priv, &xc2028_list, xc2028_list) { - if (priv->video_dev == cfg->video_dev) { + if (&priv->i2c_props.adap->dev == &cfg->i2c_adap->dev) { video_dev = NULL; + if (debug) + printk(KERN_DEBUG "xc2028: reusing device\n"); + break; } } @@ -1183,6 +1189,8 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, priv->i2c_props.addr = cfg->i2c_addr; priv->i2c_props.adap = cfg->i2c_adap; + priv->i2c_props.name = "xc2028"; + priv->video_dev = video_dev; priv->tuner_callback = cfg->callback; priv->ctrl.max_len = 13; @@ -1195,6 +1203,9 @@ struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, fe->tuner_priv = priv; priv->count++; + if (debug) + printk(KERN_DEBUG "xc2028: usage count is %i\n", priv->count); + memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops, sizeof(xc2028_dvb_tuner_ops)); diff --git a/drivers/media/video/tuner-xc2028.h b/drivers/media/video/tuner-xc2028.h index 3eb8420379a..fc2f132a554 100644 --- a/drivers/media/video/tuner-xc2028.h +++ b/drivers/media/video/tuner-xc2028.h @@ -1,6 +1,6 @@ /* tuner-xc2028 * - * Copyright (c) 2007 Mauro Carvalho Chehab (mchehab@infradead.org) + * Copyright (c) 2007-2008 Mauro Carvalho Chehab (mchehab@infradead.org) * This code is placed under the terms of the GNU General Public License v2 */ @@ -12,7 +12,7 @@ #define XC2028_DEFAULT_FIRMWARE "xc3028-v27.fw" /* Dmoduler IF (kHz) */ -#define XC3028_FE_DEFAULT 0 +#define XC3028_FE_DEFAULT 0 /* Don't load SCODE */ #define XC3028_FE_LG60 6000 #define XC3028_FE_ATI638 6380 #define XC3028_FE_OREN538 5380 @@ -55,7 +55,7 @@ static inline struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, struct xc2028_config *cfg) { printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", - __FUNCTION__); + __func__); return NULL; } #endif diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 01ebcec040c..f29a2cd0f2f 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -38,7 +38,7 @@ /* ---------------------------------------------------------------------- */ /* insmod args */ -static int debug = 0; /* insmod parameter */ +static int debug; /* insmod parameter */ module_param(debug, int, 0644); MODULE_DESCRIPTION("device driver for various i2c TV sound decoder / audiomux chips"); @@ -1235,11 +1235,11 @@ static int tda9850 = 1; static int tda9855 = 1; static int tda9873 = 1; static int tda9874a = 1; -static int tea6300 = 0; /* address clash with msp34xx */ -static int tea6320 = 0; /* address clash with msp34xx */ +static int tea6300; /* default 0 - address clash with msp34xx */ +static int tea6320; /* default 0 - address clash with msp34xx */ static int tea6420 = 1; static int pic16c54 = 1; -static int ta8874z = 0; /* address clash with tda9840 */ +static int ta8874z; /* default 0 - address clash with tda9840 */ module_param(tda8425, int, 0444); module_param(tda9840, int, 0444); diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index dc0da44a5af..3cf8a8e801e 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -745,109 +745,6 @@ int tveeprom_read(struct i2c_client *c, unsigned char *eedata, int len) } EXPORT_SYMBOL(tveeprom_read); -/* ----------------------------------------------------------------------- */ -/* needed for ivtv.sf.net at the moment. Should go away in the long */ -/* run, just call the exported tveeprom_* directly, there is no point in */ -/* using the indirect way via i2c_driver->command() */ - -static unsigned short normal_i2c[] = { - 0xa0 >> 1, - I2C_CLIENT_END, -}; - -I2C_CLIENT_INSMOD; - -static struct i2c_driver i2c_driver_tveeprom; - -static int -tveeprom_command(struct i2c_client *client, - unsigned int cmd, - void *arg) -{ - struct tveeprom eeprom; - u32 *eeprom_props = arg; - u8 *buf; - - switch (cmd) { - case 0: - buf = kzalloc(256, GFP_KERNEL); - tveeprom_read(client, buf, 256); - tveeprom_hauppauge_analog(client, &eeprom, buf); - kfree(buf); - eeprom_props[0] = eeprom.tuner_type; - eeprom_props[1] = eeprom.tuner_formats; - eeprom_props[2] = eeprom.model; - eeprom_props[3] = eeprom.revision; - eeprom_props[4] = eeprom.has_radio; - break; - default: - return -EINVAL; - } - return 0; -} - -static int -tveeprom_detect_client(struct i2c_adapter *adapter, - int address, - int kind) -{ - struct i2c_client *client; - - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (NULL == client) - return -ENOMEM; - client->addr = address; - client->adapter = adapter; - client->driver = &i2c_driver_tveeprom; - snprintf(client->name, sizeof(client->name), "tveeprom"); - i2c_attach_client(client); - - return 0; -} - -static int -tveeprom_attach_adapter(struct i2c_adapter *adapter) -{ - if (adapter->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adapter, &addr_data, tveeprom_detect_client); - return 0; -} - -static int -tveeprom_detach_client(struct i2c_client *client) -{ - int err; - - err = i2c_detach_client(client); - if (err < 0) - return err; - kfree(client); - return 0; -} - -static struct i2c_driver i2c_driver_tveeprom = { - .driver = { - .name = "tveeprom", - }, - .id = I2C_DRIVERID_TVEEPROM, - .attach_adapter = tveeprom_attach_adapter, - .detach_client = tveeprom_detach_client, - .command = tveeprom_command, -}; - -static int __init tveeprom_init(void) -{ - return i2c_add_driver(&i2c_driver_tveeprom); -} - -static void __exit tveeprom_exit(void) -{ - i2c_del_driver(&i2c_driver_tveeprom); -} - -module_init(tveeprom_init); -module_exit(tveeprom_exit); - /* * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index b6e24e714a2..6a3af1005f0 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -27,7 +27,7 @@ static unsigned short normal_i2c[] = { I2C_CLIENT_INSMOD; -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); diff --git a/drivers/media/video/usbvideo/ibmcam.c b/drivers/media/video/usbvideo/ibmcam.c index 14db95e10cf..59166b76010 100644 --- a/drivers/media/video/usbvideo/ibmcam.c +++ b/drivers/media/video/usbvideo/ibmcam.c @@ -121,7 +121,7 @@ static int init_model2_yb = -1; /* 01.01.08 - Added for RCA video in support -LO */ /* Settings for camera model 3 */ -static int init_model3_input = 0; +static int init_model3_input; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); @@ -802,6 +802,21 @@ static enum ParseState ibmcam_model2_320x240_parse_lines( return scan_Continue; } +/* + * ibmcam_model3_parse_lines() + * + * | Even lines | Odd Lines | + * -----------------------------------| + * |YYY........Y|UYVYUYVY.........UYVY| + * |YYY........Y|UYVYUYVY.........UYVY| + * |............|.....................| + * |YYY........Y|UYVYUYVY.........UYVY| + * |------------+---------------------| + * + * There is one (U, V) chroma pair for every four luma (Y) values. This + * function reads a pair of lines at a time and obtains missing chroma values + * from adjacent pixels. + */ static enum ParseState ibmcam_model3_parse_lines( struct uvd *uvd, struct usbvideo_frame *frame, @@ -816,6 +831,7 @@ static enum ParseState ibmcam_model3_parse_lines( const int ccm = 128; /* Color correction median - see below */ int i, u, v, rw, data_w=0, data_h=0, color_corr; static unsigned char lineBuffer[640*3]; + int line; color_corr = (uvd->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1); @@ -869,15 +885,15 @@ static enum ParseState ibmcam_model3_parse_lines( return scan_NextFrame; } - /* Make sure there's enough data for the entire line */ - len = 3 * data_w; /* <y-data> <uv-data> */ + /* Make sure that lineBuffer can store two lines of data */ + len = 3 * data_w; /* <y-data> <uyvy-data> */ assert(len <= sizeof(lineBuffer)); - /* Make sure there's enough data for the entire line */ + /* Make sure there's enough data for two lines */ if (RingQueue_GetLength(&uvd->dp) < len) return scan_Out; - /* Suck one line out of the ring queue */ + /* Suck two lines of data out of the ring queue */ RingQueue_Dequeue(&uvd->dp, lineBuffer, len); data = lineBuffer; @@ -887,15 +903,23 @@ static enum ParseState ibmcam_model3_parse_lines( rw = (int)VIDEOSIZE_Y(frame->request) - (int)(frame->curline) - 1; RESTRICT_TO_RANGE(rw, 0, VIDEOSIZE_Y(frame->request)-1); - for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { - int y, rv, gv, bv; /* RGB components */ + /* Iterate over two lines. */ + for (line = 0; line < 2; line++) { + for (i = 0; i < VIDEOSIZE_X(frame->request); i++) { + int y; + int rv, gv, bv; /* RGB components */ - if (i < data_w) { - y = data[i]; /* Luminosity is the first line */ + if (i >= data_w) { + RGB24_PUTPIXEL(frame, i, rw, 0, 0, 0); + continue; + } + + /* first line is YYY...Y; second is UYVY...UYVY */ + y = data[(line == 0) ? i : (i*2 + 1)]; /* Apply static color correction */ - u = color[i*2] + hue_corr; - v = color[i*2 + 1] + hue2_corr; + u = color[(i/2)*4] + hue_corr; + v = color[(i/2)*4 + 2] + hue2_corr; /* Apply color correction */ if (color_corr != 0) { @@ -903,13 +927,21 @@ static enum ParseState ibmcam_model3_parse_lines( u = 128 + ((ccm + color_corr) * (u - 128)) / ccm; v = 128 + ((ccm + color_corr) * (v - 128)) / ccm; } - } else - y = 0, u = v = 128; - YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv); - RGB24_PUTPIXEL(frame, i, rw, rv, gv, bv); /* Done by deinterlacing now */ + + YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv); + RGB24_PUTPIXEL(frame, i, rw, rv, gv, bv); /* No deinterlacing */ + } + + /* Check for the end of requested data */ + if (rw == 0) + break; + + /* Prepare for the second line */ + rw--; + data = lineBuffer + data_w; } - frame->deinterlace = Deinterlace_FillEvenLines; + frame->deinterlace = Deinterlace_None; /* * Account for number of bytes that we wrote into output V4L frame. diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c index 719b17ce83f..1c180284ec6 100644 --- a/drivers/media/video/usbvideo/konicawc.c +++ b/drivers/media/video/usbvideo/konicawc.c @@ -57,11 +57,11 @@ static struct usbvideo *cams; static int debug; #define DEBUG(n, format, arg...) \ if (n <= debug) { \ - printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \ + printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __func__ , ## arg); \ } #else #define DEBUG(n, arg...) -static const int debug = 0; +static const int debug; #endif diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index a2acba0bcc4..32e536edf09 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -46,11 +46,11 @@ static int debug; #define DEBUG(n, format, arg...) \ if (n <= debug) { \ - printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \ + printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __func__ , ## arg); \ } #else #define DEBUG(n, arg...) -static const int debug = 0; +static const int debug; #endif #define DRIVER_VERSION "v0.01" diff --git a/drivers/media/video/usbvideo/ultracam.c b/drivers/media/video/usbvideo/ultracam.c index 95453c108d4..9544e644bf0 100644 --- a/drivers/media/video/usbvideo/ultracam.c +++ b/drivers/media/video/usbvideo/ultracam.c @@ -28,9 +28,9 @@ typedef struct { static struct usbvideo *cams = NULL; -static int debug = 0; +static int debug; -static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */ +static int flags; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */ static const int min_canvasWidth = 8; static const int min_canvasHeight = 4; diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c index 5d363be7bc7..4128ee20b64 100644 --- a/drivers/media/video/usbvideo/usbvideo.c +++ b/drivers/media/video/usbvideo/usbvideo.c @@ -522,14 +522,14 @@ void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode) struct usbvideo_frame *frame; int num_cell = 0; int scan_length = 0; - static int num_pass = 0; + static int num_pass; if (uvd == NULL) { - err("%s: uvd == NULL", __FUNCTION__); + err("%s: uvd == NULL", __func__); return; } if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { - err("%s: uvd->curframe=%d.", __FUNCTION__, uvd->curframe); + err("%s: uvd->curframe=%d.", __func__, uvd->curframe); return; } @@ -630,15 +630,15 @@ EXPORT_SYMBOL(usbvideo_HexDump); static int usbvideo_ClientIncModCount(struct uvd *uvd) { if (uvd == NULL) { - err("%s: uvd == NULL", __FUNCTION__); + err("%s: uvd == NULL", __func__); return -EINVAL; } if (uvd->handle == NULL) { - err("%s: uvd->handle == NULL", __FUNCTION__); + err("%s: uvd->handle == NULL", __func__); return -EINVAL; } if (!try_module_get(uvd->handle->md_module)) { - err("%s: try_module_get() == 0", __FUNCTION__); + err("%s: try_module_get() == 0", __func__); return -ENODEV; } return 0; @@ -647,15 +647,15 @@ static int usbvideo_ClientIncModCount(struct uvd *uvd) static void usbvideo_ClientDecModCount(struct uvd *uvd) { if (uvd == NULL) { - err("%s: uvd == NULL", __FUNCTION__); + err("%s: uvd == NULL", __func__); return; } if (uvd->handle == NULL) { - err("%s: uvd->handle == NULL", __FUNCTION__); + err("%s: uvd->handle == NULL", __func__); return; } if (uvd->handle->md_module == NULL) { - err("%s: uvd->handle->md_module == NULL", __FUNCTION__); + err("%s: uvd->handle->md_module == NULL", __func__); return; } module_put(uvd->handle->md_module); @@ -675,13 +675,13 @@ int usbvideo_register( /* Check parameters for sanity */ if ((num_cams <= 0) || (pCams == NULL) || (cbTbl == NULL)) { - err("%s: Illegal call", __FUNCTION__); + err("%s: Illegal call", __func__); return -EINVAL; } /* Check registration callback - must be set! */ if (cbTbl->probe == NULL) { - err("%s: probe() is required!", __FUNCTION__); + err("%s: probe() is required!", __func__); return -EINVAL; } @@ -692,7 +692,7 @@ int usbvideo_register( return -ENOMEM; } dbg("%s: Allocated $%p (%d. bytes) for %d. cameras", - __FUNCTION__, cams, base_size, num_cams); + __func__, cams, base_size, num_cams); /* Copy callbacks, apply defaults for those that are not set */ memmove(&cams->cb, cbTbl, sizeof(cams->cb)); @@ -721,7 +721,7 @@ int usbvideo_register( up->user_data = kmalloc(up->user_size, GFP_KERNEL); if (up->user_data == NULL) { err("%s: Failed to allocate user_data (%d. bytes)", - __FUNCTION__, up->user_size); + __func__, up->user_size); while (i) { up = &cams->cam[--i]; kfree(up->user_data); @@ -730,7 +730,7 @@ int usbvideo_register( return -ENOMEM; } dbg("%s: Allocated cams[%d].user_data=$%p (%d. bytes)", - __FUNCTION__, i, up->user_data, up->user_size); + __func__, i, up->user_data, up->user_size); } } @@ -776,19 +776,19 @@ void usbvideo_Deregister(struct usbvideo **pCams) int i; if (pCams == NULL) { - err("%s: pCams == NULL", __FUNCTION__); + err("%s: pCams == NULL", __func__); return; } cams = *pCams; if (cams == NULL) { - err("%s: cams == NULL", __FUNCTION__); + err("%s: cams == NULL", __func__); return; } - dbg("%s: Deregistering %s driver.", __FUNCTION__, cams->drvName); + dbg("%s: Deregistering %s driver.", __func__, cams->drvName); usb_deregister(&cams->usbdrv); - dbg("%s: Deallocating cams=$%p (%d. cameras)", __FUNCTION__, cams, cams->num_cameras); + dbg("%s: Deallocating cams=$%p (%d. cameras)", __func__, cams, cams->num_cameras); for (i=0; i < cams->num_cameras; i++) { struct uvd *up = &cams->cam[i]; int warning = 0; @@ -802,16 +802,16 @@ void usbvideo_Deregister(struct usbvideo **pCams) } if (warning) { err("%s: Warning: user_data=$%p user_size=%d.", - __FUNCTION__, up->user_data, up->user_size); + __func__, up->user_data, up->user_size); } else { dbg("%s: Freeing %d. $%p->user_data=$%p", - __FUNCTION__, i, up, up->user_data); + __func__, i, up, up->user_data); kfree(up->user_data); } } /* Whole array was allocated in one chunk */ dbg("%s: Freed %d uvd structures", - __FUNCTION__, cams->num_cameras); + __func__, cams->num_cameras); kfree(cams); *pCams = NULL; } @@ -846,7 +846,7 @@ static void usbvideo_Disconnect(struct usb_interface *intf) int i; if (uvd == NULL) { - err("%s($%p): Illegal call.", __FUNCTION__, intf); + err("%s($%p): Illegal call.", __func__, intf); return; } @@ -854,7 +854,7 @@ static void usbvideo_Disconnect(struct usb_interface *intf) usbvideo_ClientIncModCount(uvd); if (uvd->debug > 0) - info("%s(%p.)", __FUNCTION__, intf); + info("%s(%p.)", __func__, intf); mutex_lock(&uvd->lock); uvd->remove_pending = 1; /* Now all ISO data will be ignored */ @@ -870,10 +870,10 @@ static void usbvideo_Disconnect(struct usb_interface *intf) video_unregister_device(&uvd->vdev); if (uvd->debug > 0) - info("%s: Video unregistered.", __FUNCTION__); + info("%s: Video unregistered.", __func__); if (uvd->user) - info("%s: In use, disconnect pending.", __FUNCTION__); + info("%s: In use, disconnect pending.", __func__); else usbvideo_CameraRelease(uvd); mutex_unlock(&uvd->lock); @@ -895,7 +895,7 @@ static void usbvideo_Disconnect(struct usb_interface *intf) static void usbvideo_CameraRelease(struct uvd *uvd) { if (uvd == NULL) { - err("%s: Illegal call", __FUNCTION__); + err("%s: Illegal call", __func__); return; } @@ -946,7 +946,9 @@ static const struct file_operations usbvideo_fops = { .read = usbvideo_v4l_read, .mmap = usbvideo_v4l_mmap, .ioctl = usbvideo_v4l_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, }; static const struct video_device usbvideo_template = { @@ -1011,18 +1013,18 @@ int usbvideo_RegisterVideoDevice(struct uvd *uvd) char tmp1[20], tmp2[20]; /* Buffers for printing */ if (uvd == NULL) { - err("%s: Illegal call.", __FUNCTION__); + err("%s: Illegal call.", __func__); return -EINVAL; } if (uvd->video_endp == 0) { - info("%s: No video endpoint specified; data pump disabled.", __FUNCTION__); + info("%s: No video endpoint specified; data pump disabled.", __func__); } if (uvd->paletteBits == 0) { - err("%s: No palettes specified!", __FUNCTION__); + err("%s: No palettes specified!", __func__); return -EINVAL; } if (uvd->defaultPalette == 0) { - info("%s: No default palette!", __FUNCTION__); + info("%s: No default palette!", __func__); } uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) * @@ -1032,19 +1034,19 @@ int usbvideo_RegisterVideoDevice(struct uvd *uvd) if (uvd->debug > 0) { info("%s: iface=%d. endpoint=$%02x paletteBits=$%08lx", - __FUNCTION__, uvd->iface, uvd->video_endp, uvd->paletteBits); + __func__, uvd->iface, uvd->video_endp, uvd->paletteBits); } if (uvd->dev == NULL) { - err("%s: uvd->dev == NULL", __FUNCTION__); + err("%s: uvd->dev == NULL", __func__); return -EINVAL; } - uvd->vdev.dev=&(uvd->dev->dev); + uvd->vdev.dev = &uvd->dev->dev; if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { - err("%s: video_register_device failed", __FUNCTION__); + err("%s: video_register_device failed", __func__); return -EPIPE; } if (uvd->debug > 1) { - info("%s: video_register_device() successful", __FUNCTION__); + info("%s: video_register_device() successful", __func__); } info("%s on /dev/video%d: canvas=%s videosize=%s", @@ -1111,14 +1113,14 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file) int i, errCode = 0; if (uvd->debug > 1) - info("%s($%p)", __FUNCTION__, dev); + info("%s($%p)", __func__, dev); if (0 < usbvideo_ClientIncModCount(uvd)) return -ENODEV; mutex_lock(&uvd->lock); if (uvd->user) { - err("%s: Someone tried to open an already opened device!", __FUNCTION__); + err("%s: Someone tried to open an already opened device!", __func__); errCode = -EBUSY; } else { /* Clear statistics */ @@ -1134,7 +1136,7 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file) RingQueue_Allocate(&uvd->dp, RING_QUEUE_SIZE); if ((uvd->fbuf == NULL) || (!RingQueue_IsAllocated(&uvd->dp))) { - err("%s: Failed to allocate fbuf or dp", __FUNCTION__); + err("%s: Failed to allocate fbuf or dp", __func__); errCode = -ENOMEM; } else { /* Allocate all buffers */ @@ -1178,19 +1180,19 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file) if (errCode == 0) { if (VALID_CALLBACK(uvd, setupOnOpen)) { if (uvd->debug > 1) - info("%s: setupOnOpen callback", __FUNCTION__); + info("%s: setupOnOpen callback", __func__); errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd); if (errCode < 0) { err("%s: setupOnOpen callback failed (%d.).", - __FUNCTION__, errCode); + __func__, errCode); } else if (uvd->debug > 1) { - info("%s: setupOnOpen callback successful", __FUNCTION__); + info("%s: setupOnOpen callback successful", __func__); } } if (errCode == 0) { uvd->settingsAdjusted = 0; if (uvd->debug > 1) - info("%s: Open succeeded.", __FUNCTION__); + info("%s: Open succeeded.", __func__); uvd->user++; file->private_data = uvd; } @@ -1200,7 +1202,7 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file) if (errCode != 0) usbvideo_ClientDecModCount(uvd); if (uvd->debug > 0) - info("%s: Returning %d.", __FUNCTION__, errCode); + info("%s: Returning %d.", __func__, errCode); return errCode; } @@ -1223,7 +1225,7 @@ static int usbvideo_v4l_close(struct inode *inode, struct file *file) int i; if (uvd->debug > 1) - info("%s($%p)", __FUNCTION__, dev); + info("%s($%p)", __func__, dev); mutex_lock(&uvd->lock); GET_CALLBACK(uvd, stopDataPump)(uvd); @@ -1250,7 +1252,7 @@ static int usbvideo_v4l_close(struct inode *inode, struct file *file) usbvideo_ClientDecModCount(uvd); if (uvd->debug > 1) - info("%s: Completed.", __FUNCTION__); + info("%s: Completed.", __func__); file->private_data = NULL; return 0; } @@ -1504,7 +1506,7 @@ static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, return -EFAULT; if (uvd->debug >= 1) - info("%s: %Zd. bytes, noblock=%d.", __FUNCTION__, count, noblock); + info("%s: %Zd. bytes, noblock=%d.", __func__, count, noblock); mutex_lock(&uvd->lock); @@ -1551,7 +1553,7 @@ static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, */ if (frmx == -1) { if (uvd->defaultPalette == 0) { - err("%s: No default palette; don't know what to do!", __FUNCTION__); + err("%s: No default palette; don't know what to do!", __func__); count = -EFAULT; goto read_done; } @@ -1623,7 +1625,7 @@ static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, frame->seqRead_Index += count; if (uvd->debug >= 1) { err("%s: {copy} count used=%Zd, new seqRead_Index=%ld", - __FUNCTION__, count, frame->seqRead_Index); + __func__, count, frame->seqRead_Index); } /* Finally check if the frame is done with and "release" it */ @@ -1634,7 +1636,7 @@ static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, /* Mark it as available to be used again. */ uvd->frame[frmx].frameState = FrameState_Unused; if (usbvideo_NewFrame(uvd, (frmx + 1) % USBVIDEO_NUMFRAMES)) { - err("%s: usbvideo_NewFrame failed.", __FUNCTION__); + err("%s: usbvideo_NewFrame failed.", __func__); } } read_done: @@ -1741,10 +1743,10 @@ static int usbvideo_StartDataPump(struct uvd *uvd) int i, errFlag; if (uvd->debug > 1) - info("%s($%p)", __FUNCTION__, uvd); + info("%s($%p)", __func__, uvd); if (!CAMERA_IS_OPERATIONAL(uvd)) { - err("%s: Camera is not operational", __FUNCTION__); + err("%s: Camera is not operational", __func__); return -EFAULT; } uvd->curframe = -1; @@ -1752,14 +1754,14 @@ static int usbvideo_StartDataPump(struct uvd *uvd) /* Alternate interface 1 is is the biggest frame size */ i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); if (i < 0) { - err("%s: usb_set_interface error", __FUNCTION__); + err("%s: usb_set_interface error", __func__); uvd->last_error = i; return -EBUSY; } if (VALID_CALLBACK(uvd, videoStart)) GET_CALLBACK(uvd, videoStart)(uvd); else - err("%s: videoStart not set", __FUNCTION__); + err("%s: videoStart not set", __func__); /* We double buffer the Iso lists */ for (i=0; i < USBVIDEO_NUMSBUF; i++) { @@ -1784,12 +1786,12 @@ static int usbvideo_StartDataPump(struct uvd *uvd) for (i=0; i < USBVIDEO_NUMSBUF; i++) { errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); if (errFlag) - err("%s: usb_submit_isoc(%d) ret %d", __FUNCTION__, i, errFlag); + err("%s: usb_submit_isoc(%d) ret %d", __func__, i, errFlag); } uvd->streaming = 1; if (uvd->debug > 1) - info("%s: streaming=1 video_endp=$%02x", __FUNCTION__, uvd->video_endp); + info("%s: streaming=1 video_endp=$%02x", __func__, uvd->video_endp); return 0; } @@ -1811,14 +1813,14 @@ static void usbvideo_StopDataPump(struct uvd *uvd) return; if (uvd->debug > 1) - info("%s($%p)", __FUNCTION__, uvd); + info("%s($%p)", __func__, uvd); /* Unschedule all of the iso td's */ for (i=0; i < USBVIDEO_NUMSBUF; i++) { usb_kill_urb(uvd->sbuf[i].urb); } if (uvd->debug > 1) - info("%s: streaming=0", __FUNCTION__); + info("%s: streaming=0", __func__); uvd->streaming = 0; if (!uvd->remove_pending) { @@ -1826,12 +1828,12 @@ static void usbvideo_StopDataPump(struct uvd *uvd) if (VALID_CALLBACK(uvd, videoStop)) GET_CALLBACK(uvd, videoStop)(uvd); else - err("%s: videoStop not set", __FUNCTION__); + err("%s: videoStop not set", __func__); /* Set packet size to 0 */ j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); if (j < 0) { - err("%s: usb_set_interface() error %d.", __FUNCTION__, j); + err("%s: usb_set_interface() error %d.", __func__, j); uvd->last_error = j; } } @@ -1955,12 +1957,12 @@ static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) struct usbvideo_frame *frame = &uvd->frame[frameNum]; if (uvd->debug >= 2) - info("%s($%p,%d.)", __FUNCTION__, uvd, frameNum); + info("%s($%p,%d.)", __func__, uvd, frameNum); switch (frame->frameState) { case FrameState_Unused: if (uvd->debug >= 2) - info("%s: FrameState_Unused", __FUNCTION__); + info("%s: FrameState_Unused", __func__); return -EINVAL; case FrameState_Ready: case FrameState_Grabbing: @@ -1970,7 +1972,7 @@ static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) redo: if (!CAMERA_IS_OPERATIONAL(uvd)) { if (uvd->debug >= 2) - info("%s: Camera is not operational (1)", __FUNCTION__); + info("%s: Camera is not operational (1)", __func__); return -EIO; } ntries = 0; @@ -1979,24 +1981,24 @@ static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) signalPending = signal_pending(current); if (!CAMERA_IS_OPERATIONAL(uvd)) { if (uvd->debug >= 2) - info("%s: Camera is not operational (2)", __FUNCTION__); + info("%s: Camera is not operational (2)", __func__); return -EIO; } assert(uvd->fbuf != NULL); if (signalPending) { if (uvd->debug >= 2) - info("%s: Signal=$%08x", __FUNCTION__, signalPending); + info("%s: Signal=$%08x", __func__, signalPending); if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) { usbvideo_TestPattern(uvd, 1, 0); uvd->curframe = -1; uvd->stats.frame_num++; if (uvd->debug >= 2) - info("%s: Forced test pattern screen", __FUNCTION__); + info("%s: Forced test pattern screen", __func__); return 0; } else { /* Standard answer: Interrupted! */ if (uvd->debug >= 2) - info("%s: Interrupted!", __FUNCTION__); + info("%s: Interrupted!", __func__); return -EINTR; } } else { @@ -2006,17 +2008,17 @@ static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) else if (VALID_CALLBACK(uvd, processData)) GET_CALLBACK(uvd, processData)(uvd, frame); else - err("%s: processData not set", __FUNCTION__); + err("%s: processData not set", __func__); } } while (frame->frameState == FrameState_Grabbing); if (uvd->debug >= 2) { info("%s: Grabbing done; state=%d. (%lu. bytes)", - __FUNCTION__, frame->frameState, frame->seqRead_Length); + __func__, frame->frameState, frame->seqRead_Length); } if (frame->frameState == FrameState_Error) { int ret = usbvideo_NewFrame(uvd, frameNum); if (ret < 0) { - err("%s: usbvideo_NewFrame() failed (%d.)", __FUNCTION__, ret); + err("%s: usbvideo_NewFrame() failed (%d.)", __func__, ret); return ret; } goto redo; @@ -2048,7 +2050,7 @@ static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) } frame->frameState = FrameState_Done_Hold; if (uvd->debug >= 2) - info("%s: Entered FrameState_Done_Hold state.", __FUNCTION__); + info("%s: Entered FrameState_Done_Hold state.", __func__); return 0; case FrameState_Done_Hold: @@ -2059,12 +2061,12 @@ static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) * it will be released back into the wild to roam freely. */ if (uvd->debug >= 2) - info("%s: FrameState_Done_Hold state.", __FUNCTION__); + info("%s: FrameState_Done_Hold state.", __func__); return 0; } /* Catch-all for other cases. We shall not be here. */ - err("%s: Invalid state %d.", __FUNCTION__, frame->frameState); + err("%s: Invalid state %d.", __func__, frame->frameState); frame->frameState = FrameState_Unused; return 0; } @@ -2160,7 +2162,7 @@ static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, const int ccm = 128; /* Color correction median - see below */ if ((uvd == NULL) || (frame == NULL)) { - err("%s: Illegal call.", __FUNCTION__); + err("%s: Illegal call.", __func__); return; } adj = (uvd->vpic.contrast - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c index da1ba021110..64819353276 100644 --- a/drivers/media/video/usbvideo/vicam.c +++ b/drivers/media/video/usbvideo/vicam.c @@ -48,7 +48,7 @@ // #define VICAM_DEBUG #ifdef VICAM_DEBUG -#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __FUNCTION__, lineno, ##args) +#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __func__, lineno, ##args) #define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args) #else #define DBG(fmn,args...) do {} while(0) @@ -1066,7 +1066,9 @@ static const struct file_operations vicam_fops = { .read = vicam_read, .mmap = vicam_mmap, .ioctl = vicam_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, }; diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 56775ab8b75..a9c5e5adba3 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -53,19 +53,21 @@ #include "usbvision.h" -static unsigned int core_debug = 0; +static unsigned int core_debug; module_param(core_debug,int,0644); MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); -static unsigned int force_testpattern = 0; +static unsigned int force_testpattern; 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 +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. +/* To help people with Black and White output with using s-video input. + * Some cables and input device are wired differently. */ +static int SwitchSVideoInput; 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)"); @@ -82,8 +84,10 @@ MODULE_PARM_DESC(adjust_Y_Offset, "adjust Y offset display [core]"); #ifdef USBVISION_DEBUG - #define PDEBUG(level, fmt, args...) \ - if (core_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args) + #define PDEBUG(level, fmt, args...) { \ + if (core_debug & (level)) \ + info("[%s:%d] " fmt, __func__, __LINE__ , ## args); \ + } #else #define PDEBUG(level, fmt, args...) do {} while(0) #endif @@ -384,7 +388,7 @@ int usbvision_scratch_alloc(struct usb_usbvision *usbvision) scratch_reset(usbvision); if(usbvision->scratch == NULL) { err("%s: unable to allocate %d bytes for scratch", - __FUNCTION__, scratch_buf_size); + __func__, scratch_buf_size); return -ENOMEM; } return 0; @@ -418,7 +422,7 @@ static void usbvision_testpattern(struct usb_usbvision *usbvision, unsigned char *f; int num_cell = 0; int scan_length = 0; - static int num_pass = 0; + static int num_pass; if (usbvision == NULL) { printk(KERN_ERR "%s: usbvision == NULL\n", proc); @@ -493,7 +497,8 @@ int usbvision_decompress_alloc(struct usb_usbvision *usbvision) int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2; usbvision->IntraFrameBuffer = vmalloc_32(IFB_size); if (usbvision->IntraFrameBuffer == NULL) { - err("%s: unable to allocate %d for compr. frame buffer", __FUNCTION__, IFB_size); + err("%s: unable to allocate %d for compr. frame buffer", + __func__, IFB_size); return -ENOMEM; } return 0; @@ -1430,7 +1435,7 @@ static int usbvision_compress_isochronous(struct usb_usbvision *usbvision, } #if ENABLE_HEXDUMP if (totlen > 0) { - static int foo = 0; + static int foo; if (foo < 1) { printk(KERN_DEBUG "+%d.\n", usbvision->scratchlen); usbvision_hexdump(data0, (totlen > 64) ? 64 : totlen); @@ -1516,7 +1521,7 @@ static void usbvision_isocIrq(struct urb *urb) if(errCode) { err("%s: usb_submit_urb failed: error %d", - __FUNCTION__, errCode); + __func__, errCode); } return; @@ -1547,7 +1552,7 @@ int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg) 0, (__u16) reg, buffer, 1, HZ); if (errCode < 0) { - err("%s: failed: error %d", __FUNCTION__, errCode); + err("%s: failed: error %d", __func__, errCode); return errCode; } return buffer[0]; @@ -1575,7 +1580,7 @@ int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ); if (errCode < 0) { - err("%s: failed: error %d", __FUNCTION__, errCode); + err("%s: failed: error %d", __func__, errCode); } return errCode; } @@ -1851,7 +1856,7 @@ int usbvision_set_output(struct usb_usbvision *usbvision, int width, 0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ); if (errCode < 0) { - err("%s failed: error %d", __FUNCTION__, errCode); + err("%s failed: error %d", __func__, errCode); return errCode; } usbvision->curwidth = usbvision->stretch_width * UsbWidth; @@ -2237,7 +2242,7 @@ static int usbvision_set_dram_settings(struct usb_usbvision *usbvision) (__u16) USBVISION_DRM_PRM1, value, 8, HZ); if (rc < 0) { - err("%sERROR=%d", __FUNCTION__, rc); + err("%sERROR=%d", __func__, rc); return rc; } @@ -2486,7 +2491,7 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); if (urb == NULL) { - err("%s: usb_alloc_urb() failed", __FUNCTION__); + err("%s: usb_alloc_urb() failed", __func__); return -ENOMEM; } usbvision->sbuf[bufIdx].urb = urb; @@ -2520,13 +2525,13 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) GFP_KERNEL); if (errCode) { err("%s: usb_submit_urb(%d) failed: error %d", - __FUNCTION__, bufIdx, errCode); + __func__, bufIdx, errCode); } } usbvision->streaming = Stream_Idle; PDEBUG(DBG_ISOC, "%s: streaming=1 usbvision->video_endp=$%02x", - __FUNCTION__, + __func__, usbvision->video_endp); return 0; } @@ -2560,7 +2565,7 @@ void usbvision_stop_isoc(struct usb_usbvision *usbvision) } - PDEBUG(DBG_ISOC, "%s: streaming=Stream_Off\n", __FUNCTION__); + PDEBUG(DBG_ISOC, "%s: streaming=Stream_Off\n", __func__); usbvision->streaming = Stream_Off; if (!usbvision->remove_pending) { @@ -2571,7 +2576,7 @@ void usbvision_stop_isoc(struct usb_usbvision *usbvision) usbvision->ifaceAlt); if (errCode < 0) { err("%s: usb_set_interface() failed: error %d", - __FUNCTION__, errCode); + __func__, errCode); usbvision->last_error = errCode; } regValue = (16-usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F; diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index aabc42cae9c..e2274d77ea2 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -40,13 +40,15 @@ #define DBG_I2C 1<<0 -static int i2c_debug = 0; +static int i2c_debug; 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) +#define PDEBUG(level, fmt, args...) { \ + if (i2c_debug & (level)) \ + info("[%s:%d] " fmt, __func__, __LINE__ , ## args); \ + } static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf, short len); diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index df52f8a6021..d97261ab430 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -97,10 +97,10 @@ USBVISION_DRIVER_VERSION_PATCHLEVEL) #ifdef USBVISION_DEBUG - #define PDEBUG(level, fmt, args...) \ + #define PDEBUG(level, fmt, args...) { \ if (video_debug & (level)) \ - info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ ,\ - ## args) + info("[%s:%d] " fmt, __func__, __LINE__ , ## args); \ + } #else #define PDEBUG(level, fmt, args...) do {} while(0) #endif @@ -115,7 +115,7 @@ USBVISION_DRIVER_VERSION_PATCHLEVEL) /* sequential number of usbvision device */ -static int usbvision_nr = 0; +static int usbvision_nr; static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" }, @@ -135,7 +135,7 @@ static void usbvision_release(struct usb_usbvision *usbvision); /* Set the default format for ISOC endpoint */ static int isocMode = ISOC_MODE_COMPRESS; /* Set the default Debug Mode of the device driver */ -static int video_debug = 0; +static int video_debug; /* Set the default device to power on at startup */ static int PowerOnAtOpen = 1; /* Sequential Number of Video Device */ @@ -343,7 +343,7 @@ static void usbvision_create_sysfs(struct video_device *vdev) return; } while (0); - err("%s error: %d\n", __FUNCTION__, res); + err("%s error: %d\n", __func__, res); } static void usbvision_remove_sysfs(struct video_device *vdev) @@ -490,7 +490,7 @@ static int usbvision_v4l2_close(struct inode *inode, struct file *file) mutex_unlock(&usbvision->lock); if (usbvision->remove_pending) { - printk(KERN_INFO "%s: Final disconnect\n", __FUNCTION__); + printk(KERN_INFO "%s: Final disconnect\n", __func__); usbvision_release(usbvision); } @@ -522,7 +522,7 @@ static int vidioc_g_register (struct file *file, void *priv, errCode = usbvision_read_reg(usbvision, reg->reg&0xff); if (errCode < 0) { err("%s: VIDIOC_DBG_G_REGISTER failed: error %d", - __FUNCTION__, errCode); + __func__, errCode); return errCode; } reg->val = errCode; @@ -543,7 +543,7 @@ static int vidioc_s_register (struct file *file, void *priv, errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val); if (errCode < 0) { err("%s: VIDIOC_DBG_S_REGISTER failed: error %d", - __FUNCTION__, errCode); + __func__, errCode); return errCode; } return 0; @@ -1102,7 +1102,7 @@ static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, int ret,i; struct usbvision_frame *frame; - PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __FUNCTION__, + PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __func__, (unsigned long)count, noblock); if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL)) @@ -1171,7 +1171,7 @@ static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, } PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", - __FUNCTION__, + __func__, frame->index, frame->bytes_read, frame->scanlength); /* copy bytes to user space; we allow for partials reads */ @@ -1184,7 +1184,7 @@ static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, frame->bytes_read += count; PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", - __FUNCTION__, + __func__, (unsigned long)count, frame->bytes_read); /* For now, forget the frame if it has not been read in one shot. */ @@ -1269,12 +1269,12 @@ static int usbvision_radio_open(struct inode *inode, struct file *file) (struct usb_usbvision *) video_get_drvdata(dev); int errCode = 0; - PDEBUG(DBG_IO, "%s:", __FUNCTION__); + PDEBUG(DBG_IO, "%s:", __func__); mutex_lock(&usbvision->lock); if (usbvision->user) { - err("%s: Someone tried to open an already opened USBVision Radio!", __FUNCTION__); + err("%s: Someone tried to open an already opened USBVision Radio!", __func__); errCode = -EBUSY; } else { @@ -1342,7 +1342,7 @@ static int usbvision_radio_close(struct inode *inode, struct file *file) mutex_unlock(&usbvision->lock); if (usbvision->remove_pending) { - printk(KERN_INFO "%s: Final disconnect\n", __FUNCTION__); + printk(KERN_INFO "%s: Final disconnect\n", __func__); usbvision_release(usbvision); } @@ -1507,7 +1507,7 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, struct video_device *vdev; if (usb_dev == NULL) { - err("%s: usbvision->dev is not set", __FUNCTION__); + err("%s: usbvision->dev is not set", __func__); return NULL; } @@ -1759,7 +1759,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, PDEBUG(DBG_PROBE, "model out of bounds %d",model); return -ENODEV; } - printk(KERN_INFO "%s: %s found\n", __FUNCTION__, + printk(KERN_INFO "%s: %s found\n", __func__, usbvision_device_data[model].ModelString); if (usbvision_device_data[model].Interface >= 0) { @@ -1771,20 +1771,20 @@ static int __devinit usbvision_probe(struct usb_interface *intf, if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) { err("%s: interface %d. has non-ISO endpoint!", - __FUNCTION__, ifnum); + __func__, ifnum); err("%s: Endpoint attributes %d", - __FUNCTION__, endpoint->bmAttributes); + __func__, endpoint->bmAttributes); return -ENODEV; } if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { err("%s: interface %d. has ISO OUT endpoint!", - __FUNCTION__, ifnum); + __func__, ifnum); return -ENODEV; } if ((usbvision = usbvision_alloc(dev)) == NULL) { - err("%s: couldn't allocate USBVision struct", __FUNCTION__); + err("%s: couldn't allocate USBVision struct", __func__); return -ENOMEM; } @@ -1868,7 +1868,7 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) PDEBUG(DBG_PROBE, ""); if (usbvision == NULL) { - err("%s: usb_get_intfdata() failed", __FUNCTION__); + err("%s: usb_get_intfdata() failed", __func__); return; } usb_set_intfdata (intf, NULL); @@ -1891,7 +1891,7 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) if (usbvision->user) { printk(KERN_INFO "%s: In use, disconnect pending\n", - __FUNCTION__); + __func__); wake_up_interruptible(&usbvision->wait_frame); wake_up_interruptible(&usbvision->wait_stream); } else { diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index 50e1ff9f2be..a0f6c60279e 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -39,15 +39,18 @@ #include <linux/kmod.h> #endif -static unsigned int debug = 0; +static unsigned int debug; module_param(debug, int, 0644); -MODULE_PARM_DESC(debug,"enable debug messages"); +MODULE_PARM_DESC(debug, "enable debug messages"); MODULE_AUTHOR("Bill Dirks"); MODULE_DESCRIPTION("v4l(1) compatibility layer for v4l2 drivers."); MODULE_LICENSE("GPL"); -#define dprintk(fmt, arg...) if (debug) \ - printk(KERN_DEBUG "v4l1-compat: " fmt , ## arg) +#define dprintk(fmt, arg...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "v4l1-compat: " fmt , ## arg);\ + } while (0) /* * I O C T L T R A N S L A T I O N @@ -69,14 +72,12 @@ get_v4l_control(struct inode *inode, qctrl2.id = cid; err = drv(inode, file, VIDIOC_QUERYCTRL, &qctrl2); if (err < 0) - dprintk("VIDIOC_QUERYCTRL: %d\n",err); - if (err == 0 && - !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED)) - { + dprintk("VIDIOC_QUERYCTRL: %d\n", err); + if (err == 0 && !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED)) { ctrl2.id = qctrl2.id; err = drv(inode, file, VIDIOC_G_CTRL, &ctrl2); if (err < 0) { - dprintk("VIDIOC_G_CTRL: %d\n",err); + dprintk("VIDIOC_G_CTRL: %d\n", err); return 0; } return ((ctrl2.value - qctrl2.minimum) * 65535 @@ -100,11 +101,10 @@ set_v4l_control(struct inode *inode, qctrl2.id = cid; err = drv(inode, file, VIDIOC_QUERYCTRL, &qctrl2); if (err < 0) - dprintk("VIDIOC_QUERYCTRL: %d\n",err); + dprintk("VIDIOC_QUERYCTRL: %d\n", err); if (err == 0 && !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED) && - !(qctrl2.flags & V4L2_CTRL_FLAG_GRABBED)) - { + !(qctrl2.flags & V4L2_CTRL_FLAG_GRABBED)) { if (value < 0) value = 0; if (value > 65535) @@ -119,14 +119,14 @@ set_v4l_control(struct inode *inode, ctrl2.value += qctrl2.minimum; err = drv(inode, file, VIDIOC_S_CTRL, &ctrl2); if (err < 0) - dprintk("VIDIOC_S_CTRL: %d\n",err); + dprintk("VIDIOC_S_CTRL: %d\n", err); } return 0; } /* ----------------------------------------------------------------- */ -const static unsigned int palette2pixelformat[] = { +static const unsigned int palette2pixelformat[] = { [VIDEO_PALETTE_GREY] = V4L2_PIX_FMT_GREY, [VIDEO_PALETTE_RGB555] = V4L2_PIX_FMT_RGB555, [VIDEO_PALETTE_RGB565] = V4L2_PIX_FMT_RGB565, @@ -157,8 +157,7 @@ static unsigned int __attribute_const__ pixelformat_to_palette(unsigned int pixelformat) { int palette = 0; - switch (pixelformat) - { + switch (pixelformat) { case V4L2_PIX_FMT_GREY: palette = VIDEO_PALETTE_GREY; break; @@ -200,14 +199,13 @@ pixelformat_to_palette(unsigned int pixelformat) /* ----------------------------------------------------------------- */ -static int poll_one(struct file *file) +static int poll_one(struct file *file, struct poll_wqueues *pwq) { int retval = 1; poll_table *table; - struct poll_wqueues pwq; - poll_initwait(&pwq); - table = &pwq.pt; + poll_initwait(pwq); + table = &pwq->pt; for (;;) { int mask; set_current_state(TASK_INTERRUPTIBLE); @@ -222,878 +220,1073 @@ static int poll_one(struct file *file) schedule(); } set_current_state(TASK_RUNNING); - poll_freewait(&pwq); + poll_freewait(pwq); return retval; } -static int count_inputs(struct inode *inode, - struct file *file, - v4l2_kioctl drv) +static int count_inputs( + struct inode *inode, + struct file *file, + v4l2_kioctl drv) { struct v4l2_input input2; int i; for (i = 0;; i++) { - memset(&input2,0,sizeof(input2)); + memset(&input2, 0, sizeof(input2)); input2.index = i; - if (0 != drv(inode,file,VIDIOC_ENUMINPUT, &input2)) + if (0 != drv(inode, file, VIDIOC_ENUMINPUT, &input2)) break; } return i; } -static int check_size(struct inode *inode, - struct file *file, - v4l2_kioctl drv, - int *maxw, int *maxh) +static int check_size( + struct inode *inode, + struct file *file, + v4l2_kioctl drv, + int *maxw, + int *maxh) { struct v4l2_fmtdesc desc2; struct v4l2_format fmt2; - memset(&desc2,0,sizeof(desc2)); - memset(&fmt2,0,sizeof(fmt2)); + memset(&desc2, 0, sizeof(desc2)); + memset(&fmt2, 0, sizeof(fmt2)); desc2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (0 != drv(inode,file,VIDIOC_ENUM_FMT, &desc2)) + if (0 != drv(inode, file, VIDIOC_ENUM_FMT, &desc2)) goto done; fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt2.fmt.pix.width = 10000; fmt2.fmt.pix.height = 10000; fmt2.fmt.pix.pixelformat = desc2.pixelformat; - if (0 != drv(inode,file,VIDIOC_TRY_FMT, &fmt2)) + if (0 != drv(inode, file, VIDIOC_TRY_FMT, &fmt2)) goto done; *maxw = fmt2.fmt.pix.width; *maxh = fmt2.fmt.pix.height; - done: +done: return 0; } /* ----------------------------------------------------------------- */ -/* - * This function is exported. - */ -int -v4l_compat_translate_ioctl(struct inode *inode, - struct file *file, - int cmd, - void *arg, - v4l2_kioctl drv) +static noinline int v4l1_compat_get_capabilities( + struct video_capability *cap, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) { - struct v4l2_capability *cap2 = NULL; - struct v4l2_format *fmt2 = NULL; - enum v4l2_buf_type captype = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - struct v4l2_framebuffer fbuf2; - struct v4l2_input input2; - struct v4l2_tuner tun2; - struct v4l2_standard std2; - struct v4l2_frequency freq2; - struct v4l2_audio aud2; - struct v4l2_queryctrl qctrl2; - struct v4l2_buffer buf2; - v4l2_std_id sid; - int i, err = 0; - - switch (cmd) { - case VIDIOCGCAP: /* capability */ - { - struct video_capability *cap = arg; - - cap2 = kzalloc(sizeof(*cap2), GFP_KERNEL); - if (!cap2) { - err = -ENOMEM; - break; - } - memset(cap, 0, sizeof(*cap)); - memset(&fbuf2, 0, sizeof(fbuf2)); + int err; + struct v4l2_framebuffer fbuf; + struct v4l2_capability *cap2; + + cap2 = kzalloc(sizeof(*cap2), GFP_KERNEL); + if (!cap2) { + err = -ENOMEM; + return err; + } + memset(cap, 0, sizeof(*cap)); + memset(&fbuf, 0, sizeof(fbuf)); - err = drv(inode, file, VIDIOC_QUERYCAP, cap2); + err = drv(inode, file, VIDIOC_QUERYCAP, cap2); + if (err < 0) { + dprintk("VIDIOCGCAP / VIDIOC_QUERYCAP: %d\n", err); + goto done; + } + if (cap2->capabilities & V4L2_CAP_VIDEO_OVERLAY) { + err = drv(inode, file, VIDIOC_G_FBUF, &fbuf); if (err < 0) { - dprintk("VIDIOCGCAP / VIDIOC_QUERYCAP: %d\n",err); - break; + dprintk("VIDIOCGCAP / VIDIOC_G_FBUF: %d\n", err); + memset(&fbuf, 0, sizeof(fbuf)); } - if (cap2->capabilities & V4L2_CAP_VIDEO_OVERLAY) { - err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2); - if (err < 0) { - dprintk("VIDIOCGCAP / VIDIOC_G_FBUF: %d\n",err); - memset(&fbuf2, 0, sizeof(fbuf2)); - } - err = 0; - } - - memcpy(cap->name, cap2->card, - min(sizeof(cap->name), sizeof(cap2->card))); - cap->name[sizeof(cap->name) - 1] = 0; - if (cap2->capabilities & V4L2_CAP_VIDEO_CAPTURE) - cap->type |= VID_TYPE_CAPTURE; - if (cap2->capabilities & V4L2_CAP_TUNER) - cap->type |= VID_TYPE_TUNER; - if (cap2->capabilities & V4L2_CAP_VBI_CAPTURE) - cap->type |= VID_TYPE_TELETEXT; - if (cap2->capabilities & V4L2_CAP_VIDEO_OVERLAY) - cap->type |= VID_TYPE_OVERLAY; - if (fbuf2.capability & V4L2_FBUF_CAP_LIST_CLIPPING) - cap->type |= VID_TYPE_CLIPPING; - - cap->channels = count_inputs(inode,file,drv); - check_size(inode,file,drv, - &cap->maxwidth,&cap->maxheight); - cap->audios = 0; /* FIXME */ - cap->minwidth = 48; /* FIXME */ - cap->minheight = 32; /* FIXME */ - break; + err = 0; } - case VIDIOCGFBUF: /* get frame buffer */ - { - struct video_buffer *buffer = arg; - memset(buffer, 0, sizeof(*buffer)); - memset(&fbuf2, 0, sizeof(fbuf2)); + memcpy(cap->name, cap2->card, + min(sizeof(cap->name), sizeof(cap2->card))); + cap->name[sizeof(cap->name) - 1] = 0; + if (cap2->capabilities & V4L2_CAP_VIDEO_CAPTURE) + cap->type |= VID_TYPE_CAPTURE; + if (cap2->capabilities & V4L2_CAP_TUNER) + cap->type |= VID_TYPE_TUNER; + if (cap2->capabilities & V4L2_CAP_VBI_CAPTURE) + cap->type |= VID_TYPE_TELETEXT; + if (cap2->capabilities & V4L2_CAP_VIDEO_OVERLAY) + cap->type |= VID_TYPE_OVERLAY; + if (fbuf.capability & V4L2_FBUF_CAP_LIST_CLIPPING) + cap->type |= VID_TYPE_CLIPPING; + + cap->channels = count_inputs(inode, file, drv); + check_size(inode, file, drv, + &cap->maxwidth, &cap->maxheight); + cap->audios = 0; /* FIXME */ + cap->minwidth = 48; /* FIXME */ + cap->minheight = 32; /* FIXME */ + +done: + kfree(cap2); + return err; +} - err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2); - if (err < 0) { - dprintk("VIDIOCGFBUF / VIDIOC_G_FBUF: %d\n",err); - break; - } - buffer->base = fbuf2.base; - buffer->height = fbuf2.fmt.height; - buffer->width = fbuf2.fmt.width; +static noinline int v4l1_compat_get_frame_buffer( + struct video_buffer *buffer, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_framebuffer fbuf; - switch (fbuf2.fmt.pixelformat) { - case V4L2_PIX_FMT_RGB332: - buffer->depth = 8; - break; - case V4L2_PIX_FMT_RGB555: - buffer->depth = 15; - break; - case V4L2_PIX_FMT_RGB565: - buffer->depth = 16; - break; - case V4L2_PIX_FMT_BGR24: - buffer->depth = 24; - break; - case V4L2_PIX_FMT_BGR32: - buffer->depth = 32; - break; - default: - buffer->depth = 0; - } - if (fbuf2.fmt.bytesperline) { - buffer->bytesperline = fbuf2.fmt.bytesperline; - if (!buffer->depth && buffer->width) - buffer->depth = ((fbuf2.fmt.bytesperline<<3) - + (buffer->width-1) ) - /buffer->width; - } else { - buffer->bytesperline = - (buffer->width * buffer->depth + 7) & 7; - buffer->bytesperline >>= 3; - } + memset(buffer, 0, sizeof(*buffer)); + memset(&fbuf, 0, sizeof(fbuf)); + + err = drv(inode, file, VIDIOC_G_FBUF, &fbuf); + if (err < 0) { + dprintk("VIDIOCGFBUF / VIDIOC_G_FBUF: %d\n", err); + goto done; + } + buffer->base = fbuf.base; + buffer->height = fbuf.fmt.height; + buffer->width = fbuf.fmt.width; + + switch (fbuf.fmt.pixelformat) { + case V4L2_PIX_FMT_RGB332: + buffer->depth = 8; + break; + case V4L2_PIX_FMT_RGB555: + buffer->depth = 15; + break; + case V4L2_PIX_FMT_RGB565: + buffer->depth = 16; + break; + case V4L2_PIX_FMT_BGR24: + buffer->depth = 24; + break; + case V4L2_PIX_FMT_BGR32: + buffer->depth = 32; break; + default: + buffer->depth = 0; } - case VIDIOCSFBUF: /* set frame buffer */ - { - struct video_buffer *buffer = arg; - - memset(&fbuf2, 0, sizeof(fbuf2)); - fbuf2.base = buffer->base; - fbuf2.fmt.height = buffer->height; - fbuf2.fmt.width = buffer->width; - switch (buffer->depth) { - case 8: - fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB332; - break; - case 15: - fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB555; - break; - case 16: - fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB565; - break; - case 24: - fbuf2.fmt.pixelformat = V4L2_PIX_FMT_BGR24; - break; - case 32: - fbuf2.fmt.pixelformat = V4L2_PIX_FMT_BGR32; - break; - } - fbuf2.fmt.bytesperline = buffer->bytesperline; - err = drv(inode, file, VIDIOC_S_FBUF, &fbuf2); - if (err < 0) - dprintk("VIDIOCSFBUF / VIDIOC_S_FBUF: %d\n",err); + if (fbuf.fmt.bytesperline) { + buffer->bytesperline = fbuf.fmt.bytesperline; + if (!buffer->depth && buffer->width) + buffer->depth = ((fbuf.fmt.bytesperline<<3) + + (buffer->width-1)) + / buffer->width; + } else { + buffer->bytesperline = + (buffer->width * buffer->depth + 7) & 7; + buffer->bytesperline >>= 3; + } +done: + return err; +} + +static noinline int v4l1_compat_set_frame_buffer( + struct video_buffer *buffer, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_framebuffer fbuf; + + memset(&fbuf, 0, sizeof(fbuf)); + fbuf.base = buffer->base; + fbuf.fmt.height = buffer->height; + fbuf.fmt.width = buffer->width; + switch (buffer->depth) { + case 8: + fbuf.fmt.pixelformat = V4L2_PIX_FMT_RGB332; + break; + case 15: + fbuf.fmt.pixelformat = V4L2_PIX_FMT_RGB555; + break; + case 16: + fbuf.fmt.pixelformat = V4L2_PIX_FMT_RGB565; + break; + case 24: + fbuf.fmt.pixelformat = V4L2_PIX_FMT_BGR24; + break; + case 32: + fbuf.fmt.pixelformat = V4L2_PIX_FMT_BGR32; break; } - case VIDIOCGWIN: /* get window or capture dimensions */ - { - struct video_window *win = arg; + fbuf.fmt.bytesperline = buffer->bytesperline; + err = drv(inode, file, VIDIOC_S_FBUF, &fbuf); + if (err < 0) + dprintk("VIDIOCSFBUF / VIDIOC_S_FBUF: %d\n", err); + return err; +} - fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); - if (!fmt2) { - err = -ENOMEM; - break; - } - memset(win,0,sizeof(*win)); +static noinline int v4l1_compat_get_win_cap_dimensions( + struct video_window *win, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_format *fmt; - fmt2->type = V4L2_BUF_TYPE_VIDEO_OVERLAY; - err = drv(inode, file, VIDIOC_G_FMT, fmt2); - if (err < 0) - dprintk("VIDIOCGWIN / VIDIOC_G_WIN: %d\n",err); - if (err == 0) { - win->x = fmt2->fmt.win.w.left; - win->y = fmt2->fmt.win.w.top; - win->width = fmt2->fmt.win.w.width; - win->height = fmt2->fmt.win.w.height; - win->chromakey = fmt2->fmt.win.chromakey; - win->clips = NULL; - win->clipcount = 0; - break; - } + fmt = kzalloc(sizeof(*fmt), GFP_KERNEL); + if (!fmt) { + err = -ENOMEM; + return err; + } + memset(win, 0, sizeof(*win)); - fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - err = drv(inode, file, VIDIOC_G_FMT, fmt2); - if (err < 0) { - dprintk("VIDIOCGWIN / VIDIOC_G_FMT: %d\n",err); - break; - } - win->x = 0; - win->y = 0; - win->width = fmt2->fmt.pix.width; - win->height = fmt2->fmt.pix.height; - win->chromakey = 0; + fmt->type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + err = drv(inode, file, VIDIOC_G_FMT, fmt); + if (err < 0) + dprintk("VIDIOCGWIN / VIDIOC_G_WIN: %d\n", err); + if (err == 0) { + win->x = fmt->fmt.win.w.left; + win->y = fmt->fmt.win.w.top; + win->width = fmt->fmt.win.w.width; + win->height = fmt->fmt.win.w.height; + win->chromakey = fmt->fmt.win.chromakey; win->clips = NULL; win->clipcount = 0; - break; + goto done; } - case VIDIOCSWIN: /* set window and/or capture dimensions */ - { - struct video_window *win = arg; - int err1,err2; - fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); - if (!fmt2) { - err = -ENOMEM; - break; - } - fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - drv(inode, file, VIDIOC_STREAMOFF, &fmt2->type); - err1 = drv(inode, file, VIDIOC_G_FMT, fmt2); - if (err1 < 0) - dprintk("VIDIOCSWIN / VIDIOC_G_FMT: %d\n",err); - if (err1 == 0) { - fmt2->fmt.pix.width = win->width; - fmt2->fmt.pix.height = win->height; - fmt2->fmt.pix.field = V4L2_FIELD_ANY; - fmt2->fmt.pix.bytesperline = 0; - err = drv(inode, file, VIDIOC_S_FMT, fmt2); - if (err < 0) - dprintk("VIDIOCSWIN / VIDIOC_S_FMT #1: %d\n", - err); - win->width = fmt2->fmt.pix.width; - win->height = fmt2->fmt.pix.height; - } + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = drv(inode, file, VIDIOC_G_FMT, fmt); + if (err < 0) { + dprintk("VIDIOCGWIN / VIDIOC_G_FMT: %d\n", err); + goto done; + } + win->x = 0; + win->y = 0; + win->width = fmt->fmt.pix.width; + win->height = fmt->fmt.pix.height; + win->chromakey = 0; + win->clips = NULL; + win->clipcount = 0; +done: + kfree(fmt); + return err; +} - memset(fmt2,0,sizeof(*fmt2)); - fmt2->type = V4L2_BUF_TYPE_VIDEO_OVERLAY; - fmt2->fmt.win.w.left = win->x; - fmt2->fmt.win.w.top = win->y; - fmt2->fmt.win.w.width = win->width; - fmt2->fmt.win.w.height = win->height; - fmt2->fmt.win.chromakey = win->chromakey; - fmt2->fmt.win.clips = (void __user *)win->clips; - fmt2->fmt.win.clipcount = win->clipcount; - err2 = drv(inode, file, VIDIOC_S_FMT, fmt2); - if (err2 < 0) - dprintk("VIDIOCSWIN / VIDIOC_S_FMT #2: %d\n",err); - - if (err1 != 0 && err2 != 0) - err = err1; - break; +static noinline int v4l1_compat_set_win_cap_dimensions( + struct video_window *win, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err, err1, err2; + struct v4l2_format *fmt; + + fmt = kzalloc(sizeof(*fmt), GFP_KERNEL); + if (!fmt) { + err = -ENOMEM; + return err; } - case VIDIOCCAPTURE: /* turn on/off preview */ - { - int *on = arg; - - if (0 == *on) { - /* dirty hack time. But v4l1 has no STREAMOFF - * equivalent in the API, and this one at - * least comes close ... */ - drv(inode, file, VIDIOC_STREAMOFF, &captype); - } - err = drv(inode, file, VIDIOC_OVERLAY, arg); + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + drv(inode, file, VIDIOC_STREAMOFF, &fmt->type); + err1 = drv(inode, file, VIDIOC_G_FMT, fmt); + if (err1 < 0) + dprintk("VIDIOCSWIN / VIDIOC_G_FMT: %d\n", err1); + if (err1 == 0) { + fmt->fmt.pix.width = win->width; + fmt->fmt.pix.height = win->height; + fmt->fmt.pix.field = V4L2_FIELD_ANY; + fmt->fmt.pix.bytesperline = 0; + err = drv(inode, file, VIDIOC_S_FMT, fmt); if (err < 0) - dprintk("VIDIOCCAPTURE / VIDIOC_PREVIEW: %d\n",err); - break; + dprintk("VIDIOCSWIN / VIDIOC_S_FMT #1: %d\n", + err); + win->width = fmt->fmt.pix.width; + win->height = fmt->fmt.pix.height; } - case VIDIOCGCHAN: /* get input information */ - { - struct video_channel *chan = arg; - memset(&input2,0,sizeof(input2)); - input2.index = chan->channel; - err = drv(inode, file, VIDIOC_ENUMINPUT, &input2); - if (err < 0) { - dprintk("VIDIOCGCHAN / VIDIOC_ENUMINPUT: " - "channel=%d err=%d\n",chan->channel,err); - break; - } - chan->channel = input2.index; - memcpy(chan->name, input2.name, - min(sizeof(chan->name), sizeof(input2.name))); - chan->name[sizeof(chan->name) - 1] = 0; - chan->tuners = (input2.type == V4L2_INPUT_TYPE_TUNER) ? 1 : 0; - chan->flags = (chan->tuners) ? VIDEO_VC_TUNER : 0; - switch (input2.type) { - case V4L2_INPUT_TYPE_TUNER: - chan->type = VIDEO_TYPE_TV; - break; - default: - case V4L2_INPUT_TYPE_CAMERA: - chan->type = VIDEO_TYPE_CAMERA; - break; - } - chan->norm = 0; - err = drv(inode, file, VIDIOC_G_STD, &sid); - if (err < 0) - dprintk("VIDIOCGCHAN / VIDIOC_G_STD: %d\n",err); - if (err == 0) { - if (sid & V4L2_STD_PAL) - chan->norm = VIDEO_MODE_PAL; - if (sid & V4L2_STD_NTSC) - chan->norm = VIDEO_MODE_NTSC; - if (sid & V4L2_STD_SECAM) - chan->norm = VIDEO_MODE_SECAM; - } - break; - } - case VIDIOCSCHAN: /* set input */ - { - struct video_channel *chan = arg; + memset(fmt, 0, sizeof(*fmt)); + fmt->type = V4L2_BUF_TYPE_VIDEO_OVERLAY; + fmt->fmt.win.w.left = win->x; + fmt->fmt.win.w.top = win->y; + fmt->fmt.win.w.width = win->width; + fmt->fmt.win.w.height = win->height; + fmt->fmt.win.chromakey = win->chromakey; + fmt->fmt.win.clips = (void __user *)win->clips; + fmt->fmt.win.clipcount = win->clipcount; + err2 = drv(inode, file, VIDIOC_S_FMT, fmt); + if (err2 < 0) + dprintk("VIDIOCSWIN / VIDIOC_S_FMT #2: %d\n", err2); + + if (err1 != 0 && err2 != 0) + err = err1; + else + err = 0; + kfree(fmt); + return err; +} - sid = 0; - err = drv(inode, file, VIDIOC_S_INPUT, &chan->channel); - if (err < 0) - dprintk("VIDIOCSCHAN / VIDIOC_S_INPUT: %d\n",err); - switch (chan->norm) { - case VIDEO_MODE_PAL: - sid = V4L2_STD_PAL; - break; - case VIDEO_MODE_NTSC: - sid = V4L2_STD_NTSC; - break; - case VIDEO_MODE_SECAM: - sid = V4L2_STD_SECAM; - break; - } - if (0 != sid) { - err = drv(inode, file, VIDIOC_S_STD, &sid); - if (err < 0) - dprintk("VIDIOCSCHAN / VIDIOC_S_STD: %d\n",err); - } - break; +static noinline int v4l1_compat_turn_preview_on_off( + int *on, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + enum v4l2_buf_type captype = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (0 == *on) { + /* dirty hack time. But v4l1 has no STREAMOFF + * equivalent in the API, and this one at + * least comes close ... */ + drv(inode, file, VIDIOC_STREAMOFF, &captype); } - case VIDIOCGPICT: /* get tone controls & partial capture format */ - { - struct video_picture *pict = arg; - - fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); - if (!fmt2) { - err = -ENOMEM; - break; - } + err = drv(inode, file, VIDIOC_OVERLAY, on); + if (err < 0) + dprintk("VIDIOCCAPTURE / VIDIOC_PREVIEW: %d\n", err); + return err; +} - pict->brightness = get_v4l_control(inode, file, - V4L2_CID_BRIGHTNESS,drv); - pict->hue = get_v4l_control(inode, file, - V4L2_CID_HUE, drv); - pict->contrast = get_v4l_control(inode, file, - V4L2_CID_CONTRAST, drv); - pict->colour = get_v4l_control(inode, file, - V4L2_CID_SATURATION, drv); - pict->whiteness = get_v4l_control(inode, file, - V4L2_CID_WHITENESS, drv); - - fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - err = drv(inode, file, VIDIOC_G_FMT, fmt2); - if (err < 0) { - dprintk("VIDIOCGPICT / VIDIOC_G_FMT: %d\n",err); - break; - } +static noinline int v4l1_compat_get_input_info( + struct video_channel *chan, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_input input2; + v4l2_std_id sid; - pict->depth = ((fmt2->fmt.pix.bytesperline<<3) - + (fmt2->fmt.pix.width-1) ) - /fmt2->fmt.pix.width; - pict->palette = pixelformat_to_palette( - fmt2->fmt.pix.pixelformat); + memset(&input2, 0, sizeof(input2)); + input2.index = chan->channel; + err = drv(inode, file, VIDIOC_ENUMINPUT, &input2); + if (err < 0) { + dprintk("VIDIOCGCHAN / VIDIOC_ENUMINPUT: " + "channel=%d err=%d\n", chan->channel, err); + goto done; + } + chan->channel = input2.index; + memcpy(chan->name, input2.name, + min(sizeof(chan->name), sizeof(input2.name))); + chan->name[sizeof(chan->name) - 1] = 0; + chan->tuners = (input2.type == V4L2_INPUT_TYPE_TUNER) ? 1 : 0; + chan->flags = (chan->tuners) ? VIDEO_VC_TUNER : 0; + switch (input2.type) { + case V4L2_INPUT_TYPE_TUNER: + chan->type = VIDEO_TYPE_TV; + break; + default: + case V4L2_INPUT_TYPE_CAMERA: + chan->type = VIDEO_TYPE_CAMERA; break; } - case VIDIOCSPICT: /* set tone controls & partial capture format */ - { - struct video_picture *pict = arg; - int mem_err = 0, ovl_err = 0; + chan->norm = 0; + err = drv(inode, file, VIDIOC_G_STD, &sid); + if (err < 0) + dprintk("VIDIOCGCHAN / VIDIOC_G_STD: %d\n", err); + if (err == 0) { + if (sid & V4L2_STD_PAL) + chan->norm = VIDEO_MODE_PAL; + if (sid & V4L2_STD_NTSC) + chan->norm = VIDEO_MODE_NTSC; + if (sid & V4L2_STD_SECAM) + chan->norm = VIDEO_MODE_SECAM; + } +done: + return err; +} - fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); - if (!fmt2) { - err = -ENOMEM; - break; - } - memset(&fbuf2, 0, sizeof(fbuf2)); - - set_v4l_control(inode, file, - V4L2_CID_BRIGHTNESS, pict->brightness, drv); - set_v4l_control(inode, file, - V4L2_CID_HUE, pict->hue, drv); - set_v4l_control(inode, file, - V4L2_CID_CONTRAST, pict->contrast, drv); - set_v4l_control(inode, file, - V4L2_CID_SATURATION, pict->colour, drv); - set_v4l_control(inode, file, - V4L2_CID_WHITENESS, pict->whiteness, drv); - /* - * V4L1 uses this ioctl to set both memory capture and overlay - * pixel format, while V4L2 has two different ioctls for this. - * Some cards may not support one or the other, and may support - * different pixel formats for memory vs overlay. - */ - - fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - err = drv(inode, file, VIDIOC_G_FMT, fmt2); - /* If VIDIOC_G_FMT failed, then the driver likely doesn't - support memory capture. Trying to set the memory capture - parameters would be pointless. */ - if (err < 0) { - dprintk("VIDIOCSPICT / VIDIOC_G_FMT: %d\n",err); - mem_err = -1000; /* didn't even try */ - } else if (fmt2->fmt.pix.pixelformat != - palette_to_pixelformat(pict->palette)) { - fmt2->fmt.pix.pixelformat = palette_to_pixelformat( - pict->palette); - mem_err = drv(inode, file, VIDIOC_S_FMT, fmt2); - if (mem_err < 0) - dprintk("VIDIOCSPICT / VIDIOC_S_FMT: %d\n", - mem_err); - } +static noinline int v4l1_compat_set_input( + struct video_channel *chan, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + v4l2_std_id sid = 0; - err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2); - /* If VIDIOC_G_FBUF failed, then the driver likely doesn't - support overlay. Trying to set the overlay parameters - would be quite pointless. */ - if (err < 0) { - dprintk("VIDIOCSPICT / VIDIOC_G_FBUF: %d\n",err); - ovl_err = -1000; /* didn't even try */ - } else if (fbuf2.fmt.pixelformat != - palette_to_pixelformat(pict->palette)) { - fbuf2.fmt.pixelformat = palette_to_pixelformat( - pict->palette); - ovl_err = drv(inode, file, VIDIOC_S_FBUF, &fbuf2); - if (ovl_err < 0) - dprintk("VIDIOCSPICT / VIDIOC_S_FBUF: %d\n", - ovl_err); - } - if (ovl_err < 0 && mem_err < 0) - /* ioctl failed, couldn't set either parameter */ - if (mem_err != -1000) { - err = mem_err; - } else if (ovl_err == -EPERM) { - err = 0; - } else { - err = ovl_err; - } - else - err = 0; + err = drv(inode, file, VIDIOC_S_INPUT, &chan->channel); + if (err < 0) + dprintk("VIDIOCSCHAN / VIDIOC_S_INPUT: %d\n", err); + switch (chan->norm) { + case VIDEO_MODE_PAL: + sid = V4L2_STD_PAL; + break; + case VIDEO_MODE_NTSC: + sid = V4L2_STD_NTSC; + break; + case VIDEO_MODE_SECAM: + sid = V4L2_STD_SECAM; break; } - case VIDIOCGTUNER: /* get tuner information */ - { - struct video_tuner *tun = arg; - - memset(&tun2,0,sizeof(tun2)); - err = drv(inode, file, VIDIOC_G_TUNER, &tun2); - if (err < 0) { - dprintk("VIDIOCGTUNER / VIDIOC_G_TUNER: %d\n",err); - break; - } - memcpy(tun->name, tun2.name, - min(sizeof(tun->name), sizeof(tun2.name))); - tun->name[sizeof(tun->name) - 1] = 0; - tun->rangelow = tun2.rangelow; - tun->rangehigh = tun2.rangehigh; - tun->flags = 0; - tun->mode = VIDEO_MODE_AUTO; - - for (i = 0; i < 64; i++) { - memset(&std2,0,sizeof(std2)); - std2.index = i; - if (0 != drv(inode, file, VIDIOC_ENUMSTD, &std2)) - break; - if (std2.id & V4L2_STD_PAL) - tun->flags |= VIDEO_TUNER_PAL; - if (std2.id & V4L2_STD_NTSC) - tun->flags |= VIDEO_TUNER_NTSC; - if (std2.id & V4L2_STD_SECAM) - tun->flags |= VIDEO_TUNER_SECAM; - } - - err = drv(inode, file, VIDIOC_G_STD, &sid); + if (0 != sid) { + err = drv(inode, file, VIDIOC_S_STD, &sid); if (err < 0) - dprintk("VIDIOCGTUNER / VIDIOC_G_STD: %d\n",err); - if (err == 0) { - if (sid & V4L2_STD_PAL) - tun->mode = VIDEO_MODE_PAL; - if (sid & V4L2_STD_NTSC) - tun->mode = VIDEO_MODE_NTSC; - if (sid & V4L2_STD_SECAM) - tun->mode = VIDEO_MODE_SECAM; - } - - if (tun2.capability & V4L2_TUNER_CAP_LOW) - tun->flags |= VIDEO_TUNER_LOW; - if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO) - tun->flags |= VIDEO_TUNER_STEREO_ON; - tun->signal = tun2.signal; - break; + dprintk("VIDIOCSCHAN / VIDIOC_S_STD: %d\n", err); } - case VIDIOCSTUNER: /* select a tuner input */ - { - struct video_tuner *tun = arg; - struct v4l2_tuner t; - memset(&t,0,sizeof(t)); - - t.index=tun->tuner; + return err; +} - err = drv(inode, file, VIDIOC_S_INPUT, &t); - if (err < 0) - dprintk("VIDIOCSTUNER / VIDIOC_S_INPUT: %d\n",err); +static noinline int v4l1_compat_get_picture( + struct video_picture *pict, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_format *fmt; - break; + fmt = kzalloc(sizeof(*fmt), GFP_KERNEL); + if (!fmt) { + err = -ENOMEM; + return err; } - case VIDIOCGFREQ: /* get frequency */ - { - unsigned long *freq = arg; - memset(&freq2,0,sizeof(freq2)); - freq2.tuner = 0; - err = drv(inode, file, VIDIOC_G_FREQUENCY, &freq2); - if (err < 0) - dprintk("VIDIOCGFREQ / VIDIOC_G_FREQUENCY: %d\n",err); - if (0 == err) - *freq = freq2.frequency; - break; + pict->brightness = get_v4l_control(inode, file, + V4L2_CID_BRIGHTNESS, drv); + pict->hue = get_v4l_control(inode, file, + V4L2_CID_HUE, drv); + pict->contrast = get_v4l_control(inode, file, + V4L2_CID_CONTRAST, drv); + pict->colour = get_v4l_control(inode, file, + V4L2_CID_SATURATION, drv); + pict->whiteness = get_v4l_control(inode, file, + V4L2_CID_WHITENESS, drv); + + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = drv(inode, file, VIDIOC_G_FMT, fmt); + if (err < 0) { + dprintk("VIDIOCGPICT / VIDIOC_G_FMT: %d\n", err); + goto done; } - case VIDIOCSFREQ: /* set frequency */ - { - unsigned long *freq = arg; - memset(&freq2,0,sizeof(freq2)); - drv(inode, file, VIDIOC_G_FREQUENCY, &freq2); - freq2.frequency = *freq; - err = drv(inode, file, VIDIOC_S_FREQUENCY, &freq2); - if (err < 0) - dprintk("VIDIOCSFREQ / VIDIOC_S_FREQUENCY: %d\n",err); - break; + pict->depth = ((fmt->fmt.pix.bytesperline << 3) + + (fmt->fmt.pix.width - 1)) + / fmt->fmt.pix.width; + pict->palette = pixelformat_to_palette( + fmt->fmt.pix.pixelformat); +done: + kfree(fmt); + return err; +} + +static noinline int v4l1_compat_set_picture( + struct video_picture *pict, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_framebuffer fbuf; + int mem_err = 0, ovl_err = 0; + struct v4l2_format *fmt; + + fmt = kzalloc(sizeof(*fmt), GFP_KERNEL); + if (!fmt) { + err = -ENOMEM; + return err; + } + memset(&fbuf, 0, sizeof(fbuf)); + + set_v4l_control(inode, file, + V4L2_CID_BRIGHTNESS, pict->brightness, drv); + set_v4l_control(inode, file, + V4L2_CID_HUE, pict->hue, drv); + set_v4l_control(inode, file, + V4L2_CID_CONTRAST, pict->contrast, drv); + set_v4l_control(inode, file, + V4L2_CID_SATURATION, pict->colour, drv); + set_v4l_control(inode, file, + V4L2_CID_WHITENESS, pict->whiteness, drv); + /* + * V4L1 uses this ioctl to set both memory capture and overlay + * pixel format, while V4L2 has two different ioctls for this. + * Some cards may not support one or the other, and may support + * different pixel formats for memory vs overlay. + */ + + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = drv(inode, file, VIDIOC_G_FMT, fmt); + /* If VIDIOC_G_FMT failed, then the driver likely doesn't + support memory capture. Trying to set the memory capture + parameters would be pointless. */ + if (err < 0) { + dprintk("VIDIOCSPICT / VIDIOC_G_FMT: %d\n", err); + mem_err = -1000; /* didn't even try */ + } else if (fmt->fmt.pix.pixelformat != + palette_to_pixelformat(pict->palette)) { + fmt->fmt.pix.pixelformat = palette_to_pixelformat( + pict->palette); + mem_err = drv(inode, file, VIDIOC_S_FMT, fmt); + if (mem_err < 0) + dprintk("VIDIOCSPICT / VIDIOC_S_FMT: %d\n", + mem_err); } - 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) { - dprintk("VIDIOCGAUDIO / VIDIOC_G_AUDIO: %d\n",err); - break; - } - memcpy(aud->name, aud2.name, - min(sizeof(aud->name), sizeof(aud2.name))); - aud->name[sizeof(aud->name) - 1] = 0; - aud->audio = aud2.index; - aud->flags = 0; - i = get_v4l_control(inode, file, V4L2_CID_AUDIO_VOLUME, drv); - if (i >= 0) { - aud->volume = i; - aud->flags |= VIDEO_AUDIO_VOLUME; - } - i = get_v4l_control(inode, file, V4L2_CID_AUDIO_BASS, drv); - if (i >= 0) { - aud->bass = i; - aud->flags |= VIDEO_AUDIO_BASS; - } - i = get_v4l_control(inode, file, V4L2_CID_AUDIO_TREBLE, drv); - if (i >= 0) { - aud->treble = i; - aud->flags |= VIDEO_AUDIO_TREBLE; - } - i = get_v4l_control(inode, file, V4L2_CID_AUDIO_BALANCE, drv); - if (i >= 0) { - aud->balance = i; - aud->flags |= VIDEO_AUDIO_BALANCE; - } - i = get_v4l_control(inode, file, V4L2_CID_AUDIO_MUTE, drv); - if (i >= 0) { - if (i) - aud->flags |= VIDEO_AUDIO_MUTE; - aud->flags |= VIDEO_AUDIO_MUTABLE; - } - aud->step = 1; - qctrl2.id = V4L2_CID_AUDIO_VOLUME; - if (drv(inode, file, VIDIOC_QUERYCTRL, &qctrl2) == 0 && - !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED)) - aud->step = qctrl2.step; - aud->mode = 0; - - memset(&tun2,0,sizeof(tun2)); - err = drv(inode, file, VIDIOC_G_TUNER, &tun2); - if (err < 0) { - dprintk("VIDIOCGAUDIO / VIDIOC_G_TUNER: %d\n",err); + err = drv(inode, file, VIDIOC_G_FBUF, &fbuf); + /* If VIDIOC_G_FBUF failed, then the driver likely doesn't + support overlay. Trying to set the overlay parameters + would be quite pointless. */ + if (err < 0) { + dprintk("VIDIOCSPICT / VIDIOC_G_FBUF: %d\n", err); + ovl_err = -1000; /* didn't even try */ + } else if (fbuf.fmt.pixelformat != + palette_to_pixelformat(pict->palette)) { + fbuf.fmt.pixelformat = palette_to_pixelformat( + pict->palette); + ovl_err = drv(inode, file, VIDIOC_S_FBUF, &fbuf); + if (ovl_err < 0) + dprintk("VIDIOCSPICT / VIDIOC_S_FBUF: %d\n", + ovl_err); + } + if (ovl_err < 0 && mem_err < 0) { + /* ioctl failed, couldn't set either parameter */ + if (mem_err != -1000) + err = mem_err; + else if (ovl_err == -EPERM) err = 0; + else + err = ovl_err; + } else + err = 0; + kfree(fmt); + return err; +} + +static noinline int v4l1_compat_get_tuner( + struct video_tuner *tun, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err, i; + struct v4l2_tuner tun2; + struct v4l2_standard std2; + v4l2_std_id sid; + + memset(&tun2, 0, sizeof(tun2)); + err = drv(inode, file, VIDIOC_G_TUNER, &tun2); + if (err < 0) { + dprintk("VIDIOCGTUNER / VIDIOC_G_TUNER: %d\n", err); + goto done; + } + memcpy(tun->name, tun2.name, + min(sizeof(tun->name), sizeof(tun2.name))); + tun->name[sizeof(tun->name) - 1] = 0; + tun->rangelow = tun2.rangelow; + tun->rangehigh = tun2.rangehigh; + tun->flags = 0; + tun->mode = VIDEO_MODE_AUTO; + + for (i = 0; i < 64; i++) { + memset(&std2, 0, sizeof(std2)); + std2.index = i; + if (0 != drv(inode, file, VIDIOC_ENUMSTD, &std2)) break; - } + if (std2.id & V4L2_STD_PAL) + tun->flags |= VIDEO_TUNER_PAL; + if (std2.id & V4L2_STD_NTSC) + tun->flags |= VIDEO_TUNER_NTSC; + if (std2.id & V4L2_STD_SECAM) + tun->flags |= VIDEO_TUNER_SECAM; + } - if (tun2.rxsubchans & V4L2_TUNER_SUB_LANG2) - aud->mode = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; - else if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO) - aud->mode = VIDEO_SOUND_STEREO; - else if (tun2.rxsubchans & V4L2_TUNER_SUB_MONO) - aud->mode = VIDEO_SOUND_MONO; - break; + err = drv(inode, file, VIDIOC_G_STD, &sid); + if (err < 0) + dprintk("VIDIOCGTUNER / VIDIOC_G_STD: %d\n", err); + if (err == 0) { + if (sid & V4L2_STD_PAL) + tun->mode = VIDEO_MODE_PAL; + if (sid & V4L2_STD_NTSC) + tun->mode = VIDEO_MODE_NTSC; + if (sid & V4L2_STD_SECAM) + tun->mode = VIDEO_MODE_SECAM; } - case VIDIOCSAUDIO: /* set audio controls */ - { - struct video_audio *aud = arg; - memset(&aud2,0,sizeof(aud2)); - memset(&tun2,0,sizeof(tun2)); + if (tun2.capability & V4L2_TUNER_CAP_LOW) + tun->flags |= VIDEO_TUNER_LOW; + if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO) + tun->flags |= VIDEO_TUNER_STEREO_ON; + tun->signal = tun2.signal; +done: + return err; +} - aud2.index = aud->audio; - err = drv(inode, file, VIDIOC_S_AUDIO, &aud2); - if (err < 0) { - dprintk("VIDIOCSAUDIO / VIDIOC_S_AUDIO: %d\n",err); - break; - } +static noinline int v4l1_compat_select_tuner( + struct video_tuner *tun, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_tuner t;/*84 bytes on x86_64*/ + memset(&t, 0, sizeof(t)); - set_v4l_control(inode, file, V4L2_CID_AUDIO_VOLUME, - aud->volume, drv); - set_v4l_control(inode, file, V4L2_CID_AUDIO_BASS, - aud->bass, drv); - set_v4l_control(inode, file, V4L2_CID_AUDIO_TREBLE, - aud->treble, drv); - set_v4l_control(inode, file, V4L2_CID_AUDIO_BALANCE, - aud->balance, drv); - set_v4l_control(inode, file, V4L2_CID_AUDIO_MUTE, - !!(aud->flags & VIDEO_AUDIO_MUTE), drv); - - err = drv(inode, file, VIDIOC_G_TUNER, &tun2); - if (err < 0) - dprintk("VIDIOCSAUDIO / VIDIOC_G_TUNER: %d\n",err); - if (err == 0) { - switch (aud->mode) { - default: - case VIDEO_SOUND_MONO: - case VIDEO_SOUND_LANG1: - tun2.audmode = V4L2_TUNER_MODE_MONO; - break; - case VIDEO_SOUND_STEREO: - tun2.audmode = V4L2_TUNER_MODE_STEREO; - break; - case VIDEO_SOUND_LANG2: - tun2.audmode = V4L2_TUNER_MODE_LANG2; - break; - } - err = drv(inode, file, VIDIOC_S_TUNER, &tun2); - if (err < 0) - dprintk("VIDIOCSAUDIO / VIDIOC_S_TUNER: %d\n",err); - } + t.index = tun->tuner; + + err = drv(inode, file, VIDIOC_S_INPUT, &t); + if (err < 0) + dprintk("VIDIOCSTUNER / VIDIOC_S_INPUT: %d\n", err); + return err; +} + +static noinline int v4l1_compat_get_frequency( + unsigned long *freq, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_frequency freq2; + memset(&freq2, 0, sizeof(freq2)); + + freq2.tuner = 0; + err = drv(inode, file, VIDIOC_G_FREQUENCY, &freq2); + if (err < 0) + dprintk("VIDIOCGFREQ / VIDIOC_G_FREQUENCY: %d\n", err); + if (0 == err) + *freq = freq2.frequency; + return err; +} + +static noinline int v4l1_compat_set_frequency( + unsigned long *freq, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_frequency freq2; + memset(&freq2, 0, sizeof(freq2)); + + drv(inode, file, VIDIOC_G_FREQUENCY, &freq2); + freq2.frequency = *freq; + err = drv(inode, file, VIDIOC_S_FREQUENCY, &freq2); + if (err < 0) + dprintk("VIDIOCSFREQ / VIDIOC_S_FREQUENCY: %d\n", err); + return err; +} + +static noinline int v4l1_compat_get_audio( + struct video_audio *aud, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err, i; + struct v4l2_queryctrl qctrl2; + struct v4l2_audio aud2; + struct v4l2_tuner tun2; + memset(&aud2, 0, sizeof(aud2)); + + err = drv(inode, file, VIDIOC_G_AUDIO, &aud2); + if (err < 0) { + dprintk("VIDIOCGAUDIO / VIDIOC_G_AUDIO: %d\n", err); + goto done; + } + memcpy(aud->name, aud2.name, + min(sizeof(aud->name), sizeof(aud2.name))); + aud->name[sizeof(aud->name) - 1] = 0; + aud->audio = aud2.index; + aud->flags = 0; + i = get_v4l_control(inode, file, V4L2_CID_AUDIO_VOLUME, drv); + if (i >= 0) { + aud->volume = i; + aud->flags |= VIDEO_AUDIO_VOLUME; + } + i = get_v4l_control(inode, file, V4L2_CID_AUDIO_BASS, drv); + if (i >= 0) { + aud->bass = i; + aud->flags |= VIDEO_AUDIO_BASS; + } + i = get_v4l_control(inode, file, V4L2_CID_AUDIO_TREBLE, drv); + if (i >= 0) { + aud->treble = i; + aud->flags |= VIDEO_AUDIO_TREBLE; + } + i = get_v4l_control(inode, file, V4L2_CID_AUDIO_BALANCE, drv); + if (i >= 0) { + aud->balance = i; + aud->flags |= VIDEO_AUDIO_BALANCE; + } + i = get_v4l_control(inode, file, V4L2_CID_AUDIO_MUTE, drv); + if (i >= 0) { + if (i) + aud->flags |= VIDEO_AUDIO_MUTE; + aud->flags |= VIDEO_AUDIO_MUTABLE; + } + aud->step = 1; + qctrl2.id = V4L2_CID_AUDIO_VOLUME; + if (drv(inode, file, VIDIOC_QUERYCTRL, &qctrl2) == 0 && + !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED)) + aud->step = qctrl2.step; + aud->mode = 0; + + memset(&tun2, 0, sizeof(tun2)); + err = drv(inode, file, VIDIOC_G_TUNER, &tun2); + if (err < 0) { + dprintk("VIDIOCGAUDIO / VIDIOC_G_TUNER: %d\n", err); err = 0; - break; + goto done; } - case VIDIOCMCAPTURE: /* capture a frame */ - { - struct video_mmap *mm = arg; - fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); - if (!fmt2) { - err = -ENOMEM; - break; - } - memset(&buf2,0,sizeof(buf2)); + if (tun2.rxsubchans & V4L2_TUNER_SUB_LANG2) + aud->mode = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; + else if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO) + aud->mode = VIDEO_SOUND_STEREO; + else if (tun2.rxsubchans & V4L2_TUNER_SUB_MONO) + aud->mode = VIDEO_SOUND_MONO; +done: + return err; +} - fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - err = drv(inode, file, VIDIOC_G_FMT, fmt2); - if (err < 0) { - dprintk("VIDIOCMCAPTURE / VIDIOC_G_FMT: %d\n",err); +static noinline int v4l1_compat_set_audio( + struct video_audio *aud, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_audio aud2; + struct v4l2_tuner tun2; + + memset(&aud2, 0, sizeof(aud2)); + memset(&tun2, 0, sizeof(tun2)); + + aud2.index = aud->audio; + err = drv(inode, file, VIDIOC_S_AUDIO, &aud2); + if (err < 0) { + dprintk("VIDIOCSAUDIO / VIDIOC_S_AUDIO: %d\n", err); + goto done; + } + + set_v4l_control(inode, file, V4L2_CID_AUDIO_VOLUME, + aud->volume, drv); + set_v4l_control(inode, file, V4L2_CID_AUDIO_BASS, + aud->bass, drv); + set_v4l_control(inode, file, V4L2_CID_AUDIO_TREBLE, + aud->treble, drv); + set_v4l_control(inode, file, V4L2_CID_AUDIO_BALANCE, + aud->balance, drv); + set_v4l_control(inode, file, V4L2_CID_AUDIO_MUTE, + !!(aud->flags & VIDEO_AUDIO_MUTE), drv); + + err = drv(inode, file, VIDIOC_G_TUNER, &tun2); + if (err < 0) + dprintk("VIDIOCSAUDIO / VIDIOC_G_TUNER: %d\n", err); + if (err == 0) { + switch (aud->mode) { + default: + case VIDEO_SOUND_MONO: + case VIDEO_SOUND_LANG1: + tun2.audmode = V4L2_TUNER_MODE_MONO; break; - } - if (mm->width != fmt2->fmt.pix.width || - mm->height != fmt2->fmt.pix.height || - palette_to_pixelformat(mm->format) != - fmt2->fmt.pix.pixelformat) - {/* New capture format... */ - fmt2->fmt.pix.width = mm->width; - fmt2->fmt.pix.height = mm->height; - fmt2->fmt.pix.pixelformat = - palette_to_pixelformat(mm->format); - fmt2->fmt.pix.field = V4L2_FIELD_ANY; - fmt2->fmt.pix.bytesperline = 0; - err = drv(inode, file, VIDIOC_S_FMT, fmt2); - if (err < 0) { - dprintk("VIDIOCMCAPTURE / VIDIOC_S_FMT: %d\n",err); - break; - } - } - buf2.index = mm->frame; - buf2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - err = drv(inode, file, VIDIOC_QUERYBUF, &buf2); - if (err < 0) { - dprintk("VIDIOCMCAPTURE / VIDIOC_QUERYBUF: %d\n",err); + case VIDEO_SOUND_STEREO: + tun2.audmode = V4L2_TUNER_MODE_STEREO; break; - } - err = drv(inode, file, VIDIOC_QBUF, &buf2); - if (err < 0) { - dprintk("VIDIOCMCAPTURE / VIDIOC_QBUF: %d\n",err); + case VIDEO_SOUND_LANG2: + tun2.audmode = V4L2_TUNER_MODE_LANG2; break; } - err = drv(inode, file, VIDIOC_STREAMON, &captype); + err = drv(inode, file, VIDIOC_S_TUNER, &tun2); if (err < 0) - dprintk("VIDIOCMCAPTURE / VIDIOC_STREAMON: %d\n",err); - break; + dprintk("VIDIOCSAUDIO / VIDIOC_S_TUNER: %d\n", err); } - case VIDIOCSYNC: /* wait for a frame */ - { - int *i = arg; + err = 0; +done: + return err; +} - memset(&buf2,0,sizeof(buf2)); - buf2.index = *i; - buf2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - err = drv(inode, file, VIDIOC_QUERYBUF, &buf2); - if (err < 0) { - /* No such buffer */ - dprintk("VIDIOCSYNC / VIDIOC_QUERYBUF: %d\n",err); - break; - } - if (!(buf2.flags & V4L2_BUF_FLAG_MAPPED)) { - /* Buffer is not mapped */ - err = -EINVAL; - break; - } +static noinline int v4l1_compat_capture_frame( + struct video_mmap *mm, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + enum v4l2_buf_type captype = V4L2_BUF_TYPE_VIDEO_CAPTURE; + struct v4l2_buffer buf; + struct v4l2_format *fmt; + + fmt = kzalloc(sizeof(*fmt), GFP_KERNEL); + if (!fmt) { + err = -ENOMEM; + return err; + } + memset(&buf, 0, sizeof(buf)); - /* make sure capture actually runs so we don't block forever */ - err = drv(inode, file, VIDIOC_STREAMON, &captype); + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = drv(inode, file, VIDIOC_G_FMT, fmt); + if (err < 0) { + dprintk("VIDIOCMCAPTURE / VIDIOC_G_FMT: %d\n", err); + goto done; + } + if (mm->width != fmt->fmt.pix.width || + mm->height != fmt->fmt.pix.height || + palette_to_pixelformat(mm->format) != + fmt->fmt.pix.pixelformat) { + /* New capture format... */ + fmt->fmt.pix.width = mm->width; + fmt->fmt.pix.height = mm->height; + fmt->fmt.pix.pixelformat = + palette_to_pixelformat(mm->format); + fmt->fmt.pix.field = V4L2_FIELD_ANY; + fmt->fmt.pix.bytesperline = 0; + err = drv(inode, file, VIDIOC_S_FMT, fmt); if (err < 0) { - dprintk("VIDIOCSYNC / VIDIOC_STREAMON: %d\n",err); - break; + dprintk("VIDIOCMCAPTURE / VIDIOC_S_FMT: %d\n", err); + goto done; } + } + buf.index = mm->frame; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = drv(inode, file, VIDIOC_QUERYBUF, &buf); + if (err < 0) { + dprintk("VIDIOCMCAPTURE / VIDIOC_QUERYBUF: %d\n", err); + goto done; + } + err = drv(inode, file, VIDIOC_QBUF, &buf); + if (err < 0) { + dprintk("VIDIOCMCAPTURE / VIDIOC_QBUF: %d\n", err); + goto done; + } + err = drv(inode, file, VIDIOC_STREAMON, &captype); + if (err < 0) + dprintk("VIDIOCMCAPTURE / VIDIOC_STREAMON: %d\n", err); +done: + kfree(fmt); + return err; +} - /* Loop as long as the buffer is queued, but not done */ - while ((buf2.flags & - (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)) - == V4L2_BUF_FLAG_QUEUED) - { - err = poll_one(file); - if (err < 0 || /* error or sleep was interrupted */ - err == 0) /* timeout? Shouldn't occur. */ - break; - err = drv(inode, file, VIDIOC_QUERYBUF, &buf2); - if (err < 0) - dprintk("VIDIOCSYNC / VIDIOC_QUERYBUF: %d\n",err); - } - if (!(buf2.flags & V4L2_BUF_FLAG_DONE)) /* not done */ - break; - do { - err = drv(inode, file, VIDIOC_DQBUF, &buf2); - if (err < 0) - dprintk("VIDIOCSYNC / VIDIOC_DQBUF: %d\n",err); - } while (err == 0 && buf2.index != *i); - break; +static noinline int v4l1_compat_sync( + int *i, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + enum v4l2_buf_type captype = V4L2_BUF_TYPE_VIDEO_CAPTURE; + struct v4l2_buffer buf; + struct poll_wqueues *pwq; + + memset(&buf, 0, sizeof(buf)); + buf.index = *i; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = drv(inode, file, VIDIOC_QUERYBUF, &buf); + if (err < 0) { + /* No such buffer */ + dprintk("VIDIOCSYNC / VIDIOC_QUERYBUF: %d\n", err); + goto done; + } + if (!(buf.flags & V4L2_BUF_FLAG_MAPPED)) { + /* Buffer is not mapped */ + err = -EINVAL; + goto done; } - case VIDIOCGVBIFMT: /* query VBI data capture format */ - { - struct vbi_format *fmt = arg; + /* make sure capture actually runs so we don't block forever */ + err = drv(inode, file, VIDIOC_STREAMON, &captype); + if (err < 0) { + dprintk("VIDIOCSYNC / VIDIOC_STREAMON: %d\n", err); + goto done; + } - fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); - if (!fmt2) { - err = -ENOMEM; + pwq = kmalloc(sizeof(*pwq), GFP_KERNEL); + /* Loop as long as the buffer is queued, but not done */ + while ((buf.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)) + == V4L2_BUF_FLAG_QUEUED) { + err = poll_one(file, pwq); + if (err < 0 || /* error or sleep was interrupted */ + err == 0) /* timeout? Shouldn't occur. */ break; - } - fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE; + err = drv(inode, file, VIDIOC_QUERYBUF, &buf); + if (err < 0) + dprintk("VIDIOCSYNC / VIDIOC_QUERYBUF: %d\n", err); + } + kfree(pwq); + if (!(buf.flags & V4L2_BUF_FLAG_DONE)) /* not done */ + goto done; + do { + err = drv(inode, file, VIDIOC_DQBUF, &buf); + if (err < 0) + dprintk("VIDIOCSYNC / VIDIOC_DQBUF: %d\n", err); + } while (err == 0 && buf.index != *i); +done: + return err; +} - err = drv(inode, file, VIDIOC_G_FMT, fmt2); - if (err < 0) { - dprintk("VIDIOCGVBIFMT / VIDIOC_G_FMT: %d\n", err); - break; - } - if (fmt2->fmt.vbi.sample_format != V4L2_PIX_FMT_GREY) { - err = -EINVAL; - break; - } - memset(fmt, 0, sizeof(*fmt)); - fmt->samples_per_line = fmt2->fmt.vbi.samples_per_line; - fmt->sampling_rate = fmt2->fmt.vbi.sampling_rate; - fmt->sample_format = VIDEO_PALETTE_RAW; - fmt->start[0] = fmt2->fmt.vbi.start[0]; - fmt->count[0] = fmt2->fmt.vbi.count[0]; - fmt->start[1] = fmt2->fmt.vbi.start[1]; - fmt->count[1] = fmt2->fmt.vbi.count[1]; - fmt->flags = fmt2->fmt.vbi.flags & 0x03; - break; +static noinline int v4l1_compat_get_vbi_format( + struct vbi_format *fmt, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_format *fmt2; + + fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); + if (!fmt2) { + err = -ENOMEM; + return err; } - case VIDIOCSVBIFMT: - { - struct vbi_format *fmt = arg; + fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE; - if (VIDEO_PALETTE_RAW != fmt->sample_format) { - err = -EINVAL; - break; - } + err = drv(inode, file, VIDIOC_G_FMT, fmt2); + if (err < 0) { + dprintk("VIDIOCGVBIFMT / VIDIOC_G_FMT: %d\n", err); + goto done; + } + if (fmt2->fmt.vbi.sample_format != V4L2_PIX_FMT_GREY) { + err = -EINVAL; + goto done; + } + memset(fmt, 0, sizeof(*fmt)); + fmt->samples_per_line = fmt2->fmt.vbi.samples_per_line; + fmt->sampling_rate = fmt2->fmt.vbi.sampling_rate; + fmt->sample_format = VIDEO_PALETTE_RAW; + fmt->start[0] = fmt2->fmt.vbi.start[0]; + fmt->count[0] = fmt2->fmt.vbi.count[0]; + fmt->start[1] = fmt2->fmt.vbi.start[1]; + fmt->count[1] = fmt2->fmt.vbi.count[1]; + fmt->flags = fmt2->fmt.vbi.flags & 0x03; +done: + kfree(fmt2); + return err; +} - fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); - if (!fmt2) { - err = -ENOMEM; - break; - } - fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE; - fmt2->fmt.vbi.samples_per_line = fmt->samples_per_line; - fmt2->fmt.vbi.sampling_rate = fmt->sampling_rate; - fmt2->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - fmt2->fmt.vbi.start[0] = fmt->start[0]; - fmt2->fmt.vbi.count[0] = fmt->count[0]; - fmt2->fmt.vbi.start[1] = fmt->start[1]; - fmt2->fmt.vbi.count[1] = fmt->count[1]; - fmt2->fmt.vbi.flags = fmt->flags; - err = drv(inode, file, VIDIOC_TRY_FMT, fmt2); - if (err < 0) { - dprintk("VIDIOCSVBIFMT / VIDIOC_TRY_FMT: %d\n", err); - break; - } +static noinline int v4l1_compat_set_vbi_format( + struct vbi_format *fmt, + struct inode *inode, + struct file *file, + v4l2_kioctl drv) +{ + int err; + struct v4l2_format *fmt2 = NULL; - if (fmt2->fmt.vbi.samples_per_line != fmt->samples_per_line || - fmt2->fmt.vbi.sampling_rate != fmt->sampling_rate || - fmt2->fmt.vbi.sample_format != V4L2_PIX_FMT_GREY || - fmt2->fmt.vbi.start[0] != fmt->start[0] || - fmt2->fmt.vbi.count[0] != fmt->count[0] || - fmt2->fmt.vbi.start[1] != fmt->start[1] || - fmt2->fmt.vbi.count[1] != fmt->count[1] || - fmt2->fmt.vbi.flags != fmt->flags) { - err = -EINVAL; - break; - } - err = drv(inode, file, VIDIOC_S_FMT, fmt2); - if (err < 0) - dprintk("VIDIOCSVBIFMT / VIDIOC_S_FMT: %d\n", err); - break; + if (VIDEO_PALETTE_RAW != fmt->sample_format) { + err = -EINVAL; + return err; } + fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); + if (!fmt2) { + err = -ENOMEM; + return err; + } + fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE; + fmt2->fmt.vbi.samples_per_line = fmt->samples_per_line; + fmt2->fmt.vbi.sampling_rate = fmt->sampling_rate; + fmt2->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + fmt2->fmt.vbi.start[0] = fmt->start[0]; + fmt2->fmt.vbi.count[0] = fmt->count[0]; + fmt2->fmt.vbi.start[1] = fmt->start[1]; + fmt2->fmt.vbi.count[1] = fmt->count[1]; + fmt2->fmt.vbi.flags = fmt->flags; + err = drv(inode, file, VIDIOC_TRY_FMT, fmt2); + if (err < 0) { + dprintk("VIDIOCSVBIFMT / VIDIOC_TRY_FMT: %d\n", err); + goto done; + } + + if (fmt2->fmt.vbi.samples_per_line != fmt->samples_per_line || + fmt2->fmt.vbi.sampling_rate != fmt->sampling_rate || + fmt2->fmt.vbi.sample_format != V4L2_PIX_FMT_GREY || + fmt2->fmt.vbi.start[0] != fmt->start[0] || + fmt2->fmt.vbi.count[0] != fmt->count[0] || + fmt2->fmt.vbi.start[1] != fmt->start[1] || + fmt2->fmt.vbi.count[1] != fmt->count[1] || + fmt2->fmt.vbi.flags != fmt->flags) { + err = -EINVAL; + goto done; + } + err = drv(inode, file, VIDIOC_S_FMT, fmt2); + if (err < 0) + dprintk("VIDIOCSVBIFMT / VIDIOC_S_FMT: %d\n", err); +done: + kfree(fmt2); + return err; +} + +/* + * This function is exported. + */ +int +v4l_compat_translate_ioctl(struct inode *inode, + struct file *file, + int cmd, + void *arg, + v4l2_kioctl drv) +{ + int err; + + switch (cmd) { + case VIDIOCGCAP: /* capability */ + err = v4l1_compat_get_capabilities(arg, inode, file, drv); + break; + case VIDIOCGFBUF: /* get frame buffer */ + err = v4l1_compat_get_frame_buffer(arg, inode, file, drv); + break; + case VIDIOCSFBUF: /* set frame buffer */ + err = v4l1_compat_set_frame_buffer(arg, inode, file, drv); + break; + case VIDIOCGWIN: /* get window or capture dimensions */ + err = v4l1_compat_get_win_cap_dimensions(arg, inode, file, drv); + break; + case VIDIOCSWIN: /* set window and/or capture dimensions */ + err = v4l1_compat_set_win_cap_dimensions(arg, inode, file, drv); + break; + case VIDIOCCAPTURE: /* turn on/off preview */ + err = v4l1_compat_turn_preview_on_off(arg, inode, file, drv); + break; + case VIDIOCGCHAN: /* get input information */ + err = v4l1_compat_get_input_info(arg, inode, file, drv); + break; + case VIDIOCSCHAN: /* set input */ + err = v4l1_compat_set_input(arg, inode, file, drv); + break; + case VIDIOCGPICT: /* get tone controls & partial capture format */ + err = v4l1_compat_get_picture(arg, inode, file, drv); + break; + case VIDIOCSPICT: /* set tone controls & partial capture format */ + err = v4l1_compat_set_picture(arg, inode, file, drv); + break; + case VIDIOCGTUNER: /* get tuner information */ + err = v4l1_compat_get_tuner(arg, inode, file, drv); + break; + case VIDIOCSTUNER: /* select a tuner input */ + err = v4l1_compat_select_tuner(arg, inode, file, drv); + break; + case VIDIOCGFREQ: /* get frequency */ + err = v4l1_compat_get_frequency(arg, inode, file, drv); + break; + case VIDIOCSFREQ: /* set frequency */ + err = v4l1_compat_set_frequency(arg, inode, file, drv); + break; + case VIDIOCGAUDIO: /* get audio properties/controls */ + err = v4l1_compat_get_audio(arg, inode, file, drv); + break; + case VIDIOCSAUDIO: /* set audio controls */ + err = v4l1_compat_set_audio(arg, inode, file, drv); + break; + case VIDIOCMCAPTURE: /* capture a frame */ + err = v4l1_compat_capture_frame(arg, inode, file, drv); + break; + case VIDIOCSYNC: /* wait for a frame */ + err = v4l1_compat_sync(arg, inode, file, drv); + break; + case VIDIOCGVBIFMT: /* query VBI data capture format */ + err = v4l1_compat_get_vbi_format(arg, inode, file, drv); + break; + case VIDIOCSVBIFMT: + err = v4l1_compat_set_vbi_format(arg, inode, file, drv); + break; default: err = -ENOIOCTLCMD; break; } - kfree(cap2); - kfree(fmt2); return err; } - EXPORT_SYMBOL(v4l_compat_translate_ioctl); /* diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index eab79ffdf56..fc51e4918bb 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -64,32 +64,25 @@ void *videobuf_alloc(struct videobuf_queue *q) return vb; } +#define WAITON_CONDITION (vb->state != VIDEOBUF_ACTIVE &&\ + vb->state != VIDEOBUF_QUEUED) int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr) { - int retval = 0; - DECLARE_WAITQUEUE(wait, current); - MAGIC_CHECK(vb->magic, MAGIC_BUFFER); - add_wait_queue(&vb->done, &wait); - while (vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) { - if (non_blocking) { - retval = -EAGAIN; - break; - } - set_current_state(intr ? TASK_INTERRUPTIBLE - : TASK_UNINTERRUPTIBLE); - if (vb->state == VIDEOBUF_ACTIVE || - vb->state == VIDEOBUF_QUEUED) - schedule(); - set_current_state(TASK_RUNNING); - if (intr && signal_pending(current)) { - dprintk(1, "buffer waiton: -EINTR\n"); - retval = -EINTR; - break; - } + + if (non_blocking) { + if (WAITON_CONDITION) + return 0; + else + return -EAGAIN; } - remove_wait_queue(&vb->done, &wait); - return retval; + + if (intr) + return wait_event_interruptible(vb->done, WAITON_CONDITION); + else + wait_event(vb->done, WAITON_CONDITION); + + return 0; } int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, @@ -98,29 +91,22 @@ int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, MAGIC_CHECK(vb->magic, MAGIC_BUFFER); MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - /* This is required to avoid OOPS on some cases, - since mmap_mapper() method should be called before _iolock. - On some cases, the mmap_mapper() is called only after scheduling. - */ - if (vb->memory == V4L2_MEMORY_MMAP) { - wait_event_timeout(vb->done, q->is_mmapped, - msecs_to_jiffies(100)); - if (!q->is_mmapped) { - printk(KERN_ERR - "Error: mmap_mapper() never called!\n"); - return -EINVAL; - } - } - return CALL(q, iolock, q, vb, fbuf); } +void *videobuf_queue_to_vmalloc (struct videobuf_queue *q, + struct videobuf_buffer *buf) +{ + return CALL(q, vmalloc, buf); +} +EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc); + /* --------------------------------------------------------------------- */ void videobuf_queue_core_init(struct videobuf_queue *q, struct videobuf_queue_ops *ops, - void *dev, + struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, enum v4l2_field field, @@ -144,10 +130,14 @@ void videobuf_queue_core_init(struct videobuf_queue *q, BUG_ON(!q->ops->buf_queue); BUG_ON(!q->ops->buf_release); + /* Lock is mandatory for queue_cancel to work */ + BUG_ON(!irqlock); + /* Having implementations for abstract methods are mandatory */ BUG_ON(!q->int_ops); mutex_init(&q->vb_lock); + init_waitqueue_head(&q->wait); INIT_LIST_HEAD(&q->stream); } @@ -195,19 +185,22 @@ void videobuf_queue_cancel(struct videobuf_queue *q) unsigned long flags = 0; int i; + q->streaming = 0; + q->reading = 0; + wake_up_interruptible_sync(&q->wait); + /* remove queued buffers from list */ - if (q->irqlock) - spin_lock_irqsave(q->irqlock, flags); + spin_lock_irqsave(q->irqlock, flags); for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) continue; if (q->bufs[i]->state == VIDEOBUF_QUEUED) { list_del(&q->bufs[i]->queue); q->bufs[i]->state = VIDEOBUF_ERROR; + wake_up_all(&q->bufs[i]->done); } } - if (q->irqlock) - spin_unlock_irqrestore(q->irqlock, flags); + spin_unlock_irqrestore(q->irqlock, flags); /* free all buffers + clear queue */ for (i = 0; i < VIDEO_MAX_FRAME; i++) { @@ -563,14 +556,13 @@ int videobuf_qbuf(struct videobuf_queue *q, list_add_tail(&buf->stream, &q->stream); if (q->streaming) { - if (q->irqlock) - spin_lock_irqsave(q->irqlock, flags); + spin_lock_irqsave(q->irqlock, flags); q->ops->buf_queue(q, buf); - if (q->irqlock) - spin_unlock_irqrestore(q->irqlock, flags); + spin_unlock_irqrestore(q->irqlock, flags); } dprintk(1, "qbuf: succeded\n"); retval = 0; + wake_up_interruptible_sync(&q->wait); done: mutex_unlock(&q->vb_lock); @@ -581,35 +573,88 @@ int videobuf_qbuf(struct videobuf_queue *q, return retval; } -int videobuf_dqbuf(struct videobuf_queue *q, - struct v4l2_buffer *b, int nonblocking) + +/* Locking: Caller holds q->vb_lock */ +static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock) { - struct videobuf_buffer *buf; int retval; - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - mutex_lock(&q->vb_lock); - retval = -EBUSY; - if (q->reading) { - dprintk(1, "dqbuf: Reading running...\n"); - goto done; - } - retval = -EINVAL; - if (b->type != q->type) { - dprintk(1, "dqbuf: Wrong type.\n"); +checks: + if (!q->streaming) { + dprintk(1, "next_buffer: Not streaming\n"); + retval = -EINVAL; goto done; } + if (list_empty(&q->stream)) { - dprintk(1, "dqbuf: stream running\n"); - goto done; + if (noblock) { + retval = -EAGAIN; + dprintk(2, "next_buffer: no buffers to dequeue\n"); + goto done; + } else { + dprintk(2, "next_buffer: waiting on buffer\n"); + + /* Drop lock to avoid deadlock with qbuf */ + mutex_unlock(&q->vb_lock); + + /* Checking list_empty and streaming is safe without + * locks because we goto checks to validate while + * holding locks before proceeding */ + retval = wait_event_interruptible(q->wait, + !list_empty(&q->stream) || !q->streaming); + mutex_lock(&q->vb_lock); + + if (retval) + goto done; + + goto checks; + } } + + retval = 0; + +done: + return retval; +} + + +/* Locking: Caller holds q->vb_lock */ +static int stream_next_buffer(struct videobuf_queue *q, + struct videobuf_buffer **vb, int nonblocking) +{ + int retval; + struct videobuf_buffer *buf = NULL; + + retval = stream_next_buffer_check_queue(q, nonblocking); + if (retval) + goto done; + buf = list_entry(q->stream.next, struct videobuf_buffer, stream); retval = videobuf_waiton(buf, nonblocking, 1); + if (retval < 0) + goto done; + + *vb = buf; +done: + return retval; +} + +int videobuf_dqbuf(struct videobuf_queue *q, + struct v4l2_buffer *b, int nonblocking) +{ + struct videobuf_buffer *buf = NULL; + int retval; + + MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); + + mutex_lock(&q->vb_lock); + + retval = stream_next_buffer(q, &buf, nonblocking); if (retval < 0) { - dprintk(1, "dqbuf: waiton returned %d\n", retval); + dprintk(1, "dqbuf: next_buffer error: %i\n", retval); goto done; } + switch (buf->state) { case VIDEOBUF_ERROR: dprintk(1, "dqbuf: state is error\n"); @@ -650,14 +695,13 @@ int videobuf_streamon(struct videobuf_queue *q) if (q->streaming) goto done; q->streaming = 1; - if (q->irqlock) - spin_lock_irqsave(q->irqlock, flags); + spin_lock_irqsave(q->irqlock, flags); list_for_each_entry(buf, &q->stream, stream) if (buf->state == VIDEOBUF_PREPARED) q->ops->buf_queue(q, buf); - if (q->irqlock) - spin_unlock_irqrestore(q->irqlock, flags); + spin_unlock_irqrestore(q->irqlock, flags); + wake_up_interruptible_sync(&q->wait); done: mutex_unlock(&q->vb_lock); return retval; @@ -670,7 +714,6 @@ static int __videobuf_streamoff(struct videobuf_queue *q) return -EINVAL; videobuf_queue_cancel(q); - q->streaming = 0; return 0; } @@ -712,11 +755,9 @@ static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, goto done; /* start capture & wait */ - if (q->irqlock) - spin_lock_irqsave(q->irqlock, flags); + spin_lock_irqsave(q->irqlock, flags); q->ops->buf_queue(q, q->read_buf); - if (q->irqlock) - spin_unlock_irqrestore(q->irqlock, flags); + spin_unlock_irqrestore(q->irqlock, flags); retval = videobuf_waiton(q->read_buf, 0, 0); if (0 == retval) { CALL(q, sync, q, q->read_buf); @@ -740,14 +781,13 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, { enum v4l2_field field; unsigned long flags = 0; - unsigned size, nbufs; + unsigned size = 0, nbufs = 1; int retval; MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); mutex_lock(&q->vb_lock); - nbufs = 1; size = 0; q->ops->buf_setup(q, &nbufs, &size); if (NULL == q->read_buf && @@ -778,12 +818,11 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, q->read_buf = NULL; goto done; } - if (q->irqlock) - spin_lock_irqsave(q->irqlock, flags); + spin_lock_irqsave(q->irqlock, flags); q->ops->buf_queue(q, q->read_buf); - if (q->irqlock) - spin_unlock_irqrestore(q->irqlock, flags); + spin_unlock_irqrestore(q->irqlock, flags); + q->read_off = 0; } @@ -849,12 +888,10 @@ static int __videobuf_read_start(struct videobuf_queue *q) return err; list_add_tail(&q->bufs[i]->stream, &q->stream); } - if (q->irqlock) - spin_lock_irqsave(q->irqlock, flags); + spin_lock_irqsave(q->irqlock, flags); for (i = 0; i < count; i++) q->ops->buf_queue(q, q->bufs[i]); - if (q->irqlock) - spin_unlock_irqrestore(q->irqlock, flags); + spin_unlock_irqrestore(q->irqlock, flags); q->reading = 1; return 0; } @@ -863,7 +900,6 @@ static void __videobuf_read_stop(struct videobuf_queue *q) { int i; - videobuf_queue_cancel(q); __videobuf_mmap_free(q); INIT_LIST_HEAD(&q->stream); @@ -874,7 +910,6 @@ static void __videobuf_read_stop(struct videobuf_queue *q) q->bufs[i] = NULL; } q->read_buf = NULL; - q->reading = 0; } @@ -919,7 +954,7 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q, MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - dprintk(2, "%s\n", __FUNCTION__); + dprintk(2, "%s\n", __func__); mutex_lock(&q->vb_lock); retval = -EBUSY; if (q->streaming) @@ -968,11 +1003,9 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q, if (q->read_off == q->read_buf->size) { list_add_tail(&q->read_buf->stream, &q->stream); - if (q->irqlock) - spin_lock_irqsave(q->irqlock, flags); + spin_lock_irqsave(q->irqlock, flags); q->ops->buf_queue(q, q->read_buf); - if (q->irqlock) - spin_unlock_irqrestore(q->irqlock, flags); + spin_unlock_irqrestore(q->irqlock, flags); q->read_buf = NULL; } if (retval < 0) diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index 53fed4b74ce..03a7b946bd5 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -1,5 +1,5 @@ /* - * helper functions for PCI DMA video4linux capture buffers + * helper functions for SG DMA video4linux capture buffers * * The functions expect the hardware being able to scatter gatter * (i.e. the buffers are not linear in physical memory, but fragmented @@ -24,7 +24,7 @@ #include <linux/slab.h> #include <linux/interrupt.h> -#include <linux/pci.h> +#include <linux/dma-mapping.h> #include <linux/vmalloc.h> #include <linux/pagemap.h> #include <linux/scatterlist.h> @@ -39,10 +39,10 @@ #define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \ { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); } -static int debug = 0; +static int debug; module_param(debug, int, 0644); -MODULE_DESCRIPTION("helper module to manage video4linux pci dma sg buffers"); +MODULE_DESCRIPTION("helper module to manage video4linux dma sg buffers"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_LICENSE("GPL"); @@ -119,10 +119,10 @@ videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) struct videobuf_dmabuf *videobuf_to_dma (struct videobuf_buffer *buf) { - struct videbuf_pci_sg_memory *mem=buf->priv; - BUG_ON (!mem); + struct videobuf_dma_sg_memory *mem = buf->priv; + BUG_ON(!mem); - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); + MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); return &mem->dma; } @@ -141,9 +141,14 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, dma->direction = direction; switch (dma->direction) { - case PCI_DMA_FROMDEVICE: rw = READ; break; - case PCI_DMA_TODEVICE: rw = WRITE; break; - default: BUG(); + case DMA_FROM_DEVICE: + rw = READ; + break; + case DMA_TO_DEVICE: + rw = WRITE; + break; + default: + BUG(); } first = (data & PAGE_MASK) >> PAGE_SHIFT; @@ -157,9 +162,6 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, dprintk(1,"init user [0x%lx+0x%lx => %d pages]\n", data,size,dma->nr_pages); - dma->varea = (void *) data; - - err = get_user_pages(current,current->mm, data & PAGE_MASK, dma->nr_pages, rw == READ, 1, /* force */ @@ -216,10 +218,8 @@ int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, return 0; } -int videobuf_dma_map(struct videobuf_queue* q,struct videobuf_dmabuf *dma) +int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma) { - void *dev=q->dev; - MAGIC_CHECK(dma->magic,MAGIC_DMABUF); BUG_ON(0 == dma->nr_pages); @@ -245,11 +245,11 @@ int videobuf_dma_map(struct videobuf_queue* q,struct videobuf_dmabuf *dma) return -ENOMEM; } if (!dma->bus_addr) { - dma->sglen = pci_map_sg(dev,dma->sglist, + dma->sglen = dma_map_sg(q->dev, dma->sglist, dma->nr_pages, dma->direction); if (0 == dma->sglen) { printk(KERN_WARNING - "%s: videobuf_map_sg failed\n",__FUNCTION__); + "%s: videobuf_map_sg failed\n",__func__); kfree(dma->sglist); dma->sglist = NULL; dma->sglen = 0; @@ -259,26 +259,22 @@ int videobuf_dma_map(struct videobuf_queue* q,struct videobuf_dmabuf *dma) return 0; } -int videobuf_dma_sync(struct videobuf_queue *q,struct videobuf_dmabuf *dma) +int videobuf_dma_sync(struct videobuf_queue *q, struct videobuf_dmabuf *dma) { - void *dev=q->dev; - - MAGIC_CHECK(dma->magic,MAGIC_DMABUF); + MAGIC_CHECK(dma->magic, MAGIC_DMABUF); BUG_ON(!dma->sglen); - pci_dma_sync_sg_for_cpu (dev,dma->sglist,dma->nr_pages,dma->direction); + dma_sync_sg_for_cpu(q->dev, dma->sglist, dma->nr_pages, dma->direction); return 0; } int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma) { - void *dev=q->dev; - - MAGIC_CHECK(dma->magic,MAGIC_DMABUF); + MAGIC_CHECK(dma->magic, MAGIC_DMABUF); if (!dma->sglen) return 0; - pci_unmap_sg (dev,dma->sglist,dma->nr_pages,dma->direction); + dma_unmap_sg(q->dev, dma->sglist, dma->nr_pages, dma->direction); kfree(dma->sglist); dma->sglist = NULL; @@ -301,33 +297,32 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) vfree(dma->vmalloc); dma->vmalloc = NULL; - dma->varea = NULL; if (dma->bus_addr) { dma->bus_addr = 0; } - dma->direction = PCI_DMA_NONE; + dma->direction = DMA_NONE; return 0; } /* --------------------------------------------------------------------- */ -int videobuf_pci_dma_map(struct pci_dev *pci,struct videobuf_dmabuf *dma) +int videobuf_sg_dma_map(struct device *dev, struct videobuf_dmabuf *dma) { struct videobuf_queue q; - q.dev=pci; + q.dev = dev; - return (videobuf_dma_map(&q,dma)); + return videobuf_dma_map(&q, dma); } -int videobuf_pci_dma_unmap(struct pci_dev *pci,struct videobuf_dmabuf *dma) +int videobuf_sg_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma) { struct videobuf_queue q; - q.dev=pci; + q.dev = dev; - return (videobuf_dma_unmap(&q,dma)); + return videobuf_dma_unmap(&q, dma); } /* --------------------------------------------------------------------- */ @@ -347,7 +342,7 @@ videobuf_vm_close(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; struct videobuf_queue *q = map->q; - struct videbuf_pci_sg_memory *mem; + struct videobuf_dma_sg_memory *mem; int i; dprintk(2,"vm_close %p [count=%d,vma=%08lx-%08lx]\n",map, @@ -409,18 +404,18 @@ static struct vm_operations_struct videobuf_vm_ops = }; /* --------------------------------------------------------------------- - * PCI handlers for the generic methods + * SG handlers for the generic methods */ /* Allocated area consists on 3 parts: struct video_buffer struct <driver>_buffer (cx88_buffer, saa7134_buf, ...) - struct videobuf_pci_sg_memory + struct videobuf_dma_sg_memory */ static void *__videobuf_alloc(size_t size) { - struct videbuf_pci_sg_memory *mem; + struct videobuf_dma_sg_memory *mem; struct videobuf_buffer *vb; vb = kzalloc(size+sizeof(*mem),GFP_KERNEL); @@ -431,22 +426,32 @@ static void *__videobuf_alloc(size_t size) videobuf_dma_init(&mem->dma); dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n", - __FUNCTION__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), + __func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), mem,(long)sizeof(*mem)); return vb; } +static void *__videobuf_to_vmalloc (struct videobuf_buffer *buf) +{ + struct videobuf_dma_sg_memory *mem = buf->priv; + BUG_ON(!mem); + + MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); + + return mem->dma.vmalloc; +} + static int __videobuf_iolock (struct videobuf_queue* q, struct videobuf_buffer *vb, struct v4l2_framebuffer *fbuf) { int err,pages; dma_addr_t bus; - struct videbuf_pci_sg_memory *mem=vb->priv; + struct videobuf_dma_sg_memory *mem = vb->priv; BUG_ON(!mem); - MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); + MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); switch (vb->memory) { case V4L2_MEMORY_MMAP: @@ -455,14 +460,14 @@ static int __videobuf_iolock (struct videobuf_queue* q, /* no userspace addr -- kernel bounce buffer */ pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; err = videobuf_dma_init_kernel( &mem->dma, - PCI_DMA_FROMDEVICE, + DMA_FROM_DEVICE, pages ); if (0 != err) return err; } else if (vb->memory == V4L2_MEMORY_USERPTR) { /* dma directly to userspace */ err = videobuf_dma_init_user( &mem->dma, - PCI_DMA_FROMDEVICE, + DMA_FROM_DEVICE, vb->baddr,vb->bsize ); if (0 != err) return err; @@ -473,7 +478,7 @@ static int __videobuf_iolock (struct videobuf_queue* q, locking inversion, so don't take it here */ err = videobuf_dma_init_user_locked(&mem->dma, - PCI_DMA_FROMDEVICE, + DMA_FROM_DEVICE, vb->baddr, vb->bsize); if (0 != err) return err; @@ -490,7 +495,7 @@ static int __videobuf_iolock (struct videobuf_queue* q, */ bus = (dma_addr_t)(unsigned long)fbuf->base + vb->boff; pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; - err = videobuf_dma_init_overlay(&mem->dma,PCI_DMA_FROMDEVICE, + err = videobuf_dma_init_overlay(&mem->dma, DMA_FROM_DEVICE, bus, pages); if (0 != err) return err; @@ -498,7 +503,7 @@ static int __videobuf_iolock (struct videobuf_queue* q, default: BUG(); } - err = videobuf_dma_map(q,&mem->dma); + err = videobuf_dma_map(q, &mem->dma); if (0 != err) return err; @@ -508,8 +513,8 @@ static int __videobuf_iolock (struct videobuf_queue* q, static int __videobuf_sync(struct videobuf_queue *q, struct videobuf_buffer *buf) { - struct videbuf_pci_sg_memory *mem=buf->priv; - BUG_ON (!mem); + struct videobuf_dma_sg_memory *mem = buf->priv; + BUG_ON(!mem); MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); return videobuf_dma_sync(q,&mem->dma); @@ -532,7 +537,7 @@ static int __videobuf_mmap_free(struct videobuf_queue *q) static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma) { - struct videbuf_pci_sg_memory *mem; + struct videobuf_dma_sg_memory *mem; struct videobuf_mapping *map; unsigned int first,last,size,i; int retval; @@ -547,12 +552,20 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, goto done; } + /* This function maintains backwards compatibility with V4L1 and will + * map more than one buffer if the vma length is equal to the combined + * size of multiple buffers than it will map them together. See + * VIDIOCGMBUF in the v4l spec + * + * TODO: Allow drivers to specify if they support this mode + */ + /* look for first buffer to map */ for (first = 0; first < VIDEO_MAX_FRAME; first++) { if (NULL == q->bufs[first]) continue; mem=q->bufs[first]->priv; - BUG_ON (!mem); + BUG_ON(!mem); MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); if (V4L2_MEMORY_MMAP != q->bufs[first]->memory) @@ -591,10 +604,16 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, map = kmalloc(sizeof(struct videobuf_mapping),GFP_KERNEL); if (NULL == map) goto done; - for (size = 0, i = first; i <= last; size += q->bufs[i++]->bsize) { + + size = 0; + for (i = first; i <= last; i++) { + if (NULL == q->bufs[i]) + continue; q->bufs[i]->map = map; q->bufs[i]->baddr = vma->vm_start + size; + size += q->bufs[i]->bsize; } + map->count = 1; map->start = vma->vm_start; map->end = vma->vm_end; @@ -615,8 +634,8 @@ static int __videobuf_copy_to_user ( struct videobuf_queue *q, char __user *data, size_t count, int nonblocking ) { - struct videbuf_pci_sg_memory *mem=q->read_buf->priv; - BUG_ON (!mem); + struct videobuf_dma_sg_memory *mem = q->read_buf->priv; + BUG_ON(!mem); MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); /* copy to userspace */ @@ -634,8 +653,8 @@ static int __videobuf_copy_stream ( struct videobuf_queue *q, int vbihack, int nonblocking ) { unsigned int *fc; - struct videbuf_pci_sg_memory *mem=q->read_buf->priv; - BUG_ON (!mem); + struct videobuf_dma_sg_memory *mem = q->read_buf->priv; + BUG_ON(!mem); MAGIC_CHECK(mem->magic,MAGIC_SG_MEM); if (vbihack) { @@ -658,7 +677,7 @@ static int __videobuf_copy_stream ( struct videobuf_queue *q, return count; } -static struct videobuf_qtype_ops pci_ops = { +static struct videobuf_qtype_ops sg_ops = { .magic = MAGIC_QTYPE_OPS, .alloc = __videobuf_alloc, @@ -668,23 +687,24 @@ static struct videobuf_qtype_ops pci_ops = { .mmap_mapper = __videobuf_mmap_mapper, .video_copy_to_user = __videobuf_copy_to_user, .copy_stream = __videobuf_copy_stream, + .vmalloc = __videobuf_to_vmalloc, }; -void *videobuf_pci_alloc (size_t size) +void *videobuf_sg_alloc(size_t size) { struct videobuf_queue q; /* Required to make generic handler to call __videobuf_alloc */ - q.int_ops=&pci_ops; + q.int_ops = &sg_ops; - q.msize=size; + q.msize = size; - return videobuf_alloc (&q); + return videobuf_alloc(&q); } -void videobuf_queue_pci_init(struct videobuf_queue* q, +void videobuf_queue_sg_init(struct videobuf_queue* q, struct videobuf_queue_ops *ops, - void *dev, + struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, enum v4l2_field field, @@ -692,7 +712,7 @@ void videobuf_queue_pci_init(struct videobuf_queue* q, void *priv) { videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, - priv, &pci_ops); + priv, &sg_ops); } /* --------------------------------------------------------------------- */ @@ -709,11 +729,11 @@ EXPORT_SYMBOL_GPL(videobuf_dma_sync); EXPORT_SYMBOL_GPL(videobuf_dma_unmap); EXPORT_SYMBOL_GPL(videobuf_dma_free); -EXPORT_SYMBOL_GPL(videobuf_pci_dma_map); -EXPORT_SYMBOL_GPL(videobuf_pci_dma_unmap); -EXPORT_SYMBOL_GPL(videobuf_pci_alloc); +EXPORT_SYMBOL_GPL(videobuf_sg_dma_map); +EXPORT_SYMBOL_GPL(videobuf_sg_dma_unmap); +EXPORT_SYMBOL_GPL(videobuf_sg_alloc); -EXPORT_SYMBOL_GPL(videobuf_queue_pci_init); +EXPORT_SYMBOL_GPL(videobuf_queue_sg_init); /* * Local variables: diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c index b73aba65d21..6e4d73ec685 100644 --- a/drivers/media/video/videobuf-dvb.c +++ b/drivers/media/video/videobuf-dvb.c @@ -20,9 +20,10 @@ #include <linux/fs.h> #include <linux/kthread.h> #include <linux/file.h> + #include <linux/freezer.h> -#include <media/videobuf-dma-sg.h> +#include <media/videobuf-core.h> #include <media/videobuf-dvb.h> /* ------------------------------------------------------------------ */ @@ -30,7 +31,7 @@ MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); -static unsigned int debug = 0; +static unsigned int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"enable debug messages"); @@ -45,7 +46,7 @@ static int videobuf_dvb_thread(void *data) struct videobuf_buffer *buf; unsigned long flags; int err; - struct videobuf_dmabuf *dma; + void *outp; dprintk("dvb thread started\n"); set_freezable(); @@ -66,9 +67,10 @@ static int videobuf_dvb_thread(void *data) try_to_freeze(); /* feed buffer data to demux */ - dma=videobuf_to_dma(buf); + outp = videobuf_queue_to_vmalloc (&dvb->dvbq, buf); + if (buf->state == VIDEOBUF_DONE) - dvb_dmx_swfilter(&dvb->demux, dma->vmalloc, + dvb_dmx_swfilter(&dvb->demux, outp, buf->size); /* requeue buffer */ @@ -138,14 +140,16 @@ static int videobuf_dvb_stop_feed(struct dvb_demux_feed *feed) int videobuf_dvb_register(struct videobuf_dvb *dvb, struct module *module, void *adapter_priv, - struct device *device) + struct device *device, + short *adapter_nr) { int result; mutex_init(&dvb->lock); /* register adapter */ - result = dvb_register_adapter(&dvb->adapter, dvb->name, module, device); + result = dvb_register_adapter(&dvb->adapter, dvb->name, module, device, + adapter_nr); if (result < 0) { printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n", dvb->name, result); diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index 5266ecc91da..c91e1d8e380 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -33,7 +33,7 @@ #define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \ { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); } -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers"); @@ -57,20 +57,26 @@ videobuf_vm_open(struct vm_area_struct *vma) map->count++; } -static void -videobuf_vm_close(struct vm_area_struct *vma) +static void videobuf_vm_close(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; struct videobuf_queue *q = map->q; int i; - dprintk(2,"vm_close %p [count=%u,vma=%08lx-%08lx]\n",map, - map->count,vma->vm_start,vma->vm_end); + dprintk(2,"vm_close %p [count=%u,vma=%08lx-%08lx]\n", map, + map->count, vma->vm_start, vma->vm_end); map->count--; if (0 == map->count) { - dprintk(1,"munmap %p q=%p\n",map,q); + struct videobuf_vmalloc_memory *mem; + + dprintk(1, "munmap %p q=%p\n", map, q); mutex_lock(&q->vb_lock); + + /* We need first to cancel streams, before unmapping */ + if (q->streaming) + videobuf_queue_cancel(q); + for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (NULL == q->bufs[i]) continue; @@ -78,14 +84,35 @@ videobuf_vm_close(struct vm_area_struct *vma) if (q->bufs[i]->map != map) continue; - q->ops->buf_release(q,q->bufs[i]); + mem = q->bufs[i]->priv; + if (mem) { + /* This callback is called only if kernel has + allocated memory and this memory is mmapped. + In this case, memory should be freed, + in order to do memory unmap. + */ + + MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); + + /* vfree is not atomic - can't be + called with IRQ's disabled + */ + dprintk(1, "%s: buf[%d] freeing (%p)\n", + __func__, i, mem->vmalloc); + + vfree(mem->vmalloc); + mem->vmalloc = NULL; + } q->bufs[i]->map = NULL; q->bufs[i]->baddr = 0; } - mutex_unlock(&q->vb_lock); + kfree(map); + + mutex_unlock(&q->vb_lock); } + return; } @@ -102,7 +129,7 @@ static struct vm_operations_struct videobuf_vm_ops = /* Allocated area consists on 3 parts: struct video_buffer struct <driver>_buffer (cx88_buffer, saa7134_buf, ...) - struct videobuf_pci_sg_memory + struct videobuf_dma_sg_memory */ static void *__videobuf_alloc(size_t size) @@ -116,7 +143,7 @@ static void *__videobuf_alloc(size_t size) mem->magic=MAGIC_VMAL_MEM; dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n", - __FUNCTION__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), + __func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb), mem,(long)sizeof(*mem)); return vb; @@ -126,45 +153,74 @@ static int __videobuf_iolock (struct videobuf_queue* q, struct videobuf_buffer *vb, struct v4l2_framebuffer *fbuf) { + struct videobuf_vmalloc_memory *mem = vb->priv; int pages; - struct videobuf_vmalloc_memory *mem=vb->priv; BUG_ON(!mem); - MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); + MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); - pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; + switch (vb->memory) { + case V4L2_MEMORY_MMAP: + dprintk(1, "%s memory method MMAP\n", __func__); - /* Currently, doesn't support V4L2_MEMORY_OVERLAY */ - if ((vb->memory != V4L2_MEMORY_MMAP) && - (vb->memory != V4L2_MEMORY_USERPTR) ) { - printk(KERN_ERR "Method currently unsupported.\n"); - return -EINVAL; - } + /* All handling should be done by __videobuf_mmap_mapper() */ + if (!mem->vmalloc) { + printk(KERN_ERR "memory is not alloced/mmapped.\n"); + return -EINVAL; + } + break; + case V4L2_MEMORY_USERPTR: + pages = PAGE_ALIGN(vb->size); - /* FIXME: should be tested with kernel mmap mem */ - mem->vmalloc=vmalloc_user (PAGE_ALIGN(vb->size)); - if (NULL == mem->vmalloc) { - printk(KERN_ERR "vmalloc (%d pages) failed\n",pages); - return -ENOMEM; - } + dprintk(1, "%s memory method USERPTR\n", __func__); - dprintk(1,"vmalloc is at addr 0x%08lx, size=%d\n", - (unsigned long)mem->vmalloc, - pages << PAGE_SHIFT); +#if 1 + if (vb->baddr) { + printk(KERN_ERR "USERPTR is currently not supported\n"); + return -EINVAL; + } +#endif - /* It seems that some kernel versions need to do remap *after* - the mmap() call - */ - if (mem->vma) { - int retval=remap_vmalloc_range(mem->vma, mem->vmalloc,0); - kfree(mem->vma); - mem->vma=NULL; - if (retval<0) { - dprintk(1,"mmap app bug: remap_vmalloc_range area %p error %d\n", - mem->vmalloc,retval); - return retval; + /* The only USERPTR currently supported is the one needed for + read() method. + */ + + mem->vmalloc = vmalloc_user(pages); + if (!mem->vmalloc) { + printk(KERN_ERR "vmalloc (%d pages) failed\n", pages); + return -ENOMEM; + } + dprintk(1, "vmalloc is at addr %p (%d pages)\n", + mem->vmalloc, pages); + +#if 0 + int rc; + /* Kernel userptr is used also by read() method. In this case, + there's no need to remap, since data will be copied to user + */ + if (!vb->baddr) + return 0; + + /* FIXME: to properly support USERPTR, remap should occur. + The code bellow won't work, since mem->vma = NULL + */ + /* Try to remap memory */ + rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0); + if (rc < 0) { + printk(KERN_ERR "mmap: remap failed with error %d. ", rc); + return -ENOMEM; } +#endif + + break; + case V4L2_MEMORY_OVERLAY: + default: + dprintk(1, "%s memory method OVERLAY/unknown\n", __func__); + + /* Currently, doesn't support V4L2_MEMORY_OVERLAY */ + printk(KERN_ERR "Memory method currently unsupported.\n"); + return -EINVAL; } return 0; @@ -180,6 +236,7 @@ static int __videobuf_mmap_free(struct videobuf_queue *q) { unsigned int i; + dprintk(1, "%s\n", __func__); for (i = 0; i < VIDEO_MAX_FRAME; i++) { if (q->bufs[i]) { if (q->bufs[i]->map) @@ -196,10 +253,11 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_vmalloc_memory *mem; struct videobuf_mapping *map; unsigned int first; - int retval; + int retval, pages; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - if (! (vma->vm_flags & VM_WRITE) || ! (vma->vm_flags & VM_SHARED)) + dprintk(1, "%s\n", __func__); + if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) return -EINVAL; /* look for first buffer to map */ @@ -219,46 +277,55 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, } /* create mapping + update buffer list */ - map = q->bufs[first]->map = kzalloc(sizeof(struct videobuf_mapping),GFP_KERNEL); + map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); if (NULL == map) return -ENOMEM; + q->bufs[first]->map = map; map->start = vma->vm_start; map->end = vma->vm_end; map->q = q; q->bufs[first]->baddr = vma->vm_start; - vma->vm_ops = &videobuf_vm_ops; - vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; - vma->vm_private_data = map; + mem = q->bufs[first]->priv; + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); - mem=q->bufs[first]->priv; - BUG_ON (!mem); - MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); + pages = PAGE_ALIGN(vma->vm_end - vma->vm_start); + mem->vmalloc = vmalloc_user(pages); + if (!mem->vmalloc) { + printk(KERN_ERR "vmalloc (%d pages) failed\n", pages); + goto error; + } + dprintk(1, "vmalloc is at addr %p (%d pages)\n", + mem->vmalloc, pages); /* Try to remap memory */ - retval=remap_vmalloc_range(vma, mem->vmalloc,0); - if (retval<0) { - dprintk(1,"mmap: postponing remap_vmalloc_range\n"); - - mem->vma=kmalloc(sizeof(*vma),GFP_KERNEL); - if (!mem->vma) { - kfree(map); - q->bufs[first]->map=NULL; - return -ENOMEM; - } - memcpy(mem->vma,vma,sizeof(*vma)); + retval = remap_vmalloc_range(vma, mem->vmalloc, 0); + if (retval < 0) { + printk(KERN_ERR "mmap: remap failed with error %d. ", retval); + vfree(mem->vmalloc); + goto error; } + vma->vm_ops = &videobuf_vm_ops; + vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; + vma->vm_private_data = map; + dprintk(1,"mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", - map,q,vma->vm_start,vma->vm_end, + map, q, vma->vm_start, vma->vm_end, (long int) q->bufs[first]->bsize, - vma->vm_pgoff,first); + vma->vm_pgoff, first); videobuf_vm_open(vma); - return (0); + return 0; + +error: + mem = NULL; + kfree(map); + return -ENOMEM; } static int __videobuf_copy_to_user ( struct videobuf_queue *q, @@ -320,6 +387,7 @@ static struct videobuf_qtype_ops qops = { .mmap_mapper = __videobuf_mmap_mapper, .video_copy_to_user = __videobuf_copy_to_user, .copy_stream = __videobuf_copy_stream, + .vmalloc = videobuf_to_vmalloc, }; void videobuf_queue_vmalloc_init(struct videobuf_queue* q, @@ -349,13 +417,24 @@ EXPORT_SYMBOL_GPL(videobuf_to_vmalloc); void videobuf_vmalloc_free (struct videobuf_buffer *buf) { - struct videobuf_vmalloc_memory *mem=buf->priv; - BUG_ON (!mem); + struct videobuf_vmalloc_memory *mem = buf->priv; - MAGIC_CHECK(mem->magic,MAGIC_VMAL_MEM); + /* mmapped memory can't be freed here, otherwise mmapped region + would be released, while still needed. In this case, the memory + release should happen inside videobuf_vm_close(). + So, it should free memory only if the memory were allocated for + read() operation. + */ + if ((buf->memory != V4L2_MEMORY_USERPTR) || (buf->baddr == 0)) + return; + + if (!mem) + return; + + MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); vfree(mem->vmalloc); - mem->vmalloc=NULL; + mem->vmalloc = NULL; return; } diff --git a/drivers/media/video/videocodec.c b/drivers/media/video/videocodec.c index 87951ec8254..cf24956f320 100644 --- a/drivers/media/video/videocodec.c +++ b/drivers/media/video/videocodec.c @@ -39,12 +39,13 @@ #ifdef CONFIG_PROC_FS #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <asm/uaccess.h> #endif #include "videocodec.h" -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-4)"); @@ -320,56 +321,22 @@ videocodec_unregister (const struct videocodec *codec) } #ifdef CONFIG_PROC_FS -/* ============ */ -/* procfs stuff */ -/* ============ */ - -static char *videocodec_buf = NULL; -static int videocodec_bufsize = 0; - -static int -videocodec_build_table (void) +static int proc_videocodecs_show(struct seq_file *m, void *v) { struct codec_list *h = codeclist_top; struct attached_list *a; - int i = 0, size; - - // sum up amount of slaves plus their attached masters - while (h) { - i += h->attached + 1; - h = h->next; - } -#define LINESIZE 100 - size = LINESIZE * (i + 1); - dprintk(3, "videocodec_build table: %d entries, %d bytes\n", i, - size); - - kfree(videocodec_buf); - videocodec_buf = kmalloc(size, GFP_KERNEL); - - if (!videocodec_buf) - return 0; - - i = 0; - i += scnprintf(videocodec_buf + i, size - 1, - "<S>lave or attached <M>aster name type flags magic "); - i += scnprintf(videocodec_buf + i, size -i - 1, "(connected as)\n"); + seq_printf(m, "<S>lave or attached <M>aster name type flags magic "); + seq_printf(m, "(connected as)\n"); h = codeclist_top; while (h) { - if (i > (size - LINESIZE)) - break; // security check - i += scnprintf(videocodec_buf + i, size -i -1, - "S %32s %04x %08lx %08lx (TEMPLATE)\n", + seq_printf(m, "S %32s %04x %08lx %08lx (TEMPLATE)\n", h->codec->name, h->codec->type, h->codec->flags, h->codec->magic); a = h->list; while (a) { - if (i > (size - LINESIZE)) - break; // security check - i += scnprintf(videocodec_buf + i, size -i -1, - "M %32s %04x %08lx %08lx (%s)\n", + seq_printf(m, "M %32s %04x %08lx %08lx (%s)\n", a->codec->master_data->name, a->codec->master_data->type, a->codec->master_data->flags, @@ -380,54 +347,21 @@ videocodec_build_table (void) h = h->next; } - return i; + return 0; } -//The definition: -//typedef int (read_proc_t)(char *page, char **start, off_t off, -// int count, int *eof, void *data); - -static int -videocodec_info (char *buffer, - char **buffer_location, - off_t offset, - int buffer_length, - int *eof, - void *data) +static int proc_videocodecs_open(struct inode *inode, struct file *file) { - int size; - - dprintk(3, "videocodec_info: offset: %ld, len %d / size %d\n", - offset, buffer_length, videocodec_bufsize); - - if (offset == 0) { - videocodec_bufsize = videocodec_build_table(); - } - if ((offset < 0) || (offset >= videocodec_bufsize)) { - dprintk(4, - "videocodec_info: call delivers no result, return 0\n"); - *eof = 1; - return 0; - } - - if (buffer_length < (videocodec_bufsize - offset)) { - dprintk(4, "videocodec_info: %ld needed, %d got\n", - videocodec_bufsize - offset, buffer_length); - size = buffer_length; - } else { - dprintk(4, "videocodec_info: last reading of %ld bytes\n", - videocodec_bufsize - offset); - size = videocodec_bufsize - offset; - *eof = 1; - } - - memcpy(buffer, videocodec_buf + offset, size); - /* doesn't work... */ - /* copy_to_user(buffer, videocodec_buf+offset, size); */ - /* *buffer_location = videocodec_buf+offset; */ - - return size; + return single_open(file, proc_videocodecs_show, NULL); } + +static const struct file_operations videocodecs_proc_fops = { + .owner = THIS_MODULE, + .open = proc_videocodecs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; #endif /* ===================== */ @@ -444,16 +378,8 @@ videocodec_init (void) VIDEOCODEC_VERSION); #ifdef CONFIG_PROC_FS - videocodec_buf = NULL; - videocodec_bufsize = 0; - - videocodec_proc_entry = create_proc_entry("videocodecs", 0, NULL); - if (videocodec_proc_entry) { - videocodec_proc_entry->read_proc = videocodec_info; - videocodec_proc_entry->write_proc = NULL; - videocodec_proc_entry->data = NULL; - videocodec_proc_entry->owner = THIS_MODULE; - } else { + videocodec_proc_entry = proc_create("videocodecs", 0, NULL, &videocodecs_proc_fops); + if (!videocodec_proc_entry) { dprintk(1, KERN_ERR "videocodec: can't init procfs.\n"); } #endif @@ -465,7 +391,6 @@ videocodec_exit (void) { #ifdef CONFIG_PROC_FS remove_proc_entry("videocodecs", NULL); - kfree(videocodec_buf); #endif } diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 0d9b63762a4..31e8af0ba27 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -18,14 +18,14 @@ #define dbgarg(cmd, fmt, arg...) \ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { \ - printk (KERN_DEBUG "%s: ", vfd->name); \ + printk(KERN_DEBUG "%s: ", vfd->name); \ v4l_printk_ioctl(cmd); \ - printk (KERN_DEBUG "%s: " fmt, vfd->name, ## arg); \ + printk(" " fmt, ## arg); \ } #define dbgarg2(fmt, arg...) \ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ - printk (KERN_DEBUG "%s: " fmt, vfd->name, ## arg); + printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg); #include <linux/module.h> #include <linux/types.h> @@ -378,38 +378,45 @@ static const char *v4l2_int_ioctls[] = { external ioctl messages as well as internal V4L ioctl */ void v4l_printk_ioctl(unsigned int cmd) { - char *dir; + char *dir, *type; - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: dir = "--"; break; - case _IOC_READ: dir = "r-"; break; - case _IOC_WRITE: dir = "-w"; break; - case _IOC_READ | _IOC_WRITE: dir = "rw"; break; - default: dir = "*ERR*"; break; - } switch (_IOC_TYPE(cmd)) { case 'd': - printk("v4l2_int ioctl %s, dir=%s (0x%08x)\n", - (_IOC_NR(cmd) < V4L2_INT_IOCTLS) ? - v4l2_int_ioctls[_IOC_NR(cmd)] : "UNKNOWN", dir, cmd); - break; + if (_IOC_NR(cmd) >= V4L2_INT_IOCTLS) { + type = "v4l2_int"; + break; + } + printk("%s", v4l2_int_ioctls[_IOC_NR(cmd)]); + return; #ifdef CONFIG_VIDEO_V4L1_COMPAT case 'v': - printk("v4l1 ioctl %s, dir=%s (0x%08x)\n", - (_IOC_NR(cmd) < V4L1_IOCTLS) ? - v4l1_ioctls[_IOC_NR(cmd)] : "UNKNOWN", dir, cmd); - break; + if (_IOC_NR(cmd) >= V4L1_IOCTLS) { + type = "v4l1"; + break; + } + printk("%s", v4l1_ioctls[_IOC_NR(cmd)]); + return; #endif case 'V': - printk("v4l2 ioctl %s, dir=%s (0x%08x)\n", - (_IOC_NR(cmd) < V4L2_IOCTLS) ? - v4l2_ioctls[_IOC_NR(cmd)] : "UNKNOWN", dir, cmd); - break; - + if (_IOC_NR(cmd) >= V4L2_IOCTLS) { + type = "v4l2"; + break; + } + printk("%s", v4l2_ioctls[_IOC_NR(cmd)]); + return; default: - printk("unknown ioctl '%c', dir=%s, #%d (0x%08x)\n", - _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd); + type = "unknown"; + } + + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: dir = "--"; break; + case _IOC_READ: dir = "r-"; break; + case _IOC_WRITE: dir = "-w"; break; + case _IOC_READ | _IOC_WRITE: dir = "rw"; break; + default: dir = "*ERR*"; break; } + printk("%s ioctl '%c', dir=%s, #%d (0x%08x)", + type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd); } EXPORT_SYMBOL(v4l_printk_ioctl); @@ -774,6 +781,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, if ( (vfd->debug & V4L2_DEBUG_IOCTL) && !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { v4l_print_ioctl(vfd->name, cmd); + printk("\n"); } #ifdef CONFIG_VIDEO_V4L1_COMPAT @@ -1853,12 +1861,20 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, dbgarg (cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision); break; } + default: + { + if (!vfd->vidioc_default) + break; + ret = vfd->vidioc_default(file, fh, cmd, arg); + break; + } } /* switch */ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { if (ret<0) { - printk ("%s: err:\n", vfd->name); + printk("%s: err: on ", vfd->name); v4l_print_ioctl(vfd->name, cmd); + printk("\n"); } } @@ -2019,7 +2035,7 @@ int video_register_device(struct video_device *vfd, int type, int nr) break; default: printk(KERN_ERR "%s called with unknown type: %d\n", - __FUNCTION__, type); + __func__, type); return -1; } @@ -2057,7 +2073,7 @@ int video_register_device(struct video_device *vfd, int type, int nr) ret = device_register(&vfd->class_dev); if (ret < 0) { printk(KERN_ERR "%s: device_register failed\n", - __FUNCTION__); + __func__); goto fail_minor; } diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index 5bb75294b5a..d545c98dd5e 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -333,7 +333,7 @@ struct vino_settings { * * Use non-zero value to enable conversion. */ -static int vino_pixel_conversion = 0; +static int vino_pixel_conversion; module_param_named(pixelconv, vino_pixel_conversion, int, 0); @@ -4370,8 +4370,8 @@ static int vino_ioctl(struct inode *inode, struct file *file, /* Initialization and cleanup */ -// __initdata -static int vino_init_stage = 0; +/* __initdata */ +static int vino_init_stage; static const struct file_operations vino_fops = { .owner = THIS_MODULE, @@ -4385,8 +4385,8 @@ static const struct file_operations vino_fops = { static struct video_device v4l_device_template = { .name = "NOT SET", - //.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | - // VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY + /*.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE | */ + /* VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY */ .fops = &vino_fops, .minor = -1, }; diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 1db067c0281..b1e9592acb9 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -146,8 +146,6 @@ struct vivi_buffer { struct vivi_dmaqueue { struct list_head active; - struct list_head queued; - struct timer_list timeout; /* thread for generating video stream*/ struct task_struct *kthread; @@ -162,8 +160,8 @@ static LIST_HEAD(vivi_devlist); struct vivi_dev { struct list_head vivi_devlist; - struct mutex lock; spinlock_t slock; + struct mutex mutex; int users; @@ -322,24 +320,26 @@ static void gen_line(char *basep, int inipos, int wmax, end: return; } + static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) { int h , pos = 0; int hmax = buf->vb.height; int wmax = buf->vb.width; struct timeval ts; - char *tmpbuf = kmalloc(wmax * 2, GFP_KERNEL); + char *tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC); void *vbuf = videobuf_to_vmalloc(&buf->vb); if (!tmpbuf) return; + if (!vbuf) + return; + for (h = 0; h < hmax; h++) { gen_line(tmpbuf, 0, wmax, hmax, h, dev->mv_count, dev->timestr); - /* FIXME: replacing to __copy_to_user */ - if (copy_to_user(vbuf + pos, tmpbuf, wmax * 2) != 0) - dprintk(dev, 2, "vivifill copy_to_user failed.\n"); + memcpy(vbuf + pos, tmpbuf, wmax * 2); pos += wmax*2; } @@ -372,107 +372,71 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) dev->timestr, (unsigned long)tmpbuf, pos); /* Advice that buffer was filled */ - buf->vb.state = VIDEOBUF_DONE; buf->vb.field_count++; do_gettimeofday(&ts); buf->vb.ts = ts; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + buf->vb.state = VIDEOBUF_DONE; } -static int restart_video_queue(struct vivi_dmaqueue *dma_q); - -static void vivi_thread_tick(struct vivi_dmaqueue *dma_q) +static void vivi_thread_tick(struct vivi_fh *fh) { - struct vivi_buffer *buf; - struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq); + struct vivi_buffer *buf; + struct vivi_dev *dev = fh->dev; + struct vivi_dmaqueue *dma_q = &dev->vidq; - int bc; + unsigned long flags = 0; - spin_lock(&dev->slock); - /* Announces videobuf that all went ok */ - for (bc = 0;; bc++) { - if (list_empty(&dma_q->active)) { - dprintk(dev, 1, "No active queue to serve\n"); - break; - } + dprintk(dev, 1, "Thread tick\n"); - buf = list_entry(dma_q->active.next, - struct vivi_buffer, vb.queue); + spin_lock_irqsave(&dev->slock, flags); + if (list_empty(&dma_q->active)) { + dprintk(dev, 1, "No active queue to serve\n"); + goto unlock; + } - /* Nobody is waiting something to be done, just return */ - if (!waitqueue_active(&buf->vb.done)) { - mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); - spin_unlock(&dev->slock); - return; - } + buf = list_entry(dma_q->active.next, + struct vivi_buffer, vb.queue); + + /* Nobody is waiting on this buffer, return */ + if (!waitqueue_active(&buf->vb.done)) + goto unlock; - do_gettimeofday(&buf->vb.ts); - dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i); + list_del(&buf->vb.queue); - /* Fill buffer */ - vivi_fillbuff(dev, buf); + do_gettimeofday(&buf->vb.ts); - if (list_empty(&dma_q->active)) { - del_timer(&dma_q->timeout); - } else { - mod_timer(&dma_q->timeout, jiffies + BUFFER_TIMEOUT); - } - } - if (bc != 1) - dprintk(dev, 1, "%s: %d buffers handled (should be 1)\n", - __FUNCTION__, bc); - spin_unlock(&dev->slock); + /* Fill buffer */ + vivi_fillbuff(dev, buf); + dprintk(dev, 1, "filled buffer %p\n", buf); + + wake_up(&buf->vb.done); + dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i); +unlock: + spin_unlock_irqrestore(&dev->slock, flags); + return; } #define frames_to_ms(frames) \ ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR) -static void vivi_sleep(struct vivi_dmaqueue *dma_q) +static void vivi_sleep(struct vivi_fh *fh) { - struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq); - int timeout, running_time; + struct vivi_dev *dev = fh->dev; + struct vivi_dmaqueue *dma_q = &dev->vidq; + int timeout; DECLARE_WAITQUEUE(wait, current); - dprintk(dev, 1, "%s dma_q=0x%08lx\n", __FUNCTION__, + dprintk(dev, 1, "%s dma_q=0x%08lx\n", __func__, (unsigned long)dma_q); add_wait_queue(&dma_q->wq, &wait); if (kthread_should_stop()) goto stop_task; - running_time = jiffies - dma_q->ini_jiffies; - dma_q->frame++; - /* Calculate time to wake up */ - timeout = msecs_to_jiffies(frames_to_ms(dma_q->frame)) - running_time; - - if (timeout > msecs_to_jiffies(frames_to_ms(2)) || timeout <= 0) { - int old = dma_q->frame; - int nframes; - - dma_q->frame = (jiffies_to_msecs(running_time) / - frames_to_ms(1)) + 1; - - timeout = msecs_to_jiffies(frames_to_ms(dma_q->frame)) - - running_time; - - if (unlikely (timeout <= 0)) - timeout = 1; - - nframes = (dma_q->frame > old)? - dma_q->frame - old : old - dma_q->frame; - - dprintk(dev, 1, "%ld: %s %d frames. " - "Current frame is %d. Will sleep for %d jiffies\n", - jiffies, - (dma_q->frame > old)? "Underrun, losed" : "Overrun of", - nframes, dma_q->frame, timeout); - } else - dprintk(dev, 1, "will sleep for %d jiffies\n", timeout); + timeout = msecs_to_jiffies(frames_to_ms(1)); - vivi_thread_tick(dma_q); + vivi_thread_tick(fh); schedule_timeout_interruptible(timeout); @@ -483,16 +447,15 @@ stop_task: static int vivi_thread(void *data) { - struct vivi_dmaqueue *dma_q = data; - struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq); + struct vivi_fh *fh = data; + struct vivi_dev *dev = fh->dev; dprintk(dev, 1, "thread started\n"); - mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); set_freezable(); for (;;) { - vivi_sleep(dma_q); + vivi_sleep(fh); if (kthread_should_stop()) break; @@ -501,16 +464,17 @@ static int vivi_thread(void *data) return 0; } -static int vivi_start_thread(struct vivi_dmaqueue *dma_q) +static int vivi_start_thread(struct vivi_fh *fh) { - struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq); + struct vivi_dev *dev = fh->dev; + struct vivi_dmaqueue *dma_q = &dev->vidq; dma_q->frame = 0; dma_q->ini_jiffies = jiffies; - dprintk(dev, 1, "%s\n", __FUNCTION__); + dprintk(dev, 1, "%s\n", __func__); - dma_q->kthread = kthread_run(vivi_thread, dma_q, "vivi"); + dma_q->kthread = kthread_run(vivi_thread, fh, "vivi"); if (IS_ERR(dma_q->kthread)) { printk(KERN_ERR "vivi: kernel_thread() failed\n"); @@ -519,7 +483,7 @@ static int vivi_start_thread(struct vivi_dmaqueue *dma_q) /* Wakes thread */ wake_up_interruptible(&dma_q->wq); - dprintk(dev, 1, "returning from %s\n", __FUNCTION__); + dprintk(dev, 1, "returning from %s\n", __func__); return 0; } @@ -527,7 +491,7 @@ static void vivi_stop_thread(struct vivi_dmaqueue *dma_q) { struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq); - dprintk(dev, 1, "%s\n", __FUNCTION__); + dprintk(dev, 1, "%s\n", __func__); /* shutdown control thread */ if (dma_q->kthread) { kthread_stop(dma_q->kthread); @@ -535,91 +499,6 @@ static void vivi_stop_thread(struct vivi_dmaqueue *dma_q) } } -static int restart_video_queue(struct vivi_dmaqueue *dma_q) -{ - struct vivi_dev *dev = container_of(dma_q, struct vivi_dev, vidq); - struct vivi_buffer *buf, *prev; - - dprintk(dev, 1, "%s dma_q=0x%08lx\n", __FUNCTION__, - (unsigned long)dma_q); - - if (!list_empty(&dma_q->active)) { - buf = list_entry(dma_q->active.next, - struct vivi_buffer, vb.queue); - dprintk(dev, 2, "restart_queue [%p/%d]: restart dma\n", - buf, buf->vb.i); - - dprintk(dev, 1, "Restarting video dma\n"); - vivi_stop_thread(dma_q); - - /* cancel all outstanding capture / vbi requests */ - list_for_each_entry_safe(buf, prev, &dma_q->active, vb.queue) { - list_del(&buf->vb.queue); - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); - } - mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); - - return 0; - } - - prev = NULL; - for (;;) { - if (list_empty(&dma_q->queued)) - return 0; - buf = list_entry(dma_q->queued.next, - struct vivi_buffer, vb.queue); - if (NULL == prev) { - list_del(&buf->vb.queue); - list_add_tail(&buf->vb.queue, &dma_q->active); - - dprintk(dev, 1, "Restarting video dma\n"); - vivi_stop_thread(dma_q); - vivi_start_thread(dma_q); - - buf->vb.state = VIDEOBUF_ACTIVE; - mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); - dprintk(dev, 2, - "[%p/%d] restart_queue - first active\n", - buf, buf->vb.i); - - } else if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_del(&buf->vb.queue); - list_add_tail(&buf->vb.queue, &dma_q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - dprintk(dev, 2, - "[%p/%d] restart_queue - move to active\n", - buf, buf->vb.i); - } else { - return 0; - } - prev = buf; - } -} - -static void vivi_vid_timeout(unsigned long data) -{ - struct vivi_dev *dev = (struct vivi_dev *)data; - struct vivi_dmaqueue *vidq = &dev->vidq; - struct vivi_buffer *buf; - - spin_lock(&dev->slock); - - while (!list_empty(&vidq->active)) { - buf = list_entry(vidq->active.next, - struct vivi_buffer, vb.queue); - list_del(&buf->vb.queue); - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); - printk(KERN_INFO "vivi/0: [%p/%d] timeout\n", buf, buf->vb.i); - } - restart_video_queue(vidq); - - spin_unlock(&dev->slock); -} - /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ @@ -637,7 +516,7 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) while (*size * *count > vid_limit * 1024 * 1024) (*count)--; - dprintk(dev, 1, "%s, count=%d, size=%d\n", __FUNCTION__, + dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__, *count, *size); return 0; @@ -648,13 +527,13 @@ static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf) struct vivi_fh *fh = vq->priv_data; struct vivi_dev *dev = fh->dev; - dprintk(dev, 1, "%s\n", __FUNCTION__); + dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state); if (in_interrupt()) BUG(); - videobuf_waiton(&buf->vb, 0, 0); videobuf_vmalloc_free(&buf->vb); + dprintk(dev, 1, "free_buffer: freed\n"); buf->vb.state = VIDEOBUF_NEEDS_INIT; } @@ -667,28 +546,25 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, struct vivi_fh *fh = vq->priv_data; struct vivi_dev *dev = fh->dev; struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); - int rc, init_buffer = 0; + int rc; - dprintk(dev, 1, "%s, field=%d\n", __FUNCTION__, field); + dprintk(dev, 1, "%s, field=%d\n", __func__, field); BUG_ON(NULL == fh->fmt); + if (fh->width < 48 || fh->width > norm_maxw() || fh->height < 32 || fh->height > norm_maxh()) return -EINVAL; + buf->vb.size = fh->width*fh->height*2; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; - if (buf->fmt != fh->fmt || - buf->vb.width != fh->width || - buf->vb.height != fh->height || - buf->vb.field != field) { - buf->fmt = fh->fmt; - buf->vb.width = fh->width; - buf->vb.height = fh->height; - buf->vb.field = field; - init_buffer = 1; - } + /* These properties only change when queue is idle, see s_fmt */ + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { rc = videobuf_iolock(vq, &buf->vb, NULL); @@ -711,45 +587,12 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); struct vivi_fh *fh = vq->priv_data; struct vivi_dev *dev = fh->dev; - struct vivi_dmaqueue *vidq = &dev->vidq; - struct vivi_buffer *prev; - - if (!list_empty(&vidq->queued)) { - dprintk(dev, 1, "adding vb queue=0x%08lx\n", - (unsigned long)&buf->vb.queue); - list_add_tail(&buf->vb.queue, &vidq->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(dev, 2, "[%p/%d] buffer_queue - append to queued\n", - buf, buf->vb.i); - } else if (list_empty(&vidq->active)) { - list_add_tail(&buf->vb.queue, &vidq->active); - - buf->vb.state = VIDEOBUF_ACTIVE; - mod_timer(&vidq->timeout, jiffies+BUFFER_TIMEOUT); - dprintk(dev, 2, "[%p/%d] buffer_queue - first active\n", - buf, buf->vb.i); - - vivi_start_thread(vidq); - } else { - prev = list_entry(vidq->active.prev, - struct vivi_buffer, vb.queue); - if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &vidq->active); - buf->vb.state = VIDEOBUF_ACTIVE; - dprintk(dev, 2, - "[%p/%d] buffer_queue - append to active\n", - buf, buf->vb.i); - - } else { - list_add_tail(&buf->vb.queue, &vidq->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(dev, 2, - "[%p/%d] buffer_queue - first queued\n", - buf, buf->vb.i); - } - } + struct vivi_dmaqueue *vidq = &dev->vidq; + + dprintk(dev, 1, "%s\n", __func__); + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); } static void buffer_release(struct videobuf_queue *vq, @@ -758,11 +601,8 @@ static void buffer_release(struct videobuf_queue *vq, struct vivi_buffer *buf = container_of(vb, struct vivi_buffer, vb); struct vivi_fh *fh = vq->priv_data; struct vivi_dev *dev = (struct vivi_dev *)fh->dev; - struct vivi_dmaqueue *vidq = &dev->vidq; - - dprintk(dev, 1, "%s\n", __FUNCTION__); - vivi_stop_thread(vidq); + dprintk(dev, 1, "%s\n", __func__); free_buffer(vq, buf); } @@ -869,17 +709,31 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { struct vivi_fh *fh = priv; + struct videobuf_queue *q = &fh->vb_vidq; + int ret = vidioc_try_fmt_cap(file, fh, f); if (ret < 0) return (ret); + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + dprintk(fh->dev, 1, "%s queue busy\n", __func__); + ret = -EBUSY; + goto out; + } + fh->fmt = &format; fh->width = f->fmt.pix.width; fh->height = f->fmt.pix.height; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; - return (0); + ret = 0; +out: + mutex_unlock(&q->vb_lock); + + return (ret); } static int vidioc_reqbufs(struct file *file, void *priv, @@ -1036,6 +890,7 @@ static int vivi_open(struct inode *inode, struct file *file) struct vivi_dev *dev; struct vivi_fh *fh; int i; + int retval = 0; printk(KERN_DEBUG "vivi: open called (minor=%d)\n", minor); @@ -1045,9 +900,15 @@ static int vivi_open(struct inode *inode, struct file *file) return -ENODEV; found: - /* If more than one user, mutex should be added */ + mutex_lock(&dev->mutex); dev->users++; + if (dev->users > 1) { + dev->users--; + retval = -EBUSY; + goto unlock; + } + dprintk(dev, 1, "open minor=%d type=%s users=%d\n", minor, v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); @@ -1055,8 +916,13 @@ found: fh = kzalloc(sizeof(*fh), GFP_KERNEL); if (NULL == fh) { dev->users--; - return -ENOMEM; + retval = -ENOMEM; + goto unlock; } +unlock: + mutex_unlock(&dev->mutex); + if (retval) + return retval; file->private_data = fh; fh->dev = dev; @@ -1084,6 +950,8 @@ found: NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, sizeof(struct vivi_buffer), fh); + vivi_start_thread(fh); + return 0; } @@ -1106,7 +974,7 @@ vivi_poll(struct file *file, struct poll_table_struct *wait) struct vivi_dev *dev = fh->dev; struct videobuf_queue *q = &fh->vb_vidq; - dprintk(dev, 1, "%s\n", __FUNCTION__); + dprintk(dev, 1, "%s\n", __func__); if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) return POLLERR; @@ -1128,7 +996,9 @@ static int vivi_close(struct inode *inode, struct file *file) kfree(fh); + mutex_lock(&dev->mutex); dev->users--; + mutex_unlock(&dev->mutex); dprintk(dev, 1, "close called (minor=%d, users=%d)\n", minor, dev->users); @@ -1182,6 +1052,7 @@ static const struct file_operations vivi_fops = { .read = vivi_read, .poll = vivi_poll, .ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .compat_ioctl = v4l_compat_ioctl32, .mmap = vivi_mmap, .llseek = no_llseek, }; @@ -1236,16 +1107,11 @@ static int __init vivi_init(void) /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); init_waitqueue_head(&dev->vidq.wq); /* initialize locks */ - mutex_init(&dev->lock); spin_lock_init(&dev->slock); - - dev->vidq.timeout.function = vivi_vid_timeout; - dev->vidq.timeout.data = (unsigned long)dev; - init_timer(&dev->vidq.timeout); + mutex_init(&dev->mutex); vfd = video_device_alloc(); if (NULL == vfd) diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c index a9133858e91..35293029da0 100644 --- a/drivers/media/video/vpx3220.c +++ b/drivers/media/video/vpx3220.c @@ -40,7 +40,7 @@ #define I2C_VPX3220 0x86 #define VPX3220_DEBUG KERN_DEBUG "vpx3220: " -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index 08aaae07c7e..33f702698a5 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -61,10 +61,10 @@ #include <media/v4l2-common.h> #include <linux/parport.h> -//#define DEBUG // Undef me for production +/*#define DEBUG*/ /* Undef me for production */ #ifdef DEBUG -#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __FUNCTION__ , ##a) +#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __func__ , ##a) #else #define DPRINTF(x...) #endif @@ -134,7 +134,7 @@ MODULE_PARM_DESC(pardev, "pardev: where to search for\n\ \tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n\ \tcam 1 to parport3 and search every parport for cam 2 etc..."); -static int parmode = 0; +static int parmode; module_param(parmode, int, 0); MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp"); @@ -188,7 +188,9 @@ static const struct file_operations w9966_fops = { .open = video_exclusive_open, .release = video_exclusive_release, .ioctl = w9966_v4l_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .read = w9966_v4l_read, .llseek = no_llseek, }; diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c index 2ae1430f5f7..840522442d0 100644 --- a/drivers/media/video/w9968cf.c +++ b/drivers/media/video/w9968cf.c @@ -3461,7 +3461,9 @@ static const struct file_operations w9968cf_fops = { .release = w9968cf_release, .read = w9968cf_read, .ioctl = w9968cf_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .mmap = w9968cf_mmap, .llseek = no_llseek, }; @@ -3481,7 +3483,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) enum w9968cf_model_id mod_id; struct list_head* ptr; u8 sc = 0; /* number of simultaneous cameras */ - static unsigned short dev_nr = 0; /* we are handling device number n */ + static unsigned short dev_nr; /* 0 - we are handling device number n */ if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[0].idVendor && le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[0].idProduct) diff --git a/drivers/media/video/w9968cf.h b/drivers/media/video/w9968cf.h index ec7696e8f1f..3c95316bc03 100644 --- a/drivers/media/video/w9968cf.h +++ b/drivers/media/video/w9968cf.h @@ -298,7 +298,7 @@ struct w9968cf_device { dev_warn(&cam->dev, fmt "\n", ## args); \ else if ((level) >= 5) \ dev_info(&cam->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args); \ + __func__, __LINE__ , ## args); \ } \ } /* For generic kernel (not device specific) messages */ @@ -309,7 +309,7 @@ struct w9968cf_device { if ((level) >= 1 && (level) <= 4) \ pr_info("w9968cf: " fmt "\n", ## args); \ else if ((level) >= 5) \ - pr_debug("w9968cf: [%s:%d] " fmt "\n", __FUNCTION__, \ + pr_debug("w9968cf: [%s:%d] " fmt "\n", __func__, \ __LINE__ , ## args); \ } \ } @@ -321,7 +321,7 @@ struct w9968cf_device { #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->dev, "[%s:%d] " fmt "\n", __FUNCTION__, __LINE__ , ## args); +dev_info(&cam->dev, "[%s:%d] " fmt "\n", __func__, __LINE__ , ## args); #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */ diff --git a/drivers/media/video/zc0301/zc0301.h b/drivers/media/video/zc0301/zc0301.h index a2de50efa31..7bbab541a30 100644 --- a/drivers/media/video/zc0301/zc0301.h +++ b/drivers/media/video/zc0301/zc0301.h @@ -160,7 +160,7 @@ do { \ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ else if ((level) >= 3) \ dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ - __FILE__, __FUNCTION__, __LINE__ , ## args); \ + __FILE__, __func__, __LINE__ , ## args); \ } \ } while (0) # define KDBG(level, fmt, args...) \ @@ -170,7 +170,7 @@ do { \ pr_info("zc0301: " fmt "\n", ## args); \ else if ((level) == 3) \ pr_debug("sn9c102: [%s:%s:%d] " fmt "\n", __FILE__, \ - __FUNCTION__, __LINE__ , ## args); \ + __func__, __LINE__ , ## args); \ } \ } while (0) # define V4LDBG(level, name, cmd) \ @@ -186,7 +186,7 @@ do { \ #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __FUNCTION__, \ +dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __func__, \ __LINE__ , ## args) #undef PDBGG diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index 2c5665c8244..363dd2b9475 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -1925,7 +1925,9 @@ static const struct file_operations zc0301_fops = { .open = zc0301_open, .release = zc0301_release, .ioctl = zc0301_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .read = zc0301_read, .poll = zc0301_poll, .mmap = zc0301_mmap, @@ -1939,7 +1941,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) { struct usb_device *udev = interface_to_usbdev(intf); struct zc0301_device* cam; - static unsigned int dev_nr = 0; + static unsigned int dev_nr; unsigned int i; int err = 0; diff --git a/drivers/media/video/zoran.h b/drivers/media/video/zoran.h index 498a43c1f2b..81cc3b00a07 100644 --- a/drivers/media/video/zoran.h +++ b/drivers/media/video/zoran.h @@ -243,10 +243,8 @@ struct zoran_format { #ifdef CONFIG_VIDEO_V4L1_COMPAT int palette; #endif -#ifdef CONFIG_VIDEO_V4L2 __u32 fourcc; int colorspace; -#endif int depth; __u32 flags; __u32 vfespfr; @@ -271,20 +269,6 @@ struct zoran_v4l_settings { const struct zoran_format *format; /* capture format */ }; -/* whoops, this one is undeclared if !v4l2 */ -#ifndef CONFIG_VIDEO_V4L2 -struct v4l2_jpegcompression { - int quality; - int APPn; - int APP_len; - char APP_data[60]; - int COM_len; - char COM_data[60]; - __u32 jpeg_markers; - __u8 reserved[116]; -}; -#endif - /* jpg-capture/-playback settings */ struct zoran_jpg_settings { int decimation; /* this bit is used to set everything to default */ diff --git a/drivers/media/video/zoran_card.c b/drivers/media/video/zoran_card.c index 690281bb59e..006d48847e2 100644 --- a/drivers/media/video/zoran_card.c +++ b/drivers/media/video/zoran_card.c @@ -83,7 +83,7 @@ MODULE_PARM_DESC(decoder, "i2c TV decoder"); or set in in a VIDIOCSFBUF ioctl */ -static unsigned long vidmem = 0; /* Video memory base address */ +static unsigned long vidmem; /* default = 0 - Video memory base address */ module_param(vidmem, ulong, 0444); MODULE_PARM_DESC(vidmem, "Default video memory base address"); @@ -91,7 +91,7 @@ MODULE_PARM_DESC(vidmem, "Default video memory base address"); Default input and video norm at startup of the driver. */ -static unsigned int default_input = 0; /* 0=Composite, 1=S-Video */ +static unsigned int default_input; /* default 0 = Composite, 1 = S-Video */ module_param(default_input, uint, 0444); MODULE_PARM_DESC(default_input, "Default input (0=Composite, 1=S-Video, 2=Internal)"); @@ -101,7 +101,7 @@ module_param(default_mux, int, 0644); MODULE_PARM_DESC(default_mux, "Default 6 Eyes mux setting (Input selection)"); -static int default_norm = 0; /* 0=PAL, 1=NTSC 2=SECAM */ +static int default_norm; /* default 0 = PAL, 1 = NTSC 2 = SECAM */ module_param(default_norm, int, 0444); MODULE_PARM_DESC(default_norm, "Default norm (0=PAL, 1=NTSC, 2=SECAM)"); diff --git a/drivers/media/video/zoran_card.h b/drivers/media/video/zoran_card.h index 8444ca0a5f3..1b5c4171cf9 100644 --- a/drivers/media/video/zoran_card.h +++ b/drivers/media/video/zoran_card.h @@ -50,4 +50,6 @@ extern int zoran_check_jpg_settings(struct zoran *zr, extern void zoran_open_init_params(struct zoran *zr); extern void zoran_vdev_release(struct video_device *vdev); +void zr36016_write(struct videocodec *codec, u16 reg, u32 val); + #endif /* __ZORAN_CARD_H__ */ diff --git a/drivers/media/video/zoran_device.c b/drivers/media/video/zoran_device.c index f97c2069205..7b60533efe4 100644 --- a/drivers/media/video/zoran_device.c +++ b/drivers/media/video/zoran_device.c @@ -60,7 +60,8 @@ extern const struct zoran_format zoran_formats[]; -static int lml33dpath = 0; /* 1 will use digital path in capture +static int lml33dpath; /* default = 0 + * 1 will use digital path in capture * mode instead of analog. It can be * used for picture adjustments using * tool like xawtv while watching image @@ -927,11 +928,6 @@ count_reset_interrupt (struct zoran *zr) return isr; } -/* hack */ -extern void zr36016_write (struct videocodec *codec, - u16 reg, - u32 val); - void jpeg_start (struct zoran *zr) { @@ -987,7 +983,7 @@ void zr36057_enable_jpg (struct zoran *zr, enum zoran_codec_mode mode) { - static int zero = 0; + static int zero; static int one = 1; struct vfe_settings cap; int field_size = @@ -1726,7 +1722,7 @@ decoder_command (struct zoran *zr, return -EIO; if (zr->card.type == LML33 && - (cmd == DECODER_SET_NORM || DECODER_SET_INPUT)) { + (cmd == DECODER_SET_NORM || cmd == DECODER_SET_INPUT)) { int res; // Bt819 needs to reset its FIFO buffer using #FRST pin and diff --git a/drivers/media/video/zoran_driver.c b/drivers/media/video/zoran_driver.c index fea4946ee71..0134bec1e39 100644 --- a/drivers/media/video/zoran_driver.c +++ b/drivers/media/video/zoran_driver.c @@ -85,7 +85,6 @@ #include "zoran_device.h" #include "zoran_card.h" -#ifdef CONFIG_VIDEO_V4L2 /* we declare some card type definitions here, they mean * the same as the v4l1 ZORAN_VID_TYPE above, except it's v4l2 */ #define ZORAN_V4L2_VID_FLAGS ( \ @@ -94,19 +93,15 @@ V4L2_CAP_VIDEO_OUTPUT |\ V4L2_CAP_VIDEO_OVERLAY \ ) -#endif #include <asm/byteorder.h> -#if defined(CONFIG_VIDEO_V4L2) && defined(CONFIG_VIDEO_V4L1_COMPAT) +#if defined(CONFIG_VIDEO_V4L1_COMPAT) #define ZFMT(pal, fcc, cs) \ .palette = (pal), .fourcc = (fcc), .colorspace = (cs) -#elif defined(CONFIG_VIDEO_V4L2) -#define ZFMT(pal, fcc, cs) \ - .fourcc = (fcc), .colorspace = (cs) #else #define ZFMT(pal, fcc, cs) \ - .palette = (pal) + .fourcc = (fcc), .colorspace = (cs) #endif const struct zoran_format zoran_formats[] = { @@ -205,11 +200,10 @@ extern int jpg_nbufs; extern int jpg_bufsize; extern int pass_through; -static int lock_norm = 0; /* 1=Don't change TV standard (norm) */ +static int lock_norm; /* 0 = default 1 = Don't change TV standard (norm) */ module_param(lock_norm, int, 0644); MODULE_PARM_DESC(lock_norm, "Prevent norm changes (1 = ignore, >1 = fail)"); -#ifdef CONFIG_VIDEO_V4L2 /* small helper function for calculating buffersizes for v4l2 * we calculate the nearest higher power-of-two, which * will be the recommended buffersize */ @@ -232,7 +226,6 @@ zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings) return 8192; return result; } -#endif /* forward references */ static void v4l_fbuffer_free(struct file *file); @@ -1709,7 +1702,6 @@ setup_overlay (struct file *file, return wait_grab_pending(zr); } -#ifdef CONFIG_VIDEO_V4L2 /* get the status of a buffer in the clients buffer queue */ static int zoran_v4l2_buffer_status (struct file *file, @@ -1815,7 +1807,6 @@ zoran_v4l2_buffer_status (struct file *file, return 0; } -#endif static int zoran_set_norm (struct zoran *zr, @@ -2624,8 +2615,6 @@ zoran_do_ioctl (struct inode *inode, } break; -#ifdef CONFIG_VIDEO_V4L2 - /* The new video4linux2 capture interface - much nicer than video4linux1, since * it allows for integrating the JPEG capturing calls inside standard v4l2 */ @@ -4197,7 +4186,6 @@ zoran_do_ioctl (struct inode *inode, return 0; } break; -#endif default: dprintk(1, KERN_DEBUG "%s: UNKNOWN ioctl cmd: 0x%x\n", @@ -4247,7 +4235,7 @@ zoran_poll (struct file *file, dprintk(3, KERN_DEBUG "%s: %s() raw - active=%c, sync_tail=%lu/%c, pend_tail=%lu, pend_head=%lu\n", - ZR_DEVNAME(zr), __FUNCTION__, + ZR_DEVNAME(zr), __func__, "FAL"[fh->v4l_buffers.active], zr->v4l_sync_tail, "UPMD"[zr->v4l_buffers.buffer[frame].state], zr->v4l_pend_tail, zr->v4l_pend_head); @@ -4269,7 +4257,7 @@ zoran_poll (struct file *file, dprintk(3, KERN_DEBUG "%s: %s() jpg - active=%c, que_tail=%lu/%c, que_head=%lu, dma=%lu/%lu\n", - ZR_DEVNAME(zr), __FUNCTION__, + ZR_DEVNAME(zr), __func__, "FAL"[fh->jpg_buffers.active], zr->jpg_que_tail, "UPMD"[zr->jpg_buffers.buffer[frame].state], zr->jpg_que_head, zr->jpg_dma_tail, zr->jpg_dma_head); @@ -4644,7 +4632,9 @@ static const struct file_operations zoran_fops = { .open = zoran_open, .release = zoran_close, .ioctl = zoran_ioctl, +#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32, +#endif .llseek = no_llseek, .read = zoran_read, .write = zoran_write, @@ -4655,9 +4645,7 @@ static const struct file_operations zoran_fops = { struct video_device zoran_template __devinitdata = { .name = ZORAN_NAME, .type = ZORAN_VID_TYPE, -#ifdef CONFIG_VIDEO_V4L2 .type2 = ZORAN_V4L2_VID_FLAGS, -#endif .fops = &zoran_fops, .release = &zoran_vdev_release, .minor = -1 diff --git a/drivers/media/video/zr36016.c b/drivers/media/video/zr36016.c index dd084555da8..00d132bcd1e 100644 --- a/drivers/media/video/zr36016.c +++ b/drivers/media/video/zr36016.c @@ -55,11 +55,10 @@ #define MAX_CODECS 20 /* amount of chips attached via this driver */ -static int zr36016_codecs = 0; +static int zr36016_codecs; /* debugging is available via module parameter */ - -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-4)"); diff --git a/drivers/media/video/zr36050.c b/drivers/media/video/zr36050.c index faae4ec3ea0..cf8b271a1c8 100644 --- a/drivers/media/video/zr36050.c +++ b/drivers/media/video/zr36050.c @@ -52,11 +52,10 @@ #define MAX_CODECS 20 /* amount of chips attached via this driver */ -static int zr36050_codecs = 0; +static int zr36050_codecs; /* debugging is available via module parameter */ - -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-4)"); diff --git a/drivers/media/video/zr36060.c b/drivers/media/video/zr36060.c index 7849b65969d..8e74054d5ef 100644 --- a/drivers/media/video/zr36060.c +++ b/drivers/media/video/zr36060.c @@ -52,14 +52,14 @@ #define MAX_CODECS 20 /* amount of chips attached via this driver */ -static int zr36060_codecs = 0; +static int zr36060_codecs; -static int low_bitrate = 0; +static int low_bitrate; module_param(low_bitrate, bool, 0); MODULE_PARM_DESC(low_bitrate, "Buz compatibility option, halves bitrate"); /* debugging is available via module parameter */ -static int debug = 0; +static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-4)"); diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index 04949c82365..a0e49dc6630 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -62,8 +62,8 @@ /* Module parameters */ -static int debug = 0; -static int mode = 0; +static int debug; +static int mode; /* Module parameters interface */ |