diff options
Diffstat (limited to 'drivers/media/video')
126 files changed, 15819 insertions, 4597 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index dcf9fa9264bb..14a1b616087f 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -203,9 +203,9 @@ config VIDEO_CS53L32A module will be called cs53l32a. config VIDEO_M52790 - tristate "Mitsubishi M52790 A/V switch" - depends on VIDEO_V4L2 && I2C - ---help--- + tristate "Mitsubishi M52790 A/V switch" + depends on VIDEO_V4L2 && I2C + ---help--- Support for the Mitsubishi M52790 A/V switch. To compile this driver as a module, choose M here: the @@ -493,6 +493,28 @@ config VIDEO_UPD64083 endmenu # encoder / decoder chips +config DISPLAY_DAVINCI_DM646X_EVM + tristate "DM646x EVM Video Display" + depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM + select VIDEOBUF_DMA_CONTIG + select VIDEO_DAVINCI_VPIF + select VIDEO_ADV7343 + select VIDEO_THS7303 + help + Support for DaVinci based display device. + + To compile this driver as a module, choose M here: the + module will be called davincihd_display. + +config VIDEO_DAVINCI_VPIF + tristate "DaVinci VPIF Driver" + depends on DISPLAY_DAVINCI_DM646X_EVM + help + Support for DaVinci VPIF Driver. + + To compile this driver as a module, choose M here: the + module will be called vpif. + config VIDEO_VIVI tristate "Virtual Video Driver" depends on VIDEO_DEV && VIDEO_V4L2 && !SPARC32 && !SPARC64 @@ -505,6 +527,55 @@ config VIDEO_VIVI Say Y here if you want to test video apps or debug V4L devices. In doubt, say N. +config VIDEO_VPSS_SYSTEM + tristate "VPSS System module driver" + depends on ARCH_DAVINCI + help + Support for vpss system module for video driver + default y + +config VIDEO_VPFE_CAPTURE + tristate "VPFE Video Capture Driver" + depends on VIDEO_V4L2 && ARCH_DAVINCI + select VIDEOBUF_DMA_CONTIG + help + Support for DMXXXX VPFE based frame grabber. This is the + common V4L2 module for following DMXXX SoCs from Texas + Instruments:- DM6446 & DM355. + + To compile this driver as a module, choose M here: the + module will be called vpfe-capture. + +config VIDEO_DM6446_CCDC + tristate "DM6446 CCDC HW module" + depends on ARCH_DAVINCI_DM644x && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables DaVinci CCD hw module. DaVinci CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from slave decoders. + + To compile this driver as a module, choose M here: the + module will be called vpfe. + +config VIDEO_DM355_CCDC + tristate "DM355 CCDC HW module" + depends on ARCH_DAVINCI_DM355 && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables DM355 CCD hw module. DM355 CCDC hw interfaces + with decoder modules such as TVP5146 over BT656 or + sensor module such as MT9T001 over a raw interface. This + module configures the interface and CCDC/ISIF to do + video frame capture from a slave decoders + + To compile this driver as a module, choose M here: the + module will be called vpfe. + source "drivers/media/video/bt8xx/Kconfig" config VIDEO_PMS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 9f2e3214a482..00fb23e64374 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -154,12 +154,16 @@ obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o +obj-$(CONFIG_ARCH_DAVINCI) += davinci/ + obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o +obj-$(CONFIG_ARCH_DAVINCI) += davinci/ + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c index 30f5caf5dda5..df26f2fe44eb 100644 --- a/drivers/media/video/adv7343.c +++ b/drivers/media/video/adv7343.c @@ -24,7 +24,6 @@ #include <linux/module.h> #include <linux/videodev2.h> #include <linux/uaccess.h> -#include <linux/version.h> #include <media/adv7343.h> #include <media/v4l2-device.h> diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c index 14baffc22192..b8a4b52e8d47 100644 --- a/drivers/media/video/au0828/au0828-dvb.c +++ b/drivers/media/video/au0828/au0828-dvb.c @@ -151,7 +151,7 @@ static int start_urb_transfer(struct au0828_dev *dev) dprintk(2, "%s()\n", __func__); if (dev->urb_streaming) { - dprintk(2, "%s: iso xfer already running!\n", __func__); + dprintk(2, "%s: bulk xfer already running!\n", __func__); return 0; } diff --git a/drivers/media/video/au0828/au0828-i2c.c b/drivers/media/video/au0828/au0828-i2c.c index 13e494365e70..cbdb65c34f21 100644 --- a/drivers/media/video/au0828/au0828-i2c.c +++ b/drivers/media/video/au0828/au0828-i2c.c @@ -320,7 +320,6 @@ static struct i2c_algorithm au0828_i2c_algo_template = { static struct i2c_adapter au0828_i2c_adap_template = { .name = DRIVER_NAME, .owner = THIS_MODULE, - .id = I2C_HW_B_AU0828, .algo = &au0828_i2c_algo_template, }; diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index ca6558c394be..b42251fa96ba 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -1274,6 +1274,7 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, + .has_remote = 1, }, /* ---- card 0x3c ---------------------------------- */ diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 8cc6dd28d6a7..939d1e512974 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -2652,6 +2652,8 @@ static int bttv_querycap(struct file *file, void *priv, V4L2_CAP_VBI_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (btv->has_saa6588) + cap->capabilities |= V4L2_CAP_RDS_CAPTURE; if (no_overlay <= 0) cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; @@ -4593,14 +4595,10 @@ static int bttv_resume(struct pci_dev *pci_dev) #endif static struct pci_device_id bttv_pci_tbl[] = { - {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0}, + {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0}, + {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0}, + {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0}, {0,} }; diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index ebd1ee9dc871..beda363418b0 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -352,7 +352,6 @@ int __devinit init_bttv_i2c(struct bttv *btv) /* bt878 */ strlcpy(btv->c.i2c_adap.name, "bt878", sizeof(btv->c.i2c_adap.name)); - btv->c.i2c_adap.id = I2C_HW_B_BT848; /* FIXME */ btv->c.i2c_adap.algo = &bttv_algo; } else { /* bt848 */ @@ -362,7 +361,6 @@ int __devinit init_bttv_i2c(struct bttv *btv) strlcpy(btv->c.i2c_adap.name, "bttv", sizeof(btv->c.i2c_adap.name)); - btv->c.i2c_adap.id = I2C_HW_B_BT848; memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template, sizeof(bttv_i2c_algo_bit_template)); btv->i2c_algo.udelay = i2c_udelay; diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index 2f289d981fe6..3e7b48e73b01 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -308,6 +308,7 @@ int bttv_input_init(struct bttv *btv) ir->mask_keyup = 0x008000; ir->polling = 50; // ms break; + case BTTV_BOARD_ASKEY_CPH03X: case BTTV_BOARD_CONCEPTRONIC_CTVFMI2: case BTTV_BOARD_CONTVFMI: ir_codes = ir_codes_pixelview; diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index c4d181dde1ca..9c149a781294 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -490,7 +490,6 @@ static int cafe_smbus_setup(struct cafe_camera *cam) int ret; cafe_smbus_enable_irq(cam); - adap->id = I2C_HW_SMBUS_CAFE; adap->owner = THIS_MODULE; adap->algo = &cafe_smbus_algo; strcpy(adap->name, "cafe_ccic"); diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index 36f2d76006fd..f11e47a58286 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -56,7 +56,8 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { .hw_audio_ctrl = CX18_HW_418_AV, .hw_muxer = CX18_HW_CS5345, .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | - CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, + CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | + CX18_HW_Z8F0811_IR_HAUP, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, @@ -102,7 +103,8 @@ static const struct cx18_card cx18_card_hvr1600_samsung = { .hw_audio_ctrl = CX18_HW_418_AV, .hw_muxer = CX18_HW_CS5345, .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | - CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, + CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | + CX18_HW_Z8F0811_IR_HAUP, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, @@ -204,7 +206,7 @@ static const struct cx18_card cx18_card_mpc718 = { .v4l2_capabilities = CX18_CAP_ENCODER, .hw_audio_ctrl = CX18_HW_418_AV, .hw_muxer = CX18_HW_GPIO_MUX, - .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | + .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index 3c552b6b7c4d..444e3c7c563e 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -22,13 +22,17 @@ */ /* hardware flags */ -#define CX18_HW_TUNER (1 << 0) -#define CX18_HW_TVEEPROM (1 << 1) -#define CX18_HW_CS5345 (1 << 2) -#define CX18_HW_DVB (1 << 3) -#define CX18_HW_418_AV (1 << 4) -#define CX18_HW_GPIO_MUX (1 << 5) -#define CX18_HW_GPIO_RESET_CTRL (1 << 6) +#define CX18_HW_TUNER (1 << 0) +#define CX18_HW_TVEEPROM (1 << 1) +#define CX18_HW_CS5345 (1 << 2) +#define CX18_HW_DVB (1 << 3) +#define CX18_HW_418_AV (1 << 4) +#define CX18_HW_GPIO_MUX (1 << 5) +#define CX18_HW_GPIO_RESET_CTRL (1 << 6) +#define CX18_HW_Z8F0811_IR_TX_HAUP (1 << 7) +#define CX18_HW_Z8F0811_IR_RX_HAUP (1 << 8) +#define CX18_HW_Z8F0811_IR_HAUP (CX18_HW_Z8F0811_IR_RX_HAUP | \ + CX18_HW_Z8F0811_IR_TX_HAUP) /* video inputs */ #define CX18_CARD_INPUT_VID_TUNER 1 diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 92026e82e10e..dd0224f328ad 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -268,6 +268,20 @@ static void cx18_iounmap(struct cx18 *cx) } } +static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len) +{ + int i; + + CX18_INFO("eeprom dump:\n"); + for (i = 0; i < len; i++) { + if (0 == (i % 16)) + CX18_INFO("eeprom %02x:", i); + printk(KERN_CONT " %02x", eedata[i]); + if (15 == (i % 16)) + printk(KERN_CONT "\n"); + } +} + /* Hauppauge card? get values from tveeprom */ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) { @@ -279,8 +293,26 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) c.adapter = &cx->i2c_adap[0]; c.addr = 0xA0 >> 1; - tveeprom_read(&c, eedata, sizeof(eedata)); - tveeprom_hauppauge_analog(&c, tv, eedata); + memset(tv, 0, sizeof(*tv)); + if (tveeprom_read(&c, eedata, sizeof(eedata))) + return; + + switch (cx->card->type) { + case CX18_CARD_HVR_1600_ESMT: + case CX18_CARD_HVR_1600_SAMSUNG: + tveeprom_hauppauge_analog(&c, tv, eedata); + break; + case CX18_CARD_YUAN_MPC718: + tv->model = 0x718; + cx18_eeprom_dump(cx, eedata, sizeof(eedata)); + CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n", + eedata[2], eedata[1], eedata[4], eedata[3]); + break; + default: + tv->model = 0xffffffff; + cx18_eeprom_dump(cx, eedata, sizeof(eedata)); + break; + } } static void cx18_process_eeprom(struct cx18 *cx) @@ -298,6 +330,11 @@ static void cx18_process_eeprom(struct cx18 *cx) case 74000 ... 74999: cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); break; + case 0x718: + return; + case 0xffffffff: + CX18_INFO("Unknown EEPROM encoding\n"); + return; case 0: CX18_ERR("Invalid EEPROM\n"); return; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 29969c18949c..04d9c2508b86 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -690,7 +690,7 @@ int cx18_v4l2_open(struct file *filp) int res; struct video_device *video_dev = video_devdata(filp); struct cx18_stream *s = video_get_drvdata(video_dev); - struct cx18 *cx = s->cx;; + struct cx18 *cx = s->cx; mutex_lock(&cx->serialize_lock); if (cx18_init_on_first_open(cx)) { diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 8591e4fc359f..ba754e8056fb 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -28,6 +28,7 @@ #include "cx18-gpio.h" #include "cx18-i2c.h" #include "cx18-irq.h" +#include <media/ir-kbd-i2c.h> #define CX18_REG_I2C_1_WR 0xf15000 #define CX18_REG_I2C_1_RD 0xf15008 @@ -40,16 +41,20 @@ #define GETSDL_BIT 0x0008 #define CX18_CS5345_I2C_ADDR 0x4c +#define CX18_Z8F0811_IR_TX_I2C_ADDR 0x70 +#define CX18_Z8F0811_IR_RX_I2C_ADDR 0x71 /* This array should match the CX18_HW_ defines */ static const u8 hw_addrs[] = { - 0, /* CX18_HW_TUNER */ - 0, /* CX18_HW_TVEEPROM */ - CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */ - 0, /* CX18_HW_DVB */ - 0, /* CX18_HW_418_AV */ - 0, /* CX18_HW_GPIO_MUX */ - 0, /* CX18_HW_GPIO_RESET_CTRL */ + 0, /* CX18_HW_TUNER */ + 0, /* CX18_HW_TVEEPROM */ + CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */ + 0, /* CX18_HW_DVB */ + 0, /* CX18_HW_418_AV */ + 0, /* CX18_HW_GPIO_MUX */ + 0, /* CX18_HW_GPIO_RESET_CTRL */ + CX18_Z8F0811_IR_TX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_TX_HAUP */ + CX18_Z8F0811_IR_RX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the CX18_HW_ defines */ @@ -62,6 +67,8 @@ static const u8 hw_bus[] = { 0, /* CX18_HW_418_AV */ 0, /* CX18_HW_GPIO_MUX */ 0, /* CX18_HW_GPIO_RESET_CTRL */ + 0, /* CX18_HW_Z8F0811_IR_TX_HAUP */ + 0, /* CX18_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the CX18_HW_ defines */ @@ -73,6 +80,8 @@ static const char * const hw_modules[] = { NULL, /* CX18_HW_418_AV */ NULL, /* CX18_HW_GPIO_MUX */ NULL, /* CX18_HW_GPIO_RESET_CTRL */ + NULL, /* CX18_HW_Z8F0811_IR_TX_HAUP */ + NULL, /* CX18_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the CX18_HW_ defines */ @@ -84,8 +93,37 @@ static const char * const hw_devicenames[] = { "cx23418_AV", "gpio_mux", "gpio_reset_ctrl", + "ir_tx_z8f0811_haup", + "ir_rx_z8f0811_haup", }; +static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, + u8 addr) +{ + struct i2c_board_info info; + struct IR_i2c_init_data ir_init_data; + unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, type, I2C_NAME_SIZE); + + /* Our default information for ir-kbd-i2c.c to use */ + switch (hw) { + case CX18_HW_Z8F0811_IR_RX_HAUP: + memset(&ir_init_data, 0, sizeof(struct IR_i2c_init_data)); + ir_init_data.ir_codes = ir_codes_hauppauge_new; + ir_init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + ir_init_data.type = IR_TYPE_RC5; + ir_init_data.name = "CX23418 Z8F0811 Hauppauge"; + info.platform_data = &ir_init_data; + break; + default: + break; + } + + return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0; +} + int cx18_i2c_register(struct cx18 *cx, unsigned idx) { struct v4l2_subdev *sd; @@ -115,11 +153,14 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx) return sd != NULL ? 0 : -1; } + if (hw & CX18_HW_Z8F0811_IR_HAUP) + return cx18_i2c_new_ir(adap, hw, type, hw_addrs[idx]); + /* Is it not an I2C device or one we do not wish to register? */ if (!hw_addrs[idx]) return -1; - /* It's an I2C device other than an analog tuner */ + /* It's an I2C device other than an analog tuner or IR chip */ sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, mod, type, hw_addrs[idx]); if (sd != NULL) sd->grp_id = hw; @@ -190,7 +231,6 @@ static int cx18_getsda(void *data) /* template for i2c-bit-algo */ static struct i2c_adapter cx18_i2c_adap_template = { .name = "cx18 i2c driver", - .id = I2C_HW_B_CX2341X, .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index d7b1921e6666..fc76e4d6ffa7 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -605,7 +605,7 @@ int cx18_s_input(struct file *file, void *fh, unsigned int inp) if (ret) return ret; - if (inp < 0 || inp >= cx->nof_inputs) + if (inp >= cx->nof_inputs) return -EINVAL; if (inp == cx->active_input) { diff --git a/drivers/media/video/cx231xx/cx231xx-conf-reg.h b/drivers/media/video/cx231xx/cx231xx-conf-reg.h index a6f398a175c5..31a8759f6e54 100644 --- a/drivers/media/video/cx231xx/cx231xx-conf-reg.h +++ b/drivers/media/video/cx231xx/cx231xx-conf-reg.h @@ -60,10 +60,10 @@ #define PWR_RESETOUT_EN 0x100 /* bit8 */ enum AV_MODE{ - POLARIS_AVMODE_DEFAULT = 0, - POLARIS_AVMODE_DIGITAL = 0x10, - POLARIS_AVMODE_ANALOGT_TV = 0x20, - POLARIS_AVMODE_ENXTERNAL_AV = 0x30, + POLARIS_AVMODE_DEFAULT = 0, + POLARIS_AVMODE_DIGITAL = 0x10, + POLARIS_AVMODE_ANALOGT_TV = 0x20, + POLARIS_AVMODE_ENXTERNAL_AV = 0x30, }; diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/video/cx231xx/cx231xx-i2c.c index 33219dc4d649..58d9cc0867b9 100644 --- a/drivers/media/video/cx231xx/cx231xx-i2c.c +++ b/drivers/media/video/cx231xx/cx231xx-i2c.c @@ -432,7 +432,6 @@ static struct i2c_algorithm cx231xx_algo = { static struct i2c_adapter cx231xx_adap_template = { .owner = THIS_MODULE, .name = "cx231xx", - .id = I2C_HW_B_CX231XX, .algo = &cx231xx_algo, }; diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 609bae6098d3..36503725d973 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -923,8 +923,8 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; - f->fmt.pix.pixelformat = dev->format->fourcc;; - f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;; + f->fmt.pix.pixelformat = dev->format->fourcc; + f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; diff --git a/drivers/media/video/cx23885/cimax2.c b/drivers/media/video/cx23885/cimax2.c index 08582e58bdbf..0316257b7345 100644 --- a/drivers/media/video/cx23885/cimax2.c +++ b/drivers/media/video/cx23885/cimax2.c @@ -443,6 +443,7 @@ int netup_ci_init(struct cx23885_tsport *port) goto err; INIT_WORK(&state->work, netup_read_ci_status); + schedule_work(&state->work); ci_dbg_print("%s: CI initialized!\n", __func__); diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index 1a1048b18f70..6c3b51ce3372 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -630,6 +630,39 @@ int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value) return retval; } +void mc417_gpio_set(struct cx23885_dev *dev, u32 mask) +{ + u32 val; + + /* Set the gpio value */ + mc417_register_read(dev, 0x900C, &val); + val |= (mask & 0x000ffff); + mc417_register_write(dev, 0x900C, val); +} + +void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask) +{ + u32 val; + + /* Clear the gpio value */ + mc417_register_read(dev, 0x900C, &val); + val &= ~(mask & 0x0000ffff); + mc417_register_write(dev, 0x900C, val); +} + +void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput) +{ + u32 val; + + /* Enable GPIO direction bits */ + mc417_register_read(dev, 0x9020, &val); + if (asoutput) + val |= (mask & 0x0000ffff); + else + val &= ~(mask & 0x0000ffff); + + mc417_register_write(dev, 0x9020, val); +} /* ------------------------------------------------------------------ */ /* MPEG encoder API */ @@ -955,25 +988,8 @@ static int cx23885_load_firmware(struct cx23885_dev *dev) 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); + /* F/W power up disturbs the GPIOs, restore state */ + retval |= mc417_register_write(dev, 0x9020, gpio_output); retval |= mc417_register_write(dev, 0x900C, value); retval |= mc417_register_read(dev, IVTV_REG_VPU, &value); @@ -1788,9 +1804,6 @@ int cx23885_417_register(struct cx23885_dev *dev) return err; } - /* Initialize MC417 registers */ - cx23885_mc417_init(dev); - printk(KERN_INFO "%s: registered device video%d [mpeg]\n", dev->name, dev->v4l_device->num); diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index ce29b5e34a11..3143d85ef31d 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -201,6 +201,15 @@ struct cx23885_board cx23885_boards[] = { .name = "Mygica X8506 DMB-TH", .portb = CX23885_MPEG_DVB, }, + [CX23885_BOARD_MAGICPRO_PROHDTVE2] = { + .name = "Magic-Pro ProHDTV Extreme 2", + .portb = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1850] = { + .name = "Hauppauge WinTV-HVR1850", + .portb = CX23885_MPEG_ENCODER, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -324,6 +333,14 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x14f1, .subdevice = 0x8651, .card = CX23885_BOARD_MYGICA_X8506, + }, { + .subvendor = 0x14f1, + .subdevice = 0x8657, + .card = CX23885_BOARD_MAGICPRO_PROHDTVE2, + }, { + .subvendor = 0x0070, + .subdevice = 0x8541, + .card = CX23885_BOARD_HAUPPAUGE_HVR1850, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -483,8 +500,13 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) /* WinTV-HVR1700 (PCIe, OEM, No IR, full height) * DVB-T and MPEG2 HW Encoder */ break; + case 85021: + /* WinTV-HVR1850 (PCIe, OEM, RCA in, IR, FM, + Dual channel ATSC and MPEG2 HW Encoder */ + break; default: - printk(KERN_WARNING "%s: warning: unknown hauppauge model #%d\n", + printk(KERN_WARNING "%s: warning: " + "unknown hauppauge model #%d\n", dev->name, tv.model); break; } @@ -574,13 +596,23 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* CX23417 GPIO's */ /* EIO15 Zilog Reset */ /* EIO14 S5H1409/CX24227 Reset */ + mc417_gpio_enable(dev, GPIO_15 | GPIO_14, 1); + + /* Put the demod into reset and protect the eeprom */ + mc417_gpio_clear(dev, GPIO_15 | GPIO_14); + mdelay(100); + + /* Bring the demod and blaster out of reset */ + mc417_gpio_set(dev, GPIO_15 | GPIO_14); + mdelay(100); /* Force the TDA8295A into reset and back */ - cx_set(GP0_IO, 0x00040004); + cx23885_gpio_enable(dev, GPIO_2, 1); + cx23885_gpio_set(dev, GPIO_2); mdelay(20); - cx_clear(GP0_IO, 0x00000004); + cx23885_gpio_clear(dev, GPIO_2); mdelay(20); - cx_set(GP0_IO, 0x00040004); + cx23885_gpio_set(dev, GPIO_2); mdelay(20); break; case CX23885_BOARD_HAUPPAUGE_HVR1200: @@ -715,14 +747,45 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx23885_gpio_set(dev, GPIO_9); break; case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: /* GPIO-1 reset XC5000 */ - /* GPIO-2 reset LGS8GL5 */ + /* GPIO-2 reset LGS8GL5 / LGS8G75 */ cx_set(GP0_IO, 0x00060000); cx_clear(GP0_IO, 0x00000006); mdelay(100); cx_set(GP0_IO, 0x00060006); mdelay(100); break; + case CX23885_BOARD_HAUPPAUGE_HVR1850: + /* GPIO-0 656_CLK */ + /* GPIO-1 656_D0 */ + /* GPIO-2 Wake# */ + /* GPIO-3-10 cx23417 data0-7 */ + /* GPIO-11-14 cx23417 addr0-3 */ + /* GPIO-15-18 cx23417 READY, CS, RD, WR */ + /* GPIO-19 IR_RX */ + /* GPIO-20 C_IR_TX */ + /* GPIO-21 I2S DAT */ + /* GPIO-22 I2S WCLK */ + /* GPIO-23 I2S BCLK */ + /* ALT GPIO: EXP GPIO LATCH */ + + /* CX23417 GPIO's */ + /* GPIO-14 S5H1411/CX24228 Reset */ + /* GPIO-13 EEPROM write protect */ + mc417_gpio_enable(dev, GPIO_14 | GPIO_13, 1); + + /* Put the demod into reset and protect the eeprom */ + mc417_gpio_clear(dev, GPIO_14 | GPIO_13); + mdelay(100); + + /* Bring the demod out of reset */ + mc417_gpio_set(dev, GPIO_14); + mdelay(100); + + /* CX24228 GPIO */ + /* Connected to IF / Mux */ + break; } } @@ -739,6 +802,7 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: + case CX23885_BOARD_HAUPPAUGE_HVR1850: /* FIXME: Implement me */ break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: @@ -778,6 +842,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: + case CX23885_BOARD_HAUPPAUGE_HVR1850: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -827,6 +892,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; @@ -844,6 +910,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: + case CX23885_BOARD_HAUPPAUGE_HVR1850: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index bf7bb1c412fb..40d438d7234d 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -713,12 +713,26 @@ static void cx23885_dev_checkrevision(struct cx23885_dev *dev) dev->hwrevision = 0xa1; break; case 0x02: - /* CX23885-13Z */ + /* CX23885-13Z/14Z */ dev->hwrevision = 0xb0; break; case 0x03: - /* CX23888-22Z */ - dev->hwrevision = 0xc0; + if (dev->pci->device == 0x8880) { + /* CX23888-21Z/22Z */ + dev->hwrevision = 0xc0; + } else { + /* CX23885-14Z */ + dev->hwrevision = 0xa4; + } + break; + case 0x04: + if (dev->pci->device == 0x8880) { + /* CX23888-31Z */ + dev->hwrevision = 0xd0; + } else { + /* CX23885-15Z, CX23888-31Z */ + dev->hwrevision = 0xa5; + } break; case 0x0e: /* CX23887-15Z */ @@ -756,6 +770,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) /* Configure the internal memory */ if (dev->pci->device == 0x8880) { + /* Could be 887 or 888, assume a default */ dev->bridge = CX23885_BRIDGE_887; /* Apply a sensible clock frequency for the PCIe bridge */ dev->clk_freq = 25000000; @@ -868,6 +883,14 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n", __func__, dev->radio_type, dev->radio_addr); + /* The cx23417 encoder has GPIO's that need to be initialised + * before DVB, so that demodulators and tuners are out of + * reset before DVB uses them. + */ + if ((cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER) || + (cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER)) + cx23885_mc417_init(dev); + /* init hardware */ cx23885_reset(dev); @@ -1250,6 +1273,7 @@ static int cx23885_start_dma(struct cx23885_tsport *port, switch (dev->bridge) { case CX23885_BRIDGE_885: case CX23885_BRIDGE_887: + case CX23885_BRIDGE_888: /* enable irqs */ dprintk(1, "%s() enabling TS int's and DMA\n", __func__); cx_set(port->reg_ts_int_msk, port->ts_int_msk_val); diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 86ac529e62be..022fad798fc2 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -396,7 +396,7 @@ static struct stv0900_reg stv0900_ts_regs[] = { static struct stv0900_config netup_stv0900_config = { .demod_address = 0x68, - .xtal = 27000000, + .xtal = 8000000, .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ .diseqc_mode = 2,/* 2/3 PWM */ .ts_config_regs = stv0900_ts_regs, @@ -408,14 +408,14 @@ static struct stv0900_config netup_stv0900_config = { static struct stv6110_config netup_stv6110_tunerconfig_a = { .i2c_address = 0x60, - .mclk = 27000000, - .iq_wiring = 0, + .mclk = 16000000, + .clk_div = 1, }; static struct stv6110_config netup_stv6110_tunerconfig_b = { .i2c_address = 0x63, - .mclk = 27000000, - .iq_wiring = 1, + .mclk = 16000000, + .clk_div = 1, }; static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) @@ -487,6 +487,26 @@ static int cx23885_dvb_set_frontend(struct dvb_frontend *fe, port->set_frontend_save(fe, param) : -ENODEV; } +static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = { + .prod = LGS8GXX_PROD_LGS8G75, + .demod_address = 0x19, + .serial_ts = 0, + .ts_clk_pol = 1, + .ts_clk_gated = 1, + .if_clk_freq = 30400, /* 30.4 MHz */ + .if_freq = 6500, /* 6.50 MHz */ + .if_neg_center = 1, + .ext_adc = 0, + .adc_signed = 1, + .adc_vpp = 2, /* 1.6 Vpp */ + .if_neg_edge = 1, +}; + +static struct xc5000_config magicpro_prohdtve2_xc5000_config = { + .i2c_address = 0x61, + .if_khz = 6500, +}; + static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; @@ -833,6 +853,30 @@ static int dvb_register(struct cx23885_tsport *port) &mygica_x8506_xc5000_config); } break; + case CX23885_BOARD_MAGICPRO_PROHDTVE2: + i2c_bus = &dev->i2c_bus[0]; + i2c_bus2 = &dev->i2c_bus[1]; + fe0->dvb.frontend = dvb_attach(lgs8gxx_attach, + &magicpro_prohdtve2_lgs8g75_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(xc5000_attach, + fe0->dvb.frontend, + &i2c_bus2->i2c_adap, + &magicpro_prohdtve2_xc5000_config); + } + break; + case CX23885_BOARD_HAUPPAUGE_HVR1850: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(s5h1411_attach, + &hcw_s5h1411_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_bus[0].i2c_adap, + &hauppauge_tda18271_config); + break; + default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c index 384dec34134f..4172cb387420 100644 --- a/drivers/media/video/cx23885/cx23885-i2c.c +++ b/drivers/media/video/cx23885/cx23885-i2c.c @@ -283,7 +283,6 @@ static struct i2c_algorithm cx23885_i2c_algo_template = { static struct i2c_adapter cx23885_i2c_adap_template = { .name = "cx23885", .owner = THIS_MODULE, - .id = I2C_HW_B_CX23885, .algo = &cx23885_i2c_algo_template, }; diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 214a55e943b7..86f26947bb78 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -76,6 +76,8 @@ #define CX23885_BOARD_HAUPPAUGE_HVR1255 20 #define CX23885_BOARD_HAUPPAUGE_HVR1210 21 #define CX23885_BOARD_MYGICA_X8506 22 +#define CX23885_BOARD_MAGICPRO_PROHDTVE2 23 +#define CX23885_BOARD_HAUPPAUGE_HVR1850 24 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 @@ -87,6 +89,12 @@ #define GPIO_7 0x00000080 #define GPIO_8 0x00000100 #define GPIO_9 0x00000200 +#define GPIO_10 0x00000400 +#define GPIO_11 0x00000800 +#define GPIO_12 0x00001000 +#define GPIO_13 0x00002000 +#define GPIO_14 0x00004000 +#define GPIO_15 0x00008000 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ @@ -331,6 +339,7 @@ struct cx23885_dev { CX23885_BRIDGE_UNDEFINED = 0, CX23885_BRIDGE_885 = 885, CX23885_BRIDGE_887 = 887, + CX23885_BRIDGE_888 = 888, } bridge; /* Analog video */ @@ -395,7 +404,7 @@ struct sram_channel { u32 cmds_start; u32 ctrl_start; u32 cdt; - u32 fifo_start;; + u32 fifo_start; u32 fifo_size; u32 ptr1_reg; u32 ptr2_reg; @@ -504,6 +513,9 @@ 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); +extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask); +extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask); +extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 0be51b65f098..1aeaf18a9bea 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -321,6 +321,15 @@ static void cx23885_initialize(struct i2c_client *client) /* Select AFE clock pad output source */ cx25840_write(client, 0x144, 0x05); + /* Drive GPIO2 direction and values for HVR1700 + * where an onboard mux selects the output of demodulator + * vs the 417. Failure to set this results in no DTV. + * It's safe to set this across all Hauppauge boards + * currently, regardless of the board type. + */ + cx25840_write(client, 0x160, 0x1d); + cx25840_write(client, 0x164, 0x00); + /* Do the firmware load in a work handler to prevent. Otherwise the kernel is blocked waiting for the bit-banging i2c interface to finish uploading the @@ -1578,12 +1587,6 @@ 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-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c index 0df53b0d75d9..2a535d0403ed 100644 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ b/drivers/media/video/cx25840/cx25840-firmware.c @@ -98,9 +98,14 @@ int cx25840_loadfw(struct i2c_client *client) const u8 *ptr; int size, retval; int MAX_BUF_SIZE = FWSEND; + u32 gpio_oe = 0, gpio_da = 0; - if (state->is_cx23885) + if (state->is_cx23885) { firmware = FWFILE_CX23885; + /* Preserve the GPIO OE and output bits */ + gpio_oe = cx25840_read(client, 0x160); + gpio_da = cx25840_read(client, 0x164); + } else if (state->is_cx231xx) firmware = FWFILE_CX231XX; @@ -142,5 +147,11 @@ int cx25840_loadfw(struct i2c_client *client) size = fw->size; release_firmware(fw); + if (state->is_cx23885) { + /* Restore GPIO configuration after f/w load */ + cx25840_write(client, 0x160, gpio_oe); + cx25840_write(client, 0x164, gpio_da); + } + return check_fw_load(client, size); } diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 39465301ec94..e5f07fbd5a35 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1283,6 +1283,51 @@ static const struct cx88_board cx88_boards[] = { }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_WINFAST_DTV2000H_J] = { + .name = "WinFast DTV2000 H rev. J", + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x00017300, + .gpio1 = 0x00008207, + .gpio2 = 0x00000000, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x00018300, + .gpio1 = 0x0000f207, + .gpio2 = 0x00017304, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x00018301, + .gpio1 = 0x0000f207, + .gpio2 = 0x00017304, + .gpio3 = 0x02000000, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x00018301, + .gpio1 = 0x0000f207, + .gpio2 = 0x00017304, + .gpio3 = 0x02000000, + }}, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x00015702, + .gpio1 = 0x0000f207, + .gpio2 = 0x00015702, + .gpio3 = 0x02000000, + }, + .mpeg = CX88_MPEG_DVB, + }, [CX88_BOARD_GENIATECH_DVBS] = { .name = "Geniatech DVB-S", .tuner_type = TUNER_ABSENT, @@ -1908,7 +1953,8 @@ static const struct cx88_board cx88_boards[] = { .radio_addr = ADDR_UNSET, .input = {{ .type = CX88_VMUX_DVB, - .vmux = 1, + .vmux = 0, + .gpio0 = 0x8080, } }, .mpeg = CX88_MPEG_DVB, }, @@ -2282,6 +2328,10 @@ static const struct cx88_subid cx88_subids[] = { .subdevice = 0x665e, .card = CX88_BOARD_WINFAST_DTV2000H, },{ + .subvendor = 0x107d, + .subdevice = 0x6f2b, + .card = CX88_BOARD_WINFAST_DTV2000H_J, + },{ .subvendor = 0x18ac, .subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q, @@ -3162,7 +3212,11 @@ static void cx88_card_setup(struct cx88_core *core) case CX88_BOARD_PROF_6200: case CX88_BOARD_PROF_7300: case CX88_BOARD_SATTRADE_ST4200: + cx_write(MO_GP0_IO, 0x8000); + msleep(100); cx_write(MO_SRST_IO, 0); + msleep(10); + cx_write(MO_GP0_IO, 0x8080); msleep(100); cx_write(MO_SRST_IO, 1); msleep(100); diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index e237b507659b..6e5d142b5b00 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -424,17 +424,16 @@ static int tevii_dvbs_set_voltage(struct dvb_frontend *fe, struct cx8802_dev *dev= fe->dvb->priv; struct cx88_core *core = dev->core; + cx_set(MO_GP0_IO, 0x6040); switch (voltage) { case SEC_VOLTAGE_13: - printk("LNB Voltage SEC_VOLTAGE_13\n"); - cx_write(MO_GP0_IO, 0x00006040); + cx_clear(MO_GP0_IO, 0x20); break; case SEC_VOLTAGE_18: - printk("LNB Voltage SEC_VOLTAGE_18\n"); - cx_write(MO_GP0_IO, 0x00006060); + cx_set(MO_GP0_IO, 0x20); break; case SEC_VOLTAGE_OFF: - printk("LNB Voltage SEC_VOLTAGE_off\n"); + cx_clear(MO_GP0_IO, 0x20); break; } @@ -499,9 +498,9 @@ static struct zl10353_config cx88_pinnacle_hybrid_pctv = { }; static struct zl10353_config cx88_geniatech_x8000_mt = { - .demod_address = (0x1e >> 1), - .no_tuner = 1, - .disable_i2c_gate_ctrl = 1, + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .disable_i2c_gate_ctrl = 1, }; static struct s5h1411_config dvico_fusionhdtv7_config = { @@ -696,6 +695,7 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_WINFAST_DTV2000H: + case CX88_BOARD_WINFAST_DTV2000H_J: case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR1100LP: case CX88_BOARD_HAUPPAUGE_HVR1300: diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index d91f5c51206d..79c4408a6171 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -23,7 +23,7 @@ */ #include <linux/init.h> -#include <linux/delay.h> +#include <linux/hrtimer.h> #include <linux/input.h> #include <linux/pci.h> #include <linux/module.h> @@ -48,7 +48,7 @@ struct cx88_IR { /* poll external decoder */ int polling; - struct delayed_work work; + struct hrtimer timer; u32 gpio_addr; u32 last_gpio; u32 mask_keycode; @@ -144,19 +144,28 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) } } -static void cx88_ir_work(struct work_struct *work) +static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer) { - struct cx88_IR *ir = container_of(work, struct cx88_IR, work.work); + unsigned long missed; + struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer); cx88_ir_handle_key(ir); - schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); + missed = hrtimer_forward_now(&ir->timer, + ktime_set(0, ir->polling * 1000000)); + if (missed > 1) + ir_dprintk("Missed ticks %ld\n", missed - 1); + + return HRTIMER_RESTART; } void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) { if (ir->polling) { - INIT_DELAYED_WORK(&ir->work, cx88_ir_work); - schedule_delayed_work(&ir->work, 0); + hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ir->timer.function = cx88_ir_work; + hrtimer_start(&ir->timer, + ktime_set(0, ir->polling * 1000000), + HRTIMER_MODE_REL); } if (ir->sampling) { core->pci_irqmask |= PCI_INT_IR_SMPINT; @@ -173,7 +182,7 @@ void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) } if (ir->polling) - cancel_delayed_work_sync(&ir->work); + hrtimer_cancel(&ir->timer); } /* ---------------------------------------------------------------------- */ @@ -225,6 +234,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->sampling = 1; break; case CX88_BOARD_WINFAST_DTV2000H: + case CX88_BOARD_WINFAST_DTV2000H_J: case CX88_BOARD_WINFAST_DTV1800H: ir_codes = ir_codes_winfast; ir->gpio_addr = MO_GP0_IO; diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 9d83762163f5..d5cea41f4207 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -237,6 +237,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79 #define CX88_BOARD_HAUPPAUGE_IRONLY 80 #define CX88_BOARD_WINFAST_DTV1800H 81 +#define CX88_BOARD_WINFAST_DTV2000H_J 82 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile new file mode 100644 index 000000000000..f44cad2f5412 --- /dev/null +++ b/drivers/media/video/davinci/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the davinci video device drivers. +# + +# VPIF +obj-$(CONFIG_VIDEO_DAVINCI_VPIF) += vpif.o + +#DM646x EVM Display driver +obj-$(CONFIG_DISPLAY_DAVINCI_DM646X_EVM) += vpif_display.o + +# Capture: DM6446 and DM355 +obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o +obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o +obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o +obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o diff --git a/drivers/media/video/davinci/ccdc_hw_device.h b/drivers/media/video/davinci/ccdc_hw_device.h new file mode 100644 index 000000000000..86b9b3518965 --- /dev/null +++ b/drivers/media/video/davinci/ccdc_hw_device.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ccdc device API + */ +#ifndef _CCDC_HW_DEVICE_H +#define _CCDC_HW_DEVICE_H + +#ifdef __KERNEL__ +#include <linux/videodev2.h> +#include <linux/device.h> +#include <media/davinci/vpfe_types.h> +#include <media/davinci/ccdc_types.h> + +/* + * ccdc hw operations + */ +struct ccdc_hw_ops { + /* Pointer to initialize function to initialize ccdc device */ + int (*open) (struct device *dev); + /* Pointer to deinitialize function */ + int (*close) (struct device *dev); + /* set ccdc base address */ + void (*set_ccdc_base)(void *base, int size); + /* Pointer to function to enable or disable ccdc */ + void (*enable) (int en); + /* reset sbl. only for 6446 */ + void (*reset) (void); + /* enable output to sdram */ + void (*enable_out_to_sdram) (int en); + /* Pointer to function to set hw parameters */ + int (*set_hw_if_params) (struct vpfe_hw_if_param *param); + /* get interface parameters */ + int (*get_hw_if_params) (struct vpfe_hw_if_param *param); + /* + * Pointer to function to set parameters. Used + * for implementing VPFE_S_CCDC_PARAMS + */ + int (*set_params) (void *params); + /* + * Pointer to function to get parameter. Used + * for implementing VPFE_G_CCDC_PARAMS + */ + int (*get_params) (void *params); + /* Pointer to function to configure ccdc */ + int (*configure) (void); + + /* Pointer to function to set buffer type */ + int (*set_buftype) (enum ccdc_buftype buf_type); + /* Pointer to function to get buffer type */ + enum ccdc_buftype (*get_buftype) (void); + /* Pointer to function to set frame format */ + int (*set_frame_format) (enum ccdc_frmfmt frm_fmt); + /* Pointer to function to get frame format */ + enum ccdc_frmfmt (*get_frame_format) (void); + /* enumerate hw pix formats */ + int (*enum_pix)(u32 *hw_pix, int i); + /* Pointer to function to set buffer type */ + u32 (*get_pixel_format) (void); + /* Pointer to function to get pixel format. */ + int (*set_pixel_format) (u32 pixfmt); + /* Pointer to function to set image window */ + int (*set_image_window) (struct v4l2_rect *win); + /* Pointer to function to set image window */ + void (*get_image_window) (struct v4l2_rect *win); + /* Pointer to function to get line length */ + unsigned int (*get_line_length) (void); + + /* Query CCDC control IDs */ + int (*queryctrl)(struct v4l2_queryctrl *qctrl); + /* Set CCDC control */ + int (*set_control)(struct v4l2_control *ctrl); + /* Get CCDC control */ + int (*get_control)(struct v4l2_control *ctrl); + + /* Pointer to function to set frame buffer address */ + void (*setfbaddr) (unsigned long addr); + /* Pointer to function to get field id */ + int (*getfid) (void); +}; + +struct ccdc_hw_device { + /* ccdc device name */ + char name[32]; + /* module owner */ + struct module *owner; + /* hw ops */ + struct ccdc_hw_ops hw_ops; +}; + +/* Used by CCDC module to register & unregister with vpfe capture driver */ +int vpfe_register_ccdc_device(struct ccdc_hw_device *dev); +void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev); + +#endif +#endif diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c new file mode 100644 index 000000000000..4629cabe3f28 --- /dev/null +++ b/drivers/media/video/davinci/dm355_ccdc.c @@ -0,0 +1,978 @@ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * CCDC hardware module for DM355 + * ------------------------------ + * + * This module is for configuring DM355 CCD controller of VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Bayer RGB data, before writing it to SDRAM. This + * module also allows application to configure individual + * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. + * To do so, application include dm355_ccdc.h and vpfe_capture.h header + * files. The setparams() API is called by vpfe_capture driver + * to configure module parameters + * + * TODO: 1) Raw bayer parameter settings and bayer capture + * 2) Split module parameter structure to module specific ioctl structs + * 3) add support for lense shading correction + * 4) investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/videodev2.h> +#include <media/davinci/dm355_ccdc.h> +#include <media/davinci/vpss.h> +#include "dm355_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM355"); +MODULE_AUTHOR("Texas Instruments"); + +static struct device *dev; + +/* Object for CCDC raw mode */ +static struct ccdc_params_raw ccdc_hw_params_raw = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = 256, + .gb_g = 256, + .gr_cy = 256, + .b_mg = 256 + }, + .config_params = { + .datasft = 2, + .data_sz = CCDC_DATA_10BITS, + .mfilt1 = CCDC_NO_MEDIAN_FILTER1, + .mfilt2 = CCDC_NO_MEDIAN_FILTER2, + .alaw = { + .gama_wd = 2, + }, + .blk_clamp = { + .sample_pixel = 1, + .dc_sub = 25 + }, + .col_pat_field0 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + .col_pat_field1 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + }, +}; + + +/* Object for CCDC ycbcr mode */ +static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = { + .win = CCDC_WIN_PAL, + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED +}; + +static enum vpfe_hw_if_type ccdc_if_type; +static void *__iomem ccdc_base_addr; +static int ccdc_addr_size; + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_base_addr + offset); +} + +static void ccdc_set_ccdc_base(void *addr, int size) +{ + ccdc_base_addr = addr; + ccdc_addr_size = size; +} + +static void ccdc_enable(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~CCDC_SYNCEN_VDHDEN_MASK); + temp |= (en & CCDC_SYNCEN_VDHDEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_enable_output_to_sdram(int en) +{ + unsigned int temp; + temp = regr(SYNCEN); + temp &= (~(CCDC_SYNCEN_WEN_MASK)); + temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK); + regw(temp, SYNCEN); +} + +static void ccdc_config_gain_offset(void) +{ + /* configure gain */ + regw(ccdc_hw_params_raw.gain.r_ye, RYEGAIN); + regw(ccdc_hw_params_raw.gain.gr_cy, GRCYGAIN); + regw(ccdc_hw_params_raw.gain.gb_g, GBGGAIN); + regw(ccdc_hw_params_raw.gain.b_mg, BMGGAIN); + /* configure offset */ + regw(ccdc_hw_params_raw.ccdc_offset, OFFSET); +} + +/* + * ccdc_restore_defaults() + * This function restore power on defaults in the ccdc registers + */ +static int ccdc_restore_defaults(void) +{ + int i; + + dev_dbg(dev, "\nstarting ccdc_restore_defaults..."); + /* set all registers to zero */ + for (i = 0; i <= CCDC_REG_LAST; i += 4) + regw(0, i); + + /* now override the values with power on defaults in registers */ + regw(MODESET_DEFAULT, MODESET); + /* no culling support */ + regw(CULH_DEFAULT, CULH); + regw(CULV_DEFAULT, CULV); + /* Set default Gain and Offset */ + ccdc_hw_params_raw.gain.r_ye = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.gb_g = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.gr_cy = GAIN_DEFAULT; + ccdc_hw_params_raw.gain.b_mg = GAIN_DEFAULT; + ccdc_config_gain_offset(); + regw(OUTCLIP_DEFAULT, OUTCLIP); + regw(LSCCFG2_DEFAULT, LSCCFG2); + /* select ccdc input */ + if (vpss_select_ccdc_source(VPSS_CCDCIN)) { + dev_dbg(dev, "\ncouldn't select ccdc input source"); + return -EFAULT; + } + /* select ccdc clock */ + if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) { + dev_dbg(dev, "\ncouldn't enable ccdc clock"); + return -EFAULT; + } + dev_dbg(dev, "\nEnd of ccdc_restore_defaults..."); + return 0; +} + +static int ccdc_open(struct device *device) +{ + dev = device; + return ccdc_restore_defaults(); +} + +static int ccdc_close(struct device *device) +{ + /* disable clock */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 0); + /* do nothing for now */ + return 0; +} +/* + * ccdc_setwin() + * This function will configure the window size to + * be capture in CCDC reg. + */ +static void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int mid_img = 0; + + dev_dbg(dev, "\nStarting ccdc_setwin..."); + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + regw(horz_start, SPH); + regw(horz_nr_pixels, NPH); + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 and VDINT1 */ + regw(vert_start, VDINT0); + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + regw(vert_start, VDINT0); + regw(mid_img, VDINT1); + } + regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0); + regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1); + regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV); + dev_dbg(dev, "\nEnd of ccdc_setwin..."); +} + +static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) +{ + if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT || + ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) { + dev_dbg(dev, "Invalid value of data shift\n"); + return -EINVAL; + } + + if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 || + ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) { + dev_dbg(dev, "Invalid value of median filter1\n"); + return -EINVAL; + } + + if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 || + ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) { + dev_dbg(dev, "Invalid value of median filter2\n"); + return -EINVAL; + } + + if ((ccdcparam->med_filt_thres < 0) || + (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) { + dev_dbg(dev, "Invalid value of median filter thresold\n"); + return -EINVAL; + } + + if (ccdcparam->data_sz < CCDC_DATA_16BITS || + ccdcparam->data_sz > CCDC_DATA_8BITS) { + dev_dbg(dev, "Invalid value of data size\n"); + return -EINVAL; + } + + if (ccdcparam->alaw.enable) { + if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 || + ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) { + dev_dbg(dev, "Invalid value of ALAW\n"); + return -EINVAL; + } + } + + if (ccdcparam->blk_clamp.b_clamp_enable) { + if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS || + ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) { + dev_dbg(dev, "Invalid value of sample pixel\n"); + return -EINVAL; + } + if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES || + ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) { + dev_dbg(dev, "Invalid value of sample lines\n"); + return -EINVAL; + } + } + return 0; +} + +/* Parameter operations */ +static int ccdc_set_params(void __user *params) +{ + struct ccdc_config_params_raw ccdc_raw_params; + int x; + + /* only raw module parameters can be set through the IOCTL */ + if (ccdc_if_type != VPFE_RAW_BAYER) + return -EINVAL; + + x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); + if (x) { + dev_dbg(dev, "ccdc_set_params: error in copying ccdc" + "params, %d\n", x); + return -EFAULT; + } + + if (!validate_ccdc_param(&ccdc_raw_params)) { + memcpy(&ccdc_hw_params_raw.config_params, + &ccdc_raw_params, + sizeof(ccdc_raw_params)); + return 0; + } + return -EINVAL; +} + +/* This function will configure CCDC for YCbCr video capture */ +static void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr; + u32 temp; + + /* first set the CCDC power on defaults values in all registers */ + dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + ccdc_restore_defaults(); + + /* configure pixel format & video frame format */ + temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) << + CCDC_INPUT_MODE_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << + CCDC_FRM_FMT_SHIFT)); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, REC656IF); + /* + * configure the FID, VD, HD pin polarity fld,hd pol positive, + * vd negative, 8-bit pack mode + */ + temp |= CCDC_VD_POL_NEGATIVE; + } else { /* y/c external sync mode */ + temp |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + + /* pack the data to 8-bit */ + temp |= CCDC_DATA_PACK_ENABLE; + + regw(temp, MODESET); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* configure the order of y cb cr in SD-RAM */ + temp = (params->pix_order << CCDC_Y8POS_SHIFT); + temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC; + regw(temp, CCDCFG); + + /* + * configure the horizontal line offset. This is done by rounding up + * width to a multiple of 16 pixels and multiply by two to account for + * y:cb:cr 4:2:2 data + */ + regw(((params->win.width * 2 + 31) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST); + } + + dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); +} + +/* + * ccdc_config_black_clamp() + * configure parameters for Optical Black Clamp + */ +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->b_clamp_enable) { + /* configure DCSub */ + regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB); + regw(0x0000, CLAMP); + return; + } + /* Enable the Black clamping, set sample lines and pixels */ + val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE; + regw(val, CLAMP); + + /* If Black clamping is enable then make dcsub 0 */ + val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK) + << CCDC_NUM_LINE_CALC_SHIFT; + regw(val, DCSUB); +} + +/* + * ccdc_config_black_compense() + * configure parameters for Black Compensation + */ +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = (bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT); + regw(val, BLKCMP1); + + val = ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT); + regw(val, BLKCMP0); +} + +/* + * ccdc_write_dfc_entry() + * write an entry in the dfc table. + */ +int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) +{ +/* TODO This is to be re-visited and adjusted */ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 val, count = DFC_WRITE_WAIT_COUNT; + + regw(dfc->dft_corr_vert[index], DFCMEM0); + regw(dfc->dft_corr_horz[index], DFCMEM1); + regw(dfc->dft_corr_sub1[index], DFCMEM2); + regw(dfc->dft_corr_sub2[index], DFCMEM3); + regw(dfc->dft_corr_sub3[index], DFCMEM4); + /* set WR bit to write */ + val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK; + regw(val, DFCMEMCTL); + + /* + * Assume, it is very short. If we get an error, we need to + * adjust this value + */ + while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK) + count--; + /* + * TODO We expect the count to be non-zero to be successful. Adjust + * the count if write requires more time + */ + + if (count) { + dev_err(dev, "defect table write timeout !!!\n"); + return -1; + } + return 0; +} + +/* + * ccdc_config_vdfc() + * configure parameters for Vertical Defect Correction + */ +static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc) +{ + u32 val; + int i; + + /* Configure General Defect Correction. The table used is from IPIPE */ + val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK; + + /* Configure Vertical Defect Correction if needed */ + if (!dfc->ver_dft_en) { + /* Enable only General Defect Correction */ + regw(val, DFCCTL); + return 0; + } + + if (dfc->table_size > CCDC_DFT_TABLE_SIZE) + return -EINVAL; + + val |= CCDC_DFCCTL_VDFC_DISABLE; + val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) << + CCDC_DFCCTL_VDFCSL_SHIFT; + val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) << + CCDC_DFCCTL_VDFCUDA_SHIFT; + val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) << + CCDC_DFCCTL_VDFLSFT_SHIFT; + regw(val , DFCCTL); + + /* clear address ptr to offset 0 */ + val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT; + + /* write defect table entries */ + for (i = 0; i < dfc->table_size; i++) { + /* increment address for non zero index */ + if (i != 0) + val = CCDC_DFCMEMCTL_INC_ADDR; + regw(val, DFCMEMCTL); + if (ccdc_write_dfc_entry(i, dfc) < 0) + return -EFAULT; + } + + /* update saturation level and enable dfc */ + regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT); + val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK << + CCDC_DFCCTL_VDFCEN_SHIFT); + regw(val, DFCCTL); + return 0; +} + +/* + * ccdc_config_csc() + * configure parameters for color space conversion + * Each register CSCM0-7 has two values in S8Q5 format. + */ +static void ccdc_config_csc(struct ccdc_csc *csc) +{ + u32 val1, val2; + int i; + + if (!csc->enable) + return; + + /* Enable the CSC sub-module */ + regw(CCDC_CSC_ENABLE, CSCCTL); + + /* Converting the co-eff as per the format of the register */ + for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + /* + * convert decimal part to binary. Use 2 decimal + * precision, user values range from .00 - 0.99 + */ + val1 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + } else { + + /* CSCM - MSB */ + val2 = (csc->coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) + << CCDC_CSC_COEF_INTEG_SHIFT; + val2 |= (((csc->coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK) * + CCDC_CSC_DEC_MAX) / 100); + val2 <<= CCDC_CSCM_MSB_SHIFT; + val2 |= val1; + regw(val2, (CSCM0 + ((i - 1) << 1))); + } + } +} + +/* + * ccdc_config_color_patterns() + * configure parameters for color patterns + */ +static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0, + struct ccdc_col_pat *pat1) +{ + u32 val; + + val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) | + (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) | + (pat1->elop << 12) | (pat1->elep << 14)); + regw(val, COLPTN); +} + +/* This function will configure CCDC for Raw mode image capture */ +static int ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_hw_params_raw; + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int val; + + dev_dbg(dev, "\nStarting ccdc_config_raw..."); + + /* restore power on defaults to register */ + ccdc_restore_defaults(); + + /* CCDCFG register: + * set CCD Not to swap input since input is RAW data + * set FID detection function to Latch at V-Sync + * set WENLOG - ccdc valid area to AND + * set TRGSEL to WENBIT + * set EXTRG to DISABLE + * disable latching function on VSYNC - shadowed registers + */ + regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC | + CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN | + CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG); + + /* + * Set VDHD direction to input, input type to raw input + * normal data polarity, do not use external WEN + */ + val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL | + CCDC_EXWEN_DISABLE); + + /* + * Configure the vertical sync polarity (MODESET.VDPOL), horizontal + * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL), + * frame format(progressive or interlace), & pixel format (Input mode) + */ + val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT)); + + /* set pack for alaw compression */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + val |= CCDC_DATA_PACK_ENABLE; + + /* Configure for LPF */ + if (config_params->lpf_enable) + val |= (config_params->lpf_enable & CCDC_LPF_MASK) << + CCDC_LPF_SHIFT; + + /* Configure the data shift */ + val |= (config_params->datasft & CCDC_DATASFT_MASK) << + CCDC_DATASFT_SHIFT; + regw(val , MODESET); + dev_dbg(dev, "\nWriting 0x%x to MODESET...\n", val); + + /* Configure the Median Filter threshold */ + regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT); + + /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */ + val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT | + CCDC_CFA_MOSAIC; + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val |= (CCDC_ALAW_ENABLE | + ((config_params->alaw.gama_wd & + CCDC_ALAW_GAMA_WD_MASK) << + CCDC_GAMMAWD_INPUT_SHIFT)); + } + + /* Configure Median filter1 & filter2 */ + val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) | + (config_params->mfilt2 << CCDC_MFILT2_SHIFT)); + + regw(val, GAMMAWD); + dev_dbg(dev, "\nWriting 0x%x to GAMMAWD...\n", val); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 1); + + /* Optical Clamp Averaging */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* Vertical Defect Correction if needed */ + if (ccdc_config_vdfc(&config_params->vertical_dft) < 0) + return -EFAULT; + + /* color space conversion */ + ccdc_config_csc(&config_params->csc); + + /* color pattern */ + ccdc_config_color_patterns(&config_params->col_pat_field0, + &config_params->col_pat_field1); + + /* Configure the Gain & offset control */ + ccdc_config_gain_offset(); + + dev_dbg(dev, "\nWriting %x to COLPTN...\n", val); + + /* Configure DATAOFST register */ + val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_H_SHIFT; + val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) << + CCDC_DATAOFST_V_SHIFT; + regw(val, DATAOFST); + + /* configuring HSIZE register */ + val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) << + CCDC_HSIZE_FLIP_SHIFT; + + /* If pack 8 is enable then 1 pixel will take 1 byte */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) { + val |= (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + /* adjust to multiple of 32 */ + dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } else { + /* else one pixel will take 2 byte */ + val |= (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK; + + dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n", + (((params->win.width * 2) + 31) >> 5) & + CCDC_HSIZE_VAL_MASK); + } + regw(val, HSIZE); + + /* Configure SDOFST register */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For interlace inverse mode */ + regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_INVERSE); + } else { + /* For interlace non inverse mode */ + regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_INTERLACE_NORMAL); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + if (params->image_invert_enable) { + /* For progessive inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_INVERSE); + } else { + /* For progessive non inverse mode */ + regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST); + dev_dbg(dev, "\nWriting %x to SDOFST...\n", + CCDC_SDOFST_PROGRESSIVE_NORMAL); + } + } + dev_dbg(dev, "\nend of ccdc_config_raw..."); + return 0; +} + +static int ccdc_configure(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.buf_type = buf_type; + else + ccdc_hw_params_ycbcr.buf_type = buf_type; + return 0; +} +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.buf_type; + return ccdc_hw_params_ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + alaw->enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + u32 pixfmt; + + if (ccdc_if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.win = *win; + else + ccdc_hw_params_ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + *win = ccdc_hw_params_raw.win; + else + *win = ccdc_hw_params_ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int len; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_hw_params_raw.win.width; + else + len = ccdc_hw_params_raw.win.width * 2; + } else + len = ccdc_hw_params_ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.frm_fmt = frm_fmt; + else + ccdc_hw_params_ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.frm_fmt; + else + return ccdc_hw_params_ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(MODESET) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw((addr >> 21) & 0x007f, STADRH); + regw((addr >> 5) & 0x0ffff, STADRL); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + ccdc_hw_params_ycbcr.vd_pol = params->vdpol; + ccdc_hw_params_ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM355 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .set_ccdc_base = ccdc_set_ccdc_base, + .enable = ccdc_enable, + .enable_out_to_sdram = ccdc_enable_output_to_sdram, + .set_hw_if_params = ccdc_set_hw_if_params, + .set_params = ccdc_set_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +static int dm355_ccdc_init(void) +{ + printk(KERN_NOTICE "dm355_ccdc_init\n"); + if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) + return -1; + printk(KERN_NOTICE "%s is registered with vpfe.\n", + ccdc_hw_dev.name); + return 0; +} + +static void dm355_ccdc_exit(void) +{ + vpfe_unregister_ccdc_device(&ccdc_hw_dev); +} + +module_init(dm355_ccdc_init); +module_exit(dm355_ccdc_exit); diff --git a/drivers/media/video/davinci/dm355_ccdc_regs.h b/drivers/media/video/davinci/dm355_ccdc_regs.h new file mode 100644 index 000000000000..d6d2ef0533b5 --- /dev/null +++ b/drivers/media/video/davinci/dm355_ccdc_regs.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2005-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM355_CCDC_REGS_H +#define _DM355_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDWIDTH 0x08 +#define VDWIDTH 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define NPH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define NLV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define STADRH 0x3c +#define STADRL 0x40 +#define CLAMP 0x44 +#define DCSUB 0x48 +#define COLPTN 0x4c +#define BLKCMP0 0x50 +#define BLKCMP1 0x54 +#define MEDFILT 0x58 +#define RYEGAIN 0x5c +#define GRCYGAIN 0x60 +#define GBGGAIN 0x64 +#define BMGGAIN 0x68 +#define OFFSET 0x6c +#define OUTCLIP 0x70 +#define VDINT0 0x74 +#define VDINT1 0x78 +#define RSV0 0x7c +#define GAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +#define FMTCFG 0x8c +#define FMTPLEN 0x90 +#define FMTSPH 0x94 +#define FMTLNH 0x98 +#define FMTSLV 0x9c +#define FMTLNV 0xa0 +#define FMTRLEN 0xa4 +#define FMTHCNT 0xa8 +#define FMT_ADDR_PTR_B 0xac +#define FMT_ADDR_PTR(i) (FMT_ADDR_PTR_B + (i * 4)) +#define FMTPGM_VF0 0xcc +#define FMTPGM_VF1 0xd0 +#define FMTPGM_AP0 0xd4 +#define FMTPGM_AP1 0xd8 +#define FMTPGM_AP2 0xdc +#define FMTPGM_AP3 0xe0 +#define FMTPGM_AP4 0xe4 +#define FMTPGM_AP5 0xe8 +#define FMTPGM_AP6 0xec +#define FMTPGM_AP7 0xf0 +#define LSCCFG1 0xf4 +#define LSCCFG2 0xf8 +#define LSCH0 0xfc +#define LSCV0 0x100 +#define LSCKH 0x104 +#define LSCKV 0x108 +#define LSCMEMCTL 0x10c +#define LSCMEMD 0x110 +#define LSCMEMQ 0x114 +#define DFCCTL 0x118 +#define DFCVSAT 0x11c +#define DFCMEMCTL 0x120 +#define DFCMEM0 0x124 +#define DFCMEM1 0x128 +#define DFCMEM2 0x12c +#define DFCMEM3 0x130 +#define DFCMEM4 0x134 +#define CSCCTL 0x138 +#define CSCM0 0x13c +#define CSCM1 0x140 +#define CSCM2 0x144 +#define CSCM3 0x148 +#define CSCM4 0x14c +#define CSCM5 0x150 +#define CSCM6 0x154 +#define CSCM7 0x158 +#define DATAOFST 0x15c +#define CCDC_REG_LAST DATAOFST +/************************************************************** +* Define for various register bit mask and shifts for CCDC +* +**************************************************************/ +#define CCDC_RAW_IP_MODE 0 +#define CCDC_VDHDOUT_INPUT 0 +#define CCDC_YCINSWP_RAW (0 << 4) +#define CCDC_EXWEN_DISABLE 0 +#define CCDC_DATAPOL_NORMAL 0 +#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC 0 +#define CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC (1 << 6) +#define CCDC_CCDCFG_WENLOG_AND 0 +#define CCDC_CCDCFG_TRGSEL_WEN 0 +#define CCDC_CCDCFG_EXTRG_DISABLE 0 +#define CCDC_CFA_MOSAIC 0 +#define CCDC_Y8POS_SHIFT 11 + +#define CCDC_VDC_DFCVSAT_MASK 0x3fff +#define CCDC_DATAOFST_MASK 0x0ff +#define CCDC_DATAOFST_H_SHIFT 0 +#define CCDC_DATAOFST_V_SHIFT 8 +#define CCDC_GAMMAWD_CFA_MASK 1 +#define CCDC_GAMMAWD_CFA_SHIFT 5 +#define CCDC_GAMMAWD_INPUT_SHIFT 2 +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_VD_POL_NEGATIVE (1 << 2) +#define CCDC_FRM_FMT_MASK 1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_VDHDOUT_MASK 1 +#define CCDC_VDHDOUT_SHIFT 0 +#define CCDC_EXWEN_MASK 1 +#define CCDC_EXWEN_SHIFT 5 +#define CCDC_INPUT_MODE_MASK 3 +#define CCDC_INPUT_MODE_SHIFT 12 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_DATAPOL_MASK 1 +#define CCDC_DATAPOL_SHIFT 6 +#define CCDC_WEN_ENABLE (1 << 1) +#define CCDC_VDHDEN_ENABLE (1 << 16) +#define CCDC_LPF_ENABLE (1 << 14) +#define CCDC_ALAW_ENABLE 1 +#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_REC656IF_BT656_EN 3 + +#define CCDC_FMTCFG_FMTMODE_MASK 3 +#define CCDC_FMTCFG_FMTMODE_SHIFT 1 +#define CCDC_FMTCFG_LNUM_MASK 3 +#define CCDC_FMTCFG_LNUM_SHIFT 4 +#define CCDC_FMTCFG_ADDRINC_MASK 7 +#define CCDC_FMTCFG_ADDRINC_SHIFT 8 + +#define CCDC_CCDCFG_FIDMD_SHIFT 6 +#define CCDC_CCDCFG_WENLOG_SHIFT 8 +#define CCDC_CCDCFG_TRGSEL_SHIFT 9 +#define CCDC_CCDCFG_EXTRG_SHIFT 10 +#define CCDC_CCDCFG_MSBINVI_SHIFT 13 + +#define CCDC_HSIZE_FLIP_SHIFT 12 +#define CCDC_HSIZE_FLIP_MASK 1 +#define CCDC_HSIZE_VAL_MASK 0xFFF +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_SDOFST_INTERLACE_INVERSE 0x4B6D +#define CCDC_SDOFST_INTERLACE_NORMAL 0x0B6D +#define CCDC_SDOFST_PROGRESSIVE_INVERSE 0x4000 +#define CCDC_SDOFST_PROGRESSIVE_NORMAL 0 +#define CCDC_START_PX_HOR_MASK 0x7FFF +#define CCDC_NUM_PX_HOR_MASK 0x7FFF +#define CCDC_START_VER_ONE_MASK 0x7FFF +#define CCDC_START_VER_TWO_MASK 0x7FFF +#define CCDC_NUM_LINES_VER 0x7FFF + +#define CCDC_BLK_CLAMP_ENABLE (1 << 15) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x1FFF +#define CCDC_BLK_SAMPLE_LN_MASK 3 +#define CCDC_BLK_SAMPLE_LN_SHIFT 13 + +#define CCDC_NUM_LINE_CALC_MASK 3 +#define CCDC_NUM_LINE_CALC_SHIFT 14 + +#define CCDC_BLK_DC_SUB_MASK 0x3FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 0 +#define CCDC_BLK_COMP_R_COMP_SHIFT 8 +#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define CCDC_LATCH_ON_VSYNC_ENABLE (0 << 15) +#define CCDC_FPC_ENABLE (1 << 15) +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE (1 << 11) +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF + +#define CCDC_CSC_COEF_INTEG_MASK 7 +#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f +#define CCDC_CSC_COEF_INTEG_SHIFT 5 +#define CCDC_CSCM_MSB_SHIFT 8 +#define CCDC_CSC_ENABLE 1 +#define CCDC_CSC_DEC_MAX 32 + +#define CCDC_MFILT1_SHIFT 10 +#define CCDC_MFILT2_SHIFT 8 +#define CCDC_MED_FILT_THRESH 0x3FFF +#define CCDC_LPF_MASK 1 +#define CCDC_LPF_SHIFT 14 +#define CCDC_OFFSET_MASK 0x3FF +#define CCDC_DATASFT_MASK 7 +#define CCDC_DATASFT_SHIFT 8 + +#define CCDC_DF_ENABLE 1 + +#define CCDC_FMTPLEN_P0_MASK 0xF +#define CCDC_FMTPLEN_P1_MASK 0xF +#define CCDC_FMTPLEN_P2_MASK 7 +#define CCDC_FMTPLEN_P3_MASK 7 +#define CCDC_FMTPLEN_P0_SHIFT 0 +#define CCDC_FMTPLEN_P1_SHIFT 4 +#define CCDC_FMTPLEN_P2_SHIFT 8 +#define CCDC_FMTPLEN_P3_SHIFT 12 + +#define CCDC_FMTSPH_MASK 0x1FFF +#define CCDC_FMTLNH_MASK 0x1FFF +#define CCDC_FMTSLV_MASK 0x1FFF +#define CCDC_FMTLNV_MASK 0x7FFF +#define CCDC_FMTRLEN_MASK 0x1FFF +#define CCDC_FMTHCNT_MASK 0x1FFF + +#define CCDC_ADP_INIT_MASK 0x1FFF +#define CCDC_ADP_LINE_SHIFT 13 +#define CCDC_ADP_LINE_MASK 3 +#define CCDC_FMTPGN_APTR_MASK 7 + +#define CCDC_DFCCTL_GDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFCEN_MASK 1 +#define CCDC_DFCCTL_VDFC_DISABLE (0 << 4) +#define CCDC_DFCCTL_VDFCEN_SHIFT 4 +#define CCDC_DFCCTL_VDFCSL_MASK 3 +#define CCDC_DFCCTL_VDFCSL_SHIFT 5 +#define CCDC_DFCCTL_VDFCUDA_MASK 1 +#define CCDC_DFCCTL_VDFCUDA_SHIFT 7 +#define CCDC_DFCCTL_VDFLSFT_MASK 3 +#define CCDC_DFCCTL_VDFLSFT_SHIFT 8 +#define CCDC_DFCMEMCTL_DFCMARST_MASK 1 +#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2 +#define CCDC_DFCMEMCTL_DFCMWR_MASK 1 +#define CCDC_DFCMEMCTL_DFCMWR_SHIFT 0 +#define CCDC_DFCMEMCTL_INC_ADDR (0 << 2) + +#define CCDC_LSCCFG_GFTSF_MASK 7 +#define CCDC_LSCCFG_GFTSF_SHIFT 1 +#define CCDC_LSCCFG_GFTINV_MASK 0xf +#define CCDC_LSCCFG_GFTINV_SHIFT 4 +#define CCDC_LSC_GFTABLE_SEL_MASK 3 +#define CCDC_LSC_GFTABLE_EPEL_SHIFT 8 +#define CCDC_LSC_GFTABLE_OPEL_SHIFT 10 +#define CCDC_LSC_GFTABLE_EPOL_SHIFT 12 +#define CCDC_LSC_GFTABLE_OPOL_SHIFT 14 +#define CCDC_LSC_GFMODE_MASK 3 +#define CCDC_LSC_GFMODE_SHIFT 4 +#define CCDC_LSC_DISABLE 0 +#define CCDC_LSC_ENABLE 1 +#define CCDC_LSC_TABLE1_SLC 0 +#define CCDC_LSC_TABLE2_SLC 1 +#define CCDC_LSC_TABLE3_SLC 2 +#define CCDC_LSC_MEMADDR_RESET (1 << 2) +#define CCDC_LSC_MEMADDR_INCR (0 << 2) +#define CCDC_LSC_FRAC_MASK_T1 0xFF +#define CCDC_LSC_INT_MASK 3 +#define CCDC_LSC_FRAC_MASK 0x3FFF +#define CCDC_LSC_CENTRE_MASK 0x3FFF +#define CCDC_LSC_COEF_MASK 0xff +#define CCDC_LSC_COEFL_SHIFT 0 +#define CCDC_LSC_COEFU_SHIFT 8 +#define CCDC_GAIN_MASK 0x7FF +#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0) +#define CCDC_SYNCEN_WEN_MASK (1 << 1) +#define CCDC_SYNCEN_WEN_SHIFT 1 + +/* Power on Defaults in hardware */ +#define MODESET_DEFAULT 0x200 +#define CULH_DEFAULT 0xFFFF +#define CULV_DEFAULT 0xFF +#define GAIN_DEFAULT 256 +#define OUTCLIP_DEFAULT 0x3FFF +#define LSCCFG2_DEFAULT 0xE + +#endif diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c new file mode 100644 index 000000000000..2f19a919f477 --- /dev/null +++ b/drivers/media/video/davinci/dm644x_ccdc.c @@ -0,0 +1,878 @@ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * CCDC hardware module for DM6446 + * ------------------------------ + * + * This module is for configuring CCD controller of DM6446 VPFE to capture + * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules + * such as Defect Pixel Correction, Color Space Conversion etc to + * pre-process the Raw Bayer RGB data, before writing it to SDRAM. This + * module also allows application to configure individual + * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. + * To do so, application includes dm644x_ccdc.h and vpfe_capture.h header + * files. The setparams() API is called by vpfe_capture driver + * to configure module parameters. This file is named DM644x so that other + * variants such DM6443 may be supported using the same module. + * + * TODO: Test Raw bayer parameter settings and bayer capture + * Split module parameter structure to module specific ioctl structs + * investigate if enum used for user space type definition + * to be replaced by #defines or integer + */ +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/videodev2.h> +#include <media/davinci/dm644x_ccdc.h> +#include <media/davinci/vpss.h> +#include "dm644x_ccdc_regs.h" +#include "ccdc_hw_device.h" + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CCDC Driver for DM6446"); +MODULE_AUTHOR("Texas Instruments"); + +static struct device *dev; + +/* Object for CCDC raw mode */ +static struct ccdc_params_raw ccdc_hw_params_raw = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .config_params = { + .data_sz = CCDC_DATA_10BITS, + }, +}; + +/* Object for CCDC ycbcr mode */ +static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = { + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = CCDC_WIN_PAL, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED +}; + +#define CCDC_MAX_RAW_YUV_FORMATS 2 + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = + {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = + {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +static void *__iomem ccdc_base_addr; +static int ccdc_addr_size; +static enum vpfe_hw_if_type ccdc_if_type; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(ccdc_base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, ccdc_base_addr + offset); +} + +static void ccdc_set_ccdc_base(void *addr, int size) +{ + ccdc_base_addr = addr; + ccdc_addr_size = size; +} + +static void ccdc_enable(int flag) +{ + regw(flag, CCDC_PCR); +} + +static void ccdc_enable_vport(int flag) +{ + if (flag) + /* enable video port */ + regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG); + else + regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG); +} + +/* + * ccdc_setwin() + * This function will configure the window size + * to be capture in CCDC reg + */ +void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, + int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int val = 0, mid_img = 0; + + dev_dbg(dev, "\nStarting ccdc_setwin..."); + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels, + CCDC_HORZ_INFO); + + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 */ + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT); + regw(val, CCDC_VDINT); + + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* + * configure VDINT0 and VDINT1. VDINT1 will be at half + * of image height + */ + mid_img = vert_start + (image_win->height / 2); + val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) | + (mid_img & CCDC_VDINT_VDINT1_MASK); + regw(val, CCDC_VDINT); + + } + regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start, + CCDC_VERT_START); + regw(vert_nr_lines, CCDC_VERT_LINES); + dev_dbg(dev, "\nEnd of ccdc_setwin..."); +} + +static void ccdc_readregs(void) +{ + unsigned int val = 0; + + val = regr(CCDC_ALAW); + dev_notice(dev, "\nReading 0x%x to ALAW...\n", val); + val = regr(CCDC_CLAMP); + dev_notice(dev, "\nReading 0x%x to CLAMP...\n", val); + val = regr(CCDC_DCSUB); + dev_notice(dev, "\nReading 0x%x to DCSUB...\n", val); + val = regr(CCDC_BLKCMP); + dev_notice(dev, "\nReading 0x%x to BLKCMP...\n", val); + val = regr(CCDC_FPC_ADDR); + dev_notice(dev, "\nReading 0x%x to FPC_ADDR...\n", val); + val = regr(CCDC_FPC); + dev_notice(dev, "\nReading 0x%x to FPC...\n", val); + val = regr(CCDC_FMTCFG); + dev_notice(dev, "\nReading 0x%x to FMTCFG...\n", val); + val = regr(CCDC_COLPTN); + dev_notice(dev, "\nReading 0x%x to COLPTN...\n", val); + val = regr(CCDC_FMT_HORZ); + dev_notice(dev, "\nReading 0x%x to FMT_HORZ...\n", val); + val = regr(CCDC_FMT_VERT); + dev_notice(dev, "\nReading 0x%x to FMT_VERT...\n", val); + val = regr(CCDC_HSIZE_OFF); + dev_notice(dev, "\nReading 0x%x to HSIZE_OFF...\n", val); + val = regr(CCDC_SDOFST); + dev_notice(dev, "\nReading 0x%x to SDOFST...\n", val); + val = regr(CCDC_VP_OUT); + dev_notice(dev, "\nReading 0x%x to VP_OUT...\n", val); + val = regr(CCDC_SYN_MODE); + dev_notice(dev, "\nReading 0x%x to SYN_MODE...\n", val); + val = regr(CCDC_HORZ_INFO); + dev_notice(dev, "\nReading 0x%x to HORZ_INFO...\n", val); + val = regr(CCDC_VERT_START); + dev_notice(dev, "\nReading 0x%x to VERT_START...\n", val); + val = regr(CCDC_VERT_LINES); + dev_notice(dev, "\nReading 0x%x to VERT_LINES...\n", val); +} + +static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) +{ + if (ccdcparam->alaw.enable) { + if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) || + (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) || + (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) { + dev_dbg(dev, "\nInvalid data line select"); + return -1; + } + } + return 0; +} + +static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int *fpc_virtaddr = NULL; + unsigned int *fpc_physaddr = NULL; + + memcpy(config_params, raw_params, sizeof(*raw_params)); + /* + * allocate memory for fault pixel table and copy the user + * values to the table + */ + if (!config_params->fault_pxl.enable) + return 0; + + fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; + fpc_virtaddr = (unsigned int *)phys_to_virt( + (unsigned long)fpc_physaddr); + /* + * Allocate memory for FPC table if current + * FPC table buffer is not big enough to + * accomodate FPC Number requested + */ + if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) { + if (fpc_physaddr != NULL) { + free_pages((unsigned long)fpc_physaddr, + get_order + (config_params->fault_pxl.fp_num * + FP_NUM_BYTES)); + } + + /* Allocate memory for FPC table */ + fpc_virtaddr = + (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA, + get_order(raw_params-> + fault_pxl.fp_num * + FP_NUM_BYTES)); + + if (fpc_virtaddr == NULL) { + dev_dbg(dev, + "\nUnable to allocate memory for FPC"); + return -EFAULT; + } + fpc_physaddr = + (unsigned int *)virt_to_phys((void *)fpc_virtaddr); + } + + /* Copy number of fault pixels and FPC table */ + config_params->fault_pxl.fp_num = raw_params->fault_pxl.fp_num; + if (copy_from_user(fpc_virtaddr, + (void __user *)raw_params->fault_pxl.fpc_table_addr, + config_params->fault_pxl.fp_num * FP_NUM_BYTES)) { + dev_dbg(dev, "\n copy_from_user failed"); + return -EFAULT; + } + config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr; + return 0; +} + +static int ccdc_close(struct device *dev) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL; + + fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; + + if (fpc_physaddr != NULL) { + fpc_virtaddr = (unsigned int *) + phys_to_virt((unsigned long)fpc_physaddr); + free_pages((unsigned long)fpc_virtaddr, + get_order(config_params->fault_pxl.fp_num * + FP_NUM_BYTES)); + } + return 0; +} + +/* + * ccdc_restore_defaults() + * This function will write defaults to all CCDC registers + */ +static void ccdc_restore_defaults(void) +{ + int i; + + /* disable CCDC */ + ccdc_enable(0); + /* set all registers to default value */ + for (i = 4; i <= 0x94; i += 4) + regw(0, i); + regw(CCDC_NO_CULLING, CCDC_CULLING); + regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW); +} + +static int ccdc_open(struct device *device) +{ + dev = device; + ccdc_restore_defaults(); + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_enable_vport(1); + return 0; +} + +static void ccdc_sbl_reset(void) +{ + vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O); +} + +/* Parameter operations */ +static int ccdc_set_params(void __user *params) +{ + struct ccdc_config_params_raw ccdc_raw_params; + int x; + + if (ccdc_if_type != VPFE_RAW_BAYER) + return -EINVAL; + + x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); + if (x) { + dev_dbg(dev, "ccdc_set_params: error in copying" + "ccdc params, %d\n", x); + return -EFAULT; + } + + if (!validate_ccdc_param(&ccdc_raw_params)) { + if (!ccdc_update_raw_params(&ccdc_raw_params)) + return 0; + } + return -EINVAL; +} + +/* + * ccdc_config_ycbcr() + * This function will configure CCDC for YCbCr video capture + */ +void ccdc_config_ycbcr(void) +{ + struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr; + u32 syn_mode; + + dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + /* + * first restore the CCDC registers to default values + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + ccdc_restore_defaults(); + + /* + * configure pixel format, frame format, configure video frame + * format, enable output to SDRAM, enable internal timing generator + * and 8bit pack mode + */ + syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) << + CCDC_SYN_MODE_INPMOD_SHIFT) | + ((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) << + CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE | + CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF); + + /* + * configure the FID, VD, HD pin polarity, + * fld,hd pol positive, vd negative, 8-bit data + */ + syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE | CCDC_SYN_MODE_8BITS; + } else { + /* y/c external sync mode */ + syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | + ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT)); + } + regw(syn_mode, CCDC_SYN_MODE); + + /* configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 2); + + /* + * configure the order of y cb cr in SDRAM, and disable latch + * internal register on vsync + */ + regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) | + CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * configure the horizontal line offset. This should be a + * on 32 byte bondary. So clear LSB 5 bits + */ + regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST); + + ccdc_sbl_reset(); + dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); + ccdc_readregs(); +} + +static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->enable) { + /* configure DCSub */ + val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK; + regw(val, CCDC_DCSUB); + dev_dbg(dev, "\nWriting 0x%x to DCSUB...\n", val); + regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP); + dev_dbg(dev, "\nWriting 0x0000 to CLAMP...\n"); + return; + } + /* + * Configure gain, Start pixel, No of line to be avg, + * No of pixel/line to be avg, & Enable the Black clamping + */ + val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) | + ((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) << + CCDC_BLK_ST_PXL_SHIFT) | + ((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) << + CCDC_BLK_SAMPLE_LINE_SHIFT) | + ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << + CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE); + regw(val, CCDC_CLAMP); + dev_dbg(dev, "\nWriting 0x%x to CLAMP...\n", val); + /* If Black clamping is enable then make dcsub 0 */ + regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB); + dev_dbg(dev, "\nWriting 0x00000000 to DCSUB...\n"); +} + +static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) +{ + u32 val; + + val = ((bcomp->b & CCDC_BLK_COMP_MASK) | + ((bcomp->gb & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GB_COMP_SHIFT) | + ((bcomp->gr & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & CCDC_BLK_COMP_MASK) << + CCDC_BLK_COMP_R_COMP_SHIFT)); + regw(val, CCDC_BLKCMP); +} + +static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc) +{ + u32 val; + + /* Initially disable FPC */ + val = CCDC_FPC_DISABLE; + regw(val, CCDC_FPC); + + if (!fpc->enable) + return; + + /* Configure Fault pixel if needed */ + regw(fpc->fpc_table_addr, CCDC_FPC_ADDR); + dev_dbg(dev, "\nWriting 0x%x to FPC_ADDR...\n", + (fpc->fpc_table_addr)); + /* Write the FPC params with FPC disable */ + val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK; + regw(val, CCDC_FPC); + + dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val); + /* read the FPC register */ + val = regr(CCDC_FPC) | CCDC_FPC_ENABLE; + regw(val, CCDC_FPC); + dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val); +} + +/* + * ccdc_config_raw() + * This function will configure CCDC for Raw capture mode + */ +void ccdc_config_raw(void) +{ + struct ccdc_params_raw *params = &ccdc_hw_params_raw; + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int syn_mode = 0; + unsigned int val; + + dev_dbg(dev, "\nStarting ccdc_config_raw..."); + + /* Reset CCDC */ + ccdc_restore_defaults(); + + /* Disable latching function registers on VSYNC */ + regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG); + + /* + * Configure the vertical sync polarity(SYN_MODE.VDPOL), + * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity + * (SYN_MODE.FLDPOL), frame format(progressive or interlace), + * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output + * SDRAM, enable internal timing generator + */ + syn_mode = + (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) | + ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) | + ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) | + ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | + ((config_params->data_sz & CCDC_DATA_SZ_MASK) << + CCDC_DATA_SZ_SHIFT) | + ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) | + CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE); + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val = ((config_params->alaw.gama_wd & + CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE); + regw(val, CCDC_ALAW); + dev_dbg(dev, "\nWriting 0x%x to ALAW...\n", val); + } + + /* Configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, CCDC_PPC_RAW); + + /* Configure Black Clamp */ + ccdc_config_black_clamp(&config_params->blk_clamp); + + /* Configure Black level compensation */ + ccdc_config_black_compense(&config_params->blk_comp); + + /* Configure Fault Pixel Correction */ + ccdc_config_fpc(&config_params->fault_pxl); + + /* If data size is 8 bit then pack the data */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + syn_mode |= CCDC_DATA_PACK_ENABLE; + +#ifdef CONFIG_DM644X_VIDEO_PORT_ENABLE + /* enable video port */ + val = CCDC_ENABLE_VIDEO_PORT; +#else + /* disable video port */ + val = CCDC_DISABLE_VIDEO_PORT; +#endif + + if (config_params->data_sz == CCDC_DATA_8BITS) + val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + else + val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK) + << CCDC_FMTCFG_VPIN_SHIFT; + /* Write value in FMTCFG */ + regw(val, CCDC_FMTCFG); + + dev_dbg(dev, "\nWriting 0x%x to FMTCFG...\n", val); + /* Configure the color pattern according to mt9t001 sensor */ + regw(CCDC_COLPTN_VAL, CCDC_COLPTN); + + dev_dbg(dev, "\nWriting 0xBB11BB11 to COLPTN...\n"); + /* + * Configure Data formatter(Video port) pixel selection + * (FMT_HORZ, FMT_VERT) + */ + val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) << + CCDC_FMT_HORZ_FMTSPH_SHIFT) | + (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK); + regw(val, CCDC_FMT_HORZ); + + dev_dbg(dev, "\nWriting 0x%x to FMT_HORZ...\n", val); + val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK) + << CCDC_FMT_VERT_FMTSLV_SHIFT; + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK; + else + val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK; + + dev_dbg(dev, "\nparams->win.height 0x%x ...\n", + params->win.height); + regw(val, CCDC_FMT_VERT); + + dev_dbg(dev, "\nWriting 0x%x to FMT_VERT...\n", val); + + dev_dbg(dev, "\nbelow regw(val, FMT_VERT)..."); + + /* + * Configure Horizontal offset register. If pack 8 is enabled then + * 1 pixel will take 1 byte + */ + if ((config_params->data_sz == CCDC_DATA_8BITS) || + config_params->alaw.enable) + regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) & + CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF); + else + /* else one pixel will take 2 byte */ + regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) + + CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK, + CCDC_HSIZE_OFF); + + /* Set value for SDOFST */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For intelace inverse mode */ + regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x4B6D to SDOFST...\n"); + } + + else { + /* For intelace non inverse mode */ + regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x0249 to SDOFST...\n"); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST); + dev_dbg(dev, "\nWriting 0x0000 to SDOFST...\n"); + } + + /* + * Configure video port pixel selection (VPOUT) + * Here -1 is to make the height value less than FMT_VERT.FMTLNV + */ + if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK)) + << CCDC_VP_OUT_VERT_NUM_SHIFT; + else + val = + ((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) - + 1) & CCDC_VP_OUT_VERT_NUM_MASK)) << + CCDC_VP_OUT_VERT_NUM_SHIFT; + + val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK) + << CCDC_VP_OUT_HORZ_NUM_SHIFT; + val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK; + regw(val, CCDC_VP_OUT); + + dev_dbg(dev, "\nWriting 0x%x to VP_OUT...\n", val); + regw(syn_mode, CCDC_SYN_MODE); + dev_dbg(dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode); + + ccdc_sbl_reset(); + dev_dbg(dev, "\nend of ccdc_config_raw..."); + ccdc_readregs(); +} + +static int ccdc_configure(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_config_raw(); + else + ccdc_config_ycbcr(); + return 0; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.buf_type = buf_type; + else + ccdc_hw_params_ycbcr.buf_type = buf_type; + return 0; +} + +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.buf_type; + return ccdc_hw_params_ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + if (ccdc_if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + return ret; +} + +static int ccdc_set_pixel_format(u32 pixfmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) { + ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW; + if (pixfmt == V4L2_PIX_FMT_SBGGR8) + ccdc_hw_params_raw.config_params.alaw.enable = 1; + else if (pixfmt != V4L2_PIX_FMT_SBGGR16) + return -EINVAL; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + } + return 0; +} + +static u32 ccdc_get_pixel_format(void) +{ + struct ccdc_a_law *alaw = + &ccdc_hw_params_raw.config_params.alaw; + u32 pixfmt; + + if (ccdc_if_type == VPFE_RAW_BAYER) + if (alaw->enable) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} + +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.win = *win; + else + ccdc_hw_params_ycbcr.win = *win; + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + *win = ccdc_hw_params_raw.win; + else + *win = ccdc_hw_params_ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + struct ccdc_config_params_raw *config_params = + &ccdc_hw_params_raw.config_params; + unsigned int len; + + if (ccdc_if_type == VPFE_RAW_BAYER) { + if ((config_params->alaw.enable) || + (config_params->data_sz == CCDC_DATA_8BITS)) + len = ccdc_hw_params_raw.win.width; + else + len = ccdc_hw_params_raw.win.width * 2; + } else + len = ccdc_hw_params_ycbcr.win.width * 2; + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + ccdc_hw_params_raw.frm_fmt = frm_fmt; + else + ccdc_hw_params_ycbcr.frm_fmt = frm_fmt; + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_if_type == VPFE_RAW_BAYER) + return ccdc_hw_params_raw.frm_fmt; + else + return ccdc_hw_params_ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(CCDC_SYN_MODE) >> 15) & 1; +} + +/* misc operations */ +static inline void ccdc_setfbaddr(unsigned long addr) +{ + regw(addr & 0xffffffe0, CCDC_SDR_ADDR); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + ccdc_hw_params_ycbcr.vd_pol = params->vdpol; + ccdc_hw_params_ycbcr.hd_pol = params->hdpol; + break; + default: + /* TODO add support for raw bayer here */ + return -EINVAL; + } + return 0; +} + +static struct ccdc_hw_device ccdc_hw_dev = { + .name = "DM6446 CCDC", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .set_ccdc_base = ccdc_set_ccdc_base, + .reset = ccdc_sbl_reset, + .enable = ccdc_enable, + .set_hw_if_params = ccdc_set_hw_if_params, + .set_params = ccdc_set_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +static int dm644x_ccdc_init(void) +{ + printk(KERN_NOTICE "dm644x_ccdc_init\n"); + if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) + return -1; + printk(KERN_NOTICE "%s is registered with vpfe.\n", + ccdc_hw_dev.name); + return 0; +} + +static void dm644x_ccdc_exit(void) +{ + vpfe_unregister_ccdc_device(&ccdc_hw_dev); +} + +module_init(dm644x_ccdc_init); +module_exit(dm644x_ccdc_exit); diff --git a/drivers/media/video/davinci/dm644x_ccdc_regs.h b/drivers/media/video/davinci/dm644x_ccdc_regs.h new file mode 100644 index 000000000000..6e5d05324466 --- /dev/null +++ b/drivers/media/video/davinci/dm644x_ccdc_regs.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2006-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM644X_CCDC_REGS_H +#define _DM644X_CCDC_REGS_H + +/**************************************************************************\ +* Register OFFSET Definitions +\**************************************************************************/ +#define CCDC_PID 0x0 +#define CCDC_PCR 0x4 +#define CCDC_SYN_MODE 0x8 +#define CCDC_HD_VD_WID 0xc +#define CCDC_PIX_LINES 0x10 +#define CCDC_HORZ_INFO 0x14 +#define CCDC_VERT_START 0x18 +#define CCDC_VERT_LINES 0x1c +#define CCDC_CULLING 0x20 +#define CCDC_HSIZE_OFF 0x24 +#define CCDC_SDOFST 0x28 +#define CCDC_SDR_ADDR 0x2c +#define CCDC_CLAMP 0x30 +#define CCDC_DCSUB 0x34 +#define CCDC_COLPTN 0x38 +#define CCDC_BLKCMP 0x3c +#define CCDC_FPC 0x40 +#define CCDC_FPC_ADDR 0x44 +#define CCDC_VDINT 0x48 +#define CCDC_ALAW 0x4c +#define CCDC_REC656IF 0x50 +#define CCDC_CCDCFG 0x54 +#define CCDC_FMTCFG 0x58 +#define CCDC_FMT_HORZ 0x5c +#define CCDC_FMT_VERT 0x60 +#define CCDC_FMT_ADDR0 0x64 +#define CCDC_FMT_ADDR1 0x68 +#define CCDC_FMT_ADDR2 0x6c +#define CCDC_FMT_ADDR3 0x70 +#define CCDC_FMT_ADDR4 0x74 +#define CCDC_FMT_ADDR5 0x78 +#define CCDC_FMT_ADDR6 0x7c +#define CCDC_FMT_ADDR7 0x80 +#define CCDC_PRGEVEN_0 0x84 +#define CCDC_PRGEVEN_1 0x88 +#define CCDC_PRGODD_0 0x8c +#define CCDC_PRGODD_1 0x90 +#define CCDC_VP_OUT 0x94 + + +/*************************************************************** +* Define for various register bit mask and shifts for CCDC +****************************************************************/ +#define CCDC_FID_POL_MASK 1 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 1 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 1 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_HSIZE_OFF_MASK 0xffffffe0 +#define CCDC_32BYTE_ALIGN_VAL 31 +#define CCDC_FRM_FMT_MASK 0x1 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATA_SZ_MASK 7 +#define CCDC_DATA_SZ_SHIFT 8 +#define CCDC_PIX_FMT_MASK 3 +#define CCDC_PIX_FMT_SHIFT 12 +#define CCDC_VP2SDR_DISABLE 0xFFFBFFFF +#define CCDC_WEN_ENABLE (1 << 17) +#define CCDC_SDR2RSZ_DISABLE 0xFFF7FFFF +#define CCDC_VDHDEN_ENABLE (1 << 16) +#define CCDC_LPF_ENABLE (1 << 14) +#define CCDC_ALAW_ENABLE (1 << 3) +#define CCDC_ALAW_GAMA_WD_MASK 7 +#define CCDC_BLK_CLAMP_ENABLE (1 << 31) +#define CCDC_BLK_SGAIN_MASK 0x1F +#define CCDC_BLK_ST_PXL_MASK 0x7FFF +#define CCDC_BLK_ST_PXL_SHIFT 10 +#define CCDC_BLK_SAMPLE_LN_MASK 7 +#define CCDC_BLK_SAMPLE_LN_SHIFT 28 +#define CCDC_BLK_SAMPLE_LINE_MASK 7 +#define CCDC_BLK_SAMPLE_LINE_SHIFT 25 +#define CCDC_BLK_DC_SUB_MASK 0x03FFF +#define CCDC_BLK_COMP_MASK 0xFF +#define CCDC_BLK_COMP_GB_COMP_SHIFT 8 +#define CCDC_BLK_COMP_GR_COMP_SHIFT 16 +#define CCDC_BLK_COMP_R_COMP_SHIFT 24 +#define CCDC_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define CCDC_FPC_ENABLE (1 << 15) +#define CCDC_FPC_DISABLE 0 +#define CCDC_FPC_FPC_NUM_MASK 0x7FFF +#define CCDC_DATA_PACK_ENABLE (1 << 11) +#define CCDC_FMTCFG_VPIN_MASK 7 +#define CCDC_FMTCFG_VPIN_SHIFT 12 +#define CCDC_FMT_HORZ_FMTLNH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_MASK 0x1FFF +#define CCDC_FMT_HORZ_FMTSPH_SHIFT 16 +#define CCDC_FMT_VERT_FMTLNV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_MASK 0x1FFF +#define CCDC_FMT_VERT_FMTSLV_SHIFT 16 +#define CCDC_VP_OUT_VERT_NUM_MASK 0x3FFF +#define CCDC_VP_OUT_VERT_NUM_SHIFT 17 +#define CCDC_VP_OUT_HORZ_NUM_MASK 0x1FFF +#define CCDC_VP_OUT_HORZ_NUM_SHIFT 4 +#define CCDC_VP_OUT_HORZ_ST_MASK 0xF +#define CCDC_HORZ_INFO_SPH_SHIFT 16 +#define CCDC_VERT_START_SLV0_SHIFT 16 +#define CCDC_VDINT_VDINT0_SHIFT 16 +#define CCDC_VDINT_VDINT1_MASK 0xFFFF +#define CCDC_PPC_RAW 1 +#define CCDC_DCSUB_DEFAULT_VAL 0 +#define CCDC_CLAMP_DEFAULT_VAL 0 +#define CCDC_ENABLE_VIDEO_PORT 0x8000 +#define CCDC_DISABLE_VIDEO_PORT 0 +#define CCDC_COLPTN_VAL 0xBB11BB11 +#define CCDC_TWO_BYTES_PER_PIXEL 2 +#define CCDC_INTERLACED_IMAGE_INVERT 0x4B6D +#define CCDC_INTERLACED_NO_IMAGE_INVERT 0x0249 +#define CCDC_PROGRESSIVE_IMAGE_INVERT 0x4000 +#define CCDC_PROGRESSIVE_NO_IMAGE_INVERT 0 +#define CCDC_INTERLACED_HEIGHT_SHIFT 1 +#define CCDC_SYN_MODE_INPMOD_SHIFT 12 +#define CCDC_SYN_MODE_INPMOD_MASK 3 +#define CCDC_SYN_MODE_8BITS (7 << 8) +#define CCDC_SYN_FLDMODE_MASK 1 +#define CCDC_SYN_FLDMODE_SHIFT 7 +#define CCDC_REC656IF_BT656_EN 3 +#define CCDC_SYN_MODE_VD_POL_NEGATIVE (1 << 2) +#define CCDC_CCDCFG_Y8POS_SHIFT 11 +#define CCDC_SDOFST_FIELD_INTERLEAVED 0x249 +#define CCDC_NO_CULLING 0xffff00ff +#endif diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c new file mode 100644 index 000000000000..402ce43ef38e --- /dev/null +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -0,0 +1,2124 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355 only). + * + * Features supported + * - MMAP IO + * - Capture using TVP5146 over BT.656 + * - support for interfacing decoders using sub device model + * - Work with DM355 or DM6446 CCDC to do Raw Bayer RGB/YUV + * data capture to SDRAM. + * TODO list + * - Support multiple REQBUF after open + * - Support for de-allocating buffers through REQBUF + * - Support for Raw Bayer RGB capture + * - Support for chaining Image Processor + * - Support for static allocation of buffers + * - Support for USERPTR IO + * - Support for STREAMON before QBUF + * - Support for control ioctls + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/version.h> +#include <media/v4l2-common.h> +#include <linux/io.h> +#include <media/davinci/vpfe_capture.h> +#include "ccdc_hw_device.h" + +static int debug; +static u32 numbuffers = 3; +static u32 bufsize = (720 * 576 * 2); + +module_param(numbuffers, uint, S_IRUGO); +module_param(bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +MODULE_PARM_DESC(numbuffers, "buffer count (default:3)"); +MODULE_PARM_DESC(bufsize, "buffer size in bytes (default:720 x 576 x 2)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* standard information */ +struct vpfe_standard { + v4l2_std_id std_id; + unsigned int width; + unsigned int height; + struct v4l2_fract pixelaspect; + /* 0 - progressive, 1 - interlaced */ + int frame_format; +}; + +/* ccdc configuration */ +struct ccdc_config { + /* This make sure vpfe is probed and ready to go */ + int vpfe_probed; + /* name of ccdc device */ + char name[32]; + /* for storing mem maps for CCDC */ + int ccdc_addr_size; + void *__iomem ccdc_addr; +}; + +/* data structures */ +static struct vpfe_config_params config_params = { + .min_numbuffers = 3, + .numbuffers = 3, + .min_bufsize = 720 * 480 * 2, + .device_bufsize = 720 * 576 * 2, +}; + +/* ccdc device registered */ +static struct ccdc_hw_device *ccdc_dev; +/* lock for accessing ccdc information */ +static DEFINE_MUTEX(ccdc_lock); +/* ccdc configuration */ +static struct ccdc_config *ccdc_cfg; + +const struct vpfe_standard vpfe_standards[] = { + {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, + {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, +}; + +/* Used when raw Bayer image from ccdc is directly captured to SDRAM */ +static const struct vpfe_pixel_format vpfe_pix_fmts[] = { + { + .fmtdesc = { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb 8bit A-Law compr.", + .pixelformat = V4L2_PIX_FMT_SBGGR8, + }, + .bpp = 1, + }, + { + .fmtdesc = { + .index = 1, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb - 16bit", + .pixelformat = V4L2_PIX_FMT_SBGGR16, + }, + .bpp = 2, + }, + { + .fmtdesc = { + .index = 2, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Bayer GrRBGb 8bit DPCM compr.", + .pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8, + }, + .bpp = 1, + }, + { + .fmtdesc = { + .index = 3, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "YCbCr 4:2:2 Interleaved UYVY", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + .bpp = 2, + }, + { + .fmtdesc = { + .index = 4, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "YCbCr 4:2:2 Interleaved YUYV", + .pixelformat = V4L2_PIX_FMT_YUYV, + }, + .bpp = 2, + }, + { + .fmtdesc = { + .index = 5, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .description = "Y/CbCr 4:2:0 - Semi planar", + .pixelformat = V4L2_PIX_FMT_NV12, + }, + .bpp = 1, + }, +}; + +/* + * vpfe_lookup_pix_format() + * lookup an entry in the vpfe pix format table based on pix_format + */ +static const struct vpfe_pixel_format *vpfe_lookup_pix_format(u32 pix_format) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(vpfe_pix_fmts); i++) { + if (pix_format == vpfe_pix_fmts[i].fmtdesc.pixelformat) + return &vpfe_pix_fmts[i]; + } + return NULL; +} + +/* + * vpfe_register_ccdc_device. CCDC module calls this to + * register with vpfe capture + */ +int vpfe_register_ccdc_device(struct ccdc_hw_device *dev) +{ + int ret = 0; + printk(KERN_NOTICE "vpfe_register_ccdc_device: %s\n", dev->name); + + BUG_ON(!dev->hw_ops.open); + BUG_ON(!dev->hw_ops.enable); + BUG_ON(!dev->hw_ops.set_hw_if_params); + BUG_ON(!dev->hw_ops.configure); + BUG_ON(!dev->hw_ops.set_buftype); + BUG_ON(!dev->hw_ops.get_buftype); + BUG_ON(!dev->hw_ops.enum_pix); + BUG_ON(!dev->hw_ops.set_frame_format); + BUG_ON(!dev->hw_ops.get_frame_format); + BUG_ON(!dev->hw_ops.get_pixel_format); + BUG_ON(!dev->hw_ops.set_pixel_format); + BUG_ON(!dev->hw_ops.set_params); + BUG_ON(!dev->hw_ops.set_image_window); + BUG_ON(!dev->hw_ops.get_image_window); + BUG_ON(!dev->hw_ops.get_line_length); + BUG_ON(!dev->hw_ops.setfbaddr); + BUG_ON(!dev->hw_ops.getfid); + + mutex_lock(&ccdc_lock); + if (NULL == ccdc_cfg) { + /* + * TODO. Will this ever happen? if so, we need to fix it. + * Proabably we need to add the request to a linked list and + * walk through it during vpfe probe + */ + printk(KERN_ERR "vpfe capture not initialized\n"); + ret = -1; + goto unlock; + } + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + ret = -1; + goto unlock; + } + + if (ccdc_dev) { + printk(KERN_ERR "ccdc already registered\n"); + ret = -1; + goto unlock; + } + + ccdc_dev = dev; + dev->hw_ops.set_ccdc_base(ccdc_cfg->ccdc_addr, + ccdc_cfg->ccdc_addr_size); +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} +EXPORT_SYMBOL(vpfe_register_ccdc_device); + +/* + * vpfe_unregister_ccdc_device. CCDC module calls this to + * unregister with vpfe capture + */ +void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev) +{ + if (NULL == dev) { + printk(KERN_ERR "invalid ccdc device ptr\n"); + return; + } + + printk(KERN_NOTICE "vpfe_unregister_ccdc_device, dev->name = %s\n", + dev->name); + + if (strcmp(dev->name, ccdc_cfg->name)) { + /* ignore this ccdc */ + return; + } + + mutex_lock(&ccdc_lock); + ccdc_dev = NULL; + mutex_unlock(&ccdc_lock); + return; +} +EXPORT_SYMBOL(vpfe_unregister_ccdc_device); + +/* + * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings + */ +static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe_dev, + struct v4l2_format *f) +{ + struct v4l2_rect image_win; + enum ccdc_buftype buf_type; + enum ccdc_frmfmt frm_fmt; + + memset(f, 0, sizeof(*f)); + f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ccdc_dev->hw_ops.get_image_window(&image_win); + f->fmt.pix.width = image_win.width; + f->fmt.pix.height = image_win.height; + f->fmt.pix.bytesperline = ccdc_dev->hw_ops.get_line_length(); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height; + buf_type = ccdc_dev->hw_ops.get_buftype(); + f->fmt.pix.pixelformat = ccdc_dev->hw_ops.get_pixel_format(); + frm_fmt = ccdc_dev->hw_ops.get_frame_format(); + if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) + f->fmt.pix.field = V4L2_FIELD_NONE; + else if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) + f->fmt.pix.field = V4L2_FIELD_SEQ_TB; + else { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf_type\n"); + return -EINVAL; + } + } else { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid frm_fmt\n"); + return -EINVAL; + } + return 0; +} + +/* + * vpfe_config_ccdc_image_format() + * For a pix format, configure ccdc to setup the capture + */ +static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; + int ret = 0; + + if (ccdc_dev->hw_ops.set_pixel_format( + vpfe_dev->fmt.fmt.pix.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "couldn't set pix format in ccdc\n"); + return -EINVAL; + } + /* configure the image window */ + ccdc_dev->hw_ops.set_image_window(&vpfe_dev->crop); + + switch (vpfe_dev->fmt.fmt.pix.field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_INTERLEAVED); + break; + case V4L2_FIELD_NONE: + frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + case V4L2_FIELD_SEQ_TB: + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_SEPARATED); + break; + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt); + return ret; +} +/* + * vpfe_config_image_format() + * For a given standard, this functions sets up the default + * pix format & crop values in the vpfe device and ccdc. It first + * starts with defaults based values from the standard table. + * It then checks if sub device support g_fmt and then override the + * values based on that.Sets crop values to match with scan resolution + * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the + * values in ccdc + */ +static int vpfe_config_image_format(struct vpfe_device *vpfe_dev, + const v4l2_std_id *std_id) +{ + struct vpfe_subdev_info *sdinfo = vpfe_dev->current_subdev; + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { + if (vpfe_standards[i].std_id & *std_id) { + vpfe_dev->std_info.active_pixels = + vpfe_standards[i].width; + vpfe_dev->std_info.active_lines = + vpfe_standards[i].height; + vpfe_dev->std_info.frame_format = + vpfe_standards[i].frame_format; + vpfe_dev->std_index = i; + break; + } + } + + if (i == ARRAY_SIZE(vpfe_standards)) { + v4l2_err(&vpfe_dev->v4l2_dev, "standard not supported\n"); + return -EINVAL; + } + + vpfe_dev->crop.top = 0; + vpfe_dev->crop.left = 0; + vpfe_dev->crop.width = vpfe_dev->std_info.active_pixels; + vpfe_dev->crop.height = vpfe_dev->std_info.active_lines; + vpfe_dev->fmt.fmt.pix.width = vpfe_dev->crop.width; + vpfe_dev->fmt.fmt.pix.height = vpfe_dev->crop.height; + + /* first field and frame format based on standard frame format */ + if (vpfe_dev->std_info.frame_format) { + vpfe_dev->fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + /* assume V4L2_PIX_FMT_UYVY as default */ + vpfe_dev->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + } else { + vpfe_dev->fmt.fmt.pix.field = V4L2_FIELD_NONE; + /* assume V4L2_PIX_FMT_SBGGR8 */ + vpfe_dev->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8; + } + + /* if sub device supports g_fmt, override the defaults */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, g_fmt, &vpfe_dev->fmt); + + if (ret && ret != -ENOIOCTLCMD) { + v4l2_err(&vpfe_dev->v4l2_dev, + "error in getting g_fmt from sub device\n"); + return ret; + } + + /* Sets the values in CCDC */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + if (ret) + return ret; + + /* Update the values of sizeimage and bytesperline */ + if (!ret) { + vpfe_dev->fmt.fmt.pix.bytesperline = + ccdc_dev->hw_ops.get_line_length(); + vpfe_dev->fmt.fmt.pix.sizeimage = + vpfe_dev->fmt.fmt.pix.bytesperline * + vpfe_dev->fmt.fmt.pix.height; + } + return ret; +} + +static int vpfe_initialize_device(struct vpfe_device *vpfe_dev) +{ + int ret = 0; + + /* set first input of current subdevice as the current input */ + vpfe_dev->current_input = 0; + + /* set default standard */ + vpfe_dev->std_index = 0; + + /* Configure the default format information */ + ret = vpfe_config_image_format(vpfe_dev, + &vpfe_standards[vpfe_dev->std_index].std_id); + if (ret) + return ret; + + /* now open the ccdc device to initialize it */ + mutex_lock(&ccdc_lock); + if (NULL == ccdc_dev) { + v4l2_err(&vpfe_dev->v4l2_dev, "ccdc device not registered\n"); + ret = -ENODEV; + goto unlock; + } + + if (!try_module_get(ccdc_dev->owner)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Couldn't lock ccdc module\n"); + ret = -ENODEV; + goto unlock; + } + ret = ccdc_dev->hw_ops.open(vpfe_dev->pdev); + if (!ret) + vpfe_dev->initialized = 1; +unlock: + mutex_unlock(&ccdc_lock); + return ret; +} + +/* + * vpfe_open : It creates object of file handle structure and + * stores it in private_data member of filepointer + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_open\n"); + + if (!vpfe_dev->cfg->num_subdevs) { + v4l2_err(&vpfe_dev->v4l2_dev, "No decoder registered\n"); + return -ENODEV; + } + + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpfe_fh), GFP_KERNEL); + if (NULL == fh) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + /* store pointer to fh in private_data member of file */ + file->private_data = fh; + fh->vpfe_dev = vpfe_dev; + mutex_lock(&vpfe_dev->lock); + /* If decoder is not initialized. initialize it */ + if (!vpfe_dev->initialized) { + if (vpfe_initialize_device(vpfe_dev)) { + mutex_unlock(&vpfe_dev->lock); + return -ENODEV; + } + } + /* Increment device usrs counter */ + vpfe_dev->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&vpfe_dev->prio, &fh->prio); + mutex_unlock(&vpfe_dev->lock); + return 0; +} + +static void vpfe_schedule_next_buffer(struct vpfe_device *vpfe_dev) +{ + unsigned long addr; + + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + list_del(&vpfe_dev->next_frm->queue); + vpfe_dev->next_frm->state = VIDEOBUF_ACTIVE; + addr = videobuf_to_dma_contig(vpfe_dev->next_frm); + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static void vpfe_process_buffer_complete(struct vpfe_device *vpfe_dev) +{ + struct timeval timevalue; + + do_gettimeofday(&timevalue); + vpfe_dev->cur_frm->ts = timevalue; + vpfe_dev->cur_frm->state = VIDEOBUF_DONE; + vpfe_dev->cur_frm->size = vpfe_dev->fmt.fmt.pix.sizeimage; + wake_up_interruptible(&vpfe_dev->cur_frm->done); + vpfe_dev->cur_frm = vpfe_dev->next_frm; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + enum v4l2_field field; + unsigned long addr; + int fid; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nStarting vpfe_isr...\n"); + field = vpfe_dev->fmt.fmt.pix.field; + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) + return IRQ_HANDLED; + + /* only for 6446 this will be applicable */ + if (NULL != ccdc_dev->hw_ops.reset) + ccdc_dev->hw_ops.reset(); + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "frame format is progressive...\n"); + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + return IRQ_HANDLED; + } + + /* interlaced or TB capture check which field we are in hardware */ + fid = ccdc_dev->hw_ops.getfid(); + + /* switch the software maintained field id */ + vpfe_dev->field_id ^= 1; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "field id = %x:%x.\n", + fid, vpfe_dev->field_id); + if (fid == vpfe_dev->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the next frame + * is available, release the current frame and move on + */ + if (vpfe_dev->cur_frm != vpfe_dev->next_frm) + vpfe_process_buffer_complete(vpfe_dev); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, reconfigure + * the CCDC memory address + */ + if (field == V4L2_FIELD_SEQ_TB) { + addr = + videobuf_to_dma_contig(vpfe_dev->cur_frm); + addr += vpfe_dev->field_off; + ccdc_dev->hw_ops.setfbaddr(addr); + } + return IRQ_HANDLED; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the empty + * queue if no frame is available hold on to the + * current buffer + */ + spin_lock(&vpfe_dev->dma_queue_lock); + if (!list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + vpfe_dev->field_id = fid; + } + return IRQ_HANDLED; +} + +/* vdint1_isr - isr handler for VINT1 interrupt */ +static irqreturn_t vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "\nInside vdint1_isr...\n"); + + /* if streaming not started, don't do anything */ + if (!vpfe_dev->started) + return IRQ_HANDLED; + + spin_lock(&vpfe_dev->dma_queue_lock); + if ((vpfe_dev->fmt.fmt.pix.field == V4L2_FIELD_NONE) && + !list_empty(&vpfe_dev->dma_queue) && + vpfe_dev->cur_frm == vpfe_dev->next_frm) + vpfe_schedule_next_buffer(vpfe_dev); + spin_unlock(&vpfe_dev->dma_queue_lock); + return IRQ_HANDLED; +} + +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) + free_irq(IRQ_VDINT1, vpfe_dev); +} + +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + enum ccdc_frmfmt frame_format; + + frame_format = ccdc_dev->hw_ops.get_frame_format(); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) { + return request_irq(vpfe_dev->ccdc_irq1, vdint1_isr, + IRQF_DISABLED, "vpfe_capture1", + vpfe_dev); + } + return 0; +} + +/* vpfe_stop_ccdc_capture: stop streaming in ccdc/isif */ +static void vpfe_stop_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + vpfe_dev->started = 0; + ccdc_dev->hw_ops.enable(0); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(0); +} + +/* + * vpfe_release : This function deletes buffer queue, frees the + * buffers and the vpfe file handle + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&vpfe_dev->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (vpfe_dev->started) { + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, + video, s_stream, 0); + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, + "stream off failed in subdev\n"); + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + videobuf_streamoff(&vpfe_dev->buffer_queue); + } + vpfe_dev->io_usrs = 0; + vpfe_dev->numbuffers = config_params.numbuffers; + } + + /* Decrement device usrs counter */ + vpfe_dev->usrs--; + /* Close the priority */ + v4l2_prio_close(&vpfe_dev->prio, &fh->prio); + /* If this is the last file handle */ + if (!vpfe_dev->usrs) { + vpfe_dev->initialized = 0; + if (ccdc_dev->hw_ops.close) + ccdc_dev->hw_ops.close(vpfe_dev->pdev); + module_put(ccdc_dev->owner); + } + mutex_unlock(&vpfe_dev->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + kfree(fh); + return 0; +} + +/* + * vpfe_mmap : It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + /* Get the device object and file handle object */ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + + return videobuf_mmap_mapper(&vpfe_dev->buffer_queue, vma); +} + +/* + * vpfe_poll: It is used for select/poll system call + */ +static unsigned int vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + + if (vpfe_dev->started) + return videobuf_poll_stream(file, + &vpfe_dev->buffer_queue, wait); + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_check_format() + * This function adjust the input pixel format as per hardware + * capabilities and update the same in pixfmt. + * Following algorithm used :- + * + * If given pixformat is not in the vpfe list of pix formats or not + * supported by the hardware, current value of pixformat in the device + * is used + * If given field is not supported, then current field is used. If field + * is different from current, then it is matched with that from sub device. + * Minimum height is 2 lines for interlaced or tb field and 1 line for + * progressive. Maximum height is clamped to active active lines of scan + * Minimum width is 32 bytes in memory and width is clamped to active + * pixels of scan. + * bytesperline is a multiple of 32. + */ +static const struct vpfe_pixel_format * + vpfe_check_format(struct vpfe_device *vpfe_dev, + struct v4l2_pix_format *pixfmt) +{ + u32 min_height = 1, min_width = 32, max_width, max_height; + const struct vpfe_pixel_format *vpfe_pix_fmt; + u32 pix; + int temp, found; + + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + if (NULL == vpfe_pix_fmt) { + /* + * use current pixel format in the vpfe device. We + * will find this pix format in the table + */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check if hw supports it */ + temp = 0; + found = 0; + while (ccdc_dev->hw_ops.enum_pix(&pix, temp) >= 0) { + if (vpfe_pix_fmt->fmtdesc.pixelformat == pix) { + found = 1; + break; + } + temp++; + } + + if (!found) { + /* use current pixel format */ + pixfmt->pixelformat = vpfe_dev->fmt.fmt.pix.pixelformat; + /* + * Since this is currently used in the vpfe device, we + * will find this pix format in the table + */ + vpfe_pix_fmt = vpfe_lookup_pix_format(pixfmt->pixelformat); + } + + /* check what field format is supported */ + if (pixfmt->field == V4L2_FIELD_ANY) { + /* if field is any, use current value as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + } + + /* + * if field is not same as current field in the vpfe device + * try matching the field with the sub device field + */ + if (vpfe_dev->fmt.fmt.pix.field != pixfmt->field) { + /* + * If field value is not in the supported fields, use current + * field used in the device as default + */ + switch (pixfmt->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_SEQ_TB: + /* if sub device is supporting progressive, use that */ + if (!vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_NONE; + break; + case V4L2_FIELD_NONE: + if (vpfe_dev->std_info.frame_format) + pixfmt->field = V4L2_FIELD_INTERLACED; + break; + + default: + /* use current field as default */ + pixfmt->field = vpfe_dev->fmt.fmt.pix.field; + break; + } + } + + /* Now adjust image resolutions supported */ + if (pixfmt->field == V4L2_FIELD_INTERLACED || + pixfmt->field == V4L2_FIELD_SEQ_TB) + min_height = 2; + + max_width = vpfe_dev->std_info.active_pixels; + max_height = vpfe_dev->std_info.active_lines; + min_width /= vpfe_pix_fmt->bpp; + + v4l2_info(&vpfe_dev->v4l2_dev, "width = %d, height = %d, bpp = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp); + + pixfmt->width = clamp((pixfmt->width), min_width, max_width); + pixfmt->height = clamp((pixfmt->height), min_height, max_height); + + /* If interlaced, adjust height to be a multiple of 2 */ + if (pixfmt->field == V4L2_FIELD_INTERLACED) + pixfmt->height &= (~1); + /* + * recalculate bytesperline and sizeimage since width + * and height might have changed + */ + pixfmt->bytesperline = (((pixfmt->width * vpfe_pix_fmt->bpp) + 31) + & ~31); + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + pixfmt->sizeimage = + pixfmt->bytesperline * pixfmt->height + + ((pixfmt->bytesperline * pixfmt->height) >> 1); + else + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + v4l2_info(&vpfe_dev->v4l2_dev, "adjusted width = %d, height =" + " %d, bpp = %d, bytesperline = %d, sizeimage = %d\n", + pixfmt->width, pixfmt->height, vpfe_pix_fmt->bpp, + pixfmt->bytesperline, pixfmt->sizeimage); + return vpfe_pix_fmt; +} + +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + cap->version = VPFE_CAPTURE_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + return 0; +} + +static int vpfe_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt_vid_cap\n"); + /* Fill in the information about format */ + *fmt = vpfe_dev->fmt; + return ret; +} + +static int vpfe_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmt; + int temp_index; + u32 pix; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt_vid_cap\n"); + + if (ccdc_dev->hw_ops.enum_pix(&pix, fmt->index) < 0) + return -EINVAL; + + /* Fill in the information about format */ + pix_fmt = vpfe_lookup_pix_format(pix); + if (NULL != pix_fmt) { + temp_index = fmt->index; + *fmt = pix_fmt->fmtdesc; + fmt->index = temp_index; + return 0; + } + return -EINVAL; +} + +static int vpfe_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt_vid_cap\n"); + + /* If streaming is started, return error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + + /* Check for valid frame format */ + pix_fmts = vpfe_check_format(vpfe_dev, &fmt->fmt.pix); + + if (NULL == pix_fmts) + return -EINVAL; + + /* store the pixel format in the device object */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + /* First detach any IRQ if currently attached */ + vpfe_detach_irq(vpfe_dev); + vpfe_dev->fmt = *fmt; + /* set image capture parameters in the ccdc */ + ret = vpfe_config_ccdc_image_format(vpfe_dev); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + const struct vpfe_pixel_format *pix_fmts; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt_vid_cap\n"); + + pix_fmts = vpfe_check_format(vpfe_dev, &f->fmt.pix); + if (NULL == pix_fmts) + return -EINVAL; + return 0; +} + +/* + * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a + * given app input index + */ +static int vpfe_get_subdev_input_index(struct vpfe_device *vpfe_dev, + int *subdev_index, + int *subdev_input_index, + int app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (app_input_index < (j + sdinfo->num_inputs)) { + *subdev_index = i; + *subdev_input_index = app_input_index - j; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +/* + * vpfe_get_app_input - Get app input index for a given subdev input index + * driver stores the input index of the current sub device and translate it + * when application request the current input + */ +static int vpfe_get_app_input_index(struct vpfe_device *vpfe_dev, + int *app_input_index) +{ + struct vpfe_config *cfg = vpfe_dev->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < cfg->num_subdevs; i++) { + sdinfo = &cfg->sub_devs[i]; + if (!strcmp(sdinfo->name, vpfe_dev->current_subdev->name)) { + if (vpfe_dev->current_input >= sdinfo->num_inputs) + return -1; + *app_input_index = j + vpfe_dev->current_input; + return 0; + } + j += sdinfo->num_inputs; + } + return -EINVAL; +} + +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev, index ; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + + if (vpfe_get_subdev_input_index(vpfe_dev, + &subdev, + &index, + inp->index) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "input information not found" + " for the subdev\n"); + return -EINVAL; + } + sdinfo = &vpfe_dev->cfg->sub_devs[subdev]; + memcpy(inp, &sdinfo->inputs[index], sizeof(struct v4l2_input)); + return 0; +} + +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + return vpfe_get_app_input_index(vpfe_dev, index); +} + + +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev_index, inp_index; + struct vpfe_route *route; + u32 input = 0, output = 0; + int ret = -EINVAL; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + /* + * If streaming is started return device busy + * error + */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + + if (vpfe_get_subdev_input_index(vpfe_dev, + &subdev_index, + &inp_index, + index) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid input index\n"); + goto unlock_out; + } + + sdinfo = &vpfe_dev->cfg->sub_devs[subdev_index]; + route = &sdinfo->routes[inp_index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + } + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_routing, input, output, 0); + + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "vpfe_doioctl:error in setting input in decoder\n"); + ret = -EINVAL; + goto unlock_out; + } + vpfe_dev->current_subdev = sdinfo; + vpfe_dev->current_input = index; + vpfe_dev->std_index = 0; + + /* set the bus/interface parameter for the sub device in ccdc */ + ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params); + if (ret) + goto unlock_out; + + /* set the default image parameters in the device */ + ret = vpfe_config_image_format(vpfe_dev, + &vpfe_standards[vpfe_dev->std_index].std_id); +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + sdinfo = vpfe_dev->current_subdev; + if (ret) + return ret; + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + sdinfo = vpfe_dev->current_subdev; + /* If streaming is started, return device busy error */ + if (vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_std, *std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + goto unlock_out; + } + ret = vpfe_config_image_format(vpfe_dev, std_id); + +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_std\n"); + + *std_id = vpfe_standards[vpfe_dev->std_index].std_id; + return 0; +} +/* + * Videobuf operations + */ +static int vpfe_videobuf_setup(struct videobuf_queue *vq, + unsigned int *count, + unsigned int *size) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); + *size = config_params.device_bufsize; + + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "count=%d, size=%d\n", *count, *size); + return 0; +} + +static int vpfe_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_prepare\n"); + + /* If buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = vpfe_dev->fmt.fmt.pix.width; + vb->height = vpfe_dev->fmt.fmt.pix.height; + vb->size = vpfe_dev->fmt.fmt.pix.sizeimage; + vb->field = field; + } + vb->state = VIDEOBUF_PREPARED; + return 0; +} + +static void vpfe_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_queue\n"); + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + list_add_tail(&vb->queue, &vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; +} + +static void vpfe_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_device *vpfe_dev = fh->vpfe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n"); + + /* + * We need to flush the buffer from the dma queue since + * they are de-allocated + */ + spin_lock_irqsave(&vpfe_dev->dma_queue_lock, flags); + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + spin_unlock_irqrestore(&vpfe_dev->dma_queue_lock, flags); + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops vpfe_videobuf_qops = { + .buf_setup = vpfe_videobuf_setup, + .buf_prepare = vpfe_videobuf_prepare, + .buf_queue = vpfe_videobuf_queue, + .buf_release = vpfe_videobuf_release, +}; + +/* + * vpfe_reqbufs. currently support REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + if (V4L2_MEMORY_USERPTR == req_buf->memory) { + /* we don't support user ptr IO */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs:" + " USERPTR IO not supported\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (vpfe_dev->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + + vpfe_dev->memory = req_buf->memory; + videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue, + &vpfe_videobuf_qops, + NULL, + &vpfe_dev->irqlock, + req_buf->type, + vpfe_dev->fmt.fmt.pix.field, + sizeof(struct videobuf_buffer), + fh); + + fh->io_allowed = 1; + vpfe_dev->io_usrs = 1; + INIT_LIST_HEAD(&vpfe_dev->dma_queue); + ret = videobuf_reqbufs(&vpfe_dev->buffer_queue, req_buf); +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (vpfe_dev->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + /* Call videobuf_querybuf to get information */ + return videobuf_querybuf(&vpfe_dev->buffer_queue, buf); +} + +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + return videobuf_qbuf(&vpfe_dev->buffer_queue, p); +} + +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + return videobuf_dqbuf(&vpfe_dev->buffer_queue, + buf, file->f_flags & O_NONBLOCK); +} + +/* + * vpfe_calculate_offsets : This function calculates buffers offset + * for top and bottom field + */ +static void vpfe_calculate_offsets(struct vpfe_device *vpfe_dev) +{ + struct v4l2_rect image_win; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_calculate_offsets\n"); + + ccdc_dev->hw_ops.get_image_window(&image_win); + vpfe_dev->field_off = image_win.height * image_win.width; +} + +/* vpfe_start_ccdc_capture: start streaming in ccdc/isif */ +static void vpfe_start_ccdc_capture(struct vpfe_device *vpfe_dev) +{ + ccdc_dev->hw_ops.enable(1); + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(1); + vpfe_dev->started = 1; +} + +/* + * vpfe_streamon. Assume the DMA queue is not empty. + * application is expected to call QBUF before calling + * this ioctl. If not, driver returns error + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + unsigned long addr; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 1); + + if (ret && (ret != -ENOIOCTLCMD)) { + v4l2_err(&vpfe_dev->v4l2_dev, "stream on failed in subdev\n"); + return -EINVAL; + } + + /* If buffer queue is empty, return error */ + if (list_empty(&vpfe_dev->buffer_queue.stream)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + + /* Call videobuf_streamon to start streaming * in videobuf */ + ret = videobuf_streamon(&vpfe_dev->buffer_queue); + if (ret) + return ret; + + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + goto streamoff; + /* Get the next frame from the buffer queue */ + vpfe_dev->next_frm = list_entry(vpfe_dev->dma_queue.next, + struct videobuf_buffer, queue); + vpfe_dev->cur_frm = vpfe_dev->next_frm; + /* Remove buffer from the buffer queue */ + list_del(&vpfe_dev->cur_frm->queue); + /* Mark state of the current frame to active */ + vpfe_dev->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + vpfe_dev->field_id = 0; + addr = videobuf_to_dma_contig(vpfe_dev->cur_frm); + + /* Calculate field offset */ + vpfe_calculate_offsets(vpfe_dev); + + if (vpfe_attach_irq(vpfe_dev) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in attaching interrupt handle\n"); + ret = -EFAULT; + goto unlock_out; + } + if (ccdc_dev->hw_ops.configure() < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in configuring ccdc\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.setfbaddr((unsigned long)(addr)); + vpfe_start_ccdc_capture(vpfe_dev); + mutex_unlock(&vpfe_dev->lock); + return ret; +unlock_out: + mutex_unlock(&vpfe_dev->lock); +streamoff: + ret = videobuf_streamoff(&vpfe_dev->buffer_queue); + return ret; +} + +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_fh *fh = file->private_data; + struct vpfe_subdev_info *sdinfo; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!vpfe_dev->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + vpfe_stop_ccdc_capture(vpfe_dev); + vpfe_detach_irq(vpfe_dev); + + sdinfo = vpfe_dev->current_subdev; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, s_stream, 0); + + if (ret && (ret != -ENOIOCTLCMD)) + v4l2_err(&vpfe_dev->v4l2_dev, "stream off failed in subdev\n"); + ret = videobuf_streamoff(&vpfe_dev->buffer_queue); + mutex_unlock(&vpfe_dev->lock); + return ret; +} + +static int vpfe_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n"); + + if (vpfe_dev->std_index > ARRAY_SIZE(vpfe_standards)) + return -EINVAL; + + memset(crop, 0, sizeof(struct v4l2_cropcap)); + crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop->bounds.width = crop->defrect.width = + vpfe_standards[vpfe_dev->std_index].width; + crop->bounds.height = crop->defrect.height = + vpfe_standards[vpfe_dev->std_index].height; + crop->pixelaspect = vpfe_standards[vpfe_dev->std_index].pixelaspect; + return 0; +} + +static int vpfe_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_crop\n"); + + crop->c = vpfe_dev->crop; + return 0; +} + +static int vpfe_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_crop\n"); + + if (vpfe_dev->started) { + /* make sure streaming is not started */ + v4l2_err(&vpfe_dev->v4l2_dev, + "Cannot change crop when streaming is ON\n"); + return -EBUSY; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + if (crop->c.top < 0 || crop->c.left < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "doesn't support negative values for top & left\n"); + ret = -EINVAL; + goto unlock_out; + } + + /* adjust the width to 16 pixel boundry */ + crop->c.width = ((crop->c.width + 15) & ~0xf); + + /* make sure parameters are valid */ + if ((crop->c.left + crop->c.width > + vpfe_dev->std_info.active_pixels) || + (crop->c.top + crop->c.height > + vpfe_dev->std_info.active_lines)) { + v4l2_err(&vpfe_dev->v4l2_dev, "Error in S_CROP params\n"); + ret = -EINVAL; + goto unlock_out; + } + ccdc_dev->hw_ops.set_image_window(&crop->c); + vpfe_dev->fmt.fmt.pix.width = crop->c.width; + vpfe_dev->fmt.fmt.pix.height = crop->c.height; + vpfe_dev->fmt.fmt.pix.bytesperline = + ccdc_dev->hw_ops.get_line_length(); + vpfe_dev->fmt.fmt.pix.sizeimage = + vpfe_dev->fmt.fmt.pix.bytesperline * + vpfe_dev->fmt.fmt.pix.height; + vpfe_dev->crop = crop->c; +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + + +static long vpfe_param_handler(struct file *file, void *priv, + int cmd, void *param) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_param_handler\n"); + + if (vpfe_dev->started) { + /* only allowed if streaming is not started */ + v4l2_err(&vpfe_dev->v4l2_dev, "device already started\n"); + return -EBUSY; + } + + ret = mutex_lock_interruptible(&vpfe_dev->lock); + if (ret) + return ret; + + switch (cmd) { + case VPFE_CMD_S_CCDC_RAW_PARAMS: + v4l2_warn(&vpfe_dev->v4l2_dev, + "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n"); + ret = ccdc_dev->hw_ops.set_params(param); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error in setting parameters in CCDC\n"); + goto unlock_out; + } + if (vpfe_get_ccdc_image_format(vpfe_dev, &vpfe_dev->fmt) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Invalid image format at CCDC\n"); + goto unlock_out; + } + break; + default: + ret = -EINVAL; + } +unlock_out: + mutex_unlock(&vpfe_dev->lock); + return ret; +} + + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt_vid_cap, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, + .vidioc_cropcap = vpfe_cropcap, + .vidioc_g_crop = vpfe_g_crop, + .vidioc_s_crop = vpfe_s_crop, + .vidioc_default = vpfe_param_handler, +}; + +static struct vpfe_device *vpfe_initialize(void) +{ + struct vpfe_device *vpfe_dev; + + /* Default number of buffers should be 3 */ + if ((numbuffers > 0) && + (numbuffers < config_params.min_numbuffers)) + numbuffers = config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid buffer size is + * given + */ + if (bufsize < config_params.min_bufsize) + bufsize = config_params.min_bufsize; + + config_params.numbuffers = numbuffers; + + if (numbuffers) + config_params.device_bufsize = bufsize; + + /* Allocate memory for device objects */ + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + + return vpfe_dev; +} + +static void vpfe_disable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + + clk_disable(vpfe_cfg->vpssclk); + clk_put(vpfe_cfg->vpssclk); + clk_disable(vpfe_cfg->slaveclk); + clk_put(vpfe_cfg->slaveclk); + v4l2_info(vpfe_dev->pdev->driver, + "vpfe vpss master & slave clocks disabled\n"); +} + +static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int ret = -ENOENT; + + vpfe_cfg->vpssclk = clk_get(vpfe_dev->pdev, "vpss_master"); + if (NULL == vpfe_cfg->vpssclk) { + v4l2_err(vpfe_dev->pdev->driver, "No clock defined for" + "vpss_master\n"); + return ret; + } + + if (clk_enable(vpfe_cfg->vpssclk)) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe vpss master clock not enabled\n"); + goto out; + } + v4l2_info(vpfe_dev->pdev->driver, + "vpfe vpss master clock enabled\n"); + + vpfe_cfg->slaveclk = clk_get(vpfe_dev->pdev, "vpss_slave"); + if (NULL == vpfe_cfg->slaveclk) { + v4l2_err(vpfe_dev->pdev->driver, + "No clock defined for vpss slave\n"); + goto out; + } + + if (clk_enable(vpfe_cfg->slaveclk)) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe vpss slave clock not enabled\n"); + goto out; + } + v4l2_info(vpfe_dev->pdev->driver, "vpfe vpss slave clock enabled\n"); + return 0; +out: + if (vpfe_cfg->vpssclk) + clk_put(vpfe_cfg->vpssclk); + if (vpfe_cfg->slaveclk) + clk_put(vpfe_cfg->slaveclk); + + return -1; +} + +/* + * vpfe_probe : This function creates device entries by register + * itself to the V4L2 driver and initializes fields of each + * device objects + */ +static __init int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct resource *res1; + struct vpfe_device *vpfe_dev; + struct i2c_adapter *i2c_adap; + struct video_device *vfd; + int ret = -ENOMEM, i, j; + int num_subdevs = 0; + + /* Get the pointer to the device object */ + vpfe_dev = vpfe_initialize(); + + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return ret; + } + + vpfe_dev->pdev = &pdev->dev; + + if (NULL == pdev->dev.platform_data) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + vpfe_cfg = pdev->dev.platform_data; + vpfe_dev->cfg = vpfe_cfg; + if (NULL == vpfe_cfg->ccdc || + NULL == vpfe_cfg->card_name || + NULL == vpfe_cfg->sub_devs) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* enable vpss clocks */ + ret = vpfe_enable_clock(vpfe_dev); + if (ret) + goto probe_free_dev_mem; + + mutex_lock(&ccdc_lock); + /* Allocate memory for ccdc configuration */ + ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL); + if (NULL == ccdc_cfg) { + v4l2_err(pdev->dev.driver, + "Memory allocation failed for ccdc_cfg\n"); + goto probe_disable_clock; + } + + strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32); + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, + IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + vpfe_dev->ccdc_irq1 = res1->start; + + /* Get address base of CCDC */ + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get register address map\n"); + ret = -ENOENT; + goto probe_disable_clock; + } + + ccdc_cfg->ccdc_addr_size = res1->end - res1->start + 1; + if (!request_mem_region(res1->start, ccdc_cfg->ccdc_addr_size, + pdev->dev.driver->name)) { + v4l2_err(pdev->dev.driver, + "Failed request_mem_region for ccdc base\n"); + ret = -ENXIO; + goto probe_disable_clock; + } + ccdc_cfg->ccdc_addr = ioremap_nocache(res1->start, + ccdc_cfg->ccdc_addr_size); + if (!ccdc_cfg->ccdc_addr) { + v4l2_err(pdev->dev.driver, "Unable to ioremap ccdc addr\n"); + ret = -ENXIO; + goto probe_out_release_mem1; + } + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, + "vpfe_capture0", vpfe_dev); + + if (0 != ret) { + v4l2_err(pdev->dev.driver, "Unable to request interrupt\n"); + goto probe_out_unmap1; + } + + /* Allocate memory for video device */ + vfd = video_device_alloc(); + if (NULL == vfd) { + ret = -ENOMEM; + v4l2_err(pdev->dev.driver, + "Unable to alloc video device\n"); + goto probe_out_release_irq; + } + + /* Initialize field of video device */ + vfd->release = video_device_release; + vfd->fops = &vpfe_fops; + vfd->ioctl_ops = &vpfe_ioctl_ops; + vfd->minor = -1; + vfd->tvnorms = 0; + vfd->current_norm = V4L2_STD_PAL; + vfd->v4l2_dev = &vpfe_dev->v4l2_dev; + snprintf(vfd->name, sizeof(vfd->name), + "%s_V%d.%d.%d", + CAPTURE_DRV_NAME, + (VPFE_CAPTURE_VERSION_CODE >> 16) & 0xff, + (VPFE_CAPTURE_VERSION_CODE >> 8) & 0xff, + (VPFE_CAPTURE_VERSION_CODE) & 0xff); + /* Set video_dev to the video device */ + vpfe_dev->video_dev = vfd; + + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register v4l2 device.\n"); + goto probe_out_video_release; + } + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + spin_lock_init(&vpfe_dev->irqlock); + spin_lock_init(&vpfe_dev->dma_queue_lock); + mutex_init(&vpfe_dev->lock); + + /* Initialize field of the device objects */ + vpfe_dev->numbuffers = config_params.numbuffers; + + /* Initialize prio member of device object */ + v4l2_prio_init(&vpfe_dev->prio); + /* register video device */ + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "trying to register vpfe device.\n"); + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "video_dev=%x\n", (int)&vpfe_dev->video_dev); + vpfe_dev->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = video_register_device(vpfe_dev->video_dev, + VFL_TYPE_GRABBER, -1); + + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register video device.\n"); + goto probe_out_v4l2_unregister; + } + + v4l2_info(&vpfe_dev->v4l2_dev, "video device registered\n"); + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + /* set driver private data */ + video_set_drvdata(vpfe_dev->video_dev, vpfe_dev); + i2c_adap = i2c_get_adapter(1); + vpfe_cfg = pdev->dev.platform_data; + num_subdevs = vpfe_cfg->num_subdevs; + vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs, + GFP_KERNEL); + if (NULL == vpfe_dev->sd) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for subdevice pointers\n"); + ret = -ENOMEM; + goto probe_out_video_unregister; + } + + for (i = 0; i < num_subdevs; i++) { + struct v4l2_input *inps; + + sdinfo = &vpfe_cfg->sub_devs[i]; + + /* Load up the subdevice */ + vpfe_dev->sd[i] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, + sdinfo->name, + &sdinfo->board_info, + NULL); + if (vpfe_dev->sd[i]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->name); + vpfe_dev->sd[i]->grp_id = sdinfo->grp_id; + /* update tvnorms from the sub devices */ + for (j = 0; j < sdinfo->num_inputs; j++) { + inps = &sdinfo->inputs[j]; + vfd->tvnorms |= inps->std; + } + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s register fails\n", + sdinfo->name); + goto probe_sd_out; + } + } + + /* set first sub device as current one */ + vpfe_dev->current_subdev = &vpfe_cfg->sub_devs[0]; + + /* We have at least one sub device to work with */ + mutex_unlock(&ccdc_lock); + return 0; + +probe_sd_out: + kfree(vpfe_dev->sd); +probe_out_video_unregister: + video_unregister_device(vpfe_dev->video_dev); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_video_release: + if (vpfe_dev->video_dev->minor == -1) + video_device_release(vpfe_dev->video_dev); +probe_out_release_irq: + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); +probe_out_unmap1: + iounmap(ccdc_cfg->ccdc_addr); +probe_out_release_mem1: + release_mem_region(res1->start, res1->end - res1->start + 1); +probe_disable_clock: + vpfe_disable_clock(vpfe_dev); + mutex_unlock(&ccdc_lock); + kfree(ccdc_cfg); +probe_free_dev_mem: + kfree(vpfe_dev); + return ret; +} + +/* + * vpfe_remove : It un-register device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + struct resource *res; + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + kfree(vpfe_dev->sd); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + video_unregister_device(vpfe_dev->video_dev); + mutex_lock(&ccdc_lock); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + iounmap(ccdc_cfg->ccdc_addr); + mutex_unlock(&ccdc_lock); + vpfe_disable_clock(vpfe_dev); + kfree(vpfe_dev); + kfree(ccdc_cfg); + return 0; +} + +static int +vpfe_suspend(struct device *dev) +{ + /* add suspend code here later */ + return -1; +} + +static int +vpfe_resume(struct device *dev) +{ + /* add resume code here later */ + return -1; +} + +static struct dev_pm_ops vpfe_dev_pm_ops = { + .suspend = vpfe_suspend, + .resume = vpfe_resume, +}; + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + .pm = &vpfe_dev_pm_ops, + }, + .probe = vpfe_probe, + .remove = __devexit_p(vpfe_remove), +}; + +static __init int vpfe_init(void) +{ + printk(KERN_NOTICE "vpfe_init\n"); + /* Register driver to the kernel */ + return platform_driver_register(&vpfe_driver); +} + +/* + * vpfe_cleanup : This function un-registers device driver + */ +static void vpfe_cleanup(void) +{ + platform_driver_unregister(&vpfe_driver); +} + +module_init(vpfe_init); +module_exit(vpfe_cleanup); diff --git a/drivers/media/video/davinci/vpif.c b/drivers/media/video/davinci/vpif.c new file mode 100644 index 000000000000..aa771268a5a5 --- /dev/null +++ b/drivers/media/video/davinci/vpif.c @@ -0,0 +1,234 @@ +/* + * vpif - DM646x Video Port Interface driver + * VPIF is a receiver and transmitter for video data. It has two channels(0, 1) + * that receiveing video byte stream and two channels(2, 3) for video output. + * The hardware supports SDTV, HDTV formats, raw data capture. + * Currently, the driver supports NTSC and PAL standards. + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> + +#include "vpif.h" + +MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver"); +MODULE_LICENSE("GPL"); + +#define VPIF_CH0_MAX_MODES (22) +#define VPIF_CH1_MAX_MODES (02) +#define VPIF_CH2_MAX_MODES (15) +#define VPIF_CH3_MAX_MODES (02) + +static inline void vpif_wr_bit(u32 reg, u32 bit, u32 val) +{ + if (val) + vpif_set_bit(reg, bit); + else + vpif_clr_bit(reg, bit); +} + +/* This structure is used to keep track of VPIF size register's offsets */ +struct vpif_registers { + u32 h_cfg, v_cfg_00, v_cfg_01, v_cfg_02, v_cfg, ch_ctrl; + u32 line_offset, vanc0_strt, vanc0_size, vanc1_strt; + u32 vanc1_size, width_mask, len_mask; + u8 max_modes; +}; + +static const struct vpif_registers vpifregs[VPIF_NUM_CHANNELS] = { + /* Channel0 */ + { + VPIF_CH0_H_CFG, VPIF_CH0_V_CFG_00, VPIF_CH0_V_CFG_01, + VPIF_CH0_V_CFG_02, VPIF_CH0_V_CFG_03, VPIF_CH0_CTRL, + VPIF_CH0_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF, + VPIF_CH0_MAX_MODES, + }, + /* Channel1 */ + { + VPIF_CH1_H_CFG, VPIF_CH1_V_CFG_00, VPIF_CH1_V_CFG_01, + VPIF_CH1_V_CFG_02, VPIF_CH1_V_CFG_03, VPIF_CH1_CTRL, + VPIF_CH1_IMG_ADD_OFST, 0, 0, 0, 0, 0x1FFF, 0xFFF, + VPIF_CH1_MAX_MODES, + }, + /* Channel2 */ + { + VPIF_CH2_H_CFG, VPIF_CH2_V_CFG_00, VPIF_CH2_V_CFG_01, + VPIF_CH2_V_CFG_02, VPIF_CH2_V_CFG_03, VPIF_CH2_CTRL, + VPIF_CH2_IMG_ADD_OFST, VPIF_CH2_VANC0_STRT, VPIF_CH2_VANC0_SIZE, + VPIF_CH2_VANC1_STRT, VPIF_CH2_VANC1_SIZE, 0x7FF, 0x7FF, + VPIF_CH2_MAX_MODES + }, + /* Channel3 */ + { + VPIF_CH3_H_CFG, VPIF_CH3_V_CFG_00, VPIF_CH3_V_CFG_01, + VPIF_CH3_V_CFG_02, VPIF_CH3_V_CFG_03, VPIF_CH3_CTRL, + VPIF_CH3_IMG_ADD_OFST, VPIF_CH3_VANC0_STRT, VPIF_CH3_VANC0_SIZE, + VPIF_CH3_VANC1_STRT, VPIF_CH3_VANC1_SIZE, 0x7FF, 0x7FF, + VPIF_CH3_MAX_MODES + }, +}; + +/* vpif_set_mode_info: + * This function is used to set horizontal and vertical config parameters + * As per the standard in the channel, configure the values of L1, L3, + * L5, L7 L9, L11 in VPIF Register , also write width and height + */ +static void vpif_set_mode_info(const struct vpif_channel_config_params *config, + u8 channel_id, u8 config_channel_id) +{ + u32 value; + + value = (config->eav2sav & vpifregs[config_channel_id].width_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->sav2eav & vpifregs[config_channel_id].width_mask); + regw(value, vpifregs[channel_id].h_cfg); + + value = (config->l1 & vpifregs[config_channel_id].len_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->l3 & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg_00); + + value = (config->l5 & vpifregs[config_channel_id].len_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->l7 & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg_01); + + value = (config->l9 & vpifregs[config_channel_id].len_mask); + value <<= VPIF_CH_LEN_SHIFT; + value |= (config->l11 & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg_02); + + value = (config->vsize & vpifregs[config_channel_id].len_mask); + regw(value, vpifregs[channel_id].v_cfg); +} + +/* config_vpif_params + * Function to set the parameters of a channel + * Mainly modifies the channel ciontrol register + * It sets frame format, yc mux mode + */ +static void config_vpif_params(struct vpif_params *vpifparams, + u8 channel_id, u8 found) +{ + const struct vpif_channel_config_params *config = &vpifparams->std_info; + u32 value, ch_nip, reg; + u8 start, end; + int i; + + start = channel_id; + end = channel_id + found; + + for (i = start; i < end; i++) { + reg = vpifregs[i].ch_ctrl; + if (channel_id < 2) + ch_nip = VPIF_CAPTURE_CH_NIP; + else + ch_nip = VPIF_DISPLAY_CH_NIP; + + vpif_wr_bit(reg, ch_nip, config->frm_fmt); + vpif_wr_bit(reg, VPIF_CH_YC_MUX_BIT, config->ycmux_mode); + vpif_wr_bit(reg, VPIF_CH_INPUT_FIELD_FRAME_BIT, + vpifparams->video_params.storage_mode); + + /* Set raster scanning SDR Format */ + vpif_clr_bit(reg, VPIF_CH_SDR_FMT_BIT); + vpif_wr_bit(reg, VPIF_CH_DATA_MODE_BIT, config->capture_format); + + if (channel_id > 1) /* Set the Pixel enable bit */ + vpif_set_bit(reg, VPIF_DISPLAY_PIX_EN_BIT); + else if (config->capture_format) { + /* Set the polarity of various pins */ + vpif_wr_bit(reg, VPIF_CH_FID_POLARITY_BIT, + vpifparams->params.raw_params.fid_pol); + vpif_wr_bit(reg, VPIF_CH_V_VALID_POLARITY_BIT, + vpifparams->params.raw_params.vd_pol); + vpif_wr_bit(reg, VPIF_CH_H_VALID_POLARITY_BIT, + vpifparams->params.raw_params.hd_pol); + + value = regr(reg); + /* Set data width */ + value &= ((~(unsigned int)(0x3)) << + VPIF_CH_DATA_WIDTH_BIT); + value |= ((vpifparams->params.raw_params.data_sz) << + VPIF_CH_DATA_WIDTH_BIT); + regw(value, reg); + } + + /* Write the pitch in the driver */ + regw((vpifparams->video_params.hpitch), + vpifregs[i].line_offset); + } +} + +/* vpif_set_video_params + * This function is used to set video parameters in VPIF register + */ +int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id) +{ + const struct vpif_channel_config_params *config = &vpifparams->std_info; + int found = 1; + + vpif_set_mode_info(config, channel_id, channel_id); + if (!config->ycmux_mode) { + /* YC are on separate channels (HDTV formats) */ + vpif_set_mode_info(config, channel_id + 1, channel_id); + found = 2; + } + + config_vpif_params(vpifparams, channel_id, found); + + regw(0x80, VPIF_REQ_SIZE); + regw(0x01, VPIF_EMULATION_CTRL); + + return found; +} +EXPORT_SYMBOL(vpif_set_video_params); + +void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams, + u8 channel_id) +{ + u32 value; + + value = 0x3F8 & (vbiparams->hstart0); + value |= 0x3FFFFFF & ((vbiparams->vstart0) << 16); + regw(value, vpifregs[channel_id].vanc0_strt); + + value = 0x3F8 & (vbiparams->hstart1); + value |= 0x3FFFFFF & ((vbiparams->vstart1) << 16); + regw(value, vpifregs[channel_id].vanc1_strt); + + value = 0x3F8 & (vbiparams->hsize0); + value |= 0x3FFFFFF & ((vbiparams->vsize0) << 16); + regw(value, vpifregs[channel_id].vanc0_size); + + value = 0x3F8 & (vbiparams->hsize1); + value |= 0x3FFFFFF & ((vbiparams->vsize1) << 16); + regw(value, vpifregs[channel_id].vanc1_size); + +} +EXPORT_SYMBOL(vpif_set_vbi_display_params); + +int vpif_channel_getfid(u8 channel_id) +{ + return (regr(vpifregs[channel_id].ch_ctrl) & VPIF_CH_FID_MASK) + >> VPIF_CH_FID_SHIFT; +} +EXPORT_SYMBOL(vpif_channel_getfid); + +void vpif_base_addr_init(void __iomem *base) +{ + vpif_base = base; +} +EXPORT_SYMBOL(vpif_base_addr_init); diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h new file mode 100644 index 000000000000..fca26dcb54de --- /dev/null +++ b/drivers/media/video/davinci/vpif.h @@ -0,0 +1,632 @@ +/* + * VPIF header file + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef VPIF_H +#define VPIF_H + +#include <linux/io.h> +#include <linux/videodev2.h> +#include <mach/hardware.h> + +/* Maximum channel allowed */ +#define VPIF_NUM_CHANNELS (4) +#define VPIF_CAPTURE_NUM_CHANNELS (2) +#define VPIF_DISPLAY_NUM_CHANNELS (2) + +/* Macros to read/write registers */ +static void __iomem *vpif_base; +#define regr(reg) readl((reg) + vpif_base) +#define regw(value, reg) writel(value, (reg + vpif_base)) + +/* Register Addresss Offsets */ +#define VPIF_PID (0x0000) +#define VPIF_CH0_CTRL (0x0004) +#define VPIF_CH1_CTRL (0x0008) +#define VPIF_CH2_CTRL (0x000C) +#define VPIF_CH3_CTRL (0x0010) + +#define VPIF_INTEN (0x0020) +#define VPIF_INTEN_SET (0x0024) +#define VPIF_INTEN_CLR (0x0028) +#define VPIF_STATUS (0x002C) +#define VPIF_STATUS_CLR (0x0030) +#define VPIF_EMULATION_CTRL (0x0034) +#define VPIF_REQ_SIZE (0x0038) + +#define VPIF_CH0_TOP_STRT_ADD_LUMA (0x0040) +#define VPIF_CH0_BTM_STRT_ADD_LUMA (0x0044) +#define VPIF_CH0_TOP_STRT_ADD_CHROMA (0x0048) +#define VPIF_CH0_BTM_STRT_ADD_CHROMA (0x004c) +#define VPIF_CH0_TOP_STRT_ADD_HANC (0x0050) +#define VPIF_CH0_BTM_STRT_ADD_HANC (0x0054) +#define VPIF_CH0_TOP_STRT_ADD_VANC (0x0058) +#define VPIF_CH0_BTM_STRT_ADD_VANC (0x005c) +#define VPIF_CH0_SP_CFG (0x0060) +#define VPIF_CH0_IMG_ADD_OFST (0x0064) +#define VPIF_CH0_HANC_ADD_OFST (0x0068) +#define VPIF_CH0_H_CFG (0x006c) +#define VPIF_CH0_V_CFG_00 (0x0070) +#define VPIF_CH0_V_CFG_01 (0x0074) +#define VPIF_CH0_V_CFG_02 (0x0078) +#define VPIF_CH0_V_CFG_03 (0x007c) + +#define VPIF_CH1_TOP_STRT_ADD_LUMA (0x0080) +#define VPIF_CH1_BTM_STRT_ADD_LUMA (0x0084) +#define VPIF_CH1_TOP_STRT_ADD_CHROMA (0x0088) +#define VPIF_CH1_BTM_STRT_ADD_CHROMA (0x008c) +#define VPIF_CH1_TOP_STRT_ADD_HANC (0x0090) +#define VPIF_CH1_BTM_STRT_ADD_HANC (0x0094) +#define VPIF_CH1_TOP_STRT_ADD_VANC (0x0098) +#define VPIF_CH1_BTM_STRT_ADD_VANC (0x009c) +#define VPIF_CH1_SP_CFG (0x00a0) +#define VPIF_CH1_IMG_ADD_OFST (0x00a4) +#define VPIF_CH1_HANC_ADD_OFST (0x00a8) +#define VPIF_CH1_H_CFG (0x00ac) +#define VPIF_CH1_V_CFG_00 (0x00b0) +#define VPIF_CH1_V_CFG_01 (0x00b4) +#define VPIF_CH1_V_CFG_02 (0x00b8) +#define VPIF_CH1_V_CFG_03 (0x00bc) + +#define VPIF_CH2_TOP_STRT_ADD_LUMA (0x00c0) +#define VPIF_CH2_BTM_STRT_ADD_LUMA (0x00c4) +#define VPIF_CH2_TOP_STRT_ADD_CHROMA (0x00c8) +#define VPIF_CH2_BTM_STRT_ADD_CHROMA (0x00cc) +#define VPIF_CH2_TOP_STRT_ADD_HANC (0x00d0) +#define VPIF_CH2_BTM_STRT_ADD_HANC (0x00d4) +#define VPIF_CH2_TOP_STRT_ADD_VANC (0x00d8) +#define VPIF_CH2_BTM_STRT_ADD_VANC (0x00dc) +#define VPIF_CH2_SP_CFG (0x00e0) +#define VPIF_CH2_IMG_ADD_OFST (0x00e4) +#define VPIF_CH2_HANC_ADD_OFST (0x00e8) +#define VPIF_CH2_H_CFG (0x00ec) +#define VPIF_CH2_V_CFG_00 (0x00f0) +#define VPIF_CH2_V_CFG_01 (0x00f4) +#define VPIF_CH2_V_CFG_02 (0x00f8) +#define VPIF_CH2_V_CFG_03 (0x00fc) +#define VPIF_CH2_HANC0_STRT (0x0100) +#define VPIF_CH2_HANC0_SIZE (0x0104) +#define VPIF_CH2_HANC1_STRT (0x0108) +#define VPIF_CH2_HANC1_SIZE (0x010c) +#define VPIF_CH2_VANC0_STRT (0x0110) +#define VPIF_CH2_VANC0_SIZE (0x0114) +#define VPIF_CH2_VANC1_STRT (0x0118) +#define VPIF_CH2_VANC1_SIZE (0x011c) + +#define VPIF_CH3_TOP_STRT_ADD_LUMA (0x0140) +#define VPIF_CH3_BTM_STRT_ADD_LUMA (0x0144) +#define VPIF_CH3_TOP_STRT_ADD_CHROMA (0x0148) +#define VPIF_CH3_BTM_STRT_ADD_CHROMA (0x014c) +#define VPIF_CH3_TOP_STRT_ADD_HANC (0x0150) +#define VPIF_CH3_BTM_STRT_ADD_HANC (0x0154) +#define VPIF_CH3_TOP_STRT_ADD_VANC (0x0158) +#define VPIF_CH3_BTM_STRT_ADD_VANC (0x015c) +#define VPIF_CH3_SP_CFG (0x0160) +#define VPIF_CH3_IMG_ADD_OFST (0x0164) +#define VPIF_CH3_HANC_ADD_OFST (0x0168) +#define VPIF_CH3_H_CFG (0x016c) +#define VPIF_CH3_V_CFG_00 (0x0170) +#define VPIF_CH3_V_CFG_01 (0x0174) +#define VPIF_CH3_V_CFG_02 (0x0178) +#define VPIF_CH3_V_CFG_03 (0x017c) +#define VPIF_CH3_HANC0_STRT (0x0180) +#define VPIF_CH3_HANC0_SIZE (0x0184) +#define VPIF_CH3_HANC1_STRT (0x0188) +#define VPIF_CH3_HANC1_SIZE (0x018c) +#define VPIF_CH3_VANC0_STRT (0x0190) +#define VPIF_CH3_VANC0_SIZE (0x0194) +#define VPIF_CH3_VANC1_STRT (0x0198) +#define VPIF_CH3_VANC1_SIZE (0x019c) + +#define VPIF_IODFT_CTRL (0x01c0) + +/* Functions for bit Manipulation */ +static inline void vpif_set_bit(u32 reg, u32 bit) +{ + regw((regr(reg)) | (0x01 << bit), reg); +} + +static inline void vpif_clr_bit(u32 reg, u32 bit) +{ + regw(((regr(reg)) & ~(0x01 << bit)), reg); +} + +/* Macro for Generating mask */ +#ifdef GENERATE_MASK +#undef GENERATE_MASK +#endif + +#define GENERATE_MASK(bits, pos) \ + ((((0xFFFFFFFF) << (32 - bits)) >> (32 - bits)) << pos) + +/* Bit positions in the channel control registers */ +#define VPIF_CH_DATA_MODE_BIT (2) +#define VPIF_CH_YC_MUX_BIT (3) +#define VPIF_CH_SDR_FMT_BIT (4) +#define VPIF_CH_HANC_EN_BIT (8) +#define VPIF_CH_VANC_EN_BIT (9) + +#define VPIF_CAPTURE_CH_NIP (10) +#define VPIF_DISPLAY_CH_NIP (11) + +#define VPIF_DISPLAY_PIX_EN_BIT (10) + +#define VPIF_CH_INPUT_FIELD_FRAME_BIT (12) + +#define VPIF_CH_FID_POLARITY_BIT (15) +#define VPIF_CH_V_VALID_POLARITY_BIT (14) +#define VPIF_CH_H_VALID_POLARITY_BIT (13) +#define VPIF_CH_DATA_WIDTH_BIT (28) + +#define VPIF_CH_CLK_EDGE_CTRL_BIT (31) + +/* Mask various length */ +#define VPIF_CH_EAVSAV_MASK GENERATE_MASK(13, 0) +#define VPIF_CH_LEN_MASK GENERATE_MASK(12, 0) +#define VPIF_CH_WIDTH_MASK GENERATE_MASK(13, 0) +#define VPIF_CH_LEN_SHIFT (16) + +/* VPIF masks for registers */ +#define VPIF_REQ_SIZE_MASK (0x1ff) + +/* bit posotion of interrupt vpif_ch_intr register */ +#define VPIF_INTEN_FRAME_CH0 (0x00000001) +#define VPIF_INTEN_FRAME_CH1 (0x00000002) +#define VPIF_INTEN_FRAME_CH2 (0x00000004) +#define VPIF_INTEN_FRAME_CH3 (0x00000008) + +/* bit position of clock and channel enable in vpif_chn_ctrl register */ + +#define VPIF_CH0_CLK_EN (0x00000002) +#define VPIF_CH0_EN (0x00000001) +#define VPIF_CH1_CLK_EN (0x00000002) +#define VPIF_CH1_EN (0x00000001) +#define VPIF_CH2_CLK_EN (0x00000002) +#define VPIF_CH2_EN (0x00000001) +#define VPIF_CH3_CLK_EN (0x00000002) +#define VPIF_CH3_EN (0x00000001) +#define VPIF_CH_CLK_EN (0x00000002) +#define VPIF_CH_EN (0x00000001) + +#define VPIF_INT_TOP (0x00) +#define VPIF_INT_BOTTOM (0x01) +#define VPIF_INT_BOTH (0x02) + +#define VPIF_CH0_INT_CTRL_SHIFT (6) +#define VPIF_CH1_INT_CTRL_SHIFT (6) +#define VPIF_CH2_INT_CTRL_SHIFT (6) +#define VPIF_CH3_INT_CTRL_SHIFT (6) +#define VPIF_CH_INT_CTRL_SHIFT (6) + +/* enabled interrupt on both the fields on vpid_ch0_ctrl register */ +#define channel0_intr_assert() (regw((regr(VPIF_CH0_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH0_INT_CTRL_SHIFT)), VPIF_CH0_CTRL)) + +/* enabled interrupt on both the fields on vpid_ch1_ctrl register */ +#define channel1_intr_assert() (regw((regr(VPIF_CH1_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH1_INT_CTRL_SHIFT)), VPIF_CH1_CTRL)) + +/* enabled interrupt on both the fields on vpid_ch0_ctrl register */ +#define channel2_intr_assert() (regw((regr(VPIF_CH2_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH2_INT_CTRL_SHIFT)), VPIF_CH2_CTRL)) + +/* enabled interrupt on both the fields on vpid_ch1_ctrl register */ +#define channel3_intr_assert() (regw((regr(VPIF_CH3_CTRL)|\ + (VPIF_INT_BOTH << VPIF_CH3_INT_CTRL_SHIFT)), VPIF_CH3_CTRL)) + +#define VPIF_CH_FID_MASK (0x20) +#define VPIF_CH_FID_SHIFT (5) + +#define VPIF_NTSC_VBI_START_FIELD0 (1) +#define VPIF_NTSC_VBI_START_FIELD1 (263) +#define VPIF_PAL_VBI_START_FIELD0 (624) +#define VPIF_PAL_VBI_START_FIELD1 (311) + +#define VPIF_NTSC_HBI_START_FIELD0 (1) +#define VPIF_NTSC_HBI_START_FIELD1 (263) +#define VPIF_PAL_HBI_START_FIELD0 (624) +#define VPIF_PAL_HBI_START_FIELD1 (311) + +#define VPIF_NTSC_VBI_COUNT_FIELD0 (20) +#define VPIF_NTSC_VBI_COUNT_FIELD1 (19) +#define VPIF_PAL_VBI_COUNT_FIELD0 (24) +#define VPIF_PAL_VBI_COUNT_FIELD1 (25) + +#define VPIF_NTSC_HBI_COUNT_FIELD0 (263) +#define VPIF_NTSC_HBI_COUNT_FIELD1 (262) +#define VPIF_PAL_HBI_COUNT_FIELD0 (312) +#define VPIF_PAL_HBI_COUNT_FIELD1 (313) + +#define VPIF_NTSC_VBI_SAMPLES_PER_LINE (720) +#define VPIF_PAL_VBI_SAMPLES_PER_LINE (720) +#define VPIF_NTSC_HBI_SAMPLES_PER_LINE (268) +#define VPIF_PAL_HBI_SAMPLES_PER_LINE (280) + +#define VPIF_CH_VANC_EN (0x20) +#define VPIF_DMA_REQ_SIZE (0x080) +#define VPIF_EMULATION_DISABLE (0x01) + +extern u8 irq_vpif_capture_channel[VPIF_NUM_CHANNELS]; + +/* inline function to enable/disable channel0 */ +static inline void enable_channel0(int enable) +{ + if (enable) + regw((regr(VPIF_CH0_CTRL) | (VPIF_CH0_EN)), VPIF_CH0_CTRL); + else + regw((regr(VPIF_CH0_CTRL) & (~VPIF_CH0_EN)), VPIF_CH0_CTRL); +} + +/* inline function to enable/disable channel1 */ +static inline void enable_channel1(int enable) +{ + if (enable) + regw((regr(VPIF_CH1_CTRL) | (VPIF_CH1_EN)), VPIF_CH1_CTRL); + else + regw((regr(VPIF_CH1_CTRL) & (~VPIF_CH1_EN)), VPIF_CH1_CTRL); +} + +/* inline function to enable interrupt for channel0 */ +static inline void channel0_intr_enable(int enable) +{ + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH0), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH0)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH0), + VPIF_INTEN_SET); + } +} + +/* inline function to enable interrupt for channel1 */ +static inline void channel1_intr_enable(int enable) +{ + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH1), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH1)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH1), + VPIF_INTEN_SET); + } +} + +/* inline function to set buffer addresses in case of Y/C non mux mode */ +static inline void ch0_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA); +} + +/* inline function to set buffer addresses in VPIF registers for video data */ +static inline void ch0_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH0_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH0_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH0_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH0_BTM_STRT_ADD_CHROMA); +} + +static inline void ch1_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + + regw(top_strt_luma, VPIF_CH1_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH1_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH1_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH1_BTM_STRT_ADD_CHROMA); +} + +static inline void ch0_set_vbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_VANC); + regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_VANC); +} + +static inline void ch0_set_hbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH0_TOP_STRT_ADD_HANC); + regw(btm_vbi, VPIF_CH0_BTM_STRT_ADD_HANC); +} + +static inline void ch1_set_vbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_VANC); + regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_VANC); +} + +static inline void ch1_set_hbi_addr(unsigned long top_vbi, + unsigned long btm_vbi, unsigned long a, unsigned long b) +{ + regw(top_vbi, VPIF_CH1_TOP_STRT_ADD_HANC); + regw(btm_vbi, VPIF_CH1_BTM_STRT_ADD_HANC); +} + +/* Inline function to enable raw vbi in the given channel */ +static inline void disable_raw_feature(u8 channel_id, u8 index) +{ + u32 ctrl_reg; + if (0 == channel_id) + ctrl_reg = VPIF_CH0_CTRL; + else + ctrl_reg = VPIF_CH1_CTRL; + + if (1 == index) + vpif_clr_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT); + else + vpif_clr_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT); +} + +static inline void enable_raw_feature(u8 channel_id, u8 index) +{ + u32 ctrl_reg; + if (0 == channel_id) + ctrl_reg = VPIF_CH0_CTRL; + else + ctrl_reg = VPIF_CH1_CTRL; + + if (1 == index) + vpif_set_bit(ctrl_reg, VPIF_CH_VANC_EN_BIT); + else + vpif_set_bit(ctrl_reg, VPIF_CH_HANC_EN_BIT); +} + +/* inline function to enable/disable channel2 */ +static inline void enable_channel2(int enable) +{ + if (enable) { + regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL); + regw((regr(VPIF_CH2_CTRL) | (VPIF_CH2_EN)), VPIF_CH2_CTRL); + } else { + regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_CLK_EN)), VPIF_CH2_CTRL); + regw((regr(VPIF_CH2_CTRL) & (~VPIF_CH2_EN)), VPIF_CH2_CTRL); + } +} + +/* inline function to enable/disable channel3 */ +static inline void enable_channel3(int enable) +{ + if (enable) { + regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL); + regw((regr(VPIF_CH3_CTRL) | (VPIF_CH3_EN)), VPIF_CH3_CTRL); + } else { + regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_CLK_EN)), VPIF_CH3_CTRL); + regw((regr(VPIF_CH3_CTRL) & (~VPIF_CH3_EN)), VPIF_CH3_CTRL); + } +} + +/* inline function to enable interrupt for channel2 */ +static inline void channel2_intr_enable(int enable) +{ + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH2), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH2)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH2), + VPIF_INTEN_SET); + } +} + +/* inline function to enable interrupt for channel3 */ +static inline void channel3_intr_enable(int enable) +{ + if (enable) { + regw((regr(VPIF_INTEN) | 0x10), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | 0x10), VPIF_INTEN_SET); + + regw((regr(VPIF_INTEN) | VPIF_INTEN_FRAME_CH3), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3), + VPIF_INTEN_SET); + } else { + regw((regr(VPIF_INTEN) & (~VPIF_INTEN_FRAME_CH3)), VPIF_INTEN); + regw((regr(VPIF_INTEN_SET) | VPIF_INTEN_FRAME_CH3), + VPIF_INTEN_SET); + } +} + +/* inline function to enable raw vbi data for channel2 */ +static inline void channel2_raw_enable(int enable, u8 index) +{ + u32 mask; + + if (1 == index) + mask = VPIF_CH_VANC_EN_BIT; + else + mask = VPIF_CH_HANC_EN_BIT; + + if (enable) + vpif_set_bit(VPIF_CH2_CTRL, mask); + else + vpif_clr_bit(VPIF_CH2_CTRL, mask); +} + +/* inline function to enable raw vbi data for channel3*/ +static inline void channel3_raw_enable(int enable, u8 index) +{ + u32 mask; + + if (1 == index) + mask = VPIF_CH_VANC_EN_BIT; + else + mask = VPIF_CH_HANC_EN_BIT; + + if (enable) + vpif_set_bit(VPIF_CH3_CTRL, mask); + else + vpif_clr_bit(VPIF_CH3_CTRL, mask); +} + +/* inline function to set buffer addresses in case of Y/C non mux mode */ +static inline void ch2_set_videobuf_addr_yc_nmux(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA); +} + +/* inline function to set buffer addresses in VPIF registers for video data */ +static inline void ch2_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH2_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH2_BTM_STRT_ADD_CHROMA); +} + +static inline void ch3_set_videobuf_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_LUMA); + regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_LUMA); + regw(top_strt_chroma, VPIF_CH3_TOP_STRT_ADD_CHROMA); + regw(btm_strt_chroma, VPIF_CH3_BTM_STRT_ADD_CHROMA); +} + +/* inline function to set buffer addresses in VPIF registers for vbi data */ +static inline void ch2_set_vbi_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH2_TOP_STRT_ADD_VANC); + regw(btm_strt_luma, VPIF_CH2_BTM_STRT_ADD_VANC); +} + +static inline void ch3_set_vbi_addr(unsigned long top_strt_luma, + unsigned long btm_strt_luma, + unsigned long top_strt_chroma, + unsigned long btm_strt_chroma) +{ + regw(top_strt_luma, VPIF_CH3_TOP_STRT_ADD_VANC); + regw(btm_strt_luma, VPIF_CH3_BTM_STRT_ADD_VANC); +} + +#define VPIF_MAX_NAME (30) + +/* This structure will store size parameters as per the mode selected by user */ +struct vpif_channel_config_params { + char name[VPIF_MAX_NAME]; /* Name of the mode */ + u16 width; /* Indicates width of the image */ + u16 height; /* Indicates height of the image */ + u8 fps; + u8 frm_fmt; /* Indicates whether this is interlaced + * or progressive format */ + u8 ycmux_mode; /* Indicates whether this mode requires + * single or two channels */ + u16 eav2sav; /* length of sav 2 eav */ + u16 sav2eav; /* length of sav 2 eav */ + u16 l1, l3, l5, l7, l9, l11; /* Other parameter configurations */ + u16 vsize; /* Vertical size of the image */ + u8 capture_format; /* Indicates whether capture format + * is in BT or in CCD/CMOS */ + u8 vbi_supported; /* Indicates whether this mode + * supports capturing vbi or not */ + u8 hd_sd; + v4l2_std_id stdid; +}; + +struct vpif_interface; +struct vpif_params; +struct vpif_vbi_params; + +int vpif_set_video_params(struct vpif_params *vpifparams, u8 channel_id); +void vpif_set_vbi_display_params(struct vpif_vbi_params *vbiparams, + u8 channel_id); +int vpif_channel_getfid(u8 channel_id); +void vpif_base_addr_init(void __iomem *base); + +/* Enumerated data types */ +enum vpif_capture_pinpol { + VPIF_CAPTURE_PINPOL_SAME = 0, + VPIF_CAPTURE_PINPOL_INVERT = 1 +}; + +enum data_size { + _8BITS = 0, + _10BITS, + _12BITS, +}; + +struct vpif_capture_params_raw { + enum data_size data_sz; + enum vpif_capture_pinpol fid_pol; + enum vpif_capture_pinpol vd_pol; + enum vpif_capture_pinpol hd_pol; +}; + +/* Structure for vpif parameters for raw vbi data */ +struct vpif_vbi_params { + __u32 hstart0; /* Horizontal start of raw vbi data for first field */ + __u32 vstart0; /* Vertical start of raw vbi data for first field */ + __u32 hsize0; /* Horizontal size of raw vbi data for first field */ + __u32 vsize0; /* Vertical size of raw vbi data for first field */ + __u32 hstart1; /* Horizontal start of raw vbi data for second field */ + __u32 vstart1; /* Vertical start of raw vbi data for second field */ + __u32 hsize1; /* Horizontal size of raw vbi data for second field */ + __u32 vsize1; /* Vertical size of raw vbi data for second field */ +}; + +/* structure for vpif parameters */ +struct vpif_interface { + __u8 storage_mode; /* Indicates field or frame mode */ + unsigned long hpitch; + v4l2_std_id stdid; +}; + +struct vpif_params { + struct vpif_interface video_params; + struct vpif_channel_config_params std_info; + union param { + struct vpif_vbi_params vbi_params; + struct vpif_capture_params_raw raw_params; + } params; +}; + +#endif /* End of #ifndef VPIF_H */ + diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c new file mode 100644 index 000000000000..8ea65d794dbf --- /dev/null +++ b/drivers/media/video/davinci/vpif_display.c @@ -0,0 +1,1679 @@ +/* + * vpif-display - VPIF display driver + * Display driver for TI DaVinci VPIF + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/string.h> +#include <linux/videodev2.h> +#include <linux/wait.h> +#include <linux/time.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/version.h> + +#include <asm/irq.h> +#include <asm/page.h> + +#include <media/adv7343.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> + +#include <mach/dm646x.h> + +#include "vpif_display.h" +#include "vpif.h" + +MODULE_DESCRIPTION("TI DaVinci VPIF Display driver"); +MODULE_LICENSE("GPL"); + +#define DM646X_V4L2_STD (V4L2_STD_525_60 | V4L2_STD_625_50) + +#define vpif_err(fmt, arg...) v4l2_err(&vpif_obj.v4l2_dev, fmt, ## arg) +#define vpif_dbg(level, debug, fmt, arg...) \ + v4l2_dbg(level, debug, &vpif_obj.v4l2_dev, fmt, ## arg) + +static int debug = 1; +static u32 ch2_numbuffers = 3; +static u32 ch3_numbuffers = 3; +static u32 ch2_bufsize = 1920 * 1080 * 2; +static u32 ch3_bufsize = 720 * 576 * 2; + +module_param(debug, int, 0644); +module_param(ch2_numbuffers, uint, S_IRUGO); +module_param(ch3_numbuffers, uint, S_IRUGO); +module_param(ch2_bufsize, uint, S_IRUGO); +module_param(ch3_bufsize, uint, S_IRUGO); + +MODULE_PARM_DESC(debug, "Debug level 0-1"); +MODULE_PARM_DESC(ch2_numbuffers, "Channel2 buffer count (default:3)"); +MODULE_PARM_DESC(ch3_numbuffers, "Channel3 buffer count (default:3)"); +MODULE_PARM_DESC(ch2_bufsize, "Channel2 buffer size (default:1920 x 1080 x 2)"); +MODULE_PARM_DESC(ch3_bufsize, "Channel3 buffer size (default:720 x 576 x 2)"); + +static struct vpif_config_params config_params = { + .min_numbuffers = 3, + .numbuffers[0] = 3, + .numbuffers[1] = 3, + .min_bufsize[0] = 720 * 480 * 2, + .min_bufsize[1] = 720 * 480 * 2, + .channel_bufsize[0] = 1920 * 1080 * 2, + .channel_bufsize[1] = 720 * 576 * 2, +}; + +static struct vpif_device vpif_obj = { {NULL} }; +static struct device *vpif_dev; + +static const struct vpif_channel_config_params ch_params[] = { + { + "NTSC", 720, 480, 30, 0, 1, 268, 1440, 1, 23, 263, 266, + 286, 525, 525, 0, 1, 0, V4L2_STD_525_60, + }, + { + "PAL", 720, 576, 25, 0, 1, 280, 1440, 1, 23, 311, 313, + 336, 624, 625, 0, 1, 0, V4L2_STD_625_50, + }, +}; + +/* + * vpif_uservirt_to_phys: This function is used to convert user + * space virtual address to physical address. + */ +static u32 vpif_uservirt_to_phys(u32 virtp) +{ + struct mm_struct *mm = current->mm; + unsigned long physp = 0; + struct vm_area_struct *vma; + + vma = find_vma(mm, virtp); + + /* For kernel direct-mapped memory, take the easy way */ + if (virtp >= PAGE_OFFSET) { + physp = virt_to_phys((void *)virtp); + } else if (vma && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) { + /* this will catch, kernel-allocated, mmaped-to-usermode addr */ + physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start); + } else { + /* otherwise, use get_user_pages() for general userland pages */ + int res, nr_pages = 1; + struct page *pages; + down_read(¤t->mm->mmap_sem); + + res = get_user_pages(current, current->mm, + virtp, nr_pages, 1, 0, &pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (res == nr_pages) { + physp = __pa(page_address(&pages[0]) + + (virtp & ~PAGE_MASK)); + } else { + vpif_err("get_user_pages failed\n"); + return 0; + } + } + + return physp; +} + +/* + * buffer_prepare: This is the callback function called from videobuf_qbuf() + * function the buffer is prepared and user space virtual address is converted + * into physical address + */ +static int vpif_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpif_fh *fh = q->priv_data; + struct common_obj *common; + unsigned long addr; + + common = &fh->channel->common[VPIF_VIDEO_INDEX]; + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = common->width; + vb->height = common->height; + vb->size = vb->width * vb->height; + vb->field = field; + } + vb->state = VIDEOBUF_PREPARED; + + /* if user pointer memory mechanism is used, get the physical + * address of the buffer */ + if (V4L2_MEMORY_USERPTR == common->memory) { + if (!vb->baddr) { + vpif_err("buffer_address is 0\n"); + return -EINVAL; + } + + vb->boff = vpif_uservirt_to_phys(vb->baddr); + if (!ISALIGNED(vb->boff)) + goto buf_align_exit; + } + + addr = vb->boff; + if (q->streaming && (V4L2_BUF_TYPE_SLICED_VBI_OUTPUT != q->type)) { + if (!ISALIGNED(addr + common->ytop_off) || + !ISALIGNED(addr + common->ybtm_off) || + !ISALIGNED(addr + common->ctop_off) || + !ISALIGNED(addr + common->cbtm_off)) + goto buf_align_exit; + } + return 0; + +buf_align_exit: + vpif_err("buffer offset not aligned to 8 bytes\n"); + return -EINVAL; +} + +/* + * vpif_buffer_setup: This function allocates memory for the buffers + */ +static int vpif_buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (V4L2_MEMORY_MMAP != common->memory) + return 0; + + *size = config_params.channel_bufsize[ch->channel_id]; + if (*count < config_params.min_numbuffers) + *count = config_params.min_numbuffers; + + return 0; +} + +/* + * vpif_buffer_queue: This function adds the buffer to DMA queue + */ +static void vpif_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct vpif_fh *fh = q->priv_data; + struct common_obj *common; + + common = &fh->channel->common[VPIF_VIDEO_INDEX]; + + /* add the buffer to the DMA queue */ + list_add_tail(&vb->queue, &common->dma_queue); + vb->state = VIDEOBUF_QUEUED; +} + +/* + * vpif_buffer_release: This function is called from the videobuf layer to + * free memory allocated to the buffers + */ +static void vpif_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + struct vpif_fh *fh = q->priv_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + unsigned int buf_size = 0; + + common = &ch->common[VPIF_VIDEO_INDEX]; + + videobuf_dma_contig_free(q, vb); + vb->state = VIDEOBUF_NEEDS_INIT; + + if (V4L2_MEMORY_MMAP != common->memory) + return; + + buf_size = config_params.channel_bufsize[ch->channel_id]; +} + +static struct videobuf_queue_ops video_qops = { + .buf_setup = vpif_buffer_setup, + .buf_prepare = vpif_buffer_prepare, + .buf_queue = vpif_buffer_queue, + .buf_release = vpif_buffer_release, +}; +static u8 channel_first_int[VPIF_NUMOBJECTS][2] = { {1, 1} }; + +static void process_progressive_mode(struct common_obj *common) +{ + unsigned long addr = 0; + + /* Get the next buffer from buffer queue */ + common->next_frm = list_entry(common->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&common->next_frm->queue); + /* Mark status of the buffer as active */ + common->next_frm->state = VIDEOBUF_ACTIVE; + + /* Set top and bottom field addrs in VPIF registers */ + addr = videobuf_to_dma_contig(common->next_frm); + common->set_addr(addr + common->ytop_off, + addr + common->ybtm_off, + addr + common->ctop_off, + addr + common->cbtm_off); +} + +static void process_interlaced_mode(int fid, struct common_obj *common) +{ + /* device field id and local field id are in sync */ + /* If this is even field */ + if (0 == fid) { + if (common->cur_frm == common->next_frm) + return; + + /* one frame is displayed If next frame is + * available, release cur_frm and move on */ + /* Copy frame display time */ + do_gettimeofday(&common->cur_frm->ts); + /* Change status of the cur_frm */ + common->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&common->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + common->cur_frm = common->next_frm; + + } else if (1 == fid) { /* odd field */ + if (list_empty(&common->dma_queue) + || (common->cur_frm != common->next_frm)) { + return; + } + /* one field is displayed configure the next + * frame if it is available else hold on current + * frame */ + /* Get next from the buffer queue */ + process_progressive_mode(common); + + } +} + +/* + * vpif_channel_isr: It changes status of the displayed buffer, takes next + * buffer from the queue and sets its address in VPIF registers + */ +static irqreturn_t vpif_channel_isr(int irq, void *dev_id) +{ + struct vpif_device *dev = &vpif_obj; + struct channel_obj *ch; + struct common_obj *common; + enum v4l2_field field; + int fid = -1, i; + int channel_id = 0; + + channel_id = *(int *)(dev_id); + ch = dev->dev[channel_id]; + field = ch->common[VPIF_VIDEO_INDEX].fmt.fmt.pix.field; + for (i = 0; i < VPIF_NUMOBJECTS; i++) { + common = &ch->common[i]; + /* If streaming is started in this channel */ + if (0 == common->started) + continue; + + if (1 == ch->vpifparams.std_info.frm_fmt) { + if (list_empty(&common->dma_queue)) + continue; + + /* Progressive mode */ + if (!channel_first_int[i][channel_id]) { + /* Mark status of the cur_frm to + * done and unlock semaphore on it */ + do_gettimeofday(&common->cur_frm->ts); + common->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&common->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + common->cur_frm = common->next_frm; + } + + channel_first_int[i][channel_id] = 0; + process_progressive_mode(common); + } else { + /* Interlaced mode */ + /* If it is first interrupt, ignore it */ + + if (channel_first_int[i][channel_id]) { + channel_first_int[i][channel_id] = 0; + continue; + } + + if (0 == i) { + ch->field_id ^= 1; + /* Get field id from VPIF registers */ + fid = vpif_channel_getfid(ch->channel_id + 2); + /* If fid does not match with stored field id */ + if (fid != ch->field_id) { + /* Make them in sync */ + if (0 == fid) + ch->field_id = fid; + + return IRQ_HANDLED; + } + } + process_interlaced_mode(fid, common); + } + } + + return IRQ_HANDLED; +} + +static int vpif_get_std_info(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct video_obj *vid_ch = &ch->video; + struct vpif_params *vpifparams = &ch->vpifparams; + struct vpif_channel_config_params *std_info = &vpifparams->std_info; + const struct vpif_channel_config_params *config; + + int index; + + std_info->stdid = vid_ch->stdid; + if (!std_info) + return -1; + + for (index = 0; index < ARRAY_SIZE(ch_params); index++) { + config = &ch_params[index]; + if (config->stdid & std_info->stdid) { + memcpy(std_info, config, sizeof(*config)); + break; + } + } + + if (index == ARRAY_SIZE(ch_params)) + return -1; + + common->fmt.fmt.pix.width = std_info->width; + common->fmt.fmt.pix.height = std_info->height; + vpif_dbg(1, debug, "Pixel details: Width = %d,Height = %d\n", + common->fmt.fmt.pix.width, common->fmt.fmt.pix.height); + + /* Set height and width paramateres */ + ch->common[VPIF_VIDEO_INDEX].height = std_info->height; + ch->common[VPIF_VIDEO_INDEX].width = std_info->width; + + return 0; +} + +/* + * vpif_calculate_offsets: This function calculates buffers offset for Y and C + * in the top and bottom field + */ +static void vpif_calculate_offsets(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct vpif_params *vpifparams = &ch->vpifparams; + enum v4l2_field field = common->fmt.fmt.pix.field; + struct video_obj *vid_ch = &ch->video; + unsigned int hpitch, vpitch, sizeimage; + + if (V4L2_FIELD_ANY == common->fmt.fmt.pix.field) { + if (ch->vpifparams.std_info.frm_fmt) + vid_ch->buf_field = V4L2_FIELD_NONE; + else + vid_ch->buf_field = V4L2_FIELD_INTERLACED; + } else { + vid_ch->buf_field = common->fmt.fmt.pix.field; + } + + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = common->fmt.fmt.pix.sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; + + hpitch = common->fmt.fmt.pix.bytesperline; + vpitch = sizeimage / (hpitch * 2); + if ((V4L2_FIELD_NONE == vid_ch->buf_field) || + (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { + common->ytop_off = 0; + common->ybtm_off = hpitch; + common->ctop_off = sizeimage / 2; + common->cbtm_off = sizeimage / 2 + hpitch; + } else if (V4L2_FIELD_SEQ_TB == vid_ch->buf_field) { + common->ytop_off = 0; + common->ybtm_off = sizeimage / 4; + common->ctop_off = sizeimage / 2; + common->cbtm_off = common->ctop_off + sizeimage / 4; + } else if (V4L2_FIELD_SEQ_BT == vid_ch->buf_field) { + common->ybtm_off = 0; + common->ytop_off = sizeimage / 4; + common->cbtm_off = sizeimage / 2; + common->ctop_off = common->cbtm_off + sizeimage / 4; + } + + if ((V4L2_FIELD_NONE == vid_ch->buf_field) || + (V4L2_FIELD_INTERLACED == vid_ch->buf_field)) { + vpifparams->video_params.storage_mode = 1; + } else { + vpifparams->video_params.storage_mode = 0; + } + + if (ch->vpifparams.std_info.frm_fmt == 1) { + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline; + } else { + if ((field == V4L2_FIELD_ANY) || + (field == V4L2_FIELD_INTERLACED)) + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline * 2; + else + vpifparams->video_params.hpitch = + common->fmt.fmt.pix.bytesperline; + } + + ch->vpifparams.video_params.stdid = ch->vpifparams.std_info.stdid; +} + +static void vpif_config_format(struct channel_obj *ch) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + common->fmt.fmt.pix.field = V4L2_FIELD_ANY; + if (config_params.numbuffers[ch->channel_id] == 0) + common->memory = V4L2_MEMORY_USERPTR; + else + common->memory = V4L2_MEMORY_MMAP; + + common->fmt.fmt.pix.sizeimage = + config_params.channel_bufsize[ch->channel_id]; + common->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV422P; + common->fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; +} + +static int vpif_check_format(struct channel_obj *ch, + struct v4l2_pix_format *pixfmt) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + enum v4l2_field field = pixfmt->field; + u32 sizeimage, hpitch, vpitch; + + if (pixfmt->pixelformat != V4L2_PIX_FMT_YUV422P) + goto invalid_fmt_exit; + + if (!(VPIF_VALID_FIELD(field))) + goto invalid_fmt_exit; + + if (pixfmt->bytesperline <= 0) + goto invalid_pitch_exit; + + if (V4L2_MEMORY_USERPTR == common->memory) + sizeimage = pixfmt->sizeimage; + else + sizeimage = config_params.channel_bufsize[ch->channel_id]; + + if (vpif_get_std_info(ch)) { + vpif_err("Error getting the standard info\n"); + return -EINVAL; + } + + hpitch = pixfmt->bytesperline; + vpitch = sizeimage / (hpitch * 2); + + /* Check for valid value of pitch */ + if ((hpitch < ch->vpifparams.std_info.width) || + (vpitch < ch->vpifparams.std_info.height)) + goto invalid_pitch_exit; + + /* Check for 8 byte alignment */ + if (!ISALIGNED(hpitch)) { + vpif_err("invalid pitch alignment\n"); + return -EINVAL; + } + pixfmt->width = common->fmt.fmt.pix.width; + pixfmt->height = common->fmt.fmt.pix.height; + + return 0; + +invalid_fmt_exit: + vpif_err("invalid field format\n"); + return -EINVAL; + +invalid_pitch_exit: + vpif_err("invalid pitch\n"); + return -EINVAL; +} + +static void vpif_config_addr(struct channel_obj *ch, int muxmode) +{ + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (VPIF_CHANNEL3_VIDEO == ch->channel_id) { + common->set_addr = ch3_set_videobuf_addr; + } else { + if (2 == muxmode) + common->set_addr = ch2_set_videobuf_addr_yc_nmux; + else + common->set_addr = ch2_set_videobuf_addr; + } +} + +/* + * vpif_mmap: It is used to map kernel space buffers into user spaces + */ +static int vpif_mmap(struct file *filep, struct vm_area_struct *vma) +{ + struct vpif_fh *fh = filep->private_data; + struct common_obj *common = &fh->channel->common[VPIF_VIDEO_INDEX]; + + return videobuf_mmap_mapper(&common->buffer_queue, vma); +} + +/* + * vpif_poll: It is used for select/poll system call + */ +static unsigned int vpif_poll(struct file *filep, poll_table *wait) +{ + struct vpif_fh *fh = filep->private_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (common->started) + return videobuf_poll_stream(filep, &common->buffer_queue, wait); + + return 0; +} + +/* + * vpif_open: It creates object of file handle structure and stores it in + * private_data member of filepointer + */ +static int vpif_open(struct file *filep) +{ + struct video_device *vdev = video_devdata(filep); + struct channel_obj *ch = NULL; + struct vpif_fh *fh = NULL; + + ch = video_get_drvdata(vdev); + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpif_fh), GFP_KERNEL); + if (fh == NULL) { + vpif_err("unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + + /* store pointer to fh in private_data member of filep */ + filep->private_data = fh; + fh->channel = ch; + fh->initialized = 0; + if (!ch->initialized) { + fh->initialized = 1; + ch->initialized = 1; + memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); + } + + /* Increment channel usrs counter */ + atomic_inc(&ch->usrs); + /* Set io_allowed[VPIF_VIDEO_INDEX] member to false */ + fh->io_allowed[VPIF_VIDEO_INDEX] = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&ch->prio, &fh->prio); + + return 0; +} + +/* + * vpif_release: This function deletes buffer queue, frees the buffers and + * the vpif file handle + */ +static int vpif_release(struct file *filep) +{ + struct vpif_fh *fh = filep->private_data; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* if this instance is doing IO */ + if (fh->io_allowed[VPIF_VIDEO_INDEX]) { + /* Reset io_usrs member of channel object */ + common->io_usrs = 0; + /* Disable channel */ + if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { + enable_channel2(0); + channel2_intr_enable(0); + } + if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || + (2 == common->started)) { + enable_channel3(0); + channel3_intr_enable(0); + } + common->started = 0; + /* Free buffers allocated */ + videobuf_queue_cancel(&common->buffer_queue); + videobuf_mmap_free(&common->buffer_queue); + common->numbuffers = + config_params.numbuffers[ch->channel_id]; + } + + mutex_unlock(&common->lock); + + /* Decrement channel usrs counter */ + atomic_dec(&ch->usrs); + /* If this file handle has initialize encoder device, reset it */ + if (fh->initialized) + ch->initialized = 0; + + /* Close the priority */ + v4l2_prio_close(&ch->prio, &fh->prio); + filep->private_data = NULL; + fh->initialized = 0; + kfree(fh); + + return 0; +} + +/* functions implementing ioctls */ + +static int vpif_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpif_config *config = vpif_dev->platform_data; + + cap->version = VPIF_DISPLAY_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + strlcpy(cap->driver, "vpif display", sizeof(cap->driver)); + strlcpy(cap->bus_info, "Platform", sizeof(cap->bus_info)); + strlcpy(cap->card, config->card_name, sizeof(cap->card)); + + return 0; +} + +static int vpif_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->index != 0) { + vpif_err("Invalid format index\n"); + return -EINVAL; + } + + /* Fill in the information about format */ + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + strcpy(fmt->description, "YCbCr4:2:2 YC Planar"); + fmt->pixelformat = V4L2_PIX_FMT_YUV422P; + + return 0; +} + +static int vpif_g_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + /* Check the validity of the buffer type */ + if (common->fmt.type != fmt->type) + return -EINVAL; + + /* Fill in the information about format */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (vpif_get_std_info(ch)) { + vpif_err("Error getting the standard info\n"); + return -EINVAL; + } + + *fmt = common->fmt; + mutex_unlock(&common->lock); + return 0; +} + +static int vpif_s_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct v4l2_pix_format *pixfmt; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) + || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_dbg(1, debug, "Channel Busy\n"); + return -EBUSY; + } + + /* Check for the priority */ + ret = v4l2_prio_check(&ch->prio, &fh->prio); + if (0 != ret) + return ret; + fh->initialized = 1; + } + + if (common->started) { + vpif_dbg(1, debug, "Streaming in progress\n"); + return -EBUSY; + } + + pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + ret = vpif_check_format(ch, pixfmt); + if (ret) + return ret; + + /* store the pix format in the channel object */ + common->fmt.fmt.pix = *pixfmt; + /* store the format in the channel object */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + common->fmt = *fmt; + mutex_unlock(&common->lock); + + return 0; +} + +static int vpif_try_fmt_vid_out(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + int ret = 0; + + ret = vpif_check_format(ch, pixfmt); + if (ret) { + *pixfmt = common->fmt.fmt.pix; + pixfmt->sizeimage = pixfmt->width * pixfmt->height * 2; + } + + return ret; +} + +static int vpif_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbuf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common; + enum v4l2_field field; + u8 index = 0; + int ret = 0; + + /* This file handle has not initialized the channel, + It is not allowed to do settings */ + if ((VPIF_CHANNEL2_VIDEO == ch->channel_id) + || (VPIF_CHANNEL3_VIDEO == ch->channel_id)) { + if (!fh->initialized) { + vpif_err("Channel Busy\n"); + return -EBUSY; + } + } + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != reqbuf->type) + return -EINVAL; + + index = VPIF_VIDEO_INDEX; + + common = &ch->common[index]; + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (common->fmt.type != reqbuf->type) { + ret = -EINVAL; + goto reqbuf_exit; + } + + if (0 != common->io_usrs) { + ret = -EBUSY; + goto reqbuf_exit; + } + + if (reqbuf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (common->fmt.fmt.pix.field == V4L2_FIELD_ANY) + field = V4L2_FIELD_INTERLACED; + else + field = common->fmt.fmt.pix.field; + } else { + field = V4L2_VBI_INTERLACED; + } + + /* Initialize videobuf queue as per the buffer type */ + videobuf_queue_dma_contig_init(&common->buffer_queue, + &video_qops, NULL, + &common->irqlock, + reqbuf->type, field, + sizeof(struct videobuf_buffer), fh); + + /* Set io allowed member of file handle to TRUE */ + fh->io_allowed[index] = 1; + /* Increment io usrs member of channel object to 1 */ + common->io_usrs = 1; + /* Store type of memory requested in channel object */ + common->memory = reqbuf->memory; + INIT_LIST_HEAD(&common->dma_queue); + + /* Allocate buffers */ + ret = videobuf_reqbufs(&common->buffer_queue, reqbuf); + +reqbuf_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_querybuf(struct file *file, void *priv, + struct v4l2_buffer *tbuf) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (common->fmt.type != tbuf->type) + return -EINVAL; + + return videobuf_querybuf(&common->buffer_queue, tbuf); +} + +static int vpif_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct v4l2_buffer tbuf = *buf; + struct videobuf_buffer *buf1; + unsigned long addr = 0; + unsigned long flags; + int ret = 0; + + if (common->fmt.type != tbuf.type) + return -EINVAL; + + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_err("fh->io_allowed\n"); + return -EACCES; + } + + if (!(list_empty(&common->dma_queue)) || + (common->cur_frm != common->next_frm) || + !(common->started) || + (common->started && (0 == ch->field_id))) + return videobuf_qbuf(&common->buffer_queue, buf); + + /* bufferqueue is empty store buffer address in VPIF registers */ + mutex_lock(&common->buffer_queue.vb_lock); + buf1 = common->buffer_queue.bufs[tbuf.index]; + if (buf1->memory != tbuf.memory) { + vpif_err("invalid buffer type\n"); + goto qbuf_exit; + } + + if ((buf1->state == VIDEOBUF_QUEUED) || + (buf1->state == VIDEOBUF_ACTIVE)) { + vpif_err("invalid state\n"); + goto qbuf_exit; + } + + switch (buf1->memory) { + case V4L2_MEMORY_MMAP: + if (buf1->baddr == 0) + goto qbuf_exit; + break; + + case V4L2_MEMORY_USERPTR: + if (tbuf.length < buf1->bsize) + goto qbuf_exit; + + if ((VIDEOBUF_NEEDS_INIT != buf1->state) + && (buf1->baddr != tbuf.m.userptr)) + vpif_buffer_release(&common->buffer_queue, buf1); + buf1->baddr = tbuf.m.userptr; + break; + + default: + goto qbuf_exit; + } + + local_irq_save(flags); + ret = vpif_buffer_prepare(&common->buffer_queue, buf1, + common->buffer_queue.field); + if (ret < 0) { + local_irq_restore(flags); + goto qbuf_exit; + } + + buf1->state = VIDEOBUF_ACTIVE; + addr = buf1->boff; + common->next_frm = buf1; + if (tbuf.type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + common->set_addr((addr + common->ytop_off), + (addr + common->ybtm_off), + (addr + common->ctop_off), + (addr + common->cbtm_off)); + } + + local_irq_restore(flags); + list_add_tail(&buf1->stream, &common->buffer_queue.stream); + mutex_unlock(&common->buffer_queue.vb_lock); + return 0; + +qbuf_exit: + mutex_unlock(&common->buffer_queue.vb_lock); + return -EINVAL; +} + +static int vpif_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + if (!(*std_id & DM646X_V4L2_STD)) + return -EINVAL; + + if (common->started) { + vpif_err("streaming in progress\n"); + return -EBUSY; + } + + /* Call encoder subdevice function to set the standard */ + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + ch->video.stdid = *std_id; + /* Get the information about the standard */ + if (vpif_get_std_info(ch)) { + vpif_err("Error getting the standard info\n"); + return -EINVAL; + } + + if ((ch->vpifparams.std_info.width * + ch->vpifparams.std_info.height * 2) > + config_params.channel_bufsize[ch->channel_id]) { + vpif_err("invalid std for this size\n"); + ret = -EINVAL; + goto s_std_exit; + } + + common->fmt.fmt.pix.bytesperline = common->fmt.fmt.pix.width; + /* Configure the default format information */ + vpif_config_format(ch); + + ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, + s_std_output, *std_id); + if (ret < 0) { + vpif_err("Failed to set output standard\n"); + goto s_std_exit; + } + + ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, core, + s_std, *std_id); + if (ret < 0) + vpif_err("Failed to set standard for sub devices\n"); + +s_std_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + *std = ch->video.stdid; + return 0; +} + +static int vpif_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + return videobuf_dqbuf(&common->buffer_queue, p, + (file->f_flags & O_NONBLOCK)); +} + +static int vpif_streamon(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + struct channel_obj *oth_ch = vpif_obj.dev[!ch->channel_id]; + struct vpif_params *vpif = &ch->vpifparams; + struct vpif_config *vpif_config_data = + vpif_dev->platform_data; + unsigned long addr = 0; + int ret = 0; + + if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + vpif_err("buffer type not supported\n"); + return -EINVAL; + } + + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_err("fh->io_allowed\n"); + return -EACCES; + } + + /* If Streaming is already started, return error */ + if (common->started) { + vpif_err("channel->started\n"); + return -EBUSY; + } + + if ((ch->channel_id == VPIF_CHANNEL2_VIDEO + && oth_ch->common[VPIF_VIDEO_INDEX].started && + ch->vpifparams.std_info.ycmux_mode == 0) + || ((ch->channel_id == VPIF_CHANNEL3_VIDEO) + && (2 == oth_ch->common[VPIF_VIDEO_INDEX].started))) { + vpif_err("other channel is using\n"); + return -EBUSY; + } + + ret = vpif_check_format(ch, &common->fmt.fmt.pix); + if (ret < 0) + return ret; + + /* Call videobuf_streamon to start streaming in videobuf */ + ret = videobuf_streamon(&common->buffer_queue); + if (ret < 0) { + vpif_err("videobuf_streamon\n"); + return ret; + } + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + /* If buffer queue is empty, return error */ + if (list_empty(&common->dma_queue)) { + vpif_err("buffer queue is empty\n"); + ret = -EIO; + goto streamon_exit; + } + + /* Get the next frame from the buffer queue */ + common->next_frm = common->cur_frm = + list_entry(common->dma_queue.next, + struct videobuf_buffer, queue); + + list_del(&common->cur_frm->queue); + /* Mark state of the current frame to active */ + common->cur_frm->state = VIDEOBUF_ACTIVE; + + /* Initialize field_id and started member */ + ch->field_id = 0; + common->started = 1; + if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + addr = common->cur_frm->boff; + /* Calculate the offset for Y and C data in the buffer */ + vpif_calculate_offsets(ch); + + if ((ch->vpifparams.std_info.frm_fmt && + ((common->fmt.fmt.pix.field != V4L2_FIELD_NONE) + && (common->fmt.fmt.pix.field != V4L2_FIELD_ANY))) + || (!ch->vpifparams.std_info.frm_fmt + && (common->fmt.fmt.pix.field == V4L2_FIELD_NONE))) { + vpif_err("conflict in field format and std format\n"); + ret = -EINVAL; + goto streamon_exit; + } + + /* clock settings */ + ret = + vpif_config_data->set_clock(ch->vpifparams.std_info.ycmux_mode, + ch->vpifparams.std_info.hd_sd); + if (ret < 0) { + vpif_err("can't set clock\n"); + goto streamon_exit; + } + + /* set the parameters and addresses */ + ret = vpif_set_video_params(vpif, ch->channel_id + 2); + if (ret < 0) + goto streamon_exit; + + common->started = ret; + vpif_config_addr(ch, ret); + common->set_addr((addr + common->ytop_off), + (addr + common->ybtm_off), + (addr + common->ctop_off), + (addr + common->cbtm_off)); + + /* Set interrupt for both the fields in VPIF + Register enable channel in VPIF register */ + if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { + channel2_intr_assert(); + channel2_intr_enable(1); + enable_channel2(1); + } + + if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) + || (common->started == 2)) { + channel3_intr_assert(); + channel3_intr_enable(1); + enable_channel3(1); + } + channel_first_int[VPIF_VIDEO_INDEX][ch->channel_id] = 1; + } + +streamon_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buftype) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + + if (buftype != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + vpif_err("buffer type not supported\n"); + return -EINVAL; + } + + if (!fh->io_allowed[VPIF_VIDEO_INDEX]) { + vpif_err("fh->io_allowed\n"); + return -EACCES; + } + + if (!common->started) { + vpif_err("channel->started\n"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (buftype == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + /* disable channel */ + if (VPIF_CHANNEL2_VIDEO == ch->channel_id) { + enable_channel2(0); + channel2_intr_enable(0); + } + if ((VPIF_CHANNEL3_VIDEO == ch->channel_id) || + (2 == common->started)) { + enable_channel3(0); + channel3_intr_enable(0); + } + } + + common->started = 0; + mutex_unlock(&common->lock); + + return videobuf_streamoff(&common->buffer_queue); +} + +static int vpif_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != crop->type) + return -EINVAL; + + crop->bounds.left = crop->bounds.top = 0; + crop->defrect.left = crop->defrect.top = 0; + crop->defrect.height = crop->bounds.height = common->height; + crop->defrect.width = crop->bounds.width = common->width; + + return 0; +} + +static int vpif_enum_output(struct file *file, void *fh, + struct v4l2_output *output) +{ + + struct vpif_config *config = vpif_dev->platform_data; + + if (output->index >= config->output_count) { + vpif_dbg(1, debug, "Invalid output index\n"); + return -EINVAL; + } + + strcpy(output->name, config->output[output->index]); + output->type = V4L2_OUTPUT_TYPE_ANALOG; + output->std = DM646X_V4L2_STD; + + return 0; +} + +static int vpif_s_output(struct file *file, void *priv, unsigned int i) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + struct common_obj *common = &ch->common[VPIF_VIDEO_INDEX]; + int ret = 0; + + if (mutex_lock_interruptible(&common->lock)) + return -ERESTARTSYS; + + if (common->started) { + vpif_err("Streaming in progress\n"); + ret = -EBUSY; + goto s_output_exit; + } + + ret = v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 1, video, + s_routing, 0, i, 0); + + if (ret < 0) + vpif_err("Failed to set output standard\n"); + + vid_ch->output_id = i; + +s_output_exit: + mutex_unlock(&common->lock); + return ret; +} + +static int vpif_g_output(struct file *file, void *priv, unsigned int *i) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + struct video_obj *vid_ch = &ch->video; + + *i = vid_ch->output_id; + + return 0; +} + +static int vpif_g_priority(struct file *file, void *priv, enum v4l2_priority *p) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + *p = v4l2_prio_max(&ch->prio); + + return 0; +} + +static int vpif_s_priority(struct file *file, void *priv, enum v4l2_priority p) +{ + struct vpif_fh *fh = priv; + struct channel_obj *ch = fh->channel; + + return v4l2_prio_change(&ch->prio, &fh->prio, p); +} + +/* vpif display ioctl operations */ +static const struct v4l2_ioctl_ops vpif_ioctl_ops = { + .vidioc_querycap = vpif_querycap, + .vidioc_g_priority = vpif_g_priority, + .vidioc_s_priority = vpif_s_priority, + .vidioc_enum_fmt_vid_out = vpif_enum_fmt_vid_out, + .vidioc_g_fmt_vid_out = vpif_g_fmt_vid_out, + .vidioc_s_fmt_vid_out = vpif_s_fmt_vid_out, + .vidioc_try_fmt_vid_out = vpif_try_fmt_vid_out, + .vidioc_reqbufs = vpif_reqbufs, + .vidioc_querybuf = vpif_querybuf, + .vidioc_qbuf = vpif_qbuf, + .vidioc_dqbuf = vpif_dqbuf, + .vidioc_streamon = vpif_streamon, + .vidioc_streamoff = vpif_streamoff, + .vidioc_s_std = vpif_s_std, + .vidioc_g_std = vpif_g_std, + .vidioc_enum_output = vpif_enum_output, + .vidioc_s_output = vpif_s_output, + .vidioc_g_output = vpif_g_output, + .vidioc_cropcap = vpif_cropcap, +}; + +static const struct v4l2_file_operations vpif_fops = { + .owner = THIS_MODULE, + .open = vpif_open, + .release = vpif_release, + .ioctl = video_ioctl2, + .mmap = vpif_mmap, + .poll = vpif_poll +}; + +static struct video_device vpif_video_template = { + .name = "vpif", + .fops = &vpif_fops, + .minor = -1, + .ioctl_ops = &vpif_ioctl_ops, + .tvnorms = DM646X_V4L2_STD, + .current_norm = V4L2_STD_625_50, + +}; + +/*Configure the channels, buffer sizei, request irq */ +static int initialize_vpif(void) +{ + int free_channel_objects_index; + int free_buffer_channel_index; + int free_buffer_index; + int err = 0, i, j; + + /* Default number of buffers should be 3 */ + if ((ch2_numbuffers > 0) && + (ch2_numbuffers < config_params.min_numbuffers)) + ch2_numbuffers = config_params.min_numbuffers; + if ((ch3_numbuffers > 0) && + (ch3_numbuffers < config_params.min_numbuffers)) + ch3_numbuffers = config_params.min_numbuffers; + + /* Set buffer size to min buffers size if invalid buffer size is + * given */ + if (ch2_bufsize < config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]) + ch2_bufsize = + config_params.min_bufsize[VPIF_CHANNEL2_VIDEO]; + if (ch3_bufsize < config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]) + ch3_bufsize = + config_params.min_bufsize[VPIF_CHANNEL3_VIDEO]; + + config_params.numbuffers[VPIF_CHANNEL2_VIDEO] = ch2_numbuffers; + + if (ch2_numbuffers) { + config_params.channel_bufsize[VPIF_CHANNEL2_VIDEO] = + ch2_bufsize; + } + config_params.numbuffers[VPIF_CHANNEL3_VIDEO] = ch3_numbuffers; + + if (ch3_numbuffers) { + config_params.channel_bufsize[VPIF_CHANNEL3_VIDEO] = + ch3_bufsize; + } + + /* Allocate memory for six channel objects */ + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { + vpif_obj.dev[i] = + kmalloc(sizeof(struct channel_obj), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!vpif_obj.dev[i]) { + free_channel_objects_index = i; + err = -ENOMEM; + goto vpif_init_free_channel_objects; + } + } + + free_channel_objects_index = VPIF_DISPLAY_MAX_DEVICES; + free_buffer_channel_index = VPIF_DISPLAY_NUM_CHANNELS; + free_buffer_index = config_params.numbuffers[i - 1]; + + return 0; + +vpif_init_free_channel_objects: + for (j = 0; j < free_channel_objects_index; j++) + kfree(vpif_obj.dev[j]); + return err; +} + +/* + * vpif_probe: This function creates device entries by register itself to the + * V4L2 driver and initializes fields of each channel objects + */ +static __init int vpif_probe(struct platform_device *pdev) +{ + const struct vpif_subdev_info *subdevdata; + int i, j = 0, k, q, m, err = 0; + struct i2c_adapter *i2c_adap; + struct vpif_config *config; + struct common_obj *common; + struct channel_obj *ch; + struct video_device *vfd; + struct resource *res; + int subdev_count; + + vpif_dev = &pdev->dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + v4l2_err(vpif_dev->driver, + "Error getting platform resource\n"); + return -ENOENT; + } + + if (!request_mem_region(res->start, res->end - res->start + 1, + vpif_dev->driver->name)) { + v4l2_err(vpif_dev->driver, "VPIF: failed request_mem_region\n"); + return -ENXIO; + } + + vpif_base = ioremap_nocache(res->start, res->end - res->start + 1); + if (!vpif_base) { + v4l2_err(vpif_dev->driver, "Unable to ioremap VPIF reg\n"); + err = -ENXIO; + goto resource_exit; + } + + vpif_base_addr_init(vpif_base); + + initialize_vpif(); + + err = v4l2_device_register(vpif_dev, &vpif_obj.v4l2_dev); + if (err) { + v4l2_err(vpif_dev->driver, "Error registering v4l2 device\n"); + return err; + } + + k = 0; + while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, k))) { + for (i = res->start; i <= res->end; i++) { + if (request_irq(i, vpif_channel_isr, IRQF_DISABLED, + "DM646x_Display", + (void *)(&vpif_obj.dev[k]->channel_id))) { + err = -EBUSY; + goto vpif_int_err; + } + } + k++; + } + + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { + + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[i]; + + /* Allocate memory for video device */ + vfd = video_device_alloc(); + if (vfd == NULL) { + for (j = 0; j < i; j++) { + ch = vpif_obj.dev[j]; + video_device_release(ch->video_dev); + } + err = -ENOMEM; + goto video_dev_alloc_exit; + } + + /* Initialize field of video device */ + *vfd = vpif_video_template; + vfd->v4l2_dev = &vpif_obj.v4l2_dev; + vfd->release = video_device_release; + snprintf(vfd->name, sizeof(vfd->name), + "DM646x_VPIFDisplay_DRIVER_V%d.%d.%d", + (VPIF_DISPLAY_VERSION_CODE >> 16) & 0xff, + (VPIF_DISPLAY_VERSION_CODE >> 8) & 0xff, + (VPIF_DISPLAY_VERSION_CODE) & 0xff); + + /* Set video_dev to the video device */ + ch->video_dev = vfd; + } + + for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) { + ch = vpif_obj.dev[j]; + /* Initialize field of the channel objects */ + atomic_set(&ch->usrs, 0); + for (k = 0; k < VPIF_NUMOBJECTS; k++) { + ch->common[k].numbuffers = 0; + common = &ch->common[k]; + common->io_usrs = 0; + common->started = 0; + spin_lock_init(&common->irqlock); + mutex_init(&common->lock); + common->numbuffers = 0; + common->set_addr = NULL; + common->ytop_off = common->ybtm_off = 0; + common->ctop_off = common->cbtm_off = 0; + common->cur_frm = common->next_frm = NULL; + memset(&common->fmt, 0, sizeof(common->fmt)); + common->numbuffers = config_params.numbuffers[k]; + + } + ch->initialized = 0; + ch->channel_id = j; + if (j < 2) + ch->common[VPIF_VIDEO_INDEX].numbuffers = + config_params.numbuffers[ch->channel_id]; + else + ch->common[VPIF_VIDEO_INDEX].numbuffers = 0; + + memset(&ch->vpifparams, 0, sizeof(ch->vpifparams)); + + /* Initialize prio member of channel object */ + v4l2_prio_init(&ch->prio); + ch->common[VPIF_VIDEO_INDEX].fmt.type = + V4L2_BUF_TYPE_VIDEO_OUTPUT; + + /* register video device */ + vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n", + (int)ch, (int)&ch->video_dev); + + err = video_register_device(ch->video_dev, + VFL_TYPE_GRABBER, (j ? 3 : 2)); + if (err < 0) + goto probe_out; + + video_set_drvdata(ch->video_dev, ch); + } + + i2c_adap = i2c_get_adapter(1); + config = pdev->dev.platform_data; + subdev_count = config->subdev_count; + subdevdata = config->subdevinfo; + vpif_obj.sd = kmalloc(sizeof(struct v4l2_subdev *) * subdev_count, + GFP_KERNEL); + if (vpif_obj.sd == NULL) { + vpif_err("unable to allocate memory for subdevice pointers\n"); + err = -ENOMEM; + goto probe_out; + } + + for (i = 0; i < subdev_count; i++) { + vpif_obj.sd[i] = v4l2_i2c_new_probed_subdev(&vpif_obj.v4l2_dev, + i2c_adap, subdevdata[i].name, + subdevdata[i].name, + &subdevdata[i].addr); + if (!vpif_obj.sd[i]) { + vpif_err("Error registering v4l2 subdevice\n"); + goto probe_subdev_out; + } + + if (vpif_obj.sd[i]) + vpif_obj.sd[i]->grp_id = 1 << i; + } + + return 0; + +probe_subdev_out: + kfree(vpif_obj.sd); +probe_out: + for (k = 0; k < j; k++) { + ch = vpif_obj.dev[k]; + video_unregister_device(ch->video_dev); + video_device_release(ch->video_dev); + ch->video_dev = NULL; + } +vpif_int_err: + v4l2_device_unregister(&vpif_obj.v4l2_dev); + vpif_err("VPIF IRQ request failed\n"); + for (q = k; k >= 0; k--) { + for (m = i; m >= res->start; m--) + free_irq(m, (void *)(&vpif_obj.dev[k]->channel_id)); + res = platform_get_resource(pdev, IORESOURCE_IRQ, k-1); + m = res->end; + } +video_dev_alloc_exit: + iounmap(vpif_base); +resource_exit: + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + + return err; +} + +/* + * vpif_remove: It un-register channels from V4L2 driver + */ +static int vpif_remove(struct platform_device *device) +{ + struct channel_obj *ch; + int i; + + v4l2_device_unregister(&vpif_obj.v4l2_dev); + + /* un-register device */ + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the channel object */ + ch = vpif_obj.dev[i]; + /* Unregister video device */ + video_unregister_device(ch->video_dev); + + ch->video_dev = NULL; + } + + return 0; +} + +static struct platform_driver vpif_driver = { + .driver = { + .name = "vpif_display", + .owner = THIS_MODULE, + }, + .probe = vpif_probe, + .remove = vpif_remove, +}; + +static __init int vpif_init(void) +{ + return platform_driver_register(&vpif_driver); +} + +/* + * vpif_cleanup: This function un-registers device and driver to the kernel, + * frees requested irq handler and de-allocates memory allocated for channel + * objects. + */ +static void vpif_cleanup(void) +{ + struct platform_device *pdev; + struct resource *res; + int irq_num; + int i = 0; + + pdev = container_of(vpif_dev, struct platform_device, dev); + + while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) { + for (irq_num = res->start; irq_num <= res->end; irq_num++) + free_irq(irq_num, + (void *)(&vpif_obj.dev[i]->channel_id)); + i++; + } + + iounmap(vpif_base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + platform_driver_unregister(&vpif_driver); + kfree(vpif_obj.sd); + for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) + kfree(vpif_obj.dev[i]); +} + +module_init(vpif_init); +module_exit(vpif_cleanup); diff --git a/drivers/media/video/davinci/vpif_display.h b/drivers/media/video/davinci/vpif_display.h new file mode 100644 index 000000000000..a2a7cd166bbf --- /dev/null +++ b/drivers/media/video/davinci/vpif_display.h @@ -0,0 +1,175 @@ +/* + * DM646x display header file + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef DAVINCIHD_DISPLAY_H +#define DAVINCIHD_DISPLAY_H + +/* Header files */ +#include <linux/videodev2.h> +#include <linux/version.h> +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/videobuf-core.h> +#include <media/videobuf-dma-contig.h> + +#include "vpif.h" + +/* Macros */ +#define VPIF_MAJOR_RELEASE (0) +#define VPIF_MINOR_RELEASE (0) +#define VPIF_BUILD (1) + +#define VPIF_DISPLAY_VERSION_CODE \ + ((VPIF_MAJOR_RELEASE << 16) | (VPIF_MINOR_RELEASE << 8) | VPIF_BUILD) + +#define VPIF_VALID_FIELD(field) \ + (((V4L2_FIELD_ANY == field) || (V4L2_FIELD_NONE == field)) || \ + (((V4L2_FIELD_INTERLACED == field) || (V4L2_FIELD_SEQ_TB == field)) || \ + (V4L2_FIELD_SEQ_BT == field))) + +#define VPIF_DISPLAY_MAX_DEVICES (2) +#define VPIF_SLICED_BUF_SIZE (256) +#define VPIF_SLICED_MAX_SERVICES (3) +#define VPIF_VIDEO_INDEX (0) +#define VPIF_VBI_INDEX (1) +#define VPIF_HBI_INDEX (2) + +/* Setting it to 1 as HBI/VBI support yet to be added , else 3*/ +#define VPIF_NUMOBJECTS (1) + +/* Macros */ +#define ISALIGNED(a) (0 == ((a) & 7)) + +/* enumerated data types */ +/* Enumerated data type to give id to each device per channel */ +enum vpif_channel_id { + VPIF_CHANNEL2_VIDEO = 0, /* Channel2 Video */ + VPIF_CHANNEL3_VIDEO, /* Channel3 Video */ +}; + +/* structures */ + +struct video_obj { + enum v4l2_field buf_field; + u32 latest_only; /* indicate whether to return + * most recent displayed frame only */ + v4l2_std_id stdid; /* Currently selected or default + * standard */ + u32 output_id; /* Current output id */ +}; + +struct vbi_obj { + int num_services; + struct vpif_vbi_params vbiparams; /* vpif parameters for the raw + * vbi data */ +}; + +struct common_obj { + /* Buffer specific parameters */ + u8 *fbuffers[VIDEO_MAX_FRAME]; /* List of buffer pointers for + * storing frames */ + u32 numbuffers; /* number of buffers */ + struct videobuf_buffer *cur_frm; /* Pointer pointing to current + * videobuf_buffer */ + struct videobuf_buffer *next_frm; /* Pointer pointing to next + * videobuf_buffer */ + enum v4l2_memory memory; /* This field keeps track of + * type of buffer exchange + * method user has selected */ + struct v4l2_format fmt; /* Used to store the format */ + struct videobuf_queue buffer_queue; /* Buffer queue used in + * video-buf */ + struct list_head dma_queue; /* Queue of filled frames */ + spinlock_t irqlock; /* Used in video-buf */ + + /* channel specific parameters */ + struct mutex lock; /* lock used to access this + * structure */ + u32 io_usrs; /* number of users performing + * IO */ + u8 started; /* Indicates whether streaming + * started */ + u32 ytop_off; /* offset of Y top from the + * starting of the buffer */ + u32 ybtm_off; /* offset of Y bottom from the + * starting of the buffer */ + u32 ctop_off; /* offset of C top from the + * starting of the buffer */ + u32 cbtm_off; /* offset of C bottom from the + * starting of the buffer */ + /* Function pointer to set the addresses */ + void (*set_addr) (unsigned long, unsigned long, + unsigned long, unsigned long); + u32 height; + u32 width; +}; + +struct channel_obj { + /* V4l2 specific parameters */ + struct video_device *video_dev; /* Identifies video device for + * this channel */ + struct v4l2_prio_state prio; /* Used to keep track of state of + * the priority */ + atomic_t usrs; /* number of open instances of + * the channel */ + u32 field_id; /* Indicates id of the field + * which is being displayed */ + u8 initialized; /* flag to indicate whether + * encoder is initialized */ + + enum vpif_channel_id channel_id;/* Identifies channel */ + struct vpif_params vpifparams; + struct common_obj common[VPIF_NUMOBJECTS]; + struct video_obj video; + struct vbi_obj vbi; +}; + +/* File handle structure */ +struct vpif_fh { + struct channel_obj *channel; /* pointer to channel object for + * opened device */ + u8 io_allowed[VPIF_NUMOBJECTS]; /* Indicates whether this file handle + * is doing IO */ + enum v4l2_priority prio; /* Used to keep track priority of + * this instance */ + u8 initialized; /* Used to keep track of whether this + * file handle has initialized + * channel or not */ +}; + +/* vpif device structure */ +struct vpif_device { + struct v4l2_device v4l2_dev; + struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS]; + struct v4l2_subdev **sd; + +}; + +struct vpif_config_params { + u32 min_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; + u32 channel_bufsize[VPIF_DISPLAY_NUM_CHANNELS]; + u8 numbuffers[VPIF_DISPLAY_NUM_CHANNELS]; + u8 min_numbuffers; +}; + +/* Struct which keeps track of the line numbers for the sliced vbi service */ +struct vpif_service_line { + u16 service_id; + u16 service_line[2]; + u16 enc_service_id; + u8 bytestowrite; +}; + +#endif /* DAVINCIHD_DISPLAY_H */ diff --git a/drivers/media/video/davinci/vpss.c b/drivers/media/video/davinci/vpss.c new file mode 100644 index 000000000000..6d709ca8cfb0 --- /dev/null +++ b/drivers/media/video/davinci/vpss.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2009 Texas Instruments. + * + * 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. + * + * common vpss driver for all video drivers. + */ +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/compiler.h> +#include <linux/io.h> +#include <mach/hardware.h> +#include <media/davinci/vpss.h> + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VPSS Driver"); +MODULE_AUTHOR("Texas Instruments"); + +/* DM644x defines */ +#define DM644X_SBL_PCR_VPSS (4) + +/* vpss BL register offsets */ +#define DM355_VPSSBL_CCDCMUX 0x1c +/* vpss CLK register offsets */ +#define DM355_VPSSCLK_CLKCTRL 0x04 +/* masks and shifts */ +#define VPSS_HSSISEL_SHIFT 4 + +/* + * vpss operations. Depends on platform. Not all functions are available + * on all platforms. The api, first check if a functio is available before + * invoking it. In the probe, the function ptrs are intialized based on + * vpss name. vpss name can be "dm355_vpss", "dm644x_vpss" etc. + */ +struct vpss_hw_ops { + /* enable clock */ + int (*enable_clock)(enum vpss_clock_sel clock_sel, int en); + /* select input to ccdc */ + void (*select_ccdc_source)(enum vpss_ccdc_source_sel src_sel); + /* clear wbl overlflow bit */ + int (*clear_wbl_overflow)(enum vpss_wbl_sel wbl_sel); +}; + +/* vpss configuration */ +struct vpss_oper_config { + __iomem void *vpss_bl_regs_base; + __iomem void *vpss_regs_base; + struct resource *r1; + resource_size_t len1; + struct resource *r2; + resource_size_t len2; + char vpss_name[32]; + spinlock_t vpss_lock; + struct vpss_hw_ops hw_ops; +}; + +static struct vpss_oper_config oper_cfg; + +/* register access routines */ +static inline u32 bl_regr(u32 offset) +{ + return __raw_readl(oper_cfg.vpss_bl_regs_base + offset); +} + +static inline void bl_regw(u32 val, u32 offset) +{ + __raw_writel(val, oper_cfg.vpss_bl_regs_base + offset); +} + +static inline u32 vpss_regr(u32 offset) +{ + return __raw_readl(oper_cfg.vpss_regs_base + offset); +} + +static inline void vpss_regw(u32 val, u32 offset) +{ + __raw_writel(val, oper_cfg.vpss_regs_base + offset); +} + +static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) +{ + bl_regw(src_sel << VPSS_HSSISEL_SHIFT, DM355_VPSSBL_CCDCMUX); +} + +int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) +{ + if (!oper_cfg.hw_ops.select_ccdc_source) + return -1; + + dm355_select_ccdc_source(src_sel); + return 0; +} +EXPORT_SYMBOL(vpss_select_ccdc_source); + +static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) +{ + u32 mask = 1, val; + + if (wbl_sel < VPSS_PCR_AEW_WBL_0 || + wbl_sel > VPSS_PCR_CCDC_WBL_O) + return -1; + + /* writing a 0 clear the overflow */ + mask = ~(mask << wbl_sel); + val = bl_regr(DM644X_SBL_PCR_VPSS) & mask; + bl_regw(val, DM644X_SBL_PCR_VPSS); + return 0; +} + +int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) +{ + if (!oper_cfg.hw_ops.clear_wbl_overflow) + return -1; + + return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel); +} +EXPORT_SYMBOL(vpss_clear_wbl_overflow); + +/* + * dm355_enable_clock - Enable VPSS Clock + * @clock_sel: CLock to be enabled/disabled + * @en: enable/disable flag + * + * This is called to enable or disable a vpss clock + */ +static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en) +{ + unsigned long flags; + u32 utemp, mask = 0x1, shift = 0; + + switch (clock_sel) { + case VPSS_VPBE_CLOCK: + /* nothing since lsb */ + break; + case VPSS_VENC_CLOCK_SEL: + shift = 2; + break; + case VPSS_CFALD_CLOCK: + shift = 3; + break; + case VPSS_H3A_CLOCK: + shift = 4; + break; + case VPSS_IPIPE_CLOCK: + shift = 5; + break; + case VPSS_CCDC_CLOCK: + shift = 6; + break; + default: + printk(KERN_ERR "dm355_enable_clock:" + " Invalid selector: %d\n", clock_sel); + return -1; + } + + spin_lock_irqsave(&oper_cfg.vpss_lock, flags); + utemp = vpss_regr(DM355_VPSSCLK_CLKCTRL); + if (!en) + utemp &= ~(mask << shift); + else + utemp |= (mask << shift); + + vpss_regw(utemp, DM355_VPSSCLK_CLKCTRL); + spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); + return 0; +} + +int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en) +{ + if (!oper_cfg.hw_ops.enable_clock) + return -1; + + return oper_cfg.hw_ops.enable_clock(clock_sel, en); +} +EXPORT_SYMBOL(vpss_enable_clock); + +static int __init vpss_probe(struct platform_device *pdev) +{ + int status, dm355 = 0; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "no platform data\n"); + return -ENOENT; + } + strcpy(oper_cfg.vpss_name, pdev->dev.platform_data); + + if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) + dm355 = 1; + else if (strcmp(oper_cfg.vpss_name, "dm644x_vpss")) { + dev_err(&pdev->dev, "vpss driver not supported on" + " this platform\n"); + return -ENODEV; + } + + dev_info(&pdev->dev, "%s vpss probed\n", oper_cfg.vpss_name); + oper_cfg.r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!oper_cfg.r1) + return -ENOENT; + + oper_cfg.len1 = oper_cfg.r1->end - oper_cfg.r1->start + 1; + + oper_cfg.r1 = request_mem_region(oper_cfg.r1->start, oper_cfg.len1, + oper_cfg.r1->name); + if (!oper_cfg.r1) + return -EBUSY; + + oper_cfg.vpss_bl_regs_base = ioremap(oper_cfg.r1->start, oper_cfg.len1); + if (!oper_cfg.vpss_bl_regs_base) { + status = -EBUSY; + goto fail1; + } + + if (dm355) { + oper_cfg.r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!oper_cfg.r2) { + status = -ENOENT; + goto fail2; + } + oper_cfg.len2 = oper_cfg.r2->end - oper_cfg.r2->start + 1; + oper_cfg.r2 = request_mem_region(oper_cfg.r2->start, + oper_cfg.len2, + oper_cfg.r2->name); + if (!oper_cfg.r2) { + status = -EBUSY; + goto fail2; + } + + oper_cfg.vpss_regs_base = ioremap(oper_cfg.r2->start, + oper_cfg.len2); + if (!oper_cfg.vpss_regs_base) { + status = -EBUSY; + goto fail3; + } + } + + if (dm355) { + oper_cfg.hw_ops.enable_clock = dm355_enable_clock; + oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source; + } else + oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow; + + spin_lock_init(&oper_cfg.vpss_lock); + dev_info(&pdev->dev, "%s vpss probe success\n", oper_cfg.vpss_name); + return 0; + +fail3: + release_mem_region(oper_cfg.r2->start, oper_cfg.len2); +fail2: + iounmap(oper_cfg.vpss_bl_regs_base); +fail1: + release_mem_region(oper_cfg.r1->start, oper_cfg.len1); + return status; +} + +static int vpss_remove(struct platform_device *pdev) +{ + iounmap(oper_cfg.vpss_bl_regs_base); + release_mem_region(oper_cfg.r1->start, oper_cfg.len1); + if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) { + iounmap(oper_cfg.vpss_regs_base); + release_mem_region(oper_cfg.r2->start, oper_cfg.len2); + } + return 0; +} + +static struct platform_driver vpss_driver = { + .driver = { + .name = "vpss", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(vpss_remove), + .probe = vpss_probe, +}; + +static void vpss_exit(void) +{ + platform_driver_unregister(&vpss_driver); +} + +static int __init vpss_init(void) +{ + return platform_driver_register(&vpss_driver); +} +subsys_initcall(vpss_init); +module_exit(vpss_exit); diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 1c2e544eda73..b184d482c497 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -558,6 +558,27 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2861_BOARD_GADMEI_UTV330PLUS] = { + .name = "Gadmei UTV330+", + .tuner_type = TUNER_TNF_5335MF, + .tda9887_conf = TDA9887_PRESENT, + .ir_codes = ir_codes_gadmei_rm008z, + .decoder = EM28XX_SAA711X, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, [EM2860_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Cinergy A Hybrid XS", .valid = EM28XX_BOARD_NOT_VALIDATED, @@ -870,6 +891,8 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_TVP5150, .has_dvb = 1, .dvb_gpio = default_digital, + .ir_codes = ir_codes_terratec_cinergy_xs, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1649,6 +1672,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U }, { USB_DEVICE(0x04bb, 0x0515), .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ }, + { USB_DEVICE(0xeb1a, 0x50a6), + .driver_info = EM2860_BOARD_GADMEI_UTV330 }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -1661,7 +1686,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, - {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, + {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, }; @@ -1672,6 +1697,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT}, {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, + {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, }; /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index 27e33a287dfc..71474d31e155 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -459,7 +459,6 @@ static struct i2c_algorithm em28xx_algo = { static struct i2c_adapter em28xx_adap_template = { .owner = THIS_MODULE, .name = "em28xx", - .id = I2C_HW_B_EM28XX, .algo = &em28xx_algo, }; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index ab079d9256c4..a6bdbc21410e 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -124,7 +124,7 @@ static struct em28xx_fmt format[] = { /* supported controls */ /* Common to all boards */ -static struct v4l2_queryctrl em28xx_qctrl[] = { +static struct v4l2_queryctrl ac97_qctrl[] = { { .id = V4L2_CID_AUDIO_VOLUME, .type = V4L2_CTRL_TYPE_INTEGER, @@ -133,7 +133,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { .maximum = 0x1f, .step = 0x1, .default_value = 0x1f, - .flags = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_AUDIO_MUTE, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -609,10 +609,29 @@ static void res_free(struct em28xx_fh *fh) } /* - * em28xx_get_ctrl() - * return the current saturation, brightness or contrast, mute state + * ac97_queryctrl() + * return the ac97 supported controls */ -static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) +static int ac97_queryctrl(struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (qc->id && qc->id == ac97_qctrl[i].id) { + memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); + return 0; + } + } + + /* Control is not ac97 related */ + return 1; +} + +/* + * ac97_get_ctrl() + * return the current values for ac97 mute and volume + */ +static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) { switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -622,29 +641,41 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) ctrl->value = dev->volume; return 0; default: - return -EINVAL; + /* Control is not ac97 related */ + return 1; } } /* - * em28xx_set_ctrl() - * mute or set new saturation, brightness or contrast + * ac97_set_ctrl() + * set values for ac97 mute and volume */ -static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) +static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) { + int i; + + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) + if (ctrl->id == ac97_qctrl[i].id) + goto handle; + + /* Announce that hasn't handle it */ + return 1; + +handle: + if (ctrl->value < ac97_qctrl[i].minimum || + ctrl->value > ac97_qctrl[i].maximum) + return -ERANGE; + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - if (ctrl->value != dev->mute) { - dev->mute = ctrl->value; - return em28xx_audio_analog_set(dev); - } - return 0; + dev->mute = ctrl->value; + break; case V4L2_CID_AUDIO_VOLUME: dev->volume = ctrl->value; - return em28xx_audio_analog_set(dev); - default: - return -EINVAL; + break; } + + return em28xx_audio_analog_set(dev); } static int check_dev(struct em28xx *dev) @@ -974,6 +1005,9 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + if (!dev->audio_mode.has_audio) + return -EINVAL; + switch (a->index) { case EM28XX_AMUX_VIDEO: strcpy(a->name, "Television"); @@ -1015,6 +1049,9 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) struct em28xx *dev = fh->dev; + if (!dev->audio_mode.has_audio) + return -EINVAL; + if (a->index >= MAX_EM28XX_INPUT) return -EINVAL; if (0 == INPUT(a->index)->type) @@ -1038,7 +1075,6 @@ static int vidioc_queryctrl(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; int id = qc->id; - int i; int rc; rc = check_dev(dev); @@ -1049,15 +1085,14 @@ static int vidioc_queryctrl(struct file *file, void *priv, qc->id = id; - if (!dev->board.has_msp34xx) { - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (qc->id && qc->id == em28xx_qctrl[i].id) { - memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); - return 0; - } - } + /* enumberate AC97 controls */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + rc = ac97_queryctrl(qc); + if (!rc) + return 0; } + /* enumberate V4L2 device controls */ mutex_lock(&dev->lock); v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); mutex_unlock(&dev->lock); @@ -1082,14 +1117,16 @@ static int vidioc_g_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); - if (dev->board.has_msp34xx) + /* Set an AC97 control */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) + rc = ac97_get_ctrl(dev, ctrl); + else + rc = 1; + + /* It were not an AC97 control. Sends it to the v4l2 dev interface */ + if (rc == 1) { v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - else { - rc = em28xx_get_ctrl(dev, ctrl); - if (rc < 0) { - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - rc = 0; - } + rc = 0; } mutex_unlock(&dev->lock); @@ -1101,7 +1138,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - u8 i; int rc; rc = check_dev(dev); @@ -1110,28 +1146,31 @@ static int vidioc_s_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); - if (dev->board.has_msp34xx) - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); - else { + /* Set an AC97 control */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) + rc = ac97_set_ctrl(dev, ctrl); + else rc = 1; - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (ctrl->id == em28xx_qctrl[i].id) { - if (ctrl->value < em28xx_qctrl[i].minimum || - ctrl->value > em28xx_qctrl[i].maximum) { - rc = -ERANGE; - break; - } - - rc = em28xx_set_ctrl(dev, ctrl); - break; - } - } - } - /* Control not found - try to send it to the attached devices */ + /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ if (rc == 1) { v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); - rc = 0; + + /* + * In the case of non-AC97 volume controls, we still need + * to do some setups at em28xx, in order to mute/unmute + * and to adjust audio volume. However, the value ranges + * should be checked by the corresponding V4L subdriver. + */ + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + dev->mute = ctrl->value; + rc = em28xx_audio_analog_set(dev); + break; + case V4L2_CID_AUDIO_VOLUME: + dev->volume = ctrl->value; + rc = em28xx_audio_analog_set(dev); + } } mutex_unlock(&dev->lock); @@ -1275,8 +1314,9 @@ static int vidioc_g_register(struct file *file, void *priv, v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); return 0; case V4L2_CHIP_MATCH_I2C_ADDR: - /* Not supported yet */ - return -EINVAL; + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); + return 0; default: if (!v4l2_chip_match_host(®->match)) return -EINVAL; @@ -1327,8 +1367,9 @@ static int vidioc_s_register(struct file *file, void *priv, v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); return 0; case V4L2_CHIP_MATCH_I2C_ADDR: - /* Not supported yet */ - return -EINVAL; + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); + return 0; default: if (!v4l2_chip_match_host(®->match)) return -EINVAL; @@ -1431,9 +1472,11 @@ static int vidioc_querycap(struct file *file, void *priv, cap->capabilities = V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (dev->audio_mode.has_audio) + cap->capabilities |= V4L2_CAP_AUDIO; + if (dev->tuner_type != TUNER_ABSENT) cap->capabilities |= V4L2_CAP_TUNER; @@ -1654,9 +1697,9 @@ static int radio_queryctrl(struct file *file, void *priv, qc->id >= V4L2_CID_LASTP1) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (qc->id && qc->id == em28xx_qctrl[i].id) { - memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (qc->id && qc->id == ac97_qctrl[i].id) { + memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); return 0; } } diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index a2add61f7d59..23f34dd691e9 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -108,6 +108,7 @@ #define EM2882_BOARD_KWORLD_ATSC_315U 69 #define EM2882_BOARD_EVGA_INDTUBE 70 #define EM2820_BOARD_SILVERCREST_WEBCAM 71 +#define EM2861_BOARD_GADMEI_UTV330PLUS 72 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index e994dcac43ff..8897283b0bb4 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -47,6 +47,15 @@ config USB_GSPCA_FINEPIX To compile this driver as a module, choose M here: the module will be called gspca_finepix. +config USB_GSPCA_JEILINJ + tristate "Jeilin JPEG USB V4L2 driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on this Jeilin chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_jeilinj. + config USB_GSPCA_MARS tristate "Mars USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -103,9 +112,9 @@ config USB_GSPCA_PAC7311 module will be called gspca_pac7311. config USB_GSPCA_SN9C20X - tristate "SN9C20X USB Camera Driver" - depends on VIDEO_V4L2 && USB_GSPCA - help + tristate "SN9C20X USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help Say Y here if you want support for cameras based on the sn9c20x chips (SN9C201 and SN9C202). @@ -113,10 +122,10 @@ config USB_GSPCA_SN9C20X module will be called gspca_sn9c20x. config USB_GSPCA_SN9C20X_EVDEV - bool "Enable evdev support" + bool "Enable evdev support" depends on USB_GSPCA_SN9C20X && INPUT - ---help--- - Say Y here in order to enable evdev support for sn9c20x webcam button. + ---help--- + Say Y here in order to enable evdev support for sn9c20x webcam button. config USB_GSPCA_SONIXB tristate "SONIX Bayer USB Camera Driver" diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index f6d3b86e9ad5..035616b5e867 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_USB_GSPCA) += gspca_main.o obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o +obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o @@ -30,6 +31,7 @@ gspca_main-objs := gspca.o gspca_conex-objs := conex.o gspca_etoms-objs := etoms.o gspca_finepix-objs := finepix.o +gspca_jeilinj-objs := jeilinj.o gspca_mars-objs := mars.o gspca_mr97310a-objs := mr97310a.o gspca_ov519-objs := ov519.o diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index 8d48ea1742c2..eca003566ae3 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -820,7 +820,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c index 2c20d06a03e8..c1461e63647f 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -635,10 +635,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = id->driver_info; if (sd->sensor == SENSOR_PAS106) { cam->cam_mode = sif_mode; - cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + cam->nmodes = ARRAY_SIZE(sif_mode); } else { cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); gspca_dev->ctrl_dis = (1 << COLOR_IDX); } sd->brightness = BRIGHTNESS_DEF; diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index b8561dfb6c8c..cf6540da1e42 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -47,7 +47,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 6, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 7, 0) #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; @@ -486,6 +486,7 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) } PDEBUG(D_STREAM, "use alt %d ep 0x%02x", i, ep->desc.bEndpointAddress); + gspca_dev->alt = i; /* memorize the current alt setting */ if (gspca_dev->nbalt > 1) { ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); if (ret < 0) { @@ -493,7 +494,6 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) return NULL; } } - gspca_dev->alt = i; /* memorize the current alt setting */ return ep; } @@ -512,7 +512,10 @@ static int create_urbs(struct gspca_dev *gspca_dev, if (!gspca_dev->cam.bulk) { /* isoc */ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ - psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + if (gspca_dev->pkt_size == 0) + psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); + else + psize = gspca_dev->pkt_size; npkt = gspca_dev->cam.npkt; if (npkt == 0) npkt = 32; /* default value */ @@ -597,13 +600,18 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) /* set the higher alternate setting and * loop until urb submit succeeds */ gspca_dev->alt = gspca_dev->nbalt; + if (gspca_dev->sd_desc->isoc_init) { + ret = gspca_dev->sd_desc->isoc_init(gspca_dev); + if (ret < 0) + goto out; + } + ep = get_ep(gspca_dev); + if (ep == NULL) { + ret = -EIO; + goto out; + } for (;;) { PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt); - ep = get_ep(gspca_dev); - if (ep == NULL) { - ret = -EIO; - goto out; - } ret = create_urbs(gspca_dev, ep); if (ret < 0) goto out; @@ -628,21 +636,32 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) /* submit the URBs */ for (n = 0; n < gspca_dev->nurbs; n++) { ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL); - if (ret < 0) { - PDEBUG(D_ERR|D_STREAM, - "usb_submit_urb [%d] err %d", n, ret); - gspca_dev->streaming = 0; - destroy_urbs(gspca_dev); - if (ret == -ENOSPC) { - msleep(20); /* wait for kill - * complete */ - break; /* try the previous alt */ - } - goto out; - } + if (ret < 0) + break; } if (ret >= 0) break; + PDEBUG(D_ERR|D_STREAM, + "usb_submit_urb alt %d err %d", gspca_dev->alt, ret); + gspca_dev->streaming = 0; + destroy_urbs(gspca_dev); + if (ret != -ENOSPC) + goto out; + + /* the bandwidth is not wide enough + * negociate or try a lower alternate setting */ + msleep(20); /* wait for kill complete */ + if (gspca_dev->sd_desc->isoc_nego) { + ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); + if (ret < 0) + goto out; + } else { + ep = get_ep(gspca_dev); + if (ep == NULL) { + ret = -EIO; + goto out; + } + } } out: mutex_unlock(&gspca_dev->usb_lock); @@ -1473,12 +1492,6 @@ static int vidioc_s_parm(struct file *filp, void *priv, return 0; } -static int vidioc_s_std(struct file *filp, void *priv, - v4l2_std_id *parm) -{ - return 0; -} - #ifdef CONFIG_VIDEO_V4L1_COMPAT static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) @@ -1949,7 +1962,6 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_s_jpegcomp = vidioc_s_jpegcomp, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, - .vidioc_s_std = vidioc_s_std, .vidioc_enum_framesizes = vidioc_enum_framesizes, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = vidioc_g_register, diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 46c4effdfcd5..70b1fd830876 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -98,9 +98,11 @@ struct sd_desc { /* mandatory operations */ cam_cf_op config; /* called on probe */ cam_op init; /* called on probe and resume */ - cam_op start; /* called on stream on */ + cam_op start; /* called on stream on after URBs creation */ cam_pkt_op pkt_scan; /* optional operations */ + cam_op isoc_init; /* called on stream on before getting the EP */ + cam_op isoc_nego; /* called when URB submit failed with NOSPC */ cam_v_op stopN; /* called on stream off - main alt */ cam_v_op stop0; /* called on stream off & disconnect - alt 0 */ cam_v_op dq_callback; /* called when a frame has been dequeued */ @@ -178,6 +180,7 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ __u8 nbalt; /* number of USB alternate settings */ + u16 pkt_size; /* ISOC packet size */ }; int gspca_dev_probe(struct usb_interface *intf, diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c new file mode 100644 index 000000000000..dbfa3ed6e8ef --- /dev/null +++ b/drivers/media/video/gspca/jeilinj.c @@ -0,0 +1,388 @@ +/* + * Jeilinj subdriver + * + * Supports some Jeilin dual-mode cameras which use bulk transport and + * download raw JPEG data. + * + * Copyright (C) 2009 Theodore Kilgore + * + * 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 + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MODULE_NAME "jeilinj" + +#include <linux/workqueue.h> +#include "gspca.h" +#include "jpeg.h" + +MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>"); +MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* Default timeouts, in ms */ +#define JEILINJ_CMD_TIMEOUT 500 +#define JEILINJ_DATA_TIMEOUT 1000 + +/* Maximum transfer size to use. */ +#define JEILINJ_MAX_TRANSFER 0x200 + +#define FRAME_HEADER_LEN 0x10 + +/* Structure to hold all of our device specific stuff */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + const struct v4l2_pix_format *cap_mode; + /* Driver stuff */ + struct work_struct work_struct; + struct workqueue_struct *work_thread; + u8 quality; /* image quality */ + u8 jpegqual; /* webcam quality */ + u8 *jpeg_hdr; +}; + + struct jlj_command { + unsigned char instruction[2]; + unsigned char ack_wanted; + }; + +/* AFAICT these cameras will only do 320x240. */ +static struct v4l2_pix_format jlj_mode[] = { + { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0} +}; + +/* + * cam uses endpoint 0x03 to send commands, 0x84 for read commands, + * and 0x82 for bulk transfer. + */ + +/* All commands are two bytes only */ +static int jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command) +{ + int retval; + + memcpy(gspca_dev->usb_buf, command, 2); + retval = usb_bulk_msg(gspca_dev->dev, + usb_sndbulkpipe(gspca_dev->dev, 3), + gspca_dev->usb_buf, 2, NULL, 500); + if (retval < 0) + PDEBUG(D_ERR, "command write [%02x] error %d", + gspca_dev->usb_buf[0], retval); + return retval; +} + +/* Responses are one byte only */ +static int jlj_read1(struct gspca_dev *gspca_dev, unsigned char response) +{ + int retval; + + retval = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x84), + gspca_dev->usb_buf, 1, NULL, 500); + response = gspca_dev->usb_buf[0]; + if (retval < 0) + PDEBUG(D_ERR, "read command [%02x] error %d", + gspca_dev->usb_buf[0], retval); + return retval; +} + +static int jlj_start(struct gspca_dev *gspca_dev) +{ + int i; + int retval = -1; + u8 response = 0xff; + struct jlj_command start_commands[] = { + {{0x71, 0x81}, 0}, + {{0x70, 0x05}, 0}, + {{0x95, 0x70}, 1}, + {{0x71, 0x81}, 0}, + {{0x70, 0x04}, 0}, + {{0x95, 0x70}, 1}, + {{0x71, 0x00}, 0}, + {{0x70, 0x08}, 0}, + {{0x95, 0x70}, 1}, + {{0x94, 0x02}, 0}, + {{0xde, 0x24}, 0}, + {{0x94, 0x02}, 0}, + {{0xdd, 0xf0}, 0}, + {{0x94, 0x02}, 0}, + {{0xe3, 0x2c}, 0}, + {{0x94, 0x02}, 0}, + {{0xe4, 0x00}, 0}, + {{0x94, 0x02}, 0}, + {{0xe5, 0x00}, 0}, + {{0x94, 0x02}, 0}, + {{0xe6, 0x2c}, 0}, + {{0x94, 0x03}, 0}, + {{0xaa, 0x00}, 0}, + {{0x71, 0x1e}, 0}, + {{0x70, 0x06}, 0}, + {{0x71, 0x80}, 0}, + {{0x70, 0x07}, 0} + }; + for (i = 0; i < ARRAY_SIZE(start_commands); i++) { + retval = jlj_write2(gspca_dev, start_commands[i].instruction); + if (retval < 0) + return retval; + if (start_commands[i].ack_wanted) + retval = jlj_read1(gspca_dev, response); + if (retval < 0) + return retval; + } + PDEBUG(D_ERR, "jlj_start retval is %d", retval); + return retval; +} + +static int jlj_stop(struct gspca_dev *gspca_dev) +{ + int i; + int retval; + struct jlj_command stop_commands[] = { + {{0x71, 0x00}, 0}, + {{0x70, 0x09}, 0}, + {{0x71, 0x80}, 0}, + {{0x70, 0x05}, 0} + }; + for (i = 0; i < ARRAY_SIZE(stop_commands); i++) { + retval = jlj_write2(gspca_dev, stop_commands[i].instruction); + if (retval < 0) + return retval; + } + return retval; +} + +/* This function is called as a workqueue function and runs whenever the camera + * is streaming data. Because it is a workqueue function it is allowed to sleep + * so we can use synchronous USB calls. To avoid possible collisions with other + * threads attempting to use the camera's USB interface the gspca usb_lock is + * used when performing the one USB control operation inside the workqueue, + * which tells the camera to close the stream. In practice the only thing + * which needs to be protected against is the usb_set_interface call that + * gspca makes during stream_off. Otherwise the camera doesn't provide any + * controls that the user could try to change. + */ + +static void jlj_dostream(struct work_struct *work) +{ + struct sd *dev = container_of(work, struct sd, work_struct); + struct gspca_dev *gspca_dev = &dev->gspca_dev; + struct gspca_frame *frame; + int blocks_left; /* 0x200-sized blocks remaining in current frame. */ + int size_in_blocks; + int act_len; + int discarding = 0; /* true if we failed to get space for frame. */ + int packet_type; + int ret; + u8 *buffer; + + buffer = kmalloc(JEILINJ_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); + if (!buffer) { + PDEBUG(D_ERR, "Couldn't allocate USB buffer"); + goto quit_stream; + } + while (gspca_dev->present && gspca_dev->streaming) { + if (!gspca_dev->present) + goto quit_stream; + /* Start a new frame, and add the JPEG header, first thing */ + frame = gspca_get_i_frame(gspca_dev); + if (frame && !discarding) + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + dev->jpeg_hdr, JPEG_HDR_SZ); + else + discarding = 1; + /* + * Now request data block 0. Line 0 reports the size + * to download, in blocks of size 0x200, and also tells the + * "actual" data size, in bytes, which seems best to ignore. + */ + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x82), + buffer, JEILINJ_MAX_TRANSFER, &act_len, + JEILINJ_DATA_TIMEOUT); + PDEBUG(D_STREAM, + "Got %d bytes out of %d for Block 0", + act_len, JEILINJ_MAX_TRANSFER); + if (ret < 0 || act_len < FRAME_HEADER_LEN) + goto quit_stream; + size_in_blocks = buffer[0x0a]; + blocks_left = buffer[0x0a] - 1; + PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left); + packet_type = INTER_PACKET; + if (frame && !discarding) + /* Toss line 0 of data block 0, keep the rest. */ + gspca_frame_add(gspca_dev, packet_type, + frame, buffer + FRAME_HEADER_LEN, + JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN); + else + discarding = 1; + while (blocks_left > 0) { + if (!gspca_dev->present) + goto quit_stream; + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x82), + buffer, JEILINJ_MAX_TRANSFER, &act_len, + JEILINJ_DATA_TIMEOUT); + if (ret < 0 || act_len < JEILINJ_MAX_TRANSFER) + goto quit_stream; + PDEBUG(D_STREAM, + "%d blocks remaining for frame", blocks_left); + blocks_left -= 1; + if (blocks_left == 0) + packet_type = LAST_PACKET; + else + packet_type = INTER_PACKET; + if (frame && !discarding) + gspca_frame_add(gspca_dev, packet_type, + frame, buffer, + JEILINJ_MAX_TRANSFER); + else + discarding = 1; + } + } +quit_stream: + mutex_lock(&gspca_dev->usb_lock); + if (gspca_dev->present) + jlj_stop(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + kfree(buffer); +} + +/* This function is called at probe time just before sd_init */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam = &gspca_dev->cam; + struct sd *dev = (struct sd *) gspca_dev; + + dev->quality = 85; + dev->jpegqual = 85; + PDEBUG(D_PROBE, + "JEILINJ camera detected" + " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + cam->cam_mode = jlj_mode; + cam->nmodes = 1; + cam->bulk = 1; + /* We don't use the buffer gspca allocates so make it small. */ + cam->bulk_size = 32; + INIT_WORK(&dev->work_struct, jlj_dostream); + return 0; +} + +/* called on streamoff with alt==0 and on disconnect */ +/* the usb_lock is held at entry - restore on exit */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + + /* wait for the work queue to terminate */ + mutex_unlock(&gspca_dev->usb_lock); + /* This waits for jlj_dostream to finish */ + destroy_workqueue(dev->work_thread); + dev->work_thread = NULL; + mutex_lock(&gspca_dev->usb_lock); + kfree(dev->jpeg_hdr); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* Set up for getting frames. */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + int ret; + + /* create the JPEG header */ + dev->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(dev->jpeg_hdr, dev->quality); + PDEBUG(D_STREAM, "Start streaming at 320x240"); + ret = jlj_start(gspca_dev); + if (ret < 0) { + PDEBUG(D_ERR, "Start streaming command failed"); + return ret; + } + /* Start the workqueue function to do the streaming */ + dev->work_thread = create_singlethread_workqueue(MODULE_NAME); + queue_work(dev->work_thread, &dev->work_struct); + + return 0; +} + +/* Table of supported USB devices */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0979, 0x0280)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stop0 = sd_stop0, +}; + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + &sd_desc, + sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + PDEBUG(D_PROBE, "registered"); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c index 7127321ace8c..6b89f33a4ce0 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -178,8 +178,10 @@ sensor_found: sens_priv->settings = kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL); - if (!sens_priv->settings) + if (!sens_priv->settings) { + kfree(sens_priv); return -ENOMEM; + } sd->gspca_dev.cam.cam_mode = s5k83a_modes; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes); diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index e1e3a3a50484..0caf3c075730 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -1068,6 +1068,7 @@ static __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x2629), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302}, {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302}, {} diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index fcfbbd329b4c..52a7f8edf7ac 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -1096,12 +1096,12 @@ int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer) reg_r(gspca_dev, 0x10c0, 1); if (gspca_dev->usb_buf[0] & 0x04) { if (gspca_dev->usb_buf[0] & 0x08) - return -1; + return -EIO; return 0; } msleep(1); } - return -1; + return -EIO; } int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) @@ -1152,7 +1152,7 @@ int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | 0x10; + row[0] = 0x81 | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1160,14 +1160,15 @@ int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) row[5] = 0; row[6] = 0; row[7] = 0x10; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - row[0] = 0x81 | (2 << 4) | 0x02; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + row[0] = 0x81 | (1 << 4) | 0x02; row[2] = 0; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - reg_r(gspca_dev, 0x10c2, 5); - *val = gspca_dev->usb_buf[3]; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + if (reg_r(gspca_dev, 0x10c2, 5) < 0) + return -EIO; + *val = gspca_dev->usb_buf[4]; return 0; } @@ -1176,7 +1177,7 @@ int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | 0x10; + row[0] = 0x81 | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1184,14 +1185,15 @@ int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) row[5] = 0; row[6] = 0; row[7] = 0x10; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - row[0] = 0x81 | (3 << 4) | 0x02; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + row[0] = 0x81 | (2 << 4) | 0x02; row[2] = 0; - reg_w(gspca_dev, 0x10c0, row, 8); - msleep(1); - reg_r(gspca_dev, 0x10c2, 5); - *val = (gspca_dev->usb_buf[2] << 8) | gspca_dev->usb_buf[3]; + if (i2c_w(gspca_dev, row) < 0) + return -EIO; + if (reg_r(gspca_dev, 0x10c2, 5) < 0) + return -EIO; + *val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; return 0; } diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index d6332ab80669..f0b762f770fb 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -727,13 +727,13 @@ static const u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, /* Outformat = rawRGB */ {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ - {0xd1, 0x21, 0x00, 0x01, 0x74, 0x74, 0x00, 0x10}, + {0xd1, 0x21, 0x00, 0x01, 0x74, 0x92, 0x00, 0x10}, /* GAIN BLUE RED VREF */ {0xd1, 0x21, 0x04, 0x00, 0x7d, 0x62, 0x00, 0x10}, /* COM 1 BAVE GEAVE AECHH */ {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */ {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */ - {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10}, + {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xf8, 0x10}, /* AECH CLKRC COM7 COM8 */ {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */ {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10}, @@ -1637,16 +1637,19 @@ static void setfreq(struct gspca_dev *gspca_dev) if (gspca_dev->ctrl_dis & (1 << FREQ_IDX)) return; if (sd->sensor == SENSOR_OV7660) { + u8 com8; + + com8 = 0xf8; /* no auto gain/wb/expo */ switch (sd->freq) { case 0: /* Banding filter disabled */ - i2c_w1(gspca_dev, 0x13, 0xdf); + i2c_w1(gspca_dev, 0x13, com8 & 0xdf); break; case 1: /* 50 hz */ - i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x13, com8); i2c_w1(gspca_dev, 0x3b, 0x0a); break; case 2: /* 60 hz */ - i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x13, com8); i2c_w1(gspca_dev, 0x3b, 0x02); break; } @@ -2336,7 +2339,8 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)}, #endif {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/ -/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */ +/* {USB_DEVICE(0x0c45, 0x6102), BSI(SN9C120, PO2030N, ??)}, */ +/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6802, 0x21)}, */ {USB_DEVICE(0x0c45, 0x610a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c128*/ {USB_DEVICE(0x0c45, 0x610b), BSI(SN9C120, OV7660, 0x21)}, /*sn9c128*/ {USB_DEVICE(0x0c45, 0x610c), BSI(SN9C120, HV7131R, 0x11)}, /*sn9c128*/ @@ -2352,6 +2356,7 @@ static const __devinitdata struct usb_device_id device_table[] = { #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)}, #endif +/* {USB_DEVICE(0x0c45, 0x6132), BSI(SN9C120, OV7670, 0x21)}, */ {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)}, {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE @@ -2359,7 +2364,9 @@ static const __devinitdata struct usb_device_id device_table[] = { #endif {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)}, - {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, +/* {USB_DEVICE(0x0c45, 0x6142), BSI(SN9C120, PO2030N, ??)}, *sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6148), BSI(SN9C120, OM6802, 0x21)}, /*sn9c120b*/ {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index d48b27c648ca..b74a34218da0 100644 --- a/drivers/media/video/gspca/spca501.c +++ b/drivers/media/video/gspca/spca501.c @@ -1923,7 +1923,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); sd->subtype = id->driver_info; sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value; diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c index 3a0c893f942d..a199298a6419 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -286,7 +286,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index 2ed2669bac3e..9696c4caf5c9 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -1304,19 +1304,70 @@ static int reg_read(struct gspca_dev *gspca_dev, return gspca_dev->usb_buf[0]; } +/* send 1 or 2 bytes to the sensor via the Synchronous Serial Interface */ +static int ssi_w(struct gspca_dev *gspca_dev, + u16 reg, u16 val) +{ + struct usb_device *dev = gspca_dev->dev; + int ret, retry; + + ret = reg_write(dev, 0x8802, reg >> 8); + if (ret < 0) + goto out; + ret = reg_write(dev, 0x8801, reg & 0x00ff); + if (ret < 0) + goto out; + if ((reg & 0xff00) == 0x1000) { /* if 2 bytes */ + ret = reg_write(dev, 0x8805, val & 0x00ff); + if (ret < 0) + goto out; + val >>= 8; + } + ret = reg_write(dev, 0x8800, val); + if (ret < 0) + goto out; + + /* poll until not busy */ + retry = 10; + for (;;) { + ret = reg_read(gspca_dev, 0x8803); + if (ret < 0) + break; + if (gspca_dev->usb_buf[0] == 0) + break; + if (--retry <= 0) { + PDEBUG(D_ERR, "ssi_w busy %02x", + gspca_dev->usb_buf[0]); + ret = -1; + break; + } + msleep(8); + } + +out: + return ret; +} + static int write_vector(struct gspca_dev *gspca_dev, const u16 (*data)[2]) { struct usb_device *dev = gspca_dev->dev; - int ret; + int ret = 0; while ((*data)[1] != 0) { - ret = reg_write(dev, (*data)[1], (*data)[0]); + if ((*data)[1] & 0x8000) { + if ((*data)[1] == 0xdd00) /* delay */ + msleep((*data)[0]); + else + ret = reg_write(dev, (*data)[1], (*data)[0]); + } else { + ret = ssi_w(gspca_dev, (*data)[1], (*data)[0]); + } if (ret < 0) - return ret; + break; data++; } - return 0; + return ret; } /* this function is called at probe time */ diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 0da8e0de0456..7af511b5e9c2 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -130,8 +130,8 @@ int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len) STV06XX_URB_MSG_TIMEOUT); if (err < 0) return err; - } - return stv06xx_write_sensor_finish(sd); + } + return stv06xx_write_sensor_finish(sd); } int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len) diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 5127bbf9dd26..e9481fa77749 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -52,6 +52,7 @@ struct sd { #define LogitechClickSmart420 2 #define LogitechClickSmart820 3 #define MegapixV4 4 +#define MegaImageVI 5 u8 *jpeg_hdr; }; @@ -840,15 +841,18 @@ static int sd_config(struct gspca_dev *gspca_dev, /* case BRIDGE_SPCA504: */ /* case BRIDGE_SPCA536: */ cam->cam_mode = vga_mode; - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes =ARRAY_SIZE(vga_mode); break; case BRIDGE_SPCA533: cam->cam_mode = custom_mode; - cam->nmodes = sizeof custom_mode / sizeof custom_mode[0]; + if (sd->subtype == MegaImageVI) /* 320x240 only */ + cam->nmodes = ARRAY_SIZE(custom_mode) - 1; + else + cam->nmodes = ARRAY_SIZE(custom_mode); break; case BRIDGE_SPCA504C: cam->cam_mode = vga_mode2; - cam->nmodes = sizeof vga_mode2 / sizeof vga_mode2[0]; + cam->nmodes = ARRAY_SIZE(vga_mode2); break; } sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; @@ -988,7 +992,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA533: */ /* case BRIDGE_SPCA536: */ if (sd->subtype == MegapixV4 || - sd->subtype == LogitechClickSmart820) { + sd->subtype == LogitechClickSmart820 || + sd->subtype == MegaImageVI) { reg_w(gspca_dev, 0xf0, 0, 0, 0); spca504B_WaitCmdStatus(gspca_dev); reg_r(gspca_dev, 0xf0, 4, 0); @@ -1384,6 +1389,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)}, {USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)}, {USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)}, + {USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)}, {USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)}, {USB_DEVICE(0x0546, 0x3191), BS(SPCA504B, 0)}, {USB_DEVICE(0x0546, 0x3273), BS(SPCA504B, 0)}, diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 404214b8cd2b..1d321c30d22f 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -264,6 +264,10 @@ static const struct v4l2_pix_format vga_mode_t16[] = { /* sensor specific data */ struct additional_sensor_data { + const u8 n3[6]; + const u8 *n4, n4sz; + const u8 reg80, reg8e; + const u8 nset8[6]; const u8 data1[10]; const u8 data2[9]; const u8 data3[9]; @@ -272,14 +276,55 @@ struct additional_sensor_data { const u8 stream[4]; }; +static const u8 n4_om6802[] = { + 0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c, + 0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68, + 0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1, + 0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8, + 0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48, + 0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0, + 0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68, + 0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40, + 0xac, 0x84, 0xad, 0x86, 0xaf, 0x46 +}; +static const u8 n4_other[] = { + 0x66, 0x00, 0x7f, 0x00, 0x80, 0xac, 0x81, 0x69, + 0x84, 0x40, 0x85, 0x70, 0x86, 0x20, 0x8a, 0x68, + 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xff, 0x8e, 0xb8, + 0x8f, 0x28, 0xa2, 0x60, 0xa5, 0x40, 0xa8, 0xa8, + 0xac, 0x84, 0xad, 0x84, 0xae, 0x24, 0xaf, 0x56, + 0xb0, 0x68, 0xb1, 0x00, 0xb2, 0x88, 0xbb, 0xc5, + 0xbc, 0x4a, 0xbe, 0x36, 0xc2, 0x88, 0xc5, 0xc0, + 0xc6, 0xda, 0xe9, 0x26, 0xeb, 0x00 +}; +static const u8 n4_tas5130a[] = { + 0x80, 0x3c, 0x81, 0x68, 0x83, 0xa0, 0x84, 0x20, + 0x8a, 0x68, 0x8b, 0x58, 0x8c, 0x88, 0x8e, 0xb4, + 0x8f, 0x24, 0xa1, 0xb1, 0xa2, 0x30, 0xa5, 0x10, + 0xa6, 0x4a, 0xae, 0x03, 0xb1, 0x44, 0xb2, 0x08, + 0xb7, 0x06, 0xb9, 0xe7, 0xbb, 0xc4, 0xbc, 0x4a, + 0xbe, 0x36, 0xbf, 0xff, 0xc2, 0x88, 0xc5, 0xc8, + 0xc6, 0xda +}; + static const struct additional_sensor_data sensor_data[] = { - { /* OM6802 */ + { /* 0: OM6802 */ + .n3 = + {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}, + .n4 = n4_om6802, + .n4sz = sizeof n4_om6802, + .reg80 = 0x3c, + .reg8e = 0x33, + .nset8 = {0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00}, .data1 = {0xc2, 0x28, 0x0f, 0x22, 0xcd, 0x27, 0x2c, 0x06, 0xb3, 0xfc}, .data2 = {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff, 0xff}, + .data3 = + {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff, + 0xff}, .data4 = /*Freq (50/60Hz). Splitted for test purpose */ {0x66, 0xca, 0xa8, 0xf0}, .data5 = /* this could be removed later */ @@ -287,13 +332,23 @@ static const struct additional_sensor_data sensor_data[] = { .stream = {0x0b, 0x04, 0x0a, 0x78}, }, - { /* OTHER */ + { /* 1: OTHER */ + .n3 = + {0x61, 0xc2, 0x65, 0x88, 0x60, 0x00}, + .n4 = n4_other, + .n4sz = sizeof n4_other, + .reg80 = 0xac, + .reg8e = 0xb8, + .nset8 = {0xa8, 0xa8, 0xc6, 0xda, 0xc0, 0x00}, .data1 = {0xc1, 0x48, 0x04, 0x1b, 0xca, 0x2e, 0x33, 0x3a, 0xe8, 0xfc}, .data2 = {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96, 0xd9}, + .data3 = + {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96, + 0xd9}, .data4 = {0x66, 0x00, 0xa8, 0xa8}, .data5 = @@ -301,13 +356,23 @@ static const struct additional_sensor_data sensor_data[] = { .stream = {0x0b, 0x04, 0x0a, 0x00}, }, - { /* TAS5130A */ + { /* 2: TAS5130A */ + .n3 = + {0x61, 0xc2, 0x65, 0x0d, 0x60, 0x08}, + .n4 = n4_tas5130a, + .n4sz = sizeof n4_tas5130a, + .reg80 = 0x3c, + .reg8e = 0xb4, + .nset8 = {0xa8, 0xf0, 0xc6, 0xda, 0xc0, 0x00}, .data1 = {0xbb, 0x28, 0x10, 0x10, 0xbb, 0x28, 0x1e, 0x27, 0xc8, 0xfc}, .data2 = {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0}, + .data3 = + {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8, + 0xe0}, .data4 = /* Freq (50/60Hz). Splitted for test purpose */ {0x66, 0x00, 0xa8, 0xe8}, .data5 = @@ -364,7 +429,7 @@ static const u8 gamma_table[GAMMA_MAX][17] = { {0x00, 0x18, 0x2b, 0x44, 0x60, 0x70, 0x80, 0x8e, /* 10 */ 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xd8, 0xe2, 0xf0, 0xff}, - {0x00, 0x1a, 0x34, 0x52, 0x66, 0x7e, 0x8D, 0x9B, /* 11 */ + {0x00, 0x1a, 0x34, 0x52, 0x66, 0x7e, 0x8d, 0x9b, /* 11 */ 0xa8, 0xb4, 0xc0, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5, 0xff}, {0x00, 0x3f, 0x5a, 0x6e, 0x7f, 0x8e, 0x9c, 0xa8, /* 12 */ @@ -385,8 +450,6 @@ static const u8 tas5130a_sensor_init[][8] = { {0x62, 0x08, 0x63, 0x70, 0x64, 0x1d, 0x60, 0x09}, {0x62, 0x20, 0x63, 0x01, 0x64, 0x02, 0x60, 0x09}, {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, - {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}, - {}, }; static u8 sensor_reset[] = {0x61, 0x68, 0x62, 0xff, 0x60, 0x07}; @@ -633,10 +696,10 @@ static int sd_init(struct gspca_dev *gspca_dev) * but wont hurt anyway, and can help someone with similar webcam * to see the initial parameters.*/ struct sd *sd = (struct sd *) gspca_dev; + const struct additional_sensor_data *sensor; int i; u16 sensor_id; u8 test_byte = 0; - u16 reg80, reg8e; static const u8 read_indexs[] = { 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5, @@ -645,37 +708,6 @@ static int sd_init(struct gspca_dev *gspca_dev) {0x08, 0x03, 0x09, 0x03, 0x12, 0x04}; static const u8 n2[] = {0x08, 0x00}; - static const u8 n3[6] = - {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}; - static const u8 n3_other[6] = - {0x61, 0xc2, 0x65, 0x88, 0x60, 0x00}; - static const u8 n4[] = - {0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c, - 0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68, - 0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1, - 0xa2, 0x60, 0xa5, 0x30, 0xa6, 0x3a, 0xa8, 0xe8, - 0xae, 0x05, 0xb1, 0x00, 0xbb, 0x04, 0xbc, 0x48, - 0xbe, 0x36, 0xc6, 0x88, 0xe9, 0x00, 0xc5, 0xc0, - 0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68, - 0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40, - 0xac, 0x84, 0xad, 0x86, 0xaf, 0x46}; - static const u8 n4_other[] = - {0x66, 0x00, 0x7f, 0x00, 0x80, 0xac, 0x81, 0x69, - 0x84, 0x40, 0x85, 0x70, 0x86, 0x20, 0x8a, 0x68, - 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xff, 0x8e, 0xb8, - 0x8f, 0x28, 0xa2, 0x60, 0xa5, 0x40, 0xa8, 0xa8, - 0xac, 0x84, 0xad, 0x84, 0xae, 0x24, 0xaf, 0x56, - 0xb0, 0x68, 0xb1, 0x00, 0xb2, 0x88, 0xbb, 0xc5, - 0xbc, 0x4a, 0xbe, 0x36, 0xc2, 0x88, 0xc5, 0xc0, - 0xc6, 0xda, 0xe9, 0x26, 0xeb, 0x00}; - static const u8 nset8[6] = - { 0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00 }; - static const u8 nset8_other[6] = - { 0xa8, 0xa8, 0xc6, 0xda, 0xc0, 0x00 }; - static const u8 nset9[4] = - { 0x0b, 0x04, 0x0a, 0x78 }; - static const u8 nset9_other[4] = - { 0x0b, 0x04, 0x0a, 0x00 }; sensor_id = (reg_r(gspca_dev, 0x06) << 8) | reg_r(gspca_dev, 0x07); @@ -709,8 +741,7 @@ static int sd_init(struct gspca_dev *gspca_dev) } if (i < 0) { err("Bad sensor reset %02x", test_byte); -/* return -EIO; */ -/*fixme: test - continue */ + return -EIO; } reg_w_buf(gspca_dev, n2, sizeof n2); } @@ -723,31 +754,17 @@ static int sd_init(struct gspca_dev *gspca_dev) i++; } - if (sd->sensor != SENSOR_OTHER) { - reg_w_buf(gspca_dev, n3, sizeof n3); - reg_w_buf(gspca_dev, n4, sizeof n4); - reg_r(gspca_dev, 0x0080); - reg_w(gspca_dev, 0x2c80); - reg80 = 0x3880; - reg8e = 0x338e; - } else { - reg_w_buf(gspca_dev, n3_other, sizeof n3_other); - reg_w_buf(gspca_dev, n4_other, sizeof n4_other); - sd->gamma = 5; - reg80 = 0xac80; - reg8e = 0xb88e; - } + sensor = &sensor_data[sd->sensor]; + reg_w_buf(gspca_dev, sensor->n3, sizeof sensor->n3); + reg_w_buf(gspca_dev, sensor->n4, sensor->n4sz); - reg_w_ixbuf(gspca_dev, 0xd0, sensor_data[sd->sensor].data1, - sizeof sensor_data[sd->sensor].data1); - reg_w_ixbuf(gspca_dev, 0xc7, sensor_data[sd->sensor].data2, - sizeof sensor_data[sd->sensor].data2); - reg_w_ixbuf(gspca_dev, 0xe0, sensor_data[sd->sensor].data2, - sizeof sensor_data[sd->sensor].data2); + reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1); + reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2); + reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3); - reg_w(gspca_dev, reg80); - reg_w(gspca_dev, reg80); - reg_w(gspca_dev, reg8e); + reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80); + reg_w(gspca_dev, (sensor->reg80 << 8) + 0x80); + reg_w(gspca_dev, (sensor->reg8e << 8) + 0x8e); setbrightness(gspca_dev); setcontrast(gspca_dev); @@ -760,25 +777,14 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x2088); reg_w(gspca_dev, 0x2089); - reg_w_buf(gspca_dev, sensor_data[sd->sensor].data4, - sizeof sensor_data[sd->sensor].data4); - reg_w_buf(gspca_dev, sensor_data[sd->sensor].data5, - sizeof sensor_data[sd->sensor].data5); - if (sd->sensor != SENSOR_OTHER) { - reg_w_buf(gspca_dev, nset8, sizeof nset8); - reg_w_buf(gspca_dev, nset9, sizeof nset9); - reg_w(gspca_dev, 0x2880); - } else { - reg_w_buf(gspca_dev, nset8_other, sizeof nset8_other); - reg_w_buf(gspca_dev, nset9_other, sizeof nset9_other); - } + reg_w_buf(gspca_dev, sensor->data4, sizeof sensor->data4); + reg_w_buf(gspca_dev, sensor->data5, sizeof sensor->data5); + reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8); + reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); - reg_w_ixbuf(gspca_dev, 0xd0, sensor_data[sd->sensor].data1, - sizeof sensor_data[sd->sensor].data1); - reg_w_ixbuf(gspca_dev, 0xc7, sensor_data[sd->sensor].data2, - sizeof sensor_data[sd->sensor].data2); - reg_w_ixbuf(gspca_dev, 0xe0, sensor_data[sd->sensor].data2, - sizeof sensor_data[sd->sensor].data2); + reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1); + reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2); + reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3); return 0; } @@ -828,7 +834,6 @@ static void setlightfreq(struct gspca_dev *gspca_dev) * i added some module parameters for test with some users */ static void poll_sensor(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; static const u8 poll1[] = {0x67, 0x05, 0x68, 0x81, 0x69, 0x80, 0x6a, 0x82, 0x6b, 0x68, 0x6c, 0x69, 0x72, 0xd9, 0x73, 0x34, @@ -844,24 +849,23 @@ static void poll_sensor(struct gspca_dev *gspca_dev) 0xa1, 0xb1, 0xda, 0x6b, 0xdb, 0x98, 0xdf, 0x0c, 0xc2, 0x80, 0xc3, 0x10}; - if (sd->sensor == SENSOR_OM6802) { - PDEBUG(D_STREAM, "[Sensor requires polling]"); - reg_w_buf(gspca_dev, poll1, sizeof poll1); - reg_w_buf(gspca_dev, poll2, sizeof poll2); - reg_w_buf(gspca_dev, poll3, sizeof poll3); - reg_w_buf(gspca_dev, poll4, sizeof poll4); - } + PDEBUG(D_STREAM, "[Sensor requires polling]"); + reg_w_buf(gspca_dev, poll1, sizeof poll1); + reg_w_buf(gspca_dev, poll2, sizeof poll2); + reg_w_buf(gspca_dev, poll3, sizeof poll3); + reg_w_buf(gspca_dev, poll4, sizeof poll4); } static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + const struct additional_sensor_data *sensor; int i, mode; u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 }; static const u8 t3[] = { 0x07, 0x00, 0x88, 0x02, 0x06, 0x00, 0xe7, 0x01 }; - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode]. priv; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; switch (mode) { case 0: /* 640x480 (0x00) */ break; @@ -889,34 +893,33 @@ static int sd_start(struct gspca_dev *gspca_dev) default: /* case SENSOR_TAS5130A: */ i = 0; - while (tas5130a_sensor_init[i][0] != 0) { + for (;;) { reg_w_buf(gspca_dev, tas5130a_sensor_init[i], sizeof tas5130a_sensor_init[0]); + if (i >= ARRAY_SIZE(tas5130a_sensor_init) - 1) + break; i++; } reg_w(gspca_dev, 0x3c80); /* just in case and to keep sync with logs (for mine) */ - reg_w_buf(gspca_dev, tas5130a_sensor_init[3], + reg_w_buf(gspca_dev, tas5130a_sensor_init[i], sizeof tas5130a_sensor_init[0]); reg_w(gspca_dev, 0x3c80); break; } - reg_w_buf(gspca_dev, sensor_data[sd->sensor].data4, - sizeof sensor_data[sd->sensor].data4); + sensor = &sensor_data[sd->sensor]; + reg_w_buf(gspca_dev, sensor->data4, sizeof sensor->data4); reg_r(gspca_dev, 0x0012); reg_w_buf(gspca_dev, t2, sizeof t2); reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3); reg_w(gspca_dev, 0x0013); msleep(15); - reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream, - sizeof sensor_data[sd->sensor].stream); - poll_sensor(gspca_dev); + reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); + reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); + + if (sd->sensor == SENSOR_OM6802) + poll_sensor(gspca_dev); - /* restart on each start, just in case, sometimes regs goes wrong - * when using controls from app */ - setbrightness(gspca_dev); - setcontrast(gspca_dev); - setcolors(gspca_dev); return 0; } @@ -926,10 +929,9 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream, sizeof sensor_data[sd->sensor].stream); - msleep(20); reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream, sizeof sensor_data[sd->sensor].stream); - if (sd->sensor != SENSOR_OTHER) { + if (sd->sensor == SENSOR_OM6802) { msleep(20); reg_w(gspca_dev, 0x0309); } diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index 9f243d7e3110..4b44dde9f8b8 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -426,7 +426,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, packet_type0, frame, data + 2, gspca_dev->width); gspca_frame_add(gspca_dev, packet_type1, - frame, data + gspca_dev->width + 6, gspca_dev->width); + frame, data + gspca_dev->width + 5, gspca_dev->width); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 26dd155efcc3..cdd6c02ce81d 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -32,14 +32,14 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u8 hflip; - __u8 vflip; - __u8 lightfreq; - __u8 sharpness; + u8 hflip; + u8 vflip; + u8 lightfreq; + u8 sharpness; u8 image_offset; - char bridge; + u8 bridge; #define BRIDGE_VC0321 0 #define BRIDGE_VC0323 1 u8 sensor; @@ -52,6 +52,10 @@ struct sd { #define SENSOR_OV7670 6 #define SENSOR_PO1200 7 #define SENSOR_PO3130NC 8 + u8 flags; +#define FL_SAMSUNG 0x01 /* SamsungQ1 (2 sensors) */ +#define FL_HFLIP 0x02 /* mirrored by default */ +#define FL_VFLIP 0x04 /* vertical flipped by default */ }; /* V4L2 controls supported by the driver */ @@ -65,7 +69,7 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { -/* next 2 controls work with ov7660 and ov7670 only */ +/* next 2 controls work with some sensors only */ #define HFLIP_IDX 0 { { @@ -152,9 +156,9 @@ static const struct v4l2_pix_format vc0323_mode[] = { .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, - {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi13x0_soc only */ + {1280, 960, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi1310_soc only */ .bytesperline = 1280, - .sizeimage = 1280 * 1024 * 1 / 4 + 590, + .sizeimage = 1280 * 960 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 2}, }; @@ -188,11 +192,11 @@ static const struct v4l2_pix_format svga_mode[] = { #define OV7660_MVFP_MIRROR 0x20 #define OV7660_MVFP_VFLIP 0x10 -static const __u8 mi0360_matrix[9] = { +static const u8 mi0360_matrix[9] = { 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50 }; -static const __u8 mi0360_initVGA_JPG[][4] = { +static const u8 mi0360_initVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -301,7 +305,7 @@ static const __u8 mi0360_initVGA_JPG[][4] = { {0xb3, 0x5c, 0x01, 0xcc}, {} }; -static const __u8 mi0360_initQVGA_JPG[][4] = { +static const u8 mi0360_initQVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -421,211 +425,95 @@ static const __u8 mi0360_initQVGA_JPG[][4] = { {} }; -static const __u8 mi1310_socinitVGA_JPG[][4] = { +static const u8 mi1310_socinitVGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x0d, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x26, 0x80, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb8, 0x00, 0x13, 0xcc}, - {0xbc, 0x00, 0x71, 0xcc}, - {0xb8, 0x81, 0x01, 0xcc}, - {0xb8, 0x2c, 0x5a, 0xcc}, - {0xb8, 0x2d, 0xff, 0xcc}, - {0xb8, 0x2e, 0xee, 0xcc}, - {0xb8, 0x2f, 0xfb, 0xcc}, - {0xb8, 0x30, 0x52, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf1, 0xcc}, - {0xb8, 0x33, 0xff, 0xcc}, - {0xb8, 0x34, 0x54, 0xcc}, - {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, - {0xb8, 0x37, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x0d, 0x00, 0x09, 0xbb}, - {0x0d, 0x00, 0x08, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ {0xf0, 0x00, 0x01, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x06, 0x00, 0x14, 0xbb}, - {0x3a, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x9b, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, + {0x05, 0x00, 0x07, 0xbb}, + {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, + {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, + {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, + {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, + {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, + {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x2b, 0x00, 0x28, 0xbb}, - {0x2c, 0x00, 0x30, 0xbb}, - {0x2d, 0x00, 0x30, 0xbb}, - {0x2e, 0x00, 0x28, 0xbb}, - {0x41, 0x00, 0xd7, 0xbb}, - {0x09, 0x02, 0x3a, 0xbb}, - {0x0c, 0x00, 0x00, 0xbb}, - {0x20, 0x00, 0x00, 0xbb}, - {0x05, 0x00, 0x8c, 0xbb}, - {0x06, 0x00, 0x32, 0xbb}, - {0x07, 0x00, 0xc6, 0xbb}, - {0x08, 0x00, 0x19, 0xbb}, - {0x24, 0x80, 0x6f, 0xbb}, - {0xc8, 0x00, 0x0f, 0xbb}, - {0x20, 0x00, 0x0f, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, + {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, + {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, + {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, + {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, + {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, + {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, {0xb6, 0x03, 0x02, 0xcc}, {0xb6, 0x02, 0x80, 0xcc}, {0xb6, 0x05, 0x01, 0xcc}, {0xb6, 0x04, 0xe0, 0xcc}, - {0xb6, 0x12, 0x78, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x25, 0xcc}, {0xb6, 0x18, 0x02, 0xcc}, {0xb6, 0x17, 0x58, 0xcc}, {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, - {0xbf, 0xc0, 0x39, 0xcc}, - {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x0a, 0xcc}, - {0xb9, 0x14, 0x0a, 0xcc}, - {0xb9, 0x15, 0x0a, 0xcc}, - {0xb9, 0x16, 0x0a, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x0f, 0xcc}, - {0xb9, 0x1a, 0x0f, 0xcc}, - {0xb9, 0x1b, 0x0f, 0xcc}, - {0xb9, 0x1c, 0x0f, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0xb3, 0x01, 0x41, 0xcc}, - {0x03, 0x03, 0xc0, 0xbb}, - {0x06, 0x00, 0x10, 0xbb}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0x2f, 0x00, 0xC0, 0xbb}, - {0xb8, 0xa0, 0x12, 0xcc}, - {}, -}; -static const __u8 mi1310_socinitQVGA_JPG[][4] = { - {0xb0, 0x03, 0x19, 0xcc}, - {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb3, 0x05, 0x01, 0xcc}, - {0xb3, 0x06, 0x03, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, - {0xb3, 0x08, 0x01, 0xcc}, - {0xb3, 0x09, 0x0c, 0xcc}, - {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xdd, 0xcc}, - {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x0d, 0xcc}, - {0xb3, 0x20, 0x00, 0xcc}, - {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x01, 0xcc}, - {0xb3, 0x23, 0xe0, 0xcc}, - {0xb3, 0x14, 0x00, 0xcc}, - {0xb3, 0x15, 0x00, 0xcc}, - {0xb3, 0x16, 0x02, 0xcc}, - {0xb3, 0x17, 0x7f, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x26, 0x80, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb8, 0x00, 0x13, 0xcc}, - {0xbc, 0x00, 0xd1, 0xcc}, - {0xb8, 0x81, 0x01, 0xcc}, - {0xb8, 0x2c, 0x5a, 0xcc}, - {0xb8, 0x2d, 0xff, 0xcc}, - {0xb8, 0x2e, 0xee, 0xcc}, - {0xb8, 0x2f, 0xfb, 0xcc}, - {0xb8, 0x30, 0x52, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf1, 0xcc}, - {0xb8, 0x33, 0xff, 0xcc}, - {0xb8, 0x34, 0x54, 0xcc}, - {0xb8, 0x35, 0x00, 0xcc}, - {0xb8, 0x36, 0x00, 0xcc}, - {0xb8, 0x37, 0x00, 0xcc}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x0d, 0x00, 0x09, 0xbb}, - {0x0d, 0x00, 0x08, 0xbb}, - {0xf0, 0x00, 0x01, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x06, 0x00, 0x14, 0xbb}, - {0x3a, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x9b, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x2b, 0x00, 0x28, 0xbb}, - {0x2c, 0x00, 0x30, 0xbb}, - {0x2d, 0x00, 0x30, 0xbb}, - {0x2e, 0x00, 0x28, 0xbb}, - {0x41, 0x00, 0xd7, 0xbb}, - {0x09, 0x02, 0x3a, 0xbb}, - {0x0c, 0x00, 0x00, 0xbb}, - {0x20, 0x00, 0x00, 0xbb}, - {0x05, 0x00, 0x8c, 0xbb}, - {0x06, 0x00, 0x32, 0xbb}, - {0x07, 0x00, 0xc6, 0xbb}, - {0x08, 0x00, 0x19, 0xbb}, - {0x24, 0x80, 0x6f, 0xbb}, - {0xc8, 0x00, 0x0f, 0xbb}, - {0x20, 0x00, 0x0f, 0xbb}, - {0xb6, 0x00, 0x00, 0xcc}, - {0xb6, 0x03, 0x01, 0xcc}, - {0xb6, 0x02, 0x40, 0xcc}, - {0xb6, 0x05, 0x00, 0xcc}, - {0xb6, 0x04, 0xf0, 0xcc}, - {0xb6, 0x12, 0x78, 0xcc}, - {0xb6, 0x18, 0x00, 0xcc}, - {0xb6, 0x17, 0x96, 0xcc}, - {0xb6, 0x16, 0x00, 0xcc}, - {0xb6, 0x22, 0x12, 0xcc}, - {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x0a, 0xcc}, - {0xb9, 0x14, 0x0a, 0xcc}, - {0xb9, 0x15, 0x0a, 0xcc}, - {0xb9, 0x16, 0x0a, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x0f, 0xcc}, - {0xb9, 0x1a, 0x0f, 0xcc}, - {0xb9, 0x1b, 0x0f, 0xcc}, - {0xb9, 0x1c, 0x0f, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, {0xbc, 0x04, 0x18, 0xcc}, @@ -636,133 +524,335 @@ static const __u8 mi1310_socinitQVGA_JPG[][4] = { {0xbc, 0x0a, 0x10, 0xcc}, {0xbc, 0x0b, 0x00, 0xcc}, {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, + {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, + {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, + {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, + {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, + {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, + {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, + {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, + {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, + {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, + {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, + {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, + {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, + {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, + {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, + {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, + {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, + {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x34, 0x0c, 0x50, 0xbb}, {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, + {}, +}; +static const u8 mi1310_socinitQVGA_JPG[][4] = { + {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ + {0xf0, 0x00, 0x01, 0xbb}, + {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb}, + {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc}, + {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb}, + {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb}, + {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb}, {0x03, 0x03, 0xc0, 0xbb}, - {0x06, 0x00, 0x10, 0xbb}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0x2f, 0x00, 0xC0, 0xbb}, - {0xb8, 0xa0, 0x12, 0xcc}, {}, }; static const u8 mi1310_soc_InitSXGA_JPG[][4] = { {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc}, - {0xb3, 0x00, 0x24, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, - {0xb3, 0x06, 0x01, 0xcc}, - {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x0d, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x04, 0xcc}, - {0xb3, 0x23, 0x00, 0xcc}, + {0xb3, 0x22, 0x03, 0xcc}, + {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc}, {0xb3, 0x17, 0xff, 0xcc}, - {0xb8, 0x01, 0x7d, 0xcc}, - {0xb8, 0x81, 0x09, 0xcc}, - {0xb8, 0x27, 0x20, 0xcc}, - {0xb8, 0x26, 0x80, 0xcc}, - {0xb8, 0x06, 0x00, 0xcc}, - {0xb8, 0x07, 0x05, 0xcc}, - {0xb8, 0x08, 0x00, 0xcc}, - {0xb8, 0x09, 0x04, 0xcc}, - {0xb3, 0x00, 0x25, 0xcc}, - {0xb8, 0x00, 0x11, 0xcc}, - {0xbc, 0x00, 0x71, 0xcc}, - {0xb8, 0x81, 0x01, 0xcc}, - {0xb8, 0x2c, 0x5a, 0xcc}, - {0xb8, 0x2d, 0xff, 0xcc}, - {0xb8, 0x2e, 0xee, 0xcc}, - {0xb8, 0x2f, 0xfb, 0xcc}, - {0xb8, 0x30, 0x52, 0xcc}, - {0xb8, 0x31, 0xf8, 0xcc}, - {0xb8, 0x32, 0xf1, 0xcc}, - {0xb8, 0x33, 0xff, 0xcc}, - {0xb8, 0x34, 0x54, 0xcc}, + {0xb3, 0x00, 0x65, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0x70, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x5b, 0x00, 0x01, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x0d, 0x00, 0x09, 0xbb}, - {0x0d, 0x00, 0x08, 0xbb}, + {0x20, 0x03, 0x02, 0xbb}, /* h/v flip */ {0xf0, 0x00, 0x01, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x06, 0x00, 0x14, 0xbb}, - {0x3a, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, - {0x9b, 0x10, 0x00, 0xbb}, - {0x00, 0x00, 0x10, 0xdd}, + {0x05, 0x00, 0x07, 0xbb}, + {0x34, 0x00, 0x00, 0xbb}, + {0x35, 0xff, 0x00, 0xbb}, + {0xdc, 0x07, 0x02, 0xbb}, + {0xdd, 0x3c, 0x18, 0xbb}, + {0xde, 0x92, 0x6d, 0xbb}, + {0xdf, 0xcd, 0xb1, 0xbb}, + {0xe0, 0xff, 0xe7, 0xbb}, + {0x06, 0xf0, 0x0d, 0xbb}, + {0x06, 0x70, 0x0e, 0xbb}, + {0x4c, 0x00, 0x01, 0xbb}, + {0x4d, 0x00, 0x01, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2e, 0x0c, 0x60, 0xbb}, + {0x21, 0xb6, 0x6e, 0xbb}, + {0x37, 0x01, 0x40, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, - {0x00, 0x01, 0x00, 0xdd}, - {0x2b, 0x00, 0x28, 0xbb}, - {0x2c, 0x00, 0x30, 0xbb}, - {0x2d, 0x00, 0x30, 0xbb}, - {0x2e, 0x00, 0x28, 0xbb}, - {0x41, 0x00, 0xd7, 0xbb}, - {0x09, 0x02, 0x3a, 0xbb}, - {0x0c, 0x00, 0x00, 0xbb}, - {0x20, 0x00, 0x00, 0xbb}, - {0x05, 0x00, 0x8c, 0xbb}, - {0x06, 0x00, 0x32, 0xbb}, - {0x07, 0x00, 0xc6, 0xbb}, - {0x08, 0x00, 0x19, 0xbb}, - {0x24, 0x80, 0x6f, 0xbb}, - {0xc8, 0x00, 0x0f, 0xbb}, - {0x20, 0x00, 0x03, 0xbb}, + {0x07, 0x00, 0x84, 0xbb}, + {0x08, 0x02, 0x4a, 0xbb}, + {0x05, 0x01, 0x10, 0xbb}, + {0x06, 0x00, 0x39, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x58, 0x02, 0x67, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x5a, 0x02, 0x67, 0xbb}, + {0x59, 0x02, 0x00, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x39, 0x06, 0x18, 0xbb}, + {0x3a, 0x06, 0x18, 0xbb}, + {0x3b, 0x06, 0x18, 0xbb}, + {0x3c, 0x06, 0x18, 0xbb}, + {0x64, 0x7b, 0x5b, 0xbb}, {0xb6, 0x00, 0x00, 0xcc}, {0xb6, 0x03, 0x05, 0xcc}, {0xb6, 0x02, 0x00, 0xcc}, - {0xb6, 0x05, 0x04, 0xcc}, - {0xb6, 0x04, 0x00, 0xcc}, + {0xb6, 0x05, 0x03, 0xcc}, + {0xb6, 0x04, 0xc0, 0xcc}, {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x18, 0x0a, 0xcc}, - {0xb6, 0x17, 0x00, 0xcc}, + {0xb6, 0x13, 0x29, 0xcc}, + {0xb6, 0x18, 0x09, 0xcc}, + {0xb6, 0x17, 0x60, 0xcc}, {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc}, {0xb6, 0x23, 0x0b, 0xcc}, - {0xb3, 0x02, 0x02, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc}, {0xbf, 0xc1, 0x04, 0xcc}, - {0xbf, 0xcc, 0x10, 0xcc}, - {0xb9, 0x12, 0x00, 0xcc}, - {0xb9, 0x13, 0x14, 0xcc}, - {0xb9, 0x14, 0x14, 0xcc}, - {0xb9, 0x15, 0x14, 0xcc}, - {0xb9, 0x16, 0x14, 0xcc}, - {0xb9, 0x18, 0x00, 0xcc}, - {0xb9, 0x19, 0x1e, 0xcc}, - {0xb9, 0x1a, 0x1e, 0xcc}, - {0xb9, 0x1b, 0x1e, 0xcc}, - {0xb9, 0x1c, 0x1e, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, - {0xb8, 0x8e, 0x00, 0xcc}, - {0xb8, 0x8f, 0xff, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb8, 0x0c, 0x20, 0xcc}, - {0xb8, 0x0d, 0x70, 0xcc}, - {0xb6, 0x13, 0x13, 0xcc}, - {0x2f, 0x00, 0xC0, 0xbb}, - {0xb8, 0xa0, 0x12, 0xcc}, + {0x00, 0x00, 0x80, 0xdd}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x06, 0xf0, 0x8e, 0xbb}, + {0x00, 0x00, 0x80, 0xdd}, + {0x06, 0x70, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x5e, 0x6a, 0x53, 0xbb}, + {0x5f, 0x40, 0x2c, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x58, 0x00, 0x00, 0xbb}, + {0x53, 0x09, 0x03, 0xbb}, + {0x54, 0x31, 0x18, 0xbb}, + {0x55, 0x8b, 0x5f, 0xbb}, + {0x56, 0xc0, 0xa9, 0xbb}, + {0x57, 0xe0, 0xd2, 0xbb}, + {0xe1, 0x00, 0x00, 0xbb}, + {0xdc, 0x09, 0x03, 0xbb}, + {0xdd, 0x31, 0x18, 0xbb}, + {0xde, 0x8b, 0x5f, 0xbb}, + {0xdf, 0xc0, 0xa9, 0xbb}, + {0xe0, 0xe0, 0xd2, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xf0, 0x8e, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x2f, 0xde, 0x20, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x24, 0x50, 0x20, 0xbb}, + {0xbc, 0x0e, 0x00, 0xcc}, + {0xbc, 0x0f, 0x05, 0xcc}, + {0xbc, 0x10, 0xc0, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x34, 0x0c, 0x50, 0xbb}, + {0xbc, 0x11, 0x03, 0xcc}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x80, 0x00, 0x03, 0xbb}, + {0x81, 0xc7, 0x14, 0xbb}, + {0x82, 0xeb, 0xe8, 0xbb}, + {0x83, 0xfe, 0xf4, 0xbb}, + {0x84, 0xcd, 0x10, 0xbb}, + {0x85, 0xf3, 0xee, 0xbb}, + {0x86, 0xff, 0xf1, 0xbb}, + {0x87, 0xcd, 0x10, 0xbb}, + {0x88, 0xf3, 0xee, 0xbb}, + {0x89, 0x01, 0xf1, 0xbb}, + {0x8a, 0xe5, 0x17, 0xbb}, + {0x8b, 0xe8, 0xe2, 0xbb}, + {0x8c, 0xf7, 0xed, 0xbb}, + {0x8d, 0x00, 0xff, 0xbb}, + {0x8e, 0xec, 0x10, 0xbb}, + {0x8f, 0xf0, 0xed, 0xbb}, + {0x90, 0xf9, 0xf2, 0xbb}, + {0x91, 0x00, 0x00, 0xbb}, + {0x92, 0xe9, 0x0d, 0xbb}, + {0x93, 0xf4, 0xf2, 0xbb}, + {0x94, 0xfb, 0xf5, 0xbb}, + {0x95, 0x00, 0xff, 0xbb}, + {0xb6, 0x0f, 0x08, 0xbb}, + {0xb7, 0x3d, 0x16, 0xbb}, + {0xb8, 0x0c, 0x04, 0xbb}, + {0xb9, 0x1c, 0x07, 0xbb}, + {0xba, 0x0a, 0x03, 0xbb}, + {0xbb, 0x1b, 0x09, 0xbb}, + {0xbc, 0x17, 0x0d, 0xbb}, + {0xbd, 0x23, 0x1d, 0xbb}, + {0xbe, 0x00, 0x28, 0xbb}, + {0xbf, 0x11, 0x09, 0xbb}, + {0xc0, 0x16, 0x15, 0xbb}, + {0xc1, 0x00, 0x1b, 0xbb}, + {0xc2, 0x0e, 0x07, 0xbb}, + {0xc3, 0x14, 0x10, 0xbb}, + {0xc4, 0x00, 0x17, 0xbb}, + {0x06, 0x74, 0x8e, 0xbb}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x03, 0x03, 0xc0, 0xbb}, {} }; -static const __u8 mi1320_gamma[17] = { +static const u8 mi1320_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 mi1320_matrix[9] = { +static const u8 mi1320_matrix[9] = { 0x54, 0xda, 0x06, 0xf1, 0x50, 0xf4, 0xf7, 0xea, 0x52 }; -static const __u8 mi1320_initVGA_data[][4] = { +static const u8 mi1320_initVGA_data[][4] = { {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, @@ -841,7 +931,7 @@ static const __u8 mi1320_initVGA_data[][4] = { {0xb3, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, {} }; -static const __u8 mi1320_initQVGA_data[][4] = { +static const u8 mi1320_initQVGA_data[][4] = { {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x33, 0xdd}, @@ -948,7 +1038,7 @@ static const u8 mi1320_soc_InitVGA[][4] = { {0x07, 0x00, 0xe0, 0xbb}, {0x08, 0x00, 0x0b, 0xbb}, {0x21, 0x00, 0x0c, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc}, @@ -958,7 +1048,7 @@ static const u8 mi1320_soc_InitVGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1051,7 +1141,7 @@ static const u8 mi1320_soc_InitQVGA[][4] = { {0x07, 0x00, 0xe0, 0xbb}, {0x08, 0x00, 0x0b, 0xbb}, {0x21, 0x00, 0x0c, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, {0xbf, 0xcc, 0x04, 0xcc}, @@ -1071,7 +1161,7 @@ static const u8 mi1320_soc_InitQVGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1161,7 +1251,7 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x00, 0x00, 0x20, 0xdd}, {0xf0, 0x00, 0x00, 0xbb}, {0x00, 0x00, 0x30, 0xdd}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x00, 0x00, 0x20, 0xdd}, {0xbf, 0xc0, 0x26, 0xcc}, {0xbf, 0xc1, 0x02, 0xcc}, @@ -1172,7 +1262,7 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x01, 0x42, 0xbb}, {0x08, 0x00, 0x11, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1230,7 +1320,7 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb}, {0x08, 0x00, 0x27, 0xbb}, - {0x20, 0x01, 0x03, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, /* h/v flip */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, @@ -1249,15 +1339,15 @@ static const u8 mi1320_soc_InitSXGA[][4] = { {0x64, 0x5e, 0x1c, 0xbb}, {} }; -static const __u8 po3130_gamma[17] = { +static const u8 po3130_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 po3130_matrix[9] = { +static const u8 po3130_matrix[9] = { 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 }; -static const __u8 po3130_initVGA_data[][4] = { +static const u8 po3130_initVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -1340,7 +1430,7 @@ static const __u8 po3130_initVGA_data[][4] = { {0xb3, 0x5c, 0x00, 0xcc}, {0xb3, 0x01, 0x41, 0xcc}, {} }; -static const __u8 po3130_rundata[][4] = { +static const u8 po3130_rundata[][4] = { {0x00, 0x47, 0x45, 0xaa}, {0x00, 0x48, 0x9b, 0xaa}, {0x00, 0x49, 0x3a, 0xaa}, {0x00, 0x4a, 0x01, 0xaa}, {0x00, 0x44, 0x40, 0xaa}, @@ -1355,7 +1445,7 @@ static const __u8 po3130_rundata[][4] = { {} }; -static const __u8 po3130_initQVGA_data[][4] = { +static const u8 po3130_initQVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x09, 0xcc}, {0xb3, 0x00, 0x04, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -1441,16 +1531,16 @@ static const __u8 po3130_initQVGA_data[][4] = { {} }; -static const __u8 hv7131r_gamma[17] = { +static const u8 hv7131r_gamma[17] = { /* 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, * 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff */ 0x04, 0x1a, 0x36, 0x55, 0x6f, 0x87, 0x9d, 0xb0, 0xc1, 0xcf, 0xda, 0xe4, 0xec, 0xf3, 0xf8, 0xfd, 0xff }; -static const __u8 hv7131r_matrix[9] = { +static const u8 hv7131r_matrix[9] = { 0x5f, 0xec, 0xf5, 0xf1, 0x5a, 0xf5, 0xf1, 0xec, 0x63 }; -static const __u8 hv7131r_initVGA_data[][4] = { +static const u8 hv7131r_initVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -1493,7 +1583,7 @@ static const __u8 hv7131r_initVGA_data[][4] = { {} }; -static const __u8 hv7131r_initQVGA_data[][4] = { +static const u8 hv7131r_initQVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, {0xb3, 0x00, 0x24, 0xcc}, @@ -1548,14 +1638,14 @@ static const __u8 hv7131r_initQVGA_data[][4] = { {} }; -static const __u8 ov7660_gamma[17] = { +static const u8 ov7660_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 ov7660_matrix[9] = { +static const u8 ov7660_matrix[9] = { 0x5a, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x62 }; -static const __u8 ov7660_initVGA_data[][4] = { +static const u8 ov7660_initVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, @@ -1613,7 +1703,7 @@ static const __u8 ov7660_initVGA_data[][4] = { {0x00, 0x29, 0x3c, 0xaa}, {0xb3, 0x01, 0x45, 0xcc}, {} }; -static const __u8 ov7660_initQVGA_data[][4] = { +static const u8 ov7660_initQVGA_data[][4] = { {0xb0, 0x4d, 0x00, 0xcc}, {0xb3, 0x01, 0x01, 0xcc}, {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x01, 0xcc}, {0xb3, 0x00, 0x21, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, @@ -1682,26 +1772,26 @@ static const __u8 ov7660_initQVGA_data[][4] = { {} }; -static const __u8 ov7660_50HZ[][4] = { +static const u8 ov7660_50HZ[][4] = { {0x00, 0x3b, 0x08, 0xaa}, {0x00, 0x9d, 0x40, 0xaa}, {0x00, 0x13, 0xa7, 0xaa}, {} }; -static const __u8 ov7660_60HZ[][4] = { +static const u8 ov7660_60HZ[][4] = { {0x00, 0x3b, 0x00, 0xaa}, {0x00, 0x9e, 0x40, 0xaa}, {0x00, 0x13, 0xa7, 0xaa}, {} }; -static const __u8 ov7660_NoFliker[][4] = { +static const u8 ov7660_NoFliker[][4] = { {0x00, 0x13, 0x87, 0xaa}, {} }; -static const __u8 ov7670_initVGA_JPG[][4] = { +static const u8 ov7670_initVGA_JPG[][4] = { {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x30, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, @@ -1831,7 +1921,7 @@ static const __u8 ov7670_initVGA_JPG[][4] = { {}, }; -static const __u8 ov7670_initQVGA_JPG[][4] = { +static const u8 ov7670_initQVGA_JPG[][4] = { {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x30, 0xdd}, {0xb0, 0x03, 0x19, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, @@ -1966,14 +2056,14 @@ static const __u8 ov7670_initQVGA_JPG[][4] = { }; /* PO1200 - values from usbvm326.inf and ms-win trace */ -static const __u8 po1200_gamma[17] = { +static const u8 po1200_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff }; -static const __u8 po1200_matrix[9] = { +static const u8 po1200_matrix[9] = { 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e }; -static const __u8 po1200_initVGA_data[][4] = { +static const u8 po1200_initVGA_data[][4] = { {0xb0, 0x03, 0x19, 0xcc}, /* reset? */ {0xb0, 0x03, 0x19, 0xcc}, /* {0x00, 0x00, 0x33, 0xdd}, */ @@ -2276,9 +2366,9 @@ static const struct sensor_info sensor_info_data[] = { /* read 'len' bytes in gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, - __u16 req, - __u16 index, - __u16 len) + u16 req, + u16 index, + u16 len) { usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), @@ -2290,9 +2380,9 @@ static void reg_r(struct gspca_dev *gspca_dev, } static void reg_w(struct usb_device *dev, - __u16 req, - __u16 value, - __u16 index) + u16 req, + u16 value, + u16 index) { usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -2342,11 +2432,18 @@ static u16 read_sensor_register(struct gspca_dev *gspca_dev, static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; int i; u16 value; const struct sensor_info *ptsensor_info; +/*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/ + if (sd->flags & FL_SAMSUNG) { + reg_w(dev, 0xa0, 0x01, 0xb301); + reg_w(dev, 0x89, 0xf0ff, 0xffff); /* select the back sensor */ + } + reg_r(gspca_dev, 0xa1, 0xbfcf, 1); PDEBUG(D_PROBE, "check sensor header %02x", gspca_dev->usb_buf[0]); for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) { @@ -2406,17 +2503,17 @@ static void i2c_write(struct gspca_dev *gspca_dev, } static void put_tab_to_reg(struct gspca_dev *gspca_dev, - const __u8 *tab, __u8 tabsize, __u16 addr) + const u8 *tab, u8 tabsize, u16 addr) { int j; - __u16 ad = addr; + u16 ad = addr; for (j = 0; j < tabsize; j++) reg_w(gspca_dev->dev, 0xa0, tab[j], ad++); } static void usb_exchange(struct gspca_dev *gspca_dev, - const __u8 data[][4]) + const u8 data[][4]) { struct usb_device *dev = gspca_dev->dev; int i = 0; @@ -2466,7 +2563,8 @@ static int sd_config(struct gspca_dev *gspca_dev, }; cam = &gspca_dev->cam; - sd->bridge = id->driver_info; + sd->bridge = id->driver_info >> 8; + sd->flags = id->driver_info & 0xff; sensor = vc032x_probe_sensor(gspca_dev); switch (sensor) { case -1: @@ -2519,8 +2617,6 @@ static int sd_config(struct gspca_dev *gspca_dev, case SENSOR_MI1320_SOC: cam->cam_mode = bi_mode; cam->nmodes = ARRAY_SIZE(bi_mode); - cam->input_flags = V4L2_IN_ST_VFLIP | - V4L2_IN_ST_HFLIP; break; default: cam->cam_mode = vc0323_mode; @@ -2532,14 +2628,14 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; - if (sd->sensor == SENSOR_OV7670) { - sd->hflip = 1; - sd->vflip = 1; - } + if (sd->sensor == SENSOR_OV7670) + sd->flags |= FL_HFLIP | FL_VFLIP; sd->lightfreq = FREQ_DEF; if (sd->sensor != SENSOR_OV7670) gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); switch (sd->sensor) { + case SENSOR_MI1310_SOC: + case SENSOR_MI1320_SOC: case SENSOR_OV7660: case SENSOR_OV7670: case SENSOR_PO1200: @@ -2568,39 +2664,50 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -/* for OV7660 and OV7670 only */ +/* some sensors only */ static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 data; - + u8 data[2], hflip, vflip; + + hflip = sd->hflip; + if (sd->flags & FL_HFLIP) + hflip = !hflip; + vflip = sd->vflip; + if (sd->flags & FL_VFLIP) + vflip = !vflip; switch (sd->sensor) { - case SENSOR_OV7660: - data = 1; + case SENSOR_MI1310_SOC: + case SENSOR_MI1320_SOC: + data[0] = data[1] = 0; /* select page 0 */ + i2c_write(gspca_dev, 0xf0, data, 2); + data[0] = sd->sensor == SENSOR_MI1310_SOC ? 0x03 : 0x01; + data[1] = 0x02 * hflip + | 0x01 * vflip; + i2c_write(gspca_dev, 0x20, data, 2); break; + case SENSOR_OV7660: case SENSOR_OV7670: - data = 7; + data[0] = sd->sensor == SENSOR_OV7660 ? 0x01 : 0x07; + data[0] |= OV7660_MVFP_MIRROR * hflip + | OV7660_MVFP_VFLIP * vflip; + i2c_write(gspca_dev, OV7660_REG_MVFP, data, 1); break; case SENSOR_PO1200: - data = 0; - i2c_write(gspca_dev, 0x03, &data, 1); - data = 0x80 * sd->hflip - | 0x40 * sd->vflip + data[0] = 0; + i2c_write(gspca_dev, 0x03, data, 1); + data[0] = 0x80 * hflip + | 0x40 * vflip | 0x06; - i2c_write(gspca_dev, 0x1e, &data, 1); - return; - default: - return; + i2c_write(gspca_dev, 0x1e, data, 1); + break; } - data |= OV7660_MVFP_MIRROR * sd->hflip - | OV7660_MVFP_VFLIP * sd->vflip; - i2c_write(gspca_dev, OV7660_REG_MVFP, &data, 1); } static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - static const __u8 (*ov7660_freq_tb[3])[4] = + static const u8 (*ov7660_freq_tb[3])[4] = {ov7660_NoFliker, ov7660_50HZ, ov7660_60HZ}; if (sd->sensor != SENSOR_OV7660) @@ -2612,7 +2719,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev) static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 data; + u8 data; if (sd->sensor != SENSOR_PO1200) return; @@ -2625,9 +2732,9 @@ static void setsharpness(struct gspca_dev *gspca_dev) static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - const __u8 (*init)[4]; - const __u8 *GammaT = NULL; - const __u8 *MatrixT = NULL; + const u8 (*init)[4]; + const u8 *GammaT = NULL; + const u8 *MatrixT = NULL; int mode; static const u8 (*mi1320_soc_init[])[4] = { mi1320_soc_InitSXGA, @@ -2635,6 +2742,13 @@ static int sd_start(struct gspca_dev *gspca_dev) mi1320_soc_InitQVGA, }; +/*fixme: back sensor only*/ + if (sd->flags & FL_SAMSUNG) { + reg_w(gspca_dev->dev, 0x89, 0xf0ff, 0xffff); + reg_w(gspca_dev->dev, 0xa9, 0x8348, 0x000e); + reg_w(gspca_dev->dev, 0xa9, 0x0000, 0x001a); + } + /* Assume start use the good resolution from gspca_dev->mode */ if (sd->bridge == BRIDGE_VC0321) { reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfec); @@ -2737,15 +2851,20 @@ static int sd_start(struct gspca_dev *gspca_dev) put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c); /* set the led on 0x0892 0x0896 */ - if (sd->sensor != SENSOR_PO1200) { - reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + if (sd->sensor == SENSOR_PO1200) { + setsharpness(gspca_dev); + sethvflip(gspca_dev); + reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415); + } else if (sd->sensor == SENSOR_MI1310_SOC) { + reg_w(gspca_dev->dev, 0x89, 0x058c, 0x0000); msleep(100); sethvflip(gspca_dev); setlightfreq(gspca_dev); } else { - setsharpness(gspca_dev); + reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + msleep(100); sethvflip(gspca_dev); - reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415); + setlightfreq(gspca_dev); } } return 0; @@ -2754,8 +2873,12 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; + struct sd *sd = (struct sd *) gspca_dev; - reg_w(dev, 0x89, 0xffff, 0xffff); + if (sd->sensor == SENSOR_MI1310_SOC) + reg_w(dev, 0x89, 0x058c, 0x00ff); + else + reg_w(dev, 0x89, 0xffff, 0xffff); reg_w(dev, 0xa0, 0x01, 0xb301); reg_w(dev, 0xa0, 0x09, 0xb003); } @@ -2764,15 +2887,20 @@ static void sd_stopN(struct gspca_dev *gspca_dev) static void sd_stop0(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; + struct sd *sd = (struct sd *) gspca_dev; if (!gspca_dev->present) return; - reg_w(dev, 0x89, 0xffff, 0xffff); +/*fixme: is this useful?*/ + if (sd->sensor == SENSOR_MI1310_SOC) + reg_w(dev, 0x89, 0x058c, 0x00ff); + else + reg_w(dev, 0x89, 0xffff, 0xffff); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso pkt length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -2872,21 +3000,12 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu) { + static const char *freq_nm[3] = {"NoFliker", "50 Hz", "60 Hz"}; + switch (menu->id) { case V4L2_CID_POWER_LINE_FREQUENCY: - switch (menu->index) { - case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ - strcpy((char *) menu->name, "NoFliker"); - return 0; - case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ - strcpy((char *) menu->name, "50 Hz"); - return 0; - default: -/* case 2: * V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ - strcpy((char *) menu->name, "60 Hz"); - return 0; - } - break; + strcpy((char *) menu->name, freq_nm[menu->index]); + return 0; } return -EINVAL; } @@ -2906,19 +3025,23 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ +#define BF(bridge, flags) \ + .driver_info = (BRIDGE_ ## bridge << 8) \ + | (flags) static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x405b), .driver_info = BRIDGE_VC0323}, - {USB_DEVICE(0x046d, 0x0892), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x046d, 0x0896), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x046d, 0x0897), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x0ac8, 0x0321), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x0ac8, 0x0323), .driver_info = BRIDGE_VC0323}, - {USB_DEVICE(0x0ac8, 0x0328), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x0ac8, 0xc001), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x0ac8, 0xc002), .driver_info = BRIDGE_VC0321}, - {USB_DEVICE(0x15b8, 0x6001), .driver_info = BRIDGE_VC0323}, - {USB_DEVICE(0x15b8, 0x6002), .driver_info = BRIDGE_VC0323}, - {USB_DEVICE(0x17ef, 0x4802), .driver_info = BRIDGE_VC0323}, + {USB_DEVICE(0x041e, 0x405b), BF(VC0323, FL_VFLIP)}, + {USB_DEVICE(0x046d, 0x0892), BF(VC0321, 0)}, + {USB_DEVICE(0x046d, 0x0896), BF(VC0321, 0)}, + {USB_DEVICE(0x046d, 0x0897), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0x0321), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0x0323), BF(VC0323, 0)}, + {USB_DEVICE(0x0ac8, 0x0328), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0xc001), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0xc002), BF(VC0321, 0)}, + {USB_DEVICE(0x0ac8, 0xc301), BF(VC0323, FL_SAMSUNG)}, + {USB_DEVICE(0x15b8, 0x6001), BF(VC0323, 0)}, + {USB_DEVICE(0x15b8, 0x6002), BF(VC0323, 0)}, + {USB_DEVICE(0x17ef, 0x4802), BF(VC0323, 0)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 3d2756f7874a..cdf3357b4c9f 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -7574,7 +7574,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, - .nctrls = sizeof sd_ctrls / sizeof sd_ctrls[0], + .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, .start = sd_start, diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c index 06791749d1a0..5a6b78b8d25d 100644 --- a/drivers/media/video/hdpvr/hdpvr-control.c +++ b/drivers/media/video/hdpvr/hdpvr-control.c @@ -178,24 +178,24 @@ error: int hdpvr_set_options(struct hdpvr_device *dev) { - hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std); + hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std); - hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, + hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, dev->options.video_input+1); - hdpvr_set_audio(dev, dev->options.audio_input+1, + hdpvr_set_audio(dev, dev->options.audio_input+1, dev->options.audio_codec); - hdpvr_set_bitrate(dev); - hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, + hdpvr_set_bitrate(dev); + hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, dev->options.bitrate_mode); - hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode); + hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode); - hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness); - hdpvr_config_call(dev, CTRL_CONTRAST, dev->options.contrast); - hdpvr_config_call(dev, CTRL_HUE, dev->options.hue); - hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation); - hdpvr_config_call(dev, CTRL_SHARPNESS, dev->options.sharpness); + hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness); + hdpvr_config_call(dev, CTRL_CONTRAST, dev->options.contrast); + hdpvr_config_call(dev, CTRL_HUE, dev->options.hue); + hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation); + hdpvr_config_call(dev, CTRL_SHARPNESS, dev->options.sharpness); - return 0; + return 0; } diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c index 188bd5aea258..1c9bc94c905c 100644 --- a/drivers/media/video/hdpvr/hdpvr-core.c +++ b/drivers/media/video/hdpvr/hdpvr-core.c @@ -126,7 +126,7 @@ static int device_authorization(struct hdpvr_device *dev) char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL); if (!print_buf) { v4l2_err(&dev->v4l2_dev, "Out of memory\n"); - goto error; + return retval; } #endif @@ -140,7 +140,7 @@ static int device_authorization(struct hdpvr_device *dev) if (ret != 46) { v4l2_err(&dev->v4l2_dev, "unexpected answer of status request, len %d\n", ret); - goto error; + goto unlock; } #ifdef HDPVR_DEBUG else { @@ -163,7 +163,7 @@ static int device_authorization(struct hdpvr_device *dev) v4l2_err(&dev->v4l2_dev, "unknown firmware version 0x%x\n", dev->usbc_buf[1]); ret = -EINVAL; - goto error; + goto unlock; } response = dev->usbc_buf+38; @@ -188,10 +188,10 @@ static int device_authorization(struct hdpvr_device *dev) 10000); v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "magic request returned %d\n", ret); - mutex_unlock(&dev->usbc_mutex); retval = ret != 8; -error: +unlock: + mutex_unlock(&dev->usbc_mutex); return retval; } @@ -350,6 +350,7 @@ static int hdpvr_probe(struct usb_interface *interface, mutex_lock(&dev->io_mutex); if (hdpvr_alloc_buffers(dev, NUM_BUFFERS)) { + mutex_unlock(&dev->io_mutex); v4l2_err(&dev->v4l2_dev, "allocating transfer buffers failed\n"); goto error; @@ -381,7 +382,6 @@ static int hdpvr_probe(struct usb_interface *interface, error: if (dev) { - mutex_unlock(&dev->io_mutex); /* this frees allocated memory */ hdpvr_delete(dev); } diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c index c4b5d1515c10..296330a0e1e5 100644 --- a/drivers/media/video/hdpvr/hdpvr-i2c.c +++ b/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -127,7 +127,6 @@ int hdpvr_register_i2c_adapter(struct hdpvr_device *dev) sizeof(i2c_adap->name)); i2c_adap->algo = &hdpvr_algo; i2c_adap->class = I2C_CLASS_TV_ANALOG; - i2c_adap->id = I2C_HW_B_HDPVR; i2c_adap->owner = THIS_MODULE; i2c_adap->dev.parent = &dev->udev->dev; diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index d678765cbba2..2eb9dc2ebe59 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -375,6 +375,7 @@ static int hdpvr_open(struct file *file) * in resumption */ mutex_lock(&dev->io_mutex); dev->open_count++; + mutex_unlock(&dev->io_mutex); fh->dev = dev; @@ -383,7 +384,6 @@ static int hdpvr_open(struct file *file) retval = 0; err: - mutex_unlock(&dev->io_mutex); return retval; } @@ -519,8 +519,10 @@ static unsigned int hdpvr_poll(struct file *filp, poll_table *wait) mutex_lock(&dev->io_mutex); - if (video_is_unregistered(dev->video_dev)) + if (video_is_unregistered(dev->video_dev)) { + mutex_unlock(&dev->io_mutex); return -EIO; + } if (dev->status == STATUS_IDLE) { if (hdpvr_start_streaming(dev)) { diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 86f2fefe1edf..b92ddcabf0b6 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -122,12 +122,12 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, return 1; } -static inline int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { return get_key_haup_common (ir, ir_key, ir_raw, 3, 0); } -static inline int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { return get_key_haup_common (ir, ir_key, ir_raw, 6, 3); } @@ -357,9 +357,11 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) case 0x47: case 0x71: case 0x2d: - if (adap->id == I2C_HW_B_CX2388x) { + if (adap->id == I2C_HW_B_CX2388x || + adap->id == I2C_HW_B_CX2341X) { /* Handled by cx88-input */ - name = "CX2388x remote"; + name = adap->id == I2C_HW_B_CX2341X ? "CX2341x remote" + : "CX2388x remote"; ir_type = IR_TYPE_RC5; ir->get_key = get_key_haup_xvr; if (hauppauge == 1) { @@ -392,7 +394,36 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_codes = init_data->ir_codes; name = init_data->name; - ir->get_key = init_data->get_key; + if (init_data->type) + ir_type = init_data->type; + + switch (init_data->internal_get_key_func) { + case IR_KBD_GET_KEY_CUSTOM: + /* The bridge driver provided us its own function */ + ir->get_key = init_data->get_key; + break; + case IR_KBD_GET_KEY_PIXELVIEW: + ir->get_key = get_key_pixelview; + break; + case IR_KBD_GET_KEY_PV951: + ir->get_key = get_key_pv951; + break; + case IR_KBD_GET_KEY_HAUP: + ir->get_key = get_key_haup; + break; + case IR_KBD_GET_KEY_KNC1: + ir->get_key = get_key_knc1; + break; + case IR_KBD_GET_KEY_FUSIONHDTV: + ir->get_key = get_key_fusionhdtv; + break; + case IR_KBD_GET_KEY_HAUP_XVR: + ir->get_key = get_key_haup_xvr; + break; + case IR_KBD_GET_KEY_AVERMEDIA_CARDBUS: + ir->get_key = get_key_avermedia_cardbus; + break; + } } /* Make sure we are all setup before going on */ @@ -454,7 +485,8 @@ static int ir_remove(struct i2c_client *client) static const struct i2c_device_id ir_kbd_id[] = { /* Generic entry for any IR receiver */ { "ir_video", 0 }, - /* IR device specific entries could be added here */ + /* IR device specific entries should be added here */ + { "ir_rx_z8f0811_haup", 0 }, { } }; diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c index 2883c8780760..4873b6ca5801 100644 --- a/drivers/media/video/ivtv/ivtv-cards.c +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -977,26 +977,27 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = { /* ------------------------------------------------------------------------- */ -/* AVerMedia PVR-150 Plus (M113) card */ +/* AVerMedia PVR-150 Plus / AVerTV M113 cards with a Daewoo/Partsnic Tuner */ static const struct ivtv_card_pci_info ivtv_pci_aver_pvr150[] = { - { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc035 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc034 }, /* NTSC */ + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc035 }, /* NTSC FM */ { 0, 0, 0 } }; static const struct ivtv_card ivtv_card_aver_pvr150 = { .type = IVTV_CARD_AVER_PVR150PLUS, - .name = "AVerMedia PVR-150 Plus", + .name = "AVerMedia PVR-150 Plus / AVerTV M113 Partsnic (Daewoo) Tuner", .v4l2_capabilities = IVTV_CAP_ENCODER, .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, .hw_audio_ctrl = IVTV_HW_CX25840, .hw_muxer = IVTV_HW_GPIO, - .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | + IVTV_HW_WM8739 | IVTV_HW_GPIO, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, - { IVTV_CARD_INPUT_SVIDEO1, 1, - CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, }, .audio_inputs = { @@ -1004,18 +1005,66 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = { { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, }, .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, - .gpio_init = { .direction = 0x0800, .initial_value = 0 }, - .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 }, + /* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */ + .gpio_init = { .direction = 0xc000, .initial_value = 0 }, + .gpio_audio_input = { .mask = 0xc000, + .tuner = 0x0000, + .linein = 0x4000, + .radio = 0x8000 }, .tuners = { - /* This card has a Partsnic PTI-5NF05 tuner */ - { .std = V4L2_STD_MN, .tuner = TUNER_TCL_2002N }, + /* Subsystem ID's 0xc03[45] have a Partsnic PTI-5NF05 tuner */ + { .std = V4L2_STD_MN, .tuner = TUNER_PARTSNIC_PTI_5NF05 }, }, .pci_list = ivtv_pci_aver_pvr150, + /* Subsystem ID 0xc035 has a TEA5767(?) FM tuner, 0xc034 does not */ .i2c = &ivtv_i2c_radio, }; /* ------------------------------------------------------------------------- */ +/* AVerMedia UltraTV 1500 MCE (newer non-cx88 version, M113 variant) card */ + +static const struct ivtv_card_pci_info ivtv_pci_aver_ultra1500mce[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_aver_ultra1500mce = { + .type = IVTV_CARD_AVER_ULTRA1500MCE, + .name = "AVerMedia UltraTV 1500 MCE / AVerTV M113 Philips Tuner", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_muxer = IVTV_HW_GPIO, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | + IVTV_HW_WM8739 | IVTV_HW_GPIO, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, + /* The 74HC4052 Dual 4:1 multiplexer is controlled by 2 GPIO lines */ + .gpio_init = { .direction = 0xc000, .initial_value = 0 }, + .gpio_audio_input = { .mask = 0xc000, + .tuner = 0x0000, + .linein = 0x4000, + .radio = 0x8000 }, + .tuners = { + /* The UltraTV 1500 MCE has a Philips FM1236 MK5 TV/FM tuner */ + { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, + .pci_list = ivtv_pci_aver_ultra1500mce, + .i2c = &ivtv_i2c_std, +}; + +/* ------------------------------------------------------------------------- */ + /* AVerMedia EZMaker PCI Deluxe card */ static const struct ivtv_card_pci_info ivtv_pci_aver_ezmaker[] = { @@ -1180,6 +1229,7 @@ static const struct ivtv_card *ivtv_card_list[] = { &ivtv_card_aver_ezmaker, &ivtv_card_aver_m104, &ivtv_card_buffalo, + &ivtv_card_aver_ultra1500mce, /* Variations of standard cards but with the same PCI IDs. These cards must come last in this list. */ diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h index 0b8fe85fb697..e99a0a255578 100644 --- a/drivers/media/video/ivtv/ivtv-cards.h +++ b/drivers/media/video/ivtv/ivtv-cards.h @@ -50,7 +50,8 @@ #define IVTV_CARD_AVER_EZMAKER 23 /* AVerMedia EZMaker PCI Deluxe */ #define IVTV_CARD_AVER_M104 24 /* AverMedia M104 miniPCI card */ #define IVTV_CARD_BUFFALO_MV5L 25 /* Buffalo PC-MV5L/PCI card */ -#define IVTV_CARD_LAST 25 +#define IVTV_CARD_AVER_ULTRA1500MCE 26 /* AVerMedia UltraTV 1500 MCE */ +#define IVTV_CARD_LAST 26 /* Variants of existing cards but with the same PCI IDs. The driver detects these based on other device information. diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 558f8a837ff4..63ea0fb66063 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -186,6 +186,7 @@ MODULE_PARM_DESC(cardtype, "\t\t\t24 = AverMedia EZMaker PCI Deluxe\n" "\t\t\t25 = AverMedia M104 (not yet working)\n" "\t\t\t26 = Buffalo PC-MV5L/PCI\n" + "\t\t\t27 = AVerMedia UltraTV 1500 MCE\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: BGH, DK, I, M, N, Nc, 60"); @@ -218,7 +219,7 @@ MODULE_PARM_DESC(ivtv_yuv_mode, "\t\t\tDefault: 0 (interlaced)"); MODULE_PARM_DESC(ivtv_yuv_threshold, "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n" - "\t\t\tDefault: 480");; + "\t\t\tDefault: 480"); MODULE_PARM_DESC(enc_mpg_buffers, "Encoder MPG Buffers (in MB)\n" "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS)); diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c index 85ac707228e7..aede061cae5d 100644 --- a/drivers/media/video/ivtv/ivtv-gpio.c +++ b/drivers/media/video/ivtv/ivtv-gpio.c @@ -236,18 +236,6 @@ static int subdev_s_radio(struct v4l2_subdev *sd) return 0; } -static int subdev_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct ivtv *itv = sd_to_ivtv(sd); - u16 mask, data; - - mask = itv->card->gpio_audio_input.mask; - data = itv->card->gpio_audio_input.tuner; - if (mask) - write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); - return 0; -} - static int subdev_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -344,7 +332,6 @@ static const struct v4l2_subdev_core_ops subdev_core_ops = { .g_ctrl = subdev_g_ctrl, .s_ctrl = subdev_s_ctrl, .queryctrl = subdev_queryctrl, - .s_std = subdev_s_std, }; static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = { diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index e52aa322b134..8f15a31d3f66 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -509,7 +509,6 @@ static struct i2c_algorithm ivtv_algo = { /* template for our-bit banger */ static struct i2c_adapter ivtv_i2c_adap_hw_template = { .name = "ivtv i2c driver", - .id = I2C_HW_B_CX2341X, .algo = &ivtv_algo, .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, @@ -560,7 +559,6 @@ static int ivtv_getsda_old(void *data) /* template for i2c-bit-algo */ static struct i2c_adapter ivtv_i2c_adap_template = { .name = "ivtv i2c driver", - .id = I2C_HW_B_CX2341X, .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 1d66855a379a..d0765bed79c9 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -1915,8 +1915,7 @@ static void __devexit meye_remove(struct pci_dev *pcidev) } static struct pci_device_id meye_pci_tbl[] = { - { PCI_VENDOR_ID_KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VDEVICE(KAWASAKI, PCI_DEVICE_ID_MCHIP_KL5A72002), 0 }, { } }; diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 4d794b42d6cd..45388d2ce2fd 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -13,13 +13,13 @@ #include <linux/i2c.h> #include <linux/log2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/v4l2-chip-ident.h> #include <media/soc_camera.h> /* mt9m001 i2c address 0x5d - * The platform has to define i2c_board_info - * and call i2c_register_board_info() */ + * The platform has to define ctruct i2c_board_info objects and link to them + * from struct soc_camera_link */ /* mt9m001 selected register addresses */ #define MT9M001_CHIP_VERSION 0x00 @@ -39,6 +39,13 @@ #define MT9M001_GLOBAL_GAIN 0x35 #define MT9M001_CHIP_ENABLE 0xF1 +#define MT9M001_MAX_WIDTH 1280 +#define MT9M001_MAX_HEIGHT 1024 +#define MT9M001_MIN_WIDTH 48 +#define MT9M001_MIN_HEIGHT 32 +#define MT9M001_COLUMN_SKIP 20 +#define MT9M001_ROW_SKIP 12 + static const struct soc_camera_data_format mt9m001_colour_formats[] = { /* Order important: first natively supported, * second supported with a GPIO extender */ @@ -69,12 +76,20 @@ static const struct soc_camera_data_format mt9m001_monochrome_formats[] = { }; struct mt9m001 { - struct i2c_client *client; - struct soc_camera_device icd; + struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ + __u32 fourcc; int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ + unsigned int gain; + unsigned int exposure; unsigned char autoexposure; }; +static struct mt9m001 *to_mt9m001(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9m001, subdev); +} + static int reg_read(struct i2c_client *client, const u8 reg) { s32 data = i2c_smbus_read_word_data(client, reg); @@ -109,35 +124,20 @@ static int reg_clear(struct i2c_client *client, const u8 reg, return reg_write(client, reg, ret & ~data); } -static int mt9m001_init(struct soc_camera_device *icd) +static int mt9m001_init(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct soc_camera_link *icl = client->dev.platform_data; int ret; - dev_dbg(icd->vdev->parent, "%s\n", __func__); + dev_dbg(&client->dev, "%s\n", __func__); - if (icl->power) { - ret = icl->power(&client->dev, 1); - if (ret < 0) { - dev_err(icd->vdev->parent, - "Platform failed to power-on the camera.\n"); - return ret; - } - } - - /* The camera could have been already on, we reset it additionally */ - if (icl->reset) - ret = icl->reset(&client->dev); - else - ret = -ENODEV; + /* + * We don't know, whether platform provides reset, issue a soft reset + * too. This returns all registers to their default values. + */ + ret = reg_write(client, MT9M001_RESET, 1); + if (!ret) + ret = reg_write(client, MT9M001_RESET, 0); - if (ret < 0) { - /* Either no platform reset, or platform reset failed */ - ret = reg_write(client, MT9M001_RESET, 1); - if (!ret) - ret = reg_write(client, MT9M001_RESET, 0); - } /* Disable chip, synchronous option update */ if (!ret) ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0); @@ -145,36 +145,12 @@ static int mt9m001_init(struct soc_camera_device *icd) return ret; } -static int mt9m001_release(struct soc_camera_device *icd) +static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable) { - struct i2c_client *client = to_i2c_client(icd->control); - struct soc_camera_link *icl = client->dev.platform_data; - - /* Disable the chip */ - reg_write(client, MT9M001_OUTPUT_CONTROL, 0); - - if (icl->power) - icl->power(&client->dev, 0); - - return 0; -} + struct i2c_client *client = sd->priv; -static int mt9m001_start_capture(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); - - /* Switch to master "normal" mode */ - if (reg_write(client, MT9M001_OUTPUT_CONTROL, 2) < 0) - return -EIO; - return 0; -} - -static int mt9m001_stop_capture(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); - - /* Stop sensor readout */ - if (reg_write(client, MT9M001_OUTPUT_CONTROL, 0) < 0) + /* Switch to master "normal" mode or stop sensor readout */ + if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0) return -EIO; return 0; } @@ -182,8 +158,7 @@ static int mt9m001_stop_capture(struct soc_camera_device *icd) static int mt9m001_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK; /* Only one width bit may be set */ @@ -205,8 +180,7 @@ static int mt9m001_set_bus_param(struct soc_camera_device *icd, static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); /* MT9M001 has all capture_format parameters fixed */ unsigned long flags = SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | @@ -220,13 +194,35 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) return soc_camera_apply_sensor_flags(icl, flags); } -static int mt9m001_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_rect rect = a->c; + struct soc_camera_device *icd = client->dev.platform_data; int ret; const u16 hblank = 9, vblank = 25; + unsigned int total_h; + + if (mt9m001->fourcc == V4L2_PIX_FMT_SBGGR8 || + mt9m001->fourcc == V4L2_PIX_FMT_SBGGR16) + /* + * Bayer format - even number of rows for simplicity, + * but let the user play with the top row. + */ + rect.height = ALIGN(rect.height, 2); + + /* Datasheet requirement: see register description */ + rect.width = ALIGN(rect.width, 2); + rect.left = ALIGN(rect.left, 2); + + soc_camera_limit_side(&rect.left, &rect.width, + MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT); + + total_h = rect.height + icd->y_skip_top + vblank; /* Blanking and start values - default... */ ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); @@ -236,66 +232,126 @@ static int mt9m001_set_crop(struct soc_camera_device *icd, /* The caller provides a supported format, as verified per * call to icd->try_fmt() */ if (!ret) - ret = reg_write(client, MT9M001_COLUMN_START, rect->left); + ret = reg_write(client, MT9M001_COLUMN_START, rect.left); if (!ret) - ret = reg_write(client, MT9M001_ROW_START, rect->top); + ret = reg_write(client, MT9M001_ROW_START, rect.top); if (!ret) - ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect->width - 1); + ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1); if (!ret) ret = reg_write(client, MT9M001_WINDOW_HEIGHT, - rect->height + icd->y_skip_top - 1); + rect.height + icd->y_skip_top - 1); if (!ret && mt9m001->autoexposure) { - ret = reg_write(client, MT9M001_SHUTTER_WIDTH, - rect->height + icd->y_skip_top + vblank); + ret = reg_write(client, MT9M001_SHUTTER_WIDTH, total_h); if (!ret) { 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)) / + mt9m001->exposure = (524 + (total_h - 1) * + (qctrl->maximum - qctrl->minimum)) / 1048 + qctrl->minimum; } } + if (!ret) + mt9m001->rect = rect; + return ret; } -static int mt9m001_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + + a->c = mt9m001->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = MT9M001_COLUMN_SKIP; + a->bounds.top = MT9M001_ROW_SKIP; + a->bounds.width = MT9M001_MAX_WIDTH; + a->bounds.height = MT9M001_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int mt9m001_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct v4l2_rect rect = { - .left = icd->x_current, - .top = icd->y_current, - .width = f->fmt.pix.width, - .height = f->fmt.pix.height, + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = mt9m001->rect.width; + pix->height = mt9m001->rect.height; + pix->pixelformat = mt9m001->fourcc; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int mt9m001_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_crop a = { + .c = { + .left = mt9m001->rect.left, + .top = mt9m001->rect.top, + .width = pix->width, + .height = pix->height, + }, }; + int ret; /* No support for scaling so far, just crop. TODO: use skipping */ - return mt9m001_set_crop(icd, &rect); + ret = mt9m001_s_crop(sd, &a); + if (!ret) { + pix->width = mt9m001->rect.width; + pix->height = mt9m001->rect.height; + mt9m001->fourcc = pix->pixelformat; + } + + return ret; } -static int mt9m001_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m001_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { + struct i2c_client *client = sd->priv; + struct soc_camera_device *icd = client->dev.platform_data; struct v4l2_pix_format *pix = &f->fmt.pix; - v4l_bound_align_image(&pix->width, 48, 1280, 1, - &pix->height, 32 + icd->y_skip_top, - 1024 + icd->y_skip_top, 0, 0); + v4l_bound_align_image(&pix->width, MT9M001_MIN_WIDTH, + MT9M001_MAX_WIDTH, 1, + &pix->height, MT9M001_MIN_HEIGHT + icd->y_skip_top, + MT9M001_MAX_HEIGHT + icd->y_skip_top, 0, 0); + + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8 || + pix->pixelformat == V4L2_PIX_FMT_SBGGR16) + pix->height = ALIGN(pix->height - 1, 2); return 0; } -static int mt9m001_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) +static int mt9m001_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match.addr != mt9m001->client->addr) + if (id->match.addr != client->addr) return -ENODEV; id->ident = mt9m001->model; @@ -305,10 +361,10 @@ static int mt9m001_get_chip_id(struct soc_camera_device *icd, } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9m001_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9m001_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -325,10 +381,10 @@ static int mt9m001_get_register(struct soc_camera_device *icd, return 0; } -static int mt9m001_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9m001_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -381,39 +437,17 @@ static const struct v4l2_queryctrl mt9m001_controls[] = { } }; -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_crop = mt9m001_set_crop, - .set_fmt = mt9m001_set_fmt, - .try_fmt = mt9m001_try_fmt, .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) +static int mt9m001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); int data; switch (ctrl->id) { @@ -426,14 +460,21 @@ static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_contro case V4L2_CID_EXPOSURE_AUTO: ctrl->value = mt9m001->autoexposure; break; + case V4L2_CID_GAIN: + ctrl->value = mt9m001->gain; + break; + case V4L2_CID_EXPOSURE: + ctrl->value = mt9m001->exposure; + break; } return 0; } -static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = sd->priv; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct soc_camera_device *icd = client->dev.platform_data; const struct v4l2_queryctrl *qctrl; int data; @@ -460,7 +501,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro 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); + dev_dbg(&client->dev, "Setting gain %d\n", data); data = reg_write(client, MT9M001_GLOBAL_GAIN, data); if (data < 0) return -EIO; @@ -478,7 +519,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro else data = ((gain - 64) * 7 + 28) / 56 + 96; - dev_dbg(&icd->dev, "Setting gain from %d to %d\n", + dev_dbg(&client->dev, "Setting gain from %d to %d\n", reg_read(client, MT9M001_GLOBAL_GAIN), data); data = reg_write(client, MT9M001_GLOBAL_GAIN, data); if (data < 0) @@ -486,7 +527,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro } /* Success */ - icd->gain = ctrl->value; + mt9m001->gain = ctrl->value; break; case V4L2_CID_EXPOSURE: /* mt9m001 has maximum == default */ @@ -497,23 +538,27 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro 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(client, MT9M001_SHUTTER_WIDTH), shutter); + dev_dbg(&client->dev, + "Setting shutter width from %d to %lu\n", + reg_read(client, MT9M001_SHUTTER_WIDTH), + shutter); if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0) return -EIO; - icd->exposure = ctrl->value; + mt9m001->exposure = ctrl->value; mt9m001->autoexposure = 0; } break; case V4L2_CID_EXPOSURE_AUTO: if (ctrl->value) { const u16 vblank = 25; - if (reg_write(client, MT9M001_SHUTTER_WIDTH, icd->height + - icd->y_skip_top + vblank) < 0) + unsigned int total_h = mt9m001->rect.height + + icd->y_skip_top + vblank; + if (reg_write(client, MT9M001_SHUTTER_WIDTH, + total_h) < 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)) / + mt9m001->exposure = (524 + (total_h - 1) * + (qctrl->maximum - qctrl->minimum)) / 1048 + qctrl->minimum; mt9m001->autoexposure = 1; } else @@ -525,14 +570,14 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro /* 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) +static int mt9m001_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct soc_camera_link *icl = client->dev.platform_data; + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct soc_camera_link *icl = to_soc_camera_link(icd); s32 data; - int ret; unsigned long flags; + int ret; /* We must have a parent by now. And it cannot be a wrong one. * So this entire test is completely redundant. */ @@ -542,7 +587,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) /* Enable the chip */ data = reg_write(client, MT9M001_CHIP_ENABLE, 1); - dev_dbg(&icd->dev, "write: %d\n", data); + dev_dbg(&client->dev, "write: %d\n", data); /* Read out the chip version register */ data = reg_read(client, MT9M001_CHIP_VERSION); @@ -559,10 +604,9 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) icd->formats = mt9m001_monochrome_formats; break; default: - ret = -ENODEV; - dev_err(&icd->dev, + dev_err(&client->dev, "No MT9M001 chip detected, register read %x\n", data); - goto ei2c; + return -ENODEV; } icd->num_formats = 0; @@ -585,42 +629,72 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) if (flags & SOCAM_DATAWIDTH_8) icd->num_formats++; - dev_info(&icd->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, + mt9m001->fourcc = icd->formats->fourcc; + + dev_info(&client->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; + ret = mt9m001_init(client); + if (ret < 0) + dev_err(&client->dev, "Failed to initialise the camera\n"); - return 0; + /* mt9m001_init() has reset the chip, returning registers to defaults */ + mt9m001->gain = 64; + mt9m001->exposure = 255; -eisis: -ei2c: return ret; } static void mt9m001_video_remove(struct soc_camera_device *icd) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); - dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr, + dev_dbg(&icd->dev, "Video removed: %p, %p\n", icd->dev.parent, icd->vdev); - soc_camera_video_stop(icd); if (icl->free_bus) icl->free_bus(icl); } +static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = { + .g_ctrl = mt9m001_g_ctrl, + .s_ctrl = mt9m001_s_ctrl, + .g_chip_ident = mt9m001_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9m001_g_register, + .s_register = mt9m001_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { + .s_stream = mt9m001_s_stream, + .s_fmt = mt9m001_s_fmt, + .g_fmt = mt9m001_g_fmt, + .try_fmt = mt9m001_try_fmt, + .s_crop = mt9m001_s_crop, + .g_crop = mt9m001_g_crop, + .cropcap = mt9m001_cropcap, +}; + +static struct v4l2_subdev_ops mt9m001_subdev_ops = { + .core = &mt9m001_subdev_core_ops, + .video = &mt9m001_subdev_video_ops, +}; + static int mt9m001_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9m001 *mt9m001; - struct soc_camera_device *icd; + struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = client->dev.platform_data; + struct soc_camera_link *icl; int ret; + if (!icd) { + dev_err(&client->dev, "MT9M001: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9M001 driver needs platform data\n"); return -EINVAL; @@ -636,43 +710,40 @@ static int mt9m001_probe(struct i2c_client *client, if (!mt9m001) return -ENOMEM; - mt9m001->client = client; - i2c_set_clientdata(client, mt9m001); + v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops); /* 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; + icd->ops = &mt9m001_ops; + icd->y_skip_top = 0; + + mt9m001->rect.left = MT9M001_COLUMN_SKIP; + mt9m001->rect.top = MT9M001_ROW_SKIP; + mt9m001->rect.width = MT9M001_MAX_WIDTH; + mt9m001->rect.height = MT9M001_MAX_HEIGHT; + /* Simulated autoexposure. If enabled, we calculate shutter width * ourselves in the driver based on vertical blanking and frame width */ mt9m001->autoexposure = 1; - ret = soc_camera_device_register(icd); - if (ret) - goto eisdr; - - return 0; + ret = mt9m001_video_probe(icd, client); + if (ret) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(mt9m001); + } -eisdr: - kfree(mt9m001); return ret; } static int mt9m001_remove(struct i2c_client *client) { - struct mt9m001 *mt9m001 = i2c_get_clientdata(client); + struct mt9m001 *mt9m001 = to_mt9m001(client); + struct soc_camera_device *icd = client->dev.platform_data; - soc_camera_device_unregister(&mt9m001->icd); + icd->ops = NULL; + mt9m001_video_remove(icd); + i2c_set_clientdata(client, NULL); + client->driver = NULL; kfree(mt9m001); return 0; diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index fc5e2de03766..90da699601ea 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -148,12 +148,12 @@ enum mt9m111_context { }; struct mt9m111 { - struct i2c_client *client; - struct soc_camera_device icd; + struct v4l2_subdev subdev; int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */ enum mt9m111_context context; struct v4l2_rect rect; u32 pixfmt; + unsigned int gain; unsigned char autoexposure; unsigned char datawidth; unsigned int powered:1; @@ -166,6 +166,11 @@ struct mt9m111 { unsigned int autowhitebalance:1; }; +static struct mt9m111 *to_mt9m111(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9m111, subdev); +} + static int reg_page_map_set(struct i2c_client *client, const u16 reg) { int ret; @@ -190,7 +195,7 @@ static int mt9m111_reg_read(struct i2c_client *client, const u16 reg) ret = reg_page_map_set(client, reg); if (!ret) - ret = swab16(i2c_smbus_read_word_data(client, (reg & 0xff))); + ret = swab16(i2c_smbus_read_word_data(client, reg & 0xff)); dev_dbg(&client->dev, "read reg.%03x -> %04x\n", reg, ret); return ret; @@ -203,7 +208,7 @@ static int mt9m111_reg_write(struct i2c_client *client, const u16 reg, ret = reg_page_map_set(client, reg); if (!ret) - ret = i2c_smbus_write_word_data(client, (reg & 0xff), + ret = i2c_smbus_write_word_data(client, reg & 0xff, swab16(data)); dev_dbg(&client->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret); return ret; @@ -229,10 +234,9 @@ static int mt9m111_reg_clear(struct i2c_client *client, const u16 reg, return mt9m111_reg_write(client, reg, ret & ~data); } -static int mt9m111_set_context(struct soc_camera_device *icd, +static int mt9m111_set_context(struct i2c_client *client, enum mt9m111_context ctxt) { - struct i2c_client *client = to_i2c_client(icd->control); int valB = MT9M111_CTXT_CTRL_RESTART | MT9M111_CTXT_CTRL_DEFECTCOR_B | MT9M111_CTXT_CTRL_RESIZE_B | MT9M111_CTXT_CTRL_CTRL2_B | MT9M111_CTXT_CTRL_GAMMA_B | MT9M111_CTXT_CTRL_READ_MODE_B @@ -246,17 +250,16 @@ static int mt9m111_set_context(struct soc_camera_device *icd, return reg_write(CONTEXT_CONTROL, valA); } -static int mt9m111_setup_rect(struct soc_camera_device *icd, +static int mt9m111_setup_rect(struct i2c_client *client, struct v4l2_rect *rect) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret, is_raw_format; int width = rect->width; int height = rect->height; - if ((mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8) - || (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16)) + if (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8 || + mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16) is_raw_format = 1; else is_raw_format = 0; @@ -292,9 +295,8 @@ static int mt9m111_setup_rect(struct soc_camera_device *icd, return ret; } -static int mt9m111_setup_pixfmt(struct soc_camera_device *icd, u16 outfmt) +static int mt9m111_setup_pixfmt(struct i2c_client *client, u16 outfmt) { - struct i2c_client *client = to_i2c_client(icd->control); int ret; ret = reg_write(OUTPUT_FORMAT_CTRL2_A, outfmt); @@ -303,19 +305,19 @@ static int mt9m111_setup_pixfmt(struct soc_camera_device *icd, u16 outfmt) return ret; } -static int mt9m111_setfmt_bayer8(struct soc_camera_device *icd) +static int mt9m111_setfmt_bayer8(struct i2c_client *client) { - return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_PROCESSED_BAYER); + return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_PROCESSED_BAYER); } -static int mt9m111_setfmt_bayer10(struct soc_camera_device *icd) +static int mt9m111_setfmt_bayer10(struct i2c_client *client) { - return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_BYPASS_IFP); + return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_BYPASS_IFP); } -static int mt9m111_setfmt_rgb565(struct soc_camera_device *icd) +static int mt9m111_setfmt_rgb565(struct i2c_client *client) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int val = 0; if (mt9m111->swap_rgb_red_blue) @@ -324,12 +326,12 @@ static int mt9m111_setfmt_rgb565(struct soc_camera_device *icd) val |= MT9M111_OUTFMT_SWAP_RGB_EVEN; val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565; - return mt9m111_setup_pixfmt(icd, val); + return mt9m111_setup_pixfmt(client, val); } -static int mt9m111_setfmt_rgb555(struct soc_camera_device *icd) +static int mt9m111_setfmt_rgb555(struct i2c_client *client) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int val = 0; if (mt9m111->swap_rgb_red_blue) @@ -338,12 +340,12 @@ static int mt9m111_setfmt_rgb555(struct soc_camera_device *icd) val |= MT9M111_OUTFMT_SWAP_RGB_EVEN; val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555; - return mt9m111_setup_pixfmt(icd, val); + return mt9m111_setup_pixfmt(client, val); } -static int mt9m111_setfmt_yuv(struct soc_camera_device *icd) +static int mt9m111_setfmt_yuv(struct i2c_client *client) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int val = 0; if (mt9m111->swap_yuv_cb_cr) @@ -351,52 +353,22 @@ static int mt9m111_setfmt_yuv(struct soc_camera_device *icd) if (mt9m111->swap_yuv_y_chromas) val |= MT9M111_OUTFMT_SWAP_YCbCr_C_Y; - return mt9m111_setup_pixfmt(icd, val); + return mt9m111_setup_pixfmt(client, val); } -static int mt9m111_enable(struct soc_camera_device *icd) +static int mt9m111_enable(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - struct soc_camera_link *icl = client->dev.platform_data; + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; - if (icl->power) { - ret = icl->power(&client->dev, 1); - if (ret < 0) { - dev_err(icd->vdev->parent, - "Platform failed to power-on the camera.\n"); - return ret; - } - } - ret = reg_set(RESET, MT9M111_RESET_CHIP_ENABLE); if (!ret) mt9m111->powered = 1; return ret; } -static int mt9m111_disable(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - struct soc_camera_link *icl = client->dev.platform_data; - int ret; - - ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE); - if (!ret) - mt9m111->powered = 0; - - if (icl->power) - icl->power(&client->dev, 0); - - return ret; -} - -static int mt9m111_reset(struct soc_camera_device *icd) +static int mt9m111_reset(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct soc_camera_link *icl = client->dev.platform_data; int ret; ret = reg_set(RESET, MT9M111_RESET_RESET_MODE); @@ -406,26 +378,12 @@ static int mt9m111_reset(struct soc_camera_device *icd) ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE | MT9M111_RESET_RESET_SOC); - if (icl->reset) - icl->reset(&client->dev); - return ret; } -static int mt9m111_start_capture(struct soc_camera_device *icd) -{ - return 0; -} - -static int mt9m111_stop_capture(struct soc_camera_device *icd) -{ - return 0; -} - static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - struct soc_camera_link *icl = mt9m111->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; @@ -438,62 +396,126 @@ static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f) return 0; } -static int mt9m111_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int mt9m111_make_rect(struct i2c_client *client, + struct v4l2_rect *rect) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); + + if (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8 || + mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16) { + /* Bayer format - even size lengths */ + rect->width = ALIGN(rect->width, 2); + rect->height = ALIGN(rect->height, 2); + /* Let the user play with the starting pixel */ + } + + /* FIXME: the datasheet doesn't specify minimum sizes */ + soc_camera_limit_side(&rect->left, &rect->width, + MT9M111_MIN_DARK_COLS, 2, MT9M111_MAX_WIDTH); + + soc_camera_limit_side(&rect->top, &rect->height, + MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT); + + return mt9m111_setup_rect(client, rect); +} + +static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct v4l2_rect rect = a->c; + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; - dev_dbg(&icd->dev, "%s left=%d, top=%d, width=%d, height=%d\n", - __func__, rect->left, rect->top, rect->width, - rect->height); + dev_dbg(&client->dev, "%s left=%d, top=%d, width=%d, height=%d\n", + __func__, rect.left, rect.top, rect.width, rect.height); - ret = mt9m111_setup_rect(icd, rect); + ret = mt9m111_make_rect(client, &rect); if (!ret) - mt9m111->rect = *rect; + mt9m111->rect = rect; return ret; } -static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) +static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); + + a->c = mt9m111->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = MT9M111_MIN_DARK_COLS; + a->bounds.top = MT9M111_MIN_DARK_ROWS; + a->bounds.width = MT9M111_MAX_WIDTH; + a->bounds.height = MT9M111_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int mt9m111_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = mt9m111->rect.width; + pix->height = mt9m111->rect.height; + pix->pixelformat = mt9m111->pixfmt; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int mt9m111_set_pixfmt(struct i2c_client *client, u32 pixfmt) +{ + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; switch (pixfmt) { case V4L2_PIX_FMT_SBGGR8: - ret = mt9m111_setfmt_bayer8(icd); + ret = mt9m111_setfmt_bayer8(client); break; case V4L2_PIX_FMT_SBGGR16: - ret = mt9m111_setfmt_bayer10(icd); + ret = mt9m111_setfmt_bayer10(client); break; case V4L2_PIX_FMT_RGB555: - ret = mt9m111_setfmt_rgb555(icd); + ret = mt9m111_setfmt_rgb555(client); break; case V4L2_PIX_FMT_RGB565: - ret = mt9m111_setfmt_rgb565(icd); + ret = mt9m111_setfmt_rgb565(client); break; case V4L2_PIX_FMT_UYVY: mt9m111->swap_yuv_y_chromas = 0; mt9m111->swap_yuv_cb_cr = 0; - ret = mt9m111_setfmt_yuv(icd); + ret = mt9m111_setfmt_yuv(client); break; case V4L2_PIX_FMT_VYUY: mt9m111->swap_yuv_y_chromas = 0; mt9m111->swap_yuv_cb_cr = 1; - ret = mt9m111_setfmt_yuv(icd); + ret = mt9m111_setfmt_yuv(client); break; case V4L2_PIX_FMT_YUYV: mt9m111->swap_yuv_y_chromas = 1; mt9m111->swap_yuv_cb_cr = 0; - ret = mt9m111_setfmt_yuv(icd); + ret = mt9m111_setfmt_yuv(client); break; case V4L2_PIX_FMT_YVYU: mt9m111->swap_yuv_y_chromas = 1; mt9m111->swap_yuv_cb_cr = 1; - ret = mt9m111_setfmt_yuv(icd); + ret = mt9m111_setfmt_yuv(client); break; default: - dev_err(&icd->dev, "Pixel format not handled : %x\n", pixfmt); + dev_err(&client->dev, "Pixel format not handled : %x\n", + pixfmt); ret = -EINVAL; } @@ -503,10 +525,10 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) return ret; } -static int mt9m111_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m111_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_rect rect = { .left = mt9m111->rect.left, @@ -516,40 +538,56 @@ static int mt9m111_set_fmt(struct soc_camera_device *icd, }; int ret; - dev_dbg(&icd->dev, "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n", - __func__, pix->pixelformat, rect.left, rect.top, rect.width, - rect.height); + dev_dbg(&client->dev, + "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n", __func__, + pix->pixelformat, rect.left, rect.top, rect.width, rect.height); - ret = mt9m111_setup_rect(icd, &rect); + ret = mt9m111_make_rect(client, &rect); if (!ret) - ret = mt9m111_set_pixfmt(icd, pix->pixelformat); + ret = mt9m111_set_pixfmt(client, pix->pixelformat); if (!ret) mt9m111->rect = rect; return ret; } -static int mt9m111_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9m111_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; + bool bayer = pix->pixelformat == V4L2_PIX_FMT_SBGGR8 || + pix->pixelformat == V4L2_PIX_FMT_SBGGR16; + + /* + * With Bayer format enforce even side lengths, but let the user play + * with the starting pixel + */ if (pix->height > MT9M111_MAX_HEIGHT) pix->height = MT9M111_MAX_HEIGHT; + else if (pix->height < 2) + pix->height = 2; + else if (bayer) + pix->height = ALIGN(pix->height, 2); + if (pix->width > MT9M111_MAX_WIDTH) pix->width = MT9M111_MAX_WIDTH; + else if (pix->width < 2) + pix->width = 2; + else if (bayer) + pix->width = ALIGN(pix->width, 2); return 0; } -static int mt9m111_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) +static int mt9m111_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match.addr != mt9m111->client->addr) + if (id->match.addr != client->addr) return -ENODEV; id->ident = mt9m111->model; @@ -559,11 +597,11 @@ static int mt9m111_get_chip_id(struct soc_camera_device *icd, } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9m111_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9m111_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { + struct i2c_client *client = sd->priv; int val; - struct i2c_client *client = to_i2c_client(icd->control); if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) return -EINVAL; @@ -580,10 +618,10 @@ static int mt9m111_get_register(struct soc_camera_device *icd, return 0; } -static int mt9m111_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9m111_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) return -EINVAL; @@ -635,45 +673,21 @@ static const struct v4l2_queryctrl mt9m111_controls[] = { } }; -static int mt9m111_video_probe(struct soc_camera_device *); -static void mt9m111_video_remove(struct soc_camera_device *); -static int mt9m111_get_control(struct soc_camera_device *, - struct v4l2_control *); -static int mt9m111_set_control(struct soc_camera_device *, - struct v4l2_control *); static int mt9m111_resume(struct soc_camera_device *icd); -static int mt9m111_init(struct soc_camera_device *icd); -static int mt9m111_release(struct soc_camera_device *icd); +static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state); static struct soc_camera_ops mt9m111_ops = { - .owner = THIS_MODULE, - .probe = mt9m111_video_probe, - .remove = mt9m111_video_remove, - .init = mt9m111_init, + .suspend = mt9m111_suspend, .resume = mt9m111_resume, - .release = mt9m111_release, - .start_capture = mt9m111_start_capture, - .stop_capture = mt9m111_stop_capture, - .set_crop = mt9m111_set_crop, - .set_fmt = mt9m111_set_fmt, - .try_fmt = mt9m111_try_fmt, .query_bus_param = mt9m111_query_bus_param, .set_bus_param = mt9m111_set_bus_param, .controls = mt9m111_controls, .num_controls = ARRAY_SIZE(mt9m111_controls), - .get_control = mt9m111_get_control, - .set_control = mt9m111_set_control, - .get_chip_id = mt9m111_get_chip_id, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .get_register = mt9m111_get_register, - .set_register = mt9m111_set_register, -#endif }; -static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask) +static int mt9m111_set_flip(struct i2c_client *client, int flip, int mask) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; if (mt9m111->context == HIGHPOWER) { @@ -691,9 +705,8 @@ static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask) return ret; } -static int mt9m111_get_global_gain(struct soc_camera_device *icd) +static int mt9m111_get_global_gain(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); int data; data = reg_read(GLOBAL_GAIN); @@ -703,15 +716,15 @@ static int mt9m111_get_global_gain(struct soc_camera_device *icd) return data; } -static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain) +static int mt9m111_set_global_gain(struct i2c_client *client, int gain) { - struct i2c_client *client = to_i2c_client(icd->control); + struct mt9m111 *mt9m111 = to_mt9m111(client); u16 val; if (gain > 63 * 2 * 2) return -EINVAL; - icd->gain = gain; + mt9m111->gain = gain; if ((gain >= 64 * 2) && (gain < 63 * 2 * 2)) val = (1 << 10) | (1 << 9) | (gain / 4); else if ((gain >= 64) && (gain < 64 * 2)) @@ -722,10 +735,9 @@ static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain) return reg_write(GLOBAL_GAIN, val); } -static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on) +static int mt9m111_set_autoexposure(struct i2c_client *client, int on) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; if (on) @@ -739,10 +751,9 @@ static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on) return ret; } -static int mt9m111_set_autowhitebalance(struct soc_camera_device *icd, int on) +static int mt9m111_set_autowhitebalance(struct i2c_client *client, int on) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; if (on) @@ -756,11 +767,10 @@ static int mt9m111_set_autowhitebalance(struct soc_camera_device *icd, int on) return ret; } -static int mt9m111_get_control(struct soc_camera_device *icd, - struct v4l2_control *ctrl) +static int mt9m111_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); int data; switch (ctrl->id) { @@ -785,7 +795,7 @@ static int mt9m111_get_control(struct soc_camera_device *icd, ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS); break; case V4L2_CID_GAIN: - data = mt9m111_get_global_gain(icd); + data = mt9m111_get_global_gain(client); if (data < 0) return data; ctrl->value = data; @@ -800,37 +810,36 @@ static int mt9m111_get_control(struct soc_camera_device *icd, return 0; } -static int mt9m111_set_control(struct soc_camera_device *icd, - struct v4l2_control *ctrl) +static int mt9m111_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = sd->priv; + struct mt9m111 *mt9m111 = to_mt9m111(client); const struct v4l2_queryctrl *qctrl; int ret; qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id); - if (!qctrl) return -EINVAL; switch (ctrl->id) { case V4L2_CID_VFLIP: mt9m111->vflip = ctrl->value; - ret = mt9m111_set_flip(icd, ctrl->value, + ret = mt9m111_set_flip(client, ctrl->value, MT9M111_RMB_MIRROR_ROWS); break; case V4L2_CID_HFLIP: mt9m111->hflip = ctrl->value; - ret = mt9m111_set_flip(icd, ctrl->value, + ret = mt9m111_set_flip(client, ctrl->value, MT9M111_RMB_MIRROR_COLS); break; case V4L2_CID_GAIN: - ret = mt9m111_set_global_gain(icd, ctrl->value); + ret = mt9m111_set_global_gain(client, ctrl->value); break; case V4L2_CID_EXPOSURE_AUTO: - ret = mt9m111_set_autoexposure(icd, ctrl->value); + ret = mt9m111_set_autoexposure(client, ctrl->value); break; case V4L2_CID_AUTO_WHITE_BALANCE: - ret = mt9m111_set_autowhitebalance(icd, ctrl->value); + ret = mt9m111_set_autowhitebalance(client, ctrl->value); break; default: ret = -EINVAL; @@ -839,62 +848,62 @@ static int mt9m111_set_control(struct soc_camera_device *icd, return ret; } -static int mt9m111_restore_state(struct soc_camera_device *icd) +static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - - mt9m111_set_context(icd, mt9m111->context); - mt9m111_set_pixfmt(icd, mt9m111->pixfmt); - mt9m111_setup_rect(icd, &mt9m111->rect); - mt9m111_set_flip(icd, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS); - mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS); - mt9m111_set_global_gain(icd, icd->gain); - mt9m111_set_autoexposure(icd, mt9m111->autoexposure); - mt9m111_set_autowhitebalance(icd, mt9m111->autowhitebalance); + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct mt9m111 *mt9m111 = to_mt9m111(client); + + mt9m111->gain = mt9m111_get_global_gain(client); + + return 0; +} + +static int mt9m111_restore_state(struct i2c_client *client) +{ + struct mt9m111 *mt9m111 = to_mt9m111(client); + + mt9m111_set_context(client, mt9m111->context); + mt9m111_set_pixfmt(client, mt9m111->pixfmt); + mt9m111_setup_rect(client, &mt9m111->rect); + mt9m111_set_flip(client, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS); + mt9m111_set_flip(client, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS); + mt9m111_set_global_gain(client, mt9m111->gain); + mt9m111_set_autoexposure(client, mt9m111->autoexposure); + mt9m111_set_autowhitebalance(client, mt9m111->autowhitebalance); return 0; } static int mt9m111_resume(struct soc_camera_device *icd) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret = 0; if (mt9m111->powered) { - ret = mt9m111_enable(icd); + ret = mt9m111_enable(client); if (!ret) - ret = mt9m111_reset(icd); + ret = mt9m111_reset(client); if (!ret) - ret = mt9m111_restore_state(icd); + ret = mt9m111_restore_state(client); } return ret; } -static int mt9m111_init(struct soc_camera_device *icd) +static int mt9m111_init(struct i2c_client *client) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); int ret; mt9m111->context = HIGHPOWER; - ret = mt9m111_enable(icd); + ret = mt9m111_enable(client); if (!ret) - ret = mt9m111_reset(icd); + ret = mt9m111_reset(client); if (!ret) - ret = mt9m111_set_context(icd, mt9m111->context); + ret = mt9m111_set_context(client, mt9m111->context); if (!ret) - ret = mt9m111_set_autoexposure(icd, mt9m111->autoexposure); + ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure); if (ret) - dev_err(&icd->dev, "mt9m11x init failed: %d\n", ret); - return ret; -} - -static int mt9m111_release(struct soc_camera_device *icd) -{ - int ret; - - ret = mt9m111_disable(icd); - if (ret < 0) - dev_err(&icd->dev, "mt9m11x release failed: %d\n", ret); - + dev_err(&client->dev, "mt9m11x init failed: %d\n", ret); return ret; } @@ -902,10 +911,10 @@ static int mt9m111_release(struct soc_camera_device *icd) * 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 mt9m111_video_probe(struct soc_camera_device *icd) +static int mt9m111_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); s32 data; int ret; @@ -917,10 +926,13 @@ static int mt9m111_video_probe(struct soc_camera_device *icd) to_soc_camera_host(icd->dev.parent)->nr != icd->iface) return -ENODEV; - ret = mt9m111_enable(icd); - if (ret) - goto ei2c; - ret = mt9m111_reset(icd); + mt9m111->autoexposure = 1; + mt9m111->autowhitebalance = 1; + + mt9m111->swap_rgb_even_odd = 1; + mt9m111->swap_rgb_red_blue = 1; + + ret = mt9m111_init(client); if (ret) goto ei2c; @@ -935,7 +947,7 @@ static int mt9m111_video_probe(struct soc_camera_device *icd) break; default: ret = -ENODEV; - dev_err(&icd->dev, + dev_err(&client->dev, "No MT9M11x chip detected, register read %x\n", data); goto ei2c; } @@ -943,42 +955,51 @@ static int mt9m111_video_probe(struct soc_camera_device *icd) icd->formats = mt9m111_colour_formats; icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats); - dev_info(&icd->dev, "Detected a MT9M11x chip ID %x\n", data); + dev_info(&client->dev, "Detected a MT9M11x chip ID %x\n", data); - ret = soc_camera_video_start(icd); - if (ret) - goto eisis; - - mt9m111->autoexposure = 1; - mt9m111->autowhitebalance = 1; - - mt9m111->swap_rgb_even_odd = 1; - mt9m111->swap_rgb_red_blue = 1; - - return 0; -eisis: ei2c: return ret; } -static void mt9m111_video_remove(struct soc_camera_device *icd) -{ - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); +static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = { + .g_ctrl = mt9m111_g_ctrl, + .s_ctrl = mt9m111_s_ctrl, + .g_chip_ident = mt9m111_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9m111_g_register, + .s_register = mt9m111_s_register, +#endif +}; - dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m111->client->addr, - mt9m111->icd.dev.parent, mt9m111->icd.vdev); - soc_camera_video_stop(&mt9m111->icd); -} +static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { + .s_fmt = mt9m111_s_fmt, + .g_fmt = mt9m111_g_fmt, + .try_fmt = mt9m111_try_fmt, + .s_crop = mt9m111_s_crop, + .g_crop = mt9m111_g_crop, + .cropcap = mt9m111_cropcap, +}; + +static struct v4l2_subdev_ops mt9m111_subdev_ops = { + .core = &mt9m111_subdev_core_ops, + .video = &mt9m111_subdev_video_ops, +}; static int mt9m111_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9m111 *mt9m111; - struct soc_camera_device *icd; + struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = client->dev.platform_data; + struct soc_camera_link *icl; int ret; + if (!icd) { + dev_err(&client->dev, "MT9M11x: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9M11x driver needs platform data\n"); return -EINVAL; @@ -994,38 +1015,35 @@ static int mt9m111_probe(struct i2c_client *client, if (!mt9m111) return -ENOMEM; - mt9m111->client = client; - i2c_set_clientdata(client, mt9m111); + v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops); /* Second stage probe - when a capture adapter is there */ - icd = &mt9m111->icd; - icd->ops = &mt9m111_ops; - icd->control = &client->dev; - icd->x_min = MT9M111_MIN_DARK_COLS; - icd->y_min = MT9M111_MIN_DARK_ROWS; - icd->x_current = icd->x_min; - icd->y_current = icd->y_min; - icd->width_min = MT9M111_MIN_DARK_ROWS; - icd->width_max = MT9M111_MAX_WIDTH; - icd->height_min = MT9M111_MIN_DARK_COLS; - icd->height_max = MT9M111_MAX_HEIGHT; - icd->y_skip_top = 0; - icd->iface = icl->bus_id; - - ret = soc_camera_device_register(icd); - if (ret) - goto eisdr; - return 0; + icd->ops = &mt9m111_ops; + icd->y_skip_top = 0; + + mt9m111->rect.left = MT9M111_MIN_DARK_COLS; + mt9m111->rect.top = MT9M111_MIN_DARK_ROWS; + mt9m111->rect.width = MT9M111_MAX_WIDTH; + mt9m111->rect.height = MT9M111_MAX_HEIGHT; + + ret = mt9m111_video_probe(icd, client); + if (ret) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(mt9m111); + } -eisdr: - kfree(mt9m111); return ret; } static int mt9m111_remove(struct i2c_client *client) { - struct mt9m111 *mt9m111 = i2c_get_clientdata(client); - soc_camera_device_unregister(&mt9m111->icd); + struct mt9m111 *mt9m111 = to_mt9m111(client); + struct soc_camera_device *icd = client->dev.platform_data; + + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + client->driver = NULL; kfree(mt9m111); return 0; diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 4207fb342670..6966f644977e 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -13,13 +13,13 @@ #include <linux/i2c.h> #include <linux/log2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/v4l2-chip-ident.h> #include <media/soc_camera.h> /* mt9t031 i2c address 0x5d - * The platform has to define i2c_board_info - * and call i2c_register_board_info() */ + * The platform has to define i2c_board_info and link to it from + * struct soc_camera_link */ /* mt9t031 selected register addresses */ #define MT9T031_CHIP_VERSION 0x00 @@ -47,7 +47,7 @@ #define MT9T031_MAX_HEIGHT 1536 #define MT9T031_MAX_WIDTH 2048 #define MT9T031_MIN_HEIGHT 2 -#define MT9T031_MIN_WIDTH 2 +#define MT9T031_MIN_WIDTH 18 #define MT9T031_HORIZONTAL_BLANK 142 #define MT9T031_VERTICAL_BLANK 25 #define MT9T031_COLUMN_SKIP 32 @@ -68,14 +68,21 @@ static const struct soc_camera_data_format mt9t031_colour_formats[] = { }; struct mt9t031 { - struct i2c_client *client; - struct soc_camera_device icd; + struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */ - unsigned char autoexposure; u16 xskip; u16 yskip; + unsigned int gain; + unsigned int exposure; + unsigned char autoexposure; }; +static struct mt9t031 *to_mt9t031(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9t031, subdev); +} + static int reg_read(struct i2c_client *client, const u8 reg) { s32 data = i2c_smbus_read_word_data(client, reg); @@ -136,21 +143,10 @@ static int get_shutter(struct i2c_client *client, u32 *data) return ret < 0 ? ret : 0; } -static int mt9t031_init(struct soc_camera_device *icd) +static int mt9t031_idle(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct soc_camera_link *icl = client->dev.platform_data; int ret; - if (icl->power) { - ret = icl->power(&client->dev, 1); - if (ret < 0) { - dev_err(icd->vdev->parent, - "Platform failed to power-on the camera.\n"); - return ret; - } - } - /* Disable chip output, synchronous option update */ ret = reg_write(client, MT9T031_RESET, 1); if (ret >= 0) @@ -158,50 +154,39 @@ static int mt9t031_init(struct soc_camera_device *icd) if (ret >= 0) ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - if (ret < 0 && icl->power) - icl->power(&client->dev, 0); - return ret >= 0 ? 0 : -EIO; } -static int mt9t031_release(struct soc_camera_device *icd) +static int mt9t031_disable(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct soc_camera_link *icl = client->dev.platform_data; - /* Disable the chip */ reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - if (icl->power) - icl->power(&client->dev, 0); - return 0; } -static int mt9t031_start_capture(struct soc_camera_device *icd) +static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable) { - struct i2c_client *client = to_i2c_client(icd->control); - - /* Switch to master "normal" mode */ - if (reg_set(client, MT9T031_OUTPUT_CONTROL, 2) < 0) - return -EIO; - return 0; -} + struct i2c_client *client = sd->priv; + int ret; -static int mt9t031_stop_capture(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); + if (enable) + /* Switch to master "normal" mode */ + ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2); + else + /* Stop sensor readout */ + ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); - /* Stop sensor readout */ - if (reg_clear(client, MT9T031_OUTPUT_CONTROL, 2) < 0) + if (ret < 0) return -EIO; + return 0; } static int mt9t031_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); /* The caller should have queried our parameters, check anyway */ if (flags & ~MT9T031_BUS_PARAM) @@ -217,69 +202,73 @@ static int mt9t031_set_bus_param(struct soc_camera_device *icd, static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); - struct soc_camera_link *icl = mt9t031->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM); } -/* Round up minima and round down maxima */ -static void recalculate_limits(struct soc_camera_device *icd, - u16 xskip, u16 yskip) +/* target must be _even_ */ +static u16 mt9t031_skip(s32 *source, s32 target, s32 max) { - icd->x_min = (MT9T031_COLUMN_SKIP + xskip - 1) / xskip; - icd->y_min = (MT9T031_ROW_SKIP + yskip - 1) / yskip; - icd->width_min = (MT9T031_MIN_WIDTH + xskip - 1) / xskip; - icd->height_min = (MT9T031_MIN_HEIGHT + yskip - 1) / yskip; - icd->width_max = MT9T031_MAX_WIDTH / xskip; - icd->height_max = MT9T031_MAX_HEIGHT / yskip; + unsigned int skip; + + if (*source < target + target / 2) { + *source = target; + return 1; + } + + skip = min(max, *source + target / 2) / target; + if (skip > 8) + skip = 8; + *source = target * skip; + + return skip; } +/* rect is the sensor rectangle, the caller guarantees parameter validity */ static int mt9t031_set_params(struct soc_camera_device *icd, struct v4l2_rect *rect, u16 xskip, u16 yskip) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct mt9t031 *mt9t031 = to_mt9t031(client); int ret; - u16 xbin, ybin, width, height, left, top; + u16 xbin, ybin; const u16 hblank = MT9T031_HORIZONTAL_BLANK, vblank = MT9T031_VERTICAL_BLANK; - /* Make sure we don't exceed sensor limits */ - if (rect->left + rect->width > icd->width_max) - rect->left = (icd->width_max - rect->width) / 2 + icd->x_min; - - if (rect->top + rect->height > icd->height_max) - rect->top = (icd->height_max - rect->height) / 2 + icd->y_min; - - width = rect->width * xskip; - height = rect->height * yskip; - left = rect->left * xskip; - top = rect->top * yskip; - xbin = min(xskip, (u16)3); ybin = min(yskip, (u16)3); - dev_dbg(&icd->dev, "xskip %u, width %u/%u, yskip %u, height %u/%u\n", - xskip, width, rect->width, yskip, height, rect->height); - - /* Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper */ + /* + * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper. + * There is always a valid suitably aligned value. The worst case is + * xbin = 3, width = 2048. Then we will start at 36, the last read out + * pixel will be 2083, which is < 2085 - first black pixel. + * + * MT9T031 datasheet imposes window left border alignment, depending on + * the selected xskip. Failing to conform to this requirement produces + * dark horizontal stripes in the image. However, even obeying to this + * requirement doesn't eliminate the stripes in all configurations. They + * appear "locally reproducibly," but can differ between tests under + * different lighting conditions. + */ switch (xbin) { - case 2: - left = (left + 3) & ~3; + case 1: + rect->left &= ~1; break; - case 3: - left = roundup(left, 6); - } - - switch (ybin) { case 2: - top = (top + 3) & ~3; + rect->left &= ~3; break; case 3: - top = roundup(top, 6); + rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ? + (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6); } + rect->top &= ~1; + + dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n", + xskip, yskip, rect->width, rect->height, rect->left, rect->top); + /* Disable register update, reconfigure atomically */ ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1); if (ret < 0) @@ -299,29 +288,30 @@ static int mt9t031_set_params(struct soc_camera_device *icd, ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, ((ybin - 1) << 4) | (yskip - 1)); } - dev_dbg(&icd->dev, "new physical left %u, top %u\n", left, top); + dev_dbg(&client->dev, "new physical left %u, top %u\n", + rect->left, rect->top); /* The caller provides a supported format, as guaranteed by * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */ if (ret >= 0) - ret = reg_write(client, MT9T031_COLUMN_START, left); + ret = reg_write(client, MT9T031_COLUMN_START, rect->left); if (ret >= 0) - ret = reg_write(client, MT9T031_ROW_START, top); + ret = reg_write(client, MT9T031_ROW_START, rect->top); if (ret >= 0) - ret = reg_write(client, MT9T031_WINDOW_WIDTH, width - 1); + ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1); if (ret >= 0) ret = reg_write(client, MT9T031_WINDOW_HEIGHT, - height + icd->y_skip_top - 1); + rect->height + icd->y_skip_top - 1); if (ret >= 0 && mt9t031->autoexposure) { - ret = set_shutter(client, height + icd->y_skip_top + vblank); + unsigned int total_h = rect->height + icd->y_skip_top + vblank; + ret = set_shutter(client, total_h); if (ret >= 0) { const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; const struct v4l2_queryctrl *qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); - icd->exposure = (shutter_max / 2 + (height + - icd->y_skip_top + vblank - 1) * - (qctrl->maximum - qctrl->minimum)) / + mt9t031->exposure = (shutter_max / 2 + (total_h - 1) * + (qctrl->maximum - qctrl->minimum)) / shutter_max + qctrl->minimum; } } @@ -330,58 +320,99 @@ static int mt9t031_set_params(struct soc_camera_device *icd, if (ret >= 0) ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); + if (ret >= 0) { + mt9t031->rect = *rect; + mt9t031->xskip = xskip; + mt9t031->yskip = yskip; + } + return ret < 0 ? ret : 0; } -static int mt9t031_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct v4l2_rect rect = a->c; + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct soc_camera_device *icd = client->dev.platform_data; + + rect.width = ALIGN(rect.width, 2); + rect.height = ALIGN(rect.height, 2); + + soc_camera_limit_side(&rect.left, &rect.width, + MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT); - /* CROP - no change in scaling, or in limits */ - return mt9t031_set_params(icd, rect, mt9t031->xskip, mt9t031->yskip); + return mt9t031_set_params(icd, &rect, mt9t031->xskip, mt9t031->yskip); } -static int mt9t031_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); - int ret; - u16 xskip, yskip; - struct v4l2_rect rect = { - .left = icd->x_current, - .top = icd->y_current, - .width = f->fmt.pix.width, - .height = f->fmt.pix.height, - }; + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); - /* - * try_fmt has put rectangle within limits. - * S_FMT - use binning and skipping for scaling, recalculate - * limits, used for cropping - */ - /* Is this more optimal than just a division? */ - for (xskip = 8; xskip > 1; xskip--) - if (rect.width * xskip <= MT9T031_MAX_WIDTH) - break; + a->c = mt9t031->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - for (yskip = 8; yskip > 1; yskip--) - if (rect.height * yskip <= MT9T031_MAX_HEIGHT) - break; + return 0; +} - recalculate_limits(icd, xskip, yskip); +static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = MT9T031_COLUMN_SKIP; + a->bounds.top = MT9T031_ROW_SKIP; + a->bounds.width = MT9T031_MAX_WIDTH; + a->bounds.height = MT9T031_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; - ret = mt9t031_set_params(icd, &rect, xskip, yskip); - if (!ret) { - mt9t031->xskip = xskip; - mt9t031->yskip = yskip; - } + return 0; +} - return ret; +static int mt9t031_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = mt9t031->rect.width / mt9t031->xskip; + pix->height = mt9t031->rect.height / mt9t031->yskip; + pix->pixelformat = V4L2_PIX_FMT_SGRBG10; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int mt9t031_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct soc_camera_device *icd = client->dev.platform_data; + struct v4l2_pix_format *pix = &f->fmt.pix; + u16 xskip, yskip; + struct v4l2_rect rect = mt9t031->rect; + + /* + * try_fmt has put width and height within limits. + * S_FMT: use binning and skipping for scaling + */ + xskip = mt9t031_skip(&rect.width, pix->width, MT9T031_MAX_WIDTH); + yskip = mt9t031_skip(&rect.height, pix->height, MT9T031_MAX_HEIGHT); + + /* mt9t031_set_params() doesn't change width and height */ + return mt9t031_set_params(icd, &rect, xskip, yskip); } -static int mt9t031_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +/* + * If a user window larger than sensor window is requested, we'll increase the + * sensor window. + */ +static int mt9t031_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; @@ -392,15 +423,16 @@ static int mt9t031_try_fmt(struct soc_camera_device *icd, return 0; } -static int mt9t031_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) +static int mt9t031_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match.addr != mt9t031->client->addr) + if (id->match.addr != client->addr) return -ENODEV; id->ident = mt9t031->model; @@ -410,10 +442,10 @@ static int mt9t031_get_chip_id(struct soc_camera_device *icd, } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9t031_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9t031_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -429,10 +461,10 @@ static int mt9t031_get_register(struct soc_camera_device *icd, return 0; } -static int mt9t031_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9t031_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -493,39 +525,17 @@ static const struct v4l2_queryctrl mt9t031_controls[] = { } }; -static int mt9t031_video_probe(struct soc_camera_device *); -static void mt9t031_video_remove(struct soc_camera_device *); -static int mt9t031_get_control(struct soc_camera_device *, struct v4l2_control *); -static int mt9t031_set_control(struct soc_camera_device *, struct v4l2_control *); - static struct soc_camera_ops mt9t031_ops = { - .owner = THIS_MODULE, - .probe = mt9t031_video_probe, - .remove = mt9t031_video_remove, - .init = mt9t031_init, - .release = mt9t031_release, - .start_capture = mt9t031_start_capture, - .stop_capture = mt9t031_stop_capture, - .set_crop = mt9t031_set_crop, - .set_fmt = mt9t031_set_fmt, - .try_fmt = mt9t031_try_fmt, .set_bus_param = mt9t031_set_bus_param, .query_bus_param = mt9t031_query_bus_param, .controls = mt9t031_controls, .num_controls = ARRAY_SIZE(mt9t031_controls), - .get_control = mt9t031_get_control, - .set_control = mt9t031_set_control, - .get_chip_id = mt9t031_get_chip_id, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .get_register = mt9t031_get_register, - .set_register = mt9t031_set_register, -#endif }; -static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +static int mt9t031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); int data; switch (ctrl->id) { @@ -544,14 +554,21 @@ static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_contro case V4L2_CID_EXPOSURE_AUTO: ctrl->value = mt9t031->autoexposure; break; + case V4L2_CID_GAIN: + ctrl->value = mt9t031->gain; + break; + case V4L2_CID_EXPOSURE: + ctrl->value = mt9t031->exposure; + break; } return 0; } -static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) +static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = sd->priv; + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct soc_camera_device *icd = client->dev.platform_data; const struct v4l2_queryctrl *qctrl; int data; @@ -586,7 +603,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro 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); + dev_dbg(&client->dev, "Setting gain %d\n", data); data = reg_write(client, MT9T031_GLOBAL_GAIN, data); if (data < 0) return -EIO; @@ -606,7 +623,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; - dev_dbg(&icd->dev, "Setting gain from 0x%x to 0x%x\n", + dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n", reg_read(client, MT9T031_GLOBAL_GAIN), data); data = reg_write(client, MT9T031_GLOBAL_GAIN, data); if (data < 0) @@ -614,7 +631,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro } /* Success */ - icd->gain = ctrl->value; + mt9t031->gain = ctrl->value; break; case V4L2_CID_EXPOSURE: /* mt9t031 has maximum == default */ @@ -627,11 +644,11 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro u32 old; get_shutter(client, &old); - dev_dbg(&icd->dev, "Setting shutter width from %u to %u\n", + dev_dbg(&client->dev, "Set shutter from %u to %u\n", old, shutter); if (set_shutter(client, shutter) < 0) return -EIO; - icd->exposure = ctrl->value; + mt9t031->exposure = ctrl->value; mt9t031->autoexposure = 0; } break; @@ -639,13 +656,14 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro if (ctrl->value) { const u16 vblank = MT9T031_VERTICAL_BLANK; const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; - if (set_shutter(client, icd->height + - icd->y_skip_top + vblank) < 0) + unsigned int total_h = mt9t031->rect.height + + icd->y_skip_top + vblank; + + if (set_shutter(client, total_h) < 0) return -EIO; qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); - icd->exposure = (shutter_max / 2 + (icd->height + - icd->y_skip_top + vblank - 1) * - (qctrl->maximum - qctrl->minimum)) / + mt9t031->exposure = (shutter_max / 2 + (total_h - 1) * + (qctrl->maximum - qctrl->minimum)) / shutter_max + qctrl->minimum; mt9t031->autoexposure = 1; } else @@ -657,22 +675,16 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro /* 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 mt9t031_video_probe(struct soc_camera_device *icd) +static int mt9t031_video_probe(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct soc_camera_device *icd = client->dev.platform_data; + struct mt9t031 *mt9t031 = to_mt9t031(client); 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(client, MT9T031_CHIP_ENABLE, 1); - dev_dbg(&icd->dev, "write: %d\n", data); + dev_dbg(&client->dev, "write: %d\n", data); /* Read out the chip version register */ data = reg_read(client, MT9T031_CHIP_VERSION); @@ -684,44 +696,64 @@ static int mt9t031_video_probe(struct soc_camera_device *icd) icd->num_formats = ARRAY_SIZE(mt9t031_colour_formats); break; default: - ret = -ENODEV; - dev_err(&icd->dev, + dev_err(&client->dev, "No MT9T031 chip detected, register read %x\n", data); - goto ei2c; + return -ENODEV; } - dev_info(&icd->dev, "Detected a MT9T031 chip ID %x\n", data); + dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data); - /* Now that we know the model, we can start video */ - ret = soc_camera_video_start(icd); - if (ret) - goto evstart; + ret = mt9t031_idle(client); + if (ret < 0) + dev_err(&client->dev, "Failed to initialise the camera\n"); - return 0; + /* mt9t031_idle() has reset the chip to default. */ + mt9t031->exposure = 255; + mt9t031->gain = 64; -evstart: -ei2c: return ret; } -static void mt9t031_video_remove(struct soc_camera_device *icd) -{ - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); +static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { + .g_ctrl = mt9t031_g_ctrl, + .s_ctrl = mt9t031_s_ctrl, + .g_chip_ident = mt9t031_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9t031_g_register, + .s_register = mt9t031_s_register, +#endif +}; - dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9t031->client->addr, - icd->dev.parent, icd->vdev); - soc_camera_video_stop(icd); -} +static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = { + .s_stream = mt9t031_s_stream, + .s_fmt = mt9t031_s_fmt, + .g_fmt = mt9t031_g_fmt, + .try_fmt = mt9t031_try_fmt, + .s_crop = mt9t031_s_crop, + .g_crop = mt9t031_g_crop, + .cropcap = mt9t031_cropcap, +}; + +static struct v4l2_subdev_ops mt9t031_subdev_ops = { + .core = &mt9t031_subdev_core_ops, + .video = &mt9t031_subdev_video_ops, +}; static int mt9t031_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9t031 *mt9t031; - struct soc_camera_device *icd; + struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = client->dev.platform_data; + struct soc_camera_link *icl; int ret; + if (!icd) { + dev_err(&client->dev, "MT9T031: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9T031 driver needs platform data\n"); return -EINVAL; @@ -737,23 +769,17 @@ static int mt9t031_probe(struct i2c_client *client, if (!mt9t031) return -ENOMEM; - mt9t031->client = client; - i2c_set_clientdata(client, mt9t031); + v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops); /* Second stage probe - when a capture adapter is there */ - icd = &mt9t031->icd; - icd->ops = &mt9t031_ops; - icd->control = &client->dev; - icd->x_min = MT9T031_COLUMN_SKIP; - icd->y_min = MT9T031_ROW_SKIP; - icd->x_current = icd->x_min; - icd->y_current = icd->y_min; - icd->width_min = MT9T031_MIN_WIDTH; - icd->width_max = MT9T031_MAX_WIDTH; - icd->height_min = MT9T031_MIN_HEIGHT; - icd->height_max = MT9T031_MAX_HEIGHT; - icd->y_skip_top = 0; - icd->iface = icl->bus_id; + icd->ops = &mt9t031_ops; + icd->y_skip_top = 0; + + mt9t031->rect.left = MT9T031_COLUMN_SKIP; + mt9t031->rect.top = MT9T031_ROW_SKIP; + mt9t031->rect.width = MT9T031_MAX_WIDTH; + mt9t031->rect.height = MT9T031_MAX_HEIGHT; + /* Simulated autoexposure. If enabled, we calculate shutter width * ourselves in the driver based on vertical blanking and frame width */ mt9t031->autoexposure = 1; @@ -761,24 +787,29 @@ static int mt9t031_probe(struct i2c_client *client, mt9t031->xskip = 1; mt9t031->yskip = 1; - ret = soc_camera_device_register(icd); - if (ret) - goto eisdr; + mt9t031_idle(client); - return 0; + ret = mt9t031_video_probe(client); + + mt9t031_disable(client); + + if (ret) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(mt9t031); + } -eisdr: - i2c_set_clientdata(client, NULL); - kfree(mt9t031); return ret; } static int mt9t031_remove(struct i2c_client *client) { - struct mt9t031 *mt9t031 = i2c_get_clientdata(client); + struct mt9t031 *mt9t031 = to_mt9t031(client); + struct soc_camera_device *icd = client->dev.platform_data; - soc_camera_device_unregister(&mt9t031->icd); + icd->ops = NULL; i2c_set_clientdata(client, NULL); + client->driver = NULL; kfree(mt9t031); return 0; diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index dbdcc86ae50d..995607f9d3ba 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -14,13 +14,13 @@ #include <linux/delay.h> #include <linux/log2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/v4l2-chip-ident.h> #include <media/soc_camera.h> /* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c - * The platform has to define i2c_board_info - * and call i2c_register_board_info() */ + * The platform has to define ctruct i2c_board_info objects and link to them + * from struct soc_camera_link */ static char *sensor_type; module_param(sensor_type, charp, S_IRUGO); @@ -45,7 +45,7 @@ MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); #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_ANALOG_GAIN 0x35 #define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47 #define MT9V022_PIXCLK_FV_LV 0x74 #define MT9V022_DIGITAL_TEST_PATTERN 0x7f @@ -55,6 +55,13 @@ MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); /* Progressive scan, master, defaults */ #define MT9V022_CHIP_CONTROL_DEFAULT 0x188 +#define MT9V022_MAX_WIDTH 752 +#define MT9V022_MAX_HEIGHT 480 +#define MT9V022_MIN_WIDTH 48 +#define MT9V022_MIN_HEIGHT 32 +#define MT9V022_COLUMN_SKIP 1 +#define MT9V022_ROW_SKIP 4 + static const struct soc_camera_data_format mt9v022_colour_formats[] = { /* Order important: first natively supported, * second supported with a GPIO extender */ @@ -85,12 +92,18 @@ static const struct soc_camera_data_format mt9v022_monochrome_formats[] = { }; struct mt9v022 { - struct i2c_client *client; - struct soc_camera_device icd; + struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ + __u32 fourcc; int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */ u16 chip_control; }; +static struct mt9v022 *to_mt9v022(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct mt9v022, subdev); +} + static int reg_read(struct i2c_client *client, const u8 reg) { s32 data = i2c_smbus_read_word_data(client, reg); @@ -125,29 +138,11 @@ static int reg_clear(struct i2c_client *client, const u8 reg, return reg_write(client, reg, ret & ~data); } -static int mt9v022_init(struct soc_camera_device *icd) +static int mt9v022_init(struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = client->dev.platform_data; + struct mt9v022 *mt9v022 = to_mt9v022(client); int ret; - if (icl->power) { - ret = icl->power(&client->dev, 1); - if (ret < 0) { - dev_err(icd->vdev->parent, - "Platform failed to power-on the camera.\n"); - return ret; - } - } - - /* - * The camera could have been already on, we hard-reset it additionally, - * if available. Soft reset is done in video_probe(). - */ - if (icl->reset) - icl->reset(&client->dev); - /* 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 */ @@ -161,6 +156,10 @@ static int mt9v022_init(struct soc_camera_device *icd) /* AEC, AGC on */ ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3); if (!ret) + ret = reg_write(client, MT9V022_ANALOG_GAIN, 16); + if (!ret) + ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480); + if (!ret) ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480); if (!ret) /* default - auto */ @@ -171,37 +170,19 @@ static int mt9v022_init(struct soc_camera_device *icd) return ret; } -static int mt9v022_release(struct soc_camera_device *icd) +static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = mt9v022->client->dev.platform_data; - - if (icl->power) - icl->power(&mt9v022->client->dev, 0); - - return 0; -} + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); -static int mt9v022_start_capture(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - /* Switch to master "normal" mode */ - mt9v022->chip_control &= ~0x10; - if (reg_write(client, MT9V022_CHIP_CONTROL, - mt9v022->chip_control) < 0) - return -EIO; - return 0; -} + if (enable) + /* Switch to master "normal" mode */ + mt9v022->chip_control &= ~0x10; + else + /* Switch to snapshot mode */ + mt9v022->chip_control |= 0x10; -static int mt9v022_stop_capture(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - /* Switch to snapshot mode */ - mt9v022->chip_control |= 0x10; - if (reg_write(client, MT9V022_CHIP_CONTROL, - mt9v022->chip_control) < 0) + if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) return -EIO; return 0; } @@ -209,9 +190,9 @@ static int mt9v022_stop_capture(struct soc_camera_device *icd) static int mt9v022_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = client->dev.platform_data; + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; int ret; u16 pixclk = 0; @@ -255,7 +236,7 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, if (ret < 0) return ret; - dev_dbg(&icd->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", + dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", pixclk, mt9v022->chip_control); return 0; @@ -263,8 +244,7 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = mt9v022->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned int width_flag; if (icl->query_bus_param) @@ -280,60 +260,121 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) width_flag; } -static int mt9v022_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct v4l2_rect rect = a->c; + struct soc_camera_device *icd = client->dev.platform_data; int ret; + /* Bayer format - even size lengths */ + if (mt9v022->fourcc == V4L2_PIX_FMT_SBGGR8 || + mt9v022->fourcc == V4L2_PIX_FMT_SBGGR16) { + rect.width = ALIGN(rect.width, 2); + rect.height = ALIGN(rect.height, 2); + /* Let the user play with the starting pixel */ + } + + soc_camera_limit_side(&rect.left, &rect.width, + MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH); + + soc_camera_limit_side(&rect.top, &rect.height, + MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT); + /* Like in example app. Contradicts the datasheet though */ ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); if (ret >= 0) { if (ret & 1) /* Autoexposure */ ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, - rect->height + icd->y_skip_top + 43); + rect.height + icd->y_skip_top + 43); else ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, - rect->height + icd->y_skip_top + 43); + rect.height + icd->y_skip_top + 43); } /* Setup frame format: defaults apart from width and height */ if (!ret) - ret = reg_write(client, MT9V022_COLUMN_START, rect->left); + ret = reg_write(client, MT9V022_COLUMN_START, rect.left); if (!ret) - ret = reg_write(client, MT9V022_ROW_START, rect->top); + ret = reg_write(client, MT9V022_ROW_START, rect.top); if (!ret) /* Default 94, Phytec driver says: * "width + horizontal blank >= 660" */ ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING, - rect->width > 660 - 43 ? 43 : - 660 - rect->width); + rect.width > 660 - 43 ? 43 : + 660 - rect.width); if (!ret) ret = reg_write(client, MT9V022_VERTICAL_BLANKING, 45); if (!ret) - ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect->width); + ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width); if (!ret) ret = reg_write(client, MT9V022_WINDOW_HEIGHT, - rect->height + icd->y_skip_top); + rect.height + icd->y_skip_top); if (ret < 0) return ret; - dev_dbg(&icd->dev, "Frame %ux%u pixel\n", rect->width, rect->height); + dev_dbg(&client->dev, "Frame %ux%u pixel\n", rect.width, rect.height); + + mt9v022->rect = rect; + + return 0; +} + +static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); + + a->c = mt9v022->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; return 0; } -static int mt9v022_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + a->bounds.left = MT9V022_COLUMN_SKIP; + a->bounds.top = MT9V022_ROW_SKIP; + a->bounds.width = MT9V022_MAX_WIDTH; + a->bounds.height = MT9V022_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int mt9v022_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_rect rect = { - .left = icd->x_current, - .top = icd->y_current, - .width = pix->width, - .height = pix->height, + + pix->width = mt9v022->rect.width; + pix->height = mt9v022->rect.height; + pix->pixelformat = mt9v022->fourcc; + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + + return 0; +} + +static int mt9v022_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_crop a = { + .c = { + .left = mt9v022->rect.left, + .top = mt9v022->rect.top, + .width = pix->width, + .height = pix->height, + }, }; + int ret; /* The caller provides a supported format, as verified per call to * icd->try_fmt(), datawidth is from our supported format list */ @@ -356,30 +397,42 @@ static int mt9v022_set_fmt(struct soc_camera_device *icd, } /* No support for scaling on this camera, just crop. */ - return mt9v022_set_crop(icd, &rect); + ret = mt9v022_s_crop(sd, &a); + if (!ret) { + pix->width = mt9v022->rect.width; + pix->height = mt9v022->rect.height; + mt9v022->fourcc = pix->pixelformat; + } + + return ret; } -static int mt9v022_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int mt9v022_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { + struct i2c_client *client = sd->priv; + struct soc_camera_device *icd = client->dev.platform_data; struct v4l2_pix_format *pix = &f->fmt.pix; + int align = pix->pixelformat == V4L2_PIX_FMT_SBGGR8 || + pix->pixelformat == V4L2_PIX_FMT_SBGGR16; - v4l_bound_align_image(&pix->width, 48, 752, 2 /* ? */, - &pix->height, 32 + icd->y_skip_top, - 480 + icd->y_skip_top, 0, 0); + v4l_bound_align_image(&pix->width, MT9V022_MIN_WIDTH, + MT9V022_MAX_WIDTH, align, + &pix->height, MT9V022_MIN_HEIGHT + icd->y_skip_top, + MT9V022_MAX_HEIGHT + icd->y_skip_top, align, 0); return 0; } -static int mt9v022_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) +static int mt9v022_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct i2c_client *client = sd->priv; + struct mt9v022 *mt9v022 = to_mt9v022(client); if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; - if (id->match.addr != mt9v022->client->addr) + if (id->match.addr != client->addr) return -ENODEV; id->ident = mt9v022->model; @@ -389,10 +442,10 @@ static int mt9v022_get_chip_id(struct soc_camera_device *icd, } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9v022_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9v022_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -409,10 +462,10 @@ static int mt9v022_get_register(struct soc_camera_device *icd, return 0; } -static int mt9v022_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int mt9v022_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; @@ -481,41 +534,22 @@ static const struct v4l2_queryctrl mt9v022_controls[] = { } }; -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_crop = mt9v022_set_crop, - .set_fmt = mt9v022_set_fmt, - .try_fmt = mt9v022_try_fmt, .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) +static int mt9v022_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; + const struct v4l2_queryctrl *qctrl; + unsigned long range; int data; + qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id); + switch (ctrl->id) { case V4L2_CID_VFLIP: data = reg_read(client, MT9V022_READ_MODE); @@ -541,19 +575,35 @@ static int mt9v022_get_control(struct soc_camera_device *icd, return -EIO; ctrl->value = !!(data & 0x2); break; + case V4L2_CID_GAIN: + data = reg_read(client, MT9V022_ANALOG_GAIN); + if (data < 0) + return -EIO; + + range = qctrl->maximum - qctrl->minimum; + ctrl->value = ((data - 16) * range + 24) / 48 + qctrl->minimum; + + break; + case V4L2_CID_EXPOSURE: + data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); + if (data < 0) + return -EIO; + + range = qctrl->maximum - qctrl->minimum; + ctrl->value = ((data - 1) * range + 239) / 479 + qctrl->minimum; + + break; } return 0; } -static int mt9v022_set_control(struct soc_camera_device *icd, - struct v4l2_control *ctrl) +static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { int data; - struct i2c_client *client = to_i2c_client(icd->control); + struct i2c_client *client = sd->priv; const struct v4l2_queryctrl *qctrl; qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id); - if (!qctrl) return -EINVAL; @@ -580,12 +630,9 @@ static int mt9v022_set_control(struct soc_camera_device *icd, 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. */ + /* Valid values 16 to 64, 32 to 64 must be even. */ unsigned long gain = ((ctrl->value - qctrl->minimum) * - 10 + range / 2) / range + 4; + 48 + range / 2) / range + 16; if (gain >= 32) gain &= ~1; /* The user wants to set gain manually, hope, she @@ -594,11 +641,10 @@ static int mt9v022_set_control(struct soc_camera_device *icd, if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) return -EIO; - dev_info(&icd->dev, "Setting gain from %d to %lu\n", - reg_read(client, MT9V022_ANALOG_GAIN), gain); + dev_dbg(&client->dev, "Setting gain from %d to %lu\n", + reg_read(client, MT9V022_ANALOG_GAIN), gain); if (reg_write(client, MT9V022_ANALOG_GAIN, gain) < 0) return -EIO; - icd->gain = ctrl->value; } break; case V4L2_CID_EXPOSURE: @@ -615,13 +661,12 @@ static int mt9v022_set_control(struct soc_camera_device *icd, if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1) < 0) return -EIO; - dev_dbg(&icd->dev, "Shutter width from %d to %lu\n", + dev_dbg(&client->dev, "Shutter width from %d to %lu\n", reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), shutter); if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, shutter) < 0) return -EIO; - icd->exposure = ctrl->value; } break; case V4L2_CID_AUTOGAIN: @@ -646,11 +691,11 @@ static int mt9v022_set_control(struct soc_camera_device *icd, /* 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) +static int mt9v022_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(icd->control); - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = client->dev.platform_data; + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct soc_camera_link *icl = to_soc_camera_link(icd); s32 data; int ret; unsigned long flags; @@ -665,7 +710,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) /* must be 0x1311 or 0x1313 */ if (data != 0x1311 && data != 0x1313) { ret = -ENODEV; - dev_info(&icd->dev, "No MT9V022 detected, ID register 0x%x\n", + dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n", data); goto ei2c; } @@ -677,7 +722,9 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) /* 15 clock cycles */ udelay(200); if (reg_read(client, MT9V022_RESET)) { - dev_err(&icd->dev, "Resetting MT9V022 failed!\n"); + dev_err(&client->dev, "Resetting MT9V022 failed!\n"); + if (ret > 0) + ret = -EIO; goto ei2c; } @@ -694,7 +741,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) } if (ret < 0) - goto eisis; + goto ei2c; icd->num_formats = 0; @@ -716,42 +763,70 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) if (flags & SOCAM_DATAWIDTH_8) icd->num_formats++; - ret = soc_camera_video_start(icd); - if (ret < 0) - goto eisis; + mt9v022->fourcc = icd->formats->fourcc; - dev_info(&icd->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", + dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ? "monochrome" : "colour"); - return 0; + ret = mt9v022_init(client); + if (ret < 0) + dev_err(&client->dev, "Failed to initialise the camera\n"); -eisis: ei2c: return ret; } static void mt9v022_video_remove(struct soc_camera_device *icd) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = mt9v022->client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); - dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9v022->client->addr, + dev_dbg(&icd->dev, "Video removed: %p, %p\n", icd->dev.parent, icd->vdev); - soc_camera_video_stop(icd); if (icl->free_bus) icl->free_bus(icl); } +static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { + .g_ctrl = mt9v022_g_ctrl, + .s_ctrl = mt9v022_s_ctrl, + .g_chip_ident = mt9v022_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = mt9v022_g_register, + .s_register = mt9v022_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { + .s_stream = mt9v022_s_stream, + .s_fmt = mt9v022_s_fmt, + .g_fmt = mt9v022_g_fmt, + .try_fmt = mt9v022_try_fmt, + .s_crop = mt9v022_s_crop, + .g_crop = mt9v022_g_crop, + .cropcap = mt9v022_cropcap, +}; + +static struct v4l2_subdev_ops mt9v022_subdev_ops = { + .core = &mt9v022_subdev_core_ops, + .video = &mt9v022_subdev_video_ops, +}; + static int mt9v022_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9v022 *mt9v022; - struct soc_camera_device *icd; + struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl = client->dev.platform_data; + struct soc_camera_link *icl; int ret; + if (!icd) { + dev_err(&client->dev, "MT9V022: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9V022 driver needs platform data\n"); return -EINVAL; @@ -767,40 +842,41 @@ static int mt9v022_probe(struct i2c_client *client, if (!mt9v022) return -ENOMEM; + v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); + 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; - - ret = soc_camera_device_register(icd); - if (ret) - goto eisdr; - return 0; + icd->ops = &mt9v022_ops; + /* + * MT9V022 _really_ corrupts the first read out line. + * TODO: verify on i.MX31 + */ + icd->y_skip_top = 1; + + mt9v022->rect.left = MT9V022_COLUMN_SKIP; + mt9v022->rect.top = MT9V022_ROW_SKIP; + mt9v022->rect.width = MT9V022_MAX_WIDTH; + mt9v022->rect.height = MT9V022_MAX_HEIGHT; + + ret = mt9v022_video_probe(icd, client); + if (ret) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(mt9v022); + } -eisdr: - kfree(mt9v022); return ret; } static int mt9v022_remove(struct i2c_client *client) { - struct mt9v022 *mt9v022 = i2c_get_clientdata(client); + struct mt9v022 *mt9v022 = to_mt9v022(client); + struct soc_camera_device *icd = client->dev.platform_data; - soc_camera_device_unregister(&mt9v022->icd); + icd->ops = NULL; + mt9v022_video_remove(icd); + i2c_set_clientdata(client, NULL); + client->driver = NULL; kfree(mt9v022); return 0; diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 736c31d23194..5f37952c75cf 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -126,7 +126,7 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, { struct soc_camera_device *icd = vq->priv_data; - *size = icd->width * icd->height * + *size = icd->user_width * icd->user_height * ((icd->current_fmt->depth + 7) >> 3); if (!*count) @@ -135,7 +135,7 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, while (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) (*count)--; - dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); return 0; } @@ -147,7 +147,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf) BUG_ON(in_interrupt()); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* This waits until this buffer is out of danger, i.e., until it is no @@ -165,7 +165,7 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq, struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); int ret; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* Added list head initialization on alloc */ @@ -178,12 +178,12 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq, buf->inwork = 1; if (buf->fmt != icd->current_fmt || - vb->width != icd->width || - vb->height != icd->height || + vb->width != icd->user_width || + vb->height != icd->user_height || vb->field != field) { buf->fmt = icd->current_fmt; - vb->width = icd->width; - vb->height = icd->height; + vb->width = icd->user_width; + vb->height = icd->user_height; vb->field = field; vb->state = VIDEOBUF_NEEDS_INIT; } @@ -216,10 +216,11 @@ out: static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) { struct videobuf_buffer *vbuf = &pcdev->active->vb; + struct device *dev = pcdev->icd->dev.parent; int ret; if (unlikely(!pcdev->active)) { - dev_err(pcdev->soc_host.dev, "DMA End IRQ with no active buffer\n"); + dev_err(dev, "DMA End IRQ with no active buffer\n"); return -EFAULT; } @@ -229,7 +230,7 @@ static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) vbuf->size, pcdev->res->start + CSIRXR, DMA_MODE_READ); if (unlikely(ret)) - dev_err(pcdev->soc_host.dev, "Failed to setup DMA sg list\n"); + dev_err(dev, "Failed to setup DMA sg list\n"); return ret; } @@ -243,7 +244,7 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq, struct mx1_camera_dev *pcdev = ici->priv; struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); list_add_tail(&vb->queue, &pcdev->capture); @@ -270,22 +271,23 @@ static void mx1_videobuf_release(struct videobuf_queue *vq, struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); #ifdef DEBUG struct soc_camera_device *icd = vq->priv_data; + struct device *dev = icd->dev.parent; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(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__); + dev_dbg(dev, "%s (active)\n", __func__); break; case VIDEOBUF_QUEUED: - dev_dbg(&icd->dev, "%s (queued)\n", __func__); + dev_dbg(dev, "%s (queued)\n", __func__); break; case VIDEOBUF_PREPARED: - dev_dbg(&icd->dev, "%s (prepared)\n", __func__); + dev_dbg(dev, "%s (prepared)\n", __func__); break; default: - dev_dbg(&icd->dev, "%s (unknown)\n", __func__); + dev_dbg(dev, "%s (unknown)\n", __func__); break; } #endif @@ -325,6 +327,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev, static void mx1_camera_dma_irq(int channel, void *data) { struct mx1_camera_dev *pcdev = data; + struct device *dev = pcdev->icd->dev.parent; struct mx1_buffer *buf; struct videobuf_buffer *vb; unsigned long flags; @@ -334,14 +337,14 @@ static void mx1_camera_dma_irq(int channel, void *data) imx_dma_disable(channel); if (unlikely(!pcdev->active)) { - dev_err(pcdev->soc_host.dev, "DMA End IRQ with no active buffer\n"); + dev_err(dev, "DMA End IRQ with no active buffer\n"); goto out; } vb = &pcdev->active->vb; buf = container_of(vb, struct mx1_buffer, vb); WARN_ON(buf->inwork || list_empty(&vb->queue)); - dev_dbg(pcdev->soc_host.dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); mx1_camera_wakeup(pcdev, vb, buf); @@ -362,7 +365,7 @@ static void mx1_camera_init_videobuf(struct videobuf_queue *q, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx1_camera_dev *pcdev = ici->priv; - videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, ici->dev, + videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->dev.parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, @@ -381,8 +384,9 @@ static int mclk_get_divisor(struct mx1_camera_dev *pcdev) * they get a nice Oops */ div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1; - dev_dbg(pcdev->soc_host.dev, "System clock %lukHz, target freq %dkHz, " - "divisor %lu\n", lcdclk / 1000, mclk / 1000, div); + dev_dbg(pcdev->icd->dev.parent, + "System clock %lukHz, target freq %dkHz, divisor %lu\n", + lcdclk / 1000, mclk / 1000, div); return div; } @@ -391,7 +395,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev) { unsigned int csicr1 = CSICR1_EN; - dev_dbg(pcdev->soc_host.dev, "Activate device\n"); + dev_dbg(pcdev->icd->dev.parent, "Activate device\n"); clk_enable(pcdev->clk); @@ -407,7 +411,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev) static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev) { - dev_dbg(pcdev->soc_host.dev, "Deactivate device\n"); + dev_dbg(pcdev->icd->dev.parent, "Deactivate device\n"); /* Disable all CSI interface */ __raw_writel(0x00, pcdev->base + CSICR1); @@ -428,14 +432,12 @@ static int mx1_camera_add_device(struct soc_camera_device *icd) goto ebusy; } - dev_info(&icd->dev, "MX1 Camera driver attached to camera %d\n", + dev_info(icd->dev.parent, "MX1 Camera driver attached to camera %d\n", icd->devnum); mx1_camera_activate(pcdev); - ret = icd->ops->init(icd); - if (!ret) - pcdev->icd = icd; + pcdev->icd = icd; ebusy: return ret; @@ -456,20 +458,20 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd) /* Stop DMA engine */ imx_dma_disable(pcdev->dma_chan); - dev_info(&icd->dev, "MX1 Camera driver detached from camera %d\n", + dev_info(icd->dev.parent, "MX1 Camera driver detached from camera %d\n", icd->devnum); - icd->ops->release(icd); - mx1_camera_deactivate(pcdev); pcdev->icd = NULL; } static int mx1_camera_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) + struct v4l2_crop *a) { - return icd->ops->set_crop(icd, rect); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_subdev_call(sd, video, s_crop, a); } static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) @@ -539,18 +541,19 @@ static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) static int mx1_camera_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; int ret; xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); + dev_warn(icd->dev.parent, "Format %x not found\n", + pix->pixelformat); return -EINVAL; } - ret = icd->ops->set_fmt(icd, f); + ret = v4l2_subdev_call(sd, video, s_fmt, f); if (!ret) { icd->buswidth = xlate->buswidth; icd->current_fmt = xlate->host_fmt; @@ -562,10 +565,11 @@ static int mx1_camera_set_fmt(struct soc_camera_device *icd, static int mx1_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); /* TODO: limit to mx1 hardware capabilities */ /* limit to sensor capabilities */ - return icd->ops->try_fmt(icd, f); + return v4l2_subdev_call(sd, video, try_fmt, f); } static int mx1_camera_reqbufs(struct soc_camera_file *icf, @@ -737,7 +741,7 @@ static int __init mx1_camera_probe(struct platform_device *pdev) pcdev->soc_host.drv_name = DRIVER_NAME; pcdev->soc_host.ops = &mx1_soc_camera_host_ops; pcdev->soc_host.priv = pcdev; - pcdev->soc_host.dev = &pdev->dev; + pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; err = soc_camera_host_register(&pcdev->soc_host); if (err) diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index 9770cb7932ca..dff2e5e2d8c6 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -178,7 +178,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx3_camera_buffer *buf BUG_ON(in_interrupt()); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* @@ -220,7 +220,7 @@ static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, if (!mx3_cam->idmac_channel[0]) return -EINVAL; - *size = icd->width * icd->height * bpp; + *size = icd->user_width * icd->user_height * bpp; if (!*count) *count = 32; @@ -241,7 +241,7 @@ static int mx3_videobuf_prepare(struct videobuf_queue *vq, struct mx3_camera_buffer *buf = container_of(vb, struct mx3_camera_buffer, vb); /* current_fmt _must_ always be set */ - size_t new_size = icd->width * icd->height * + size_t new_size = icd->user_width * icd->user_height * ((icd->current_fmt->depth + 7) >> 3); int ret; @@ -251,12 +251,12 @@ static int mx3_videobuf_prepare(struct videobuf_queue *vq, */ if (buf->fmt != icd->current_fmt || - vb->width != icd->width || - vb->height != icd->height || + vb->width != icd->user_width || + vb->height != icd->user_height || vb->field != field) { buf->fmt = icd->current_fmt; - vb->width = icd->width; - vb->height = icd->height; + vb->width = icd->user_width; + vb->height = icd->user_height; vb->field = field; if (vb->state != VIDEOBUF_NEEDS_INIT) free_buffer(vq, buf); @@ -354,9 +354,9 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, /* This is the configuration of one sg-element */ video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc); - video->out_width = icd->width; - video->out_height = icd->height; - video->out_stride = icd->width; + video->out_width = icd->user_width; + video->out_height = icd->user_height; + video->out_stride = icd->user_width; #ifdef DEBUG /* helps to see what DMA actually has written */ @@ -375,7 +375,8 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, spin_unlock_irq(&mx3_cam->lock); cookie = txd->tx_submit(txd); - dev_dbg(&icd->dev, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg)); + dev_dbg(icd->dev.parent, "Submitted cookie %d DMA 0x%08x\n", + cookie, sg_dma_address(&buf->sg)); spin_lock_irq(&mx3_cam->lock); @@ -402,9 +403,10 @@ static void mx3_videobuf_release(struct videobuf_queue *vq, container_of(vb, struct mx3_camera_buffer, vb); unsigned long flags; - dev_dbg(&icd->dev, "Release%s DMA 0x%08x (state %d), queue %sempty\n", + dev_dbg(icd->dev.parent, + "Release%s DMA 0x%08x (state %d), queue %sempty\n", mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg), - vb->state, list_empty(&vb->queue) ? "" : "not "); + vb->state, list_empty(&vb->queue) ? "" : "not "); spin_lock_irqsave(&mx3_cam->lock, flags); if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) && !list_empty(&vb->queue)) { @@ -431,7 +433,7 @@ static void mx3_camera_init_videobuf(struct videobuf_queue *q, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; - videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, ici->dev, + videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, icd->dev.parent, &mx3_cam->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, @@ -484,7 +486,7 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, clk_enable(mx3_cam->clk); rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk); - dev_dbg(&icd->dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate); + dev_dbg(icd->dev.parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate); if (rate) clk_set_rate(mx3_cam->clk, rate); } @@ -494,29 +496,18 @@ static int mx3_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; - int ret; - if (mx3_cam->icd) { - ret = -EBUSY; - goto ebusy; - } + if (mx3_cam->icd) + return -EBUSY; mx3_camera_activate(mx3_cam, icd); - ret = icd->ops->init(icd); - if (ret < 0) { - clk_disable(mx3_cam->clk); - goto einit; - } mx3_cam->icd = icd; -einit: -ebusy: - if (!ret) - dev_info(&icd->dev, "MX3 Camera driver attached to camera %d\n", - icd->devnum); + dev_info(icd->dev.parent, "MX3 Camera driver attached to camera %d\n", + icd->devnum); - return ret; + return 0; } /* Called with .video_lock held */ @@ -533,13 +524,11 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd) *ichan = NULL; } - icd->ops->release(icd); - clk_disable(mx3_cam->clk); mx3_cam->icd = NULL; - dev_info(&icd->dev, "MX3 Camera driver detached from camera %d\n", + dev_info(icd->dev.parent, "MX3 Camera driver detached from camera %d\n", icd->devnum); } @@ -551,7 +540,8 @@ static bool channel_change_requested(struct soc_camera_device *icd, struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; /* Do buffers have to be re-allocated or channel re-configured? */ - return ichan && rect->width * rect->height > icd->width * icd->height; + return ichan && rect->width * rect->height > + icd->user_width * icd->user_height; } static int test_platform_param(struct mx3_camera_dev *mx3_cam, @@ -599,8 +589,8 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam, *flags |= SOCAM_DATAWIDTH_4; break; default: - dev_info(mx3_cam->soc_host.dev, "Unsupported bus width %d\n", - buswidth); + dev_warn(mx3_cam->soc_host.v4l2_dev.dev, + "Unsupported bus width %d\n", buswidth); return -EINVAL; } @@ -615,7 +605,7 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd, unsigned long bus_flags, camera_flags; int ret = test_platform_param(mx3_cam, depth, &bus_flags); - dev_dbg(ici->dev, "requested bus width %d bit: %d\n", depth, ret); + dev_dbg(icd->dev.parent, "request bus width %d bit: %d\n", depth, ret); if (ret < 0) return ret; @@ -624,7 +614,8 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd, ret = soc_camera_bus_param_compatible(camera_flags, bus_flags); if (ret < 0) - dev_warn(&icd->dev, "Flags incompatible: camera %lx, host %lx\n", + dev_warn(icd->dev.parent, + "Flags incompatible: camera %lx, host %lx\n", camera_flags, bus_flags); return ret; @@ -638,7 +629,7 @@ static bool chan_filter(struct dma_chan *chan, void *arg) if (!rq) return false; - pdata = rq->mx3_cam->soc_host.dev->platform_data; + pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data; return rq->id == chan->chan_id && pdata->dma_dev == chan->device->dev; @@ -698,7 +689,8 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->dev, "Providing format %s using %s\n", + dev_dbg(icd->dev.parent, + "Providing format %s using %s\n", mx3_camera_formats[0].name, icd->formats[idx].name); } @@ -710,7 +702,8 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->dev, "Providing format %s using %s\n", + dev_dbg(icd->dev.parent, + "Providing format %s using %s\n", mx3_camera_formats[0].name, icd->formats[idx].name); } @@ -723,7 +716,7 @@ passthrough: xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->dev, + dev_dbg(icd->dev.parent, "Providing format %s in pass-through mode\n", icd->formats[idx].name); } @@ -733,13 +726,13 @@ passthrough: } static void configure_geometry(struct mx3_camera_dev *mx3_cam, - struct v4l2_rect *rect) + unsigned int width, unsigned int height) { u32 ctrl, width_field, height_field; /* Setup frame size - this cannot be changed on-the-fly... */ - width_field = rect->width - 1; - height_field = rect->height - 1; + width_field = width - 1; + height_field = height - 1; csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE); csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1); @@ -751,11 +744,6 @@ static void configure_geometry(struct mx3_camera_dev *mx3_cam, ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; /* Sensor does the cropping */ csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL); - - /* - * No need to free resources here if we fail, we'll see if we need to - * do this next time we are called - */ } static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) @@ -792,25 +780,74 @@ static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) return 0; } +/* + * FIXME: learn to use stride != width, then we can keep stride properly aligned + * and support arbitrary (even) widths. + */ +static inline void stride_align(__s32 *width) +{ + if (((*width + 7) & ~7) < 4096) + *width = (*width + 7) & ~7; + else + *width = *width & ~7; +} + +/* + * As long as we don't implement host-side cropping and scaling, we can use + * default g_crop and cropcap from soc_camera.c + */ static int mx3_camera_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) + struct v4l2_crop *a) { + struct v4l2_rect *rect = &a->c; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_format f = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE}; + struct v4l2_pix_format *pix = &f.fmt.pix; + int ret; - /* - * We now know pixel formats and can decide upon DMA-channel(s) - * So far only direct camera-to-memory is supported - */ - if (channel_change_requested(icd, rect)) { - int ret = acquire_dma_channel(mx3_cam); + soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096); + soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096); + + ret = v4l2_subdev_call(sd, video, s_crop, a); + if (ret < 0) + return ret; + + /* The capture device might have changed its output */ + ret = v4l2_subdev_call(sd, video, g_fmt, &f); + if (ret < 0) + return ret; + + if (pix->width & 7) { + /* Ouch! We can only handle 8-byte aligned width... */ + stride_align(&pix->width); + ret = v4l2_subdev_call(sd, video, s_fmt, &f); if (ret < 0) return ret; } - configure_geometry(mx3_cam, rect); + if (pix->width != icd->user_width || pix->height != icd->user_height) { + /* + * We now know pixel formats and can decide upon DMA-channel(s) + * So far only direct camera-to-memory is supported + */ + if (channel_change_requested(icd, rect)) { + int ret = acquire_dma_channel(mx3_cam); + if (ret < 0) + return ret; + } + + configure_geometry(mx3_cam, pix->width, pix->height); + } + + dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n", + pix->width, pix->height); - return icd->ops->set_crop(icd, rect); + icd->user_width = pix->width; + icd->user_height = pix->height; + + return ret; } static int mx3_camera_set_fmt(struct soc_camera_device *icd, @@ -818,22 +855,21 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_rect rect = { - .left = icd->x_current, - .top = icd->y_current, - .width = pix->width, - .height = pix->height, - }; int ret; xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); + dev_warn(icd->dev.parent, "Format %x not found\n", + pix->pixelformat); return -EINVAL; } + stride_align(&pix->width); + dev_dbg(icd->dev.parent, "Set format %dx%d\n", pix->width, pix->height); + ret = acquire_dma_channel(mx3_cam); if (ret < 0) return ret; @@ -844,21 +880,23 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, * mxc_v4l2_s_fmt() */ - configure_geometry(mx3_cam, &rect); + configure_geometry(mx3_cam, pix->width, pix->height); - ret = icd->ops->set_fmt(icd, f); + ret = v4l2_subdev_call(sd, video, s_fmt, f); if (!ret) { icd->buswidth = xlate->buswidth; icd->current_fmt = xlate->host_fmt; } + dev_dbg(icd->dev.parent, "Sensor set %dx%d\n", pix->width, pix->height); + return ret; } static int mx3_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; __u32 pixfmt = pix->pixelformat; @@ -867,7 +905,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (pixfmt && !xlate) { - dev_warn(ici->dev, "Format %x not found\n", pixfmt); + dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); return -EINVAL; } @@ -884,7 +922,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, /* camera has to see its format, but the user the original one */ pix->pixelformat = xlate->cam_fmt->fourcc; /* limit to sensor capabilities */ - ret = icd->ops->try_fmt(icd, f); + ret = v4l2_subdev_call(sd, video, try_fmt, f); pix->pixelformat = xlate->host_fmt->fourcc; field = pix->field; @@ -892,7 +930,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, if (field == V4L2_FIELD_ANY) { pix->field = V4L2_FIELD_NONE; } else if (field != V4L2_FIELD_NONE) { - dev_err(&icd->dev, "Field type %d unsupported.\n", field); + dev_err(icd->dev.parent, "Field type %d unsupported.\n", field); return -EINVAL; } @@ -931,14 +969,15 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) u32 dw, sens_conf; int ret = test_platform_param(mx3_cam, icd->buswidth, &bus_flags); const struct soc_camera_format_xlate *xlate; + struct device *dev = icd->dev.parent; xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pixfmt); + dev_warn(dev, "Format %x not found\n", pixfmt); return -EINVAL; } - dev_dbg(ici->dev, "requested bus width %d bit: %d\n", + dev_dbg(dev, "requested bus width %d bit: %d\n", icd->buswidth, ret); if (ret < 0) @@ -947,9 +986,10 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) camera_flags = icd->ops->query_bus_param(icd); common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); + dev_dbg(dev, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n", + camera_flags, bus_flags, common_flags); if (!common_flags) { - dev_dbg(ici->dev, "no common flags: camera %lx, host %lx\n", - camera_flags, bus_flags); + dev_dbg(dev, "no common flags"); return -EINVAL; } @@ -1002,8 +1042,11 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) SOCAM_DATAWIDTH_4; ret = icd->ops->set_bus_param(icd, common_flags); - if (ret < 0) + if (ret < 0) { + dev_dbg(dev, "camera set_bus_param(%lx) returned %d\n", + common_flags, ret); return ret; + } /* * So far only gated clock mode is supported. Add a line @@ -1055,7 +1098,7 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF); - dev_dbg(ici->dev, "Set SENS_CONF to %x\n", sens_conf | dw); + dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw); return 0; } @@ -1127,8 +1170,9 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev) INIT_LIST_HEAD(&mx3_cam->capture); spin_lock_init(&mx3_cam->lock); - base = ioremap(res->start, res->end - res->start + 1); + base = ioremap(res->start, resource_size(res)); if (!base) { + pr_err("Couldn't map %x@%x\n", resource_size(res), res->start); err = -ENOMEM; goto eioremap; } @@ -1139,7 +1183,7 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev) soc_host->drv_name = MX3_CAM_DRV_NAME; soc_host->ops = &mx3_soc_camera_host_ops; soc_host->priv = mx3_cam; - soc_host->dev = &pdev->dev; + soc_host->v4l2_dev.dev = &pdev->dev; soc_host->nr = pdev->id; err = soc_camera_host_register(soc_host); @@ -1215,3 +1259,4 @@ module_exit(mx3_camera_exit); MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver"); MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME); diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 0bce255168bd..eccb40ab7fec 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -22,7 +22,7 @@ #include <linux/delay.h> #include <linux/videodev2.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/soc_camera.h> #include <media/ov772x.h> @@ -382,11 +382,10 @@ struct regval_list { }; struct ov772x_color_format { - char *name; - __u32 fourcc; - u8 dsp3; - u8 com3; - u8 com7; + const struct soc_camera_data_format *format; + u8 dsp3; + u8 com3; + u8 com7; }; struct ov772x_win_size { @@ -398,14 +397,15 @@ struct ov772x_win_size { }; struct ov772x_priv { + struct v4l2_subdev subdev; struct ov772x_camera_info *info; - struct i2c_client *client; - struct soc_camera_device icd; const struct ov772x_color_format *fmt; const struct ov772x_win_size *win; int model; - unsigned int flag_vflip:1; - unsigned int flag_hflip:1; + unsigned short flag_vflip:1; + unsigned short flag_hflip:1; + /* band_filter = COM8[5] ? 256 - BDBASE : 0 */ + unsigned short band_filter; }; #define ENDMARKER { 0xff, 0xff } @@ -481,43 +481,43 @@ static const struct soc_camera_data_format ov772x_fmt_lists[] = { */ static const struct ov772x_color_format ov772x_cfmts[] = { { - SETFOURCC(YUYV), + .format = &ov772x_fmt_lists[0], .dsp3 = 0x0, .com3 = SWAP_YUV, .com7 = OFMT_YUV, }, { - SETFOURCC(YVYU), + .format = &ov772x_fmt_lists[1], .dsp3 = UV_ON, .com3 = SWAP_YUV, .com7 = OFMT_YUV, }, { - SETFOURCC(UYVY), + .format = &ov772x_fmt_lists[2], .dsp3 = 0x0, .com3 = 0x0, .com7 = OFMT_YUV, }, { - SETFOURCC(RGB555), + .format = &ov772x_fmt_lists[3], .dsp3 = 0x0, .com3 = SWAP_RGB, .com7 = FMT_RGB555 | OFMT_RGB, }, { - SETFOURCC(RGB555X), + .format = &ov772x_fmt_lists[4], .dsp3 = 0x0, .com3 = 0x0, .com7 = FMT_RGB555 | OFMT_RGB, }, { - SETFOURCC(RGB565), + .format = &ov772x_fmt_lists[5], .dsp3 = 0x0, .com3 = SWAP_RGB, .com7 = FMT_RGB565 | OFMT_RGB, }, { - SETFOURCC(RGB565X), + .format = &ov772x_fmt_lists[6], .dsp3 = 0x0, .com3 = 0x0, .com7 = FMT_RGB565 | OFMT_RGB, @@ -570,6 +570,15 @@ static const struct v4l2_queryctrl ov772x_controls[] = { .step = 1, .default_value = 0, }, + { + .id = V4L2_CID_BAND_STOP_FILTER, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Band-stop filter", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0, + }, }; @@ -577,6 +586,12 @@ static const struct v4l2_queryctrl ov772x_controls[] = { * general function */ +static struct ov772x_priv *to_ov772x(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct ov772x_priv, + subdev); +} + static int ov772x_write_array(struct i2c_client *client, const struct regval_list *vals) { @@ -617,58 +632,29 @@ static int ov772x_reset(struct i2c_client *client) * soc_camera_ops function */ -static int ov772x_init(struct soc_camera_device *icd) +static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - int ret = 0; + struct i2c_client *client = sd->priv; + struct ov772x_priv *priv = to_ov772x(client); - if (priv->info->link.power) { - ret = priv->info->link.power(&priv->client->dev, 1); - if (ret < 0) - return ret; + if (!enable) { + ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); + return 0; } - if (priv->info->link.reset) - ret = priv->info->link.reset(&priv->client->dev); - - return ret; -} - -static int ov772x_release(struct soc_camera_device *icd) -{ - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - int ret = 0; - - if (priv->info->link.power) - ret = priv->info->link.power(&priv->client->dev, 0); - - return ret; -} - -static int ov772x_start_capture(struct soc_camera_device *icd) -{ - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - if (!priv->win || !priv->fmt) { - dev_err(&icd->dev, "norm or win select error\n"); + dev_err(&client->dev, "norm or win select error\n"); return -EPERM; } - ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, 0); + ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0); - dev_dbg(&icd->dev, - "format %s, win %s\n", priv->fmt->name, priv->win->name); + dev_dbg(&client->dev, "format %s, win %s\n", + priv->fmt->format->name, priv->win->name); return 0; } -static int ov772x_stop_capture(struct soc_camera_device *icd) -{ - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); - return 0; -} - static int ov772x_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { @@ -677,8 +663,9 @@ static int ov772x_set_bus_param(struct soc_camera_device *icd, static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - struct soc_camera_link *icl = &priv->info->link; + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct ov772x_priv *priv = i2c_get_clientdata(client); + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth; @@ -686,10 +673,10 @@ static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) return soc_camera_apply_sensor_flags(icl, flags); } -static int ov772x_get_control(struct soc_camera_device *icd, - struct v4l2_control *ctrl) +static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct i2c_client *client = sd->priv; + struct ov772x_priv *priv = to_ov772x(client); switch (ctrl->id) { case V4L2_CID_VFLIP: @@ -698,14 +685,17 @@ static int ov772x_get_control(struct soc_camera_device *icd, case V4L2_CID_HFLIP: ctrl->value = priv->flag_hflip; break; + case V4L2_CID_BAND_STOP_FILTER: + ctrl->value = priv->band_filter; + break; } return 0; } -static int ov772x_set_control(struct soc_camera_device *icd, - struct v4l2_control *ctrl) +static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct i2c_client *client = sd->priv; + struct ov772x_priv *priv = to_ov772x(client); int ret = 0; u8 val; @@ -715,24 +705,48 @@ static int ov772x_set_control(struct soc_camera_device *icd, priv->flag_vflip = ctrl->value; if (priv->info->flags & OV772X_FLAG_VFLIP) val ^= VFLIP_IMG; - ret = ov772x_mask_set(priv->client, COM3, VFLIP_IMG, val); + ret = ov772x_mask_set(client, COM3, VFLIP_IMG, val); break; case V4L2_CID_HFLIP: val = ctrl->value ? HFLIP_IMG : 0x00; priv->flag_hflip = ctrl->value; if (priv->info->flags & OV772X_FLAG_HFLIP) val ^= HFLIP_IMG; - ret = ov772x_mask_set(priv->client, COM3, HFLIP_IMG, val); + ret = ov772x_mask_set(client, COM3, HFLIP_IMG, val); + break; + case V4L2_CID_BAND_STOP_FILTER: + if ((unsigned)ctrl->value > 256) + ctrl->value = 256; + if (ctrl->value == priv->band_filter) + break; + if (!ctrl->value) { + /* Switch the filter off, it is on now */ + ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff); + if (!ret) + ret = ov772x_mask_set(client, COM8, + BNDF_ON_OFF, 0); + } else { + /* Switch the filter on, set AEC low limit */ + val = 256 - ctrl->value; + ret = ov772x_mask_set(client, COM8, + BNDF_ON_OFF, BNDF_ON_OFF); + if (!ret) + ret = ov772x_mask_set(client, BDBASE, + 0xff, val); + } + if (!ret) + priv->band_filter = ctrl->value; break; } return ret; } -static int ov772x_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) +static int ov772x_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct i2c_client *client = sd->priv; + struct ov772x_priv *priv = to_ov772x(client); id->ident = priv->model; id->revision = 0; @@ -741,17 +755,17 @@ static int ov772x_get_chip_id(struct soc_camera_device *icd, } #ifdef CONFIG_VIDEO_ADV_DEBUG -static int ov772x_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int ov772x_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - int ret; + struct i2c_client *client = sd->priv; + int ret; reg->size = 1; if (reg->reg > 0xff) return -EINVAL; - ret = i2c_smbus_read_byte_data(priv->client, reg->reg); + ret = i2c_smbus_read_byte_data(client, reg->reg); if (ret < 0) return ret; @@ -760,21 +774,20 @@ static int ov772x_get_register(struct soc_camera_device *icd, return 0; } -static int ov772x_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int ov772x_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct i2c_client *client = sd->priv; if (reg->reg > 0xff || reg->val > 0xff) return -EINVAL; - return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val); + return i2c_smbus_write_byte_data(client, reg->reg, reg->val); } #endif -static const struct ov772x_win_size* -ov772x_select_win(u32 width, u32 height) +static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) { __u32 diff; const struct ov772x_win_size *win; @@ -793,9 +806,10 @@ ov772x_select_win(u32 width, u32 height) return win; } -static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, - u32 pixfmt) +static int ov772x_set_params(struct i2c_client *client, + u32 *width, u32 *height, u32 pixfmt) { + struct ov772x_priv *priv = to_ov772x(client); int ret = -EINVAL; u8 val; int i; @@ -805,7 +819,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, */ priv->fmt = NULL; for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { - if (pixfmt == ov772x_cfmts[i].fourcc) { + if (pixfmt == ov772x_cfmts[i].format->fourcc) { priv->fmt = ov772x_cfmts + i; break; } @@ -816,12 +830,12 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, /* * select win */ - priv->win = ov772x_select_win(width, height); + priv->win = ov772x_select_win(*width, *height); /* * reset hardware */ - ov772x_reset(priv->client); + ov772x_reset(client); /* * Edge Ctrl @@ -835,17 +849,17 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, * Remove it when manual mode. */ - ret = ov772x_mask_set(priv->client, DSPAUTO, EDGE_ACTRL, 0x00); + ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, EDGE_TRSHLD, EDGE_THRESHOLD_MASK, priv->info->edgectrl.threshold); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, EDGE_STRNGT, EDGE_STRENGTH_MASK, priv->info->edgectrl.strength); if (ret < 0) @@ -857,13 +871,13 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, * * set upper and lower limit */ - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, EDGE_UPPER, EDGE_UPPER_MASK, priv->info->edgectrl.upper); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, EDGE_LOWER, EDGE_LOWER_MASK, priv->info->edgectrl.lower); if (ret < 0) @@ -873,7 +887,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, /* * set size format */ - ret = ov772x_write_array(priv->client, priv->win->regs); + ret = ov772x_write_array(client, priv->win->regs); if (ret < 0) goto ov772x_set_fmt_error; @@ -882,7 +896,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, */ val = priv->fmt->dsp3; if (val) { - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, DSP_CTRL3, UV_MASK, val); if (ret < 0) goto ov772x_set_fmt_error; @@ -901,7 +915,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, if (priv->flag_hflip) val ^= HFLIP_IMG; - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, COM3, SWAP_MASK | IMG_MASK, val); if (ret < 0) goto ov772x_set_fmt_error; @@ -910,47 +924,99 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, * set COM7 */ val = priv->win->com7_bit | priv->fmt->com7; - ret = ov772x_mask_set(priv->client, + ret = ov772x_mask_set(client, COM7, (SLCT_MASK | FMT_MASK | OFMT_MASK), val); if (ret < 0) goto ov772x_set_fmt_error; + /* + * set COM8 + */ + if (priv->band_filter) { + ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1); + if (!ret) + ret = ov772x_mask_set(client, BDBASE, + 0xff, 256 - priv->band_filter); + if (ret < 0) + goto ov772x_set_fmt_error; + } + + *width = priv->win->width; + *height = priv->win->height; + return ret; ov772x_set_fmt_error: - ov772x_reset(priv->client); + ov772x_reset(client); priv->win = NULL; priv->fmt = NULL; return ret; } -static int ov772x_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int ov772x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + a->c.left = 0; + a->c.top = 0; + a->c.width = VGA_WIDTH; + a->c.height = VGA_HEIGHT; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (!priv->fmt) - return -EINVAL; + return 0; +} - return ov772x_set_params(priv, rect->width, rect->height, - priv->fmt->fourcc); +static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = VGA_WIDTH; + a->bounds.height = VGA_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; } -static int ov772x_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int ov772x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct ov772x_priv *priv = to_ov772x(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (!priv->win || !priv->fmt) { + u32 width = VGA_WIDTH, height = VGA_HEIGHT; + int ret = ov772x_set_params(client, &width, &height, + V4L2_PIX_FMT_YUYV); + if (ret < 0) + return ret; + } + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + pix->width = priv->win->width; + pix->height = priv->win->height; + pix->pixelformat = priv->fmt->format->fourcc; + pix->colorspace = priv->fmt->format->colorspace; + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov772x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct i2c_client *client = sd->priv; struct v4l2_pix_format *pix = &f->fmt.pix; - return ov772x_set_params(priv, pix->width, pix->height, + return ov772x_set_params(client, &pix->width, &pix->height, pix->pixelformat); } -static int ov772x_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int ov772x_try_fmt(struct v4l2_subdev *sd, + struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; const struct ov772x_win_size *win; @@ -967,9 +1033,10 @@ static int ov772x_try_fmt(struct soc_camera_device *icd, return 0; } -static int ov772x_video_probe(struct soc_camera_device *icd) +static int ov772x_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct ov772x_priv *priv = to_ov772x(client); u8 pid, ver; const char *devname; @@ -986,7 +1053,7 @@ static int ov772x_video_probe(struct soc_camera_device *icd) */ if (SOCAM_DATAWIDTH_10 != priv->info->buswidth && SOCAM_DATAWIDTH_8 != priv->info->buswidth) { - dev_err(&icd->dev, "bus width error\n"); + dev_err(&client->dev, "bus width error\n"); return -ENODEV; } @@ -996,8 +1063,8 @@ static int ov772x_video_probe(struct soc_camera_device *icd) /* * check and show product ID and manufacturer ID */ - pid = i2c_smbus_read_byte_data(priv->client, PID); - ver = i2c_smbus_read_byte_data(priv->client, VER); + pid = i2c_smbus_read_byte_data(client, PID); + ver = i2c_smbus_read_byte_data(client, VER); switch (VERSION(pid, ver)) { case OV7720: @@ -1009,69 +1076,77 @@ static int ov772x_video_probe(struct soc_camera_device *icd) priv->model = V4L2_IDENT_OV7725; break; default: - dev_err(&icd->dev, + dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); return -ENODEV; } - dev_info(&icd->dev, + dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", devname, pid, ver, - i2c_smbus_read_byte_data(priv->client, MIDH), - i2c_smbus_read_byte_data(priv->client, MIDL)); - - return soc_camera_video_start(icd); -} + i2c_smbus_read_byte_data(client, MIDH), + i2c_smbus_read_byte_data(client, MIDL)); -static void ov772x_video_remove(struct soc_camera_device *icd) -{ - soc_camera_video_stop(icd); + return 0; } static struct soc_camera_ops ov772x_ops = { - .owner = THIS_MODULE, - .probe = ov772x_video_probe, - .remove = ov772x_video_remove, - .init = ov772x_init, - .release = ov772x_release, - .start_capture = ov772x_start_capture, - .stop_capture = ov772x_stop_capture, - .set_crop = ov772x_set_crop, - .set_fmt = ov772x_set_fmt, - .try_fmt = ov772x_try_fmt, .set_bus_param = ov772x_set_bus_param, .query_bus_param = ov772x_query_bus_param, .controls = ov772x_controls, .num_controls = ARRAY_SIZE(ov772x_controls), - .get_control = ov772x_get_control, - .set_control = ov772x_set_control, - .get_chip_id = ov772x_get_chip_id, +}; + +static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { + .g_ctrl = ov772x_g_ctrl, + .s_ctrl = ov772x_s_ctrl, + .g_chip_ident = ov772x_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - .get_register = ov772x_get_register, - .set_register = ov772x_set_register, + .g_register = ov772x_g_register, + .s_register = ov772x_s_register, #endif }; +static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = { + .s_stream = ov772x_s_stream, + .g_fmt = ov772x_g_fmt, + .s_fmt = ov772x_s_fmt, + .try_fmt = ov772x_try_fmt, + .cropcap = ov772x_cropcap, + .g_crop = ov772x_g_crop, +}; + +static struct v4l2_subdev_ops ov772x_subdev_ops = { + .core = &ov772x_subdev_core_ops, + .video = &ov772x_subdev_video_ops, +}; + /* * i2c_driver function */ static int ov772x_probe(struct i2c_client *client, - const struct i2c_device_id *did) + const struct i2c_device_id *did) { struct ov772x_priv *priv; struct ov772x_camera_info *info; - struct soc_camera_device *icd; + struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl; int ret; - if (!client->dev.platform_data) + if (!icd) { + dev_err(&client->dev, "OV772X: missing soc-camera data!\n"); return -EINVAL; + } - info = container_of(client->dev.platform_data, - struct ov772x_camera_info, link); + icl = to_soc_camera_link(icd); + if (!icl) + return -EINVAL; + + info = container_of(icl, struct ov772x_camera_info, link); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&adapter->dev, @@ -1084,20 +1159,15 @@ static int ov772x_probe(struct i2c_client *client, if (!priv) return -ENOMEM; - priv->info = info; - priv->client = client; - i2c_set_clientdata(client, priv); + priv->info = info; - icd = &priv->icd; - icd->ops = &ov772x_ops; - icd->control = &client->dev; - icd->width_max = MAX_WIDTH; - icd->height_max = MAX_HEIGHT; - icd->iface = priv->info->link.bus_id; + v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); - ret = soc_camera_device_register(icd); + icd->ops = &ov772x_ops; + ret = ov772x_video_probe(icd, client); if (ret) { + icd->ops = NULL; i2c_set_clientdata(client, NULL); kfree(priv); } @@ -1107,9 +1177,10 @@ static int ov772x_probe(struct i2c_client *client, static int ov772x_remove(struct i2c_client *client) { - struct ov772x_priv *priv = i2c_get_clientdata(client); + struct ov772x_priv *priv = to_ov772x(client); + struct soc_camera_device *icd = client->dev.platform_data; - soc_camera_device_unregister(&priv->icd); + icd->ops = NULL; i2c_set_clientdata(client, NULL); kfree(priv); return 0; diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c index 416933ca607d..cc06d5e4adcc 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -65,9 +65,10 @@ void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) u32 input; pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo"); + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes[sid]) != NULL) && + if ((sp != NULL) && (hdw->input_val >= 0) && (hdw->input_val < sp->cnt)) { input = sp->def[hdw->input_val]; diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 610bd848df24..a334b1a966a2 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -540,7 +540,6 @@ static struct i2c_algorithm pvr2_i2c_algo_template = { static struct i2c_adapter pvr2_i2c_adap_template = { .owner = THIS_MODULE, .class = 0, - .id = I2C_HW_B_BT848, }; diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 8d17cf613306..f976df452a34 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -1057,7 +1057,8 @@ static int pwc_create_sysfs_files(struct video_device *vdev) goto err; if (pdev->features & FEATURE_MOTOR_PANTILT) { rc = device_create_file(&vdev->dev, &dev_attr_pan_tilt); - if (rc) goto err_button; + if (rc) + goto err_button; } return 0; @@ -1072,6 +1073,7 @@ err: static void pwc_remove_sysfs_files(struct video_device *vdev) { struct pwc_device *pdev = video_get_drvdata(vdev); + if (pdev->features & FEATURE_MOTOR_PANTILT) device_remove_file(&vdev->dev, &dev_attr_pan_tilt); device_remove_file(&vdev->dev, &dev_attr_button); @@ -1229,13 +1231,11 @@ static void pwc_cleanup(struct pwc_device *pdev) video_unregister_device(pdev->vdev); #ifdef CONFIG_USB_PWC_INPUT_EVDEV - if (pdev->button_dev) { + if (pdev->button_dev) input_unregister_device(pdev->button_dev); - input_free_device(pdev->button_dev); - kfree(pdev->button_dev->phys); - pdev->button_dev = NULL; - } #endif + + kfree(pdev); } /* Note that all cleanup is done in the reverse order as in _open */ @@ -1281,8 +1281,6 @@ static int pwc_video_close(struct file *file) PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen); } else { pwc_cleanup(pdev); - /* Free memory (don't set pdev to 0 just yet) */ - kfree(pdev); /* search device_hint[] table if we occupy a slot, by any chance */ for (hint = 0; hint < MAX_DEV_HINTS; hint++) if (device_hint[hint].pdev == pdev) @@ -1499,13 +1497,10 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id struct usb_device *udev = interface_to_usbdev(intf); struct pwc_device *pdev = NULL; int vendor_id, product_id, type_id; - int i, hint, rc; + int hint, rc; int features = 0; int video_nr = -1; /* default: use next available device */ char serial_number[30], *name; -#ifdef CONFIG_USB_PWC_INPUT_EVDEV - char *phys = NULL; -#endif vendor_id = le16_to_cpu(udev->descriptor.idVendor); product_id = le16_to_cpu(udev->descriptor.idProduct); @@ -1757,8 +1752,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vframes = default_fps; strcpy(pdev->serial, serial_number); pdev->features = features; - if (vendor_id == 0x046D && product_id == 0x08B5) - { + if (vendor_id == 0x046D && product_id == 0x08B5) { /* Logitech QuickCam Orbit The ranges have been determined experimentally; they may differ from cam to cam. Also, the exact ranges left-right and up-down are different for my cam @@ -1780,8 +1774,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vdev = video_device_alloc(); if (!pdev->vdev) { PWC_ERROR("Err, cannot allocate video_device struture. Failing probe."); - kfree(pdev); - return -ENOMEM; + rc = -ENOMEM; + goto err_free_mem; } memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); pdev->vdev->parent = &intf->dev; @@ -1806,25 +1800,23 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id } pdev->vdev->release = video_device_release; - i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr); - if (i < 0) { - PWC_ERROR("Failed to register as video device (%d).\n", i); - rc = i; - goto err; - } - else { - PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->num); + rc = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr); + if (rc < 0) { + PWC_ERROR("Failed to register as video device (%d).\n", rc); + goto err_video_release; } + PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->num); + /* occupy slot */ if (hint < MAX_DEV_HINTS) device_hint[hint].pdev = pdev; PWC_DEBUG_PROBE("probe() function returning struct at 0x%p.\n", pdev); - usb_set_intfdata (intf, pdev); + usb_set_intfdata(intf, pdev); rc = pwc_create_sysfs_files(pdev->vdev); if (rc) - goto err_unreg; + goto err_video_unreg; /* Set the leds off */ pwc_set_leds(pdev, 0, 0); @@ -1835,16 +1827,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->button_dev = input_allocate_device(); if (!pdev->button_dev) { PWC_ERROR("Err, insufficient memory for webcam snapshot button device."); - return -ENOMEM; + rc = -ENOMEM; + pwc_remove_sysfs_files(pdev->vdev); + goto err_video_unreg; } + usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys)); + strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys)); + pdev->button_dev->name = "PWC snapshot button"; - phys = kasprintf(GFP_KERNEL,"usb-%s-%s", pdev->udev->bus->bus_name, pdev->udev->devpath); - if (!phys) { - input_free_device(pdev->button_dev); - return -ENOMEM; - } - pdev->button_dev->phys = phys; + pdev->button_dev->phys = pdev->button_phys; usb_to_input_id(pdev->udev, &pdev->button_dev->id); pdev->button_dev->dev.parent = &pdev->udev->dev; pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY); @@ -1853,25 +1845,27 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id rc = input_register_device(pdev->button_dev); if (rc) { input_free_device(pdev->button_dev); - kfree(pdev->button_dev->phys); pdev->button_dev = NULL; - return rc; + pwc_remove_sysfs_files(pdev->vdev); + goto err_video_unreg; } #endif return 0; -err_unreg: +err_video_unreg: if (hint < MAX_DEV_HINTS) device_hint[hint].pdev = NULL; video_unregister_device(pdev->vdev); -err: - video_device_release(pdev->vdev); /* Drip... drip... drip... */ - kfree(pdev); /* Oops, no memory leaks please */ + pdev->vdev = NULL; /* So we don't try to release it below */ +err_video_release: + video_device_release(pdev->vdev); +err_free_mem: + kfree(pdev); return rc; } -/* The user janked out the cable... */ +/* The user yanked out the cable... */ static void usb_pwc_disconnect(struct usb_interface *intf) { struct pwc_device *pdev; @@ -1902,7 +1896,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf) /* Alert waiting processes */ wake_up_interruptible(&pdev->frameq); /* Wait until device is closed */ - if(pdev->vopen) { + if (pdev->vopen) { mutex_lock(&pdev->modlock); pdev->unplugged = 1; mutex_unlock(&pdev->modlock); @@ -1911,8 +1905,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf) /* Device is closed, so we can safely unregister it */ PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n"); pwc_cleanup(pdev); - /* Free memory (don't set pdev to 0 just yet) */ - kfree(pdev); disconnect_out: /* search device_hint[] table if we occupy a slot, by any chance */ diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 2876ce084510..bdb4ced57496 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -1033,7 +1033,7 @@ long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (std->index != 0) return -EINVAL; std->id = V4L2_STD_UNKNOWN; - strncpy(std->name, "webcam", sizeof(std->name)); + strlcpy(std->name, "webcam", sizeof(std->name)); return 0; } diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 0b658dee05a4..0902355dfa77 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -135,12 +135,6 @@ #define DEVICE_USE_CODEC3(x) ((x)>=700) #define DEVICE_USE_CODEC23(x) ((x)>=675) - -#ifndef V4L2_PIX_FMT_PWC1 -#define V4L2_PIX_FMT_PWC1 v4l2_fourcc('P','W','C','1') -#define V4L2_PIX_FMT_PWC2 v4l2_fourcc('P','W','C','2') -#endif - /* The following structures were based on cpia.h. Why reinvent the wheel? :-) */ struct pwc_iso_buf { @@ -259,6 +253,7 @@ struct pwc_device int snapshot_button_status; /* set to 1 when the user push the button, reset to 0 when this value is read */ #ifdef CONFIG_USB_PWC_INPUT_EVDEV struct input_dev *button_dev; /* webcam snapshot button input */ + char button_phys[64]; #endif /*** Misc. data ***/ diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 016bb45ba0c3..6952e9602d5d 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -225,6 +225,10 @@ struct pxa_camera_dev { u32 save_cicr[5]; }; +struct pxa_cam { + unsigned long flags; +}; + static const char *pxa_cam_driver_description = "PXA_Camera"; static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ @@ -237,9 +241,9 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, { struct soc_camera_device *icd = vq->priv_data; - dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); - *size = roundup(icd->width * icd->height * + *size = roundup(icd->user_width * icd->user_height * ((icd->current_fmt->depth + 7) >> 3), 8); if (0 == *count) @@ -259,7 +263,7 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) BUG_ON(in_interrupt()); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(icd->dev.parent, "%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 @@ -270,7 +274,8 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) { if (buf->dmas[i].sg_cpu) - dma_free_coherent(ici->dev, buf->dmas[i].sg_size, + dma_free_coherent(ici->v4l2_dev.dev, + buf->dmas[i].sg_size, buf->dmas[i].sg_cpu, buf->dmas[i].sg_dma); buf->dmas[i].sg_cpu = NULL; @@ -325,19 +330,20 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, struct scatterlist **sg_first, int *sg_first_ofs) { struct pxa_cam_dma *pxa_dma = &buf->dmas[channel]; + struct device *dev = pcdev->soc_host.v4l2_dev.dev; struct scatterlist *sg; int i, offset, sglen; int dma_len = 0, xfer_len = 0; if (pxa_dma->sg_cpu) - dma_free_coherent(pcdev->soc_host.dev, pxa_dma->sg_size, + dma_free_coherent(dev, pxa_dma->sg_size, pxa_dma->sg_cpu, pxa_dma->sg_dma); sglen = calculate_dma_sglen(*sg_first, dma->sglen, *sg_first_ofs, size); pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc); - pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->soc_host.dev, pxa_dma->sg_size, + pxa_dma->sg_cpu = dma_alloc_coherent(dev, pxa_dma->sg_size, &pxa_dma->sg_dma, GFP_KERNEL); if (!pxa_dma->sg_cpu) return -ENOMEM; @@ -345,7 +351,7 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, pxa_dma->sglen = sglen; offset = *sg_first_ofs; - dev_dbg(pcdev->soc_host.dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n", + dev_dbg(dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n", *sg_first, sglen, *sg_first_ofs, pxa_dma->sg_dma); @@ -368,7 +374,7 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, pxa_dma->sg_cpu[i].ddadr = pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); - dev_vdbg(pcdev->soc_host.dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n", + dev_vdbg(dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n", pxa_dma->sg_dma + i * sizeof(struct pxa_dma_desc), sg_dma_address(sg) + offset, xfer_len); offset = 0; @@ -418,11 +424,12 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, 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 device *dev = pcdev->soc_host.v4l2_dev.dev; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); int ret; int size_y, size_u = 0, size_v = 0; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); /* Added list head initialization on alloc */ @@ -441,12 +448,12 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, buf->inwork = 1; if (buf->fmt != icd->current_fmt || - vb->width != icd->width || - vb->height != icd->height || + vb->width != icd->user_width || + vb->height != icd->user_height || vb->field != field) { buf->fmt = icd->current_fmt; - vb->width = icd->width; - vb->height = icd->height; + vb->width = icd->user_width; + vb->height = icd->user_height; vb->field = field; vb->state = VIDEOBUF_NEEDS_INIT; } @@ -480,8 +487,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, size_y, &sg, &next_ofs); if (ret) { - dev_err(pcdev->soc_host.dev, - "DMA initialization for Y/RGB failed\n"); + dev_err(dev, "DMA initialization for Y/RGB failed\n"); goto fail; } @@ -490,8 +496,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1, size_u, &sg, &next_ofs); if (ret) { - dev_err(pcdev->soc_host.dev, - "DMA initialization for U failed\n"); + dev_err(dev, "DMA initialization for U failed\n"); goto fail_u; } @@ -500,8 +505,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2, size_v, &sg, &next_ofs); if (ret) { - dev_err(pcdev->soc_host.dev, - "DMA initialization for V failed\n"); + dev_err(dev, "DMA initialization for V failed\n"); goto fail_v; } @@ -514,10 +518,10 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, return 0; fail_v: - dma_free_coherent(pcdev->soc_host.dev, buf->dmas[1].sg_size, + dma_free_coherent(dev, buf->dmas[1].sg_size, buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma); fail_u: - dma_free_coherent(pcdev->soc_host.dev, buf->dmas[0].sg_size, + dma_free_coherent(dev, buf->dmas[0].sg_size, buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma); fail: free_buffer(vq, buf); @@ -541,7 +545,8 @@ static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev) active = pcdev->active; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->soc_host.dev, "%s (channel=%d) ddadr=%08x\n", __func__, + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "%s (channel=%d) ddadr=%08x\n", __func__, i, active->dmas[i].sg_dma); DDADR(pcdev->dma_chans[i]) = active->dmas[i].sg_dma; DCSR(pcdev->dma_chans[i]) = DCSR_RUN; @@ -553,7 +558,8 @@ static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev) int i; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->soc_host.dev, "%s (channel=%d)\n", __func__, i); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "%s (channel=%d)\n", __func__, i); DCSR(pcdev->dma_chans[i]) = 0; } } @@ -589,7 +595,7 @@ static void pxa_camera_start_capture(struct pxa_camera_dev *pcdev) { unsigned long cicr0, cifr; - dev_dbg(pcdev->soc_host.dev, "%s\n", __func__); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__); /* Reset the FIFOs */ cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F; __raw_writel(cifr, pcdev->base + CIFR); @@ -609,7 +615,7 @@ static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev) __raw_writel(cicr0, pcdev->base + CICR0); pcdev->active = NULL; - dev_dbg(pcdev->soc_host.dev, "%s\n", __func__); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__); } /* Called under spinlock_irqsave(&pcdev->lock, ...) */ @@ -621,8 +627,8 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d active=%p\n", __func__, - vb, vb->baddr, vb->bsize, pcdev->active); + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n", + __func__, vb, vb->baddr, vb->bsize, pcdev->active); list_add_tail(&vb->queue, &pcdev->capture); @@ -639,22 +645,23 @@ static void pxa_videobuf_release(struct videobuf_queue *vq, struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); #ifdef DEBUG struct soc_camera_device *icd = vq->priv_data; + struct device *dev = icd->dev.parent; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + dev_dbg(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__); + dev_dbg(dev, "%s (active)\n", __func__); break; case VIDEOBUF_QUEUED: - dev_dbg(&icd->dev, "%s (queued)\n", __func__); + dev_dbg(dev, "%s (queued)\n", __func__); break; case VIDEOBUF_PREPARED: - dev_dbg(&icd->dev, "%s (prepared)\n", __func__); + dev_dbg(dev, "%s (prepared)\n", __func__); break; default: - dev_dbg(&icd->dev, "%s (unknown)\n", __func__); + dev_dbg(dev, "%s (unknown)\n", __func__); break; } #endif @@ -674,7 +681,8 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, do_gettimeofday(&vb->ts); vb->field_count++; wake_up(&vb->done); - dev_dbg(pcdev->soc_host.dev, "%s dequeud buffer (vb=0x%p)\n", __func__, vb); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s dequeud buffer (vb=0x%p)\n", + __func__, vb); if (list_empty(&pcdev->capture)) { pxa_camera_stop_capture(pcdev); @@ -710,7 +718,8 @@ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev) for (i = 0; i < pcdev->channels; i++) if (DDADR(pcdev->dma_chans[i]) != DDADR_STOP) is_dma_stopped = 0; - dev_dbg(pcdev->soc_host.dev, "%s : top queued buffer=%p, dma_stopped=%d\n", + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "%s : top queued buffer=%p, dma_stopped=%d\n", __func__, pcdev->active, is_dma_stopped); if (pcdev->active && is_dma_stopped) pxa_camera_start_capture(pcdev); @@ -719,6 +728,7 @@ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev) static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, enum pxa_camera_active_dma act_dma) { + struct device *dev = pcdev->soc_host.v4l2_dev.dev; struct pxa_buffer *buf; unsigned long flags; u32 status, camera_status, overrun; @@ -735,13 +745,13 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, overrun |= CISR_IFO_1 | CISR_IFO_2; if (status & DCSR_BUSERR) { - dev_err(pcdev->soc_host.dev, "DMA Bus Error IRQ!\n"); + dev_err(dev, "DMA Bus Error IRQ!\n"); goto out; } if (!(status & (DCSR_ENDINTR | DCSR_STARTINTR))) { - dev_err(pcdev->soc_host.dev, "Unknown DMA IRQ source, " - "status: 0x%08x\n", status); + dev_err(dev, "Unknown DMA IRQ source, status: 0x%08x\n", + status); goto out; } @@ -764,7 +774,7 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, buf = container_of(vb, struct pxa_buffer, vb); WARN_ON(buf->inwork || list_empty(&vb->queue)); - dev_dbg(pcdev->soc_host.dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n", + dev_dbg(dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n", __func__, channel, status & DCSR_STARTINTR ? "SOF " : "", status & DCSR_ENDINTR ? "EOF " : "", vb, DDADR(channel)); @@ -775,7 +785,7 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, */ if (camera_status & overrun && !list_is_last(pcdev->capture.next, &pcdev->capture)) { - dev_dbg(pcdev->soc_host.dev, "FIFO overrun! CISR: %x\n", + dev_dbg(dev, "FIFO overrun! CISR: %x\n", camera_status); pxa_camera_stop_capture(pcdev); pxa_camera_start_capture(pcdev); @@ -830,9 +840,11 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q, sizeof(struct pxa_buffer), icd); } -static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev) +static u32 mclk_get_divisor(struct platform_device *pdev, + struct pxa_camera_dev *pcdev) { unsigned long mclk = pcdev->mclk; + struct device *dev = &pdev->dev; u32 div; unsigned long lcdclk; @@ -842,7 +854,7 @@ static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev) /* mclk <= ciclk / 4 (27.4.2) */ if (mclk > lcdclk / 4) { mclk = lcdclk / 4; - dev_warn(pcdev->soc_host.dev, "Limiting master clock to %lu\n", mclk); + dev_warn(dev, "Limiting master clock to %lu\n", mclk); } /* We verify mclk != 0, so if anyone breaks it, here comes their Oops */ @@ -852,8 +864,8 @@ static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev) if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) pcdev->mclk = lcdclk / (2 * (div + 1)); - dev_dbg(pcdev->soc_host.dev, "LCD clock %luHz, target freq %luHz, " - "divisor %u\n", lcdclk, mclk, div); + dev_dbg(dev, "LCD clock %luHz, target freq %luHz, divisor %u\n", + lcdclk, mclk, div); return div; } @@ -870,14 +882,15 @@ static void recalculate_fifo_timeout(struct pxa_camera_dev *pcdev, static void pxa_camera_activate(struct pxa_camera_dev *pcdev) { struct pxacamera_platform_data *pdata = pcdev->pdata; + struct device *dev = pcdev->soc_host.v4l2_dev.dev; u32 cicr4 = 0; - dev_dbg(pcdev->soc_host.dev, "Registered platform device at %p data %p\n", + dev_dbg(dev, "Registered platform device at %p data %p\n", pcdev, pdata); if (pdata && pdata->init) { - dev_dbg(pcdev->soc_host.dev, "%s: Init gpios\n", __func__); - pdata->init(pcdev->soc_host.dev); + dev_dbg(dev, "%s: Init gpios\n", __func__); + pdata->init(dev); } /* disable all interrupts */ @@ -919,7 +932,8 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) struct videobuf_buffer *vb; status = __raw_readl(pcdev->base + CISR); - dev_dbg(pcdev->soc_host.dev, "Camera interrupt status 0x%lx\n", status); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, + "Camera interrupt status 0x%lx\n", status); if (!status) return IRQ_NONE; @@ -951,24 +965,18 @@ 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; - if (pcdev->icd) { - ret = -EBUSY; - goto ebusy; - } - - dev_info(&icd->dev, "PXA Camera driver attached to camera %d\n", - icd->devnum); + if (pcdev->icd) + return -EBUSY; pxa_camera_activate(pcdev); - ret = icd->ops->init(icd); - if (!ret) - pcdev->icd = icd; + pcdev->icd = icd; -ebusy: - return ret; + dev_info(icd->dev.parent, "PXA Camera driver attached to camera %d\n", + icd->devnum); + + return 0; } /* Called with .video_lock held */ @@ -979,7 +987,7 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd) BUG_ON(icd != pcdev->icd); - dev_info(&icd->dev, "PXA Camera driver detached from camera %d\n", + dev_info(icd->dev.parent, "PXA Camera driver detached from camera %d\n", icd->devnum); /* disable capture, disable interrupts */ @@ -990,8 +998,6 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd) DCSR(pcdev->dma_chans[1]) = 0; DCSR(pcdev->dma_chans[2]) = 0; - icd->ops->release(icd); - pxa_camera_deactivate(pcdev); pcdev->icd = NULL; @@ -1039,57 +1045,17 @@ static int test_platform_param(struct pxa_camera_dev *pcdev, return 0; } -static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +static void pxa_camera_setup_cicr(struct soc_camera_device *icd, + unsigned long flags, __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; + unsigned long dw, bpp; u32 cicr0, cicr1, cicr2, cicr3, 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) { + switch (flags & SOCAM_DATAWIDTH_MASK) { case SOCAM_DATAWIDTH_10: dw = 4; bpp = 0x40; @@ -1110,18 +1076,18 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) cicr4 |= CICR4_PCLK_EN; if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) cicr4 |= CICR4_MCLK_EN; - if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) + if (flags & SOCAM_PCLK_SAMPLE_FALLING) cicr4 |= CICR4_PCP; - if (common_flags & SOCAM_HSYNC_ACTIVE_LOW) + if (flags & SOCAM_HSYNC_ACTIVE_LOW) cicr4 |= CICR4_HSP; - if (common_flags & SOCAM_VSYNC_ACTIVE_LOW) + if (flags & SOCAM_VSYNC_ACTIVE_LOW) cicr4 |= CICR4_VSP; cicr0 = __raw_readl(pcdev->base + CICR0); if (cicr0 & CICR0_ENB) __raw_writel(cicr0 & ~CICR0_ENB, pcdev->base + CICR0); - cicr1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw; + cicr1 = CICR1_PPL_VAL(icd->user_width - 1) | bpp | dw; switch (pixfmt) { case V4L2_PIX_FMT_YUV422P: @@ -1150,7 +1116,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) } cicr2 = 0; - cicr3 = CICR3_LPF_VAL(icd->height - 1) | + cicr3 = CICR3_LPF_VAL(icd->user_height - 1) | CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top)); cicr4 |= pcdev->mclk_divisor; @@ -1164,6 +1130,59 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP)); cicr0 |= CICR0_DMAEN | CICR0_IRQ_MASK; __raw_writel(cicr0, pcdev->base + CICR0); +} + +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 bus_flags, camera_flags, common_flags; + int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); + struct pxa_cam *cam = icd->host_priv; + + 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; + } + + cam->flags = common_flags; + + ret = icd->ops->set_bus_param(icd, common_flags); + if (ret < 0) + return ret; + + pxa_camera_setup_cicr(icd, common_flags, pixfmt); return 0; } @@ -1227,8 +1246,9 @@ static int required_buswidth(const struct soc_camera_data_format *fmt) static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, struct soc_camera_format_xlate *xlate) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->dev.parent; int formats = 0, buswidth, ret; + struct pxa_cam *cam; buswidth = required_buswidth(icd->formats + idx); @@ -1239,6 +1259,16 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, if (ret < 0) return 0; + if (!icd->host_priv) { + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) + return -ENOMEM; + + icd->host_priv = cam; + } else { + cam = icd->host_priv; + } + switch (icd->formats[idx].fourcc) { case V4L2_PIX_FMT_UYVY: formats++; @@ -1247,7 +1277,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->dev, "Providing format %s using %s\n", + dev_dbg(dev, "Providing format %s using %s\n", pxa_camera_formats[0].name, icd->formats[idx].name); } @@ -1262,7 +1292,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(ici->dev, "Providing format %s packed\n", + dev_dbg(dev, "Providing format %s packed\n", icd->formats[idx].name); } break; @@ -1274,7 +1304,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = icd->formats[idx].depth; xlate++; - dev_dbg(ici->dev, + dev_dbg(dev, "Providing format %s in pass-through mode\n", icd->formats[idx].name); } @@ -1283,31 +1313,80 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, return formats; } +static void pxa_camera_put_formats(struct soc_camera_device *icd) +{ + kfree(icd->host_priv); + icd->host_priv = NULL; +} + +static int pxa_camera_check_frame(struct v4l2_pix_format *pix) +{ + /* limit to pxa hardware capabilities */ + return pix->height < 32 || pix->height > 2048 || pix->width < 48 || + pix->width > 2048 || (pix->width & 0x01); +} + static int pxa_camera_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) + struct v4l2_crop *a) { + struct v4l2_rect *rect = &a->c; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; + struct device *dev = icd->dev.parent; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_sense sense = { .master_clock = pcdev->mclk, .pixel_clock_max = pcdev->ciclk / 4, }; + struct v4l2_format f; + struct v4l2_pix_format *pix = &f.fmt.pix, pix_tmp; + struct pxa_cam *cam = icd->host_priv; int ret; /* If PCLK is used to latch data from the sensor, check sense */ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) icd->sense = &sense; - ret = icd->ops->set_crop(icd, rect); + ret = v4l2_subdev_call(sd, video, s_crop, a); icd->sense = NULL; if (ret < 0) { - dev_warn(ici->dev, "Failed to crop to %ux%u@%u:%u\n", + dev_warn(dev, "Failed to crop to %ux%u@%u:%u\n", rect->width, rect->height, rect->left, rect->top); - } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { + return ret; + } + + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, g_fmt, &f); + if (ret < 0) + return ret; + + pix_tmp = *pix; + if (pxa_camera_check_frame(pix)) { + /* + * Camera cropping produced a frame beyond our capabilities. + * FIXME: just extract a subframe, that we can process. + */ + v4l_bound_align_image(&pix->width, 48, 2048, 1, + &pix->height, 32, 2048, 0, + icd->current_fmt->fourcc == V4L2_PIX_FMT_YUV422P ? + 4 : 0); + ret = v4l2_subdev_call(sd, video, s_fmt, &f); + if (ret < 0) + return ret; + + if (pxa_camera_check_frame(pix)) { + dev_warn(icd->dev.parent, + "Inconsistent state. Use S_FMT to repair\n"); + return -EINVAL; + } + } + + if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { if (sense.pixel_clock > sense.pixel_clock_max) { - dev_err(ici->dev, + dev_err(dev, "pixel clock %lu set by the camera too high!", sense.pixel_clock); return -EIO; @@ -1315,6 +1394,11 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd, recalculate_fifo_timeout(pcdev, sense.pixel_clock); } + icd->user_width = pix->width; + icd->user_height = pix->height; + + pxa_camera_setup_cicr(icd, cam->flags, icd->current_fmt->fourcc); + return ret; } @@ -1323,6 +1407,8 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; + struct device *dev = icd->dev.parent; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_data_format *cam_fmt = NULL; const struct soc_camera_format_xlate *xlate = NULL; struct soc_camera_sense sense = { @@ -1335,7 +1421,7 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); + dev_warn(dev, "Format %x not found\n", pix->pixelformat); return -EINVAL; } @@ -1346,16 +1432,21 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, icd->sense = &sense; cam_f.fmt.pix.pixelformat = cam_fmt->fourcc; - ret = icd->ops->set_fmt(icd, &cam_f); + ret = v4l2_subdev_call(sd, video, s_fmt, f); icd->sense = NULL; if (ret < 0) { - dev_warn(ici->dev, "Failed to configure for format %x\n", + dev_warn(dev, "Failed to configure for format %x\n", pix->pixelformat); + } else if (pxa_camera_check_frame(pix)) { + dev_warn(dev, + "Camera driver produced an unsupported frame %dx%d\n", + pix->width, pix->height); + ret = -EINVAL; } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { if (sense.pixel_clock > sense.pixel_clock_max) { - dev_err(ici->dev, + dev_err(dev, "pixel clock %lu set by the camera too high!", sense.pixel_clock); return -EIO; @@ -1375,6 +1466,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; __u32 pixfmt = pix->pixelformat; @@ -1383,7 +1475,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pixfmt); + dev_warn(ici->v4l2_dev.dev, "Format %x not found\n", pixfmt); return -EINVAL; } @@ -1395,7 +1487,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, */ v4l_bound_align_image(&pix->width, 48, 2048, 1, &pix->height, 32, 2048, 0, - xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0); + pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0); pix->bytesperline = pix->width * DIV_ROUND_UP(xlate->host_fmt->depth, 8); @@ -1404,15 +1496,15 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, /* camera has to see its format, but the user the original one */ pix->pixelformat = xlate->cam_fmt->fourcc; /* limit to sensor capabilities */ - ret = icd->ops->try_fmt(icd, f); - pix->pixelformat = xlate->host_fmt->fourcc; + ret = v4l2_subdev_call(sd, video, try_fmt, f); + pix->pixelformat = pixfmt; field = pix->field; if (field == V4L2_FIELD_ANY) { pix->field = V4L2_FIELD_NONE; } else if (field != V4L2_FIELD_NONE) { - dev_err(&icd->dev, "Field type %d unsupported.\n", field); + dev_err(icd->dev.parent, "Field type %d unsupported.\n", field); return -EINVAL; } @@ -1518,6 +1610,7 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .resume = pxa_camera_resume, .set_crop = pxa_camera_set_crop, .get_formats = pxa_camera_get_formats, + .put_formats = pxa_camera_put_formats, .set_fmt = pxa_camera_set_fmt, .try_fmt = pxa_camera_try_fmt, .init_videobuf = pxa_camera_init_videobuf, @@ -1575,8 +1668,7 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev) pcdev->mclk = 20000000; } - pcdev->soc_host.dev = &pdev->dev; - pcdev->mclk_divisor = mclk_get_divisor(pcdev); + pcdev->mclk_divisor = mclk_get_divisor(pdev, pcdev); INIT_LIST_HEAD(&pcdev->capture); spin_lock_init(&pcdev->lock); @@ -1641,6 +1733,7 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev) pcdev->soc_host.drv_name = PXA_CAM_DRV_NAME; pcdev->soc_host.ops = &pxa_soc_camera_host_ops; pcdev->soc_host.priv = pcdev; + pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; err = soc_camera_host_register(&pcdev->soc_host); @@ -1722,3 +1815,4 @@ module_exit(pxa_camera_exit); MODULE_DESCRIPTION("PXA27x SoC Camera Host driver"); MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" PXA_CAM_DRV_NAME); diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c index c25e81af5ce0..c3e96f070973 100644 --- a/drivers/media/video/saa6588.c +++ b/drivers/media/video/saa6588.c @@ -40,7 +40,7 @@ /* insmod options */ static unsigned int debug; static unsigned int xtal; -static unsigned int rbds; +static unsigned int mmbs; static unsigned int plvl; static unsigned int bufblocks = 100; @@ -48,8 +48,8 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable debug messages"); module_param(xtal, int, 0); MODULE_PARM_DESC(xtal, "select oscillator frequency (0..3), default 0"); -module_param(rbds, int, 0); -MODULE_PARM_DESC(rbds, "select mode, 0=RDS, 1=RBDS, default 0"); +module_param(mmbs, int, 0); +MODULE_PARM_DESC(mmbs, "enable MMBS mode: 0=off (default), 1=on"); module_param(plvl, int, 0); MODULE_PARM_DESC(plvl, "select pause level (0..3), default 0"); module_param(bufblocks, int, 0); @@ -78,6 +78,7 @@ struct saa6588 { unsigned char last_blocknum; wait_queue_head_t read_queue; int data_available_for_read; + u8 sync; }; static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd) @@ -261,13 +262,16 @@ static void saa6588_i2c_poll(struct saa6588 *s) unsigned char tmp; /* Although we only need 3 bytes, we have to read at least 6. - SAA6588 returns garbage otherwise */ + SAA6588 returns garbage otherwise. */ if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) { if (debug > 1) dprintk(PREFIX "read error!\n"); return; } + s->sync = tmpbuf[0] & 0x10; + if (!s->sync) + return; blocknum = tmpbuf[0] >> 5; if (blocknum == s->last_blocknum) { if (debug > 3) @@ -286,9 +290,8 @@ static void saa6588_i2c_poll(struct saa6588 *s) occurred during reception of this block. Bit 6: Corrected bit. Indicates that an error was corrected for this data block. - Bits 5-3: Received Offset. Indicates the offset received - by the sync system. - Bits 2-0: Offset Name. Indicates the offset applied to this data. + Bits 5-3: Same as bits 0-2. + Bits 2-0: Block number. SAA6588 byte order is Status-MSB-LSB, so we have to swap the first and the last of the 3 bytes block. @@ -298,12 +301,21 @@ static void saa6588_i2c_poll(struct saa6588 *s) tmpbuf[2] = tmpbuf[0]; tmpbuf[0] = tmp; + /* Map 'Invalid block E' to 'Invalid Block' */ + if (blocknum == 6) + blocknum = V4L2_RDS_BLOCK_INVALID; + /* And if are not in mmbs mode, then 'Block E' is also mapped + to 'Invalid Block'. As far as I can tell MMBS is discontinued, + and if there is ever a need to support E blocks, then please + contact the linux-media mailinglist. */ + else if (!mmbs && blocknum == 5) + blocknum = V4L2_RDS_BLOCK_INVALID; tmp = blocknum; tmp |= blocknum << 3; /* Received offset == Offset Name (OK ?) */ if ((tmpbuf[2] & 0x03) == 0x03) - tmp |= 0x80; /* uncorrectable error */ + tmp |= V4L2_RDS_BLOCK_ERROR; /* uncorrectable error */ else if ((tmpbuf[2] & 0x03) != 0x00) - tmp |= 0x40; /* corrected error */ + tmp |= V4L2_RDS_BLOCK_CORRECTED; /* corrected error */ tmpbuf[2] = tmp; /* Is this enough ? Should we also check other bits ? */ spin_lock_irqsave(&s->lock, flags); @@ -321,14 +333,14 @@ static void saa6588_work(struct work_struct *work) schedule_delayed_work(&s->work, msecs_to_jiffies(20)); } -static int saa6588_configure(struct saa6588 *s) +static void saa6588_configure(struct saa6588 *s) { struct i2c_client *client = v4l2_get_subdevdata(&s->sd); unsigned char buf[3]; int rc; buf[0] = cSyncRestart; - if (rbds) + if (mmbs) buf[0] |= cProcessingModeRBDS; buf[1] = cFlywheelDefault; @@ -374,8 +386,6 @@ static int saa6588_configure(struct saa6588 *s) rc = i2c_master_send(client, buf, 3); if (rc != 3) printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc); - - return 0; } /* ---------------------------------------------------------------------- */ @@ -416,6 +426,24 @@ static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) return 0; } +static int saa6588_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa6588 *s = to_saa6588(sd); + + vt->capability |= V4L2_TUNER_CAP_RDS; + if (s->sync) + vt->rxsubchans |= V4L2_TUNER_SUB_RDS; + return 0; +} + +static int saa6588_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa6588 *s = to_saa6588(sd); + + saa6588_configure(s); + return 0; +} + static int saa6588_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -430,8 +458,14 @@ static const struct v4l2_subdev_core_ops saa6588_core_ops = { .ioctl = saa6588_ioctl, }; +static const struct v4l2_subdev_tuner_ops saa6588_tuner_ops = { + .g_tuner = saa6588_g_tuner, + .s_tuner = saa6588_s_tuner, +}; + static const struct v4l2_subdev_ops saa6588_ops = { .core = &saa6588_core_ops, + .tuner = &saa6588_tuner_ops, }; /* ---------------------------------------------------------------------- */ diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 5bcce092e804..22bfd62c9551 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -47,6 +47,7 @@ config VIDEO_SAA7134_DVB select DVB_TDA10048 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE + select DVB_ZL10039 if !DVB_FE_CUSTOMISE ---help--- This adds support for DVB cards based on the Philips saa7134 chip. diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c index 63c4b8f1f541..1eabff6b2456 100644 --- a/drivers/media/video/saa7134/saa6752hs.c +++ b/drivers/media/video/saa7134/saa6752hs.c @@ -468,7 +468,7 @@ static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params, if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3)) return -ERANGE; - new = old; + params->au_encoding = new; break; case V4L2_CID_MPEG_AUDIO_L2_BITRATE: old = params->au_l2_bitrate; diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index 8b0b64a89874..d48c450ed77c 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -40,6 +40,7 @@ MODULE_PARM_DESC(debug,"enable debug messages [alsa]"); */ /* defaults */ +#define MIXER_ADDR_UNSELECTED -1 #define MIXER_ADDR_TVTUNER 0 #define MIXER_ADDR_LINE1 1 #define MIXER_ADDR_LINE2 2 @@ -68,7 +69,9 @@ typedef struct snd_card_saa7134 { struct snd_card *card; spinlock_t mixer_lock; int mixer_volume[MIXER_ADDR_LAST+1][2]; - int capture_source[MIXER_ADDR_LAST+1][2]; + int capture_source_addr; + int capture_source[2]; + struct snd_kcontrol *capture_ctl[MIXER_ADDR_LAST+1]; struct pci_dev *pci; struct saa7134_dev *dev; @@ -314,6 +317,115 @@ static int dsp_buffer_free(struct saa7134_dev *dev) return 0; } +/* + * Setting the capture source and updating the ALSA controls + */ +static int snd_saa7134_capsrc_set(struct snd_kcontrol *kcontrol, + int left, int right, bool force_notify) +{ + snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); + int change = 0, addr = kcontrol->private_value; + int active, old_addr; + u32 anabar, xbarin; + int analog_io, rate; + struct saa7134_dev *dev; + + dev = chip->dev; + + spin_lock_irq(&chip->mixer_lock); + + active = left != 0 || right != 0; + old_addr = chip->capture_source_addr; + + /* The active capture source cannot be deactivated */ + if (active) { + change = old_addr != addr || + chip->capture_source[0] != left || + chip->capture_source[1] != right; + + chip->capture_source[0] = left; + chip->capture_source[1] = right; + chip->capture_source_addr = addr; + dev->dmasound.input = addr; + } + spin_unlock_irq(&chip->mixer_lock); + + if (change) { + switch (dev->pci->device) { + + case PCI_DEVICE_ID_PHILIPS_SAA7134: + switch (addr) { + case MIXER_ADDR_TVTUNER: + saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, + 0xc0, 0xc0); + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, + 0x03, 0x00); + break; + case MIXER_ADDR_LINE1: + case MIXER_ADDR_LINE2: + analog_io = (MIXER_ADDR_LINE1 == addr) ? + 0x00 : 0x08; + rate = (32000 == dev->dmasound.rate) ? + 0x01 : 0x03; + saa_andorb(SAA7134_ANALOG_IO_SELECT, + 0x08, analog_io); + saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, + 0xc0, 0x80); + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, + 0x03, rate); + break; + } + + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + xbarin = 0x03; /* adc */ + anabar = 0; + switch (addr) { + case MIXER_ADDR_TVTUNER: + xbarin = 0; /* Demodulator */ + anabar = 2; /* DACs */ + break; + case MIXER_ADDR_LINE1: + anabar = 0; /* aux1, aux1 */ + break; + case MIXER_ADDR_LINE2: + anabar = 9; /* aux2, aux2 */ + break; + } + + /* output xbar always main channel */ + saa_dsp_writel(dev, SAA7133_DIGITAL_OUTPUT_SEL1, + 0xbbbb10); + + if (left || right) { + /* We've got data, turn the input on */ + saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, + xbarin); + saa_writel(SAA7133_ANALOG_IO_SELECT, anabar); + } else { + saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, + 0); + saa_writel(SAA7133_ANALOG_IO_SELECT, 0); + } + break; + } + } + + if (change) { + if (force_notify) + snd_ctl_notify(chip->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &chip->capture_ctl[addr]->id); + + if (old_addr != MIXER_ADDR_UNSELECTED && old_addr != addr) + snd_ctl_notify(chip->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &chip->capture_ctl[old_addr]->id); + } + + return change; +} /* * ALSA PCM preparation @@ -401,6 +513,10 @@ static int snd_card_saa7134_capture_prepare(struct snd_pcm_substream * substream dev->dmasound.rate = runtime->rate; + /* Setup and update the card/ALSA controls */ + snd_saa7134_capsrc_set(saa7134->capture_ctl[dev->dmasound.input], 1, 1, + true); + return 0; } @@ -435,6 +551,16 @@ snd_card_saa7134_capture_pointer(struct snd_pcm_substream * substream) /* * ALSA hardware capabilities definition + * + * Report only 32kHz for ALSA: + * + * - SAA7133/35 uses DDEP (DemDec Easy Programming mode), which works in 32kHz + * only + * - SAA7134 for TV mode uses DemDec mode (32kHz) + * - Radio works in 32kHz only + * - When recording 48kHz from Line1/Line2, switching of capture source to TV + * means + * switching to 32kHz without any frequency translation */ static struct snd_pcm_hardware snd_card_saa7134_capture = @@ -448,9 +574,9 @@ static struct snd_pcm_hardware snd_card_saa7134_capture = SNDRV_PCM_FMTBIT_U8 | \ SNDRV_PCM_FMTBIT_U16_LE | \ SNDRV_PCM_FMTBIT_U16_BE, - .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000, + .rates = SNDRV_PCM_RATE_32000, .rate_min = 32000, - .rate_max = 48000, + .rate_max = 32000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (256*1024), @@ -836,8 +962,13 @@ static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol, int addr = kcontrol->private_value; spin_lock_irq(&chip->mixer_lock); - ucontrol->value.integer.value[0] = chip->capture_source[addr][0]; - ucontrol->value.integer.value[1] = chip->capture_source[addr][1]; + if (chip->capture_source_addr == addr) { + ucontrol->value.integer.value[0] = chip->capture_source[0]; + ucontrol->value.integer.value[1] = chip->capture_source[1]; + } else { + ucontrol->value.integer.value[0] = 0; + ucontrol->value.integer.value[1] = 0; + } spin_unlock_irq(&chip->mixer_lock); return 0; @@ -846,87 +977,22 @@ static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol, static int snd_saa7134_capsrc_put(struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol) { - snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); - int change, addr = kcontrol->private_value; int left, right; - u32 anabar, xbarin; - int analog_io, rate; - struct saa7134_dev *dev; - - dev = chip->dev; - left = ucontrol->value.integer.value[0] & 1; right = ucontrol->value.integer.value[1] & 1; - spin_lock_irq(&chip->mixer_lock); - - change = chip->capture_source[addr][0] != left || - chip->capture_source[addr][1] != right; - chip->capture_source[addr][0] = left; - chip->capture_source[addr][1] = right; - dev->dmasound.input=addr; - spin_unlock_irq(&chip->mixer_lock); - - - if (change) { - switch (dev->pci->device) { - - case PCI_DEVICE_ID_PHILIPS_SAA7134: - switch (addr) { - case MIXER_ADDR_TVTUNER: - saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0xc0); - saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x00); - break; - case MIXER_ADDR_LINE1: - case MIXER_ADDR_LINE2: - analog_io = (MIXER_ADDR_LINE1 == addr) ? 0x00 : 0x08; - rate = (32000 == dev->dmasound.rate) ? 0x01 : 0x03; - saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, analog_io); - saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, 0x80); - saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, rate); - break; - } - - break; - case PCI_DEVICE_ID_PHILIPS_SAA7133: - case PCI_DEVICE_ID_PHILIPS_SAA7135: - xbarin = 0x03; // adc - anabar = 0; - switch (addr) { - case MIXER_ADDR_TVTUNER: - xbarin = 0; // Demodulator - anabar = 2; // DACs - break; - case MIXER_ADDR_LINE1: - anabar = 0; // aux1, aux1 - break; - case MIXER_ADDR_LINE2: - anabar = 9; // aux2, aux2 - break; - } - - /* output xbar always main channel */ - saa_dsp_writel(dev, SAA7133_DIGITAL_OUTPUT_SEL1, 0xbbbb10); - if (left || right) { // We've got data, turn the input on - saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, xbarin); - saa_writel(SAA7133_ANALOG_IO_SELECT, anabar); - } else { - saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, 0); - saa_writel(SAA7133_ANALOG_IO_SELECT, 0); - } - break; - } - } - - return change; + return snd_saa7134_capsrc_set(kcontrol, left, right, false); } -static struct snd_kcontrol_new snd_saa7134_controls[] = { +static struct snd_kcontrol_new snd_saa7134_volume_controls[] = { SAA713x_VOLUME("Video Volume", 0, MIXER_ADDR_TVTUNER), -SAA713x_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER), SAA713x_VOLUME("Line Volume", 1, MIXER_ADDR_LINE1), -SAA713x_CAPSRC("Line Capture Switch", 1, MIXER_ADDR_LINE1), SAA713x_VOLUME("Line Volume", 2, MIXER_ADDR_LINE2), +}; + +static struct snd_kcontrol_new snd_saa7134_capture_controls[] = { +SAA713x_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER), +SAA713x_CAPSRC("Line Capture Switch", 1, MIXER_ADDR_LINE1), SAA713x_CAPSRC("Line Capture Switch", 2, MIXER_ADDR_LINE2), }; @@ -941,17 +1007,33 @@ SAA713x_CAPSRC("Line Capture Switch", 2, MIXER_ADDR_LINE2), static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) { struct snd_card *card = chip->card; + struct snd_kcontrol *kcontrol; unsigned int idx; - int err; + int err, addr; if (snd_BUG_ON(!chip)) return -EINVAL; strcpy(card->mixername, "SAA7134 Mixer"); - for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_controls); idx++) { - if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_saa7134_controls[idx], chip))) < 0) + for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_volume_controls); idx++) { + kcontrol = snd_ctl_new1(&snd_saa7134_volume_controls[idx], + chip); + err = snd_ctl_add(card, kcontrol); + if (err < 0) return err; } + + for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_capture_controls); idx++) { + kcontrol = snd_ctl_new1(&snd_saa7134_capture_controls[idx], + chip); + addr = snd_saa7134_capture_controls[idx].private_value; + chip->capture_ctl[addr] = kcontrol; + err = snd_ctl_add(card, kcontrol); + if (err < 0) + return err; + } + + chip->capture_source_addr = MIXER_ADDR_UNSELECTED; return 0; } diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 6eebe3ef97d3..1b29487fd254 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -32,6 +32,7 @@ #include <media/tveeprom.h> #include "tea5767.h" #include "tda18271.h" +#include "xc5000.h" /* commly used strings */ static char name_mute[] = "mute"; @@ -265,6 +266,56 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x10000, }, }, + [SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM] = { + /* RoverMedia TV Link Pro FM (LR138 REV:I) */ + /* Eugene Yudin <Eugene.Yudin@gmail.com> */ + .name = "RoverMedia TV Link Pro FM", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MFPE05 2 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0xe000, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x8000, + .tv = 1, + }, { + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x4000, + }, { + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + .gpio = 0x4000, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x4000, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x2000, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x8000, + }, + }, [SAA7134_BOARD_EMPRESS] = { /* "Gert Vervoort" <gert.vervoort@philips.com> */ .name = "EMPRESS", @@ -1364,6 +1415,42 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE1, }, }, + [SAA7134_BOARD_AVERMEDIA_STUDIO_505] = { + /* Vasiliy Temnikov <vaka@newmail.ru> */ + .name = "AverMedia AverTV Studio 505", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, [SAA7134_BOARD_UPMOST_PURPLE_TV] = { .name = "UPMOST PURPLE TV", .audio_clock = 0x00187de7, @@ -1633,7 +1720,7 @@ struct saa7134_board saa7134_boards[] = { }}, .radio = { .name = name_radio, - .amux = LINE1, + .amux = TV, .gpio = 0x00300001, }, .mute = { @@ -3663,8 +3750,8 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, .gpio = 0x0200000, }, - }, - [SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = { + }, + [SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = { .name = "ASUSTeK P7131 Analog", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, @@ -4081,6 +4168,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .gpiomask = 0x00008000, .inputs = {{ @@ -4145,6 +4233,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .gpiomask = 0x00008000, .inputs = {{ @@ -4175,6 +4264,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .gpiomask = 0x00008000, .inputs = {{ @@ -4350,6 +4440,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, @@ -4378,6 +4469,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, @@ -4406,6 +4498,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, @@ -4434,6 +4527,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, @@ -4540,6 +4634,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .empress_addr = 0x20, .tda9887_conf = TDA9887_PRESENT, .inputs = { { @@ -4861,7 +4956,7 @@ struct saa7134_board saa7134_boards[] = { /* Igor Kuznetsov <igk@igk.ru> */ .name = "Beholder BeholdTV H6", .audio_clock = 0x00187de7, - .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -5116,6 +5211,53 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x00, }, }, + [SAA7134_BOARD_VIDEOMATE_S350] = { + /* Jan D. Louw <jd.louw@mweb.co.za */ + .name = "Compro VideoMate S350/S300", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, /* Not tested */ + .amux = LINE1 + } }, + }, + [SAA7134_BOARD_BEHOLD_X7] = { + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV X7", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC5000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = { { + .name = name_tv, + .vmux = 2, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 9, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, + }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -5374,6 +5516,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa115, + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_505, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ .subdevice = 0x2108, .driver_data = SAA7134_BOARD_AVERMEDIA_305, },{ @@ -6223,7 +6371,24 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x1461, /* Avermedia Technologies Inc */ .subdevice = 0xf31d, .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS, - + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x185b, + .subdevice = 0xc900, + .driver_data = SAA7134_BOARD_VIDEOMATE_S350, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ + .subdevice = 0x7595, + .driver_data = SAA7134_BOARD_BEHOLD_X7, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x19d1, /* RoverMedia */ + .subdevice = 0x0138, /* LifeView FlyTV Prime30 OEM */ + .driver_data = SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM, }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -6310,6 +6475,32 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev, return -EINVAL; } +static int saa7134_xc5000_callback(struct saa7134_dev *dev, + int command, int arg) +{ + switch (dev->board) { + case SAA7134_BOARD_BEHOLD_X7: + if (command == XC5000_TUNER_RESET) { + /* Down and UP pheripherial RESET pin for reset all chips */ + saa_writeb(SAA7134_SPECIAL_MODE, 0x00); + msleep(10); + saa_writeb(SAA7134_SPECIAL_MODE, 0x01); + msleep(10); + } + break; + default: + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); + 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); + break; + } + return 0; +} static int saa7134_tda8290_827x_callback(struct saa7134_dev *dev, int command, int arg) @@ -6406,6 +6597,8 @@ int saa7134_tuner_callback(void *priv, int component, int command, int arg) return saa7134_tda8290_callback(dev, command, arg); case TUNER_XC2028: return saa7134_xc2028_callback(dev, command, arg); + case TUNER_XC5000: + return saa7134_xc5000_callback(dev, command, arg); } } else { printk(KERN_ERR "saa7134: Error - device struct undefined.\n"); @@ -6476,6 +6669,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: case SAA7134_BOARD_KWORLD_XPERT: case SAA7134_BOARD_AVERMEDIA_STUDIO_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_505: case SAA7134_BOARD_AVERMEDIA_305: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: case SAA7134_BOARD_AVERMEDIA_307: @@ -6500,7 +6694,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: - case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: + case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_PROTEUS_2309: case SAA7134_BOARD_AVERMEDIA_A16AR: @@ -6525,6 +6719,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_REAL_ANGEL_220: case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: + case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -6653,6 +6848,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_M63: case SAA7134_BOARD_BEHOLD_M6_EXTRA: case SAA7134_BOARD_BEHOLD_H6: + case SAA7134_BOARD_BEHOLD_X7: dev->has_remote = SAA7134_REMOTE_I2C; break; case SAA7134_BOARD_AVERMEDIA_A169_B: @@ -6673,6 +6869,11 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100); break; + case SAA7134_BOARD_VIDEOMATE_S350: + dev->has_remote = SAA7134_REMOTE_GPIO; + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00008000, 0x00008000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000); + break; } return 0; } diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 94a023a14bbc..cb78c956d810 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -1012,8 +1012,10 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, sd = v4l2_i2c_new_probed_subdev_addr(&dev->v4l2_dev, &dev->i2c_adap, "saa6588", "saa6588", saa7134_boards[dev->board].rds_addr); - if (sd) + if (sd) { printk(KERN_INFO "%s: found RDS decoder\n", dev->name); + dev->has_rds = 1; + } } request_submodules(dev); diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 98f3efd1e944..ebde21dba7e3 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -56,6 +56,7 @@ #include "zl10353.h" #include "zl10036.h" +#include "zl10039.h" #include "mt312.h" MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); @@ -968,6 +969,10 @@ static struct zl10036_config avertv_a700_tuner = { .tuner_address = 0x60, }; +static struct mt312_config zl10313_compro_s350_config = { + .demod_address = 0x0e, +}; + static struct lgdt3305_config hcw_lgdt3305_config = { .i2c_addr = 0x0e, .mpeg_mode = LGDT3305_MPEG_SERIAL, @@ -1457,7 +1462,7 @@ static int dvb_init(struct saa7134_dev *dev) if (fe0->dvb.frontend) { dvb_attach(simple_tuner_attach, fe0->dvb.frontend, &dev->i2c_adap, 0x61, - TUNER_PHILIPS_FMD1216ME_MK3); + TUNER_PHILIPS_FMD1216MEX_MK3); } break; case SAA7134_BOARD_AVERMEDIA_A700_PRO: @@ -1473,6 +1478,16 @@ static int dvb_init(struct saa7134_dev *dev) } } break; + case SAA7134_BOARD_VIDEOMATE_S350: + fe0->dvb.frontend = dvb_attach(mt312_attach, + &zl10313_compro_s350_config, &dev->i2c_adap); + if (fe0->dvb.frontend) + if (dvb_attach(zl10039_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap) == NULL) + wprintk("%s: No zl10039 found!\n", + __func__); + + break; default: wprintk("Huh? unknown DVB card?\n"); break; diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 6e219c2db841..65fb7b17b678 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -415,6 +415,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_FLYVIDEO3000: case SAA7134_BOARD_FLYTVPLATINUM_FM: case SAA7134_BOARD_FLYTVPLATINUM_MINI2: + case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: ir_codes = ir_codes_flyvideo; mask_keycode = 0xEC00000; mask_keydown = 0x0040000; @@ -445,6 +446,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_305: case SAA7134_BOARD_AVERMEDIA_307: case SAA7134_BOARD_AVERMEDIA_STUDIO_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_505: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: case SAA7134_BOARD_AVERMEDIA_STUDIO_507: case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA: @@ -564,7 +566,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: - case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: + case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: ir_codes = ir_codes_asus_pc39; mask_keydown = 0x0040000; rc5_gpio = 1; @@ -605,6 +607,11 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0x7f; polling = 40; /* ms */ break; + case SAA7134_BOARD_VIDEOMATE_S350: + ir_codes = ir_codes_videomate_s350; + mask_keycode = 0x003f00; + mask_keydown = 0x040000; + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", @@ -716,9 +723,11 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) if (pinnacle_remote == 0) { init_data.get_key = get_key_pinnacle_color; init_data.ir_codes = ir_codes_pinnacle_color; + info.addr = 0x47; } else { init_data.get_key = get_key_pinnacle_grey; init_data.ir_codes = ir_codes_pinnacle_grey; + info.addr = 0x47; } break; case SAA7134_BOARD_UPMOST_PURPLE_TV: @@ -757,6 +766,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_M63: case SAA7134_BOARD_BEHOLD_M6_EXTRA: case SAA7134_BOARD_BEHOLD_H6: + case SAA7134_BOARD_BEHOLD_X7: init_data.name = "BeholdTV"; init_data.get_key = get_key_beholdm6xx; init_data.ir_codes = ir_codes_behold; diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index ba87128542e0..da26f476a302 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1444,7 +1444,6 @@ video_poll(struct file *file, struct poll_table_struct *wait) fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf); fh->cap.read_off = 0; } - mutex_unlock(&fh->cap.vb_lock); buf = fh->cap.read_buf; } @@ -1790,7 +1789,7 @@ static int saa7134_s_input(struct file *file, void *priv, unsigned int i) if (0 != err) return err; - if (i < 0 || i >= SAA7134_INPUT_MAX) + if (i >= SAA7134_INPUT_MAX) return -EINVAL; if (NULL == card_in(dev, i).name) return -EINVAL; @@ -1819,6 +1818,8 @@ static int saa7134_querycap(struct file *file, void *priv, V4L2_CAP_READWRITE | V4L2_CAP_STREAMING | V4L2_CAP_TUNER; + if (dev->has_rds) + cap->capabilities |= V4L2_CAP_RDS_CAPTURE; if (saa7134_no_overlay <= 0) cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index fb564f14887c..ac74903a5bd4 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -292,6 +292,10 @@ struct saa7134_format { #define SAA7134_BOARD_BEHOLD_607RDS_MK5 166 #define SAA7134_BOARD_BEHOLD_609RDS_MK3 167 #define SAA7134_BOARD_BEHOLD_609RDS_MK5 168 +#define SAA7134_BOARD_VIDEOMATE_S350 169 +#define SAA7134_BOARD_AVERMEDIA_STUDIO_505 170 +#define SAA7134_BOARD_BEHOLD_X7 171 +#define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -539,6 +543,7 @@ struct saa7134_dev { struct i2c_adapter i2c_adap; struct i2c_client i2c_client; unsigned char eedata[256]; + int has_rds; /* video overlay */ struct v4l2_framebuffer ovbuf; diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 61c47b824083..c56bf16de593 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -74,6 +74,13 @@ #define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */ #define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */ +#undef DEBUG_GEOMETRY +#ifdef DEBUG_GEOMETRY +#define dev_geo dev_info +#else +#define dev_geo dev_dbg +#endif + /* per video frame buffer */ struct sh_mobile_ceu_buffer { struct videobuf_buffer vb; /* v4l buffer must be first */ @@ -92,10 +99,21 @@ struct sh_mobile_ceu_dev { spinlock_t lock; struct list_head capture; struct videobuf_buffer *active; - int is_interlaced; struct sh_mobile_ceu_info *pdata; + u32 cflcr; + + unsigned int is_interlaced:1; + unsigned int image_mode:1; + unsigned int is_16bit:1; +}; + +struct sh_mobile_ceu_cam { + struct v4l2_rect ceu_rect; + unsigned int cam_width; + unsigned int cam_height; + const struct soc_camera_data_format *extra_fmt; const struct soc_camera_data_format *camera_fmt; }; @@ -146,7 +164,8 @@ static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq, struct sh_mobile_ceu_dev *pcdev = ici->priv; int bytes_per_pixel = (icd->current_fmt->depth + 7) >> 3; - *size = PAGE_ALIGN(icd->width * icd->height * bytes_per_pixel); + *size = PAGE_ALIGN(icd->user_width * icd->user_height * + bytes_per_pixel); if (0 == *count) *count = 2; @@ -156,7 +175,7 @@ static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq, (*count)--; } - dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size); return 0; } @@ -165,8 +184,9 @@ static void free_buffer(struct videobuf_queue *vq, struct sh_mobile_ceu_buffer *buf) { struct soc_camera_device *icd = vq->priv_data; + struct device *dev = icd->dev.parent; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, + dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, &buf->vb, buf->vb.baddr, buf->vb.bsize); if (in_interrupt()) @@ -174,7 +194,7 @@ static void free_buffer(struct videobuf_queue *vq, videobuf_waiton(&buf->vb, 0, 0); videobuf_dma_contig_free(vq, &buf->vb); - dev_dbg(&icd->dev, "%s freed\n", __func__); + dev_dbg(dev, "%s freed\n", __func__); buf->vb.state = VIDEOBUF_NEEDS_INIT; } @@ -205,7 +225,7 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) phys_addr_top = videobuf_to_dma_contig(pcdev->active); ceu_write(pcdev, CDAYR, phys_addr_top); if (pcdev->is_interlaced) { - phys_addr_bottom = phys_addr_top + icd->width; + phys_addr_bottom = phys_addr_top + icd->user_width; ceu_write(pcdev, CDBYR, phys_addr_bottom); } @@ -214,10 +234,12 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: - phys_addr_top += icd->width * icd->height; + phys_addr_top += icd->user_width * + icd->user_height; ceu_write(pcdev, CDACR, phys_addr_top); if (pcdev->is_interlaced) { - phys_addr_bottom = phys_addr_top + icd->width; + phys_addr_bottom = phys_addr_top + + icd->user_width; ceu_write(pcdev, CDBCR, phys_addr_bottom); } } @@ -236,7 +258,7 @@ static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, buf = container_of(vb, struct sh_mobile_ceu_buffer, vb); - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, vb, vb->baddr, vb->bsize); /* Added list head initialization on alloc */ @@ -251,12 +273,12 @@ static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, BUG_ON(NULL == icd->current_fmt); if (buf->fmt != icd->current_fmt || - vb->width != icd->width || - vb->height != icd->height || + vb->width != icd->user_width || + vb->height != icd->user_height || vb->field != field) { buf->fmt = icd->current_fmt; - vb->width = icd->width; - vb->height = icd->height; + vb->width = icd->user_width; + vb->height = icd->user_height; vb->field = field; vb->state = VIDEOBUF_NEEDS_INIT; } @@ -289,7 +311,7 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, + dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, vb, vb->baddr, vb->bsize); vb->state = VIDEOBUF_QUEUED; @@ -304,6 +326,27 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, static void sh_mobile_ceu_videobuf_release(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 sh_mobile_ceu_dev *pcdev = ici->priv; + unsigned long flags; + + spin_lock_irqsave(&pcdev->lock, flags); + + if (pcdev->active == vb) { + /* disable capture (release DMA buffer), reset */ + ceu_write(pcdev, CAPSR, 1 << 16); + pcdev->active = NULL; + } + + if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) && + !list_empty(&vb->queue)) { + vb->state = VIDEOBUF_ERROR; + list_del_init(&vb->queue); + } + + spin_unlock_irqrestore(&pcdev->lock, flags); + free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb)); } @@ -323,6 +366,10 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) spin_lock_irqsave(&pcdev->lock, flags); vb = pcdev->active; + if (!vb) + /* Stale interrupt from a released buffer */ + goto out; + list_del_init(&vb->queue); if (!list_empty(&pcdev->capture)) @@ -337,6 +384,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) do_gettimeofday(&vb->ts); vb->field_count++; wake_up(&vb->done); + +out: spin_unlock_irqrestore(&pcdev->lock, flags); return IRQ_HANDLED; @@ -347,19 +396,14 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - int ret = -EBUSY; if (pcdev->icd) - goto err; + return -EBUSY; - dev_info(&icd->dev, + dev_info(icd->dev.parent, "SuperH Mobile CEU driver attached to camera %d\n", icd->devnum); - ret = icd->ops->init(icd); - if (ret) - goto err; - pm_runtime_get_sync(ici->dev); ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ @@ -367,8 +411,8 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) msleep(1); pcdev->icd = icd; -err: - return ret; + + return 0; } /* Called with .video_lock held */ @@ -396,23 +440,149 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) pm_runtime_put_sync(ici->dev); - icd->ops->release(icd); - - dev_info(&icd->dev, + dev_info(icd->dev.parent, "SuperH Mobile CEU driver detached from camera %d\n", icd->devnum); pcdev->icd = NULL; } +/* + * See chapter 29.4.12 "Capture Filter Control Register (CFLCR)" + * in SH7722 Hardware Manual + */ +static unsigned int size_dst(unsigned int src, unsigned int scale) +{ + unsigned int mant_pre = scale >> 12; + if (!src || !scale) + return src; + return ((mant_pre + 2 * (src - 1)) / (2 * mant_pre) - 1) * + mant_pre * 4096 / scale + 1; +} + +static u16 calc_scale(unsigned int src, unsigned int *dst) +{ + u16 scale; + + if (src == *dst) + return 0; + + scale = (src * 4096 / *dst) & ~7; + + while (scale > 4096 && size_dst(src, scale) < *dst) + scale -= 8; + + *dst = size_dst(src, scale); + + return scale; +} + +/* rect is guaranteed to not exceed the scaled camera rectangle */ +static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd, + unsigned int out_width, + unsigned int out_height) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct v4l2_rect *rect = &cam->ceu_rect; + struct sh_mobile_ceu_dev *pcdev = ici->priv; + unsigned int height, width, cdwdr_width, in_width, in_height; + unsigned int left_offset, top_offset; + u32 camor; + + dev_dbg(icd->dev.parent, "Crop %ux%u@%u:%u\n", + rect->width, rect->height, rect->left, rect->top); + + left_offset = rect->left; + top_offset = rect->top; + + if (pcdev->image_mode) { + in_width = rect->width; + if (!pcdev->is_16bit) { + in_width *= 2; + left_offset *= 2; + } + width = cdwdr_width = out_width; + } else { + unsigned int w_factor = (icd->current_fmt->depth + 7) >> 3; + + width = out_width * w_factor / 2; + + if (!pcdev->is_16bit) + w_factor *= 2; + + in_width = rect->width * w_factor / 2; + left_offset = left_offset * w_factor / 2; + + cdwdr_width = width * 2; + } + + height = out_height; + in_height = rect->height; + if (pcdev->is_interlaced) { + height /= 2; + in_height /= 2; + top_offset /= 2; + cdwdr_width *= 2; + } + + /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */ + camor = left_offset | (top_offset << 16); + + dev_geo(icd->dev.parent, + "CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor, + (in_height << 16) | in_width, (height << 16) | width, + cdwdr_width); + + ceu_write(pcdev, CAMOR, camor); + ceu_write(pcdev, CAPWR, (in_height << 16) | in_width); + ceu_write(pcdev, CFSZR, (height << 16) | width); + ceu_write(pcdev, CDWDR, cdwdr_width); +} + +static u32 capture_save_reset(struct sh_mobile_ceu_dev *pcdev) +{ + u32 capsr = ceu_read(pcdev, CAPSR); + ceu_write(pcdev, CAPSR, 1 << 16); /* reset, stop capture */ + return capsr; +} + +static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr) +{ + unsigned long timeout = jiffies + 10 * HZ; + + /* + * Wait until the end of the current frame. It can take a long time, + * but if it has been aborted by a CAPSR reset, it shoule exit sooner. + */ + while ((ceu_read(pcdev, CSTSR) & 1) && time_before(jiffies, timeout)) + msleep(1); + + if (time_after(jiffies, timeout)) { + dev_err(pcdev->ici.v4l2_dev.dev, + "Timeout waiting for frame end! Interface problem?\n"); + return; + } + + /* Wait until reset clears, this shall not hang... */ + while (ceu_read(pcdev, CAPSR) & (1 << 16)) + udelay(10); + + /* Anything to restore? */ + if (capsr & ~(1 << 16)) + ceu_write(pcdev, CAPSR, capsr); +} + static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - int ret, buswidth, width, height, cfszr_width, cdwdr_width; + int ret; unsigned long camera_flags, common_flags, value; - int yuv_mode, yuv_lineskip; + int yuv_lineskip; + struct sh_mobile_ceu_cam *cam = icd->host_priv; + u32 capsr = capture_save_reset(pcdev); camera_flags = icd->ops->query_bus_param(icd); common_flags = soc_camera_bus_param_compatible(camera_flags, @@ -426,10 +596,10 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, switch (common_flags & SOCAM_DATAWIDTH_MASK) { case SOCAM_DATAWIDTH_8: - buswidth = 8; + pcdev->is_16bit = 0; break; case SOCAM_DATAWIDTH_16: - buswidth = 16; + pcdev->is_16bit = 1; break; default: return -EINVAL; @@ -439,7 +609,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, ceu_write(pcdev, CRCMPR, 0); value = 0x00000010; /* data fetch by default */ - yuv_mode = yuv_lineskip = 0; + yuv_lineskip = 0; switch (icd->current_fmt->fourcc) { case V4L2_PIX_FMT_NV12: @@ -448,8 +618,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, /* fall-through */ case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: - yuv_mode = 1; - switch (pcdev->camera_fmt->fourcc) { + switch (cam->camera_fmt->fourcc) { case V4L2_PIX_FMT_UYVY: value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */ break; @@ -473,36 +642,16 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; - value |= buswidth == 16 ? 1 << 12 : 0; + value |= pcdev->is_16bit ? 1 << 12 : 0; ceu_write(pcdev, CAMCR, value); ceu_write(pcdev, CAPCR, 0x00300000); ceu_write(pcdev, CAIFR, pcdev->is_interlaced ? 0x101 : 0); + sh_mobile_ceu_set_rect(icd, icd->user_width, icd->user_height); mdelay(1); - if (yuv_mode) { - width = icd->width * 2; - width = buswidth == 16 ? width / 2 : width; - cfszr_width = cdwdr_width = icd->width; - } else { - width = icd->width * ((icd->current_fmt->depth + 7) >> 3); - width = buswidth == 16 ? width / 2 : width; - cfszr_width = buswidth == 8 ? width / 2 : width; - cdwdr_width = buswidth == 16 ? width * 2 : width; - } - - height = icd->height; - if (pcdev->is_interlaced) { - height /= 2; - cdwdr_width *= 2; - } - - ceu_write(pcdev, CAMOR, 0); - ceu_write(pcdev, CAPWR, (height << 16) | width); - ceu_write(pcdev, CFLCR, 0); /* no scaling */ - ceu_write(pcdev, CFSZR, (height << 16) | cfszr_width); - ceu_write(pcdev, CLFCR, 0); /* no lowpass filter */ + ceu_write(pcdev, CFLCR, pcdev->cflcr); /* A few words about byte order (observed in Big Endian mode) * @@ -521,10 +670,15 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */ ceu_write(pcdev, CDOCR, value); - - ceu_write(pcdev, CDWDR, cdwdr_width); ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */ + dev_dbg(icd->dev.parent, "S_FMT successful for %c%c%c%c %ux%u\n", + pixfmt & 0xff, (pixfmt >> 8) & 0xff, + (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff, + icd->user_width, icd->user_height); + + capture_restore(pcdev, capsr); + /* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */ return 0; } @@ -574,24 +728,35 @@ static const struct soc_camera_data_format sh_mobile_ceu_formats[] = { static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, struct soc_camera_format_xlate *xlate) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct device *dev = icd->dev.parent; int ret, k, n; int formats = 0; + struct sh_mobile_ceu_cam *cam; ret = sh_mobile_ceu_try_bus_param(icd); if (ret < 0) return 0; + if (!icd->host_priv) { + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) + return -ENOMEM; + + icd->host_priv = cam; + } else { + cam = icd->host_priv; + } + /* Beginning of a pass */ if (!idx) - icd->host_priv = NULL; + cam->extra_fmt = NULL; switch (icd->formats[idx].fourcc) { case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: - if (icd->host_priv) + if (cam->extra_fmt) goto add_single_format; /* @@ -603,7 +768,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, * the host_priv pointer and check whether the format you're * going to add now is already there. */ - icd->host_priv = (void *)sh_mobile_ceu_formats; + cam->extra_fmt = (void *)sh_mobile_ceu_formats; n = ARRAY_SIZE(sh_mobile_ceu_formats); formats += n; @@ -612,7 +777,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = icd->formats[idx].depth; xlate++; - dev_dbg(ici->dev, "Providing format %s using %s\n", + dev_dbg(dev, "Providing format %s using %s\n", sh_mobile_ceu_formats[k].name, icd->formats[idx].name); } @@ -625,7 +790,7 @@ add_single_format: xlate->cam_fmt = icd->formats + idx; xlate->buswidth = icd->formats[idx].depth; xlate++; - dev_dbg(ici->dev, + dev_dbg(dev, "Providing format %s in pass-through mode\n", icd->formats[idx].name); } @@ -634,82 +799,714 @@ add_single_format: return formats; } +static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd) +{ + kfree(icd->host_priv); + icd->host_priv = NULL; +} + +/* Check if any dimension of r1 is smaller than respective one of r2 */ +static bool is_smaller(struct v4l2_rect *r1, struct v4l2_rect *r2) +{ + return r1->width < r2->width || r1->height < r2->height; +} + +/* Check if r1 fails to cover r2 */ +static bool is_inside(struct v4l2_rect *r1, struct v4l2_rect *r2) +{ + return r1->left > r2->left || r1->top > r2->top || + r1->left + r1->width < r2->left + r2->width || + r1->top + r1->height < r2->top + r2->height; +} + +static unsigned int scale_down(unsigned int size, unsigned int scale) +{ + return (size * 4096 + scale / 2) / scale; +} + +static unsigned int scale_up(unsigned int size, unsigned int scale) +{ + return (size * scale + 2048) / 4096; +} + +static unsigned int calc_generic_scale(unsigned int input, unsigned int output) +{ + return (input * 4096 + output / 2) / output; +} + +static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) +{ + struct v4l2_crop crop; + struct v4l2_cropcap cap; + int ret; + + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, g_crop, &crop); + if (!ret) { + *rect = crop.c; + return ret; + } + + /* Camera driver doesn't support .g_crop(), assume default rectangle */ + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, cropcap, &cap); + if (ret < 0) + return ret; + + *rect = cap.defrect; + + return ret; +} + +/* + * The common for both scaling and cropping iterative approach is: + * 1. try if the client can produce exactly what requested by the user + * 2. if (1) failed, try to double the client image until we get one big enough + * 3. if (2) failed, try to request the maximum image + */ +static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop, + struct v4l2_crop *cam_crop) +{ + struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; + struct device *dev = sd->v4l2_dev->dev; + struct v4l2_cropcap cap; + int ret; + unsigned int width, height; + + v4l2_subdev_call(sd, video, s_crop, crop); + ret = client_g_rect(sd, cam_rect); + if (ret < 0) + return ret; + + /* + * Now cam_crop contains the current camera input rectangle, and it must + * be within camera cropcap bounds + */ + if (!memcmp(rect, cam_rect, sizeof(*rect))) { + /* Even if camera S_CROP failed, but camera rectangle matches */ + dev_dbg(dev, "Camera S_CROP successful for %ux%u@%u:%u\n", + rect->width, rect->height, rect->left, rect->top); + return 0; + } + + /* Try to fix cropping, that camera hasn't managed to set */ + dev_geo(dev, "Fix camera S_CROP for %ux%u@%u:%u to %ux%u@%u:%u\n", + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top, + rect->width, rect->height, rect->left, rect->top); + + /* We need sensor maximum rectangle */ + ret = v4l2_subdev_call(sd, video, cropcap, &cap); + if (ret < 0) + return ret; + + soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2, + cap.bounds.width); + soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4, + cap.bounds.height); + + /* + * Popular special case - some cameras can only handle fixed sizes like + * QVGA, VGA,... Take care to avoid infinite loop. + */ + width = max(cam_rect->width, 2); + height = max(cam_rect->height, 2); + + while (!ret && (is_smaller(cam_rect, rect) || + is_inside(cam_rect, rect)) && + (cap.bounds.width > width || cap.bounds.height > height)) { + + width *= 2; + height *= 2; + + cam_rect->width = width; + cam_rect->height = height; + + /* + * We do not know what capabilities the camera has to set up + * left and top borders. We could try to be smarter in iterating + * them, e.g., if camera current left is to the right of the + * target left, set it to the middle point between the current + * left and minimum left. But that would add too much + * complexity: we would have to iterate each border separately. + */ + if (cam_rect->left > rect->left) + cam_rect->left = cap.bounds.left; + + if (cam_rect->left + cam_rect->width < rect->left + rect->width) + cam_rect->width = rect->left + rect->width - + cam_rect->left; + + if (cam_rect->top > rect->top) + cam_rect->top = cap.bounds.top; + + if (cam_rect->top + cam_rect->height < rect->top + rect->height) + cam_rect->height = rect->top + rect->height - + cam_rect->top; + + v4l2_subdev_call(sd, video, s_crop, cam_crop); + ret = client_g_rect(sd, cam_rect); + dev_geo(dev, "Camera S_CROP %d for %ux%u@%u:%u\n", ret, + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top); + } + + /* S_CROP must not modify the rectangle */ + if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) { + /* + * The camera failed to configure a suitable cropping, + * we cannot use the current rectangle, set to max + */ + *cam_rect = cap.bounds; + v4l2_subdev_call(sd, video, s_crop, cam_crop); + ret = client_g_rect(sd, cam_rect); + dev_geo(dev, "Camera S_CROP %d for max %ux%u@%u:%u\n", ret, + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top); + } + + return ret; +} + +static int get_camera_scales(struct v4l2_subdev *sd, struct v4l2_rect *rect, + unsigned int *scale_h, unsigned int *scale_v) +{ + struct v4l2_format f; + int ret; + + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, g_fmt, &f); + if (ret < 0) + return ret; + + *scale_h = calc_generic_scale(rect->width, f.fmt.pix.width); + *scale_v = calc_generic_scale(rect->height, f.fmt.pix.height); + + return 0; +} + +static int get_camera_subwin(struct soc_camera_device *icd, + struct v4l2_rect *cam_subrect, + unsigned int cam_hscale, unsigned int cam_vscale) +{ + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct v4l2_rect *ceu_rect = &cam->ceu_rect; + + if (!ceu_rect->width) { + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->dev.parent; + struct v4l2_format f; + struct v4l2_pix_format *pix = &f.fmt.pix; + int ret; + /* First time */ + + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, g_fmt, &f); + if (ret < 0) + return ret; + + dev_geo(dev, "camera fmt %ux%u\n", pix->width, pix->height); + + if (pix->width > 2560) { + ceu_rect->width = 2560; + ceu_rect->left = (pix->width - 2560) / 2; + } else { + ceu_rect->width = pix->width; + ceu_rect->left = 0; + } + + if (pix->height > 1920) { + ceu_rect->height = 1920; + ceu_rect->top = (pix->height - 1920) / 2; + } else { + ceu_rect->height = pix->height; + ceu_rect->top = 0; + } + + dev_geo(dev, "initialised CEU rect %ux%u@%u:%u\n", + ceu_rect->width, ceu_rect->height, + ceu_rect->left, ceu_rect->top); + } + + cam_subrect->width = scale_up(ceu_rect->width, cam_hscale); + cam_subrect->left = scale_up(ceu_rect->left, cam_hscale); + cam_subrect->height = scale_up(ceu_rect->height, cam_vscale); + cam_subrect->top = scale_up(ceu_rect->top, cam_vscale); + + return 0; +} + +static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_format *f, + bool ceu_can_scale) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->dev.parent; + struct v4l2_pix_format *pix = &f->fmt.pix; + unsigned int width = pix->width, height = pix->height, tmp_w, tmp_h; + unsigned int max_width, max_height; + struct v4l2_cropcap cap; + int ret; + + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, cropcap, &cap); + if (ret < 0) + return ret; + + max_width = min(cap.bounds.width, 2560); + max_height = min(cap.bounds.height, 1920); + + ret = v4l2_subdev_call(sd, video, s_fmt, f); + if (ret < 0) + return ret; + + dev_geo(dev, "camera scaled to %ux%u\n", pix->width, pix->height); + + if ((width == pix->width && height == pix->height) || !ceu_can_scale) + return 0; + + /* Camera set a format, but geometry is not precise, try to improve */ + tmp_w = pix->width; + tmp_h = pix->height; + + /* width <= max_width && height <= max_height - guaranteed by try_fmt */ + while ((width > tmp_w || height > tmp_h) && + tmp_w < max_width && tmp_h < max_height) { + tmp_w = min(2 * tmp_w, max_width); + tmp_h = min(2 * tmp_h, max_height); + pix->width = tmp_w; + pix->height = tmp_h; + ret = v4l2_subdev_call(sd, video, s_fmt, f); + dev_geo(dev, "Camera scaled to %ux%u\n", + pix->width, pix->height); + if (ret < 0) { + /* This shouldn't happen */ + dev_err(dev, "Client failed to set format: %d\n", ret); + return ret; + } + } + + return 0; +} + +/** + * @rect - camera cropped rectangle + * @sub_rect - CEU cropped rectangle, mapped back to camera input area + * @ceu_rect - on output calculated CEU crop rectangle + */ +static int client_scale(struct soc_camera_device *icd, struct v4l2_rect *rect, + struct v4l2_rect *sub_rect, struct v4l2_rect *ceu_rect, + struct v4l2_format *f, bool ceu_can_scale) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct device *dev = icd->dev.parent; + struct v4l2_format f_tmp = *f; + struct v4l2_pix_format *pix_tmp = &f_tmp.fmt.pix; + unsigned int scale_h, scale_v; + int ret; + + /* 5. Apply iterative camera S_FMT for camera user window. */ + ret = client_s_fmt(icd, &f_tmp, ceu_can_scale); + if (ret < 0) + return ret; + + dev_geo(dev, "5: camera scaled to %ux%u\n", + pix_tmp->width, pix_tmp->height); + + /* 6. Retrieve camera output window (g_fmt) */ + + /* unneeded - it is already in "f_tmp" */ + + /* 7. Calculate new camera scales. */ + ret = get_camera_scales(sd, rect, &scale_h, &scale_v); + if (ret < 0) + return ret; + + dev_geo(dev, "7: camera scales %u:%u\n", scale_h, scale_v); + + cam->cam_width = pix_tmp->width; + cam->cam_height = pix_tmp->height; + f->fmt.pix.width = pix_tmp->width; + f->fmt.pix.height = pix_tmp->height; + + /* + * 8. Calculate new CEU crop - apply camera scales to previously + * calculated "effective" crop. + */ + ceu_rect->left = scale_down(sub_rect->left, scale_h); + ceu_rect->width = scale_down(sub_rect->width, scale_h); + ceu_rect->top = scale_down(sub_rect->top, scale_v); + ceu_rect->height = scale_down(sub_rect->height, scale_v); + + dev_geo(dev, "8: new CEU rect %ux%u@%u:%u\n", + ceu_rect->width, ceu_rect->height, + ceu_rect->left, ceu_rect->top); + + return 0; +} + +/* Get combined scales */ +static int get_scales(struct soc_camera_device *icd, + unsigned int *scale_h, unsigned int *scale_v) +{ + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_crop cam_crop; + unsigned int width_in, height_in; + int ret; + + cam_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = client_g_rect(sd, &cam_crop.c); + if (ret < 0) + return ret; + + ret = get_camera_scales(sd, &cam_crop.c, scale_h, scale_v); + if (ret < 0) + return ret; + + width_in = scale_up(cam->ceu_rect.width, *scale_h); + height_in = scale_up(cam->ceu_rect.height, *scale_v); + + *scale_h = calc_generic_scale(cam->ceu_rect.width, icd->user_width); + *scale_v = calc_generic_scale(cam->ceu_rect.height, icd->user_height); + + return 0; +} + +/* + * CEU can scale and crop, but we don't want to waste bandwidth and kill the + * framerate by always requesting the maximum image from the client. See + * Documentation/video4linux/sh_mobile_camera_ceu.txt for a description of + * scaling and cropping algorithms and for the meaning of referenced here steps. + */ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) + struct v4l2_crop *a) { - return icd->ops->set_crop(icd, rect); + struct v4l2_rect *rect = &a->c; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + struct v4l2_crop cam_crop; + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct v4l2_rect *cam_rect = &cam_crop.c, *ceu_rect = &cam->ceu_rect; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->dev.parent; + struct v4l2_format f; + struct v4l2_pix_format *pix = &f.fmt.pix; + unsigned int scale_comb_h, scale_comb_v, scale_ceu_h, scale_ceu_v, + out_width, out_height; + u32 capsr, cflcr; + int ret; + + /* 1. Calculate current combined scales. */ + ret = get_scales(icd, &scale_comb_h, &scale_comb_v); + if (ret < 0) + return ret; + + dev_geo(dev, "1: combined scales %u:%u\n", scale_comb_h, scale_comb_v); + + /* 2. Apply iterative camera S_CROP for new input window. */ + ret = client_s_crop(sd, a, &cam_crop); + if (ret < 0) + return ret; + + dev_geo(dev, "2: camera cropped to %ux%u@%u:%u\n", + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top); + + /* On success cam_crop contains current camera crop */ + + /* + * 3. If old combined scales applied to new crop produce an impossible + * user window, adjust scales to produce nearest possible window. + */ + out_width = scale_down(rect->width, scale_comb_h); + out_height = scale_down(rect->height, scale_comb_v); + + if (out_width > 2560) + out_width = 2560; + else if (out_width < 2) + out_width = 2; + + if (out_height > 1920) + out_height = 1920; + else if (out_height < 4) + out_height = 4; + + dev_geo(dev, "3: Adjusted output %ux%u\n", out_width, out_height); + + /* 4. Use G_CROP to retrieve actual input window: already in cam_crop */ + + /* + * 5. Using actual input window and calculated combined scales calculate + * camera target output window. + */ + pix->width = scale_down(cam_rect->width, scale_comb_h); + pix->height = scale_down(cam_rect->height, scale_comb_v); + + dev_geo(dev, "5: camera target %ux%u\n", pix->width, pix->height); + + /* 6. - 9. */ + pix->pixelformat = cam->camera_fmt->fourcc; + pix->colorspace = cam->camera_fmt->colorspace; + + capsr = capture_save_reset(pcdev); + dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr); + + /* Make relative to camera rectangle */ + rect->left -= cam_rect->left; + rect->top -= cam_rect->top; + + f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = client_scale(icd, cam_rect, rect, ceu_rect, &f, + pcdev->image_mode && !pcdev->is_interlaced); + + dev_geo(dev, "6-9: %d\n", ret); + + /* 10. Use CEU cropping to crop to the new window. */ + sh_mobile_ceu_set_rect(icd, out_width, out_height); + + dev_geo(dev, "10: CEU cropped to %ux%u@%u:%u\n", + ceu_rect->width, ceu_rect->height, + ceu_rect->left, ceu_rect->top); + + /* + * 11. Calculate CEU scales from camera scales from results of (10) and + * user window from (3) + */ + scale_ceu_h = calc_scale(ceu_rect->width, &out_width); + scale_ceu_v = calc_scale(ceu_rect->height, &out_height); + + dev_geo(dev, "11: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v); + + /* 12. Apply CEU scales. */ + cflcr = scale_ceu_h | (scale_ceu_v << 16); + if (cflcr != pcdev->cflcr) { + pcdev->cflcr = cflcr; + ceu_write(pcdev, CFLCR, cflcr); + } + + /* Restore capture */ + if (pcdev->active) + capsr |= 1; + capture_restore(pcdev, capsr); + + icd->user_width = out_width; + icd->user_height = out_height; + + /* Even if only camera cropping succeeded */ + return ret; } +/* Similar to set_crop multistage iterative algorithm */ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - __u32 pixfmt = f->fmt.pix.pixelformat; - const struct soc_camera_format_xlate *xlate; + struct sh_mobile_ceu_cam *cam = icd->host_priv; + struct v4l2_pix_format *pix = &f->fmt.pix; struct v4l2_format cam_f = *f; + struct v4l2_pix_format *cam_pix = &cam_f.fmt.pix; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->dev.parent; + __u32 pixfmt = pix->pixelformat; + const struct soc_camera_format_xlate *xlate; + struct v4l2_crop cam_crop; + struct v4l2_rect *cam_rect = &cam_crop.c, cam_subrect, ceu_rect; + unsigned int scale_cam_h, scale_cam_v; + u16 scale_v, scale_h; int ret; + bool is_interlaced, image_mode; + + switch (pix->field) { + case V4L2_FIELD_INTERLACED: + is_interlaced = true; + break; + case V4L2_FIELD_ANY: + default: + pix->field = V4L2_FIELD_NONE; + /* fall-through */ + case V4L2_FIELD_NONE: + is_interlaced = false; + break; + } xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pixfmt); + dev_warn(dev, "Format %x not found\n", pixfmt); return -EINVAL; } - cam_f.fmt.pix.pixelformat = xlate->cam_fmt->fourcc; - ret = icd->ops->set_fmt(icd, &cam_f); + /* 1. Calculate current camera scales. */ + cam_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (!ret) { - icd->buswidth = xlate->buswidth; - icd->current_fmt = xlate->host_fmt; - pcdev->camera_fmt = xlate->cam_fmt; + ret = client_g_rect(sd, cam_rect); + if (ret < 0) + return ret; + + ret = get_camera_scales(sd, cam_rect, &scale_cam_h, &scale_cam_v); + if (ret < 0) + return ret; + + dev_geo(dev, "1: camera scales %u:%u\n", scale_cam_h, scale_cam_v); + + /* + * 2. Calculate "effective" input crop (sensor subwindow) - CEU crop + * scaled back at current camera scales onto input window. + */ + ret = get_camera_subwin(icd, &cam_subrect, scale_cam_h, scale_cam_v); + if (ret < 0) + return ret; + + dev_geo(dev, "2: subwin %ux%u@%u:%u\n", + cam_subrect.width, cam_subrect.height, + cam_subrect.left, cam_subrect.top); + + /* + * 3. Calculate new combined scales from "effective" input window to + * requested user window. + */ + scale_h = calc_generic_scale(cam_subrect.width, pix->width); + scale_v = calc_generic_scale(cam_subrect.height, pix->height); + + dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); + + /* + * 4. Calculate camera output window by applying combined scales to real + * input window. + */ + cam_pix->width = scale_down(cam_rect->width, scale_h); + cam_pix->height = scale_down(cam_rect->height, scale_v); + cam_pix->pixelformat = xlate->cam_fmt->fourcc; + + switch (pixfmt) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + image_mode = true; + break; + default: + image_mode = false; } - return ret; + dev_geo(dev, "4: camera output %ux%u\n", + cam_pix->width, cam_pix->height); + + /* 5. - 9. */ + ret = client_scale(icd, cam_rect, &cam_subrect, &ceu_rect, &cam_f, + image_mode && !is_interlaced); + + dev_geo(dev, "5-9: client scale %d\n", ret); + + /* Done with the camera. Now see if we can improve the result */ + + dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n", + ret, cam_pix->width, cam_pix->height, pix->width, pix->height); + if (ret < 0) + return ret; + + /* 10. Use CEU scaling to scale to the requested user window. */ + + /* We cannot scale up */ + if (pix->width > cam_pix->width) + pix->width = cam_pix->width; + if (pix->width > ceu_rect.width) + pix->width = ceu_rect.width; + + if (pix->height > cam_pix->height) + pix->height = cam_pix->height; + if (pix->height > ceu_rect.height) + pix->height = ceu_rect.height; + + /* Let's rock: scale pix->{width x height} down to width x height */ + scale_h = calc_scale(ceu_rect.width, &pix->width); + scale_v = calc_scale(ceu_rect.height, &pix->height); + + dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n", + ceu_rect.width, scale_h, pix->width, + ceu_rect.height, scale_v, pix->height); + + pcdev->cflcr = scale_h | (scale_v << 16); + + icd->buswidth = xlate->buswidth; + icd->current_fmt = xlate->host_fmt; + cam->camera_fmt = xlate->cam_fmt; + cam->ceu_rect = ceu_rect; + + pcdev->is_interlaced = is_interlaced; + pcdev->image_mode = image_mode; + + return 0; } static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; const struct soc_camera_format_xlate *xlate; - __u32 pixfmt = f->fmt.pix.pixelformat; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + __u32 pixfmt = pix->pixelformat; + int width, height; int ret; xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(ici->dev, "Format %x not found\n", pixfmt); + dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt); return -EINVAL; } /* FIXME: calculate using depth and bus width */ - v4l_bound_align_image(&f->fmt.pix.width, 2, 2560, 1, - &f->fmt.pix.height, 4, 1920, 2, 0); + v4l_bound_align_image(&pix->width, 2, 2560, 1, + &pix->height, 4, 1920, 2, 0); + + width = pix->width; + height = pix->height; - f->fmt.pix.bytesperline = f->fmt.pix.width * + pix->bytesperline = pix->width * DIV_ROUND_UP(xlate->host_fmt->depth, 8); - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + pix->sizeimage = pix->height * pix->bytesperline; + + pix->pixelformat = xlate->cam_fmt->fourcc; /* limit to sensor capabilities */ - ret = icd->ops->try_fmt(icd, f); + ret = v4l2_subdev_call(sd, video, try_fmt, f); + pix->pixelformat = pixfmt; if (ret < 0) return ret; - switch (f->fmt.pix.field) { - case V4L2_FIELD_INTERLACED: - pcdev->is_interlaced = 1; - break; - case V4L2_FIELD_ANY: - f->fmt.pix.field = V4L2_FIELD_NONE; - /* fall-through */ - case V4L2_FIELD_NONE: - pcdev->is_interlaced = 0; - break; - default: - ret = -EINVAL; - break; + switch (pixfmt) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + /* FIXME: check against rect_max after converting soc-camera */ + /* We can scale precisely, need a bigger image from camera */ + if (pix->width < width || pix->height < height) { + int tmp_w = pix->width, tmp_h = pix->height; + pix->width = 2560; + pix->height = 1920; + ret = v4l2_subdev_call(sd, video, try_fmt, f); + if (ret < 0) { + /* Shouldn't actually happen... */ + dev_err(icd->dev.parent, + "FIXME: try_fmt() returned %d\n", ret); + pix->width = tmp_w; + pix->height = tmp_h; + } + } + if (pix->width > width) + pix->width = width; + if (pix->height > height) + pix->height = height; } return ret; @@ -769,7 +1566,7 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &sh_mobile_ceu_videobuf_ops, - ici->dev, &pcdev->lock, + icd->dev.parent, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, pcdev->is_interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE, @@ -777,22 +1574,76 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q, icd); } +static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + u32 val; + + switch (ctrl->id) { + case V4L2_CID_SHARPNESS: + val = ceu_read(pcdev, CLFCR); + ctrl->value = val ^ 1; + return 0; + } + return -ENOIOCTLCMD; +} + +static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + + switch (ctrl->id) { + case V4L2_CID_SHARPNESS: + switch (icd->current_fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + ceu_write(pcdev, CLFCR, !ctrl->value); + return 0; + } + return -EINVAL; + } + return -ENOIOCTLCMD; +} + +static const struct v4l2_queryctrl sh_mobile_ceu_controls[] = { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Low-pass filter", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, +}; + static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .owner = THIS_MODULE, .add = sh_mobile_ceu_add_device, .remove = sh_mobile_ceu_remove_device, .get_formats = sh_mobile_ceu_get_formats, + .put_formats = sh_mobile_ceu_put_formats, .set_crop = sh_mobile_ceu_set_crop, .set_fmt = sh_mobile_ceu_set_fmt, .try_fmt = sh_mobile_ceu_try_fmt, + .set_ctrl = sh_mobile_ceu_set_ctrl, + .get_ctrl = sh_mobile_ceu_get_ctrl, .reqbufs = sh_mobile_ceu_reqbufs, .poll = sh_mobile_ceu_poll, .querycap = sh_mobile_ceu_querycap, .set_bus_param = sh_mobile_ceu_set_bus_param, .init_videobuf = sh_mobile_ceu_init_videobuf, + .controls = sh_mobile_ceu_controls, + .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls), }; -static int sh_mobile_ceu_probe(struct platform_device *pdev) +static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) { struct sh_mobile_ceu_dev *pcdev; struct resource *res; @@ -865,7 +1716,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pm_runtime_resume(&pdev->dev); pcdev->ici.priv = pcdev; - pcdev->ici.dev = &pdev->dev; + pcdev->ici.v4l2_dev.dev = &pdev->dev; pcdev->ici.nr = pdev->id; pcdev->ici.drv_name = dev_name(&pdev->dev); pcdev->ici.ops = &sh_mobile_ceu_host_ops; @@ -889,7 +1740,7 @@ exit: return err; } -static int sh_mobile_ceu_remove(struct platform_device *pdev) +static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev) { struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); struct sh_mobile_ceu_dev *pcdev = container_of(soc_host, @@ -927,7 +1778,7 @@ static struct platform_driver sh_mobile_ceu_driver = { .pm = &sh_mobile_ceu_dev_pm_ops, }, .probe = sh_mobile_ceu_probe, - .remove = sh_mobile_ceu_remove, + .remove = __exit_p(sh_mobile_ceu_remove), }; static int __init sh_mobile_ceu_init(void) @@ -946,3 +1797,4 @@ module_exit(sh_mobile_ceu_exit); MODULE_DESCRIPTION("SuperH Mobile CEU driver"); MODULE_AUTHOR("Magnus Damm"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sh_mobile_ceu"); diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 9f5ae8167855..59aa7a3694c2 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -21,15 +21,15 @@ #include <linux/i2c.h> #include <linux/init.h> #include <linux/list.h> -#include <linux/module.h> #include <linux/mutex.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/vmalloc.h> #include <media/soc_camera.h> #include <media/v4l2-common.h> -#include <media/v4l2-dev.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-dev.h> #include <media/videobuf-core.h> /* Default to VGA resolution */ @@ -38,7 +38,7 @@ static LIST_HEAD(hosts); static LIST_HEAD(devices); -static DEFINE_MUTEX(list_lock); +static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */ const struct soc_camera_data_format *soc_camera_format_by_fourcc( struct soc_camera_device *icd, unsigned int fourcc) @@ -152,12 +152,9 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a) { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; - int ret = 0; - - if (icd->ops->set_std) - ret = icd->ops->set_std(icd, a); + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - return ret; + return v4l2_subdev_call(sd, core, s_std, *a); } static int soc_camera_reqbufs(struct file *file, void *priv, @@ -170,8 +167,6 @@ static int soc_camera_reqbufs(struct file *file, void *priv, 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; @@ -209,10 +204,11 @@ static int soc_camera_dqbuf(struct file *file, void *priv, return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK); } +/* Always entered with .video_lock held */ static int soc_camera_init_user_formats(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - int i, fmts = 0; + int i, fmts = 0, ret; if (!ici->ops->get_formats) /* @@ -225,8 +221,12 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd) * First pass - only count formats this host-sensor * configuration can provide */ - for (i = 0; i < icd->num_formats; i++) - fmts += ici->ops->get_formats(icd, i, NULL); + for (i = 0; i < icd->num_formats; i++) { + ret = ici->ops->get_formats(icd, i, NULL); + if (ret < 0) + return ret; + fmts += ret; + } if (!fmts) return -ENXIO; @@ -248,20 +248,39 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd) icd->user_formats[i].cam_fmt = icd->formats + i; icd->user_formats[i].buswidth = icd->formats[i].depth; } else { - fmts += ici->ops->get_formats(icd, i, - &icd->user_formats[fmts]); + ret = ici->ops->get_formats(icd, i, + &icd->user_formats[fmts]); + if (ret < 0) + goto egfmt; + fmts += ret; } icd->current_fmt = icd->user_formats[0].host_fmt; return 0; + +egfmt: + icd->num_user_formats = 0; + vfree(icd->user_formats); + return ret; } +/* Always entered with .video_lock held */ static void soc_camera_free_user_formats(struct soc_camera_device *icd) { + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + + if (ici->ops->put_formats) + ici->ops->put_formats(icd); + icd->current_fmt = NULL; + icd->num_user_formats = 0; vfree(icd->user_formats); + icd->user_formats = NULL; } +#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \ + ((x) >> 24) & 0xff + /* Called with .vb_lock held */ static int soc_camera_set_fmt(struct soc_camera_file *icf, struct v4l2_format *f) @@ -271,6 +290,9 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf, struct v4l2_pix_format *pix = &f->fmt.pix; int ret; + dev_dbg(&icd->dev, "S_FMT(%c%c%c%c, %ux%u)\n", + pixfmtstr(pix->pixelformat), pix->width, pix->height); + /* We always call try_fmt() before set_fmt() or set_crop() */ ret = ici->ops->try_fmt(icd, f); if (ret < 0) @@ -281,13 +303,13 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf, return ret; } else if (!icd->current_fmt || icd->current_fmt->fourcc != pix->pixelformat) { - dev_err(ici->dev, + dev_err(&icd->dev, "Host driver hasn't set up current format correctly!\n"); return -EINVAL; } - icd->width = pix->width; - icd->height = pix->height; + icd->user_width = pix->width; + icd->user_height = pix->height; icf->vb_vidq.field = icd->field = pix->field; @@ -296,7 +318,7 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf, f->type); dev_dbg(&icd->dev, "set width: %d height: %d\n", - icd->width, icd->height); + icd->user_width, icd->user_height); /* set physical bus parameters */ return ici->ops->set_bus_param(icd, pix->pixelformat); @@ -304,30 +326,24 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf, static int soc_camera_open(struct file *file) { - struct video_device *vdev; - struct soc_camera_device *icd; + struct video_device *vdev = video_devdata(file); + struct soc_camera_device *icd = container_of(vdev->parent, + struct soc_camera_device, + dev); + struct soc_camera_link *icl = to_soc_camera_link(icd); struct soc_camera_host *ici; struct soc_camera_file *icf; int ret; - icf = vmalloc(sizeof(*icf)); - if (!icf) - return -ENOMEM; - - /* - * It is safe to dereference these pointers now as long as a user has - * the video device open - we are protected by the held cdev reference. - */ + if (!icd->ops) + /* No device driver attached */ + return -ENODEV; - vdev = video_devdata(file); - icd = container_of(vdev->parent, 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; - } + icf = vmalloc(sizeof(*icf)); + if (!icf) + return -ENOMEM; if (!try_module_get(ici->ops->owner)) { dev_err(&icd->dev, "Couldn't lock capture bus driver.\n"); @@ -335,7 +351,10 @@ static int soc_camera_open(struct file *file) goto emgi; } - /* Protect against icd->remove() until we module_get() both drivers. */ + /* + * Protect against icd->ops->remove() until we module_get() both + * drivers. + */ mutex_lock(&icd->video_lock); icf->icd = icd; @@ -347,14 +366,24 @@ static int soc_camera_open(struct file *file) struct v4l2_format f = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .fmt.pix = { - .width = icd->width, - .height = icd->height, + .width = icd->user_width, + .height = icd->user_height, .field = icd->field, .pixelformat = icd->current_fmt->fourcc, .colorspace = icd->current_fmt->colorspace, }, }; + if (icl->power) { + ret = icl->power(icd->pdev, 1); + if (ret < 0) + goto epower; + } + + /* The camera could have been already on, try to reset */ + if (icl->reset) + icl->reset(icd->pdev); + ret = ici->ops->add(icd); if (ret < 0) { dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); @@ -367,28 +396,29 @@ static int soc_camera_open(struct file *file) goto esfmt; } - mutex_unlock(&icd->video_lock); - file->private_data = icf; dev_dbg(&icd->dev, "camera device open\n"); ici->ops->init_videobuf(&icf->vb_vidq, icd); + mutex_unlock(&icd->video_lock); + return 0; /* - * First three errors are entered with the .video_lock held + * First five errors are entered with the .video_lock held * and use_count == 1 */ esfmt: ici->ops->remove(icd); eiciadd: + if (icl->power) + icl->power(icd->pdev, 0); +epower: icd->use_count--; mutex_unlock(&icd->video_lock); module_put(ici->ops->owner); emgi: - module_put(icd->ops->owner); -emgd: vfree(icf); return ret; } @@ -398,21 +428,24 @@ static int soc_camera_close(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; mutex_lock(&icd->video_lock); icd->use_count--; - if (!icd->use_count) + if (!icd->use_count) { + struct soc_camera_link *icl = to_soc_camera_link(icd); + ici->ops->remove(icd); + if (icl->power) + icl->power(icd->pdev, 0); + } mutex_unlock(&icd->video_lock); - module_put(icd->ops->owner); module_put(ici->ops->owner); vfree(icf); - dev_dbg(vdev->parent, "camera device close\n"); + dev_dbg(&icd->dev, "camera device close\n"); return 0; } @@ -422,10 +455,9 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf, { 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->parent, "camera device read not implemented\n"); + dev_err(&icd->dev, "camera device read not implemented\n"); return err; } @@ -483,8 +515,8 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, mutex_lock(&icf->vb_vidq.vb_lock); - if (videobuf_queue_is_busy(&icf->vb_vidq)) { - dev_err(&icd->dev, "S_FMT denied: queue busy\n"); + if (icf->vb_vidq.bufs[0]) { + dev_err(&icd->dev, "S_FMT denied: queue initialised\n"); ret = -EBUSY; goto unlock; } @@ -525,8 +557,8 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv, WARN_ON(priv != file->private_data); - pix->width = icd->width; - pix->height = icd->height; + pix->width = icd->user_width; + pix->height = icd->user_height; pix->field = icf->vb_vidq.field; pix->pixelformat = icd->current_fmt->fourcc; pix->bytesperline = pix->width * @@ -555,18 +587,17 @@ static int soc_camera_streamon(struct file *file, void *priv, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); int ret; WARN_ON(priv != file->private_data); - dev_dbg(&icd->dev, "%s\n", __func__); - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; mutex_lock(&icd->video_lock); - icd->ops->start_capture(icd); + v4l2_subdev_call(sd, video, s_stream, 1); /* This calls buf_queue from host driver's videobuf_queue_ops */ ret = videobuf_streamon(&icf->vb_vidq); @@ -581,11 +612,10 @@ static int soc_camera_streamoff(struct file *file, void *priv, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); WARN_ON(priv != file->private_data); - dev_dbg(&icd->dev, "%s\n", __func__); - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -595,7 +625,7 @@ static int soc_camera_streamoff(struct file *file, void *priv, * remaining buffers. When the last buffer is freed, stop capture */ videobuf_streamoff(&icf->vb_vidq); - icd->ops->stop_capture(icd); + v4l2_subdev_call(sd, video, s_stream, 0); mutex_unlock(&icd->video_lock); @@ -607,6 +637,7 @@ static int soc_camera_queryctrl(struct file *file, void *priv, { 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 i; WARN_ON(priv != file->private_data); @@ -614,6 +645,15 @@ static int soc_camera_queryctrl(struct file *file, void *priv, if (!qc->id) return -EINVAL; + /* First check host controls */ + for (i = 0; i < ici->ops->num_controls; i++) + if (qc->id == ici->ops->controls[i].id) { + memcpy(qc, &(ici->ops->controls[i]), + sizeof(*qc)); + return 0; + } + + /* Then device controls */ for (i = 0; i < icd->ops->num_controls; i++) if (qc->id == icd->ops->controls[i].id) { memcpy(qc, &(icd->ops->controls[i]), @@ -629,25 +669,19 @@ static int soc_camera_g_ctrl(struct file *file, void *priv, { 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 v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; 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 (ici->ops->get_ctrl) { + ret = ici->ops->get_ctrl(icd, ctrl); + if (ret != -ENOIOCTLCMD) + return ret; } - if (icd->ops->get_control) - return icd->ops->get_control(icd, ctrl); - return -EINVAL; + return v4l2_subdev_call(sd, core, g_ctrl, ctrl); } static int soc_camera_s_ctrl(struct file *file, void *priv, @@ -655,12 +689,19 @@ static int soc_camera_s_ctrl(struct file *file, void *priv, { 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 v4l2_subdev *sd = soc_camera_to_subdev(icd); + int ret; WARN_ON(priv != file->private_data); - if (icd->ops->set_control) - return icd->ops->set_control(icd, ctrl); - return -EINVAL; + if (ici->ops->set_ctrl) { + ret = ici->ops->set_ctrl(icd, ctrl); + if (ret != -ENOIOCTLCMD) + return ret; + } + + return v4l2_subdev_call(sd, core, s_ctrl, ctrl); } static int soc_camera_cropcap(struct file *file, void *fh, @@ -668,20 +709,9 @@ static int soc_camera_cropcap(struct file *file, void *fh, { 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); - 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 = DEFAULT_WIDTH; - a->defrect.height = DEFAULT_HEIGHT; - a->pixelaspect.numerator = 1; - a->pixelaspect.denominator = 1; - - return 0; + return ici->ops->cropcap(icd, a); } static int soc_camera_g_crop(struct file *file, void *fh, @@ -689,36 +719,53 @@ static int soc_camera_g_crop(struct file *file, void *fh, { 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; - 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; + mutex_lock(&icf->vb_vidq.vb_lock); + ret = ici->ops->get_crop(icd, a); + mutex_unlock(&icf->vb_vidq.vb_lock); - return 0; + return ret; } +/* + * According to the V4L2 API, drivers shall not update the struct v4l2_crop + * argument with the actual geometry, instead, the user shall use G_CROP to + * retrieve it. However, we expect camera host and client drivers to update + * the argument, which we then use internally, but do not return to the user. + */ 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); + struct v4l2_rect *rect = &a->c; + struct v4l2_crop current_crop; int ret; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + dev_dbg(&icd->dev, "S_CROP(%ux%u@%u:%u)\n", + rect->width, rect->height, rect->left, rect->top); + /* Cropping is allowed during a running capture, guard consistency */ mutex_lock(&icf->vb_vidq.vb_lock); - ret = ici->ops->set_crop(icd, &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; + /* If get_crop fails, we'll let host and / or client drivers decide */ + ret = ici->ops->get_crop(icd, ¤t_crop); + + /* Prohibit window size change with initialised buffers */ + if (icf->vb_vidq.bufs[0] && !ret && + (a->c.width != current_crop.c.width || + a->c.height != current_crop.c.height)) { + dev_err(&icd->dev, + "S_CROP denied: queue initialised and sizes differ\n"); + ret = -EBUSY; + } else { + ret = ici->ops->set_crop(icd, a); } mutex_unlock(&icf->vb_vidq.vb_lock); @@ -731,11 +778,9 @@ static int soc_camera_g_chip_ident(struct file *file, void *fh, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - if (!icd->ops->get_chip_id) - return -EINVAL; - - return icd->ops->get_chip_id(icd, id); + return v4l2_subdev_call(sd, core, g_chip_ident, id); } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -744,11 +789,9 @@ static int soc_camera_g_register(struct file *file, void *fh, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - if (!icd->ops->get_register) - return -EINVAL; - - return icd->ops->get_register(icd, reg); + return v4l2_subdev_call(sd, core, g_register, reg); } static int soc_camera_s_register(struct file *file, void *fh, @@ -756,37 +799,12 @@ static int soc_camera_s_register(struct file *file, void *fh, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - if (!icd->ops->set_register) - return -EINVAL; - - return icd->ops->set_register(icd, reg); + return v4l2_subdev_call(sd, core, s_register, reg); } #endif -static int device_register_link(struct soc_camera_device *icd) -{ - int ret = dev_set_name(&icd->dev, "%u-%u", icd->iface, icd->devnum); - - if (!ret) - 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) { @@ -796,106 +814,193 @@ static void scan_add_host(struct soc_camera_host *ici) list_for_each_entry(icd, &devices, list) { if (icd->iface == ici->nr) { - icd->dev.parent = ici->dev; - device_register_link(icd); + int ret; + icd->dev.parent = ici->v4l2_dev.dev; + dev_set_name(&icd->dev, "%u-%u", icd->iface, + icd->devnum); + ret = device_register(&icd->dev); + if (ret < 0) { + icd->dev.parent = NULL; + dev_err(&icd->dev, + "Cannot register device: %d\n", ret); + } } } 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) +#ifdef CONFIG_I2C_BOARDINFO +static int soc_camera_init_i2c(struct soc_camera_device *icd, + struct soc_camera_link *icl) { - struct soc_camera_host *ici; - int ret = 0; + struct i2c_client *client; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id); + struct v4l2_subdev *subdev; + int ret; - mutex_lock(&list_lock); + if (!adap) { + ret = -ENODEV; + dev_err(&icd->dev, "Cannot get I2C adapter #%d. No driver?\n", + icl->i2c_adapter_id); + goto ei2cga; + } - list_add_tail(&icd->list, &devices); + icl->board_info->platform_data = icd; - /* 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; - } + subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, + icl->module_name, icl->board_info, NULL); + if (!subdev) { + ret = -ENOMEM; + goto ei2cnd; } - mutex_unlock(&list_lock); + client = subdev->priv; - if (ret) - ret = device_register_link(icd); + /* Use to_i2c_client(dev) to recover the i2c client */ + dev_set_drvdata(&icd->dev, &client->dev); + return 0; +ei2cnd: + i2c_put_adapter(adap); +ei2cga: return ret; } +static void soc_camera_free_i2c(struct soc_camera_device *icd) +{ + struct i2c_client *client = + to_i2c_client(to_soc_camera_control(icd)); + dev_set_drvdata(&icd->dev, NULL); + v4l2_device_unregister_subdev(i2c_get_clientdata(client)); + i2c_unregister_device(client); + i2c_put_adapter(client->adapter); +} +#else +#define soc_camera_init_i2c(icd, icl) (-ENODEV) +#define soc_camera_free_i2c(icd) do {} while (0) +#endif + +static int soc_camera_video_start(struct soc_camera_device *icd); +static int video_dev_create(struct soc_camera_device *icd); +/* Called during host-driver probe */ 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); + struct soc_camera_host *ici = to_soc_camera_host(dev->parent); + struct soc_camera_link *icl = to_soc_camera_link(icd); + struct device *control = NULL; + struct v4l2_subdev *sd; + struct v4l2_format f = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE}; int ret; - /* - * Possible race scenario: - * modprobe <camera-host-driver> triggers __func__ - * at this moment respective <camera-sensor-driver> gets rmmod'ed - * to protect take module references. - */ + dev_info(dev, "Probing %s\n", dev_name(dev)); - if (!try_module_get(icd->ops->owner)) { - dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); - ret = -EINVAL; - goto emgd; + if (icl->power) { + ret = icl->power(icd->pdev, 1); + if (ret < 0) { + dev_err(dev, + "Platform failed to power-on the camera.\n"); + goto epower; + } } - if (!try_module_get(ici->ops->owner)) { - dev_err(&icd->dev, "Couldn't lock capture bus driver.\n"); + /* The camera could have been already on, try to reset */ + if (icl->reset) + icl->reset(icd->pdev); + + ret = ici->ops->add(icd); + if (ret < 0) + goto eadd; + + /* Must have icd->vdev before registering the device */ + ret = video_dev_create(icd); + if (ret < 0) + goto evdc; + + /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ + if (icl->board_info) { + ret = soc_camera_init_i2c(icd, icl); + if (ret < 0) + goto eadddev; + } else if (!icl->add_device || !icl->del_device) { ret = -EINVAL; - goto emgi; + goto eadddev; + } else { + if (icl->module_name) + ret = request_module(icl->module_name); + + ret = icl->add_device(icl, &icd->dev); + if (ret < 0) + goto eadddev; + + /* + * FIXME: this is racy, have to use driver-binding notification, + * when it is available + */ + control = to_soc_camera_control(icd); + if (!control || !control->driver || !dev_get_drvdata(control) || + !try_module_get(control->driver->owner)) { + icl->del_device(icl); + goto enodrv; + } } + /* At this point client .probe() should have run already */ + ret = soc_camera_init_user_formats(icd); + if (ret < 0) + goto eiufmt; + + icd->field = V4L2_FIELD_ANY; + + /* ..._video_start() will create a device node, so we have to protect */ mutex_lock(&icd->video_lock); - /* We only call ->add() here to activate and probe the camera. - * We shall ->remove() and deactivate it immediately afterwards. */ - ret = ici->ops->add(icd); + ret = soc_camera_video_start(icd); if (ret < 0) - goto eiadd; + goto evidstart; + + /* Try to improve our guess of a reasonable window format */ + sd = soc_camera_to_subdev(icd); + if (!v4l2_subdev_call(sd, video, g_fmt, &f)) { + icd->user_width = f.fmt.pix.width; + icd->user_height = f.fmt.pix.height; + } - ret = icd->ops->probe(icd); - if (ret >= 0) { - const struct v4l2_queryctrl *qctrl; + /* Do we have to sysfs_remove_link() before device_unregister()? */ + if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj, + "control")) + dev_warn(&icd->dev, "Failed creating the control symlink\n"); - 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); - ret = soc_camera_init_user_formats(icd); - if (ret < 0) { - if (icd->ops->remove) - icd->ops->remove(icd); - goto eiufmt; - } + if (icl->power) + icl->power(icd->pdev, 0); - icd->height = DEFAULT_HEIGHT; - icd->width = DEFAULT_WIDTH; - icd->field = V4L2_FIELD_ANY; - } + mutex_unlock(&icd->video_lock); + return 0; + +evidstart: + mutex_unlock(&icd->video_lock); + soc_camera_free_user_formats(icd); eiufmt: + if (icl->board_info) { + soc_camera_free_i2c(icd); + } else { + icl->del_device(icl); + module_put(control->driver->owner); + } +enodrv: +eadddev: + video_device_release(icd->vdev); +evdc: ici->ops->remove(icd); -eiadd: - mutex_unlock(&icd->video_lock); - module_put(ici->ops->owner); -emgi: - module_put(icd->ops->owner); -emgd: +eadd: + if (icl->power) + icl->power(icd->pdev, 0); +epower: return ret; } @@ -904,12 +1009,28 @@ emgd: static int soc_camera_remove(struct device *dev) { struct soc_camera_device *icd = to_soc_camera_dev(dev); + struct soc_camera_link *icl = to_soc_camera_link(icd); + struct video_device *vdev = icd->vdev; - mutex_lock(&icd->video_lock); - if (icd->ops->remove) - icd->ops->remove(icd); - mutex_unlock(&icd->video_lock); + BUG_ON(!dev->parent); + if (vdev) { + mutex_lock(&icd->video_lock); + video_unregister_device(vdev); + icd->vdev = NULL; + mutex_unlock(&icd->video_lock); + } + + if (icl->board_info) { + soc_camera_free_i2c(icd); + } else { + struct device_driver *drv = to_soc_camera_control(icd) ? + to_soc_camera_control(icd)->driver : NULL; + if (drv) { + icl->del_device(icl); + module_put(drv->owner); + } + } soc_camera_free_user_formats(icd); return 0; @@ -957,14 +1078,33 @@ static void dummy_release(struct device *dev) { } +static int default_cropcap(struct soc_camera_device *icd, + struct v4l2_cropcap *a) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + return v4l2_subdev_call(sd, video, cropcap, a); +} + +static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + return v4l2_subdev_call(sd, video, g_crop, a); +} + +static int default_s_crop(struct soc_camera_device *icd, struct v4l2_crop *a) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + return v4l2_subdev_call(sd, video, s_crop, a); +} + int soc_camera_host_register(struct soc_camera_host *ici) { struct soc_camera_host *ix; + int ret; if (!ici || !ici->ops || !ici->ops->try_fmt || !ici->ops->set_fmt || - !ici->ops->set_crop || !ici->ops->set_bus_param || !ici->ops->querycap || !ici->ops->init_videobuf || @@ -972,18 +1112,27 @@ int soc_camera_host_register(struct soc_camera_host *ici) !ici->ops->add || !ici->ops->remove || !ici->ops->poll || - !ici->dev) + !ici->v4l2_dev.dev) return -EINVAL; + if (!ici->ops->set_crop) + ici->ops->set_crop = default_s_crop; + if (!ici->ops->get_crop) + ici->ops->get_crop = default_g_crop; + if (!ici->ops->cropcap) + ici->ops->cropcap = default_cropcap; + mutex_lock(&list_lock); list_for_each_entry(ix, &hosts, list) { if (ix->nr == ici->nr) { - mutex_unlock(&list_lock); - return -EBUSY; + ret = -EBUSY; + goto edevreg; } } - dev_set_drvdata(ici->dev, ici); + ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev); + if (ret < 0) + goto edevreg; list_add_tail(&ici->list, &hosts); mutex_unlock(&list_lock); @@ -991,6 +1140,10 @@ int soc_camera_host_register(struct soc_camera_host *ici) scan_add_host(ici); return 0; + +edevreg: + mutex_unlock(&list_lock); + return ret; } EXPORT_SYMBOL(soc_camera_host_register); @@ -1004,42 +1157,34 @@ void soc_camera_host_unregister(struct soc_camera_host *ici) list_del(&ici->list); list_for_each_entry(icd, &devices, list) { - if (icd->dev.parent == ici->dev) { + if (icd->iface == ici->nr) { + /* The bus->remove will be called */ device_unregister(&icd->dev); /* Not before device_unregister(), .remove * needs parent to call ici->ops->remove() */ icd->dev.parent = NULL; + + /* If the host module is loaded again, device_register() + * would complain "already initialised" */ memset(&icd->dev.kobj, 0, sizeof(icd->dev.kobj)); } } mutex_unlock(&list_lock); - dev_set_drvdata(ici->dev, NULL); + v4l2_device_unregister(&ici->v4l2_dev); } EXPORT_SYMBOL(soc_camera_host_unregister); /* Image capture device */ -int soc_camera_device_register(struct soc_camera_device *icd) +static int soc_camera_device_register(struct soc_camera_device *icd) { struct soc_camera_device *ix; int num = -1, i; - if (!icd || !icd->ops || - !icd->ops->probe || - !icd->ops->init || - !icd->ops->release || - !icd->ops->start_capture || - !icd->ops->stop_capture || - !icd->ops->set_crop || - !icd->ops->set_fmt || - !icd->ops->try_fmt || - !icd->ops->query_bus_param || - !icd->ops->set_bus_param) - return -EINVAL; - for (i = 0; i < 256 && num < 0; i++) { num = i; + /* Check if this index is available on this interface */ list_for_each_entry(ix, &devices, list) { if (ix->iface == icd->iface && ix->devnum == i) { num = -1; @@ -1061,21 +1206,15 @@ int soc_camera_device_register(struct soc_camera_device *icd) icd->host_priv = NULL; mutex_init(&icd->video_lock); - return scan_add_device(icd); + list_add_tail(&icd->list, &devices); + + return 0; } -EXPORT_SYMBOL(soc_camera_device_register); -void soc_camera_device_unregister(struct soc_camera_device *icd) +static 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); static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_querycap = soc_camera_querycap, @@ -1106,23 +1245,13 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { #endif }; -/* - * Usually called from the struct soc_camera_ops .probe() method, i.e., from - * soc_camera_probe() above with .video_lock held - */ -int soc_camera_video_start(struct soc_camera_device *icd) +static int video_dev_create(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - int err = -ENOMEM; - struct video_device *vdev; + struct video_device *vdev = video_device_alloc(); - if (!icd->dev.parent) - return -ENODEV; - - vdev = video_device_alloc(); if (!vdev) - goto evidallocd; - dev_dbg(ici->dev, "Allocated video_device %p\n", vdev); + return -ENOMEM; strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); @@ -1132,87 +1261,93 @@ int soc_camera_video_start(struct soc_camera_device *icd) vdev->ioctl_ops = &soc_camera_ioctl_ops; vdev->release = video_device_release; vdev->minor = -1; - vdev->tvnorms = V4L2_STD_UNKNOWN, + vdev->tvnorms = V4L2_STD_UNKNOWN; - err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor); - if (err < 0) { - dev_err(vdev->parent, "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); -/* Called from client .remove() methods with .video_lock held */ -void soc_camera_video_stop(struct soc_camera_device *icd) +/* + * Called from soc_camera_probe() above (with .video_lock held???) + */ +static int soc_camera_video_start(struct soc_camera_device *icd) { - struct video_device *vdev = icd->vdev; + int ret; - dev_dbg(&icd->dev, "%s\n", __func__); + if (!icd->dev.parent) + return -ENODEV; - if (!icd->dev.parent || !vdev) - return; + if (!icd->ops || + !icd->ops->query_bus_param || + !icd->ops->set_bus_param) + return -EINVAL; + + ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, + icd->vdev->minor); + if (ret < 0) { + dev_err(&icd->dev, "video_register_device failed: %d\n", ret); + return ret; + } - video_unregister_device(vdev); - icd->vdev = NULL; + return 0; } -EXPORT_SYMBOL(soc_camera_video_stop); static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) { struct soc_camera_link *icl = pdev->dev.platform_data; - struct i2c_adapter *adap; - struct i2c_client *client; + struct soc_camera_device *icd; + int ret; if (!icl) return -EINVAL; - adap = i2c_get_adapter(icl->i2c_adapter_id); - if (!adap) { - dev_warn(&pdev->dev, "Cannot get adapter #%d. No driver?\n", - icl->i2c_adapter_id); - /* -ENODEV and -ENXIO do not produce an error on probe()... */ - return -ENOENT; - } - - icl->board_info->platform_data = icl; - client = i2c_new_device(adap, icl->board_info); - if (!client) { - i2c_put_adapter(adap); + icd = kzalloc(sizeof(*icd), GFP_KERNEL); + if (!icd) return -ENOMEM; - } - platform_set_drvdata(pdev, client); + icd->iface = icl->bus_id; + icd->pdev = &pdev->dev; + platform_set_drvdata(pdev, icd); + icd->dev.platform_data = icl; + + ret = soc_camera_device_register(icd); + if (ret < 0) + goto escdevreg; + + icd->user_width = DEFAULT_WIDTH; + icd->user_height = DEFAULT_HEIGHT; return 0; + +escdevreg: + kfree(icd); + + return ret; } +/* Only called on rmmod for each platform device, since they are not + * hot-pluggable. Now we know, that all our users - hosts and devices have + * been unloaded already */ static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev) { - struct i2c_client *client = platform_get_drvdata(pdev); + struct soc_camera_device *icd = platform_get_drvdata(pdev); - if (!client) - return -ENODEV; + if (!icd) + return -EINVAL; - i2c_unregister_device(client); - i2c_put_adapter(client->adapter); + soc_camera_device_unregister(icd); + + kfree(icd); return 0; } static struct platform_driver __refdata soc_camera_pdrv = { - .probe = soc_camera_pdrv_probe, - .remove = __devexit_p(soc_camera_pdrv_remove), - .driver = { - .name = "soc-camera-pdrv", - .owner = THIS_MODULE, + .remove = __devexit_p(soc_camera_pdrv_remove), + .driver = { + .name = "soc-camera-pdrv", + .owner = THIS_MODULE, }, }; @@ -1225,7 +1360,7 @@ static int __init soc_camera_init(void) if (ret) goto edrvr; - ret = platform_driver_register(&soc_camera_pdrv); + ret = platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe); if (ret) goto epdr; diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index c48676356ab7..b6a575ce5da2 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -16,54 +16,32 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/soc_camera.h> #include <media/soc_camera_platform.h> struct soc_camera_platform_priv { - struct soc_camera_platform_info *info; - struct soc_camera_device icd; + struct v4l2_subdev subdev; struct soc_camera_data_format format; }; -static struct soc_camera_platform_info * -soc_camera_platform_get_info(struct soc_camera_device *icd) +static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev) { - struct soc_camera_platform_priv *priv; - priv = container_of(icd, struct soc_camera_platform_priv, icd); - return priv->info; -} - -static int soc_camera_platform_init(struct soc_camera_device *icd) -{ - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); - - if (p->power) - p->power(1); - - return 0; -} - -static int soc_camera_platform_release(struct soc_camera_device *icd) -{ - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); - - if (p->power) - p->power(0); - - return 0; + struct v4l2_subdev *subdev = platform_get_drvdata(pdev); + return container_of(subdev, struct soc_camera_platform_priv, subdev); } -static int soc_camera_platform_start_capture(struct soc_camera_device *icd) +static struct soc_camera_platform_info *get_info(struct soc_camera_device *icd) { - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); - return p->set_capture(p, 1); + struct platform_device *pdev = + to_platform_device(to_soc_camera_control(icd)); + return pdev->dev.platform_data; } -static int soc_camera_platform_stop_capture(struct soc_camera_device *icd) +static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable) { - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); - return p->set_capture(p, 0); + struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); + return p->set_capture(p, enable); } static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd, @@ -75,26 +53,14 @@ static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd, static unsigned long soc_camera_platform_query_bus_param(struct soc_camera_device *icd) { - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); + struct soc_camera_platform_info *p = get_info(icd); return p->bus_param; } -static int soc_camera_platform_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) -{ - return 0; -} - -static int soc_camera_platform_set_fmt(struct soc_camera_device *icd, +static int soc_camera_platform_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { - return 0; -} - -static int soc_camera_platform_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) -{ - struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd); + struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); struct v4l2_pix_format *pix = &f->fmt.pix; pix->width = p->format.width; @@ -102,82 +68,99 @@ static int soc_camera_platform_try_fmt(struct soc_camera_device *icd, return 0; } -static int soc_camera_platform_video_probe(struct soc_camera_device *icd) +static void soc_camera_platform_video_probe(struct soc_camera_device *icd, + struct platform_device *pdev) { - struct soc_camera_platform_priv *priv; - priv = container_of(icd, struct soc_camera_platform_priv, icd); + struct soc_camera_platform_priv *priv = get_priv(pdev); + struct soc_camera_platform_info *p = pdev->dev.platform_data; - priv->format.name = priv->info->format_name; - priv->format.depth = priv->info->format_depth; - priv->format.fourcc = priv->info->format.pixelformat; - priv->format.colorspace = priv->info->format.colorspace; + priv->format.name = p->format_name; + priv->format.depth = p->format_depth; + priv->format.fourcc = p->format.pixelformat; + priv->format.colorspace = p->format.colorspace; icd->formats = &priv->format; icd->num_formats = 1; - - return soc_camera_video_start(icd); } -static void soc_camera_platform_video_remove(struct soc_camera_device *icd) -{ - soc_camera_video_stop(icd); -} +static struct v4l2_subdev_core_ops platform_subdev_core_ops; + +static struct v4l2_subdev_video_ops platform_subdev_video_ops = { + .s_stream = soc_camera_platform_s_stream, + .try_fmt = soc_camera_platform_try_fmt, +}; + +static struct v4l2_subdev_ops platform_subdev_ops = { + .core = &platform_subdev_core_ops, + .video = &platform_subdev_video_ops, +}; static struct soc_camera_ops soc_camera_platform_ops = { - .owner = THIS_MODULE, - .probe = soc_camera_platform_video_probe, - .remove = soc_camera_platform_video_remove, - .init = soc_camera_platform_init, - .release = soc_camera_platform_release, - .start_capture = soc_camera_platform_start_capture, - .stop_capture = soc_camera_platform_stop_capture, - .set_crop = soc_camera_platform_set_crop, - .set_fmt = soc_camera_platform_set_fmt, - .try_fmt = soc_camera_platform_try_fmt, .set_bus_param = soc_camera_platform_set_bus_param, .query_bus_param = soc_camera_platform_query_bus_param, }; static int soc_camera_platform_probe(struct platform_device *pdev) { + struct soc_camera_host *ici; struct soc_camera_platform_priv *priv; - struct soc_camera_platform_info *p; + struct soc_camera_platform_info *p = pdev->dev.platform_data; struct soc_camera_device *icd; int ret; - p = pdev->dev.platform_data; if (!p) return -EINVAL; + if (!p->dev) { + dev_err(&pdev->dev, + "Platform has not set soc_camera_device pointer!\n"); + return -EINVAL; + } + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - priv->info = p; - platform_set_drvdata(pdev, priv); + icd = to_soc_camera_dev(p->dev); + + /* soc-camera convention: control's drvdata points to the subdev */ + platform_set_drvdata(pdev, &priv->subdev); + /* Set the control device reference */ + dev_set_drvdata(&icd->dev, &pdev->dev); + + icd->y_skip_top = 0; + icd->ops = &soc_camera_platform_ops; + + ici = to_soc_camera_host(icd->dev.parent); - icd = &priv->icd; - icd->ops = &soc_camera_platform_ops; - icd->control = &pdev->dev; - icd->width_min = 0; - icd->width_max = priv->info->format.width; - icd->height_min = 0; - icd->height_max = priv->info->format.height; - icd->y_skip_top = 0; - icd->iface = priv->info->iface; + soc_camera_platform_video_probe(icd, pdev); - ret = soc_camera_device_register(icd); + v4l2_subdev_init(&priv->subdev, &platform_subdev_ops); + v4l2_set_subdevdata(&priv->subdev, p); + strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE); + + ret = v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev); if (ret) - kfree(priv); + goto evdrs; + + return ret; +evdrs: + icd->ops = NULL; + platform_set_drvdata(pdev, NULL); + kfree(priv); return ret; } static int soc_camera_platform_remove(struct platform_device *pdev) { - struct soc_camera_platform_priv *priv = platform_get_drvdata(pdev); + struct soc_camera_platform_priv *priv = get_priv(pdev); + struct soc_camera_platform_info *p = pdev->dev.platform_data; + struct soc_camera_device *icd = to_soc_camera_dev(p->dev); - soc_camera_device_unregister(&priv->icd); + v4l2_device_unregister_subdev(&priv->subdev); + icd->ops = NULL; + platform_set_drvdata(pdev, NULL); kfree(priv); return 0; } @@ -185,6 +168,7 @@ static int soc_camera_platform_remove(struct platform_device *pdev) static struct platform_driver soc_camera_platform_driver = { .driver = { .name = "soc_camera_platform", + .owner = THIS_MODULE, }, .probe = soc_camera_platform_probe, .remove = soc_camera_platform_remove, @@ -206,3 +190,4 @@ module_exit(soc_camera_platform_module_exit); MODULE_DESCRIPTION("SoC Camera Platform driver"); MODULE_AUTHOR("Magnus Damm"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:soc_camera_platform"); diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index b154bd961e3b..0b996ea4134e 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -1400,7 +1400,6 @@ static int stk_camera_probe(struct usb_interface *interface, } stk_create_sysfs_files(&dev->vdev); - usb_autopm_enable(dev->interface); return 0; diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c index 8b4e7dafce7b..6a91714125d2 100644 --- a/drivers/media/video/stv680.c +++ b/drivers/media/video/stv680.c @@ -734,10 +734,6 @@ static int stv680_start_stream (struct usb_stv *stv680) return 0; nomem_err: - for (i = 0; i < STV680_NUMSCRATCH; i++) { - kfree(stv680->scratch[i].data); - stv680->scratch[i].data = NULL; - } for (i = 0; i < STV680_NUMSBUF; i++) { usb_kill_urb(stv680->urb[i]); usb_free_urb(stv680->urb[i]); @@ -745,6 +741,11 @@ static int stv680_start_stream (struct usb_stv *stv680) kfree(stv680->sbuf[i].data); stv680->sbuf[i].data = NULL; } + /* used in irq, free only as all URBs are dead */ + for (i = 0; i < STV680_NUMSCRATCH; i++) { + kfree(stv680->scratch[i].data); + stv680->scratch[i].data = NULL; + } return -ENOMEM; } diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 537594211a90..2816f1839230 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -819,8 +819,8 @@ static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) fe_tuner_ops->get_frequency(&t->fe, &abs_freq); f->frequency = (V4L2_TUNER_RADIO == t->mode) ? - (abs_freq * 2 + 125/2) / 125 : - (abs_freq + 62500/2) / 62500; + DIV_ROUND_CLOSEST(abs_freq * 2, 125) : + DIV_ROUND_CLOSEST(abs_freq, 62500); return 0; } f->frequency = (V4L2_TUNER_RADIO == t->mode) ? diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index ac02808106c1..d533ea57e7b1 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -646,14 +646,14 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, tvee->has_radio = 1; } - if (tuner1 < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) { + if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) { tvee->tuner_type = hauppauge_tuner[tuner1].id; t_name1 = hauppauge_tuner[tuner1].name; } else { t_name1 = "unknown"; } - if (tuner2 < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) { + if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) { tvee->tuner2_type = hauppauge_tuner[tuner2].id; t_name2 = hauppauge_tuner[tuner2].name; } else { diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index 3750f7fadb12..244372627df2 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -31,7 +31,10 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/videodev2.h> -#include <media/v4l2-int-device.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #include <media/tvp514x.h> #include "tvp514x_regs.h" @@ -49,15 +52,11 @@ static int debug; module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#define dump_reg(client, reg, val) \ - do { \ - val = tvp514x_read_reg(client, reg); \ - v4l_info(client, "Reg(0x%.2X): 0x%.2X\n", reg, val); \ - } while (0) +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TVP514X linux decoder driver"); +MODULE_LICENSE("GPL"); -/** - * enum tvp514x_std - enum for supported standards - */ +/* enum tvp514x_std - enum for supported standards */ enum tvp514x_std { STD_NTSC_MJ = 0, STD_PAL_BDGHIN, @@ -65,14 +64,6 @@ enum tvp514x_std { }; /** - * enum tvp514x_state - enum for different decoder states - */ -enum tvp514x_state { - STATE_NOT_DETECTED, - STATE_DETECTED -}; - -/** * struct tvp514x_std_info - Structure to store standard informations * @width: Line width in pixels * @height:Number of active lines @@ -89,33 +80,27 @@ struct tvp514x_std_info { static struct tvp514x_reg tvp514x_reg_list_default[0x40]; /** * struct tvp514x_decoder - TVP5146/47 decoder object - * @v4l2_int_device: Slave handle - * @tvp514x_slave: Slave pointer which is used by @v4l2_int_device + * @sd: Subdevice Slave handle * @tvp514x_regs: copy of hw's regs with preset values. * @pdata: Board specific - * @client: I2C client data - * @id: Entry from I2C table * @ver: Chip version - * @state: TVP5146/47 decoder state - detected or not-detected + * @streaming: TVP5146/47 decoder streaming - enabled or disabled. * @pix: Current pixel format * @num_fmts: Number of formats * @fmt_list: Format list * @current_std: Current standard * @num_stds: Number of standards * @std_list: Standards list - * @route: input and output routing at chip level + * @input: Input routing at chip level + * @output: Output routing at chip level */ struct tvp514x_decoder { - struct v4l2_int_device v4l2_int_device; - struct v4l2_int_slave tvp514x_slave; + struct v4l2_subdev sd; struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; const struct tvp514x_platform_data *pdata; - struct i2c_client *client; - - struct i2c_device_id *id; int ver; - enum tvp514x_state state; + int streaming; struct v4l2_pix_format pix; int num_fmts; @@ -124,15 +109,18 @@ struct tvp514x_decoder { enum tvp514x_std current_std; int num_stds; struct tvp514x_std_info *std_list; - - struct v4l2_routing route; + /* Input and Output Routing parameters */ + u32 input; + u32 output; }; /* TVP514x default register values */ static struct tvp514x_reg tvp514x_reg_list_default[] = { - {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */ + /* Composite selected */ + {TOK_WRITE, REG_INPUT_SEL, 0x05}, {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, - {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */ + /* Auto mode */ + {TOK_WRITE, REG_VIDEO_STD, 0x00}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F}, {TOK_WRITE, REG_COLOR_KILLER, 0x10}, @@ -145,53 +133,74 @@ static struct tvp514x_reg tvp514x_reg_list_default[] = { {TOK_WRITE, REG_HUE, 0x00}, {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00}, {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E}, - {TOK_SKIP, 0x0F, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x0F, 0x00}, {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80}, {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80}, {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80}, - {TOK_SKIP, 0x13, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x13, 0x00}, {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80}, - {TOK_SKIP, 0x15, 0x00}, /* Reserved */ - {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, /* NTSC timing */ + /* Reserved */ + {TOK_SKIP, 0x15, 0x00}, + /* NTSC timing */ + {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00}, {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25}, {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03}, - {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, /* NTSC timing */ + /* NTSC timing */ + {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00}, {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40}, {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00}, - {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, /* NTSC timing */ + /* NTSC timing */ + {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00}, {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07}, {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00}, - {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, /* NTSC timing */ + /* NTSC timing */ + {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00}, {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15}, {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00}, - {TOK_SKIP, 0x26, 0x00}, /* Reserved */ - {TOK_SKIP, 0x27, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x26, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x27, 0x00}, {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC}, - {TOK_SKIP, 0x29, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x29, 0x00}, {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00}, - {TOK_SKIP, 0x2B, 0x00}, /* Reserved */ + /* Reserved */ + {TOK_SKIP, 0x2B, 0x00}, {TOK_SKIP, REG_SCART_DELAY, 0x00}, {TOK_SKIP, REG_CTI_DELAY, 0x00}, {TOK_SKIP, REG_CTI_CONTROL, 0x00}, - {TOK_SKIP, 0x2F, 0x00}, /* Reserved */ - {TOK_SKIP, 0x30, 0x00}, /* Reserved */ - {TOK_SKIP, 0x31, 0x00}, /* Reserved */ - {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, /* HS, VS active high */ - {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, /* 10-bit BT.656 */ - {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, /* Enable clk & data */ - {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, /* Enable AVID & FLD */ - {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, /* Enable VS & HS */ + /* Reserved */ + {TOK_SKIP, 0x2F, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x30, 0x00}, + /* Reserved */ + {TOK_SKIP, 0x31, 0x00}, + /* HS, VS active high */ + {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, + /* 10-bit BT.656 */ + {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, + /* Enable clk & data */ + {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, + /* Enable AVID & FLD */ + {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, + /* Enable VS & HS */ + {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF}, {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF}, - {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, /* Clear status */ + /* Clear status */ + {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, {TOK_TERM, 0, 0}, }; -/* List of image formats supported by TVP5146/47 decoder +/** + * List of image formats supported by TVP5146/47 decoder * Currently we are using 8 bit mode only, but can be * extended to 10/20 bit mode. */ @@ -205,7 +214,7 @@ static const struct v4l2_fmtdesc tvp514x_fmt_list[] = { }, }; -/* +/** * Supported standards - * * Currently supports two standards only, need to add support for rest of the @@ -240,35 +249,32 @@ static struct tvp514x_std_info tvp514x_std_list[] = { }, /* Standard: need to add for additional standard */ }; -/* - * Control structure for Auto Gain - * This is temporary data, will get replaced once - * v4l2_ctrl_query_fill supports it. - */ -static const struct v4l2_queryctrl tvp514x_autogain_ctrl = { - .id = V4L2_CID_AUTOGAIN, - .name = "Gain, Automatic", - .type = V4L2_CTRL_TYPE_BOOLEAN, - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, -}; -/* - * Read a value from a register in an TVP5146/47 decoder device. + +static inline struct tvp514x_decoder *to_decoder(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tvp514x_decoder, sd); +} + + +/** + * tvp514x_read_reg() - Read a value from a register in an TVP5146/47. + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + * * Returns value read if successful, or non-zero (-1) otherwise. */ -static int tvp514x_read_reg(struct i2c_client *client, u8 reg) +static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) { - int err; - int retry = 0; + int err, retry = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + read_again: err = i2c_smbus_read_byte_data(client, reg); if (err == -1) { if (retry <= I2C_RETRY_COUNT) { - v4l_warn(client, "Read: retry ... %d\n", retry); + v4l2_warn(sd, "Read: retry ... %d\n", retry); retry++; msleep_interruptible(10); goto read_again; @@ -278,20 +284,39 @@ read_again: return err; } -/* +/** + * dump_reg() - dump the register content of TVP5146/47. + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + */ +static void dump_reg(struct v4l2_subdev *sd, u8 reg) +{ + u32 val; + + val = tvp514x_read_reg(sd, reg); + v4l2_info(sd, "Reg(0x%.2X): 0x%.2X\n", reg, val); +} + +/** + * tvp514x_write_reg() - Write a value to a register in TVP5146/47 + * @sd: ptr to v4l2_subdev struct + * @reg: TVP5146/47 register address + * @val: value to be written to the register + * * Write a value to a register in an TVP5146/47 decoder device. * Returns zero if successful, or non-zero otherwise. */ -static int tvp514x_write_reg(struct i2c_client *client, u8 reg, u8 val) +static int tvp514x_write_reg(struct v4l2_subdev *sd, u8 reg, u8 val) { - int err; - int retry = 0; + int err, retry = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + write_again: err = i2c_smbus_write_byte_data(client, reg, val); if (err) { if (retry <= I2C_RETRY_COUNT) { - v4l_warn(client, "Write: retry ... %d\n", retry); + v4l2_warn(sd, "Write: retry ... %d\n", retry); retry++; msleep_interruptible(10); goto write_again; @@ -301,17 +326,19 @@ write_again: return err; } -/* - * tvp514x_write_regs : Initializes a list of TVP5146/47 registers +/** + * tvp514x_write_regs() : Initializes a list of TVP5146/47 registers + * @sd: ptr to v4l2_subdev struct + * @reglist: list of TVP5146/47 registers and values + * + * Initializes a list of TVP5146/47 registers:- * if token is TOK_TERM, then entire write operation terminates * if token is TOK_DELAY, then a delay of 'val' msec is introduced * if token is TOK_SKIP, then the register write is skipped * if token is TOK_WRITE, then the register write is performed - * - * reglist - list of registers to be written * Returns zero if successful, or non-zero otherwise. */ -static int tvp514x_write_regs(struct i2c_client *client, +static int tvp514x_write_regs(struct v4l2_subdev *sd, const struct tvp514x_reg reglist[]) { int err; @@ -326,31 +353,33 @@ static int tvp514x_write_regs(struct i2c_client *client, if (next->token == TOK_SKIP) continue; - err = tvp514x_write_reg(client, next->reg, (u8) next->val); + err = tvp514x_write_reg(sd, next->reg, (u8) next->val); if (err) { - v4l_err(client, "Write failed. Err[%d]\n", err); + v4l2_err(sd, "Write failed. Err[%d]\n", err); return err; } } return 0; } -/* - * tvp514x_get_current_std: - * Returns the current standard detected by TVP5146/47 +/** + * tvp514x_get_current_std() : Get the current standard detected by TVP5146/47 + * @sd: ptr to v4l2_subdev struct + * + * Get current standard detected by TVP5146/47, STD_INVALID if there is no + * standard detected. */ -static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder - *decoder) +static enum tvp514x_std tvp514x_get_current_std(struct v4l2_subdev *sd) { u8 std, std_status; - std = tvp514x_read_reg(decoder->client, REG_VIDEO_STD); - if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) { + std = tvp514x_read_reg(sd, REG_VIDEO_STD); + if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) /* use the standard status register */ - std_status = tvp514x_read_reg(decoder->client, - REG_VIDEO_STD_STATUS); - } else - std_status = std; /* use the standard register itself */ + std_status = tvp514x_read_reg(sd, REG_VIDEO_STD_STATUS); + else + /* use the standard register itself */ + std_status = std; switch (std_status & VIDEO_STD_MASK) { case VIDEO_STD_NTSC_MJ_BIT: @@ -366,94 +395,99 @@ static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder return STD_INVALID; } -/* - * TVP5146/47 register dump function - */ -static void tvp514x_reg_dump(struct tvp514x_decoder *decoder) +/* TVP5146/47 register dump function */ +static void tvp514x_reg_dump(struct v4l2_subdev *sd) { - u8 value; - - dump_reg(decoder->client, REG_INPUT_SEL, value); - dump_reg(decoder->client, REG_AFE_GAIN_CTRL, value); - dump_reg(decoder->client, REG_VIDEO_STD, value); - dump_reg(decoder->client, REG_OPERATION_MODE, value); - dump_reg(decoder->client, REG_COLOR_KILLER, value); - dump_reg(decoder->client, REG_LUMA_CONTROL1, value); - dump_reg(decoder->client, REG_LUMA_CONTROL2, value); - dump_reg(decoder->client, REG_LUMA_CONTROL3, value); - dump_reg(decoder->client, REG_BRIGHTNESS, value); - dump_reg(decoder->client, REG_CONTRAST, value); - dump_reg(decoder->client, REG_SATURATION, value); - dump_reg(decoder->client, REG_HUE, value); - dump_reg(decoder->client, REG_CHROMA_CONTROL1, value); - dump_reg(decoder->client, REG_CHROMA_CONTROL2, value); - dump_reg(decoder->client, REG_COMP_PR_SATURATION, value); - dump_reg(decoder->client, REG_COMP_Y_CONTRAST, value); - dump_reg(decoder->client, REG_COMP_PB_SATURATION, value); - dump_reg(decoder->client, REG_COMP_Y_BRIGHTNESS, value); - dump_reg(decoder->client, REG_AVID_START_PIXEL_LSB, value); - dump_reg(decoder->client, REG_AVID_START_PIXEL_MSB, value); - dump_reg(decoder->client, REG_AVID_STOP_PIXEL_LSB, value); - dump_reg(decoder->client, REG_AVID_STOP_PIXEL_MSB, value); - dump_reg(decoder->client, REG_HSYNC_START_PIXEL_LSB, value); - dump_reg(decoder->client, REG_HSYNC_START_PIXEL_MSB, value); - dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_LSB, value); - dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_MSB, value); - dump_reg(decoder->client, REG_VSYNC_START_LINE_LSB, value); - dump_reg(decoder->client, REG_VSYNC_START_LINE_MSB, value); - dump_reg(decoder->client, REG_VSYNC_STOP_LINE_LSB, value); - dump_reg(decoder->client, REG_VSYNC_STOP_LINE_MSB, value); - dump_reg(decoder->client, REG_VBLK_START_LINE_LSB, value); - dump_reg(decoder->client, REG_VBLK_START_LINE_MSB, value); - dump_reg(decoder->client, REG_VBLK_STOP_LINE_LSB, value); - dump_reg(decoder->client, REG_VBLK_STOP_LINE_MSB, value); - dump_reg(decoder->client, REG_SYNC_CONTROL, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER1, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER2, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER3, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER4, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER5, value); - dump_reg(decoder->client, REG_OUTPUT_FORMATTER6, value); - dump_reg(decoder->client, REG_CLEAR_LOST_LOCK, value); + dump_reg(sd, REG_INPUT_SEL); + dump_reg(sd, REG_AFE_GAIN_CTRL); + dump_reg(sd, REG_VIDEO_STD); + dump_reg(sd, REG_OPERATION_MODE); + dump_reg(sd, REG_COLOR_KILLER); + dump_reg(sd, REG_LUMA_CONTROL1); + dump_reg(sd, REG_LUMA_CONTROL2); + dump_reg(sd, REG_LUMA_CONTROL3); + dump_reg(sd, REG_BRIGHTNESS); + dump_reg(sd, REG_CONTRAST); + dump_reg(sd, REG_SATURATION); + dump_reg(sd, REG_HUE); + dump_reg(sd, REG_CHROMA_CONTROL1); + dump_reg(sd, REG_CHROMA_CONTROL2); + dump_reg(sd, REG_COMP_PR_SATURATION); + dump_reg(sd, REG_COMP_Y_CONTRAST); + dump_reg(sd, REG_COMP_PB_SATURATION); + dump_reg(sd, REG_COMP_Y_BRIGHTNESS); + dump_reg(sd, REG_AVID_START_PIXEL_LSB); + dump_reg(sd, REG_AVID_START_PIXEL_MSB); + dump_reg(sd, REG_AVID_STOP_PIXEL_LSB); + dump_reg(sd, REG_AVID_STOP_PIXEL_MSB); + dump_reg(sd, REG_HSYNC_START_PIXEL_LSB); + dump_reg(sd, REG_HSYNC_START_PIXEL_MSB); + dump_reg(sd, REG_HSYNC_STOP_PIXEL_LSB); + dump_reg(sd, REG_HSYNC_STOP_PIXEL_MSB); + dump_reg(sd, REG_VSYNC_START_LINE_LSB); + dump_reg(sd, REG_VSYNC_START_LINE_MSB); + dump_reg(sd, REG_VSYNC_STOP_LINE_LSB); + dump_reg(sd, REG_VSYNC_STOP_LINE_MSB); + dump_reg(sd, REG_VBLK_START_LINE_LSB); + dump_reg(sd, REG_VBLK_START_LINE_MSB); + dump_reg(sd, REG_VBLK_STOP_LINE_LSB); + dump_reg(sd, REG_VBLK_STOP_LINE_MSB); + dump_reg(sd, REG_SYNC_CONTROL); + dump_reg(sd, REG_OUTPUT_FORMATTER1); + dump_reg(sd, REG_OUTPUT_FORMATTER2); + dump_reg(sd, REG_OUTPUT_FORMATTER3); + dump_reg(sd, REG_OUTPUT_FORMATTER4); + dump_reg(sd, REG_OUTPUT_FORMATTER5); + dump_reg(sd, REG_OUTPUT_FORMATTER6); + dump_reg(sd, REG_CLEAR_LOST_LOCK); } -/* - * Configure the TVP5146/47 with the current register settings +/** + * tvp514x_configure() - Configure the TVP5146/47 registers + * @sd: ptr to v4l2_subdev struct + * @decoder: ptr to tvp514x_decoder structure + * * Returns zero if successful, or non-zero otherwise. */ -static int tvp514x_configure(struct tvp514x_decoder *decoder) +static int tvp514x_configure(struct v4l2_subdev *sd, + struct tvp514x_decoder *decoder) { int err; /* common register initialization */ err = - tvp514x_write_regs(decoder->client, decoder->tvp514x_regs); + tvp514x_write_regs(sd, decoder->tvp514x_regs); if (err) return err; if (debug) - tvp514x_reg_dump(decoder); + tvp514x_reg_dump(sd); return 0; } -/* - * Detect if an tvp514x is present, and if so which revision. +/** + * tvp514x_detect() - Detect if an tvp514x is present, and if so which revision. + * @sd: pointer to standard V4L2 sub-device structure + * @decoder: pointer to tvp514x_decoder structure + * * A device is considered to be detected if the chip ID (LSB and MSB) * registers match the expected values. * Any value of the rom version register is accepted. * Returns ENODEV error number if no device is detected, or zero * if a device is detected. */ -static int tvp514x_detect(struct tvp514x_decoder *decoder) +static int tvp514x_detect(struct v4l2_subdev *sd, + struct tvp514x_decoder *decoder) { u8 chip_id_msb, chip_id_lsb, rom_ver; + struct i2c_client *client = v4l2_get_subdevdata(sd); - chip_id_msb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_MSB); - chip_id_lsb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_LSB); - rom_ver = tvp514x_read_reg(decoder->client, REG_ROM_VERSION); + chip_id_msb = tvp514x_read_reg(sd, REG_CHIP_ID_MSB); + chip_id_lsb = tvp514x_read_reg(sd, REG_CHIP_ID_LSB); + rom_ver = tvp514x_read_reg(sd, REG_ROM_VERSION); - v4l_dbg(1, debug, decoder->client, + v4l2_dbg(1, debug, sd, "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n", chip_id_msb, chip_id_lsb, rom_ver); if ((chip_id_msb != TVP514X_CHIP_ID_MSB) @@ -462,38 +496,30 @@ static int tvp514x_detect(struct tvp514x_decoder *decoder) /* We didn't read the values we expected, so this must not be * an TVP5146/47. */ - v4l_err(decoder->client, - "chip id mismatch msb:0x%x lsb:0x%x\n", - chip_id_msb, chip_id_lsb); + v4l2_err(sd, "chip id mismatch msb:0x%x lsb:0x%x\n", + chip_id_msb, chip_id_lsb); return -ENODEV; } decoder->ver = rom_ver; - decoder->state = STATE_DETECTED; - v4l_info(decoder->client, - "%s found at 0x%x (%s)\n", decoder->client->name, - decoder->client->addr << 1, - decoder->client->adapter->name); + v4l2_info(sd, "%s (Version - 0x%.2x) found at 0x%x (%s)\n", + client->name, decoder->ver, + client->addr << 1, client->adapter->name); return 0; } -/* - * Following are decoder interface functions implemented by - * TVP5146/47 decoder driver. - */ - /** - * ioctl_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_querystd() - V4L2 decoder interface handler for querystd + * @sd: pointer to standard V4L2 sub-device structure * @std_id: standard V4L2 std_id ioctl enum * * Returns the current standard detected by TVP5146/47. If no active input is * detected, returns -EINVAL */ -static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) +static int tvp514x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); enum tvp514x_std current_std; enum tvp514x_input input_sel; u8 sync_lock_status, lock_mask; @@ -502,11 +528,11 @@ static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) return -EINVAL; /* get the current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; - input_sel = decoder->route.input; + input_sel = decoder->input; switch (input_sel) { case INPUT_CVBS_VI1A: @@ -544,42 +570,39 @@ static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id) return -EINVAL; } /* check whether signal is locked */ - sync_lock_status = tvp514x_read_reg(decoder->client, REG_STATUS1); + sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); if (lock_mask != (sync_lock_status & lock_mask)) return -EINVAL; /* No input detected */ decoder->current_std = current_std; *std_id = decoder->std_list[current_std].standard.id; - v4l_dbg(1, debug, decoder->client, "Current STD: %s", + v4l2_dbg(1, debug, sd, "Current STD: %s", decoder->std_list[current_std].standard.name); return 0; } /** - * ioctl_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_s_std() - V4L2 decoder interface handler for s_std + * @sd: pointer to standard V4L2 sub-device structure * @std_id: standard V4L2 v4l2_std_id ioctl enum * * If std_id is supported, sets the requested standard. Otherwise, returns * -EINVAL */ -static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) +static int tvp514x_s_std(struct v4l2_subdev *sd, v4l2_std_id std_id) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int err, i; - if (std_id == NULL) - return -EINVAL; - for (i = 0; i < decoder->num_stds; i++) - if (*std_id & decoder->std_list[i].standard.id) + if (std_id & decoder->std_list[i].standard.id) break; if ((i == decoder->num_stds) || (i == STD_INVALID)) return -EINVAL; - err = tvp514x_write_reg(decoder->client, REG_VIDEO_STD, + err = tvp514x_write_reg(sd, REG_VIDEO_STD, decoder->std_list[i].video_std); if (err) return err; @@ -588,24 +611,26 @@ static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) decoder->tvp514x_regs[REG_VIDEO_STD].val = decoder->std_list[i].video_std; - v4l_dbg(1, debug, decoder->client, "Standard set to: %s", + v4l2_dbg(1, debug, sd, "Standard set to: %s", decoder->std_list[i].standard.name); return 0; } /** - * ioctl_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl - * @s: pointer to standard V4L2 device structure - * @index: number of the input + * tvp514x_s_routing() - V4L2 decoder interface handler for s_routing + * @sd: pointer to standard V4L2 sub-device structure + * @input: input selector for routing the signal + * @output: output selector for routing the signal + * @config: config value. Not used * * If index is valid, selects the requested input. Otherwise, returns -EINVAL if * the input is not supported or there is no active signal present in the * selected input. */ -static int ioctl_s_routing(struct v4l2_int_device *s, - struct v4l2_routing *route) +static int tvp514x_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int err; enum tvp514x_input input_sel; enum tvp514x_output output_sel; @@ -613,20 +638,21 @@ static int ioctl_s_routing(struct v4l2_int_device *s, u8 sync_lock_status, lock_mask; int try_count = LOCK_RETRY_COUNT; - if ((!route) || (route->input >= INPUT_INVALID) || - (route->output >= OUTPUT_INVALID)) - return -EINVAL; /* Index out of bound */ + if ((input >= INPUT_INVALID) || + (output >= OUTPUT_INVALID)) + /* Index out of bound */ + return -EINVAL; - input_sel = route->input; - output_sel = route->output; + input_sel = input; + output_sel = output; - err = tvp514x_write_reg(decoder->client, REG_INPUT_SEL, input_sel); + err = tvp514x_write_reg(sd, REG_INPUT_SEL, input_sel); if (err) return err; - output_sel |= tvp514x_read_reg(decoder->client, + output_sel |= tvp514x_read_reg(sd, REG_OUTPUT_FORMATTER1) & 0x7; - err = tvp514x_write_reg(decoder->client, REG_OUTPUT_FORMATTER1, + err = tvp514x_write_reg(sd, REG_OUTPUT_FORMATTER1, output_sel); if (err) return err; @@ -637,7 +663,7 @@ static int ioctl_s_routing(struct v4l2_int_device *s, /* Clear status */ msleep(LOCK_RETRY_DELAY); err = - tvp514x_write_reg(decoder->client, REG_CLEAR_LOST_LOCK, 0x01); + tvp514x_write_reg(sd, REG_CLEAR_LOST_LOCK, 0x01); if (err) return err; @@ -672,7 +698,7 @@ static int ioctl_s_routing(struct v4l2_int_device *s, lock_mask = STATUS_HORZ_SYNC_LOCK_BIT | STATUS_VIRT_SYNC_LOCK_BIT; break; - /*Need to add other interfaces*/ + /* Need to add other interfaces*/ default: return -EINVAL; } @@ -682,42 +708,41 @@ static int ioctl_s_routing(struct v4l2_int_device *s, msleep(LOCK_RETRY_DELAY); /* get the current standard for future reference */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) continue; - sync_lock_status = tvp514x_read_reg(decoder->client, + sync_lock_status = tvp514x_read_reg(sd, REG_STATUS1); if (lock_mask == (sync_lock_status & lock_mask)) - break; /* Input detected */ + /* Input detected */ + break; } if ((current_std == STD_INVALID) || (try_count < 0)) return -EINVAL; decoder->current_std = current_std; - decoder->route.input = route->input; - decoder->route.output = route->output; + decoder->input = input; + decoder->output = output; - v4l_dbg(1, debug, decoder->client, - "Input set to: %d, std : %d", + v4l2_dbg(1, debug, sd, "Input set to: %d, std : %d", input_sel, current_std); return 0; } /** - * ioctl_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_queryctrl() - V4L2 decoder interface handler for queryctrl + * @sd: pointer to standard V4L2 sub-device structure * @qctrl: standard V4L2 v4l2_queryctrl structure * * If the requested control is supported, returns the control information. * Otherwise, returns -EINVAL if the control is not supported. */ static int -ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) +tvp514x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl) { - struct tvp514x_decoder *decoder = s->priv; int err = -EINVAL; if (qctrl == NULL) @@ -725,13 +750,13 @@ ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) switch (qctrl->id) { case V4L2_CID_BRIGHTNESS: - /* Brightness supported is (0-255), - */ + /* Brightness supported is (0-255), */ err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); break; case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: - /* Saturation and Contrast supported is - + /** + * Saturation and Contrast supported is - * Contrast: 0 - 255 (Default - 128) * Saturation: 0 - 255 (Default - 128) */ @@ -744,30 +769,27 @@ ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0); break; case V4L2_CID_AUTOGAIN: - /* Autogain is either 0 or 1*/ - memcpy(qctrl, &tvp514x_autogain_ctrl, - sizeof(struct v4l2_queryctrl)); - err = 0; + /** + * Auto Gain supported is - + * 0 - 1 (Default - 1) + */ + err = v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); break; default: - v4l_err(decoder->client, - "invalid control id %d\n", qctrl->id); + v4l2_err(sd, "invalid control id %d\n", qctrl->id); return err; } - v4l_dbg(1, debug, decoder->client, - "Query Control: %s : Min - %d, Max - %d, Def - %d", - qctrl->name, - qctrl->minimum, - qctrl->maximum, + v4l2_dbg(1, debug, sd, "Query Control:%s: Min - %d, Max - %d, Def - %d", + qctrl->name, qctrl->minimum, qctrl->maximum, qctrl->default_value); return err; } /** - * ioctl_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_g_ctrl() - V4L2 decoder interface handler for g_ctrl + * @sd: pointer to standard V4L2 sub-device structure * @ctrl: pointer to v4l2_control structure * * If the requested control is supported, returns the control's current @@ -775,9 +797,9 @@ ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) * supported. */ static int -ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +tvp514x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); if (ctrl == NULL) return -EINVAL; @@ -811,74 +833,70 @@ ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) break; default: - v4l_err(decoder->client, - "invalid control id %d\n", ctrl->id); + v4l2_err(sd, "invalid control id %d\n", ctrl->id); return -EINVAL; } - v4l_dbg(1, debug, decoder->client, - "Get Control: ID - %d - %d", + v4l2_dbg(1, debug, sd, "Get Control: ID - %d - %d", ctrl->id, ctrl->value); return 0; } /** - * ioctl_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl + * @sd: pointer to standard V4L2 sub-device structure * @ctrl: pointer to v4l2_control structure * * If the requested control is supported, sets the control's current * value in HW. Otherwise, returns -EINVAL if the control is not supported. */ static int -ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) +tvp514x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int err = -EINVAL, value; if (ctrl == NULL) return err; - value = (__s32) ctrl->value; + value = ctrl->value; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(decoder->client, - "invalid brightness setting %d\n", + v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_BRIGHTNESS, + err = tvp514x_write_reg(sd, REG_BRIGHTNESS, value); if (err) return err; + decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(decoder->client, - "invalid contrast setting %d\n", + v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_CONTRAST, - value); + err = tvp514x_write_reg(sd, REG_CONTRAST, value); if (err) return err; + decoder->tvp514x_regs[REG_CONTRAST].val = value; break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(decoder->client, - "invalid saturation setting %d\n", + v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_SATURATION, - value); + err = tvp514x_write_reg(sd, REG_SATURATION, value); if (err) return err; + decoder->tvp514x_regs[REG_SATURATION].val = value; break; case V4L2_CID_HUE: @@ -889,15 +907,13 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) else if (value == 0) value = 0; else { - v4l_err(decoder->client, - "invalid hue setting %d\n", - ctrl->value); + v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_HUE, - value); + err = tvp514x_write_reg(sd, REG_HUE, value); if (err) return err; + decoder->tvp514x_regs[REG_HUE].val = value; break; case V4L2_CID_AUTOGAIN: @@ -906,41 +922,38 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) else if (value == 0) value = 0x0C; else { - v4l_err(decoder->client, - "invalid auto gain setting %d\n", + v4l2_err(sd, "invalid auto gain setting %d\n", ctrl->value); return -ERANGE; } - err = tvp514x_write_reg(decoder->client, REG_AFE_GAIN_CTRL, - value); + err = tvp514x_write_reg(sd, REG_AFE_GAIN_CTRL, value); if (err) return err; + decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; break; default: - v4l_err(decoder->client, - "invalid control id %d\n", ctrl->id); + v4l2_err(sd, "invalid control id %d\n", ctrl->id); return err; } - v4l_dbg(1, debug, decoder->client, - "Set Control: ID - %d - %d", + v4l2_dbg(1, debug, sd, "Set Control: ID - %d - %d", ctrl->id, ctrl->value); return err; } /** - * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_enum_fmt_cap() - V4L2 decoder interface handler for enum_fmt + * @sd: pointer to standard V4L2 sub-device structure * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure * * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats */ static int -ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) +tvp514x_enum_fmt_cap(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int index; if (fmt == NULL) @@ -948,24 +961,25 @@ ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) index = fmt->index; if ((index >= decoder->num_fmts) || (index < 0)) - return -EINVAL; /* Index out of bound */ + /* Index out of bound */ + return -EINVAL; if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; memcpy(fmt, &decoder->fmt_list[index], sizeof(struct v4l2_fmtdesc)); - v4l_dbg(1, debug, decoder->client, - "Current FMT: index - %d (%s)", + v4l2_dbg(1, debug, sd, "Current FMT: index - %d (%s)", decoder->fmt_list[index].index, decoder->fmt_list[index].description); return 0; } /** - * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_try_fmt_cap() - V4L2 decoder interface handler for try_fmt + * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure * * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This @@ -973,9 +987,9 @@ ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt) * without actually making it take effect. */ static int -ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +tvp514x_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); int ifmt; struct v4l2_pix_format *pix; enum tvp514x_std current_std; @@ -984,12 +998,13 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) return -EINVAL; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + /* only capture is supported */ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; pix = &f->fmt.pix; /* Calculate height and width based on current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; @@ -1003,7 +1018,8 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) break; } if (ifmt == decoder->num_fmts) - ifmt = 0; /* None of the format matched, select default */ + /* None of the format matched, select default */ + ifmt = 0; pix->pixelformat = decoder->fmt_list[ifmt].pixelformat; pix->field = V4L2_FIELD_INTERLACED; @@ -1012,8 +1028,7 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) pix->colorspace = V4L2_COLORSPACE_SMPTE170M; pix->priv = 0; - v4l_dbg(1, debug, decoder->client, - "Try FMT: pixelformat - %s, bytesperline - %d" + v4l2_dbg(1, debug, sd, "Try FMT: pixelformat - %s, bytesperline - %d" "Width - %d, Height - %d", decoder->fmt_list[ifmt].description, pix->bytesperline, pix->width, pix->height); @@ -1021,8 +1036,8 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) } /** - * ioctl_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_s_fmt_cap() - V4L2 decoder interface handler for s_fmt + * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure * * If the requested format is supported, configures the HW to use that @@ -1030,9 +1045,9 @@ ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) * correctly configured. */ static int -ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +tvp514x_s_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); struct v4l2_pix_format *pix; int rval; @@ -1040,10 +1055,11 @@ ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) return -EINVAL; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; pix = &f->fmt.pix; - rval = ioctl_try_fmt_cap(s, f); + rval = tvp514x_try_fmt_cap(sd, f); if (rval) return rval; @@ -1053,28 +1069,28 @@ ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) } /** - * ioctl_g_fmt_cap - V4L2 decoder interface handler for ioctl_g_fmt_cap - * @s: pointer to standard V4L2 device structure + * tvp514x_g_fmt_cap() - V4L2 decoder interface handler for tvp514x_g_fmt_cap + * @sd: pointer to standard V4L2 sub-device structure * @f: pointer to standard V4L2 v4l2_format structure * * Returns the decoder's current pixel format in the v4l2_format * parameter. */ static int -ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) +tvp514x_g_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); if (f == NULL) return -EINVAL; if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; f->fmt.pix = decoder->pix; - v4l_dbg(1, debug, decoder->client, - "Current FMT: bytesperline - %d" + v4l2_dbg(1, debug, sd, "Current FMT: bytesperline - %d" "Width - %d, Height - %d", decoder->pix.bytesperline, decoder->pix.width, decoder->pix.height); @@ -1082,16 +1098,16 @@ ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) } /** - * ioctl_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_g_parm() - V4L2 decoder interface handler for g_parm + * @sd: pointer to standard V4L2 sub-device structure * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure * * Returns the decoder's video CAPTURE parameters. */ static int -ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +tvp514x_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); struct v4l2_captureparm *cparm; enum tvp514x_std current_std; @@ -1099,13 +1115,14 @@ ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) return -EINVAL; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; memset(a, 0, sizeof(*a)); a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* get the current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; @@ -1120,17 +1137,17 @@ ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) } /** - * ioctl_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl - * @s: pointer to standard V4L2 device structure + * tvp514x_s_parm() - V4L2 decoder interface handler for s_parm + * @sd: pointer to standard V4L2 sub-device structure * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure * * Configures the decoder to use the input parameters, if possible. If * not possible, returns the appropriate error code. */ static int -ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) +tvp514x_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) { - struct tvp514x_decoder *decoder = s->priv; + struct tvp514x_decoder *decoder = to_decoder(sd); struct v4l2_fract *timeperframe; enum tvp514x_std current_std; @@ -1138,12 +1155,13 @@ ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) return -EINVAL; if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; /* only capture is supported */ + /* only capture is supported */ + return -EINVAL; timeperframe = &a->parm.capture.timeperframe; /* get the current standard */ - current_std = tvp514x_get_current_std(decoder); + current_std = tvp514x_get_current_std(sd); if (current_std == STD_INVALID) return -EINVAL; @@ -1156,111 +1174,58 @@ ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) } /** - * ioctl_g_ifparm - V4L2 decoder interface handler for vidioc_int_g_ifparm_num - * @s: pointer to standard V4L2 device structure - * @p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure - * - * Gets slave interface parameters. - * Calculates the required xclk value to support the requested - * clock parameters in p. This value is returned in the p - * parameter. - */ -static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p) -{ - struct tvp514x_decoder *decoder = s->priv; - int rval; - - if (p == NULL) - return -EINVAL; - - if (NULL == decoder->pdata->ifparm) - return -EINVAL; - - rval = decoder->pdata->ifparm(p); - if (rval) { - v4l_err(decoder->client, "g_ifparm.Err[%d]\n", rval); - return rval; - } - - p->u.bt656.clock_curr = TVP514X_XCLK_BT656; - - return 0; -} - -/** - * ioctl_g_priv - V4L2 decoder interface handler for vidioc_int_g_priv_num - * @s: pointer to standard V4L2 device structure - * @p: void pointer to hold decoder's private data address - * - * Returns device's (decoder's) private data area address in p parameter - */ -static int ioctl_g_priv(struct v4l2_int_device *s, void *p) -{ - struct tvp514x_decoder *decoder = s->priv; - - if (NULL == decoder->pdata->priv_data_set) - return -EINVAL; - - return decoder->pdata->priv_data_set(p); -} - -/** - * ioctl_s_power - V4L2 decoder interface handler for vidioc_int_s_power_num - * @s: pointer to standard V4L2 device structure - * @on: power state to which device is to be set + * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream + * @sd: pointer to standard V4L2 sub-device structure + * @enable: streaming enable or disable * - * Sets devices power state to requrested state, if possible. + * Sets streaming to enable or disable, if possible. */ -static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) +static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable) { - struct tvp514x_decoder *decoder = s->priv; int err = 0; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tvp514x_decoder *decoder = to_decoder(sd); - switch (on) { - case V4L2_POWER_OFF: - /* Power Down Sequence */ - err = - tvp514x_write_reg(decoder->client, REG_OPERATION_MODE, - 0x01); - /* Disable mux for TVP5146/47 decoder data path */ - if (decoder->pdata->power_set) - err |= decoder->pdata->power_set(on); - decoder->state = STATE_NOT_DETECTED; - break; + if (decoder->streaming == enable) + return 0; - case V4L2_POWER_STANDBY: - if (decoder->pdata->power_set) - err = decoder->pdata->power_set(on); + switch (enable) { + case 0: + { + /* Power Down Sequence */ + err = tvp514x_write_reg(sd, REG_OPERATION_MODE, 0x01); + if (err) { + v4l2_err(sd, "Unable to turn off decoder\n"); + return err; + } + decoder->streaming = enable; break; + } + case 1: + { + struct tvp514x_reg *int_seq = (struct tvp514x_reg *) + client->driver->id_table->driver_data; - case V4L2_POWER_ON: - /* Enable mux for TVP5146/47 decoder data path */ - if ((decoder->pdata->power_set) && - (decoder->state == STATE_NOT_DETECTED)) { - int i; - struct tvp514x_init_seq *int_seq = - (struct tvp514x_init_seq *) - decoder->id->driver_data; - - err = decoder->pdata->power_set(on); - - /* Power Up Sequence */ - for (i = 0; i < int_seq->no_regs; i++) { - err |= tvp514x_write_reg(decoder->client, - int_seq->init_reg_seq[i].reg, - int_seq->init_reg_seq[i].val); - } - /* Detect the sensor is not already detected */ - err |= tvp514x_detect(decoder); - if (err) { - v4l_err(decoder->client, - "Unable to detect decoder\n"); - return err; - } + /* Power Up Sequence */ + err = tvp514x_write_regs(sd, int_seq); + if (err) { + v4l2_err(sd, "Unable to turn on decoder\n"); + return err; } - err |= tvp514x_configure(decoder); + /* Detect if not already detected */ + err = tvp514x_detect(sd, decoder); + if (err) { + v4l2_err(sd, "Unable to detect decoder\n"); + return err; + } + err = tvp514x_configure(sd, decoder); + if (err) { + v4l2_err(sd, "Unable to configure decoder\n"); + return err; + } + decoder->streaming = enable; break; - + } default: err = -ENODEV; break; @@ -1269,93 +1234,38 @@ static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on) return err; } -/** - * ioctl_init - V4L2 decoder interface handler for VIDIOC_INT_INIT - * @s: pointer to standard V4L2 device structure - * - * Initialize the decoder device (calls tvp514x_configure()) - */ -static int ioctl_init(struct v4l2_int_device *s) -{ - struct tvp514x_decoder *decoder = s->priv; - - /* Set default standard to auto */ - decoder->tvp514x_regs[REG_VIDEO_STD].val = - VIDEO_STD_AUTO_SWITCH_BIT; - - return tvp514x_configure(decoder); -} - -/** - * ioctl_dev_exit - V4L2 decoder interface handler for vidioc_int_dev_exit_num - * @s: pointer to standard V4L2 device structure - * - * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init. - */ -static int ioctl_dev_exit(struct v4l2_int_device *s) -{ - return 0; -} - -/** - * ioctl_dev_init - V4L2 decoder interface handler for vidioc_int_dev_init_num - * @s: pointer to standard V4L2 device structure - * - * Initialise the device when slave attaches to the master. Returns 0 if - * TVP5146/47 device could be found, otherwise returns appropriate error. - */ -static int ioctl_dev_init(struct v4l2_int_device *s) -{ - struct tvp514x_decoder *decoder = s->priv; - int err; - - err = tvp514x_detect(decoder); - if (err < 0) { - v4l_err(decoder->client, - "Unable to detect decoder\n"); - return err; - } - - v4l_info(decoder->client, - "chip version 0x%.2x detected\n", decoder->ver); +static const struct v4l2_subdev_core_ops tvp514x_core_ops = { + .queryctrl = tvp514x_queryctrl, + .g_ctrl = tvp514x_g_ctrl, + .s_ctrl = tvp514x_s_ctrl, + .s_std = tvp514x_s_std, +}; - return 0; -} +static const struct v4l2_subdev_video_ops tvp514x_video_ops = { + .s_routing = tvp514x_s_routing, + .querystd = tvp514x_querystd, + .enum_fmt = tvp514x_enum_fmt_cap, + .g_fmt = tvp514x_g_fmt_cap, + .try_fmt = tvp514x_try_fmt_cap, + .s_fmt = tvp514x_s_fmt_cap, + .g_parm = tvp514x_g_parm, + .s_parm = tvp514x_s_parm, + .s_stream = tvp514x_s_stream, +}; -static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = { - {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init}, - {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*) ioctl_dev_exit}, - {vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power}, - {vidioc_int_g_priv_num, (v4l2_int_ioctl_func*) ioctl_g_priv}, - {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm}, - {vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init}, - {vidioc_int_enum_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap}, - {vidioc_int_try_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_try_fmt_cap}, - {vidioc_int_g_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_g_fmt_cap}, - {vidioc_int_s_fmt_cap_num, - (v4l2_int_ioctl_func *) ioctl_s_fmt_cap}, - {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm}, - {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm}, - {vidioc_int_queryctrl_num, - (v4l2_int_ioctl_func *) ioctl_queryctrl}, - {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl}, - {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl}, - {vidioc_int_querystd_num, (v4l2_int_ioctl_func *) ioctl_querystd}, - {vidioc_int_s_std_num, (v4l2_int_ioctl_func *) ioctl_s_std}, - {vidioc_int_s_video_routing_num, - (v4l2_int_ioctl_func *) ioctl_s_routing}, +static const struct v4l2_subdev_ops tvp514x_ops = { + .core = &tvp514x_core_ops, + .video = &tvp514x_video_ops, }; static struct tvp514x_decoder tvp514x_dev = { - .state = STATE_NOT_DETECTED, + .streaming = 0, .fmt_list = tvp514x_fmt_list, .num_fmts = ARRAY_SIZE(tvp514x_fmt_list), - .pix = { /* Default to NTSC 8-bit YUV 422 */ + .pix = { + /* Default to NTSC 8-bit YUV 422 */ .width = NTSC_NUM_ACTIVE_PIXELS, .height = NTSC_NUM_ACTIVE_LINES, .pixelformat = V4L2_PIX_FMT_UYVY, @@ -1369,20 +1279,13 @@ static struct tvp514x_decoder tvp514x_dev = { .current_std = STD_NTSC_MJ, .std_list = tvp514x_std_list, .num_stds = ARRAY_SIZE(tvp514x_std_list), - .v4l2_int_device = { - .module = THIS_MODULE, - .name = TVP514X_MODULE_NAME, - .type = v4l2_int_type_slave, - }, - .tvp514x_slave = { - .ioctls = tvp514x_ioctl_desc, - .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc), - }, + }; /** - * tvp514x_probe - decoder driver i2c probe handler + * tvp514x_probe() - decoder driver i2c probe handler * @client: i2c driver client device structure + * @id: i2c driver id table * * Register decoder as an i2c client device and V4L2 * device. @@ -1391,88 +1294,71 @@ static int tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tvp514x_decoder *decoder; - int err; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; + if (!client->dev.platform_data) { + v4l2_err(client, "No platform data!!\n"); + return -ENODEV; + } + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); if (!decoder) return -ENOMEM; - if (!client->dev.platform_data) { - v4l_err(client, "No platform data!!\n"); - err = -ENODEV; - goto out_free; - } - + /* Initialize the tvp514x_decoder with default configuration */ *decoder = tvp514x_dev; - decoder->v4l2_int_device.priv = decoder; - decoder->pdata = client->dev.platform_data; - decoder->v4l2_int_device.u.slave = &decoder->tvp514x_slave; + /* Copy default register configuration */ memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, sizeof(tvp514x_reg_list_default)); - /* + + /* Copy board specific information here */ + decoder->pdata = client->dev.platform_data; + + /** * Fetch platform specific data, and configure the * tvp514x_reg_list[] accordingly. Since this is one * time configuration, no need to preserve. */ decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |= - (decoder->pdata->clk_polarity << 1); + (decoder->pdata->clk_polarity << 1); decoder->tvp514x_regs[REG_SYNC_CONTROL].val |= - ((decoder->pdata->hs_polarity << 2) | - (decoder->pdata->vs_polarity << 3)); - /* - * Save the id data, required for power up sequence - */ - decoder->id = (struct i2c_device_id *)id; - /* Attach to Master */ - strcpy(decoder->v4l2_int_device.u.slave->attach_to, - decoder->pdata->master); - decoder->client = client; - i2c_set_clientdata(client, decoder); + ((decoder->pdata->hs_polarity << 2) | + (decoder->pdata->vs_polarity << 3)); + /* Set default standard to auto */ + decoder->tvp514x_regs[REG_VIDEO_STD].val = + VIDEO_STD_AUTO_SWITCH_BIT; /* Register with V4L2 layer as slave device */ - err = v4l2_int_device_register(&decoder->v4l2_int_device); - if (err) { - i2c_set_clientdata(client, NULL); - v4l_err(client, - "Unable to register to v4l2. Err[%d]\n", err); - goto out_free; - - } else - v4l_info(client, "Registered to v4l2 master %s!!\n", - decoder->pdata->master); + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &tvp514x_ops); + + v4l2_info(sd, "%s decoder driver registered !!\n", sd->name); + return 0; -out_free: - kfree(decoder); - return err; } /** - * tvp514x_remove - decoder driver i2c remove handler + * tvp514x_remove() - decoder driver i2c remove handler * @client: i2c driver client device structure * * Unregister decoder as an i2c client device and V4L2 * device. Complement of tvp514x_probe(). */ -static int __exit tvp514x_remove(struct i2c_client *client) +static int tvp514x_remove(struct i2c_client *client) { - struct tvp514x_decoder *decoder = i2c_get_clientdata(client); - - if (!client->adapter) - return -ENODEV; /* our client isn't attached */ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct tvp514x_decoder *decoder = to_decoder(sd); - v4l2_int_device_unregister(&decoder->v4l2_int_device); - i2c_set_clientdata(client, NULL); + v4l2_device_unregister_subdev(sd); kfree(decoder); return 0; } -/* - * TVP5146 Init/Power on Sequence - */ +/* TVP5146 Init/Power on Sequence */ static const struct tvp514x_reg tvp5146_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, @@ -1485,14 +1371,10 @@ static const struct tvp514x_reg tvp5146_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, {TOK_WRITE, REG_OPERATION_MODE, 0x01}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, }; -static const struct tvp514x_init_seq tvp5146_init = { - .no_regs = ARRAY_SIZE(tvp5146_init_reg_seq), - .init_reg_seq = tvp5146_init_reg_seq, -}; -/* - * TVP5147 Init/Power on Sequence - */ + +/* TVP5147 Init/Power on Sequence */ static const struct tvp514x_reg tvp5147_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02}, {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00}, @@ -1512,71 +1394,51 @@ static const struct tvp514x_reg tvp5147_init_reg_seq[] = { {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00}, {TOK_WRITE, REG_OPERATION_MODE, 0x01}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, }; -static const struct tvp514x_init_seq tvp5147_init = { - .no_regs = ARRAY_SIZE(tvp5147_init_reg_seq), - .init_reg_seq = tvp5147_init_reg_seq, -}; -/* - * TVP5146M2/TVP5147M1 Init/Power on Sequence - */ + +/* TVP5146M2/TVP5147M1 Init/Power on Sequence */ static const struct tvp514x_reg tvp514xm_init_reg_seq[] = { {TOK_WRITE, REG_OPERATION_MODE, 0x01}, {TOK_WRITE, REG_OPERATION_MODE, 0x00}, + {TOK_TERM, 0, 0}, }; -static const struct tvp514x_init_seq tvp514xm_init = { - .no_regs = ARRAY_SIZE(tvp514xm_init_reg_seq), - .init_reg_seq = tvp514xm_init_reg_seq, -}; -/* + +/** * I2C Device Table - * * name - Name of the actual device/chip. * driver_data - Driver data */ static const struct i2c_device_id tvp514x_id[] = { - {"tvp5146", (unsigned long)&tvp5146_init}, - {"tvp5146m2", (unsigned long)&tvp514xm_init}, - {"tvp5147", (unsigned long)&tvp5147_init}, - {"tvp5147m1", (unsigned long)&tvp514xm_init}, + {"tvp5146", (unsigned long)tvp5146_init_reg_seq}, + {"tvp5146m2", (unsigned long)tvp514xm_init_reg_seq}, + {"tvp5147", (unsigned long)tvp5147_init_reg_seq}, + {"tvp5147m1", (unsigned long)tvp514xm_init_reg_seq}, {}, }; MODULE_DEVICE_TABLE(i2c, tvp514x_id); -static struct i2c_driver tvp514x_i2c_driver = { +static struct i2c_driver tvp514x_driver = { .driver = { - .name = TVP514X_MODULE_NAME, - .owner = THIS_MODULE, - }, + .owner = THIS_MODULE, + .name = TVP514X_MODULE_NAME, + }, .probe = tvp514x_probe, - .remove = __exit_p(tvp514x_remove), + .remove = tvp514x_remove, .id_table = tvp514x_id, }; -/** - * tvp514x_init - * - * Module init function - */ static int __init tvp514x_init(void) { - return i2c_add_driver(&tvp514x_i2c_driver); + return i2c_add_driver(&tvp514x_driver); } -/** - * tvp514x_cleanup - * - * Module exit function - */ -static void __exit tvp514x_cleanup(void) +static void __exit tvp514x_exit(void) { - i2c_del_driver(&tvp514x_i2c_driver); + i2c_del_driver(&tvp514x_driver); } module_init(tvp514x_init); -module_exit(tvp514x_cleanup); - -MODULE_AUTHOR("Texas Instruments"); -MODULE_DESCRIPTION("TVP514X linux decoder driver"); -MODULE_LICENSE("GPL"); +module_exit(tvp514x_exit); diff --git a/drivers/media/video/tvp514x_regs.h b/drivers/media/video/tvp514x_regs.h index 351620aeecc2..18f29ad0dfe2 100644 --- a/drivers/media/video/tvp514x_regs.h +++ b/drivers/media/video/tvp514x_regs.h @@ -284,14 +284,4 @@ struct tvp514x_reg { u32 val; }; -/** - * struct tvp514x_init_seq - Structure for TVP5146/47/46M2/47M1 power up - * Sequence. - * @ no_regs - Number of registers to write for power up sequence. - * @ init_reg_seq - Array of registers and respective value to write. - */ -struct tvp514x_init_seq { - unsigned int no_regs; - const struct tvp514x_reg *init_reg_seq; -}; #endif /* ifndef _TVP514X_REGS_H */ diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index aa5065ea09ed..269ab044072a 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -24,7 +24,7 @@ #include <linux/delay.h> #include <linux/videodev2.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-common.h> +#include <media/v4l2-subdev.h> #include <media/soc_camera.h> #include <media/tw9910.h> @@ -223,9 +223,8 @@ struct tw9910_hsync_ctrl { }; struct tw9910_priv { + struct v4l2_subdev subdev; struct tw9910_video_info *info; - struct i2c_client *client; - struct soc_camera_device icd; const struct tw9910_scale_ctrl *scale; }; @@ -356,6 +355,12 @@ static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = { /* * general function */ +static struct tw9910_priv *to_tw9910(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct tw9910_priv, + subdev); +} + static int tw9910_set_scale(struct i2c_client *client, const struct tw9910_scale_ctrl *scale) { @@ -509,44 +514,20 @@ tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height) /* * soc_camera_ops function */ -static int tw9910_init(struct soc_camera_device *icd) -{ - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); - int ret = 0; - - if (priv->info->link.power) { - ret = priv->info->link.power(&priv->client->dev, 1); - if (ret < 0) - return ret; - } - - if (priv->info->link.reset) - ret = priv->info->link.reset(&priv->client->dev); - - return ret; -} - -static int tw9910_release(struct soc_camera_device *icd) +static int tw9910_s_stream(struct v4l2_subdev *sd, int enable) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); - int ret = 0; - - if (priv->info->link.power) - ret = priv->info->link.power(&priv->client->dev, 0); - - return ret; -} + struct i2c_client *client = sd->priv; + struct tw9910_priv *priv = to_tw9910(client); -static int tw9910_start_capture(struct soc_camera_device *icd) -{ - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + if (!enable) + return 0; if (!priv->scale) { - dev_err(&icd->dev, "norm select error\n"); + dev_err(&client->dev, "norm select error\n"); return -EPERM; } - dev_dbg(&icd->dev, "%s %dx%d\n", + dev_dbg(&client->dev, "%s %dx%d\n", priv->scale->name, priv->scale->width, priv->scale->height); @@ -554,11 +535,6 @@ static int tw9910_start_capture(struct soc_camera_device *icd) return 0; } -static int tw9910_stop_capture(struct soc_camera_device *icd) -{ - return 0; -} - static int tw9910_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { @@ -567,8 +543,9 @@ static int tw9910_set_bus_param(struct soc_camera_device *icd, static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); - struct soc_camera_link *icl = priv->client->dev.platform_data; + struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); + struct tw9910_priv *priv = to_tw9910(client); + struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth; @@ -576,21 +553,11 @@ static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd) return soc_camera_apply_sensor_flags(icl, flags); } -static int tw9910_get_chip_id(struct soc_camera_device *icd, - struct v4l2_dbg_chip_ident *id) -{ - id->ident = V4L2_IDENT_TW9910; - id->revision = 0; - - return 0; -} - -static int tw9910_set_std(struct soc_camera_device *icd, - v4l2_std_id *a) +static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) { int ret = -EINVAL; - if (*a & (V4L2_STD_NTSC | V4L2_STD_PAL)) + if (norm & (V4L2_STD_NTSC | V4L2_STD_PAL)) ret = 0; return ret; @@ -606,17 +573,26 @@ static int tw9910_enum_input(struct soc_camera_device *icd, return 0; } +static int tw9910_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + id->ident = V4L2_IDENT_TW9910; + id->revision = 0; + + return 0; +} + #ifdef CONFIG_VIDEO_ADV_DEBUG -static int tw9910_get_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int tw9910_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + struct i2c_client *client = sd->priv; int ret; if (reg->reg > 0xff) return -EINVAL; - ret = i2c_smbus_read_byte_data(priv->client, reg->reg); + ret = i2c_smbus_read_byte_data(client, reg->reg); if (ret < 0) return ret; @@ -628,23 +604,25 @@ static int tw9910_get_register(struct soc_camera_device *icd, return 0; } -static int tw9910_set_register(struct soc_camera_device *icd, - struct v4l2_dbg_register *reg) +static int tw9910_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + struct i2c_client *client = sd->priv; if (reg->reg > 0xff || reg->val > 0xff) return -EINVAL; - return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val); + return i2c_smbus_write_byte_data(client, reg->reg, reg->val); } #endif -static int tw9910_set_crop(struct soc_camera_device *icd, - struct v4l2_rect *rect) +static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + struct v4l2_rect *rect = &a->c; + struct i2c_client *client = sd->priv; + struct tw9910_priv *priv = to_tw9910(client); + struct soc_camera_device *icd = client->dev.platform_data; int ret = -EINVAL; u8 val; @@ -658,8 +636,8 @@ static int tw9910_set_crop(struct soc_camera_device *icd, /* * reset hardware */ - tw9910_reset(priv->client); - ret = tw9910_write_array(priv->client, tw9910_default_regs); + tw9910_reset(client); + ret = tw9910_write_array(client, tw9910_default_regs); if (ret < 0) goto tw9910_set_fmt_error; @@ -670,7 +648,7 @@ static int tw9910_set_crop(struct soc_camera_device *icd, if (SOCAM_DATAWIDTH_16 == priv->info->buswidth) val = LEN; - ret = tw9910_mask_set(priv->client, OPFORM, LEN, val); + ret = tw9910_mask_set(client, OPFORM, LEN, val); if (ret < 0) goto tw9910_set_fmt_error; @@ -698,52 +676,139 @@ static int tw9910_set_crop(struct soc_camera_device *icd, val = 0; } - ret = tw9910_mask_set(priv->client, VBICNTL, RTSEL_MASK, val); + ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val); if (ret < 0) goto tw9910_set_fmt_error; /* * set scale */ - ret = tw9910_set_scale(priv->client, priv->scale); + ret = tw9910_set_scale(client, priv->scale); if (ret < 0) goto tw9910_set_fmt_error; /* * set cropping */ - ret = tw9910_set_cropping(priv->client, &tw9910_cropping_ctrl); + ret = tw9910_set_cropping(client, &tw9910_cropping_ctrl); if (ret < 0) goto tw9910_set_fmt_error; /* * set hsync */ - ret = tw9910_set_hsync(priv->client, &tw9910_hsync_ctrl); + ret = tw9910_set_hsync(client, &tw9910_hsync_ctrl); if (ret < 0) goto tw9910_set_fmt_error; + rect->width = priv->scale->width; + rect->height = priv->scale->height; + rect->left = 0; + rect->top = 0; + return ret; tw9910_set_fmt_error: - tw9910_reset(priv->client); + tw9910_reset(client); priv->scale = NULL; return ret; } -static int tw9910_set_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct tw9910_priv *priv = to_tw9910(client); + + if (!priv->scale) { + int ret; + struct v4l2_crop crop = { + .c = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }; + ret = tw9910_s_crop(sd, &crop); + if (ret < 0) + return ret; + } + + a->c.left = 0; + a->c.top = 0; + a->c.width = priv->scale->width; + a->c.height = priv->scale->height; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) { + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = 768; + a->bounds.height = 576; + a->defrect.left = 0; + a->defrect.top = 0; + a->defrect.width = 640; + a->defrect.height = 480; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int tw9910_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct tw9910_priv *priv = to_tw9910(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (!priv->scale) { + int ret; + struct v4l2_crop crop = { + .c = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }; + ret = tw9910_s_crop(sd, &crop); + if (ret < 0) + return ret; + } + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + pix->width = priv->scale->width; + pix->height = priv->scale->height; + pix->pixelformat = V4L2_PIX_FMT_VYUY; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->field = V4L2_FIELD_INTERLACED; + + return 0; +} + +static int tw9910_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct tw9910_priv *priv = to_tw9910(client); struct v4l2_pix_format *pix = &f->fmt.pix; - struct v4l2_rect rect = { - .left = icd->x_current, - .top = icd->y_current, - .width = pix->width, - .height = pix->height, + /* See tw9910_s_crop() - no proper cropping support */ + struct v4l2_crop a = { + .c = { + .left = 0, + .top = 0, + .width = pix->width, + .height = pix->height, + }, }; - int i; + int i, ret; /* * check color format @@ -755,19 +820,25 @@ static int tw9910_set_fmt(struct soc_camera_device *icd, if (i == ARRAY_SIZE(tw9910_color_fmt)) return -EINVAL; - return tw9910_set_crop(icd, &rect); + ret = tw9910_s_crop(sd, &a); + if (!ret) { + pix->width = priv->scale->width; + pix->height = priv->scale->height; + } + return ret; } -static int tw9910_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) +static int tw9910_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) { + struct i2c_client *client = sd->priv; + struct soc_camera_device *icd = client->dev.platform_data; struct v4l2_pix_format *pix = &f->fmt.pix; const struct tw9910_scale_ctrl *scale; if (V4L2_FIELD_ANY == pix->field) { pix->field = V4L2_FIELD_INTERLACED; } else if (V4L2_FIELD_INTERLACED != pix->field) { - dev_err(&icd->dev, "Field type invalid.\n"); + dev_err(&client->dev, "Field type invalid.\n"); return -EINVAL; } @@ -784,11 +855,11 @@ static int tw9910_try_fmt(struct soc_camera_device *icd, return 0; } -static int tw9910_video_probe(struct soc_camera_device *icd) +static int tw9910_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) { - struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); + struct tw9910_priv *priv = to_tw9910(client); s32 val; - int ret; /* * We must have a parent by now. And it cannot be a wrong one. @@ -803,7 +874,7 @@ static int tw9910_video_probe(struct soc_camera_device *icd) */ if (SOCAM_DATAWIDTH_16 != priv->info->buswidth && SOCAM_DATAWIDTH_8 != priv->info->buswidth) { - dev_err(&icd->dev, "bus width error\n"); + dev_err(&client->dev, "bus width error\n"); return -ENODEV; } @@ -813,54 +884,54 @@ static int tw9910_video_probe(struct soc_camera_device *icd) /* * check and show Product ID */ - val = i2c_smbus_read_byte_data(priv->client, ID); + val = i2c_smbus_read_byte_data(client, ID); + if (0x0B != GET_ID(val) || 0x00 != GET_ReV(val)) { - dev_err(&icd->dev, + dev_err(&client->dev, "Product ID error %x:%x\n", GET_ID(val), GET_ReV(val)); return -ENODEV; } - dev_info(&icd->dev, + dev_info(&client->dev, "tw9910 Product ID %0x:%0x\n", GET_ID(val), GET_ReV(val)); - ret = soc_camera_video_start(icd); - if (ret < 0) - return ret; - icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL; icd->vdev->current_norm = V4L2_STD_NTSC; - return ret; -} - -static void tw9910_video_remove(struct soc_camera_device *icd) -{ - soc_camera_video_stop(icd); + return 0; } static struct soc_camera_ops tw9910_ops = { - .owner = THIS_MODULE, - .probe = tw9910_video_probe, - .remove = tw9910_video_remove, - .init = tw9910_init, - .release = tw9910_release, - .start_capture = tw9910_start_capture, - .stop_capture = tw9910_stop_capture, - .set_crop = tw9910_set_crop, - .set_fmt = tw9910_set_fmt, - .try_fmt = tw9910_try_fmt, .set_bus_param = tw9910_set_bus_param, .query_bus_param = tw9910_query_bus_param, - .get_chip_id = tw9910_get_chip_id, - .set_std = tw9910_set_std, .enum_input = tw9910_enum_input, +}; + +static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { + .g_chip_ident = tw9910_g_chip_ident, + .s_std = tw9910_s_std, #ifdef CONFIG_VIDEO_ADV_DEBUG - .get_register = tw9910_get_register, - .set_register = tw9910_set_register, + .g_register = tw9910_g_register, + .s_register = tw9910_s_register, #endif }; +static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = { + .s_stream = tw9910_s_stream, + .g_fmt = tw9910_g_fmt, + .s_fmt = tw9910_s_fmt, + .try_fmt = tw9910_try_fmt, + .cropcap = tw9910_cropcap, + .g_crop = tw9910_g_crop, + .s_crop = tw9910_s_crop, +}; + +static struct v4l2_subdev_ops tw9910_subdev_ops = { + .core = &tw9910_subdev_core_ops, + .video = &tw9910_subdev_video_ops, +}; + /* * i2c_driver function */ @@ -871,18 +942,24 @@ static int tw9910_probe(struct i2c_client *client, { struct tw9910_priv *priv; struct tw9910_video_info *info; - struct soc_camera_device *icd; - const struct tw9910_scale_ctrl *scale; - int i, ret; + struct soc_camera_device *icd = client->dev.platform_data; + struct i2c_adapter *adapter = + to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&client->dev, "TW9910: missing soc-camera data!\n"); + return -EINVAL; + } - if (!client->dev.platform_data) + icl = to_soc_camera_link(icd); + if (!icl) return -EINVAL; - info = container_of(client->dev.platform_data, - struct tw9910_video_info, link); + info = container_of(icl, struct tw9910_video_info, link); - if (!i2c_check_functionality(to_i2c_adapter(client->dev.parent), - I2C_FUNC_SMBUS_BYTE_DATA)) { + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, "I2C-Adapter doesn't support " "I2C_FUNC_SMBUS_BYTE_DATA\n"); @@ -894,40 +971,15 @@ static int tw9910_probe(struct i2c_client *client, return -ENOMEM; priv->info = info; - priv->client = client; - i2c_set_clientdata(client, priv); - icd = &priv->icd; + v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); + icd->ops = &tw9910_ops; - icd->control = &client->dev; icd->iface = info->link.bus_id; - /* - * set width and height - */ - icd->width_max = tw9910_ntsc_scales[0].width; /* set default */ - icd->width_min = tw9910_ntsc_scales[0].width; - icd->height_max = tw9910_ntsc_scales[0].height; - icd->height_min = tw9910_ntsc_scales[0].height; - - scale = tw9910_ntsc_scales; - for (i = 0; i < ARRAY_SIZE(tw9910_ntsc_scales); i++) { - icd->width_max = max(scale[i].width, icd->width_max); - icd->width_min = min(scale[i].width, icd->width_min); - icd->height_max = max(scale[i].height, icd->height_max); - icd->height_min = min(scale[i].height, icd->height_min); - } - scale = tw9910_pal_scales; - for (i = 0; i < ARRAY_SIZE(tw9910_pal_scales); i++) { - icd->width_max = max(scale[i].width, icd->width_max); - icd->width_min = min(scale[i].width, icd->width_min); - icd->height_max = max(scale[i].height, icd->height_max); - icd->height_min = min(scale[i].height, icd->height_min); - } - - ret = soc_camera_device_register(icd); - + ret = tw9910_video_probe(icd, client); if (ret) { + icd->ops = NULL; i2c_set_clientdata(client, NULL); kfree(priv); } @@ -937,9 +989,10 @@ static int tw9910_probe(struct i2c_client *client, static int tw9910_remove(struct i2c_client *client) { - struct tw9910_priv *priv = i2c_get_clientdata(client); + struct tw9910_priv *priv = to_tw9910(client); + struct soc_camera_device *icd = client->dev.platform_data; - soc_camera_device_unregister(&priv->icd); + icd->ops = NULL; i2c_set_clientdata(client, NULL); kfree(priv); return 0; diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 36a6ba92df27..c3225a561748 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -34,7 +34,7 @@ static struct uvc_control_info uvc_ctrls[] = { { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_BRIGHTNESS_CONTROL, + .selector = UVC_PU_BRIGHTNESS_CONTROL, .index = 0, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -42,7 +42,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_CONTRAST_CONTROL, + .selector = UVC_PU_CONTRAST_CONTROL, .index = 1, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -50,7 +50,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_HUE_CONTROL, + .selector = UVC_PU_HUE_CONTROL, .index = 2, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -58,7 +58,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_SATURATION_CONTROL, + .selector = UVC_PU_SATURATION_CONTROL, .index = 3, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -66,7 +66,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_SHARPNESS_CONTROL, + .selector = UVC_PU_SHARPNESS_CONTROL, .index = 4, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -74,7 +74,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_GAMMA_CONTROL, + .selector = UVC_PU_GAMMA_CONTROL, .index = 5, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -82,7 +82,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL, .index = 6, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -90,7 +90,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, .index = 7, .size = 4, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -98,7 +98,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, + .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL, .index = 8, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -106,7 +106,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_GAIN_CONTROL, + .selector = UVC_PU_GAIN_CONTROL, .index = 9, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -114,7 +114,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_POWER_LINE_FREQUENCY_CONTROL, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, .index = 10, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -122,7 +122,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_HUE_AUTO_CONTROL, + .selector = UVC_PU_HUE_AUTO_CONTROL, .index = 11, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -130,7 +130,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, .index = 12, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -138,7 +138,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, .index = 13, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -146,7 +146,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_DIGITAL_MULTIPLIER_CONTROL, + .selector = UVC_PU_DIGITAL_MULTIPLIER_CONTROL, .index = 14, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -154,7 +154,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL, + .selector = UVC_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL, .index = 15, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -162,21 +162,21 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_ANALOG_VIDEO_STANDARD_CONTROL, + .selector = UVC_PU_ANALOG_VIDEO_STANDARD_CONTROL, .index = 16, .size = 1, .flags = UVC_CONTROL_GET_CUR, }, { .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_ANALOG_LOCK_STATUS_CONTROL, + .selector = UVC_PU_ANALOG_LOCK_STATUS_CONTROL, .index = 17, .size = 1, .flags = UVC_CONTROL_GET_CUR, }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_SCANNING_MODE_CONTROL, + .selector = UVC_CT_SCANNING_MODE_CONTROL, .index = 0, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -184,7 +184,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_AE_MODE_CONTROL, + .selector = UVC_CT_AE_MODE_CONTROL, .index = 1, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -193,7 +193,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_AE_PRIORITY_CONTROL, + .selector = UVC_CT_AE_PRIORITY_CONTROL, .index = 2, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -201,7 +201,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, .index = 3, .size = 4, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -209,7 +209,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_EXPOSURE_TIME_RELATIVE_CONTROL, + .selector = UVC_CT_EXPOSURE_TIME_RELATIVE_CONTROL, .index = 4, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -217,7 +217,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_FOCUS_ABSOLUTE_CONTROL, + .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL, .index = 5, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -225,7 +225,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_FOCUS_RELATIVE_CONTROL, + .selector = UVC_CT_FOCUS_RELATIVE_CONTROL, .index = 6, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -233,7 +233,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_IRIS_ABSOLUTE_CONTROL, + .selector = UVC_CT_IRIS_ABSOLUTE_CONTROL, .index = 7, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -241,7 +241,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_IRIS_RELATIVE_CONTROL, + .selector = UVC_CT_IRIS_RELATIVE_CONTROL, .index = 8, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -249,7 +249,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ZOOM_ABSOLUTE_CONTROL, + .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL, .index = 9, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -257,7 +257,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ZOOM_RELATIVE_CONTROL, + .selector = UVC_CT_ZOOM_RELATIVE_CONTROL, .index = 10, .size = 3, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -265,7 +265,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_PANTILT_ABSOLUTE_CONTROL, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, .index = 11, .size = 8, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -273,7 +273,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_PANTILT_RELATIVE_CONTROL, + .selector = UVC_CT_PANTILT_RELATIVE_CONTROL, .index = 12, .size = 4, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -281,7 +281,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ROLL_ABSOLUTE_CONTROL, + .selector = UVC_CT_ROLL_ABSOLUTE_CONTROL, .index = 13, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -289,7 +289,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ROLL_RELATIVE_CONTROL, + .selector = UVC_CT_ROLL_RELATIVE_CONTROL, .index = 14, .size = 2, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE @@ -297,7 +297,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_FOCUS_AUTO_CONTROL, + .selector = UVC_CT_FOCUS_AUTO_CONTROL, .index = 17, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -305,7 +305,7 @@ static struct uvc_control_info uvc_ctrls[] = { }, { .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_PRIVACY_CONTROL, + .selector = UVC_CT_PRIVACY_CONTROL, .index = 18, .size = 1, .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR @@ -332,13 +332,13 @@ static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, __s8 zoom = (__s8)data[0]; switch (query) { - case GET_CUR: + case UVC_GET_CUR: return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]); - case GET_MIN: - case GET_MAX: - case GET_RES: - case GET_DEF: + case UVC_GET_MIN: + case UVC_GET_MAX: + case UVC_GET_RES: + case UVC_GET_DEF: default: return data[2]; } @@ -356,7 +356,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_BRIGHTNESS, .name = "Brightness", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_BRIGHTNESS_CONTROL, + .selector = UVC_PU_BRIGHTNESS_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -366,7 +366,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_CONTRAST, .name = "Contrast", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_CONTRAST_CONTROL, + .selector = UVC_PU_CONTRAST_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -376,7 +376,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_HUE, .name = "Hue", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_HUE_CONTROL, + .selector = UVC_PU_HUE_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -386,7 +386,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_SATURATION, .name = "Saturation", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_SATURATION_CONTROL, + .selector = UVC_PU_SATURATION_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -396,7 +396,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_SHARPNESS, .name = "Sharpness", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_SHARPNESS_CONTROL, + .selector = UVC_PU_SHARPNESS_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -406,7 +406,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_GAMMA, .name = "Gamma", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_GAMMA_CONTROL, + .selector = UVC_PU_GAMMA_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -416,7 +416,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_BACKLIGHT_COMPENSATION, .name = "Backlight Compensation", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, + .selector = UVC_PU_BACKLIGHT_COMPENSATION_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -426,7 +426,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_GAIN, .name = "Gain", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_GAIN_CONTROL, + .selector = UVC_PU_GAIN_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -436,7 +436,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_POWER_LINE_FREQUENCY, .name = "Power Line Frequency", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_POWER_LINE_FREQUENCY_CONTROL, + .selector = UVC_PU_POWER_LINE_FREQUENCY_CONTROL, .size = 2, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, @@ -448,7 +448,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_HUE_AUTO, .name = "Hue, Auto", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_HUE_AUTO_CONTROL, + .selector = UVC_PU_HUE_AUTO_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -458,7 +458,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_EXPOSURE_AUTO, .name = "Exposure, Auto", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_AE_MODE_CONTROL, + .selector = UVC_CT_AE_MODE_CONTROL, .size = 4, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_MENU, @@ -470,7 +470,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY, .name = "Exposure, Auto Priority", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_AE_PRIORITY_CONTROL, + .selector = UVC_CT_AE_PRIORITY_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -480,7 +480,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_EXPOSURE_ABSOLUTE, .name = "Exposure (Absolute)", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, + .selector = UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, .size = 32, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -490,7 +490,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_AUTO_WHITE_BALANCE, .name = "White Balance Temperature, Auto", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -500,7 +500,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, .name = "White Balance Temperature", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_TEMPERATURE_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -510,7 +510,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_AUTO_WHITE_BALANCE, .name = "White Balance Component, Auto", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -520,7 +520,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_BLUE_BALANCE, .name = "White Balance Blue Component", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -530,7 +530,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_RED_BALANCE, .name = "White Balance Red Component", .entity = UVC_GUID_UVC_PROCESSING, - .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, + .selector = UVC_PU_WHITE_BALANCE_COMPONENT_CONTROL, .size = 16, .offset = 16, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -540,7 +540,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_FOCUS_ABSOLUTE, .name = "Focus (absolute)", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_FOCUS_ABSOLUTE_CONTROL, + .selector = UVC_CT_FOCUS_ABSOLUTE_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -550,7 +550,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_FOCUS_AUTO, .name = "Focus, Auto", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_FOCUS_AUTO_CONTROL, + .selector = UVC_CT_FOCUS_AUTO_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -560,7 +560,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_ZOOM_ABSOLUTE, .name = "Zoom, Absolute", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ZOOM_ABSOLUTE_CONTROL, + .selector = UVC_CT_ZOOM_ABSOLUTE_CONTROL, .size = 16, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -570,7 +570,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_ZOOM_CONTINUOUS, .name = "Zoom, Continuous", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_ZOOM_RELATIVE_CONTROL, + .selector = UVC_CT_ZOOM_RELATIVE_CONTROL, .size = 0, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, @@ -582,7 +582,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .id = V4L2_CID_PRIVACY, .name = "Privacy", .entity = UVC_GUID_UVC_CAMERA, - .selector = CT_PRIVACY_CONTROL, + .selector = UVC_CT_PRIVACY_CONTROL, .size = 1, .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, @@ -675,16 +675,16 @@ static const __u8 uvc_media_transport_input_guid[16] = static int uvc_entity_match_guid(struct uvc_entity *entity, __u8 guid[16]) { switch (UVC_ENTITY_TYPE(entity)) { - case ITT_CAMERA: + case UVC_ITT_CAMERA: return memcmp(uvc_camera_guid, guid, 16) == 0; - case ITT_MEDIA_TRANSPORT_INPUT: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: return memcmp(uvc_media_transport_input_guid, guid, 16) == 0; - case VC_PROCESSING_UNIT: + case UVC_VC_PROCESSING_UNIT: return memcmp(uvc_processing_guid, guid, 16) == 0; - case VC_EXTENSION_UNIT: + case UVC_VC_EXTENSION_UNIT: return memcmp(entity->extension.guidExtensionCode, guid, 16) == 0; @@ -729,7 +729,7 @@ static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id, } } -struct uvc_control *uvc_find_control(struct uvc_video_device *video, +struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, __u32 v4l2_id, struct uvc_control_mapping **mapping) { struct uvc_control *ctrl = NULL; @@ -742,17 +742,17 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video, v4l2_id &= V4L2_CTRL_ID_MASK; /* Find the control. */ - __uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next); + __uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; - list_for_each_entry(entity, &video->iterms, chain) { + list_for_each_entry(entity, &chain->iterms, chain) { __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; } - list_for_each_entry(entity, &video->extensions, chain) { + list_for_each_entry(entity, &chain->extensions, chain) { __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; @@ -765,7 +765,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_device *video, return ctrl; } -int uvc_query_v4l2_ctrl(struct uvc_video_device *video, +int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct v4l2_queryctrl *v4l2_ctrl) { struct uvc_control *ctrl; @@ -775,7 +775,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, __u8 *data; int ret; - ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping); + ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); if (ctrl == NULL) return -EINVAL; @@ -793,11 +793,13 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { - if ((ret = uvc_query_ctrl(video->dev, GET_DEF, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size)) < 0) + ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); + if (ret < 0) goto out; - v4l2_ctrl->default_value = mapping->get(mapping, GET_DEF, data); + v4l2_ctrl->default_value = + mapping->get(mapping, UVC_GET_DEF, data); } switch (mapping->v4l2_type) { @@ -829,25 +831,28 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, } if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { - if ((ret = uvc_query_ctrl(video->dev, GET_MIN, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size)) < 0) + ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); + if (ret < 0) goto out; - v4l2_ctrl->minimum = mapping->get(mapping, GET_MIN, data); + v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, data); } if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { - if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size)) < 0) + ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); + if (ret < 0) goto out; - v4l2_ctrl->maximum = mapping->get(mapping, GET_MAX, data); + v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, data); } if (ctrl->info->flags & UVC_CONTROL_GET_RES) { - if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size)) < 0) + ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + data, ctrl->info->size); + if (ret < 0) goto out; - v4l2_ctrl->step = mapping->get(mapping, GET_RES, data); + v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, data); } ret = 0; @@ -881,9 +886,9 @@ out: * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the * control lock. */ -int uvc_ctrl_begin(struct uvc_video_device *video) +int uvc_ctrl_begin(struct uvc_video_chain *chain) { - return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0; + return mutex_lock_interruptible(&chain->ctrl_mutex) ? -ERESTARTSYS : 0; } static int uvc_ctrl_commit_entity(struct uvc_device *dev, @@ -912,7 +917,7 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, continue; if (!rollback) - ret = uvc_query_ctrl(dev, SET_CUR, ctrl->entity->id, + ret = uvc_query_ctrl(dev, UVC_SET_CUR, ctrl->entity->id, dev->intfnum, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); @@ -933,34 +938,34 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, return 0; } -int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback) +int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) { struct uvc_entity *entity; int ret = 0; /* Find the control. */ - ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback); + ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback); if (ret < 0) goto done; - list_for_each_entry(entity, &video->iterms, chain) { - ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + list_for_each_entry(entity, &chain->iterms, chain) { + ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); if (ret < 0) goto done; } - list_for_each_entry(entity, &video->extensions, chain) { - ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); + list_for_each_entry(entity, &chain->extensions, chain) { + ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); if (ret < 0) goto done; } done: - mutex_unlock(&video->ctrl_mutex); + mutex_unlock(&chain->ctrl_mutex); return ret; } -int uvc_ctrl_get(struct uvc_video_device *video, +int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl) { struct uvc_control *ctrl; @@ -969,13 +974,13 @@ int uvc_ctrl_get(struct uvc_video_device *video, unsigned int i; int ret; - ctrl = uvc_find_control(video, xctrl->id, &mapping); + ctrl = uvc_find_control(chain, xctrl->id, &mapping); if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) return -EINVAL; if (!ctrl->loaded) { - ret = uvc_query_ctrl(video->dev, GET_CUR, ctrl->entity->id, - video->dev->intfnum, ctrl->info->selector, + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); if (ret < 0) @@ -984,7 +989,7 @@ int uvc_ctrl_get(struct uvc_video_device *video, ctrl->loaded = 1; } - xctrl->value = mapping->get(mapping, GET_CUR, + xctrl->value = mapping->get(mapping, UVC_GET_CUR, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { @@ -1000,7 +1005,7 @@ int uvc_ctrl_get(struct uvc_video_device *video, return 0; } -int uvc_ctrl_set(struct uvc_video_device *video, +int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl) { struct uvc_control *ctrl; @@ -1008,7 +1013,7 @@ int uvc_ctrl_set(struct uvc_video_device *video, s32 value = xctrl->value; int ret; - ctrl = uvc_find_control(video, xctrl->id, &mapping); + ctrl = uvc_find_control(chain, xctrl->id, &mapping); if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0) return -EINVAL; @@ -1023,8 +1028,8 @@ int uvc_ctrl_set(struct uvc_video_device *video, memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), 0, ctrl->info->size); } else { - ret = uvc_query_ctrl(video->dev, GET_CUR, - ctrl->entity->id, video->dev->intfnum, + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, + ctrl->entity->id, chain->dev->intfnum, ctrl->info->selector, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), ctrl->info->size); @@ -1053,7 +1058,7 @@ int uvc_ctrl_set(struct uvc_video_device *video, * Dynamic controls */ -int uvc_xu_ctrl_query(struct uvc_video_device *video, +int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control *xctrl, int set) { struct uvc_entity *entity; @@ -1063,7 +1068,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, int ret; /* Find the extension unit. */ - list_for_each_entry(entity, &video->extensions, chain) { + list_for_each_entry(entity, &chain->extensions, chain) { if (entity->id == xctrl->unit) break; } @@ -1102,7 +1107,7 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR))) return -EINVAL; - if (mutex_lock_interruptible(&video->ctrl_mutex)) + if (mutex_lock_interruptible(&chain->ctrl_mutex)) return -ERESTARTSYS; memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), @@ -1115,9 +1120,9 @@ int uvc_xu_ctrl_query(struct uvc_video_device *video, goto out; } - ret = uvc_query_ctrl(video->dev, set ? SET_CUR : GET_CUR, xctrl->unit, - video->dev->intfnum, xctrl->selector, data, - xctrl->size); + ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR, + xctrl->unit, chain->dev->intfnum, xctrl->selector, + data, xctrl->size); if (ret < 0) goto out; @@ -1132,7 +1137,7 @@ out: uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), xctrl->size); - mutex_unlock(&video->ctrl_mutex); + mutex_unlock(&chain->ctrl_mutex); return ret; } @@ -1211,7 +1216,7 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, if (!found) return; - if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { + if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { /* Check if the device control information and length match * the user supplied information. */ @@ -1219,8 +1224,9 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, __le16 size; __u8 inf; - if ((ret = uvc_query_ctrl(dev, GET_LEN, ctrl->entity->id, - dev->intfnum, info->selector, (__u8 *)&size, 2)) < 0) { + ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, + dev->intfnum, info->selector, (__u8 *)&size, 2); + if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on " "control " UVC_GUID_FORMAT "/%u (%d).\n", UVC_GUID_ARGS(info->entity), info->selector, @@ -1236,8 +1242,9 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, return; } - if ((ret = uvc_query_ctrl(dev, GET_INFO, ctrl->entity->id, - dev->intfnum, info->selector, &inf, 1)) < 0) { + ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, + dev->intfnum, info->selector, &inf, 1); + if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on " "control " UVC_GUID_FORMAT "/%u (%d).\n", UVC_GUID_ARGS(info->entity), info->selector, @@ -1391,7 +1398,7 @@ uvc_ctrl_prune_entity(struct uvc_device *dev, struct uvc_entity *entity) unsigned int size; unsigned int i; - if (UVC_ENTITY_TYPE(entity) != VC_PROCESSING_UNIT) + if (UVC_ENTITY_TYPE(entity) != UVC_VC_PROCESSING_UNIT) return; controls = entity->processing.bmControls; @@ -1427,13 +1434,13 @@ int uvc_ctrl_init_device(struct uvc_device *dev) unsigned int bControlSize = 0, ncontrols = 0; __u8 *bmControls = NULL; - if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { + if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { bmControls = entity->extension.bmControls; bControlSize = entity->extension.bControlSize; - } else if (UVC_ENTITY_TYPE(entity) == VC_PROCESSING_UNIT) { + } else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) { bmControls = entity->processing.bmControls; bControlSize = entity->processing.bControlSize; - } else if (UVC_ENTITY_TYPE(entity) == ITT_CAMERA) { + } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) { bmControls = entity->camera.bmControls; bControlSize = entity->camera.bControlSize; } diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 04b47832fa0a..8756be569154 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -249,23 +249,23 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, list_for_each_entry_continue(entity, &dev->entities, list) { switch (UVC_ENTITY_TYPE(entity)) { - case TT_STREAMING: + case UVC_TT_STREAMING: if (entity->output.bSourceID == id) return entity; break; - case VC_PROCESSING_UNIT: + case UVC_VC_PROCESSING_UNIT: if (entity->processing.bSourceID == id) return entity; break; - case VC_SELECTOR_UNIT: + case UVC_VC_SELECTOR_UNIT: for (i = 0; i < entity->selector.bNrInPins; ++i) if (entity->selector.baSourceID[i] == id) return entity; break; - case VC_EXTENSION_UNIT: + case UVC_VC_EXTENSION_UNIT: for (i = 0; i < entity->extension.bNrInPins; ++i) if (entity->extension.baSourceID[i] == id) return entity; @@ -276,8 +276,20 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, return NULL; } +static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id) +{ + struct uvc_streaming *stream; + + list_for_each_entry(stream, &dev->streams, list) { + if (stream->header.bTerminalLink == id) + return stream; + } + + return NULL; +} + /* ------------------------------------------------------------------------ - * Descriptors handling + * Descriptors parsing */ static int uvc_parse_format(struct uvc_device *dev, @@ -297,9 +309,9 @@ static int uvc_parse_format(struct uvc_device *dev, format->index = buffer[3]; switch (buffer[2]) { - case VS_FORMAT_UNCOMPRESSED: - case VS_FORMAT_FRAME_BASED: - n = buffer[2] == VS_FORMAT_UNCOMPRESSED ? 27 : 28; + case UVC_VS_FORMAT_UNCOMPRESSED: + case UVC_VS_FORMAT_FRAME_BASED: + n = buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED ? 27 : 28; if (buflen < n) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT error\n", @@ -325,16 +337,16 @@ static int uvc_parse_format(struct uvc_device *dev, } format->bpp = buffer[21]; - if (buffer[2] == VS_FORMAT_UNCOMPRESSED) { - ftype = VS_FRAME_UNCOMPRESSED; + if (buffer[2] == UVC_VS_FORMAT_UNCOMPRESSED) { + ftype = UVC_VS_FRAME_UNCOMPRESSED; } else { - ftype = VS_FRAME_FRAME_BASED; + ftype = UVC_VS_FRAME_FRAME_BASED; if (buffer[27]) format->flags = UVC_FMT_FLAG_COMPRESSED; } break; - case VS_FORMAT_MJPEG: + case UVC_VS_FORMAT_MJPEG: if (buflen < 11) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT error\n", @@ -347,10 +359,10 @@ static int uvc_parse_format(struct uvc_device *dev, format->fcc = V4L2_PIX_FMT_MJPEG; format->flags = UVC_FMT_FLAG_COMPRESSED; format->bpp = 0; - ftype = VS_FRAME_MJPEG; + ftype = UVC_VS_FRAME_MJPEG; break; - case VS_FORMAT_DV: + case UVC_VS_FORMAT_DV: if (buflen < 9) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT error\n", @@ -395,8 +407,8 @@ static int uvc_parse_format(struct uvc_device *dev, format->nframes = 1; break; - case VS_FORMAT_MPEG2TS: - case VS_FORMAT_STREAM_BASED: + case UVC_VS_FORMAT_MPEG2TS: + case UVC_VS_FORMAT_STREAM_BASED: /* Not supported yet. */ default: uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " @@ -416,7 +428,7 @@ static int uvc_parse_format(struct uvc_device *dev, */ while (buflen > 2 && buffer[2] == ftype) { frame = &format->frame[format->nframes]; - if (ftype != VS_FRAME_FRAME_BASED) + if (ftype != UVC_VS_FRAME_FRAME_BASED) n = buflen > 25 ? buffer[25] : 0; else n = buflen > 21 ? buffer[21] : 0; @@ -436,7 +448,7 @@ static int uvc_parse_format(struct uvc_device *dev, frame->wHeight = get_unaligned_le16(&buffer[7]); frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); - if (ftype != VS_FRAME_FRAME_BASED) { + if (ftype != UVC_VS_FRAME_FRAME_BASED) { frame->dwMaxVideoFrameBufferSize = get_unaligned_le32(&buffer[17]); frame->dwDefaultFrameInterval = @@ -491,12 +503,12 @@ static int uvc_parse_format(struct uvc_device *dev, buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == VS_STILL_IMAGE_FRAME) { + if (buflen > 2 && buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { buflen -= buffer[0]; buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == VS_COLORFORMAT) { + if (buflen > 2 && buffer[2] == UVC_VS_COLORFORMAT) { if (buflen < 6) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d COLORFORMAT error\n", @@ -530,7 +542,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, int ret = -EINVAL; if (intf->cur_altsetting->desc.bInterfaceSubClass - != SC_VIDEOSTREAMING) { + != UVC_SC_VIDEOSTREAMING) { uvc_trace(UVC_TRACE_DESCR, "device %d interface %d isn't a " "video streaming interface\n", dev->udev->devnum, intf->altsetting[0].desc.bInterfaceNumber); @@ -551,6 +563,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, } mutex_init(&streaming->mutex); + streaming->dev = dev; streaming->intf = usb_get_intf(intf); streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; @@ -589,12 +602,12 @@ static int uvc_parse_streaming(struct uvc_device *dev, /* Parse the header descriptor. */ switch (buffer[2]) { - case VS_OUTPUT_HEADER: + case UVC_VS_OUTPUT_HEADER: streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; size = 9; break; - case VS_INPUT_HEADER: + case UVC_VS_INPUT_HEADER: streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; size = 13; break; @@ -618,7 +631,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, streaming->header.bNumFormats = p; streaming->header.bEndpointAddress = buffer[6]; - if (buffer[2] == VS_INPUT_HEADER) { + if (buffer[2] == UVC_VS_INPUT_HEADER) { streaming->header.bmInfo = buffer[7]; streaming->header.bTerminalLink = buffer[8]; streaming->header.bStillCaptureMethod = buffer[9]; @@ -644,15 +657,15 @@ static int uvc_parse_streaming(struct uvc_device *dev, _buflen = buflen; /* Count the format and frame descriptors. */ - while (_buflen > 2 && _buffer[1] == CS_INTERFACE) { + while (_buflen > 2 && _buffer[1] == USB_DT_CS_INTERFACE) { switch (_buffer[2]) { - case VS_FORMAT_UNCOMPRESSED: - case VS_FORMAT_MJPEG: - case VS_FORMAT_FRAME_BASED: + case UVC_VS_FORMAT_UNCOMPRESSED: + case UVC_VS_FORMAT_MJPEG: + case UVC_VS_FORMAT_FRAME_BASED: nformats++; break; - case VS_FORMAT_DV: + case UVC_VS_FORMAT_DV: /* DV format has no frame descriptor. We will create a * dummy frame descriptor with a dummy frame interval. */ @@ -661,22 +674,22 @@ static int uvc_parse_streaming(struct uvc_device *dev, nintervals++; break; - case VS_FORMAT_MPEG2TS: - case VS_FORMAT_STREAM_BASED: + case UVC_VS_FORMAT_MPEG2TS: + case UVC_VS_FORMAT_STREAM_BASED: uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT %u is not supported.\n", dev->udev->devnum, alts->desc.bInterfaceNumber, _buffer[2]); break; - case VS_FRAME_UNCOMPRESSED: - case VS_FRAME_MJPEG: + case UVC_VS_FRAME_UNCOMPRESSED: + case UVC_VS_FRAME_MJPEG: nframes++; if (_buflen > 25) nintervals += _buffer[25] ? _buffer[25] : 3; break; - case VS_FRAME_FRAME_BASED: + case UVC_VS_FRAME_FRAME_BASED: nframes++; if (_buflen > 21) nintervals += _buffer[21] ? _buffer[21] : 3; @@ -709,12 +722,12 @@ static int uvc_parse_streaming(struct uvc_device *dev, streaming->nformats = nformats; /* Parse the format descriptors. */ - while (buflen > 2 && buffer[1] == CS_INTERFACE) { + while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE) { switch (buffer[2]) { - case VS_FORMAT_UNCOMPRESSED: - case VS_FORMAT_MJPEG: - case VS_FORMAT_DV: - case VS_FORMAT_FRAME_BASED: + case UVC_VS_FORMAT_UNCOMPRESSED: + case UVC_VS_FORMAT_MJPEG: + case UVC_VS_FORMAT_DV: + case UVC_VS_FORMAT_FRAME_BASED: format->frame = frame; ret = uvc_parse_format(dev, streaming, format, &interval, buffer, buflen); @@ -751,7 +764,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, streaming->maxpsize = psize; } - list_add_tail(&streaming->list, &dev->streaming); + list_add_tail(&streaming->list, &dev->streams); return 0; error: @@ -819,7 +832,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, return -ENOMEM; unit->id = buffer[3]; - unit->type = VC_EXTENSION_UNIT; + unit->type = UVC_VC_EXTENSION_UNIT; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); @@ -856,7 +869,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, __u16 type; switch (buffer[2]) { - case VC_HEADER: + case UVC_VC_HEADER: n = buflen >= 12 ? buffer[11] : 0; if (buflen < 12 || buflen < 12 + n) { @@ -883,7 +896,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, } break; - case VC_INPUT_TERMINAL: + case UVC_VC_INPUT_TERMINAL: if (buflen < 8) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d INPUT_TERMINAL error\n", @@ -908,11 +921,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, p = 0; len = 8; - if (type == ITT_CAMERA) { + if (type == UVC_ITT_CAMERA) { n = buflen >= 15 ? buffer[14] : 0; len = 15; - } else if (type == ITT_MEDIA_TRANSPORT_INPUT) { + } else if (type == UVC_ITT_MEDIA_TRANSPORT_INPUT) { n = buflen >= 9 ? buffer[8] : 0; p = buflen >= 10 + n ? buffer[9+n] : 0; len = 10; @@ -932,7 +945,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, term->id = buffer[3]; term->type = type | UVC_TERM_INPUT; - if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) { + if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { term->camera.bControlSize = n; term->camera.bmControls = (__u8 *)term + sizeof *term; term->camera.wObjectiveFocalLengthMin = @@ -942,7 +955,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev, term->camera.wOcularFocalLength = get_unaligned_le16(&buffer[12]); memcpy(term->camera.bmControls, &buffer[15], n); - } else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) { + } else if (UVC_ENTITY_TYPE(term) == + UVC_ITT_MEDIA_TRANSPORT_INPUT) { term->media.bControlSize = n; term->media.bmControls = (__u8 *)term + sizeof *term; term->media.bTransportModeSize = p; @@ -955,9 +969,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, if (buffer[7] != 0) usb_string(udev, buffer[7], term->name, sizeof term->name); - else if (UVC_ENTITY_TYPE(term) == ITT_CAMERA) + else if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) sprintf(term->name, "Camera %u", buffer[3]); - else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) + else if (UVC_ENTITY_TYPE(term) == UVC_ITT_MEDIA_TRANSPORT_INPUT) sprintf(term->name, "Media %u", buffer[3]); else sprintf(term->name, "Input %u", buffer[3]); @@ -965,7 +979,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, list_add_tail(&term->list, &dev->entities); break; - case VC_OUTPUT_TERMINAL: + case UVC_VC_OUTPUT_TERMINAL: if (buflen < 9) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d OUTPUT_TERMINAL error\n", @@ -1002,7 +1016,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, list_add_tail(&term->list, &dev->entities); break; - case VC_SELECTOR_UNIT: + case UVC_VC_SELECTOR_UNIT: p = buflen >= 5 ? buffer[4] : 0; if (buflen < 5 || buflen < 6 + p) { @@ -1031,7 +1045,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, list_add_tail(&unit->list, &dev->entities); break; - case VC_PROCESSING_UNIT: + case UVC_VC_PROCESSING_UNIT: n = buflen >= 8 ? buffer[7] : 0; p = dev->uvc_version >= 0x0110 ? 10 : 9; @@ -1066,7 +1080,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, list_add_tail(&unit->list, &dev->entities); break; - case VC_EXTENSION_UNIT: + case UVC_VC_EXTENSION_UNIT: p = buflen >= 22 ? buffer[21] : 0; n = buflen >= 24 + p ? buffer[22+p] : 0; @@ -1158,43 +1172,40 @@ next_descriptor: } /* ------------------------------------------------------------------------ - * USB probe and disconnect + * UVC device scan */ /* - * Unregister the video devices. - */ -static void uvc_unregister_video(struct uvc_device *dev) -{ - if (dev->video.vdev) { - if (dev->video.vdev->minor == -1) - video_device_release(dev->video.vdev); - else - video_unregister_device(dev->video.vdev); - dev->video.vdev = NULL; - } -} - -/* * Scan the UVC descriptors to locate a chain starting at an Output Terminal * and containing the following units: * - * - one Output Terminal (USB Streaming or Display) + * - one or more Output Terminals (USB Streaming or Display) * - zero or one Processing Unit - * - zero, one or mode single-input Selector Units + * - zero, one or more single-input Selector Units * - zero or one multiple-input Selector Units, provided all inputs are * connected to input terminals * - zero, one or mode single-input Extension Units * - one or more Input Terminals (Camera, External or USB Streaming) * - * A side forward scan is made on each detected entity to check for additional - * extension units. + * The terminal and units must match on of the following structures: + * + * ITT_*(0) -> +---------+ +---------+ +---------+ -> TT_STREAMING(0) + * ... | SU{0,1} | -> | PU{0,1} | -> | XU{0,n} | ... + * ITT_*(n) -> +---------+ +---------+ +---------+ -> TT_STREAMING(n) + * + * +---------+ +---------+ -> OTT_*(0) + * TT_STREAMING -> | PU{0,1} | -> | XU{0,n} | ... + * +---------+ +---------+ -> OTT_*(n) + * + * The Processing Unit and Extension Units can be in any order. Additional + * Extension Units connected to the main chain as single-unit branches are + * also supported. Single-input Selector Units are ignored. */ -static int uvc_scan_chain_entity(struct uvc_video_device *video, +static int uvc_scan_chain_entity(struct uvc_video_chain *chain, struct uvc_entity *entity) { switch (UVC_ENTITY_TYPE(entity)) { - case VC_EXTENSION_UNIT: + case UVC_VC_EXTENSION_UNIT: if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- XU %d", entity->id); @@ -1204,23 +1215,23 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, return -1; } - list_add_tail(&entity->chain, &video->extensions); + list_add_tail(&entity->chain, &chain->extensions); break; - case VC_PROCESSING_UNIT: + case UVC_VC_PROCESSING_UNIT: if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- PU %d", entity->id); - if (video->processing != NULL) { + if (chain->processing != NULL) { uvc_trace(UVC_TRACE_DESCR, "Found multiple " "Processing Units in chain.\n"); return -1; } - video->processing = entity; + chain->processing = entity; break; - case VC_SELECTOR_UNIT: + case UVC_VC_SELECTOR_UNIT: if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- SU %d", entity->id); @@ -1228,25 +1239,25 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, if (entity->selector.bNrInPins == 1) break; - if (video->selector != NULL) { + if (chain->selector != NULL) { uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector " "Units in chain.\n"); return -1; } - video->selector = entity; + chain->selector = entity; break; - case ITT_VENDOR_SPECIFIC: - case ITT_CAMERA: - case ITT_MEDIA_TRANSPORT_INPUT: + case UVC_ITT_VENDOR_SPECIFIC: + case UVC_ITT_CAMERA: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d\n", entity->id); - list_add_tail(&entity->chain, &video->iterms); + list_add_tail(&entity->chain, &chain->iterms); break; - case TT_STREAMING: + case UVC_TT_STREAMING: if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d\n", entity->id); @@ -1256,14 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, return -1; } - if (video->sterm != NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming " - "entities in chain.\n"); - return -1; - } - - list_add_tail(&entity->chain, &video->iterms); - video->sterm = entity; + list_add_tail(&entity->chain, &chain->iterms); break; default: @@ -1275,7 +1279,7 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, return 0; } -static int uvc_scan_chain_forward(struct uvc_video_device *video, +static int uvc_scan_chain_forward(struct uvc_video_chain *chain, struct uvc_entity *entity, struct uvc_entity *prev) { struct uvc_entity *forward; @@ -1286,28 +1290,51 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video, found = 0; while (1) { - forward = uvc_entity_by_reference(video->dev, entity->id, + forward = uvc_entity_by_reference(chain->dev, entity->id, forward); if (forward == NULL) break; - - if (UVC_ENTITY_TYPE(forward) != VC_EXTENSION_UNIT || - forward == prev) + if (forward == prev) continue; - if (forward->extension.bNrInPins != 1) { - uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has " - "more than 1 input pin.\n", entity->id); - return -1; - } + switch (UVC_ENTITY_TYPE(forward)) { + case UVC_VC_EXTENSION_UNIT: + if (forward->extension.bNrInPins != 1) { + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " + "has more than 1 input pin.\n", + entity->id); + return -EINVAL; + } + + list_add_tail(&forward->chain, &chain->extensions); + if (uvc_trace_param & UVC_TRACE_PROBE) { + if (!found) + printk(" (->"); - list_add_tail(&forward->chain, &video->extensions); - if (uvc_trace_param & UVC_TRACE_PROBE) { - if (!found) - printk(" (-> XU"); + printk(" XU %d", forward->id); + found = 1; + } + break; + + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_TT_STREAMING: + if (UVC_ENTITY_IS_ITERM(forward)) { + uvc_trace(UVC_TRACE_DESCR, "Unsupported input " + "terminal %u.\n", forward->id); + return -EINVAL; + } - printk(" %d", forward->id); - found = 1; + list_add_tail(&forward->chain, &chain->oterms); + if (uvc_trace_param & UVC_TRACE_PROBE) { + if (!found) + printk(" (->"); + + printk(" OT %d", forward->id); + found = 1; + } + break; } } if (found) @@ -1316,22 +1343,22 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video, return 0; } -static int uvc_scan_chain_backward(struct uvc_video_device *video, +static int uvc_scan_chain_backward(struct uvc_video_chain *chain, struct uvc_entity *entity) { struct uvc_entity *term; int id = -1, i; switch (UVC_ENTITY_TYPE(entity)) { - case VC_EXTENSION_UNIT: + case UVC_VC_EXTENSION_UNIT: id = entity->extension.baSourceID[0]; break; - case VC_PROCESSING_UNIT: + case UVC_VC_PROCESSING_UNIT: id = entity->processing.bSourceID; break; - case VC_SELECTOR_UNIT: + case UVC_VC_SELECTOR_UNIT: /* Single-input selector units are ignored. */ if (entity->selector.bNrInPins == 1) { id = entity->selector.baSourceID[0]; @@ -1341,10 +1368,10 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT"); - video->selector = entity; + chain->selector = entity; for (i = 0; i < entity->selector.bNrInPins; ++i) { id = entity->selector.baSourceID[i]; - term = uvc_entity_by_id(video->dev, id); + term = uvc_entity_by_id(chain->dev, id); if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " "input %d isn't connected to an " @@ -1355,8 +1382,8 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" %d", term->id); - list_add_tail(&term->chain, &video->iterms); - uvc_scan_chain_forward(video, term, entity); + list_add_tail(&term->chain, &chain->iterms); + uvc_scan_chain_forward(chain, term, entity); } if (uvc_trace_param & UVC_TRACE_PROBE) @@ -1369,125 +1396,170 @@ static int uvc_scan_chain_backward(struct uvc_video_device *video, return id; } -static int uvc_scan_chain(struct uvc_video_device *video) +static int uvc_scan_chain(struct uvc_video_chain *chain, + struct uvc_entity *oterm) { struct uvc_entity *entity, *prev; int id; - entity = video->oterm; + entity = oterm; + list_add_tail(&entity->chain, &chain->oterms); uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); - if (UVC_ENTITY_TYPE(entity) == TT_STREAMING) - video->sterm = entity; - id = entity->output.bSourceID; while (id != 0) { prev = entity; - entity = uvc_entity_by_id(video->dev, id); + entity = uvc_entity_by_id(chain->dev, id); if (entity == NULL) { uvc_trace(UVC_TRACE_DESCR, "Found reference to " "unknown entity %d.\n", id); - return -1; + return -EINVAL; + } + + if (entity->chain.next || entity->chain.prev) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "entity %d already in chain.\n", id); + return -EINVAL; } /* Process entity */ - if (uvc_scan_chain_entity(video, entity) < 0) - return -1; + if (uvc_scan_chain_entity(chain, entity) < 0) + return -EINVAL; /* Forward scan */ - if (uvc_scan_chain_forward(video, entity, prev) < 0) - return -1; + if (uvc_scan_chain_forward(chain, entity, prev) < 0) + return -EINVAL; /* Stop when a terminal is found. */ - if (!UVC_ENTITY_IS_UNIT(entity)) + if (UVC_ENTITY_IS_TERM(entity)) break; /* Backward scan */ - id = uvc_scan_chain_backward(video, entity); + id = uvc_scan_chain_backward(chain, entity); if (id < 0) return id; } - if (video->sterm == NULL) { - uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in " - "chain.\n"); - return -1; + return 0; +} + +static unsigned int uvc_print_terms(struct list_head *terms, char *buffer) +{ + struct uvc_entity *term; + unsigned int nterms = 0; + char *p = buffer; + + list_for_each_entry(term, terms, chain) { + p += sprintf(p, "%u", term->id); + if (term->chain.next != terms) { + p += sprintf(p, ","); + if (++nterms >= 4) { + p += sprintf(p, "..."); + break; + } + } } - return 0; + return p - buffer; +} + +static const char *uvc_print_chain(struct uvc_video_chain *chain) +{ + static char buffer[43]; + char *p = buffer; + + p += uvc_print_terms(&chain->iterms, p); + p += sprintf(p, " -> "); + uvc_print_terms(&chain->oterms, p); + + return buffer; } /* - * Register the video devices. - * - * The driver currently supports a single video device per control interface - * only. The terminal and units must match the following structure: + * Scan the device for video chains and register video devices. * - * ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING - * TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_* - * - * The Extension Units, if present, must have a single input pin. The - * Processing Unit and Extension Units can be in any order. Additional - * Extension Units connected to the main chain as single-unit branches are - * also supported. + * Chains are scanned starting at their output terminals and walked backwards. */ -static int uvc_register_video(struct uvc_device *dev) +static int uvc_scan_device(struct uvc_device *dev) { - struct video_device *vdev; + struct uvc_video_chain *chain; struct uvc_entity *term; - int found = 0, ret; - /* Check if the control interface matches the structure we expect. */ list_for_each_entry(term, &dev->entities, list) { - struct uvc_streaming *streaming; - - if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term)) + if (!UVC_ENTITY_IS_OTERM(term)) continue; - memset(&dev->video, 0, sizeof dev->video); - mutex_init(&dev->video.ctrl_mutex); - INIT_LIST_HEAD(&dev->video.iterms); - INIT_LIST_HEAD(&dev->video.extensions); - dev->video.oterm = term; - dev->video.dev = dev; - if (uvc_scan_chain(&dev->video) < 0) + /* If the terminal is already included in a chain, skip it. + * This can happen for chains that have multiple output + * terminals, where all output terminals beside the first one + * will be inserted in the chain in forward scans. + */ + if (term->chain.next || term->chain.prev) continue; - list_for_each_entry(streaming, &dev->streaming, list) { - if (streaming->header.bTerminalLink == - dev->video.sterm->id) { - dev->video.streaming = streaming; - found = 1; - break; - } + chain = kzalloc(sizeof(*chain), GFP_KERNEL); + if (chain == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&chain->iterms); + INIT_LIST_HEAD(&chain->oterms); + INIT_LIST_HEAD(&chain->extensions); + mutex_init(&chain->ctrl_mutex); + chain->dev = dev; + + if (uvc_scan_chain(chain, term) < 0) { + kfree(chain); + continue; } - if (found) - break; + uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n", + uvc_print_chain(chain)); + + list_add_tail(&chain->list, &dev->chains); } - if (!found) { + if (list_empty(&dev->chains)) { uvc_printk(KERN_INFO, "No valid video chain found.\n"); return -1; } - if (uvc_trace_param & UVC_TRACE_PROBE) { - uvc_printk(KERN_INFO, "Found a valid video chain ("); - list_for_each_entry(term, &dev->video.iterms, chain) { - printk("%d", term->id); - if (term->chain.next != &dev->video.iterms) - printk(","); - } - printk(" -> %d).\n", dev->video.oterm->id); + return 0; +} + +/* ------------------------------------------------------------------------ + * Video device registration and unregistration + */ + +/* + * Unregister the video devices. + */ +static void uvc_unregister_video(struct uvc_device *dev) +{ + struct uvc_streaming *stream; + + list_for_each_entry(stream, &dev->streams, list) { + if (stream->vdev == NULL) + continue; + + if (stream->vdev->minor == -1) + video_device_release(stream->vdev); + else + video_unregister_device(stream->vdev); + stream->vdev = NULL; } +} - /* Initialize the video buffers queue. */ - uvc_queue_init(&dev->video.queue, dev->video.streaming->type); +static int uvc_register_video(struct uvc_device *dev, + struct uvc_streaming *stream) +{ + struct video_device *vdev; + int ret; /* Initialize the streaming interface with default streaming * parameters. */ - if ((ret = uvc_video_init(&dev->video)) < 0) { + ret = uvc_video_init(stream); + if (ret < 0) { uvc_printk(KERN_ERR, "Failed to initialize the device " "(%d).\n", ret); return ret; @@ -1495,8 +1567,11 @@ static int uvc_register_video(struct uvc_device *dev) /* Register the device with V4L. */ vdev = video_device_alloc(); - if (vdev == NULL) - return -1; + if (vdev == NULL) { + uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n", + ret); + return -ENOMEM; + } /* We already hold a reference to dev->udev. The video device will be * unregistered before the reference is released, so we don't need to @@ -1511,19 +1586,74 @@ static int uvc_register_video(struct uvc_device *dev) /* Set the driver data before calling video_register_device, otherwise * uvc_v4l2_open might race us. */ - dev->video.vdev = vdev; - video_set_drvdata(vdev, &dev->video); - - if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) { - dev->video.vdev = NULL; + stream->vdev = vdev; + video_set_drvdata(vdev, stream); + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + uvc_printk(KERN_ERR, "Failed to register video device (%d).\n", + ret); + stream->vdev = NULL; video_device_release(vdev); - return -1; + return ret; } return 0; } /* + * Register all video devices in all chains. + */ +static int uvc_register_terms(struct uvc_device *dev, + struct uvc_video_chain *chain, struct list_head *terms) +{ + struct uvc_streaming *stream; + struct uvc_entity *term; + int ret; + + list_for_each_entry(term, terms, chain) { + if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) + continue; + + stream = uvc_stream_by_id(dev, term->id); + if (stream == NULL) { + uvc_printk(KERN_INFO, "No streaming interface found " + "for terminal %u.", term->id); + continue; + } + + stream->chain = chain; + ret = uvc_register_video(dev, stream); + if (ret < 0) + return ret; + } + + return 0; +} + +static int uvc_register_chains(struct uvc_device *dev) +{ + struct uvc_video_chain *chain; + int ret; + + list_for_each_entry(chain, &dev->chains, list) { + ret = uvc_register_terms(dev, chain, &chain->iterms); + if (ret < 0) + return ret; + + ret = uvc_register_terms(dev, chain, &chain->oterms); + if (ret < 0) + return ret; + } + + return 0; +} + +/* ------------------------------------------------------------------------ + * USB probe, disconnect, suspend and resume + */ + +/* * Delete the UVC device. * * Called by the kernel when the last reference to the uvc_device structure @@ -1544,7 +1674,7 @@ void uvc_delete(struct kref *kref) struct uvc_device *dev = container_of(kref, struct uvc_device, kref); struct list_head *p, *n; - /* Unregister the video device. */ + /* Unregister the video devices. */ uvc_unregister_video(dev); usb_put_intf(dev->intf); usb_put_dev(dev->udev); @@ -1552,13 +1682,19 @@ void uvc_delete(struct kref *kref) uvc_status_cleanup(dev); uvc_ctrl_cleanup_device(dev); + list_for_each_safe(p, n, &dev->chains) { + struct uvc_video_chain *chain; + chain = list_entry(p, struct uvc_video_chain, list); + kfree(chain); + } + list_for_each_safe(p, n, &dev->entities) { struct uvc_entity *entity; entity = list_entry(p, struct uvc_entity, list); kfree(entity); } - list_for_each_safe(p, n, &dev->streaming) { + list_for_each_safe(p, n, &dev->streams) { struct uvc_streaming *streaming; streaming = list_entry(p, struct uvc_streaming, list); usb_driver_release_interface(&uvc_driver.driver, @@ -1592,7 +1728,8 @@ static int uvc_probe(struct usb_interface *intf, return -ENOMEM; INIT_LIST_HEAD(&dev->entities); - INIT_LIST_HEAD(&dev->streaming); + INIT_LIST_HEAD(&dev->chains); + INIT_LIST_HEAD(&dev->streams); kref_init(&dev->kref); atomic_set(&dev->users, 0); @@ -1633,8 +1770,12 @@ static int uvc_probe(struct usb_interface *intf, if (uvc_ctrl_init_device(dev) < 0) goto error; - /* Register the video devices. */ - if (uvc_register_video(dev) < 0) + /* Scan the device for video chains. */ + if (uvc_scan_device(dev) < 0) + goto error; + + /* Register video devices. */ + if (uvc_register_chains(dev) < 0) goto error; /* Save our data pointer in the interface data. */ @@ -1664,7 +1805,8 @@ static void uvc_disconnect(struct usb_interface *intf) */ usb_set_intfdata(intf, NULL); - if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOSTREAMING) + if (intf->cur_altsetting->desc.bInterfaceSubClass == + UVC_SC_VIDEOSTREAMING) return; /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide @@ -1687,31 +1829,36 @@ static void uvc_disconnect(struct usb_interface *intf) static int uvc_suspend(struct usb_interface *intf, pm_message_t message) { struct uvc_device *dev = usb_get_intfdata(intf); + struct uvc_streaming *stream; uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); /* Controls are cached on the fly so they don't need to be saved. */ - if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) + if (intf->cur_altsetting->desc.bInterfaceSubClass == + UVC_SC_VIDEOCONTROL) return uvc_status_suspend(dev); - if (dev->video.streaming->intf != intf) { - uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB " - "interface mismatch.\n"); - return -EINVAL; + list_for_each_entry(stream, &dev->streams, list) { + if (stream->intf == intf) + return uvc_video_suspend(stream); } - return uvc_video_suspend(&dev->video); + uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface " + "mismatch.\n"); + return -EINVAL; } static int __uvc_resume(struct usb_interface *intf, int reset) { struct uvc_device *dev = usb_get_intfdata(intf); + struct uvc_streaming *stream; uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); - if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) { + if (intf->cur_altsetting->desc.bInterfaceSubClass == + UVC_SC_VIDEOCONTROL) { if (reset) { int ret = uvc_ctrl_resume_device(dev); @@ -1722,13 +1869,14 @@ static int __uvc_resume(struct usb_interface *intf, int reset) return uvc_status_resume(dev); } - if (dev->video.streaming->intf != intf) { - uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB " - "interface mismatch.\n"); - return -EINVAL; + list_for_each_entry(stream, &dev->streams, list) { + if (stream->intf == intf) + return uvc_video_resume(stream); } - return uvc_video_resume(&dev->video); + uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface " + "mismatch.\n"); + return -EINVAL; } static int uvc_resume(struct usb_interface *intf) @@ -1880,7 +2028,8 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_PROBE_DEF }, /* Syntek (HP Spartan) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1943,7 +2092,8 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, + .driver_info = UVC_QUIRK_PROBE_MINMAX + | UVC_QUIRK_PROBE_EXTRAFIELDS }, /* Ecamm Pico iMage */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/video/uvc/uvc_isight.c b/drivers/media/video/uvc/uvc_isight.c index 436f462685a0..a9285b570dbe 100644 --- a/drivers/media/video/uvc/uvc_isight.c +++ b/drivers/media/video/uvc/uvc_isight.c @@ -99,7 +99,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf, return 0; } -void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, +void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, struct uvc_buffer *buf) { int ret, i; @@ -120,7 +120,7 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, * processes the data of the first payload of the new frame. */ do { - ret = isight_decode(&video->queue, buf, + ret = isight_decode(&stream->queue, buf, urb->transfer_buffer + urb->iso_frame_desc[i].offset, urb->iso_frame_desc[i].actual_length); @@ -130,7 +130,8 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } while (ret == -EAGAIN); } } diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 5e77cad29690..9e7351569b5d 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -40,7 +40,7 @@ * table for the controls that can be mapped directly, and handle the others * manually. */ -static int uvc_v4l2_query_menu(struct uvc_video_device *video, +static int uvc_v4l2_query_menu(struct uvc_video_chain *chain, struct v4l2_querymenu *query_menu) { struct uvc_menu_info *menu_info; @@ -49,7 +49,7 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video, u32 index = query_menu->index; u32 id = query_menu->id; - ctrl = uvc_find_control(video, query_menu->id, &mapping); + ctrl = uvc_find_control(chain, query_menu->id, &mapping); if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) return -EINVAL; @@ -103,7 +103,7 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval) return interval; } -static int uvc_v4l2_try_format(struct uvc_video_device *video, +static int uvc_v4l2_try_format(struct uvc_streaming *stream, struct v4l2_format *fmt, struct uvc_streaming_control *probe, struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) { @@ -116,7 +116,7 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video, int ret = 0; __u8 *fcc; - if (fmt->type != video->streaming->type) + if (fmt->type != stream->type) return -EINVAL; fcc = (__u8 *)&fmt->fmt.pix.pixelformat; @@ -126,8 +126,8 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video, fmt->fmt.pix.width, fmt->fmt.pix.height); /* Check if the hardware supports the requested format. */ - for (i = 0; i < video->streaming->nformats; ++i) { - format = &video->streaming->format[i]; + for (i = 0; i < stream->nformats; ++i) { + format = &stream->format[i]; if (format->fcc == fmt->fmt.pix.pixelformat) break; } @@ -191,12 +191,13 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video, * developers test their webcams with the Linux driver as well as with * the Windows driver). */ - if (video->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) + if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) probe->dwMaxVideoFrameSize = - video->streaming->ctrl.dwMaxVideoFrameSize; + stream->ctrl.dwMaxVideoFrameSize; /* Probe the device. */ - if ((ret = uvc_probe_video(video, probe)) < 0) + ret = uvc_probe_video(stream, probe); + if (ret < 0) goto done; fmt->fmt.pix.width = frame->wWidth; @@ -216,13 +217,13 @@ done: return ret; } -static int uvc_v4l2_get_format(struct uvc_video_device *video, +static int uvc_v4l2_get_format(struct uvc_streaming *stream, struct v4l2_format *fmt) { - struct uvc_format *format = video->streaming->cur_format; - struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_format *format = stream->cur_format; + struct uvc_frame *frame = stream->cur_frame; - if (fmt->type != video->streaming->type) + if (fmt->type != stream->type) return -EINVAL; if (format == NULL || frame == NULL) @@ -233,14 +234,14 @@ static int uvc_v4l2_get_format(struct uvc_video_device *video, fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8; - fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize; + fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; fmt->fmt.pix.priv = 0; return 0; } -static int uvc_v4l2_set_format(struct uvc_video_device *video, +static int uvc_v4l2_set_format(struct uvc_streaming *stream, struct v4l2_format *fmt) { struct uvc_streaming_control probe; @@ -248,39 +249,39 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video, struct uvc_frame *frame; int ret; - if (fmt->type != video->streaming->type) + if (fmt->type != stream->type) return -EINVAL; - if (uvc_queue_allocated(&video->queue)) + if (uvc_queue_allocated(&stream->queue)) return -EBUSY; - ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame); + ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame); if (ret < 0) return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); - video->streaming->cur_format = format; - video->streaming->cur_frame = frame; + memcpy(&stream->ctrl, &probe, sizeof probe); + stream->cur_format = format; + stream->cur_frame = frame; return 0; } -static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, +static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream, struct v4l2_streamparm *parm) { uint32_t numerator, denominator; - if (parm->type != video->streaming->type) + if (parm->type != stream->type) return -EINVAL; - numerator = video->streaming->ctrl.dwFrameInterval; + numerator = stream->ctrl.dwFrameInterval; denominator = 10000000; uvc_simplify_fraction(&numerator, &denominator, 8, 333); memset(parm, 0, sizeof *parm); - parm->type = video->streaming->type; + parm->type = stream->type; - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; parm->parm.capture.capturemode = 0; parm->parm.capture.timeperframe.numerator = numerator; @@ -297,19 +298,19 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, return 0; } -static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, +static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, struct v4l2_streamparm *parm) { - struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_frame *frame = stream->cur_frame; struct uvc_streaming_control probe; struct v4l2_fract timeperframe; uint32_t interval; int ret; - if (parm->type != video->streaming->type) + if (parm->type != stream->type) return -EINVAL; - if (uvc_queue_streaming(&video->queue)) + if (uvc_queue_streaming(&stream->queue)) return -EBUSY; if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -317,7 +318,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, else timeperframe = parm->parm.output.timeperframe; - memcpy(&probe, &video->streaming->ctrl, sizeof probe); + memcpy(&probe, &stream->ctrl, sizeof probe); interval = uvc_fraction_to_interval(timeperframe.numerator, timeperframe.denominator); @@ -326,10 +327,11 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, probe.dwFrameInterval = uvc_try_frame_interval(frame, interval); /* Probe the device with the new settings. */ - if ((ret = uvc_probe_video(video, &probe)) < 0) + ret = uvc_probe_video(stream, &probe); + if (ret < 0) return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); + memcpy(&stream->ctrl, &probe, sizeof probe); /* Return the actual frame period. */ timeperframe.numerator = probe.dwFrameInterval; @@ -382,8 +384,8 @@ static int uvc_acquire_privileges(struct uvc_fh *handle) /* Check if the device already has a privileged handle. */ mutex_lock(&uvc_driver.open_mutex); - if (atomic_inc_return(&handle->device->active) != 1) { - atomic_dec(&handle->device->active); + if (atomic_inc_return(&handle->stream->active) != 1) { + atomic_dec(&handle->stream->active); ret = -EBUSY; goto done; } @@ -398,7 +400,7 @@ done: static void uvc_dismiss_privileges(struct uvc_fh *handle) { if (handle->state == UVC_HANDLE_ACTIVE) - atomic_dec(&handle->device->active); + atomic_dec(&handle->stream->active); handle->state = UVC_HANDLE_PASSIVE; } @@ -414,45 +416,47 @@ static int uvc_has_privileges(struct uvc_fh *handle) static int uvc_v4l2_open(struct file *file) { - struct uvc_video_device *video; + struct uvc_streaming *stream; struct uvc_fh *handle; int ret = 0; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); mutex_lock(&uvc_driver.open_mutex); - video = video_drvdata(file); + stream = video_drvdata(file); - if (video->dev->state & UVC_DEV_DISCONNECTED) { + if (stream->dev->state & UVC_DEV_DISCONNECTED) { ret = -ENODEV; goto done; } - ret = usb_autopm_get_interface(video->dev->intf); + ret = usb_autopm_get_interface(stream->dev->intf); if (ret < 0) goto done; /* Create the device handle. */ handle = kzalloc(sizeof *handle, GFP_KERNEL); if (handle == NULL) { - usb_autopm_put_interface(video->dev->intf); + usb_autopm_put_interface(stream->dev->intf); ret = -ENOMEM; goto done; } - if (atomic_inc_return(&video->dev->users) == 1) { - if ((ret = uvc_status_start(video->dev)) < 0) { - usb_autopm_put_interface(video->dev->intf); - atomic_dec(&video->dev->users); + if (atomic_inc_return(&stream->dev->users) == 1) { + ret = uvc_status_start(stream->dev); + if (ret < 0) { + usb_autopm_put_interface(stream->dev->intf); + atomic_dec(&stream->dev->users); kfree(handle); goto done; } } - handle->device = video; + handle->chain = stream->chain; + handle->stream = stream; handle->state = UVC_HANDLE_PASSIVE; file->private_data = handle; - kref_get(&video->dev->kref); + kref_get(&stream->dev->kref); done: mutex_unlock(&uvc_driver.open_mutex); @@ -461,20 +465,20 @@ done: static int uvc_v4l2_release(struct file *file) { - struct uvc_video_device *video = video_drvdata(file); struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); /* Only free resources if this is a privileged handle. */ if (uvc_has_privileges(handle)) { - uvc_video_enable(video, 0); + uvc_video_enable(stream, 0); - mutex_lock(&video->queue.mutex); - if (uvc_free_buffers(&video->queue) < 0) + mutex_lock(&stream->queue.mutex); + if (uvc_free_buffers(&stream->queue) < 0) uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to " "free buffers.\n"); - mutex_unlock(&video->queue.mutex); + mutex_unlock(&stream->queue.mutex); } /* Release the file handle. */ @@ -482,19 +486,20 @@ static int uvc_v4l2_release(struct file *file) kfree(handle); file->private_data = NULL; - if (atomic_dec_return(&video->dev->users) == 0) - uvc_status_stop(video->dev); + if (atomic_dec_return(&stream->dev->users) == 0) + uvc_status_stop(stream->dev); - usb_autopm_put_interface(video->dev->intf); - kref_put(&video->dev->kref, uvc_delete); + usb_autopm_put_interface(stream->dev->intf); + kref_put(&stream->dev->kref, uvc_delete); return 0; } static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); - struct uvc_video_device *video = video_get_drvdata(vdev); struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_video_chain *chain = handle->chain; + struct uvc_streaming *stream = handle->stream; long ret = 0; switch (cmd) { @@ -506,10 +511,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(cap, 0, sizeof *cap); strlcpy(cap->driver, "uvcvideo", sizeof cap->driver); strlcpy(cap->card, vdev->name, sizeof cap->card); - usb_make_path(video->dev->udev, + usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info)); cap->version = DRIVER_VERSION_NUMBER; - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; else @@ -520,7 +525,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) /* Get, Set & Query control */ case VIDIOC_QUERYCTRL: - return uvc_query_v4l2_ctrl(video, arg); + return uvc_query_v4l2_ctrl(chain, arg); case VIDIOC_G_CTRL: { @@ -530,12 +535,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&xctrl, 0, sizeof xctrl); xctrl.id = ctrl->id; - ret = uvc_ctrl_begin(video); - if (ret < 0) + ret = uvc_ctrl_begin(chain); + if (ret < 0) return ret; - ret = uvc_ctrl_get(video, &xctrl); - uvc_ctrl_rollback(video); + ret = uvc_ctrl_get(chain, &xctrl); + uvc_ctrl_rollback(chain); if (ret >= 0) ctrl->value = xctrl.value; break; @@ -550,21 +555,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) xctrl.id = ctrl->id; xctrl.value = ctrl->value; - ret = uvc_ctrl_begin(video); - if (ret < 0) + uvc_ctrl_begin(chain); + if (ret < 0) return ret; - ret = uvc_ctrl_set(video, &xctrl); + ret = uvc_ctrl_set(chain, &xctrl); if (ret < 0) { - uvc_ctrl_rollback(video); + uvc_ctrl_rollback(chain); return ret; } - ret = uvc_ctrl_commit(video); + ret = uvc_ctrl_commit(chain); break; } case VIDIOC_QUERYMENU: - return uvc_v4l2_query_menu(video, arg); + return uvc_v4l2_query_menu(chain, arg); case VIDIOC_G_EXT_CTRLS: { @@ -572,20 +577,20 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_ext_control *ctrl = ctrls->controls; unsigned int i; - ret = uvc_ctrl_begin(video); - if (ret < 0) + ret = uvc_ctrl_begin(chain); + if (ret < 0) return ret; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_get(video, ctrl); + ret = uvc_ctrl_get(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(video); + uvc_ctrl_rollback(chain); ctrls->error_idx = i; return ret; } } ctrls->error_idx = 0; - ret = uvc_ctrl_rollback(video); + ret = uvc_ctrl_rollback(chain); break; } @@ -596,14 +601,14 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_ext_control *ctrl = ctrls->controls; unsigned int i; - ret = uvc_ctrl_begin(video); + ret = uvc_ctrl_begin(chain); if (ret < 0) return ret; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_set(video, ctrl); + ret = uvc_ctrl_set(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(video); + uvc_ctrl_rollback(chain); ctrls->error_idx = i; return ret; } @@ -612,31 +617,31 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ctrls->error_idx = 0; if (cmd == VIDIOC_S_EXT_CTRLS) - ret = uvc_ctrl_commit(video); + ret = uvc_ctrl_commit(chain); else - ret = uvc_ctrl_rollback(video); + ret = uvc_ctrl_rollback(chain); break; } /* Get, Set & Enum input */ case VIDIOC_ENUMINPUT: { - const struct uvc_entity *selector = video->selector; + const struct uvc_entity *selector = chain->selector; struct v4l2_input *input = arg; struct uvc_entity *iterm = NULL; u32 index = input->index; int pin = 0; if (selector == NULL || - (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { if (index != 0) return -EINVAL; - iterm = list_first_entry(&video->iterms, + iterm = list_first_entry(&chain->iterms, struct uvc_entity, chain); pin = iterm->id; } else if (pin < selector->selector.bNrInPins) { pin = selector->selector.baSourceID[index]; - list_for_each_entry(iterm, video->iterms.next, chain) { + list_for_each_entry(iterm, chain->iterms.next, chain) { if (iterm->id == pin) break; } @@ -648,7 +653,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(input, 0, sizeof *input); input->index = index; strlcpy(input->name, iterm->name, sizeof input->name); - if (UVC_ENTITY_TYPE(iterm) == ITT_CAMERA) + if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA) input->type = V4L2_INPUT_TYPE_CAMERA; break; } @@ -657,15 +662,15 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { u8 input; - if (video->selector == NULL || - (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (chain->selector == NULL || + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { *(int *)arg = 0; break; } - ret = uvc_query_ctrl(video->dev, GET_CUR, video->selector->id, - video->dev->intfnum, SU_INPUT_SELECT_CONTROL, - &input, 1); + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, + chain->selector->id, chain->dev->intfnum, + UVC_SU_INPUT_SELECT_CONTROL, &input, 1); if (ret < 0) return ret; @@ -680,19 +685,19 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - if (video->selector == NULL || - (video->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { + if (chain->selector == NULL || + (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { if (input != 1) return -EINVAL; break; } - if (input == 0 || input > video->selector->selector.bNrInPins) + if (input == 0 || input > chain->selector->selector.bNrInPins) return -EINVAL; - return uvc_query_ctrl(video->dev, SET_CUR, video->selector->id, - video->dev->intfnum, SU_INPUT_SELECT_CONTROL, - &input, 1); + return uvc_query_ctrl(chain->dev, UVC_SET_CUR, + chain->selector->id, chain->dev->intfnum, + UVC_SU_INPUT_SELECT_CONTROL, &input, 1); } /* Try, Get, Set & Enum format */ @@ -703,15 +708,15 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) enum v4l2_buf_type type = fmt->type; __u32 index = fmt->index; - if (fmt->type != video->streaming->type || - fmt->index >= video->streaming->nformats) + if (fmt->type != stream->type || + fmt->index >= stream->nformats) return -EINVAL; memset(fmt, 0, sizeof(*fmt)); fmt->index = index; fmt->type = type; - format = &video->streaming->format[fmt->index]; + format = &stream->format[fmt->index]; fmt->flags = 0; if (format->flags & UVC_FMT_FLAG_COMPRESSED) fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; @@ -729,17 +734,17 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - return uvc_v4l2_try_format(video, arg, &probe, NULL, NULL); + return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL); } case VIDIOC_S_FMT: if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - return uvc_v4l2_set_format(video, arg); + return uvc_v4l2_set_format(stream, arg); case VIDIOC_G_FMT: - return uvc_v4l2_get_format(video, arg); + return uvc_v4l2_get_format(stream, arg); /* Frame size enumeration */ case VIDIOC_ENUM_FRAMESIZES: @@ -750,10 +755,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) int i; /* Look for the given pixel format */ - for (i = 0; i < video->streaming->nformats; i++) { - if (video->streaming->format[i].fcc == + for (i = 0; i < stream->nformats; i++) { + if (stream->format[i].fcc == fsize->pixel_format) { - format = &video->streaming->format[i]; + format = &stream->format[i]; break; } } @@ -779,10 +784,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) int i; /* Look for the given pixel format and frame size */ - for (i = 0; i < video->streaming->nformats; i++) { - if (video->streaming->format[i].fcc == + for (i = 0; i < stream->nformats; i++) { + if (stream->format[i].fcc == fival->pixel_format) { - format = &video->streaming->format[i]; + format = &stream->format[i]; break; } } @@ -832,21 +837,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) /* Get & Set streaming parameters */ case VIDIOC_G_PARM: - return uvc_v4l2_get_streamparm(video, arg); + return uvc_v4l2_get_streamparm(stream, arg); case VIDIOC_S_PARM: if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - return uvc_v4l2_set_streamparm(video, arg); + return uvc_v4l2_set_streamparm(stream, arg); /* Cropping and scaling */ case VIDIOC_CROPCAP: { struct v4l2_cropcap *ccap = arg; - struct uvc_frame *frame = video->streaming->cur_frame; + struct uvc_frame *frame = stream->cur_frame; - if (ccap->type != video->streaming->type) + if (ccap->type != stream->type) return -EINVAL; ccap->bounds.left = 0; @@ -870,16 +875,16 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_requestbuffers *rb = arg; unsigned int bufsize = - video->streaming->ctrl.dwMaxVideoFrameSize; + stream->ctrl.dwMaxVideoFrameSize; - if (rb->type != video->streaming->type || + if (rb->type != stream->type || rb->memory != V4L2_MEMORY_MMAP) return -EINVAL; if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; - ret = uvc_alloc_buffers(&video->queue, rb->count, bufsize); + ret = uvc_alloc_buffers(&stream->queue, rb->count, bufsize); if (ret < 0) return ret; @@ -892,39 +897,40 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_buffer *buf = arg; - if (buf->type != video->streaming->type) + if (buf->type != stream->type) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_query_buffer(&video->queue, buf); + return uvc_query_buffer(&stream->queue, buf); } case VIDIOC_QBUF: if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_queue_buffer(&video->queue, arg); + return uvc_queue_buffer(&stream->queue, arg); case VIDIOC_DQBUF: if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_dequeue_buffer(&video->queue, arg, + return uvc_dequeue_buffer(&stream->queue, arg, file->f_flags & O_NONBLOCK); case VIDIOC_STREAMON: { int *type = arg; - if (*type != video->streaming->type) + if (*type != stream->type) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; - if ((ret = uvc_video_enable(video, 1)) < 0) + ret = uvc_video_enable(stream, 1); + if (ret < 0) return ret; break; } @@ -933,13 +939,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { int *type = arg; - if (*type != video->streaming->type) + if (*type != stream->type) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; - return uvc_video_enable(video, 0); + return uvc_video_enable(stream, 0); } /* Analog video standards make no sense for digital cameras. */ @@ -1013,10 +1019,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) } case UVCIOC_CTRL_GET: - return uvc_xu_ctrl_query(video, arg, 0); + return uvc_xu_ctrl_query(chain, arg, 0); case UVCIOC_CTRL_SET: - return uvc_xu_ctrl_query(video, arg, 1); + return uvc_xu_ctrl_query(chain, arg, 1); default: if ((ret = v4l_compat_translate_ioctl(file, cmd, arg, @@ -1070,7 +1076,9 @@ static struct vm_operations_struct uvc_vm_ops = { static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) { - struct uvc_video_device *video = video_drvdata(file); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_streaming *stream = handle->stream; + struct uvc_video_queue *queue = &stream->queue; struct uvc_buffer *uninitialized_var(buffer); struct page *page; unsigned long addr, start, size; @@ -1082,15 +1090,15 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) start = vma->vm_start; size = vma->vm_end - vma->vm_start; - mutex_lock(&video->queue.mutex); + mutex_lock(&queue->mutex); - for (i = 0; i < video->queue.count; ++i) { - buffer = &video->queue.buffer[i]; + for (i = 0; i < queue->count; ++i) { + buffer = &queue->buffer[i]; if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) break; } - if (i == video->queue.count || size != video->queue.buf_size) { + if (i == queue->count || size != queue->buf_size) { ret = -EINVAL; goto done; } @@ -1101,7 +1109,7 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) */ vma->vm_flags |= VM_IO; - addr = (unsigned long)video->queue.mem + buffer->buf.m.offset; + addr = (unsigned long)queue->mem + buffer->buf.m.offset; while (size > 0) { page = vmalloc_to_page((void *)addr); if ((ret = vm_insert_page(vma, start, page)) < 0) @@ -1117,17 +1125,18 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) uvc_vm_open(vma); done: - mutex_unlock(&video->queue.mutex); + mutex_unlock(&queue->mutex); return ret; } static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) { - struct uvc_video_device *video = video_drvdata(file); + struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); - return uvc_queue_poll(&video->queue, file, wait); + return uvc_queue_poll(&stream->queue, file, wait); } const struct v4l2_file_operations uvc_fops = { diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 01b633c73480..5b757f32d997 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -61,7 +61,7 @@ int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, return 0; } -static void uvc_fixup_video_ctrl(struct uvc_video_device *video, +static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl) { struct uvc_format *format; @@ -69,10 +69,10 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, unsigned int i; if (ctrl->bFormatIndex <= 0 || - ctrl->bFormatIndex > video->streaming->nformats) + ctrl->bFormatIndex > stream->nformats) return; - format = &video->streaming->format[ctrl->bFormatIndex - 1]; + format = &stream->format[ctrl->bFormatIndex - 1]; for (i = 0; i < format->nframes; ++i) { if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) { @@ -86,12 +86,12 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) || (ctrl->dwMaxVideoFrameSize == 0 && - video->dev->uvc_version < 0x0110)) + stream->dev->uvc_version < 0x0110)) ctrl->dwMaxVideoFrameSize = frame->dwMaxVideoFrameBufferSize; - if (video->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && - video->streaming->intf->num_altsetting > 1) { + if (stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && + stream->intf->num_altsetting > 1) { u32 interval; u32 bandwidth; @@ -108,7 +108,7 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp; bandwidth *= 10000000 / interval + 1; bandwidth /= 1000; - if (video->dev->udev->speed == USB_SPEED_HIGH) + if (stream->dev->udev->speed == USB_SPEED_HIGH) bandwidth /= 8; bandwidth += 12; @@ -116,40 +116,43 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, } } -static int uvc_get_video_ctrl(struct uvc_video_device *video, +static int uvc_get_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl, int probe, __u8 query) { __u8 *data; __u16 size; int ret; - size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; data = kmalloc(size, GFP_KERNEL); if (data == NULL) return -ENOMEM; - ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum, - probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, - UVC_CTRL_STREAMING_TIMEOUT); + if ((stream->dev->quirks & UVC_QUIRK_PROBE_DEF) && query == UVC_GET_DEF) + return -EIO; + + ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum, + probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, + size, UVC_CTRL_STREAMING_TIMEOUT); - if ((query == GET_MIN || query == GET_MAX) && ret == 2) { + if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) { /* Some cameras, mostly based on Bison Electronics chipsets, * answer a GET_MIN or GET_MAX request with the wCompQuality * field only. */ - uvc_warn_once(video->dev, UVC_WARN_MINMAX, "UVC non " + uvc_warn_once(stream->dev, UVC_WARN_MINMAX, "UVC non " "compliance - GET_MIN/MAX(PROBE) incorrectly " "supported. Enabling workaround.\n"); memset(ctrl, 0, sizeof ctrl); ctrl->wCompQuality = le16_to_cpup((__le16 *)data); ret = 0; goto out; - } else if (query == GET_DEF && probe == 1 && ret != size) { + } else if (query == UVC_GET_DEF && probe == 1 && ret != size) { /* Many cameras don't support the GET_DEF request on their * video probe control. Warn once and return, the caller will * fall back to GET_CUR. */ - uvc_warn_once(video->dev, UVC_WARN_PROBE_DEF, "UVC non " + uvc_warn_once(stream->dev, UVC_WARN_PROBE_DEF, "UVC non " "compliance - GET_DEF(PROBE) not supported. " "Enabling workaround.\n"); ret = -EIO; @@ -181,7 +184,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ctrl->bMinVersion = data[32]; ctrl->bMaxVersion = data[33]; } else { - ctrl->dwClockFrequency = video->dev->clock_frequency; + ctrl->dwClockFrequency = stream->dev->clock_frequency; ctrl->bmFramingInfo = 0; ctrl->bPreferedVersion = 0; ctrl->bMinVersion = 0; @@ -192,7 +195,7 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, * dwMaxPayloadTransferSize fields. Try to get the value from the * format and frame descriptors. */ - uvc_fixup_video_ctrl(video, ctrl); + uvc_fixup_video_ctrl(stream, ctrl); ret = 0; out: @@ -200,14 +203,14 @@ out: return ret; } -static int uvc_set_video_ctrl(struct uvc_video_device *video, +static int uvc_set_video_ctrl(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl, int probe) { __u8 *data; __u16 size; int ret; - size = video->dev->uvc_version >= 0x0110 ? 34 : 26; + size = stream->dev->uvc_version >= 0x0110 ? 34 : 26; data = kzalloc(size, GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -232,10 +235,9 @@ static int uvc_set_video_ctrl(struct uvc_video_device *video, data[33] = ctrl->bMaxVersion; } - ret = __uvc_query_ctrl(video->dev, SET_CUR, 0, - video->streaming->intfnum, - probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, - UVC_CTRL_STREAMING_TIMEOUT); + ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum, + probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, + size, UVC_CTRL_STREAMING_TIMEOUT); if (ret != size) { uvc_printk(KERN_ERR, "Failed to set UVC %s control : " "%d (exp. %u).\n", probe ? "probe" : "commit", @@ -247,7 +249,7 @@ static int uvc_set_video_ctrl(struct uvc_video_device *video, return ret; } -int uvc_probe_video(struct uvc_video_device *video, +int uvc_probe_video(struct uvc_streaming *stream, struct uvc_streaming_control *probe) { struct uvc_streaming_control probe_min, probe_max; @@ -255,7 +257,7 @@ int uvc_probe_video(struct uvc_video_device *video, unsigned int i; int ret; - mutex_lock(&video->streaming->mutex); + mutex_lock(&stream->mutex); /* Perform probing. The device should adjust the requested values * according to its capabilities. However, some devices, namely the @@ -264,15 +266,16 @@ int uvc_probe_video(struct uvc_video_device *video, * that reason, if the needed bandwidth exceeds the maximum available * bandwidth, try to lower the quality. */ - if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0) + ret = uvc_set_video_ctrl(stream, probe, 1); + if (ret < 0) goto done; /* Get the minimum and maximum values for compression settings. */ - if (!(video->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) { - ret = uvc_get_video_ctrl(video, &probe_min, 1, GET_MIN); + if (!(stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX)) { + ret = uvc_get_video_ctrl(stream, &probe_min, 1, UVC_GET_MIN); if (ret < 0) goto done; - ret = uvc_get_video_ctrl(video, &probe_max, 1, GET_MAX); + ret = uvc_get_video_ctrl(stream, &probe_max, 1, UVC_GET_MAX); if (ret < 0) goto done; @@ -280,18 +283,21 @@ int uvc_probe_video(struct uvc_video_device *video, } for (i = 0; i < 2; ++i) { - if ((ret = uvc_set_video_ctrl(video, probe, 1)) < 0 || - (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + ret = uvc_set_video_ctrl(stream, probe, 1); + if (ret < 0) + goto done; + ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); + if (ret < 0) goto done; - if (video->streaming->intf->num_altsetting == 1) + if (stream->intf->num_altsetting == 1) break; bandwidth = probe->dwMaxPayloadTransferSize; - if (bandwidth <= video->streaming->maxpsize) + if (bandwidth <= stream->maxpsize) break; - if (video->dev->quirks & UVC_QUIRK_PROBE_MINMAX) { + if (stream->dev->quirks & UVC_QUIRK_PROBE_MINMAX) { ret = -ENOSPC; goto done; } @@ -304,14 +310,14 @@ int uvc_probe_video(struct uvc_video_device *video, } done: - mutex_unlock(&video->streaming->mutex); + mutex_unlock(&stream->mutex); return ret; } -int uvc_commit_video(struct uvc_video_device *video, +int uvc_commit_video(struct uvc_streaming *stream, struct uvc_streaming_control *probe) { - return uvc_set_video_ctrl(video, probe, 0); + return uvc_set_video_ctrl(stream, probe, 0); } /* ------------------------------------------------------------------------ @@ -363,7 +369,7 @@ int uvc_commit_video(struct uvc_video_device *video, * to be called with a NULL buf parameter. uvc_video_decode_data and * uvc_video_decode_end will never be called with a NULL buffer. */ -static int uvc_video_decode_start(struct uvc_video_device *video, +static int uvc_video_decode_start(struct uvc_streaming *stream, struct uvc_buffer *buf, const __u8 *data, int len) { __u8 fid; @@ -389,25 +395,25 @@ static int uvc_video_decode_start(struct uvc_video_device *video, * NULL. */ if (buf == NULL) { - video->last_fid = fid; + stream->last_fid = fid; return -ENODATA; } /* Synchronize to the input stream by waiting for the FID bit to be * toggled when the the buffer state is not UVC_BUF_STATE_ACTIVE. - * video->last_fid is initialized to -1, so the first isochronous + * stream->last_fid is initialized to -1, so the first isochronous * frame will always be in sync. * - * If the device doesn't toggle the FID bit, invert video->last_fid + * If the device doesn't toggle the FID bit, invert stream->last_fid * when the EOF bit is set to force synchronisation on the next packet. */ if (buf->state != UVC_BUF_STATE_ACTIVE) { - if (fid == video->last_fid) { + if (fid == stream->last_fid) { uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of " "sync).\n"); - if ((video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && + if ((stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) && (data[1] & UVC_STREAM_EOF)) - video->last_fid ^= UVC_STREAM_FID; + stream->last_fid ^= UVC_STREAM_FID; return -ENODATA; } @@ -422,7 +428,7 @@ static int uvc_video_decode_start(struct uvc_video_device *video, * last payload can be lost anyway). We thus must check if the FID has * been toggled. * - * video->last_fid is initialized to -1, so the first isochronous + * stream->last_fid is initialized to -1, so the first isochronous * frame will never trigger an end of frame detection. * * Empty buffers (bytesused == 0) don't trigger end of frame detection @@ -430,22 +436,22 @@ static int uvc_video_decode_start(struct uvc_video_device *video, * avoids detecting end of frame conditions at FID toggling if the * previous payload had the EOF bit set. */ - if (fid != video->last_fid && buf->buf.bytesused != 0) { + if (fid != stream->last_fid && buf->buf.bytesused != 0) { uvc_trace(UVC_TRACE_FRAME, "Frame complete (FID bit " "toggled).\n"); buf->state = UVC_BUF_STATE_DONE; return -EAGAIN; } - video->last_fid = fid; + stream->last_fid = fid; return data[0]; } -static void uvc_video_decode_data(struct uvc_video_device *video, +static void uvc_video_decode_data(struct uvc_streaming *stream, struct uvc_buffer *buf, const __u8 *data, int len) { - struct uvc_video_queue *queue = &video->queue; + struct uvc_video_queue *queue = &stream->queue; unsigned int maxlen, nbytes; void *mem; @@ -466,7 +472,7 @@ static void uvc_video_decode_data(struct uvc_video_device *video, } } -static void uvc_video_decode_end(struct uvc_video_device *video, +static void uvc_video_decode_end(struct uvc_streaming *stream, struct uvc_buffer *buf, const __u8 *data, int len) { /* Mark the buffer as done if the EOF marker is set. */ @@ -475,8 +481,8 @@ static void uvc_video_decode_end(struct uvc_video_device *video, if (data[0] == len) uvc_trace(UVC_TRACE_FRAME, "EOF in empty payload.\n"); buf->state = UVC_BUF_STATE_DONE; - if (video->dev->quirks & UVC_QUIRK_STREAM_NO_FID) - video->last_fid ^= UVC_STREAM_FID; + if (stream->dev->quirks & UVC_QUIRK_STREAM_NO_FID) + stream->last_fid ^= UVC_STREAM_FID; } } @@ -491,26 +497,26 @@ static void uvc_video_decode_end(struct uvc_video_device *video, * uvc_video_encode_data is called for every URB and copies the data from the * video buffer to the transfer buffer. */ -static int uvc_video_encode_header(struct uvc_video_device *video, +static int uvc_video_encode_header(struct uvc_streaming *stream, struct uvc_buffer *buf, __u8 *data, int len) { data[0] = 2; /* Header length */ data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF - | (video->last_fid & UVC_STREAM_FID); + | (stream->last_fid & UVC_STREAM_FID); return 2; } -static int uvc_video_encode_data(struct uvc_video_device *video, +static int uvc_video_encode_data(struct uvc_streaming *stream, struct uvc_buffer *buf, __u8 *data, int len) { - struct uvc_video_queue *queue = &video->queue; + struct uvc_video_queue *queue = &stream->queue; unsigned int nbytes; void *mem; /* Copy video data to the URB buffer. */ mem = queue->mem + buf->buf.m.offset + queue->buf_used; nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used); - nbytes = min(video->bulk.max_payload_size - video->bulk.payload_size, + nbytes = min(stream->bulk.max_payload_size - stream->bulk.payload_size, nbytes); memcpy(data, mem, nbytes); @@ -526,8 +532,8 @@ static int uvc_video_encode_data(struct uvc_video_device *video, /* * Completion handler for video URBs. */ -static void uvc_video_decode_isoc(struct urb *urb, - struct uvc_video_device *video, struct uvc_buffer *buf) +static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) { u8 *mem; int ret, i; @@ -542,31 +548,32 @@ static void uvc_video_decode_isoc(struct urb *urb, /* Decode the payload header. */ mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset; do { - ret = uvc_video_decode_start(video, buf, mem, + ret = uvc_video_decode_start(stream, buf, mem, urb->iso_frame_desc[i].actual_length); if (ret == -EAGAIN) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } while (ret == -EAGAIN); if (ret < 0) continue; /* Decode the payload data. */ - uvc_video_decode_data(video, buf, mem + ret, + uvc_video_decode_data(stream, buf, mem + ret, urb->iso_frame_desc[i].actual_length - ret); /* Process the header again. */ - uvc_video_decode_end(video, buf, mem, + uvc_video_decode_end(stream, buf, mem, urb->iso_frame_desc[i].actual_length); if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, buf); } } -static void uvc_video_decode_bulk(struct urb *urb, - struct uvc_video_device *video, struct uvc_buffer *buf) +static void uvc_video_decode_bulk(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) { u8 *mem; int len, ret; @@ -576,24 +583,25 @@ static void uvc_video_decode_bulk(struct urb *urb, mem = urb->transfer_buffer; len = urb->actual_length; - video->bulk.payload_size += len; + stream->bulk.payload_size += len; /* If the URB is the first of its payload, decode and save the * header. */ - if (video->bulk.header_size == 0 && !video->bulk.skip_payload) { + if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) { do { - ret = uvc_video_decode_start(video, buf, mem, len); + ret = uvc_video_decode_start(stream, buf, mem, len); if (ret == -EAGAIN) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } while (ret == -EAGAIN); /* If an error occured skip the rest of the payload. */ if (ret < 0 || buf == NULL) { - video->bulk.skip_payload = 1; + stream->bulk.skip_payload = 1; } else { - memcpy(video->bulk.header, mem, ret); - video->bulk.header_size = ret; + memcpy(stream->bulk.header, mem, ret); + stream->bulk.header_size = ret; mem += ret; len -= ret; @@ -606,33 +614,34 @@ static void uvc_video_decode_bulk(struct urb *urb, */ /* Process video data. */ - if (!video->bulk.skip_payload && buf != NULL) - uvc_video_decode_data(video, buf, mem, len); + if (!stream->bulk.skip_payload && buf != NULL) + uvc_video_decode_data(stream, buf, mem, len); /* Detect the payload end by a URB smaller than the maximum size (or * a payload size equal to the maximum) and process the header again. */ if (urb->actual_length < urb->transfer_buffer_length || - video->bulk.payload_size >= video->bulk.max_payload_size) { - if (!video->bulk.skip_payload && buf != NULL) { - uvc_video_decode_end(video, buf, video->bulk.header, - video->bulk.payload_size); + stream->bulk.payload_size >= stream->bulk.max_payload_size) { + if (!stream->bulk.skip_payload && buf != NULL) { + uvc_video_decode_end(stream, buf, stream->bulk.header, + stream->bulk.payload_size); if (buf->state == UVC_BUF_STATE_DONE || buf->state == UVC_BUF_STATE_ERROR) - buf = uvc_queue_next_buffer(&video->queue, buf); + buf = uvc_queue_next_buffer(&stream->queue, + buf); } - video->bulk.header_size = 0; - video->bulk.skip_payload = 0; - video->bulk.payload_size = 0; + stream->bulk.header_size = 0; + stream->bulk.skip_payload = 0; + stream->bulk.payload_size = 0; } } -static void uvc_video_encode_bulk(struct urb *urb, - struct uvc_video_device *video, struct uvc_buffer *buf) +static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream, + struct uvc_buffer *buf) { u8 *mem = urb->transfer_buffer; - int len = video->urb_size, ret; + int len = stream->urb_size, ret; if (buf == NULL) { urb->transfer_buffer_length = 0; @@ -640,40 +649,40 @@ static void uvc_video_encode_bulk(struct urb *urb, } /* If the URB is the first of its payload, add the header. */ - if (video->bulk.header_size == 0) { - ret = uvc_video_encode_header(video, buf, mem, len); - video->bulk.header_size = ret; - video->bulk.payload_size += ret; + if (stream->bulk.header_size == 0) { + ret = uvc_video_encode_header(stream, buf, mem, len); + stream->bulk.header_size = ret; + stream->bulk.payload_size += ret; mem += ret; len -= ret; } /* Process video data. */ - ret = uvc_video_encode_data(video, buf, mem, len); + ret = uvc_video_encode_data(stream, buf, mem, len); - video->bulk.payload_size += ret; + stream->bulk.payload_size += ret; len -= ret; - if (buf->buf.bytesused == video->queue.buf_used || - video->bulk.payload_size == video->bulk.max_payload_size) { - if (buf->buf.bytesused == video->queue.buf_used) { - video->queue.buf_used = 0; + if (buf->buf.bytesused == stream->queue.buf_used || + stream->bulk.payload_size == stream->bulk.max_payload_size) { + if (buf->buf.bytesused == stream->queue.buf_used) { + stream->queue.buf_used = 0; buf->state = UVC_BUF_STATE_DONE; - uvc_queue_next_buffer(&video->queue, buf); - video->last_fid ^= UVC_STREAM_FID; + uvc_queue_next_buffer(&stream->queue, buf); + stream->last_fid ^= UVC_STREAM_FID; } - video->bulk.header_size = 0; - video->bulk.payload_size = 0; + stream->bulk.header_size = 0; + stream->bulk.payload_size = 0; } - urb->transfer_buffer_length = video->urb_size - len; + urb->transfer_buffer_length = stream->urb_size - len; } static void uvc_video_complete(struct urb *urb) { - struct uvc_video_device *video = urb->context; - struct uvc_video_queue *queue = &video->queue; + struct uvc_streaming *stream = urb->context; + struct uvc_video_queue *queue = &stream->queue; struct uvc_buffer *buf = NULL; unsigned long flags; int ret; @@ -687,7 +696,7 @@ static void uvc_video_complete(struct urb *urb) "completion handler.\n", urb->status); case -ENOENT: /* usb_kill_urb() called. */ - if (video->frozen) + if (stream->frozen) return; case -ECONNRESET: /* usb_unlink_urb() called. */ @@ -702,7 +711,7 @@ static void uvc_video_complete(struct urb *urb) queue); spin_unlock_irqrestore(&queue->irqlock, flags); - video->decode(urb, video, buf); + stream->decode(urb, stream, buf); if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { uvc_printk(KERN_ERR, "Failed to resubmit video URB (%d).\n", @@ -713,19 +722,19 @@ static void uvc_video_complete(struct urb *urb) /* * Free transfer buffers. */ -static void uvc_free_urb_buffers(struct uvc_video_device *video) +static void uvc_free_urb_buffers(struct uvc_streaming *stream) { unsigned int i; for (i = 0; i < UVC_URBS; ++i) { - if (video->urb_buffer[i]) { - usb_buffer_free(video->dev->udev, video->urb_size, - video->urb_buffer[i], video->urb_dma[i]); - video->urb_buffer[i] = NULL; + if (stream->urb_buffer[i]) { + usb_buffer_free(stream->dev->udev, stream->urb_size, + stream->urb_buffer[i], stream->urb_dma[i]); + stream->urb_buffer[i] = NULL; } } - video->urb_size = 0; + stream->urb_size = 0; } /* @@ -739,15 +748,15 @@ static void uvc_free_urb_buffers(struct uvc_video_device *video) * * Return the number of allocated packets on success or 0 when out of memory. */ -static int uvc_alloc_urb_buffers(struct uvc_video_device *video, +static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, unsigned int size, unsigned int psize, gfp_t gfp_flags) { unsigned int npackets; unsigned int i; /* Buffers are already allocated, bail out. */ - if (video->urb_size) - return video->urb_size / psize; + if (stream->urb_size) + return stream->urb_size / psize; /* Compute the number of packets. Bulk endpoints might transfer UVC * payloads accross multiple URBs. @@ -759,17 +768,17 @@ static int uvc_alloc_urb_buffers(struct uvc_video_device *video, /* Retry allocations until one succeed. */ for (; npackets > 1; npackets /= 2) { for (i = 0; i < UVC_URBS; ++i) { - video->urb_buffer[i] = usb_buffer_alloc( - video->dev->udev, psize * npackets, - gfp_flags | __GFP_NOWARN, &video->urb_dma[i]); - if (!video->urb_buffer[i]) { - uvc_free_urb_buffers(video); + stream->urb_buffer[i] = usb_buffer_alloc( + stream->dev->udev, psize * npackets, + gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]); + if (!stream->urb_buffer[i]) { + uvc_free_urb_buffers(stream); break; } } if (i == UVC_URBS) { - video->urb_size = psize * npackets; + stream->urb_size = psize * npackets; return npackets; } } @@ -780,29 +789,30 @@ static int uvc_alloc_urb_buffers(struct uvc_video_device *video, /* * Uninitialize isochronous/bulk URBs and free transfer buffers. */ -static void uvc_uninit_video(struct uvc_video_device *video, int free_buffers) +static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers) { struct urb *urb; unsigned int i; for (i = 0; i < UVC_URBS; ++i) { - if ((urb = video->urb[i]) == NULL) + urb = stream->urb[i]; + if (urb == NULL) continue; usb_kill_urb(urb); usb_free_urb(urb); - video->urb[i] = NULL; + stream->urb[i] = NULL; } if (free_buffers) - uvc_free_urb_buffers(video); + uvc_free_urb_buffers(stream); } /* * Initialize isochronous URBs and allocate transfer buffers. The packet size * is given by the endpoint. */ -static int uvc_init_video_isoc(struct uvc_video_device *video, +static int uvc_init_video_isoc(struct uvc_streaming *stream, struct usb_host_endpoint *ep, gfp_t gfp_flags) { struct urb *urb; @@ -812,9 +822,9 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, psize = le16_to_cpu(ep->desc.wMaxPacketSize); psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); - size = video->streaming->ctrl.dwMaxVideoFrameSize; + size = stream->ctrl.dwMaxVideoFrameSize; - npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags); + npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); if (npackets == 0) return -ENOMEM; @@ -823,18 +833,18 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, for (i = 0; i < UVC_URBS; ++i) { urb = usb_alloc_urb(npackets, gfp_flags); if (urb == NULL) { - uvc_uninit_video(video, 1); + uvc_uninit_video(stream, 1); return -ENOMEM; } - urb->dev = video->dev->udev; - urb->context = video; - urb->pipe = usb_rcvisocpipe(video->dev->udev, + urb->dev = stream->dev->udev; + urb->context = stream; + urb->pipe = usb_rcvisocpipe(stream->dev->udev, ep->desc.bEndpointAddress); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->interval = ep->desc.bInterval; - urb->transfer_buffer = video->urb_buffer[i]; - urb->transfer_dma = video->urb_dma[i]; + urb->transfer_buffer = stream->urb_buffer[i]; + urb->transfer_dma = stream->urb_dma[i]; urb->complete = uvc_video_complete; urb->number_of_packets = npackets; urb->transfer_buffer_length = size; @@ -844,7 +854,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, urb->iso_frame_desc[j].length = psize; } - video->urb[i] = urb; + stream->urb[i] = urb; } return 0; @@ -854,7 +864,7 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, * Initialize bulk URBs and allocate transfer buffers. The packet size is * given by the endpoint. */ -static int uvc_init_video_bulk(struct uvc_video_device *video, +static int uvc_init_video_bulk(struct uvc_streaming *stream, struct usb_host_endpoint *ep, gfp_t gfp_flags) { struct urb *urb; @@ -863,39 +873,39 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, u32 size; psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff; - size = video->streaming->ctrl.dwMaxPayloadTransferSize; - video->bulk.max_payload_size = size; + size = stream->ctrl.dwMaxPayloadTransferSize; + stream->bulk.max_payload_size = size; - npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags); + npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); if (npackets == 0) return -ENOMEM; size = npackets * psize; if (usb_endpoint_dir_in(&ep->desc)) - pipe = usb_rcvbulkpipe(video->dev->udev, + pipe = usb_rcvbulkpipe(stream->dev->udev, ep->desc.bEndpointAddress); else - pipe = usb_sndbulkpipe(video->dev->udev, + pipe = usb_sndbulkpipe(stream->dev->udev, ep->desc.bEndpointAddress); - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) size = 0; for (i = 0; i < UVC_URBS; ++i) { urb = usb_alloc_urb(0, gfp_flags); if (urb == NULL) { - uvc_uninit_video(video, 1); + uvc_uninit_video(stream, 1); return -ENOMEM; } - usb_fill_bulk_urb(urb, video->dev->udev, pipe, - video->urb_buffer[i], size, uvc_video_complete, - video); + usb_fill_bulk_urb(urb, stream->dev->udev, pipe, + stream->urb_buffer[i], size, uvc_video_complete, + stream); urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - urb->transfer_dma = video->urb_dma[i]; + urb->transfer_dma = stream->urb_dma[i]; - video->urb[i] = urb; + stream->urb[i] = urb; } return 0; @@ -904,35 +914,35 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, /* * Initialize isochronous/bulk URBs and allocate transfer buffers. */ -static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) +static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) { - struct usb_interface *intf = video->streaming->intf; + struct usb_interface *intf = stream->intf; struct usb_host_interface *alts; struct usb_host_endpoint *ep = NULL; - int intfnum = video->streaming->intfnum; + int intfnum = stream->intfnum; unsigned int bandwidth, psize, i; int ret; - video->last_fid = -1; - video->bulk.header_size = 0; - video->bulk.skip_payload = 0; - video->bulk.payload_size = 0; + stream->last_fid = -1; + stream->bulk.header_size = 0; + stream->bulk.skip_payload = 0; + stream->bulk.payload_size = 0; if (intf->num_altsetting > 1) { /* Isochronous endpoint, select the alternate setting. */ - bandwidth = video->streaming->ctrl.dwMaxPayloadTransferSize; + bandwidth = stream->ctrl.dwMaxPayloadTransferSize; if (bandwidth == 0) { uvc_printk(KERN_WARNING, "device %s requested null " "bandwidth, defaulting to lowest.\n", - video->vdev->name); + stream->dev->name); bandwidth = 1; } for (i = 0; i < intf->num_altsetting; ++i) { alts = &intf->altsetting[i]; ep = uvc_find_endpoint(alts, - video->streaming->header.bEndpointAddress); + stream->header.bEndpointAddress); if (ep == NULL) continue; @@ -946,18 +956,19 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) if (i >= intf->num_altsetting) return -EIO; - if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0) + ret = usb_set_interface(stream->dev->udev, intfnum, i); + if (ret < 0) return ret; - ret = uvc_init_video_isoc(video, ep, gfp_flags); + ret = uvc_init_video_isoc(stream, ep, gfp_flags); } else { /* Bulk endpoint, proceed to URB initialization. */ ep = uvc_find_endpoint(&intf->altsetting[0], - video->streaming->header.bEndpointAddress); + stream->header.bEndpointAddress); if (ep == NULL) return -EIO; - ret = uvc_init_video_bulk(video, ep, gfp_flags); + ret = uvc_init_video_bulk(stream, ep, gfp_flags); } if (ret < 0) @@ -965,10 +976,11 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) /* Submit the URBs. */ for (i = 0; i < UVC_URBS; ++i) { - if ((ret = usb_submit_urb(video->urb[i], gfp_flags)) < 0) { + ret = usb_submit_urb(stream->urb[i], gfp_flags); + if (ret < 0) { uvc_printk(KERN_ERR, "Failed to submit URB %u " "(%d).\n", i, ret); - uvc_uninit_video(video, 1); + uvc_uninit_video(stream, 1); return ret; } } @@ -987,14 +999,14 @@ static int uvc_init_video(struct uvc_video_device *video, gfp_t gfp_flags) * video buffers in any way. We mark the device as frozen to make sure the URB * completion handler won't try to cancel the queue when we kill the URBs. */ -int uvc_video_suspend(struct uvc_video_device *video) +int uvc_video_suspend(struct uvc_streaming *stream) { - if (!uvc_queue_streaming(&video->queue)) + if (!uvc_queue_streaming(&stream->queue)) return 0; - video->frozen = 1; - uvc_uninit_video(video, 0); - usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + stream->frozen = 1; + uvc_uninit_video(stream, 0); + usb_set_interface(stream->dev->udev, stream->intfnum, 0); return 0; } @@ -1006,22 +1018,24 @@ int uvc_video_suspend(struct uvc_video_device *video) * buffers, making sure userspace applications are notified of the problem * instead of waiting forever. */ -int uvc_video_resume(struct uvc_video_device *video) +int uvc_video_resume(struct uvc_streaming *stream) { int ret; - video->frozen = 0; + stream->frozen = 0; - if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) { - uvc_queue_enable(&video->queue, 0); + ret = uvc_commit_video(stream, &stream->ctrl); + if (ret < 0) { + uvc_queue_enable(&stream->queue, 0); return ret; } - if (!uvc_queue_streaming(&video->queue)) + if (!uvc_queue_streaming(&stream->queue)) return 0; - if ((ret = uvc_init_video(video, GFP_NOIO)) < 0) - uvc_queue_enable(&video->queue, 0); + ret = uvc_init_video(stream, GFP_NOIO); + if (ret < 0) + uvc_queue_enable(&stream->queue, 0); return ret; } @@ -1040,47 +1054,53 @@ int uvc_video_resume(struct uvc_video_device *video) * * This function is called before registering the device with V4L. */ -int uvc_video_init(struct uvc_video_device *video) +int uvc_video_init(struct uvc_streaming *stream) { - struct uvc_streaming_control *probe = &video->streaming->ctrl; + struct uvc_streaming_control *probe = &stream->ctrl; struct uvc_format *format = NULL; struct uvc_frame *frame = NULL; unsigned int i; int ret; - if (video->streaming->nformats == 0) { + if (stream->nformats == 0) { uvc_printk(KERN_INFO, "No supported video formats found.\n"); return -EINVAL; } + atomic_set(&stream->active, 0); + + /* Initialize the video buffers queue. */ + uvc_queue_init(&stream->queue, stream->type); + /* Alternate setting 0 should be the default, yet the XBox Live Vision * Cam (and possibly other devices) crash or otherwise misbehave if * they don't receive a SET_INTERFACE request before any other video * control request. */ - usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); + usb_set_interface(stream->dev->udev, stream->intfnum, 0); /* Set the streaming probe control with default streaming parameters * retrieved from the device. Webcams that don't suport GET_DEF * requests on the probe control will just keep their current streaming * parameters. */ - if (uvc_get_video_ctrl(video, probe, 1, GET_DEF) == 0) - uvc_set_video_ctrl(video, probe, 1); + if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0) + uvc_set_video_ctrl(stream, probe, 1); /* Initialize the streaming parameters with the probe control current * value. This makes sure SET_CUR requests on the streaming commit * control will always use values retrieved from a successful GET_CUR * request on the probe control, as required by the UVC specification. */ - if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR); + if (ret < 0) return ret; /* Check if the default format descriptor exists. Use the first * available format otherwise. */ - for (i = video->streaming->nformats; i > 0; --i) { - format = &video->streaming->format[i-1]; + for (i = stream->nformats; i > 0; --i) { + format = &stream->format[i-1]; if (format->index == probe->bFormatIndex) break; } @@ -1105,21 +1125,20 @@ int uvc_video_init(struct uvc_video_device *video) probe->bFormatIndex = format->index; probe->bFrameIndex = frame->bFrameIndex; - video->streaming->cur_format = format; - video->streaming->cur_frame = frame; - atomic_set(&video->active, 0); + stream->cur_format = format; + stream->cur_frame = frame; /* Select the video decoding function */ - if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) - video->decode = uvc_video_decode_isight; - else if (video->streaming->intf->num_altsetting > 1) - video->decode = uvc_video_decode_isoc; + if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) + stream->decode = uvc_video_decode_isight; + else if (stream->intf->num_altsetting > 1) + stream->decode = uvc_video_decode_isoc; else - video->decode = uvc_video_decode_bulk; + stream->decode = uvc_video_decode_bulk; } else { - if (video->streaming->intf->num_altsetting == 1) - video->decode = uvc_video_encode_bulk; + if (stream->intf->num_altsetting == 1) + stream->decode = uvc_video_encode_bulk; else { uvc_printk(KERN_INFO, "Isochronous endpoints are not " "supported for video output devices.\n"); @@ -1133,31 +1152,32 @@ int uvc_video_init(struct uvc_video_device *video) /* * Enable or disable the video stream. */ -int uvc_video_enable(struct uvc_video_device *video, int enable) +int uvc_video_enable(struct uvc_streaming *stream, int enable) { int ret; if (!enable) { - uvc_uninit_video(video, 1); - usb_set_interface(video->dev->udev, - video->streaming->intfnum, 0); - uvc_queue_enable(&video->queue, 0); + uvc_uninit_video(stream, 1); + usb_set_interface(stream->dev->udev, stream->intfnum, 0); + uvc_queue_enable(&stream->queue, 0); return 0; } - if ((video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || + if ((stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || uvc_no_drop_param) - video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; + stream->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; else - video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; + stream->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; - if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) + ret = uvc_queue_enable(&stream->queue, 1); + if (ret < 0) return ret; /* Commit the streaming parameters. */ - if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) + ret = uvc_commit_video(stream, &stream->ctrl); + if (ret < 0) return ret; - return uvc_init_video(video, GFP_KERNEL); + return uvc_init_video(stream, GFP_KERNEL); } diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 3c78d3c1e4c0..e7958aa454ce 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -67,155 +67,12 @@ struct uvc_xu_control { #ifdef __KERNEL__ #include <linux/poll.h> +#include <linux/usb/video.h> /* -------------------------------------------------------------------------- * UVC constants */ -#define SC_UNDEFINED 0x00 -#define SC_VIDEOCONTROL 0x01 -#define SC_VIDEOSTREAMING 0x02 -#define SC_VIDEO_INTERFACE_COLLECTION 0x03 - -#define PC_PROTOCOL_UNDEFINED 0x00 - -#define CS_UNDEFINED 0x20 -#define CS_DEVICE 0x21 -#define CS_CONFIGURATION 0x22 -#define CS_STRING 0x23 -#define CS_INTERFACE 0x24 -#define CS_ENDPOINT 0x25 - -/* VideoControl class specific interface descriptor */ -#define VC_DESCRIPTOR_UNDEFINED 0x00 -#define VC_HEADER 0x01 -#define VC_INPUT_TERMINAL 0x02 -#define VC_OUTPUT_TERMINAL 0x03 -#define VC_SELECTOR_UNIT 0x04 -#define VC_PROCESSING_UNIT 0x05 -#define VC_EXTENSION_UNIT 0x06 - -/* VideoStreaming class specific interface descriptor */ -#define VS_UNDEFINED 0x00 -#define VS_INPUT_HEADER 0x01 -#define VS_OUTPUT_HEADER 0x02 -#define VS_STILL_IMAGE_FRAME 0x03 -#define VS_FORMAT_UNCOMPRESSED 0x04 -#define VS_FRAME_UNCOMPRESSED 0x05 -#define VS_FORMAT_MJPEG 0x06 -#define VS_FRAME_MJPEG 0x07 -#define VS_FORMAT_MPEG2TS 0x0a -#define VS_FORMAT_DV 0x0c -#define VS_COLORFORMAT 0x0d -#define VS_FORMAT_FRAME_BASED 0x10 -#define VS_FRAME_FRAME_BASED 0x11 -#define VS_FORMAT_STREAM_BASED 0x12 - -/* Endpoint type */ -#define EP_UNDEFINED 0x00 -#define EP_GENERAL 0x01 -#define EP_ENDPOINT 0x02 -#define EP_INTERRUPT 0x03 - -/* Request codes */ -#define RC_UNDEFINED 0x00 -#define SET_CUR 0x01 -#define GET_CUR 0x81 -#define GET_MIN 0x82 -#define GET_MAX 0x83 -#define GET_RES 0x84 -#define GET_LEN 0x85 -#define GET_INFO 0x86 -#define GET_DEF 0x87 - -/* VideoControl interface controls */ -#define VC_CONTROL_UNDEFINED 0x00 -#define VC_VIDEO_POWER_MODE_CONTROL 0x01 -#define VC_REQUEST_ERROR_CODE_CONTROL 0x02 - -/* Terminal controls */ -#define TE_CONTROL_UNDEFINED 0x00 - -/* Selector Unit controls */ -#define SU_CONTROL_UNDEFINED 0x00 -#define SU_INPUT_SELECT_CONTROL 0x01 - -/* Camera Terminal controls */ -#define CT_CONTROL_UNDEFINED 0x00 -#define CT_SCANNING_MODE_CONTROL 0x01 -#define CT_AE_MODE_CONTROL 0x02 -#define CT_AE_PRIORITY_CONTROL 0x03 -#define CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04 -#define CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05 -#define CT_FOCUS_ABSOLUTE_CONTROL 0x06 -#define CT_FOCUS_RELATIVE_CONTROL 0x07 -#define CT_FOCUS_AUTO_CONTROL 0x08 -#define CT_IRIS_ABSOLUTE_CONTROL 0x09 -#define CT_IRIS_RELATIVE_CONTROL 0x0a -#define CT_ZOOM_ABSOLUTE_CONTROL 0x0b -#define CT_ZOOM_RELATIVE_CONTROL 0x0c -#define CT_PANTILT_ABSOLUTE_CONTROL 0x0d -#define CT_PANTILT_RELATIVE_CONTROL 0x0e -#define CT_ROLL_ABSOLUTE_CONTROL 0x0f -#define CT_ROLL_RELATIVE_CONTROL 0x10 -#define CT_PRIVACY_CONTROL 0x11 - -/* Processing Unit controls */ -#define PU_CONTROL_UNDEFINED 0x00 -#define PU_BACKLIGHT_COMPENSATION_CONTROL 0x01 -#define PU_BRIGHTNESS_CONTROL 0x02 -#define PU_CONTRAST_CONTROL 0x03 -#define PU_GAIN_CONTROL 0x04 -#define PU_POWER_LINE_FREQUENCY_CONTROL 0x05 -#define PU_HUE_CONTROL 0x06 -#define PU_SATURATION_CONTROL 0x07 -#define PU_SHARPNESS_CONTROL 0x08 -#define PU_GAMMA_CONTROL 0x09 -#define PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0a -#define PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0b -#define PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0c -#define PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0d -#define PU_DIGITAL_MULTIPLIER_CONTROL 0x0e -#define PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0f -#define PU_HUE_AUTO_CONTROL 0x10 -#define PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11 -#define PU_ANALOG_LOCK_STATUS_CONTROL 0x12 - -#define LXU_MOTOR_PANTILT_RELATIVE_CONTROL 0x01 -#define LXU_MOTOR_PANTILT_RESET_CONTROL 0x02 -#define LXU_MOTOR_FOCUS_MOTOR_CONTROL 0x03 - -/* VideoStreaming interface controls */ -#define VS_CONTROL_UNDEFINED 0x00 -#define VS_PROBE_CONTROL 0x01 -#define VS_COMMIT_CONTROL 0x02 -#define VS_STILL_PROBE_CONTROL 0x03 -#define VS_STILL_COMMIT_CONTROL 0x04 -#define VS_STILL_IMAGE_TRIGGER_CONTROL 0x05 -#define VS_STREAM_ERROR_CODE_CONTROL 0x06 -#define VS_GENERATE_KEY_FRAME_CONTROL 0x07 -#define VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08 -#define VS_SYNC_DELAY_CONTROL 0x09 - -#define TT_VENDOR_SPECIFIC 0x0100 -#define TT_STREAMING 0x0101 - -/* Input Terminal types */ -#define ITT_VENDOR_SPECIFIC 0x0200 -#define ITT_CAMERA 0x0201 -#define ITT_MEDIA_TRANSPORT_INPUT 0x0202 - -/* Output Terminal types */ -#define OTT_VENDOR_SPECIFIC 0x0300 -#define OTT_DISPLAY 0x0301 -#define OTT_MEDIA_TRANSPORT_OUTPUT 0x0302 - -/* External Terminal types */ -#define EXTERNAL_VENDOR_SPECIFIC 0x0400 -#define COMPOSITE_CONNECTOR 0x0401 -#define SVIDEO_CONNECTOR 0x0402 -#define COMPONENT_CONNECTOR 0x0403 - #define UVC_TERM_INPUT 0x0000 #define UVC_TERM_OUTPUT 0x8000 @@ -223,12 +80,12 @@ struct uvc_xu_control { #define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) #define UVC_ENTITY_IS_TERM(entity) (((entity)->type & 0xff00) != 0) #define UVC_ENTITY_IS_ITERM(entity) \ - (((entity)->type & 0x8000) == UVC_TERM_INPUT) + (UVC_ENTITY_IS_TERM(entity) && \ + ((entity)->type & 0x8000) == UVC_TERM_INPUT) #define UVC_ENTITY_IS_OTERM(entity) \ - (((entity)->type & 0x8000) == UVC_TERM_OUTPUT) + (UVC_ENTITY_IS_TERM(entity) && \ + ((entity)->type & 0x8000) == UVC_TERM_OUTPUT) -#define UVC_STATUS_TYPE_CONTROL 1 -#define UVC_STATUS_TYPE_STREAMING 2 /* ------------------------------------------------------------------------ * GUIDs @@ -249,19 +106,6 @@ struct uvc_xu_control { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02} -#define UVC_GUID_LOGITECH_DEV_INFO \ - {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ - 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1e} -#define UVC_GUID_LOGITECH_USER_HW \ - {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ - 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f} -#define UVC_GUID_LOGITECH_VIDEO \ - {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ - 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x50} -#define UVC_GUID_LOGITECH_MOTOR \ - {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \ - 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56} - #define UVC_GUID_FORMAT_MJPEG \ { 'M', 'J', 'P', 'G', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} @@ -314,6 +158,7 @@ struct uvc_xu_control { #define UVC_QUIRK_STREAM_NO_FID 0x00000010 #define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 #define UVC_QUIRK_FIX_BANDWIDTH 0x00000080 +#define UVC_QUIRK_PROBE_DEF 0x00000100 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 @@ -518,26 +363,6 @@ struct uvc_streaming_header { __u8 bTriggerUsage; }; -struct uvc_streaming { - struct list_head list; - - struct usb_interface *intf; - int intfnum; - __u16 maxpsize; - - struct uvc_streaming_header header; - enum v4l2_buf_type type; - - unsigned int nformats; - struct uvc_format *format; - - struct uvc_streaming_control ctrl; - struct uvc_format *cur_format; - struct uvc_frame *cur_frame; - - struct mutex mutex; -}; - enum uvc_buffer_state { UVC_BUF_STATE_IDLE = 0, UVC_BUF_STATE_QUEUED = 1, @@ -579,26 +404,45 @@ struct uvc_video_queue { struct list_head irqqueue; }; -struct uvc_video_device { +struct uvc_video_chain { struct uvc_device *dev; - struct video_device *vdev; - atomic_t active; - unsigned int frozen : 1; + struct list_head list; struct list_head iterms; /* Input terminals */ - struct uvc_entity *oterm; /* Output terminal */ - struct uvc_entity *sterm; /* USB streaming terminal */ - struct uvc_entity *processing; - struct uvc_entity *selector; - struct list_head extensions; + struct list_head oterms; /* Output terminals */ + struct uvc_entity *processing; /* Processing unit */ + struct uvc_entity *selector; /* Selector unit */ + struct list_head extensions; /* Extension units */ + struct mutex ctrl_mutex; +}; - struct uvc_video_queue queue; +struct uvc_streaming { + struct list_head list; + struct uvc_device *dev; + struct video_device *vdev; + struct uvc_video_chain *chain; + atomic_t active; - /* Video streaming object, must always be non-NULL. */ - struct uvc_streaming *streaming; + struct usb_interface *intf; + int intfnum; + __u16 maxpsize; - void (*decode) (struct urb *urb, struct uvc_video_device *video, + struct uvc_streaming_header header; + enum v4l2_buf_type type; + + unsigned int nformats; + struct uvc_format *format; + + struct uvc_streaming_control ctrl; + struct uvc_format *cur_format; + struct uvc_frame *cur_frame; + + struct mutex mutex; + + unsigned int frozen : 1; + struct uvc_video_queue queue; + void (*decode) (struct urb *urb, struct uvc_streaming *video, struct uvc_buffer *buf); /* Context data used by the bulk completion handler. */ @@ -640,8 +484,10 @@ struct uvc_device { __u32 clock_frequency; struct list_head entities; + struct list_head chains; - struct uvc_video_device video; + /* Video Streaming interfaces */ + struct list_head streams; /* Status Interrupt Endpoint */ struct usb_host_endpoint *int_ep; @@ -649,9 +495,6 @@ struct uvc_device { __u8 *status; struct input_dev *input; char input_phys[64]; - - /* Video Streaming interfaces */ - struct list_head streaming; }; enum uvc_handle_state { @@ -660,7 +503,8 @@ enum uvc_handle_state { }; struct uvc_fh { - struct uvc_video_device *device; + struct uvc_video_chain *chain; + struct uvc_streaming *stream; enum uvc_handle_state state; }; @@ -757,13 +601,13 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) extern const struct v4l2_file_operations uvc_fops; /* Video */ -extern int uvc_video_init(struct uvc_video_device *video); -extern int uvc_video_suspend(struct uvc_video_device *video); -extern int uvc_video_resume(struct uvc_video_device *video); -extern int uvc_video_enable(struct uvc_video_device *video, int enable); -extern int uvc_probe_video(struct uvc_video_device *video, +extern int uvc_video_init(struct uvc_streaming *stream); +extern int uvc_video_suspend(struct uvc_streaming *stream); +extern int uvc_video_resume(struct uvc_streaming *stream); +extern int uvc_video_enable(struct uvc_streaming *stream, int enable); +extern int uvc_probe_video(struct uvc_streaming *stream, struct uvc_streaming_control *probe); -extern int uvc_commit_video(struct uvc_video_device *video, +extern int uvc_commit_video(struct uvc_streaming *stream, struct uvc_streaming_control *ctrl); extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, __u8 intfnum, __u8 cs, void *data, __u16 size); @@ -777,9 +621,9 @@ extern int uvc_status_suspend(struct uvc_device *dev); extern int uvc_status_resume(struct uvc_device *dev); /* Controls */ -extern struct uvc_control *uvc_find_control(struct uvc_video_device *video, +extern struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, __u32 v4l2_id, struct uvc_control_mapping **mapping); -extern int uvc_query_v4l2_ctrl(struct uvc_video_device *video, +extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct v4l2_queryctrl *v4l2_ctrl); extern int uvc_ctrl_add_info(struct uvc_control_info *info); @@ -789,23 +633,23 @@ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); extern int uvc_ctrl_resume_device(struct uvc_device *dev); extern void uvc_ctrl_init(void); -extern int uvc_ctrl_begin(struct uvc_video_device *video); -extern int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback); -static inline int uvc_ctrl_commit(struct uvc_video_device *video) +extern int uvc_ctrl_begin(struct uvc_video_chain *chain); +extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); +static inline int uvc_ctrl_commit(struct uvc_video_chain *chain) { - return __uvc_ctrl_commit(video, 0); + return __uvc_ctrl_commit(chain, 0); } -static inline int uvc_ctrl_rollback(struct uvc_video_device *video) +static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain) { - return __uvc_ctrl_commit(video, 1); + return __uvc_ctrl_commit(chain, 1); } -extern int uvc_ctrl_get(struct uvc_video_device *video, +extern int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); -extern int uvc_ctrl_set(struct uvc_video_device *video, +extern int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); -extern int uvc_xu_ctrl_query(struct uvc_video_device *video, +extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control *ctrl, int set); /* Utility functions */ @@ -817,7 +661,7 @@ extern struct usb_host_endpoint *uvc_find_endpoint( struct usb_host_interface *alts, __u8 epaddr); /* Quirks support */ -void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video, +void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream, struct uvc_buffer *buf); #endif /* __KERNEL__ */ diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index 02f2a6d18b45..761fbd64db58 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -76,9 +76,8 @@ get_v4l_control(struct file *file, dprintk("VIDIOC_G_CTRL: %d\n", err); return 0; } - return ((ctrl2.value - qctrl2.minimum) * 65535 - + (qctrl2.maximum - qctrl2.minimum) / 2) - / (qctrl2.maximum - qctrl2.minimum); + return DIV_ROUND_CLOSEST((ctrl2.value-qctrl2.minimum) * 65535, + qctrl2.maximum - qctrl2.minimum); } return 0; } diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index b91d66a767d7..3a0c64935b0e 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -156,6 +156,8 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, return -EINVAL; if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED) return -EBUSY; + if (qctrl->type == V4L2_CTRL_TYPE_STRING) + return 0; if (qctrl->type == V4L2_CTRL_TYPE_BUTTON || qctrl->type == V4L2_CTRL_TYPE_INTEGER64 || qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS) @@ -340,6 +342,12 @@ const char **v4l2_ctrl_get_menu(u32 id) "Sepia", NULL }; + static const char *tune_preemphasis[] = { + "No preemphasis", + "50 useconds", + "75 useconds", + NULL, + }; switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: @@ -378,6 +386,8 @@ const char **v4l2_ctrl_get_menu(u32 id) return camera_exposure_auto; case V4L2_CID_COLORFX: return colorfx; + case V4L2_CID_TUNE_PREEMPHASIS: + return tune_preemphasis; default: return NULL; } @@ -476,6 +486,28 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous"; case V4L2_CID_PRIVACY: return "Privacy"; + /* FM Radio Modulator control */ + case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls"; + case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation"; + case V4L2_CID_RDS_TX_PI: return "RDS Program ID"; + case V4L2_CID_RDS_TX_PTY: return "RDS Program Type"; + case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name"; + case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text"; + case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled"; + case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time"; + case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation"; + case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Feature Enabled"; + case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain"; + case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold"; + case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time"; + case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time"; + case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled"; + case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation"; + case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency"; + case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-emphasis settings"; + case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level"; + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor"; + default: return NULL; } @@ -508,6 +540,9 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_EXPOSURE_AUTO_PRIORITY: case V4L2_CID_FOCUS_AUTO: case V4L2_CID_PRIVACY: + case V4L2_CID_AUDIO_LIMITER_ENABLED: + case V4L2_CID_AUDIO_COMPRESSION_ENABLED: + case V4L2_CID_PILOT_TONE_ENABLED: qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; min = 0; max = step = 1; @@ -536,12 +571,18 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_STREAM_VBI_FMT: case V4L2_CID_EXPOSURE_AUTO: case V4L2_CID_COLORFX: + case V4L2_CID_TUNE_PREEMPHASIS: qctrl->type = V4L2_CTRL_TYPE_MENU; step = 1; break; + case V4L2_CID_RDS_TX_PS_NAME: + case V4L2_CID_RDS_TX_RADIO_TEXT: + qctrl->type = V4L2_CTRL_TYPE_STRING; + break; case V4L2_CID_USER_CLASS: case V4L2_CID_CAMERA_CLASS: case V4L2_CID_MPEG_CLASS: + case V4L2_CID_FM_TX_CLASS: qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS; qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; min = max = step = def = 0; @@ -570,6 +611,17 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_BLUE_BALANCE: case V4L2_CID_GAMMA: case V4L2_CID_SHARPNESS: + case V4L2_CID_RDS_TX_DEVIATION: + case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: + case V4L2_CID_AUDIO_LIMITER_DEVIATION: + case V4L2_CID_AUDIO_COMPRESSION_GAIN: + case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: + case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: + case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: + case V4L2_CID_PILOT_TONE_DEVIATION: + case V4L2_CID_PILOT_TONE_FREQUENCY: + case V4L2_CID_TUNE_POWER_LEVEL: + case V4L2_CID_TUNE_ANTENNA_CAPACITOR: qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; break; case V4L2_CID_PAN_RELATIVE: diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 0056b115b42e..997975d5e024 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -600,9 +600,37 @@ struct v4l2_ext_controls32 { compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */ }; +struct v4l2_ext_control32 { + __u32 id; + __u32 size; + __u32 reserved2[1]; + union { + __s32 value; + __s64 value64; + compat_caddr_t string; /* actually char * */ + }; +} __attribute__ ((packed)); + +/* The following function really belong in v4l2-common, but that causes + a circular dependency between modules. We need to think about this, but + for now this will do. */ + +/* Return non-zero if this control is a pointer type. Currently only + type STRING is a pointer type. */ +static inline int ctrl_is_pointer(u32 id) +{ + switch (id) { + case V4L2_CID_RDS_TX_PS_NAME: + case V4L2_CID_RDS_TX_RADIO_TEXT: + return 1; + default: + return 0; + } +} + static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control32 __user *ucontrols; struct v4l2_ext_control __user *kcontrols; int n; compat_caddr_t p; @@ -626,15 +654,17 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control)); kp->controls = kcontrols; while (--n >= 0) { - if (copy_in_user(&kcontrols->id, &ucontrols->id, sizeof(__u32))) - return -EFAULT; - if (copy_in_user(&kcontrols->reserved2, &ucontrols->reserved2, sizeof(ucontrols->reserved2))) - return -EFAULT; - /* Note: if the void * part of the union ever becomes relevant - then we need to know the type of the control in order to do - the right thing here. Luckily, that is not yet an issue. */ - if (copy_in_user(&kcontrols->value, &ucontrols->value, sizeof(ucontrols->value))) + if (copy_in_user(kcontrols, ucontrols, sizeof(*kcontrols))) return -EFAULT; + if (ctrl_is_pointer(kcontrols->id)) { + void __user *s; + + if (get_user(p, &ucontrols->string)) + return -EFAULT; + s = compat_ptr(p); + if (put_user(s, &kcontrols->string)) + return -EFAULT; + } ucontrols++; kcontrols++; } @@ -643,7 +673,7 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control32 __user *ucontrols; struct v4l2_ext_control __user *kcontrols = kp->controls; int n = kp->count; compat_caddr_t p; @@ -664,15 +694,14 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext return -EFAULT; while (--n >= 0) { - if (copy_in_user(&ucontrols->id, &kcontrols->id, sizeof(__u32))) - return -EFAULT; - if (copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2, - sizeof(ucontrols->reserved2))) - return -EFAULT; - /* Note: if the void * part of the union ever becomes relevant - then we need to know the type of the control in order to do - the right thing here. Luckily, that is not yet an issue. */ - if (copy_in_user(&ucontrols->value, &kcontrols->value, sizeof(ucontrols->value))) + unsigned size = sizeof(*ucontrols); + + /* Do not modify the pointer when copying a pointer control. + The contents of the pointer was changed, not the pointer + itself. */ + if (ctrl_is_pointer(kcontrols->id)) + size -= sizeof(ucontrols->value64); + if (copy_in_user(ucontrols, kcontrols, size)) return -EFAULT; ucontrols++; kcontrols++; diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index f2afc4e08379..30cc3347ae52 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -42,6 +42,12 @@ printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\ } while (0) +#define dbgarg3(fmt, arg...) \ + do { \ + if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) \ + printk(KERN_CONT "%s: " fmt, vfd->name, ## arg);\ + } while (0) + /* Zero out the end of the struct pointed to by p. Everthing after, but * not including, the specified field is cleared. */ #define CLEAR_AFTER_FIELD(p, field) \ @@ -507,11 +513,12 @@ static inline void v4l_print_ext_ctrls(unsigned int cmd, dbgarg(cmd, ""); printk(KERN_CONT "class=0x%x", c->ctrl_class); for (i = 0; i < c->count; i++) { - if (show_vals) + if (show_vals && !c->controls[i].size) printk(KERN_CONT " id/val=0x%x/0x%x", c->controls[i].id, c->controls[i].value); else - printk(KERN_CONT " id=0x%x", c->controls[i].id); + printk(KERN_CONT " id=0x%x,size=%u", + c->controls[i].id, c->controls[i].size); } printk(KERN_CONT "\n"); }; @@ -522,10 +529,9 @@ static inline int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) /* zero the reserved fields */ c->reserved[0] = c->reserved[1] = 0; - for (i = 0; i < c->count; i++) { + for (i = 0; i < c->count; i++) c->controls[i].reserved2[0] = 0; - c->controls[i].reserved2[1] = 0; - } + /* V4L2_CID_PRIVATE_BASE cannot be used as control class when using extended controls. Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL @@ -1726,24 +1732,29 @@ static long __video_do_ioctl(struct file *file, ret = ops->vidioc_enum_framesizes(file, fh, p); dbgarg(cmd, - "index=%d, pixelformat=%d, type=%d ", - p->index, p->pixel_format, p->type); + "index=%d, pixelformat=%c%c%c%c, type=%d ", + p->index, + (p->pixel_format & 0xff), + (p->pixel_format >> 8) & 0xff, + (p->pixel_format >> 16) & 0xff, + (p->pixel_format >> 24) & 0xff, + p->type); switch (p->type) { case V4L2_FRMSIZE_TYPE_DISCRETE: - dbgarg2("width = %d, height=%d\n", + dbgarg3("width = %d, height=%d\n", p->discrete.width, p->discrete.height); break; case V4L2_FRMSIZE_TYPE_STEPWISE: - dbgarg2("min %dx%d, max %dx%d, step %dx%d\n", + dbgarg3("min %dx%d, max %dx%d, step %dx%d\n", p->stepwise.min_width, p->stepwise.min_height, p->stepwise.step_width, p->stepwise.step_height, p->stepwise.max_width, p->stepwise.max_height); break; case V4L2_FRMSIZE_TYPE_CONTINUOUS: - dbgarg2("continuous\n"); + dbgarg3("continuous\n"); break; default: - dbgarg2("- Unknown type!\n"); + dbgarg3("- Unknown type!\n"); } break; diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index 97b082fe4473..f3b6e15d91f2 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -1776,7 +1776,6 @@ static struct i2c_algo_sgi_data i2c_sgi_vino_data = { static struct i2c_adapter vino_i2c_adapter = { .name = "VINO I2C bus", - .id = I2C_HW_SGI_VINO, .algo = &sgi_algo, .algo_data = &i2c_sgi_vino_data, .owner = THIS_MODULE, diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c index 6c3f23e31b5c..602484dd3da9 100644 --- a/drivers/media/video/w9968cf.c +++ b/drivers/media/video/w9968cf.c @@ -1497,7 +1497,6 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam) }; static struct i2c_adapter adap = { - .id = I2C_HW_SMBUS_W9968CF, .owner = THIS_MODULE, .algo = &algo, }; diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c index 03dc2f3cf84a..0c4d9b1f8e6f 100644 --- a/drivers/media/video/zoran/zoran_card.c +++ b/drivers/media/video/zoran/zoran_card.c @@ -732,7 +732,6 @@ zoran_register_i2c (struct zoran *zr) memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template, sizeof(struct i2c_algo_bit_data)); zr->i2c_algo.data = zr; - zr->i2c_adapter.id = I2C_HW_B_ZR36067; strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), sizeof(zr->i2c_adapter.name)); i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev); @@ -1169,7 +1168,7 @@ zoran_setup_videocodec (struct zoran *zr, m->type = 0; m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER; - strncpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); + strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name)); m->data = zr; switch (type) diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index 2622a6e63da1..9aae011d92ab 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -1,5 +1,5 @@ /* - * Zoran 364xx based USB webcam module version 0.72 + * Zoran 364xx based USB webcam module version 0.73 * * Allows you to use your USB webcam with V4L2 applications * This is still in heavy developpement ! @@ -10,6 +10,8 @@ * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers * V4L2 version inspired by meye.c driver * + * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers. + * * 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 @@ -27,6 +29,7 @@ #include <linux/module.h> +#include <linux/version.h> #include <linux/init.h> #include <linux/usb.h> #include <linux/vmalloc.h> @@ -35,24 +38,40 @@ #include <linux/highmem.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/videobuf-vmalloc.h> /* Version Information */ -#define DRIVER_VERSION "v0.72" +#define DRIVER_VERSION "v0.73" +#define ZR364XX_VERSION_CODE KERNEL_VERSION(0, 7, 3) #define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" #define DRIVER_DESC "Zoran 364xx" /* Camera */ -#define FRAMES 2 -#define MAX_FRAME_SIZE 100000 +#define FRAMES 1 +#define MAX_FRAME_SIZE 200000 #define BUFFER_SIZE 0x1000 #define CTRL_TIMEOUT 500 +#define ZR364XX_DEF_BUFS 4 +#define ZR364XX_READ_IDLE 0 +#define ZR364XX_READ_FRAME 1 /* Debug macro */ -#define DBG(x...) if (debug) printk(KERN_INFO KBUILD_MODNAME x) - +#define DBG(fmt, args...) \ + do { \ + if (debug) { \ + printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \ + } \ + } while (0) + +/*#define FULL_DEBUG 1*/ +#ifdef FULL_DEBUG +#define _DBG DBG +#else +#define _DBG(fmt, args...) +#endif /* Init methods, need to find nicer names for these * the exact names of the chipsets would be the best if someone finds it */ @@ -101,24 +120,93 @@ static struct usb_device_id device_table[] = { MODULE_DEVICE_TABLE(usb, device_table); +struct zr364xx_mode { + u32 color; /* output video color format */ + u32 brightness; /* brightness */ +}; + +/* frame structure */ +struct zr364xx_framei { + unsigned long ulState; /* ulState:ZR364XX_READ_IDLE, + ZR364XX_READ_FRAME */ + void *lpvbits; /* image data */ + unsigned long cur_size; /* current data copied to it */ +}; + +/* image buffer structure */ +struct zr364xx_bufferi { + unsigned long dwFrames; /* number of frames in buffer */ + struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */ +}; + +struct zr364xx_dmaqueue { + struct list_head active; + struct zr364xx_camera *cam; +}; + +struct zr364xx_pipeinfo { + u32 transfer_size; + u8 *transfer_buffer; + u32 state; + void *stream_urb; + void *cam; /* back pointer to zr364xx_camera struct */ + u32 err_count; + u32 idx; +}; + +struct zr364xx_fmt { + char *name; + u32 fourcc; + int depth; +}; + +/* image formats. */ +static const struct zr364xx_fmt formats[] = { + { + .name = "JPG", + .fourcc = V4L2_PIX_FMT_JPEG, + .depth = 24 + } +}; /* Camera stuff */ struct zr364xx_camera { struct usb_device *udev; /* save off the usb device pointer */ struct usb_interface *interface;/* the interface for this device */ struct video_device *vdev; /* v4l video device */ - u8 *framebuf; int nb; - unsigned char *buffer; + struct zr364xx_bufferi buffer; int skip; - int brightness; int width; int height; int method; struct mutex lock; + struct mutex open_lock; int users; + + spinlock_t slock; + struct zr364xx_dmaqueue vidq; + int resources; + int last_frame; + int cur_frame; + unsigned long frame_count; + int b_acquire; + struct zr364xx_pipeinfo pipe[1]; + + u8 read_endpoint; + + const struct zr364xx_fmt *fmt; + struct videobuf_queue vb_vidq; + enum v4l2_buf_type type; + struct zr364xx_mode mode; }; +/* buffer for one video frame */ +struct zr364xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct zr364xx_fmt *fmt; +}; /* function used to send initialisation commands to the camera */ static int send_control_msg(struct usb_device *udev, u8 request, u16 value, @@ -272,139 +360,116 @@ static unsigned char header2[] = { }; static unsigned char header3; +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct zr364xx_camera *cam = vq->priv_data; -/********************/ -/* V4L2 integration */ -/********************/ + *size = cam->width * cam->height * (cam->fmt->depth >> 3); -/* this function reads a full JPEG picture synchronously - * TODO: do it asynchronously... */ -static int read_frame(struct zr364xx_camera *cam, int framenum) -{ - int i, n, temp, head, size, actual_length; - unsigned char *ptr = NULL, *jpeg; - - redo: - /* hardware brightness */ - n = send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); - temp = (0x60 << 8) + 127 - cam->brightness; - n = send_control_msg(cam->udev, 1, temp, 0, NULL, 0); - - /* during the first loop we are going to insert JPEG header */ - head = 0; - /* this is the place in memory where we are going to build - * the JPEG image */ - jpeg = cam->framebuf + framenum * MAX_FRAME_SIZE; - /* read data... */ - do { - n = usb_bulk_msg(cam->udev, - usb_rcvbulkpipe(cam->udev, 0x81), - cam->buffer, BUFFER_SIZE, &actual_length, - CTRL_TIMEOUT); - DBG("buffer : %d %d", cam->buffer[0], cam->buffer[1]); - DBG("bulk : n=%d size=%d", n, actual_length); - if (n < 0) { - dev_err(&cam->udev->dev, "error reading bulk msg\n"); - return 0; - } - if (actual_length < 0 || actual_length > BUFFER_SIZE) { - dev_err(&cam->udev->dev, "wrong number of bytes\n"); - return 0; - } + if (*count == 0) + *count = ZR364XX_DEF_BUFS; - /* swap bytes if camera needs it */ - if (cam->method == METHOD0) { - u16 *buf = (u16*)cam->buffer; - for (i = 0; i < BUFFER_SIZE/2; i++) - swab16s(buf + i); - } + while (*size * (*count) > ZR364XX_DEF_BUFS * 1024 * 1024) + (*count)--; - /* write the JPEG header */ - if (!head) { - DBG("jpeg header"); - ptr = jpeg; - memcpy(ptr, header1, sizeof(header1)); - ptr += sizeof(header1); - header3 = 0; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, cam->buffer, 64); - ptr += 64; - header3 = 1; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, cam->buffer + 64, 64); - ptr += 64; - memcpy(ptr, header2, sizeof(header2)); - ptr += sizeof(header2); - memcpy(ptr, cam->buffer + 128, - actual_length - 128); - ptr += actual_length - 128; - head = 1; - DBG("header : %d %d %d %d %d %d %d %d %d", - cam->buffer[0], cam->buffer[1], cam->buffer[2], - cam->buffer[3], cam->buffer[4], cam->buffer[5], - cam->buffer[6], cam->buffer[7], cam->buffer[8]); - } else { - memcpy(ptr, cam->buffer, actual_length); - ptr += actual_length; - } - } - /* ... until there is no more */ - while (actual_length == BUFFER_SIZE); + return 0; +} - /* we skip the 2 first frames which are usually buggy */ - if (cam->skip) { - cam->skip--; - goto redo; - } +static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) +{ + _DBG("%s\n", __func__); - /* go back to find the JPEG EOI marker */ - size = ptr - jpeg; - ptr -= 2; - while (ptr > jpeg) { - if (*ptr == 0xFF && *(ptr + 1) == 0xD9 - && *(ptr + 2) == 0xFF) - break; - ptr--; - } - if (ptr == jpeg) - DBG("No EOI marker"); + if (in_interrupt()) + BUG(); - /* Sometimes there is junk data in the middle of the picture, - * we want to skip this bogus frames */ - while (ptr > jpeg) { - if (*ptr == 0xFF && *(ptr + 1) == 0xFF - && *(ptr + 2) == 0xFF) - break; - ptr--; + 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 zr364xx_camera *cam = vq->priv_data; + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + int rc; + + DBG("%s, field=%d, fmt name = %s\n", __func__, field, cam->fmt != NULL ? + cam->fmt->name : ""); + if (cam->fmt == NULL) + return -EINVAL; + + buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3); + + if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) { + DBG("invalid buffer prepare\n"); + return -EINVAL; } - if (ptr != jpeg) { - DBG("Bogus frame ? %d", cam->nb); - goto redo; + + buf->fmt = cam->fmt; + buf->vb.width = cam->width; + buf->vb.height = cam->height; + buf->vb.field = field; + + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; } - DBG("jpeg : %d %d %d %d %d %d %d %d", - jpeg[0], jpeg[1], jpeg[2], jpeg[3], - jpeg[4], jpeg[5], jpeg[6], jpeg[7]); + 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 zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + struct zr364xx_camera *cam = vq->priv_data; + + _DBG("%s\n", __func__); + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &cam->vidq.active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); - return size; + _DBG("%s\n", __func__); + free_buffer(vq, buf); } +static struct videobuf_queue_ops zr364xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************/ +/* V4L2 integration */ +/********************/ +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type); -static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt, +static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, loff_t * ppos) { - unsigned long count = cnt; - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; + struct zr364xx_camera *cam = video_drvdata(file); - DBG("zr364xx_read: read %d bytes.", (int) count); - - if (vdev == NULL) - return -ENODEV; - cam = video_get_drvdata(vdev); + _DBG("%s\n", __func__); if (!buf) return -EINVAL; @@ -412,21 +477,276 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt, if (!count) return -EINVAL; - /* NoMan Sux ! */ - count = read_frame(cam, 0); + if (cam->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + zr364xx_vidioc_streamon(file, cam, cam->type) == 0) { + DBG("%s: reading %d bytes at pos %d.\n", __func__, (int) count, + (int) *ppos); + + /* NoMan Sux ! */ + return videobuf_read_one(&cam->vb_vidq, buf, count, ppos, + file->f_flags & O_NONBLOCK); + } + + return 0; +} + +/* video buffer vmalloc implementation based partly on VIVI driver which is + * Copyright (c) 2006 by + * Mauro Carvalho Chehab <mchehab--a.t--infradead.org> + * Ted Walther <ted--a.t--enumera.com> + * John Sokol <sokol--a.t--videotechnology.com> + * http://v4l.videotechnology.com/ + * + */ +static void zr364xx_fillbuff(struct zr364xx_camera *cam, + struct zr364xx_buffer *buf, + int jpgsize) +{ + int pos = 0; + struct timeval ts; + const char *tmpbuf; + char *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned long last_frame; + struct zr364xx_framei *frm; + + if (!vbuf) + return; + + last_frame = cam->last_frame; + if (last_frame != -1) { + frm = &cam->buffer.frame[last_frame]; + tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; + switch (buf->fmt->fourcc) { + case V4L2_PIX_FMT_JPEG: + buf->vb.size = jpgsize; + memcpy(vbuf, tmpbuf, buf->vb.size); + break; + default: + printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n"); + } + cam->last_frame = -1; + } else { + printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n"); + return; + } + DBG("%s: Buffer 0x%08lx size= %d\n", __func__, + (unsigned long)vbuf, pos); + /* tell v4l buffer was filled */ + + buf->vb.field_count = cam->frame_count * 2; + do_gettimeofday(&ts); + buf->vb.ts = ts; + buf->vb.state = VIDEOBUF_DONE; +} + +static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) +{ + struct zr364xx_dmaqueue *dma_q = &cam->vidq; + struct zr364xx_buffer *buf; + unsigned long flags = 0; + int rc = 0; + + DBG("wakeup: %p\n", &dma_q); + spin_lock_irqsave(&cam->slock, flags); + + if (list_empty(&dma_q->active)) { + DBG("No active queue to serve\n"); + rc = -1; + goto unlock; + } + buf = list_entry(dma_q->active.next, + struct zr364xx_buffer, vb.queue); + + if (!waitqueue_active(&buf->vb.done)) { + /* no one active */ + rc = -1; + goto unlock; + } + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + DBG("[%p/%d] wakeup\n", buf, buf->vb.i); + zr364xx_fillbuff(cam, buf, jpgsize); + wake_up(&buf->vb.done); + DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); +unlock: + spin_unlock_irqrestore(&cam->slock, flags); + return 0; +} + +/* this function moves the usb stream read pipe data + * into the system buffers. + * returns 0 on success, EAGAIN if more data to process (call this + * function again). + */ +static int zr364xx_read_video_callback(struct zr364xx_camera *cam, + struct zr364xx_pipeinfo *pipe_info, + struct urb *purb) +{ + unsigned char *pdest; + unsigned char *psrc; + s32 idx = -1; + struct zr364xx_framei *frm; + int i = 0; + unsigned char *ptr = NULL; + + _DBG("buffer to user\n"); + idx = cam->cur_frame; + frm = &cam->buffer.frame[idx]; + + /* swap bytes if camera needs it */ + if (cam->method == METHOD0) { + u16 *buf = (u16 *)pipe_info->transfer_buffer; + for (i = 0; i < purb->actual_length/2; i++) + swab16s(buf + i); + } + + /* search done. now find out if should be acquiring */ + if (!cam->b_acquire) { + /* we found a frame, but this channel is turned off */ + frm->ulState = ZR364XX_READ_IDLE; + return -EINVAL; + } + + psrc = (u8 *)pipe_info->transfer_buffer; + ptr = pdest = frm->lpvbits; + + if (frm->ulState == ZR364XX_READ_IDLE) { + frm->ulState = ZR364XX_READ_FRAME; + frm->cur_size = 0; + + _DBG("jpeg header, "); + memcpy(ptr, header1, sizeof(header1)); + ptr += sizeof(header1); + header3 = 0; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc, 64); + ptr += 64; + header3 = 1; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc + 64, 64); + ptr += 64; + memcpy(ptr, header2, sizeof(header2)); + ptr += sizeof(header2); + memcpy(ptr, psrc + 128, + purb->actual_length - 128); + ptr += purb->actual_length - 128; + _DBG("header : %d %d %d %d %d %d %d %d %d\n", + psrc[0], psrc[1], psrc[2], + psrc[3], psrc[4], psrc[5], + psrc[6], psrc[7], psrc[8]); + frm->cur_size = ptr - pdest; + } else { + if (frm->cur_size + purb->actual_length > MAX_FRAME_SIZE) { + dev_info(&cam->udev->dev, + "%s: buffer (%d bytes) too small to hold " + "frame data. Discarding frame data.\n", + __func__, MAX_FRAME_SIZE); + } else { + pdest += frm->cur_size; + memcpy(pdest, psrc, purb->actual_length); + frm->cur_size += purb->actual_length; + } + } + /*_DBG("cur_size %lu urb size %d\n", frm->cur_size, + purb->actual_length);*/ + + if (purb->actual_length < pipe_info->transfer_size) { + _DBG("****************Buffer[%d]full*************\n", idx); + cam->last_frame = cam->cur_frame; + cam->cur_frame++; + /* end of system frame ring buffer, start at zero */ + if (cam->cur_frame == cam->buffer.dwFrames) + cam->cur_frame = 0; + + /* frame ready */ + /* go back to find the JPEG EOI marker */ + ptr = pdest = frm->lpvbits; + ptr += frm->cur_size - 2; + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xD9 + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr == pdest) + DBG("No EOI marker\n"); + + /* Sometimes there is junk data in the middle of the picture, + * we want to skip this bogus frames */ + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xFF + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr != pdest) { + DBG("Bogus frame ? %d\n", ++(cam->nb)); + } else if (cam->b_acquire) { + /* we skip the 2 first frames which are usually buggy */ + if (cam->skip) + cam->skip--; + else { + _DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", + frm->cur_size, + pdest[0], pdest[1], pdest[2], pdest[3], + pdest[4], pdest[5], pdest[6], pdest[7]); + + zr364xx_got_frame(cam, frm->cur_size); + } + } + cam->frame_count++; + frm->ulState = ZR364XX_READ_IDLE; + frm->cur_size = 0; + } + /* done successfully */ + return 0; +} - if (copy_to_user(buf, cam->framebuf, count)) - return -EFAULT; +static int res_get(struct zr364xx_camera *cam) +{ + /* is it free? */ + mutex_lock(&cam->lock); + if (cam->resources) { + /* no, someone else uses it */ + mutex_unlock(&cam->lock); + return 0; + } + /* it's free, grab it */ + cam->resources = 1; + _DBG("res: get\n"); + mutex_unlock(&cam->lock); + return 1; +} - return count; +static inline int res_check(struct zr364xx_camera *cam) +{ + return cam->resources; } +static void res_free(struct zr364xx_camera *cam) +{ + mutex_lock(&cam->lock); + cam->resources = 0; + mutex_unlock(&cam->lock); + _DBG("res: put\n"); +} static int zr364xx_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - strcpy(cap->driver, DRIVER_DESC); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + struct zr364xx_camera *cam = video_drvdata(file); + + strlcpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); + strlcpy(cap->card, cam->udev->product, sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&cam->udev->dev), + sizeof(cap->bus_info)); + cap->version = ZR364XX_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + return 0; } @@ -458,12 +778,11 @@ static int zr364xx_vidioc_s_input(struct file *file, void *priv, static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); switch (c->id) { case V4L2_CID_BRIGHTNESS: @@ -472,7 +791,7 @@ static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, c->minimum = 0; c->maximum = 127; c->step = 1; - c->default_value = cam->brightness; + c->default_value = cam->mode.brightness; c->flags = 0; break; default: @@ -484,36 +803,42 @@ static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *c) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; + int temp; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); switch (c->id) { case V4L2_CID_BRIGHTNESS: - cam->brightness = c->value; + cam->mode.brightness = c->value; + /* hardware brightness */ + mutex_lock(&cam->lock); + send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); + temp = (0x60 << 8) + 127 - cam->mode.brightness; + send_control_msg(cam->udev, 1, temp, 0, NULL, 0); + mutex_unlock(&cam->lock); break; default: return -EINVAL; } + return 0; } static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *c) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); switch (c->id) { case V4L2_CID_BRIGHTNESS: - c->value = cam->brightness; + c->value = cam->mode.brightness; break; default: return -EINVAL; @@ -527,47 +852,63 @@ static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, if (f->index > 0) return -EINVAL; f->flags = V4L2_FMT_FLAG_COMPRESSED; - strcpy(f->description, "JPEG"); - f->pixelformat = V4L2_PIX_FMT_JPEG; + strcpy(f->description, formats[0].name); + f->pixelformat = formats[0].fourcc; return 0; } +static char *decode_fourcc(__u32 pixelformat, char *buf) +{ + buf[0] = pixelformat & 0xff; + buf[1] = (pixelformat >> 8) & 0xff; + buf[2] = (pixelformat >> 16) & 0xff; + buf[3] = (pixelformat >> 24) & 0xff; + buf[4] = '\0'; + return buf; +} + static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; + struct zr364xx_camera *cam = video_drvdata(file); + char pixelformat_name[5]; - if (vdev == NULL) + if (cam == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) - return -EINVAL; - if (f->fmt.pix.field != V4L2_FIELD_ANY && - f->fmt.pix.field != V4L2_FIELD_NONE) + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) { + DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name)); return -EINVAL; + } + + if (!(f->fmt.pix.width == 160 && f->fmt.pix.height == 120) && + !(f->fmt.pix.width == 640 && f->fmt.pix.height == 480)) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } + f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = cam->width; - f->fmt.pix.height = cam->height; 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; + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); return 0; } static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; - if (vdev == NULL) + if (file == NULL) return -ENODEV; - cam = video_get_drvdata(vdev); + cam = video_drvdata(file); - f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; + f->fmt.pix.pixelformat = formats[0].fourcc; f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.width = cam->width; f->fmt.pix.height = cam->height; @@ -581,38 +922,327 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + char pixelformat_name[5]; + int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f); + int i; - if (vdev == NULL) - return -ENODEV; - cam = video_get_drvdata(vdev); + if (ret < 0) + return ret; - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) - 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; - f->fmt.pix.width = cam->width; - f->fmt.pix.height = cam->height; + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&cam->vb_vidq)) { + DBG("%s queue busy\n", __func__); + ret = -EBUSY; + goto out; + } + + if (res_check(cam)) { + DBG("%s can't change format after started\n", __func__); + ret = -EBUSY; + goto out; + } + + cam->width = f->fmt.pix.width; + cam->height = f->fmt.pix.height; + dev_info(&cam->udev->dev, "%s: %dx%d mode selected\n", __func__, + cam->width, cam->height); 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; - DBG("ok!"); + cam->vb_vidq.field = f->fmt.pix.field; + cam->mode.color = V4L2_PIX_FMT_JPEG; + + if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) + mode = 1; + else if (f->fmt.pix.width == 640 && f->fmt.pix.height == 480) + mode = 2; + else + mode = 0; + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + for (i = 0; init[cam->method][i].size != -1; i++) { + ret = + send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (ret < 0) { + dev_err(&cam->udev->dev, + "error during resolution change sequence: %d\n", i); + goto out; + } + } + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + cam->skip = 2; + ret = 0; + +out: + mutex_unlock(&q->vb_lock); + + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); + return ret; +} + +static int zr364xx_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + rc = videobuf_reqbufs(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_querybuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + rc = videobuf_querybuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_qbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + rc = videobuf_qbuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_dqbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); + return rc; +} + +static void read_pipe_completion(struct urb *purb) +{ + struct zr364xx_pipeinfo *pipe_info; + struct zr364xx_camera *cam; + int pipe; + + pipe_info = purb->context; + _DBG("%s %p, status %d\n", __func__, purb, purb->status); + if (pipe_info == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + cam = pipe_info->cam; + if (cam == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + /* if shutting down, do not resubmit, exit immediately */ + if (purb->status == -ESHUTDOWN) { + DBG("%s, err shutdown\n", __func__); + pipe_info->err_count++; + return; + } + + if (pipe_info->state == 0) { + DBG("exiting USB pipe\n"); + return; + } + + if (purb->actual_length < 0 || + purb->actual_length > pipe_info->transfer_size) { + dev_err(&cam->udev->dev, "wrong number of bytes\n"); + return; + } + + if (purb->status == 0) + zr364xx_read_video_callback(cam, pipe_info, purb); + else { + pipe_info->err_count++; + DBG("%s: failed URB %d\n", __func__, purb->status); + } + + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + + /* reuse urb */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + if (pipe_info->state != 0) { + purb->status = usb_submit_urb(pipe_info->stream_urb, + GFP_ATOMIC); + + if (purb->status) + dev_err(&cam->udev->dev, + "error submitting urb (error=%i)\n", + purb->status); + } else + DBG("read pipe complete state 0\n"); +} + +static int zr364xx_start_readpipe(struct zr364xx_camera *cam) +{ + int pipe; + int retval; + struct zr364xx_pipeinfo *pipe_info = cam->pipe; + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint); + + pipe_info->state = 1; + pipe_info->err_count = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) { + dev_err(&cam->udev->dev, "ReadStream: Unable to alloc URB\n"); + return -ENOMEM; + } + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + DBG("submitting URB %p\n", pipe_info->stream_urb); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n"); + return retval; + } + + return 0; +} + +static void zr364xx_stop_readpipe(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe_info; + + if (cam == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": invalid device\n"); + return; + } + DBG("stop read pipe\n"); + pipe_info = cam->pipe; + if (pipe_info) { + if (pipe_info->state != 0) + pipe_info->state = 0; + + if (pipe_info->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe_info->stream_urb); + usb_free_urb(pipe_info->stream_urb); + pipe_info->stream_urb = NULL; + } + } + return; +} + +/* starts acquisition process */ +static int zr364xx_start_acquire(struct zr364xx_camera *cam) +{ + int j; + + DBG("start acquire\n"); + + cam->last_frame = -1; + cam->cur_frame = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + cam->b_acquire = 1; + return 0; +} + +static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam) +{ + cam->b_acquire = 0; return 0; } static int zr364xx_vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { - return 0; + struct zr364xx_camera *cam = video_drvdata(file); + int j; + int res; + + DBG("%s\n", __func__); + + if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_err(&cam->udev->dev, "invalid fh type0\n"); + return -EINVAL; + } + if (cam->type != type) { + dev_err(&cam->udev->dev, "invalid fh type1\n"); + return -EINVAL; + } + + if (!res_get(cam)) { + dev_err(&cam->udev->dev, "stream busy\n"); + return -EBUSY; + } + + cam->last_frame = -1; + cam->cur_frame = 0; + cam->frame_count = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + res = videobuf_streamon(&cam->vb_vidq); + if (res == 0) { + zr364xx_start_acquire(cam); + } else { + res_free(cam); + } + return res; } static int zr364xx_vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { + int res; + struct zr364xx_camera *cam = video_drvdata(file); + + DBG("%s\n", __func__); + if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_err(&cam->udev->dev, "invalid fh type0\n"); + return -EINVAL; + } + if (cam->type != type) { + dev_err(&cam->udev->dev, "invalid fh type1\n"); + return -EINVAL; + } + zr364xx_stop_acquire(cam); + res = videobuf_streamoff(&cam->vb_vidq); + if (res < 0) + return res; + res_free(cam); return 0; } @@ -621,28 +1251,19 @@ static int zr364xx_vidioc_streamoff(struct file *file, void *priv, static int zr364xx_open(struct file *file) { struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam = video_get_drvdata(vdev); + struct zr364xx_camera *cam = video_drvdata(file); struct usb_device *udev = cam->udev; int i, err; - DBG("zr364xx_open"); + DBG("%s\n", __func__); - mutex_lock(&cam->lock); + mutex_lock(&cam->open_lock); if (cam->users) { err = -EBUSY; goto out; } - if (!cam->framebuf) { - cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES); - if (!cam->framebuf) { - dev_err(&cam->udev->dev, "vmalloc_32 failed!\n"); - err = -ENOMEM; - goto out; - } - } - for (i = 0; init[cam->method][i].size != -1; i++) { err = send_control_msg(udev, 1, init[cam->method][i].value, @@ -658,6 +1279,14 @@ static int zr364xx_open(struct file *file) cam->skip = 2; cam->users++; file->private_data = vdev; + cam->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->fmt = formats; + + videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, + NULL, &cam->slock, + cam->type, + V4L2_FIELD_NONE, + sizeof(struct zr364xx_buffer), cam); /* Added some delay here, since opening/closing the camera quickly, * like Ekiga does during its startup, can crash the webcam @@ -666,28 +1295,70 @@ static int zr364xx_open(struct file *file) err = 0; out: - mutex_unlock(&cam->lock); + mutex_unlock(&cam->open_lock); + DBG("%s: %d\n", __func__, err); return err; } +static void zr364xx_destroy(struct zr364xx_camera *cam) +{ + unsigned long i; + + if (!cam) { + printk(KERN_ERR KBUILD_MODNAME ", %s: no device\n", __func__); + return; + } + mutex_lock(&cam->open_lock); + if (cam->vdev) + video_unregister_device(cam->vdev); + cam->vdev = NULL; + + /* stops the read pipe if it is running */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + + zr364xx_stop_readpipe(cam); + + /* release sys buffers */ + for (i = 0; i < FRAMES; i++) { + if (cam->buffer.frame[i].lpvbits) { + DBG("vfree %p\n", cam->buffer.frame[i].lpvbits); + vfree(cam->buffer.frame[i].lpvbits); + } + cam->buffer.frame[i].lpvbits = NULL; + } + + /* release transfer buffer */ + kfree(cam->pipe->transfer_buffer); + cam->pipe->transfer_buffer = NULL; + mutex_unlock(&cam->open_lock); + kfree(cam); + cam = NULL; +} /* release the camera */ static int zr364xx_release(struct file *file) { - struct video_device *vdev = video_devdata(file); struct zr364xx_camera *cam; struct usb_device *udev; int i, err; - DBG("zr364xx_release"); + DBG("%s\n", __func__); + cam = video_drvdata(file); - if (vdev == NULL) + if (!cam) return -ENODEV; - cam = video_get_drvdata(vdev); + mutex_lock(&cam->open_lock); udev = cam->udev; - mutex_lock(&cam->lock); + /* turn off stream */ + if (res_check(cam)) { + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + videobuf_streamoff(&cam->vb_vidq); + res_free(cam); + } cam->users--; file->private_data = NULL; @@ -710,40 +1381,43 @@ static int zr364xx_release(struct file *file) err = 0; out: - mutex_unlock(&cam->lock); + mutex_unlock(&cam->open_lock); + return err; } static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) { - void *pos; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - struct video_device *vdev = video_devdata(file); - struct zr364xx_camera *cam; - - DBG("zr364xx_mmap: %ld\n", size); + struct zr364xx_camera *cam = video_drvdata(file); + int ret; - if (vdev == NULL) + if (cam == NULL) { + DBG("%s: cam == NULL\n", __func__); return -ENODEV; - cam = video_get_drvdata(vdev); - - pos = cam->framebuf; - while (size > 0) { - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) - return -EAGAIN; - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; } + DBG("mmap called, vma=0x%08lx\n", (unsigned long)vma); - return 0; + ret = videobuf_mmap_mapper(&cam->vb_vidq, vma); + + DBG("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); + return ret; } +static unsigned int zr364xx_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + _DBG("%s\n", __func__); + + if (cam->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return POLLERR; + + return videobuf_poll_stream(file, q, wait); +} static const struct v4l2_file_operations zr364xx_fops = { .owner = THIS_MODULE, @@ -752,6 +1426,7 @@ static const struct v4l2_file_operations zr364xx_fops = { .read = zr364xx_read, .mmap = zr364xx_mmap, .ioctl = video_ioctl2, + .poll = zr364xx_poll, }; static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { @@ -768,6 +1443,10 @@ static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { .vidioc_queryctrl = zr364xx_vidioc_queryctrl, .vidioc_g_ctrl = zr364xx_vidioc_g_ctrl, .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl, + .vidioc_reqbufs = zr364xx_vidioc_reqbufs, + .vidioc_querybuf = zr364xx_vidioc_querybuf, + .vidioc_qbuf = zr364xx_vidioc_qbuf, + .vidioc_dqbuf = zr364xx_vidioc_dqbuf, }; static struct video_device zr364xx_template = { @@ -783,15 +1462,76 @@ static struct video_device zr364xx_template = { /*******************/ /* USB integration */ /*******************/ +static int zr364xx_board_init(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe = cam->pipe; + unsigned long i; + + DBG("board init: %p\n", cam); + memset(pipe, 0, sizeof(*pipe)); + pipe->cam = cam; + pipe->transfer_size = BUFFER_SIZE; + + pipe->transfer_buffer = kzalloc(pipe->transfer_size, + GFP_KERNEL); + if (pipe->transfer_buffer == NULL) { + DBG("out of memory!\n"); + return -ENOMEM; + } + + cam->b_acquire = 0; + cam->frame_count = 0; + + /*** start create system buffers ***/ + for (i = 0; i < FRAMES; i++) { + /* always allocate maximum size for system buffers */ + cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE); + + DBG("valloc %p, idx %lu, pdata %p\n", + &cam->buffer.frame[i], i, + cam->buffer.frame[i].lpvbits); + if (cam->buffer.frame[i].lpvbits == NULL) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. " + "Using less frames\n"); + break; + } + } + + if (i == 0) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n"); + kfree(cam->pipe->transfer_buffer); + cam->pipe->transfer_buffer = NULL; + return -ENOMEM; + } else + cam->buffer.dwFrames = i; + + /* make sure internal states are set */ + for (i = 0; i < FRAMES; i++) { + cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[i].cur_size = 0; + } + + cam->cur_frame = 0; + cam->last_frame = -1; + /*** end create system buffers ***/ + + /* start read pipe */ + zr364xx_start_readpipe(cam); + DBG(": board initialized\n"); + return 0; +} static int zr364xx_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); struct zr364xx_camera *cam = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; int err; + int i; - DBG("probing..."); + DBG("probing...\n"); dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n"); dev_info(&intf->dev, "model %04x:%04x detected\n", @@ -810,22 +1550,17 @@ static int zr364xx_probe(struct usb_interface *intf, if (cam->vdev == NULL) { dev_err(&udev->dev, "cam->vdev: out of memory !\n"); kfree(cam); + cam = NULL; return -ENOMEM; } memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template)); + cam->vdev->parent = &intf->dev; video_set_drvdata(cam->vdev, cam); if (debug) cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; cam->udev = udev; - if ((cam->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)) == NULL) { - dev_info(&udev->dev, "cam->buffer: out of memory !\n"); - video_device_release(cam->vdev); - kfree(cam); - return -ENODEV; - } - switch (mode) { case 1: dev_info(&udev->dev, "160x120 mode selected\n"); @@ -852,21 +1587,53 @@ static int zr364xx_probe(struct usb_interface *intf, header2[439] = cam->width / 256; header2[440] = cam->width % 256; + cam->users = 0; cam->nb = 0; - cam->brightness = 64; + cam->mode.brightness = 64; mutex_init(&cam->lock); + mutex_init(&cam->open_lock); + + DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf); + + /* set up the endpoint information */ + iface_desc = intf->cur_altsetting; + DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints); + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { + /* we found the bulk in endpoint */ + cam->read_endpoint = endpoint->bEndpointAddress; + } + } + + if (!cam->read_endpoint) { + dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); + return -ENOMEM; + } + /* v4l */ + INIT_LIST_HEAD(&cam->vidq.active); + cam->vidq.cam = cam; err = video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1); if (err) { dev_err(&udev->dev, "video_register_device failed\n"); video_device_release(cam->vdev); - kfree(cam->buffer); kfree(cam); + cam = NULL; return err; } usb_set_intfdata(intf, cam); + /* load zr364xx board specific */ + err = zr364xx_board_init(cam); + if (err) { + spin_lock_init(&cam->slock); + return err; + } + + spin_lock_init(&cam->slock); + dev_info(&udev->dev, DRIVER_DESC " controlling video device %d\n", cam->vdev->num); return 0; @@ -876,17 +1643,10 @@ static int zr364xx_probe(struct usb_interface *intf, static void zr364xx_disconnect(struct usb_interface *intf) { struct zr364xx_camera *cam = usb_get_intfdata(intf); + videobuf_mmap_free(&cam->vb_vidq); usb_set_intfdata(intf, NULL); dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n"); - if (cam->vdev) - video_unregister_device(cam->vdev); - cam->vdev = NULL; - kfree(cam->buffer); - cam->buffer = NULL; - vfree(cam->framebuf); - cam->framebuf = NULL; - kfree(cam); - cam = NULL; + zr364xx_destroy(cam); } |