diff options
Diffstat (limited to 'drivers/media/video')
310 files changed, 27675 insertions, 21736 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 19cf3b8..76bad58 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -249,11 +249,25 @@ config VIDEO_VP27SMPX To compile this driver as a module, choose M here: the module will be called vp27smpx. +comment "RDS decoders" + +config VIDEO_SAA6588 + tristate "SAA6588 Radio Chip RDS decoder support" + depends on VIDEO_V4L2 && I2C + + help + Support for this Radio Data System (RDS) decoder. This allows + seeing radio station identification transmitted using this + standard. + + To compile this driver as a module, choose M here: the + module will be called saa6588. + comment "Video decoders" config VIDEO_BT819 tristate "BT819A VideoStream decoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for BT819A video decoder. @@ -262,7 +276,7 @@ config VIDEO_BT819 config VIDEO_BT856 tristate "BT856 VideoStream decoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for BT856 video decoder. @@ -271,7 +285,7 @@ config VIDEO_BT856 config VIDEO_BT866 tristate "BT866 VideoStream decoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for BT866 video decoder. @@ -280,7 +294,7 @@ config VIDEO_BT866 config VIDEO_KS0127 tristate "KS0127 video decoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for KS0127 video decoder. @@ -307,38 +321,18 @@ config VIDEO_TCM825X config VIDEO_SAA7110 tristate "Philips SAA7110 video decoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for the Philips SAA7110 video decoders. To compile this driver as a module, choose M here: the module will be called saa7110. -config VIDEO_SAA7111 - tristate "Philips SAA7111 video decoder" - depends on VIDEO_V4L1 && I2C - ---help--- - Support for the Philips SAA711 video decoder. - - To compile this driver as a module, choose M here: the - module will be called saa7111. - -config VIDEO_SAA7114 - tristate "Philips SAA7114 video decoder" - depends on VIDEO_V4L1 && I2C - ---help--- - Support for the Philips SAA7114 video decoder. This driver - is used only on Zoran driver and should be moved soon to - SAA711x module. - - To compile this driver as a module, choose M here: the - module will be called saa7114. - config VIDEO_SAA711X - tristate "Philips SAA7113/4/5 video decoders" + tristate "Philips SAA7111/3/4/5 video decoders" depends on VIDEO_V4L2 && I2C ---help--- - Support for the Philips SAA7113/4/5 video decoders. + Support for the Philips SAA7111/3/4/5 video decoders. To compile this driver as a module, choose M here: the module will be called saa7115. @@ -383,7 +377,7 @@ config VIDEO_TVP5150 config VIDEO_VPX3220 tristate "vpx3220a, vpx3216b & vpx3214c video decoders" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for VPX322x video decoders. @@ -421,7 +415,7 @@ config VIDEO_SAA7127 config VIDEO_SAA7185 tristate "Philips SAA7185 video encoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for the Philips SAA7185 video encoder. @@ -430,7 +424,7 @@ config VIDEO_SAA7185 config VIDEO_ADV7170 tristate "Analog Devices ADV7170 video encoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for the Analog Devices ADV7170 video encoder driver @@ -439,7 +433,7 @@ config VIDEO_ADV7170 config VIDEO_ADV7175 tristate "Analog Devices ADV7175 video encoder" - depends on VIDEO_V4L1 && I2C + depends on VIDEO_V4L2 && I2C ---help--- Support for the Analog Devices ADV7175 video encoder driver @@ -487,18 +481,6 @@ config VIDEO_VIVI source "drivers/media/video/bt8xx/Kconfig" -config VIDEO_SAA6588 - tristate "SAA6588 Radio Chip RDS decoder support on BT848 cards" - depends on I2C && VIDEO_BT848 - - help - Support for Radio Data System (RDS) decoder. This allows seeing - radio station identification transmitted using this standard. - Currently, it works only with bt8x8 chips. - - To compile this driver as a module, choose M here: the - module will be called saa6588. - config VIDEO_PMS tristate "Mediavision Pro Movie Studio Video For Linux" depends on ISA && VIDEO_V4L1 @@ -602,7 +584,6 @@ config VIDEO_SAA5249 config VIDEO_VINO tristate "SGI Vino Video For Linux (EXPERIMENTAL)" depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2 - select I2C_ALGO_SGI select VIDEO_SAA7191 if VIDEO_HELPER_CHIPS_AUTO help Say Y here to build in support for the Vino video input system found @@ -639,7 +620,7 @@ config VIDEO_MXB depends on PCI && VIDEO_V4L1 && I2C select VIDEO_SAA7146_VV select VIDEO_TUNER - select VIDEO_SAA7115 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TEA6420 if VIDEO_HELPER_CHIPS_AUTO @@ -728,13 +709,6 @@ config SOC_CAMERA_MT9M001 This driver supports MT9M001 cameras from Micron, monochrome and colour models. -config MT9M001_PCA9536_SWITCH - bool "pca9536 datawidth switch for mt9m001" - depends on SOC_CAMERA_MT9M001 && GENERIC_GPIO - help - Select this if your MT9M001 camera uses a PCA9536 I2C GPIO - extender to switch between 8 and 10 bit datawidth modes - config SOC_CAMERA_MT9M111 tristate "mt9m111 and mt9m112 support" depends on SOC_CAMERA && I2C @@ -754,13 +728,6 @@ config SOC_CAMERA_MT9V022 help This driver supports MT9V022 cameras from Micron -config MT9V022_PCA9536_SWITCH - bool "pca9536 datawidth switch for mt9v022" - depends on SOC_CAMERA_MT9V022 && GENERIC_GPIO - help - Select this if your MT9V022 camera uses a PCA9536 I2C GPIO - extender to switch between 8 and 10 bit datawidth modes - config SOC_CAMERA_TW9910 tristate "tw9910 support" depends on SOC_CAMERA && I2C @@ -779,6 +746,13 @@ config SOC_CAMERA_OV772X help This is a ov772x camera driver +config VIDEO_MX3 + tristate "i.MX3x Camera Sensor Interface driver" + depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA + select VIDEOBUF_DMA_CONTIG + ---help--- + This is a v4l2 driver for the i.MX3x Camera Sensor Interface + config VIDEO_PXA27x tristate "PXA27x Quick Capture Interface driver" depends on VIDEO_DEV && PXA27x && SOC_CAMERA @@ -817,6 +791,8 @@ source "drivers/media/video/gspca/Kconfig" source "drivers/media/video/pvrusb2/Kconfig" +source "drivers/media/video/hdpvr/Kconfig" + source "drivers/media/video/em28xx/Kconfig" source "drivers/media/video/usbvision/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 72f6d03..b904674 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o -obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o @@ -43,8 +42,6 @@ obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o -obj-$(CONFIG_VIDEO_SAA7111) += saa7111.o -obj-$(CONFIG_VIDEO_SAA7114) += saa7114.o obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o @@ -122,6 +119,8 @@ obj-$(CONFIG_USB_PWC) += pwc/ obj-$(CONFIG_USB_ZC0301) += zc0301/ obj-$(CONFIG_USB_GSPCA) += gspca/ +obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ + obj-$(CONFIG_USB_IBMCAM) += usbvideo/ obj-$(CONFIG_USB_KONICAWC) += usbvideo/ obj-$(CONFIG_USB_VICAM) += usbvideo/ @@ -134,10 +133,11 @@ obj-$(CONFIG_VIDEO_CX18) += cx18/ obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ -obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o +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_VIDEO_OMAP2) += omap2cam.o -obj-$(CONFIG_SOC_CAMERA) += soc_camera.o +obj-$(CONFIG_SOC_CAMERA) += soc_camera.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c index e0eb4f3..873c30a 100644 --- a/drivers/media/video/adv7170.c +++ b/drivers/media/video/adv7170.c @@ -34,15 +34,16 @@ #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> -#include <linux/video_encoder.h> -#include <media/v4l2-common.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver"); MODULE_AUTHOR("Maxim Yevtyushkin"); MODULE_LICENSE("GPL"); + static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); @@ -50,38 +51,43 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); /* ----------------------------------------------------------------------- */ struct adv7170 { + struct v4l2_subdev sd; unsigned char reg[128]; - int norm; + v4l2_std_id norm; int input; - int enable; - int bright; - int contrast; - int hue; - int sat; }; +static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7170, sd); +} + static char *inputs[] = { "pass_through", "play_back" }; -static char *norms[] = { "PAL", "NTSC" }; /* ----------------------------------------------------------------------- */ -static inline int adv7170_write(struct i2c_client *client, u8 reg, u8 value) +static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value) { - struct adv7170 *encoder = i2c_get_clientdata(client); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7170 *encoder = to_adv7170(sd); encoder->reg[reg] = value; return i2c_smbus_write_byte_data(client, reg, value); } -static inline int adv7170_read(struct i2c_client *client, u8 reg) +static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } -static int adv7170_write_block(struct i2c_client *client, +static int adv7170_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7170 *encoder = to_adv7170(sd); int ret = -1; u8 reg; @@ -89,7 +95,6 @@ static int adv7170_write_block(struct i2c_client *client, * the adapter understands raw I2C */ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { /* do raw I2C, not smbus compatible */ - struct adv7170 *encoder = i2c_get_clientdata(client); u8 block_data[32]; int block_len; @@ -110,7 +115,7 @@ static int adv7170_write_block(struct i2c_client *client, /* do some slow I2C emulation kind of thing */ while (len >= 2) { reg = *data++; - ret = adv7170_write(client, reg, *data++); + ret = adv7170_write(sd, reg, *data++); if (ret < 0) break; len -= 2; @@ -128,203 +133,161 @@ static int adv7170_write_block(struct i2c_client *client, #define TR1PLAY 0x00 static const unsigned char init_NTSC[] = { - 0x00, 0x10, // MR0 - 0x01, 0x20, // MR1 - 0x02, 0x0e, // MR2 RTC control: bits 2 and 1 - 0x03, 0x80, // MR3 - 0x04, 0x30, // MR4 - 0x05, 0x00, // Reserved - 0x06, 0x00, // Reserved - 0x07, TR0MODE, // TM0 - 0x08, TR1CAPT, // TM1 - 0x09, 0x16, // Fsc0 - 0x0a, 0x7c, // Fsc1 - 0x0b, 0xf0, // Fsc2 - 0x0c, 0x21, // Fsc3 - 0x0d, 0x00, // Subcarrier Phase - 0x0e, 0x00, // Closed Capt. Ext 0 - 0x0f, 0x00, // Closed Capt. Ext 1 - 0x10, 0x00, // Closed Capt. 0 - 0x11, 0x00, // Closed Capt. 1 - 0x12, 0x00, // Pedestal Ctl 0 - 0x13, 0x00, // Pedestal Ctl 1 - 0x14, 0x00, // Pedestal Ctl 2 - 0x15, 0x00, // Pedestal Ctl 3 - 0x16, 0x00, // CGMS_WSS_0 - 0x17, 0x00, // CGMS_WSS_1 - 0x18, 0x00, // CGMS_WSS_2 - 0x19, 0x00, // Teletext Ctl + 0x00, 0x10, /* MR0 */ + 0x01, 0x20, /* MR1 */ + 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ + 0x03, 0x80, /* MR3 */ + 0x04, 0x30, /* MR4 */ + 0x05, 0x00, /* Reserved */ + 0x06, 0x00, /* Reserved */ + 0x07, TR0MODE, /* TM0 */ + 0x08, TR1CAPT, /* TM1 */ + 0x09, 0x16, /* Fsc0 */ + 0x0a, 0x7c, /* Fsc1 */ + 0x0b, 0xf0, /* Fsc2 */ + 0x0c, 0x21, /* Fsc3 */ + 0x0d, 0x00, /* Subcarrier Phase */ + 0x0e, 0x00, /* Closed Capt. Ext 0 */ + 0x0f, 0x00, /* Closed Capt. Ext 1 */ + 0x10, 0x00, /* Closed Capt. 0 */ + 0x11, 0x00, /* Closed Capt. 1 */ + 0x12, 0x00, /* Pedestal Ctl 0 */ + 0x13, 0x00, /* Pedestal Ctl 1 */ + 0x14, 0x00, /* Pedestal Ctl 2 */ + 0x15, 0x00, /* Pedestal Ctl 3 */ + 0x16, 0x00, /* CGMS_WSS_0 */ + 0x17, 0x00, /* CGMS_WSS_1 */ + 0x18, 0x00, /* CGMS_WSS_2 */ + 0x19, 0x00, /* Teletext Ctl */ }; static const unsigned char init_PAL[] = { - 0x00, 0x71, // MR0 - 0x01, 0x20, // MR1 - 0x02, 0x0e, // MR2 RTC control: bits 2 and 1 - 0x03, 0x80, // MR3 - 0x04, 0x30, // MR4 - 0x05, 0x00, // Reserved - 0x06, 0x00, // Reserved - 0x07, TR0MODE, // TM0 - 0x08, TR1CAPT, // TM1 - 0x09, 0xcb, // Fsc0 - 0x0a, 0x8a, // Fsc1 - 0x0b, 0x09, // Fsc2 - 0x0c, 0x2a, // Fsc3 - 0x0d, 0x00, // Subcarrier Phase - 0x0e, 0x00, // Closed Capt. Ext 0 - 0x0f, 0x00, // Closed Capt. Ext 1 - 0x10, 0x00, // Closed Capt. 0 - 0x11, 0x00, // Closed Capt. 1 - 0x12, 0x00, // Pedestal Ctl 0 - 0x13, 0x00, // Pedestal Ctl 1 - 0x14, 0x00, // Pedestal Ctl 2 - 0x15, 0x00, // Pedestal Ctl 3 - 0x16, 0x00, // CGMS_WSS_0 - 0x17, 0x00, // CGMS_WSS_1 - 0x18, 0x00, // CGMS_WSS_2 - 0x19, 0x00, // Teletext Ctl + 0x00, 0x71, /* MR0 */ + 0x01, 0x20, /* MR1 */ + 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */ + 0x03, 0x80, /* MR3 */ + 0x04, 0x30, /* MR4 */ + 0x05, 0x00, /* Reserved */ + 0x06, 0x00, /* Reserved */ + 0x07, TR0MODE, /* TM0 */ + 0x08, TR1CAPT, /* TM1 */ + 0x09, 0xcb, /* Fsc0 */ + 0x0a, 0x8a, /* Fsc1 */ + 0x0b, 0x09, /* Fsc2 */ + 0x0c, 0x2a, /* Fsc3 */ + 0x0d, 0x00, /* Subcarrier Phase */ + 0x0e, 0x00, /* Closed Capt. Ext 0 */ + 0x0f, 0x00, /* Closed Capt. Ext 1 */ + 0x10, 0x00, /* Closed Capt. 0 */ + 0x11, 0x00, /* Closed Capt. 1 */ + 0x12, 0x00, /* Pedestal Ctl 0 */ + 0x13, 0x00, /* Pedestal Ctl 1 */ + 0x14, 0x00, /* Pedestal Ctl 2 */ + 0x15, 0x00, /* Pedestal Ctl 3 */ + 0x16, 0x00, /* CGMS_WSS_0 */ + 0x17, 0x00, /* CGMS_WSS_1 */ + 0x18, 0x00, /* CGMS_WSS_2 */ + 0x19, 0x00, /* Teletext Ctl */ }; -static int adv7170_command(struct i2c_client *client, unsigned cmd, void *arg) +static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) { - struct adv7170 *encoder = i2c_get_clientdata(client); - - switch (cmd) { - case 0: -#if 0 - /* This is just for testing!!! */ - adv7170_write_block(client, init_common, - sizeof(init_common)); - adv7170_write(client, 0x07, TR0MODE | TR0RST); - adv7170_write(client, 0x07, TR0MODE); -#endif - break; - - case ENCODER_GET_CAPABILITIES: - { - struct video_encoder_capability *cap = arg; - - cap->flags = VIDEO_ENCODER_PAL | - VIDEO_ENCODER_NTSC; - cap->inputs = 2; - cap->outputs = 1; - break; + struct adv7170 *encoder = to_adv7170(sd); + + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); + + if (std & V4L2_STD_NTSC) { + adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); + if (encoder->input == 0) + adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + } else if (std & V4L2_STD_PAL) { + adv7170_write_block(sd, init_PAL, sizeof(init_PAL)); + if (encoder->input == 0) + adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + } else { + v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", + (unsigned long long)std); + return -EINVAL; } + v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); + encoder->norm = std; + return 0; +} - case ENCODER_SET_NORM: - { - int iarg = *(int *) arg; - - v4l_dbg(1, debug, client, "set norm %d\n", iarg); - - switch (iarg) { - case VIDEO_MODE_NTSC: - adv7170_write_block(client, init_NTSC, - sizeof(init_NTSC)); - if (encoder->input == 0) - adv7170_write(client, 0x02, 0x0e); // Enable genlock - adv7170_write(client, 0x07, TR0MODE | TR0RST); - adv7170_write(client, 0x07, TR0MODE); - break; - - case VIDEO_MODE_PAL: - adv7170_write_block(client, init_PAL, - sizeof(init_PAL)); - if (encoder->input == 0) - adv7170_write(client, 0x02, 0x0e); // Enable genlock - adv7170_write(client, 0x07, TR0MODE | TR0RST); - adv7170_write(client, 0x07, TR0MODE); - break; - - default: - v4l_dbg(1, debug, client, "illegal norm: %d\n", iarg); - return -EINVAL; - } - v4l_dbg(1, debug, client, "switched to %s\n", norms[iarg]); - encoder->norm = iarg; - break; - } +static int adv7170_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct adv7170 *encoder = to_adv7170(sd); - case ENCODER_SET_INPUT: - { - int iarg = *(int *) arg; - - /* RJ: *iarg = 0: input is from decoder - *iarg = 1: input is from ZR36060 - *iarg = 2: color bar */ - - v4l_dbg(1, debug, client, "set input from %s\n", - iarg == 0 ? "decoder" : "ZR36060"); - - switch (iarg) { - case 0: - adv7170_write(client, 0x01, 0x20); - adv7170_write(client, 0x08, TR1CAPT); /* TR1 */ - adv7170_write(client, 0x02, 0x0e); // Enable genlock - adv7170_write(client, 0x07, TR0MODE | TR0RST); - adv7170_write(client, 0x07, TR0MODE); - /* udelay(10); */ - break; - - case 1: - adv7170_write(client, 0x01, 0x00); - adv7170_write(client, 0x08, TR1PLAY); /* TR1 */ - adv7170_write(client, 0x02, 0x08); - adv7170_write(client, 0x07, TR0MODE | TR0RST); - adv7170_write(client, 0x07, TR0MODE); - /* udelay(10); */ - break; - - default: - v4l_dbg(1, debug, client, "illegal input: %d\n", iarg); - return -EINVAL; - } - v4l_dbg(1, debug, client, "switched to %s\n", inputs[iarg]); - encoder->input = iarg; - break; - } + /* RJ: route->input = 0: input is from decoder + route->input = 1: input is from ZR36060 + route->input = 2: color bar */ - case ENCODER_SET_OUTPUT: - { - int *iarg = arg; + v4l2_dbg(1, debug, sd, "set input from %s\n", + route->input == 0 ? "decoder" : "ZR36060"); - /* not much choice of outputs */ - if (*iarg != 0) { - return -EINVAL; - } + switch (route->input) { + case 0: + adv7170_write(sd, 0x01, 0x20); + adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */ + adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */ + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + /* udelay(10); */ break; - } - - case ENCODER_ENABLE_OUTPUT: - { - int *iarg = arg; - encoder->enable = !!*iarg; + case 1: + adv7170_write(sd, 0x01, 0x00); + adv7170_write(sd, 0x08, TR1PLAY); /* TR1 */ + adv7170_write(sd, 0x02, 0x08); + adv7170_write(sd, 0x07, TR0MODE | TR0RST); + adv7170_write(sd, 0x07, TR0MODE); + /* udelay(10); */ break; - } default: + v4l2_dbg(1, debug, sd, "illegal input: %d\n", route->input); return -EINVAL; } - + v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[route->input]); + encoder->input = route->input; return 0; } +static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0); +} + /* ----------------------------------------------------------------------- */ -static unsigned short normal_i2c[] = { - 0xd4 >> 1, 0xd6 >> 1, /* adv7170 IDs */ - 0x54 >> 1, 0x56 >> 1, /* adv7171 IDs */ - I2C_CLIENT_END +static const struct v4l2_subdev_core_ops adv7170_core_ops = { + .g_chip_ident = adv7170_g_chip_ident, }; -I2C_CLIENT_INSMOD; +static const struct v4l2_subdev_video_ops adv7170_video_ops = { + .s_std_output = adv7170_s_std_output, + .s_routing = adv7170_s_routing, +}; + +static const struct v4l2_subdev_ops adv7170_ops = { + .core = &adv7170_core_ops, + .video = &adv7170_video_ops, +}; + +/* ----------------------------------------------------------------------- */ static int adv7170_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct adv7170 *encoder; + struct v4l2_subdev *sd; int i; /* Check if the adapter supports the needed features */ @@ -337,26 +300,29 @@ static int adv7170_probe(struct i2c_client *client, encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL); if (encoder == NULL) return -ENOMEM; - encoder->norm = VIDEO_MODE_NTSC; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &adv7170_ops); + encoder->norm = V4L2_STD_NTSC; encoder->input = 0; - encoder->enable = 1; - i2c_set_clientdata(client, encoder); - i = adv7170_write_block(client, init_NTSC, sizeof(init_NTSC)); + i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC)); if (i >= 0) { - i = adv7170_write(client, 0x07, TR0MODE | TR0RST); - i = adv7170_write(client, 0x07, TR0MODE); - i = adv7170_read(client, 0x12); - v4l_dbg(1, debug, client, "revision %d\n", i & 1); + i = adv7170_write(sd, 0x07, TR0MODE | TR0RST); + i = adv7170_write(sd, 0x07, TR0MODE); + i = adv7170_read(sd, 0x12); + v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); } if (i < 0) - v4l_dbg(1, debug, client, "init error 0x%x\n", i); + v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); return 0; } static int adv7170_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_adv7170(sd)); return 0; } @@ -371,8 +337,6 @@ MODULE_DEVICE_TABLE(i2c, adv7170_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "adv7170", - .driverid = I2C_DRIVERID_ADV7170, - .command = adv7170_command, .probe = adv7170_probe, .remove = adv7170_remove, .id_table = adv7170_id, diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c index 6008e84..ff12103 100644 --- a/drivers/media/video/adv7175.c +++ b/drivers/media/video/adv7175.c @@ -30,15 +30,19 @@ #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> -#include <linux/video_encoder.h> -#include <media/v4l2-common.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver"); MODULE_AUTHOR("Dave Perks"); MODULE_LICENSE("GPL"); +#define I2C_ADV7175 0xd4 +#define I2C_ADV7176 0x54 + + static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); @@ -46,36 +50,38 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); /* ----------------------------------------------------------------------- */ struct adv7175 { - int norm; + struct v4l2_subdev sd; + v4l2_std_id norm; int input; - int enable; - int bright; - int contrast; - int hue; - int sat; }; -#define I2C_ADV7175 0xd4 -#define I2C_ADV7176 0x54 +static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7175, sd); +} static char *inputs[] = { "pass_through", "play_back", "color_bar" }; -static char *norms[] = { "PAL", "NTSC", "SECAM->PAL (may not work!)" }; /* ----------------------------------------------------------------------- */ -static inline int adv7175_write(struct i2c_client *client, u8 reg, u8 value) +static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_write_byte_data(client, reg, value); } -static inline int adv7175_read(struct i2c_client *client, u8 reg) +static inline int adv7175_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } -static int adv7175_write_block(struct i2c_client *client, +static int adv7175_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = -1; u8 reg; @@ -103,7 +109,7 @@ static int adv7175_write_block(struct i2c_client *client, /* do some slow I2C emulation kind of thing */ while (len >= 2) { reg = *data++; - ret = adv7175_write(client, reg, *data++); + ret = adv7175_write(sd, reg, *data++); if (ret < 0) break; len -= 2; @@ -113,18 +119,18 @@ static int adv7175_write_block(struct i2c_client *client, return ret; } -static void set_subcarrier_freq(struct i2c_client *client, int pass_through) +static void set_subcarrier_freq(struct v4l2_subdev *sd, int pass_through) { /* for some reason pass_through NTSC needs * a different sub-carrier freq to remain stable. */ if (pass_through) - adv7175_write(client, 0x02, 0x00); + adv7175_write(sd, 0x02, 0x00); else - adv7175_write(client, 0x02, 0x55); + adv7175_write(sd, 0x02, 0x55); - adv7175_write(client, 0x03, 0x55); - adv7175_write(client, 0x04, 0x55); - adv7175_write(client, 0x05, 0x25); + adv7175_write(sd, 0x03, 0x55); + adv7175_write(sd, 0x04, 0x55); + adv7175_write(sd, 0x05, 0x25); } /* ----------------------------------------------------------------------- */ @@ -184,180 +190,144 @@ static const unsigned char init_ntsc[] = { 0x06, 0x1a, /* subc. phase */ }; -static int adv7175_command(struct i2c_client *client, unsigned cmd, void *arg) +static int adv7175_init(struct v4l2_subdev *sd, u32 val) { - struct adv7175 *encoder = i2c_get_clientdata(client); + /* This is just for testing!!! */ + adv7175_write_block(sd, init_common, sizeof(init_common)); + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + return 0; +} - switch (cmd) { - case 0: - /* This is just for testing!!! */ - adv7175_write_block(client, init_common, - sizeof(init_common)); - adv7175_write(client, 0x07, TR0MODE | TR0RST); - adv7175_write(client, 0x07, TR0MODE); - break; +static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7175 *encoder = to_adv7175(sd); + + if (std & V4L2_STD_NTSC) { + adv7175_write_block(sd, init_ntsc, sizeof(init_ntsc)); + if (encoder->input == 0) + adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + } else if (std & V4L2_STD_PAL) { + adv7175_write_block(sd, init_pal, sizeof(init_pal)); + if (encoder->input == 0) + adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + } else if (std & V4L2_STD_SECAM) { + /* This is an attempt to convert + * SECAM->PAL (typically it does not work + * due to genlock: when decoder is in SECAM + * and encoder in in PAL the subcarrier can + * not be syncronized with horizontal + * quency) */ + adv7175_write_block(sd, init_pal, sizeof(init_pal)); + if (encoder->input == 0) + adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + } else { + v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std); + encoder->norm = std; + return 0; +} - case ENCODER_GET_CAPABILITIES: - { - struct video_encoder_capability *cap = arg; +static int adv7175_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct adv7175 *encoder = to_adv7175(sd); - cap->flags = VIDEO_ENCODER_PAL | - VIDEO_ENCODER_NTSC | - VIDEO_ENCODER_SECAM; /* well, hacky */ - cap->inputs = 2; - cap->outputs = 1; - break; - } + /* RJ: route->input = 0: input is from decoder + route->input = 1: input is from ZR36060 + route->input = 2: color bar */ - case ENCODER_SET_NORM: - { - int iarg = *(int *) arg; - - switch (iarg) { - case VIDEO_MODE_NTSC: - adv7175_write_block(client, init_ntsc, - sizeof(init_ntsc)); - if (encoder->input == 0) - adv7175_write(client, 0x0d, 0x4f); // Enable genlock - adv7175_write(client, 0x07, TR0MODE | TR0RST); - adv7175_write(client, 0x07, TR0MODE); - break; - - case VIDEO_MODE_PAL: - adv7175_write_block(client, init_pal, - sizeof(init_pal)); - if (encoder->input == 0) - adv7175_write(client, 0x0d, 0x4f); // Enable genlock - adv7175_write(client, 0x07, TR0MODE | TR0RST); - adv7175_write(client, 0x07, TR0MODE); - break; - - case VIDEO_MODE_SECAM: // WARNING! ADV7176 does not support SECAM. - /* This is an attempt to convert - * SECAM->PAL (typically it does not work - * due to genlock: when decoder is in SECAM - * and encoder in in PAL the subcarrier can - * not be syncronized with horizontal - * quency) */ - adv7175_write_block(client, init_pal, - sizeof(init_pal)); - if (encoder->input == 0) - adv7175_write(client, 0x0d, 0x49); // Disable genlock - adv7175_write(client, 0x07, TR0MODE | TR0RST); - adv7175_write(client, 0x07, TR0MODE); - break; - default: - v4l_dbg(1, debug, client, "illegal norm: %d\n", iarg); - return -EINVAL; - } - v4l_dbg(1, debug, client, "switched to %s\n", norms[iarg]); - encoder->norm = iarg; + switch (route->input) { + case 0: + adv7175_write(sd, 0x01, 0x00); + + if (encoder->norm & V4L2_STD_NTSC) + set_subcarrier_freq(sd, 1); + + adv7175_write(sd, 0x0c, TR1CAPT); /* TR1 */ + if (encoder->norm & V4L2_STD_SECAM) + adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ + else + adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + /*udelay(10);*/ break; - } - case ENCODER_SET_INPUT: - { - int iarg = *(int *) arg; - - /* RJ: *iarg = 0: input is from SAA7110 - *iarg = 1: input is from ZR36060 - *iarg = 2: color bar */ - - switch (iarg) { - case 0: - adv7175_write(client, 0x01, 0x00); - - if (encoder->norm == VIDEO_MODE_NTSC) - set_subcarrier_freq(client, 1); - - adv7175_write(client, 0x0c, TR1CAPT); /* TR1 */ - if (encoder->norm == VIDEO_MODE_SECAM) - adv7175_write(client, 0x0d, 0x49); // Disable genlock - else - adv7175_write(client, 0x0d, 0x4f); // Enable genlock - adv7175_write(client, 0x07, TR0MODE | TR0RST); - adv7175_write(client, 0x07, TR0MODE); - //udelay(10); - break; - - case 1: - adv7175_write(client, 0x01, 0x00); - - if (encoder->norm == VIDEO_MODE_NTSC) - set_subcarrier_freq(client, 0); - - adv7175_write(client, 0x0c, TR1PLAY); /* TR1 */ - adv7175_write(client, 0x0d, 0x49); - adv7175_write(client, 0x07, TR0MODE | TR0RST); - adv7175_write(client, 0x07, TR0MODE); - /* udelay(10); */ - break; - - case 2: - adv7175_write(client, 0x01, 0x80); - - if (encoder->norm == VIDEO_MODE_NTSC) - set_subcarrier_freq(client, 0); - - adv7175_write(client, 0x0d, 0x49); - adv7175_write(client, 0x07, TR0MODE | TR0RST); - adv7175_write(client, 0x07, TR0MODE); - /* udelay(10); */ - break; - - default: - v4l_dbg(1, debug, client, "illegal input: %d\n", iarg); - return -EINVAL; - } - v4l_dbg(1, debug, client, "switched to %s\n", inputs[iarg]); - encoder->input = iarg; - break; - } + case 1: + adv7175_write(sd, 0x01, 0x00); - case ENCODER_SET_OUTPUT: - { - int *iarg = arg; + if (encoder->norm & V4L2_STD_NTSC) + set_subcarrier_freq(sd, 0); - /* not much choice of outputs */ - if (*iarg != 0) - return -EINVAL; + adv7175_write(sd, 0x0c, TR1PLAY); /* TR1 */ + adv7175_write(sd, 0x0d, 0x49); + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + /* udelay(10); */ break; - } - case ENCODER_ENABLE_OUTPUT: - { - int *iarg = arg; + case 2: + adv7175_write(sd, 0x01, 0x80); + + if (encoder->norm & V4L2_STD_NTSC) + set_subcarrier_freq(sd, 0); - encoder->enable = !!*iarg; + adv7175_write(sd, 0x0d, 0x49); + adv7175_write(sd, 0x07, TR0MODE | TR0RST); + adv7175_write(sd, 0x07, TR0MODE); + /* udelay(10); */ break; - } default: + v4l2_dbg(1, debug, sd, "illegal input: %d\n", route->input); return -EINVAL; } - + v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[route->input]); + encoder->input = route->input; return 0; } +static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0); +} + /* ----------------------------------------------------------------------- */ -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ -static unsigned short normal_i2c[] = { - I2C_ADV7175 >> 1, (I2C_ADV7175 >> 1) + 1, - I2C_ADV7176 >> 1, (I2C_ADV7176 >> 1) + 1, - I2C_CLIENT_END +static const struct v4l2_subdev_core_ops adv7175_core_ops = { + .g_chip_ident = adv7175_g_chip_ident, + .init = adv7175_init, }; -I2C_CLIENT_INSMOD; +static const struct v4l2_subdev_video_ops adv7175_video_ops = { + .s_std_output = adv7175_s_std_output, + .s_routing = adv7175_s_routing, +}; + +static const struct v4l2_subdev_ops adv7175_ops = { + .core = &adv7175_core_ops, + .video = &adv7175_video_ops, +}; + +/* ----------------------------------------------------------------------- */ static int adv7175_probe(struct i2c_client *client, const struct i2c_device_id *id) { int i; struct adv7175 *encoder; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -369,26 +339,29 @@ static int adv7175_probe(struct i2c_client *client, encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL); if (encoder == NULL) return -ENOMEM; - encoder->norm = VIDEO_MODE_PAL; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &adv7175_ops); + encoder->norm = V4L2_STD_NTSC; encoder->input = 0; - encoder->enable = 1; - i2c_set_clientdata(client, encoder); - i = adv7175_write_block(client, init_common, sizeof(init_common)); + i = adv7175_write_block(sd, init_common, sizeof(init_common)); if (i >= 0) { - i = adv7175_write(client, 0x07, TR0MODE | TR0RST); - i = adv7175_write(client, 0x07, TR0MODE); - i = adv7175_read(client, 0x12); - v4l_dbg(1, debug, client, "revision %d\n", i & 1); + i = adv7175_write(sd, 0x07, TR0MODE | TR0RST); + i = adv7175_write(sd, 0x07, TR0MODE); + i = adv7175_read(sd, 0x12); + v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); } if (i < 0) - v4l_dbg(1, debug, client, "init error 0x%x\n", i); + v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); return 0; } static int adv7175_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_adv7175(sd)); return 0; } @@ -403,8 +376,6 @@ MODULE_DEVICE_TABLE(i2c, adv7175_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "adv7175", - .driverid = I2C_DRIVERID_ADV7175, - .command = adv7175_command, .probe = adv7175_probe, .remove = adv7175_remove, .id_table = adv7175_id, diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig index 018f72b..05cdf49 100644 --- a/drivers/media/video/au0828/Kconfig +++ b/drivers/media/video/au0828/Kconfig @@ -1,13 +1,13 @@ config VIDEO_AU0828 tristate "Auvitek AU0828 support" - depends on I2C && INPUT && DVB_CORE && USB + depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 select I2C_ALGOBIT select VIDEO_TVEEPROM - select DVB_AU8522 if !DVB_FE_CUSTOMIZE - select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE - select MEDIA_TUNER_MXL5007T if !DVB_FE_CUSTOMIZE - select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE + select DVB_AU8522 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE ---help--- This is a video4linux driver for Auvitek's USB device. diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile index cd2c582..4d26231 100644 --- a/drivers/media/video/au0828/Makefile +++ b/drivers/media/video/au0828/Makefile @@ -1,4 +1,4 @@ -au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o obj-$(CONFIG_VIDEO_AU0828) += au0828.o diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c index d60123b..1aabaa7 100644 --- a/drivers/media/video/au0828/au0828-cards.c +++ b/drivers/media/video/au0828/au0828-cards.c @@ -21,25 +21,89 @@ #include "au0828.h" #include "au0828-cards.h" +#include "au8522.h" +#include "media/tuner.h" +#include "media/v4l2-common.h" + +void hvr950q_cs5340_audio(void *priv, int enable) +{ + /* Because the HVR-950q shares an i2s bus between the cs5340 and the + au8522, we need to hold cs5340 in reset when using the au8522 */ + struct au0828_dev *dev = priv; + if (enable == 1) + au0828_set(dev, REG_000, 0x10); + else + au0828_clear(dev, REG_000, 0x10); +} struct au0828_board au0828_boards[] = { [AU0828_BOARD_UNKNOWN] = { .name = "Unknown board", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, }, [AU0828_BOARD_HAUPPAUGE_HVR850] = { .name = "Hauppauge HVR850", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .input = { + { + .type = AU0828_VMUX_TELEVISION, + .vmux = AU8522_COMPOSITE_CH4_SIF, + .amux = AU8522_AUDIO_SIF, + }, + { + .type = AU0828_VMUX_COMPOSITE, + .vmux = AU8522_COMPOSITE_CH1, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + { + .type = AU0828_VMUX_SVIDEO, + .vmux = AU8522_SVIDEO_CH13, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + }, }, [AU0828_BOARD_HAUPPAUGE_HVR950Q] = { .name = "Hauppauge HVR950Q", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .input = { + { + .type = AU0828_VMUX_TELEVISION, + .vmux = AU8522_COMPOSITE_CH4_SIF, + .amux = AU8522_AUDIO_SIF, + }, + { + .type = AU0828_VMUX_COMPOSITE, + .vmux = AU8522_COMPOSITE_CH1, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + { + .type = AU0828_VMUX_SVIDEO, + .vmux = AU8522_SVIDEO_CH13, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + }, }, [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = { .name = "Hauppauge HVR950Q rev xxF8", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, }, [AU0828_BOARD_DVICO_FUSIONHDTV7] = { .name = "DViCO FusionHDTV USB", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, }, [AU0828_BOARD_HAUPPAUGE_WOODBURY] = { .name = "Hauppauge Woodbury", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, }, }; @@ -52,7 +116,7 @@ int au0828_tuner_callback(void *priv, int component, int command, int arg) dprintk(1, "%s()\n", __func__); - switch (dev->board) { + switch (dev->boardnr) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: @@ -81,17 +145,18 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) struct tveeprom tv; tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data); + dev->board.tuner_type = tv.tuner_type; /* Make sure we support the board model */ switch (tv.model) { case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */ - case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */ - case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ - case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ - case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ - case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and basic analog video */ - case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */ - case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and basic analog video */ + case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ + case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ + case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ + case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; default: @@ -107,15 +172,21 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) void au0828_card_setup(struct au0828_dev *dev) { static u8 eeprom[256]; + struct tuner_setup tun_setup; + struct v4l2_subdev *sd; + unsigned int mode_mask = T_ANALOG_TV | + T_DIGITAL_TV; dprintk(1, "%s()\n", __func__); + memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board)); + if (dev->i2c_rc == 0) { dev->i2c_client.addr = 0xa0 >> 1; tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom)); } - switch (dev->board) { + switch (dev->boardnr) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: @@ -124,6 +195,32 @@ void au0828_card_setup(struct au0828_dev *dev) hauppauge_eeprom(dev, eeprom+0xa0); break; } + + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { + /* Load the analog demodulator driver (note this would need to + be abstracted out if we ever need to support a different + demod) */ + sd = v4l2_i2c_new_subdev(&dev->i2c_adap, "au8522", "au8522", + 0x8e >> 1); + if (sd == NULL) + printk(KERN_ERR "analog subdev registration failed\n"); + } + + /* Setup tuners */ + if (dev->board.tuner_type != TUNER_ABSENT) { + /* Load the tuner module, which does the attach */ + sd = v4l2_i2c_new_subdev(&dev->i2c_adap, "tuner", "tuner", + dev->board.tuner_addr); + if (sd == NULL) + printk(KERN_ERR "tuner subdev registration fail\n"); + + tun_setup.mode_mask = mode_mask; + tun_setup.type = dev->board.tuner_type; + tun_setup.addr = dev->board.tuner_addr; + tun_setup.tuner_callback = au0828_tuner_callback; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, + &tun_setup); + } } /* @@ -135,7 +232,7 @@ void au0828_gpio_setup(struct au0828_dev *dev) { dprintk(1, "%s()\n", __func__); - switch (dev->board) { + switch (dev->boardnr) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: @@ -144,21 +241,23 @@ void au0828_gpio_setup(struct au0828_dev *dev) * 4 - CS5340 * 5 - AU8522 Demodulator * 6 - eeprom W/P + * 7 - power supply * 9 - XC5000 Tuner */ /* Into reset */ au0828_write(dev, REG_003, 0x02); - au0828_write(dev, REG_002, 0x88 | 0x20); + au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); au0828_write(dev, REG_001, 0x0); au0828_write(dev, REG_000, 0x0); msleep(100); - /* Out of reset */ + /* Out of reset (leave the cs5340 in reset until needed) */ au0828_write(dev, REG_003, 0x02); au0828_write(dev, REG_001, 0x02); - au0828_write(dev, REG_002, 0x88 | 0x20); - au0828_write(dev, REG_000, 0x88 | 0x20 | 0x40); + au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); + au0828_write(dev, REG_000, 0x80 | 0x40 | 0x20); + msleep(250); break; case AU0828_BOARD_DVICO_FUSIONHDTV7: diff --git a/drivers/media/video/au0828/au0828-core.c b/drivers/media/video/au0828/au0828-core.c index 5765e86..8c761d164 100644 --- a/drivers/media/video/au0828/au0828-core.c +++ b/drivers/media/video/au0828/au0828-core.c @@ -36,6 +36,8 @@ int au0828_debug; module_param_named(debug, au0828_debug, int, 0644); MODULE_PARM_DESC(debug, "enable debug messages"); +static atomic_t au0828_instance = ATOMIC_INIT(0); + #define _AU0828_BULKPIPE 0x03 #define _BULKPIPESIZE 0xffff @@ -51,13 +53,13 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, u32 au0828_readreg(struct au0828_dev *dev, u16 reg) { recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, dev->ctrlmsg, 1); - dprintk(8, "%s(0x%x) = 0x%x\n", __func__, reg, dev->ctrlmsg[0]); + dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, dev->ctrlmsg[0]); return dev->ctrlmsg[0]; } u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val) { - dprintk(8, "%s(0x%x, 0x%x)\n", __func__, reg, val); + dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val); return send_control_msg(dev, CMD_REQUEST_OUT, val, reg, dev->ctrlmsg, 0); } @@ -146,9 +148,14 @@ static void au0828_usb_disconnect(struct usb_interface *interface) /* Digital TV */ au0828_dvb_unregister(dev); + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) + au0828_analog_unregister(dev); + /* I2C */ au0828_i2c_unregister(dev); + v4l2_device_unregister(&dev->v4l2_dev); + usb_set_intfdata(interface, NULL); mutex_lock(&dev->mutex); @@ -162,7 +169,7 @@ static void au0828_usb_disconnect(struct usb_interface *interface) static int au0828_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { - int ifnum; + int ifnum, retval, i; struct au0828_dev *dev; struct usb_device *usbdev = interface_to_usbdev(interface); @@ -185,10 +192,22 @@ static int au0828_usb_probe(struct usb_interface *interface, mutex_init(&dev->mutex); mutex_init(&dev->dvb.lock); dev->usbdev = usbdev; - dev->board = id->driver_info; + dev->boardnr = id->driver_info; usb_set_intfdata(interface, dev); + /* Create the v4l2_device */ + i = atomic_inc_return(&au0828_instance) - 1; + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s-%03d", + "au0828", i); + retval = v4l2_device_register(&dev->usbdev->dev, &dev->v4l2_dev); + if (retval) { + printk(KERN_ERR "%s() v4l2_device_register failed\n", + __func__); + kfree(dev); + return -EIO; + } + /* Power Up the bridge */ au0828_write(dev, REG_600, 1 << 4); @@ -201,12 +220,15 @@ static int au0828_usb_probe(struct usb_interface *interface, /* Setup */ au0828_card_setup(dev); + /* Analog TV */ + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) + au0828_analog_register(dev, interface); + /* Digital TV */ au0828_dvb_register(dev); printk(KERN_INFO "Registered device AU0828 [%s]\n", - au0828_boards[dev->board].name == NULL ? "Unset" : - au0828_boards[dev->board].name); + dev->board.name == NULL ? "Unset" : dev->board.name); return 0; } diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c index a882cf5..14baffc 100644 --- a/drivers/media/video/au0828/au0828-dvb.c +++ b/drivers/media/video/au0828/au0828-dvb.c @@ -378,7 +378,7 @@ int au0828_dvb_register(struct au0828_dev *dev) dprintk(1, "%s()\n", __func__); /* init frontend */ - switch (dev->board) { + switch (dev->boardnr) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: dvb->frontend = dvb_attach(au8522_attach, diff --git a/drivers/media/video/au0828/au0828-i2c.c b/drivers/media/video/au0828/au0828-i2c.c index d618fba..f9a958d 100644 --- a/drivers/media/video/au0828/au0828-i2c.c +++ b/drivers/media/video/au0828/au0828-i2c.c @@ -140,13 +140,39 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap, dprintk(4, "%s()\n", __func__); au0828_write(dev, REG_2FF, 0x01); - au0828_write(dev, REG_202, 0x07); + + /* FIXME: There is a problem with i2c communications with xc5000 that + requires us to slow down the i2c clock until we have a better + strategy (such as using the secondary i2c bus to do firmware + loading */ + if ((msg->addr << 1) == 0xc2) + au0828_write(dev, REG_202, 0x40); + else + au0828_write(dev, REG_202, 0x07); /* Hardware needs 8 bit addresses */ au0828_write(dev, REG_203, msg->addr << 1); dprintk(4, "SEND: %02x\n", msg->addr); + /* Deal with i2c_scan */ + if (msg->len == 0) { + /* The analog tuner detection code makes use of the SMBUS_QUICK + message (which involves a zero length i2c write). To avoid + checking the status register when we didn't strobe out any + actual bytes to the bus, just do a read check. This is + consistent with how I saw i2c device checking done in the + USB trace of the Windows driver */ + au0828_write(dev, REG_200, 0x20); + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + if (i2c_wait_read_ack(i2c_adap)) + return -EIO; + + return 0; + } + for (i = 0; i < msg->len;) { dprintk(4, " %02x\n", msg->buf[i]); @@ -191,7 +217,15 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap, dprintk(4, "%s()\n", __func__); au0828_write(dev, REG_2FF, 0x01); - au0828_write(dev, REG_202, 0x07); + + /* FIXME: There is a problem with i2c communications with xc5000 that + requires us to slow down the i2c clock until we have a better + strategy (such as using the secondary i2c bus to do firmware + loading */ + if ((msg->addr << 1) == 0xc2) + au0828_write(dev, REG_202, 0x40); + else + au0828_write(dev, REG_202, 0x07); /* Hardware needs 8 bit addresses */ au0828_write(dev, REG_203, msg->addr << 1); @@ -265,33 +299,6 @@ err: return retval; } -static int attach_inform(struct i2c_client *client) -{ - dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, client->addr, client->name); - - if (!client->driver->command) - return 0; - - return 0; -} - -static int detach_inform(struct i2c_client *client) -{ - dprintk(1, "i2c detach [client=%s]\n", client->name); - - return 0; -} - -void au0828_call_i2c_clients(struct au0828_dev *dev, - unsigned int cmd, void *arg) -{ - if (dev->i2c_rc != 0) - return; - - i2c_clients_command(&dev->i2c_adap, cmd, arg); -} - static u32 au0828_functionality(struct i2c_adapter *adap) { return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; @@ -309,9 +316,6 @@ static struct i2c_adapter au0828_i2c_adap_template = { .owner = THIS_MODULE, .id = I2C_HW_B_AU0828, .algo = &au0828_i2c_algo_template, - .class = I2C_CLASS_TV_ANALOG, - .client_register = attach_inform, - .client_unregister = detach_inform, }; static struct i2c_client au0828_i2c_client_template = { @@ -356,9 +360,9 @@ int au0828_i2c_register(struct au0828_dev *dev) strlcpy(dev->i2c_adap.name, DRIVER_NAME, sizeof(dev->i2c_adap.name)); - dev->i2c_algo.data = dev; + dev->i2c_adap.algo = &dev->i2c_algo; dev->i2c_adap.algo_data = dev; - i2c_set_adapdata(&dev->i2c_adap, dev); + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); i2c_add_adapter(&dev->i2c_adap); dev->i2c_client.adapter = &dev->i2c_adap; diff --git a/drivers/media/video/au0828/au0828-reg.h b/drivers/media/video/au0828/au0828-reg.h index 1e87fa0c6..b15e4a3 100644 --- a/drivers/media/video/au0828/au0828-reg.h +++ b/drivers/media/video/au0828/au0828-reg.h @@ -27,6 +27,9 @@ #define REG_002 0x002 #define REG_003 0x003 +#define AU0828_SENSORCTRL_100 0x100 +#define AU0828_SENSORCTRL_VBI_103 0x103 + #define REG_200 0x200 #define REG_201 0x201 #define REG_202 0x202 @@ -35,4 +38,7 @@ #define REG_209 0x209 #define REG_2FF 0x2ff +/* Audio registers */ +#define AU0828_AUDIOCTRL_50C 0x50C + #define REG_600 0x600 diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c new file mode 100644 index 0000000..f7ad495 --- /dev/null +++ b/drivers/media/video/au0828/au0828-video.c @@ -0,0 +1,1712 @@ +/* + * Auvitek AU0828 USB Bridge (Analog video support) + * + * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org> + * Copyright (C) 2005-2008 Auvitek International, Ltd. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/* Developer Notes: + * + * VBI support is not yet working + * The hardware scaler supported is unimplemented + * AC97 audio support is unimplemented (only i2s audio mode) + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/suspend.h> +#include <linux/version.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-chip-ident.h> +#include <media/tuner.h> +#include "au0828.h" +#include "au0828-reg.h" + +static LIST_HEAD(au0828_devlist); +static DEFINE_MUTEX(au0828_sysfs_lock); + +#define AU0828_VERSION_CODE KERNEL_VERSION(0, 0, 1) + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static unsigned int isoc_debug; +module_param(isoc_debug, int, 0644); +MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); + +#define au0828_isocdbg(fmt, arg...) \ +do {\ + if (isoc_debug) { \ + printk(KERN_INFO "au0828 %s :"fmt, \ + __func__ , ##arg); \ + } \ + } while (0) + +static inline void print_err_status(struct au0828_dev *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + au0828_isocdbg("URB status %d [%s].\n", status, errmsg); + } else { + au0828_isocdbg("URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static int check_dev(struct au0828_dev *dev) +{ + if (dev->dev_state & DEV_DISCONNECTED) { + printk(KERN_INFO "v4l2 ioctl: device not present\n"); + return -ENODEV; + } + + if (dev->dev_state & DEV_MISCONFIGURED) { + printk(KERN_INFO "v4l2 ioctl: device is misconfigured; " + "close and open it again\n"); + return -EIO; + } + return 0; +} + +/* + * IRQ callback, called by URB callback + */ +static void au0828_irq_callback(struct urb *urb) +{ + struct au0828_dmaqueue *dma_q = urb->context; + struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq); + int rc, i; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + au0828_isocdbg("au0828_irq_callback called: status kill\n"); + return; + default: /* unknown error */ + au0828_isocdbg("urb completition error %d.\n", urb->status); + break; + } + + /* Copy data from URB */ + spin_lock(&dev->slock); + rc = dev->isoc_ctl.isoc_copy(dev, urb); + spin_unlock(&dev->slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + au0828_isocdbg("urb resubmit failed (error=%i)\n", + urb->status); + } +} + +/* + * Stop and Deallocate URBs + */ +void au0828_uninit_isoc(struct au0828_dev *dev) +{ + struct urb *urb; + int i; + + au0828_isocdbg("au0828: called au0828_uninit_isoc\n"); + + dev->isoc_ctl.nfields = -1; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = dev->isoc_ctl.urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + + if (dev->isoc_ctl.transfer_buffer[i]) { + usb_buffer_free(dev->usbdev, + urb->transfer_buffer_length, + dev->isoc_ctl.transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb = NULL; + dev->isoc_ctl.transfer_buffer = NULL; + dev->isoc_ctl.num_bufs = 0; +} + +/* + * Allocate URBs and start IRQ + */ +int au0828_init_isoc(struct au0828_dev *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb)) +{ + struct au0828_dmaqueue *dma_q = &dev->vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int j, k; + int rc; + + au0828_isocdbg("au0828: called au0828_prepare_isoc\n"); + + /* De-allocates all pending stuff */ + au0828_uninit_isoc(dev); + + dev->isoc_ctl.isoc_copy = isoc_copy; + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + au0828_isocdbg("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!dev->isoc_ctl.transfer_buffer) { + au0828_isocdbg("cannot allocate memory for usb transfer\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dev->isoc_ctl.max_pkt_size = max_pkt_size; + dev->isoc_ctl.buf = NULL; + + sb_size = max_packets * dev->isoc_ctl.max_pkt_size; + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + au0828_isocdbg("cannot alloc isoc_ctl.urb %i\n", i); + au0828_uninit_isoc(dev); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->usbdev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!dev->isoc_ctl.transfer_buffer[i]) { + printk("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt() ? " while in int" : ""); + au0828_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + + pipe = usb_rcvisocpipe(dev->usbdev, + dev->isoc_in_endpointaddr), + + usb_fill_int_urb(urb, dev->usbdev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + au0828_irq_callback, dma_q, 1); + + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + + k = 0; + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + dev->isoc_ctl.max_pkt_size; + k += dev->isoc_ctl.max_pkt_size; + } + } + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + au0828_isocdbg("submit of urb %i failed (error=%i)\n", + i, rc); + au0828_uninit_isoc(dev); + return rc; + } + } + + return 0; +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf) +{ + /* Advice that buffer was filled */ + au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +/* + * Identify the buffer header type and properly handles + */ +static void au0828_copy_video(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + void *fieldstart, *startwrite, *startread; + int linesdone, currlinedone, offset, lencopy, remain; + int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */ + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + startread = p; + remain = len; + + /* Interlaces frame */ + if (buf->top_field) + fieldstart = outp; + else + fieldstart = outp + bytesperline; + + linesdone = dma_q->pos / bytesperline; + currlinedone = dma_q->pos % bytesperline; + offset = linesdone * bytesperline * 2 + currlinedone; + startwrite = fieldstart + offset; + lencopy = bytesperline - currlinedone; + lencopy = lencopy > remain ? remain : lencopy; + + if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + au0828_isocdbg("Overflow of %zi bytes past buffer end (1)\n", + ((char *)startwrite + lencopy) - + ((char *)outp + buf->vb.size)); + remain = (char *)outp + buf->vb.size - (char *)startwrite; + lencopy = remain; + } + if (lencopy <= 0) + return; + memcpy(startwrite, startread, lencopy); + + remain -= lencopy; + + while (remain > 0) { + startwrite += lencopy + bytesperline; + startread += lencopy; + if (bytesperline > remain) + lencopy = remain; + else + lencopy = bytesperline; + + if ((char *)startwrite + lencopy > (char *)outp + + buf->vb.size) { + au0828_isocdbg("Overflow %zi bytes past buf end (2)\n", + ((char *)startwrite + lencopy) - + ((char *)outp + buf->vb.size)); + lencopy = remain = (char *)outp + buf->vb.size - + (char *)startwrite; + } + if (lencopy <= 0) + break; + + memcpy(startwrite, startread, lencopy); + + remain -= lencopy; + } + + if (offset > 1440) { + /* We have enough data to check for greenscreen */ + if (outp[0] < 0x60 && outp[1440] < 0x60) + dev->greenscreen_detected = 1; + } + + dma_q->pos += len; +} + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct au0828_dmaqueue *dma_q, + struct au0828_buffer **buf) +{ + struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq); + + if (list_empty(&dma_q->active)) { + au0828_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue); + dev->isoc_ctl.buf = *buf; + + return; +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) +{ + struct au0828_buffer *buf; + struct au0828_dmaqueue *dma_q = urb->context; + unsigned char *outp = NULL; + int i, len = 0, rc = 1; + unsigned char *p; + unsigned char fbyte; + + if (!dev) + return 0; + + if ((dev->dev_state & DEV_DISCONNECTED) || + (dev->dev_state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->isoc_ctl.buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + if (urb->iso_frame_desc[i].actual_length <= 0) + continue; + + if (urb->iso_frame_desc[i].actual_length > + dev->max_pkt_size) { + au0828_isocdbg("packet bigger than packet size"); + continue; + } + + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + fbyte = p[0]; + len = urb->iso_frame_desc[i].actual_length - 4; + p += 4; + + if (fbyte & 0x80) { + len -= 4; + p += 4; + au0828_isocdbg("Video frame %s\n", + (fbyte & 0x40) ? "odd" : "even"); + if (!(fbyte & 0x40)) { + if (buf != NULL) + buffer_filled(dev, dma_q, buf); + get_next_buf(dma_q, &buf); + if (buf == NULL) + outp = NULL; + else + outp = videobuf_to_vmalloc(&buf->vb); + } + + if (buf != NULL) { + if (fbyte & 0x40) + buf->top_field = 1; + else + buf->top_field = 0; + } + + dma_q->pos = 0; + } + if (buf != NULL) + au0828_copy_video(dev, dma_q, buf, p, outp, len); + } + return rc; +} + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct au0828_fh *fh = vq->priv_data; + *size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3; + + if (0 == *count) + *count = AU0828_DEF_BUF; + + if (*count < AU0828_MIN_BUF) + *count = AU0828_MIN_BUF; + return 0; +} + +/* This is called *without* dev->slock held; please keep it that way */ +static void free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf) +{ + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.buf == buf) + dev->isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct au0828_fh *fh = vq->priv_data; + struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); + struct au0828_dev *dev = fh->dev; + int rc = 0, urb_init = 0; + + buf->vb.size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->width; + buf->vb.height = dev->height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) { + printk(KERN_INFO "videobuf_iolock failed\n"); + goto fail; + } + } + + if (!dev->isoc_ctl.num_bufs) + urb_init = 1; + + if (urb_init) { + rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB, + AU0828_MAX_ISO_BUFS, dev->max_pkt_size, + au0828_isoc_copy); + if (rc < 0) { + printk(KERN_INFO "au0828_init_isoc failed\n"); + goto fail; + } + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, + struct au0828_buffer, + vb); + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + struct au0828_dmaqueue *vidq = &dev->vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, + struct au0828_buffer, + vb); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops au0828_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ + V4L2 interface + ------------------------------------------------------------------*/ + +static int au0828_i2s_init(struct au0828_dev *dev) +{ + /* Enable i2s mode */ + au0828_writereg(dev, AU0828_AUDIOCTRL_50C, 0x01); + return 0; +} + +/* + * Auvitek au0828 analog stream enable + * Please set interface0 to AS5 before enable the stream + */ +int au0828_analog_stream_enable(struct au0828_dev *d) +{ + dprintk(1, "au0828_analog_stream_enable called\n"); + au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00); + au0828_writereg(d, 0x106, 0x00); + /* set x position */ + au0828_writereg(d, 0x110, 0x00); + au0828_writereg(d, 0x111, 0x00); + au0828_writereg(d, 0x114, 0xa0); + au0828_writereg(d, 0x115, 0x05); + /* set y position */ + au0828_writereg(d, 0x112, 0x02); + au0828_writereg(d, 0x113, 0x00); + au0828_writereg(d, 0x116, 0xf2); + au0828_writereg(d, 0x117, 0x00); + au0828_writereg(d, AU0828_SENSORCTRL_100, 0xb3); + + return 0; +} + +int au0828_analog_stream_disable(struct au0828_dev *d) +{ + dprintk(1, "au0828_analog_stream_disable called\n"); + au0828_writereg(d, AU0828_SENSORCTRL_100, 0x0); + return 0; +} + +void au0828_analog_stream_reset(struct au0828_dev *dev) +{ + dprintk(1, "au0828_analog_stream_reset called\n"); + au0828_writereg(dev, AU0828_SENSORCTRL_100, 0x0); + mdelay(30); + au0828_writereg(dev, AU0828_SENSORCTRL_100, 0xb3); +} + +/* + * Some operations needs to stop current streaming + */ +static int au0828_stream_interrupt(struct au0828_dev *dev) +{ + int ret = 0; + + dev->stream_state = STREAM_INTERRUPT; + if (dev->dev_state == DEV_DISCONNECTED) + return -ENODEV; + else if (ret) { + dev->dev_state = DEV_MISCONFIGURED; + dprintk(1, "%s device is misconfigured!\n", __func__); + return ret; + } + return 0; +} + +/* + * au0828_release_resources + * unregister v4l2 devices + */ +void au0828_analog_unregister(struct au0828_dev *dev) +{ + dprintk(1, "au0828_release_resources called\n"); + mutex_lock(&au0828_sysfs_lock); + + if (dev->vdev) { + list_del(&dev->au0828list); + video_unregister_device(dev->vdev); + } + if (dev->vbi_dev) + video_unregister_device(dev->vbi_dev); + + mutex_unlock(&au0828_sysfs_lock); +} + + +/* Usage lock check functions */ +static int res_get(struct au0828_fh *fh) +{ + struct au0828_dev *dev = fh->dev; + int rc = 0; + + /* This instance already has stream_on */ + if (fh->stream_on) + return rc; + + if (dev->stream_on) + return -EBUSY; + + dev->stream_on = 1; + fh->stream_on = 1; + return rc; +} + +static int res_check(struct au0828_fh *fh) +{ + return fh->stream_on; +} + +static void res_free(struct au0828_fh *fh) +{ + struct au0828_dev *dev = fh->dev; + + fh->stream_on = 0; + dev->stream_on = 0; +} + +static int au0828_v4l2_open(struct file *filp) +{ + int minor = video_devdata(filp)->minor; + int ret = 0; + struct au0828_dev *h, *dev = NULL; + struct au0828_fh *fh; + int type = 0; + struct list_head *list; + + list_for_each(list, &au0828_devlist) { + h = list_entry(list, struct au0828_dev, au0828list); + if (h->vdev->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } +#ifdef VBI_IS_WORKING + if (h->vbi_dev->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VBI_CAPTURE; + } +#endif + } + + if (NULL == dev) + return -ENODEV; + + fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL); + if (NULL == fh) { + dprintk(1, "Failed allocate au0828_fh struct!\n"); + return -ENOMEM; + } + + fh->type = type; + fh->dev = dev; + filp->private_data = fh; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + /* set au0828 interface0 to AS5 here again */ + ret = usb_set_interface(dev->usbdev, 0, 5); + if (ret < 0) { + printk(KERN_INFO "Au0828 can't set alternate to 5!\n"); + return -EBUSY; + } + dev->width = NTSC_STD_W; + dev->height = NTSC_STD_H; + dev->frame_size = dev->width * dev->height * 2; + dev->field_size = dev->width * dev->height; + dev->bytesperline = dev->width * 2; + + au0828_analog_stream_enable(dev); + au0828_analog_stream_reset(dev); + + /* If we were doing ac97 instead of i2s, it would go here...*/ + au0828_i2s_init(dev); + + dev->stream_state = STREAM_OFF; + dev->dev_state |= DEV_INITIALIZED; + } + + dev->users++; + + videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops, + NULL, &dev->slock, fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct au0828_buffer), fh); + + return ret; +} + +static int au0828_v4l2_close(struct file *filp) +{ + int ret; + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + + mutex_lock(&dev->lock); + if (res_check(fh)) + res_free(fh); + + if (dev->users == 1) { + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + + if (dev->dev_state & DEV_DISCONNECTED) { + au0828_analog_unregister(dev); + mutex_unlock(&dev->lock); + kfree(dev); + return 0; + } + + au0828_analog_stream_disable(dev); + + au0828_uninit_isoc(dev); + + /* When close the device, set the usb intf0 into alt0 to free + USB bandwidth */ + ret = usb_set_interface(dev->usbdev, 0, 0); + if (ret < 0) + printk(KERN_INFO "Au0828 can't set alternate to 0!\n"); + } + + kfree(fh); + dev->users--; + wake_up_interruptible_nr(&dev->open, 1); + mutex_unlock(&dev->lock); + return 0; +} + +static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf, + size_t count, loff_t *pos) +{ + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; + + return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + return 0; +} + +static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait) +{ + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return POLLERR; + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); +} + +static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; + + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + + dprintk(2, "vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, + rc); + + return rc; +} + +static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, + struct v4l2_format *format) +{ + int ret; + int width = format->fmt.pix.width; + int height = format->fmt.pix.height; + unsigned int maxwidth, maxheight; + + maxwidth = 720; + maxheight = 480; + +#ifdef VBI_IS_WORKING + if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { + dprintk(1, "VBI format set: to be supported!\n"); + return 0; + } + if (format->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return 0; +#endif + if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* If they are demanding a format other than the one we support, + bail out (tvtime asks for UYVY and then retries with YUYV) */ + if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY) + return -EINVAL; + + /* format->fmt.pix.width only support 720 and height 480 */ + if (width != 720) + width = 720; + if (height != 480) + height = 480; + + format->fmt.pix.width = width; + format->fmt.pix.height = height; + format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + format->fmt.pix.bytesperline = width * 2; + format->fmt.pix.sizeimage = width * height * 2; + format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + format->fmt.pix.field = V4L2_FIELD_INTERLACED; + + if (cmd == VIDIOC_TRY_FMT) + return 0; + + /* maybe set new image format, driver current only support 720*480 */ + dev->width = width; + dev->height = height; + dev->frame_size = width * height * 2; + dev->field_size = width * height; + dev->bytesperline = width * 2; + + if (dev->stream_state == STREAM_ON) { + dprintk(1, "VIDIOC_SET_FMT: interrupting stream!\n"); + ret = au0828_stream_interrupt(dev); + if (ret != 0) { + dprintk(1, "error interrupting video stream!\n"); + return ret; + } + } + + /* set au0828 interface0 to AS5 here again */ + ret = usb_set_interface(dev->usbdev, 0, 5); + if (ret < 0) { + printk(KERN_INFO "Au0828 can't set alt setting to 5!\n"); + return -EBUSY; + } + + au0828_analog_stream_enable(dev); + + return 0; +} + + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); + if (qc->type) + return 0; + else + return -EINVAL; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + strlcpy(cap->driver, "au0828", sizeof(cap->driver)); + strlcpy(cap->card, dev->board.name, sizeof(cap->card)); + strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); + + cap->version = AU0828_VERSION_CODE; + + /*set the device capabilities */ + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | +#ifdef VBI_IS_WORKING + V4L2_CAP_VBI_CAPTURE | +#endif + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strcpy(f->description, "Packed YUV2"); + + f->flags = 0; + f->pixelformat = V4L2_PIX_FMT_UYVY; + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + f->fmt.pix.bytesperline = dev->bytesperline; + f->fmt.pix.sizeimage = dev->frame_size; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* NTSC/PAL */ + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + return au0828_set_format(dev, VIDIOC_TRY_FMT, f); +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + printk(KERN_INFO "%s queue busy\n", __func__); + rc = -EBUSY; + goto out; + } + + if (dev->stream_on && !fh->stream_on) { + printk(KERN_INFO "%s device in use by another fh\n", __func__); + rc = -EBUSY; + goto out; + } + + return au0828_set_format(dev, VIDIOC_S_FMT, f); +out: + return rc; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + /* FIXME: when we support something other than NTSC, we are going to + have to make the au0828 bridge adjust the size of its capture + buffer, which is currently hardcoded at 720x480 */ + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_std, *norm); + return 0; +} + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + unsigned int tmp; + + static const char *inames[] = { + [AU0828_VMUX_UNDEFINED] = "Undefined", + [AU0828_VMUX_COMPOSITE] = "Composite", + [AU0828_VMUX_SVIDEO] = "S-Video", + [AU0828_VMUX_CABLE] = "Cable TV", + [AU0828_VMUX_TELEVISION] = "Television", + [AU0828_VMUX_DVB] = "DVB", + [AU0828_VMUX_DEBUG] = "tv debug" + }; + + tmp = input->index; + + if (tmp > AU0828_MAX_INPUT) + return -EINVAL; + if (AUVI_INPUT(tmp).type == 0) + return -EINVAL; + + input->index = tmp; + strcpy(input->name, inames[AUVI_INPUT(tmp).type]); + if ((AUVI_INPUT(tmp).type == AU0828_VMUX_TELEVISION) || + (AUVI_INPUT(tmp).type == AU0828_VMUX_CABLE)) + input->type |= V4L2_INPUT_TYPE_TUNER; + else + input->type |= V4L2_INPUT_TYPE_CAMERA; + + input->std = dev->vdev->tvnorms; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + *i = dev->ctrl_input; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int index) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int i; + struct v4l2_routing route; + + dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__, + index); + if (index >= AU0828_MAX_INPUT) + return -EINVAL; + if (AUVI_INPUT(index).type == 0) + return -EINVAL; + dev->ctrl_input = index; + + switch (AUVI_INPUT(index).type) { + case AU0828_VMUX_SVIDEO: + dev->input_type = AU0828_VMUX_SVIDEO; + break; + case AU0828_VMUX_COMPOSITE: + dev->input_type = AU0828_VMUX_COMPOSITE; + break; + case AU0828_VMUX_TELEVISION: + dev->input_type = AU0828_VMUX_TELEVISION; + break; + default: + dprintk(1, "VIDIOC_S_INPUT unknown input type set [%d]\n", + AUVI_INPUT(index).type); + break; + } + + route.input = AUVI_INPUT(index).vmux; + route.output = 0; + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, &route); + + for (i = 0; i < AU0828_MAX_INPUT; i++) { + int enable = 0; + if (AUVI_INPUT(i).audio_setup == NULL) + continue; + + if (i == index) + enable = 1; + else + enable = 0; + if (enable) { + (AUVI_INPUT(i).audio_setup)(dev, enable); + } else { + /* Make sure we leave it turned on if some + other input is routed to this callback */ + if ((AUVI_INPUT(i).audio_setup) != + ((AUVI_INPUT(index).audio_setup))) { + (AUVI_INPUT(i).audio_setup)(dev, enable); + } + } + } + + route.input = AUVI_INPUT(index).amux; + v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, &route); + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + unsigned int index = a->index; + + if (a->index > 1) + return -EINVAL; + + index = dev->ctrl_ainput; + if (index == 0) + strcpy(a->name, "Television"); + else + strcpy(a->name, "Line in"); + + a->capability = V4L2_AUDCAP_STEREO; + a->index = index; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + if (a->index != dev->ctrl_ainput) + return -EINVAL; + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); + return 0; + +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (t->index != 0) + return -EINVAL; + + strcpy(t->name, "Auvitek tuner"); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (t->index != 0) + return -EINVAL; + + t->type = V4L2_TUNER_ANALOG_TV; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal, + t->afc); + return 0; + +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + freq->type = V4L2_TUNER_ANALOG_TV; + freq->frequency = dev->ctrl_freq; + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (freq->tuner != 0) + return -EINVAL; + if (freq->type != V4L2_TUNER_ANALOG_TV) + return -EINVAL; + + dev->ctrl_freq = freq->frequency; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq); + + au0828_analog_stream_reset(dev); + + return 0; +} + +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + + if (v4l2_chip_match_host(&chip->match)) { + chip->ident = V4L2_IDENT_AU0828; + return 0; + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip); + if (chip->ident == V4L2_IDENT_NONE) + return -EINVAL; + + return 0; +} + +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cc) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cc->bounds.left = 0; + cc->bounds.top = 0; + cc->bounds.width = dev->width; + cc->bounds.height = dev->height; + + cc->defrect = cc->bounds; + + cc->pixelaspect.numerator = 54; + cc->pixelaspect.denominator = 59; + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + au0828_analog_stream_enable(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); + } + + mutex_lock(&dev->lock); + rc = res_get(fh); + + if (likely(rc >= 0)) + rc = videobuf_streamon(&fh->vb_vidq); + mutex_unlock(&dev->lock); + + return rc; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int i; + int ret; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (type != fh->type) + return -EINVAL; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); + ret = au0828_stream_interrupt(dev); + if (ret != 0) + return ret; + } + + for (i = 0; i < AU0828_MAX_INPUT; i++) { + if (AUVI_INPUT(i).audio_setup == NULL) + continue; + (AUVI_INPUT(i).audio_setup)(dev, 0); + } + + mutex_lock(&dev->lock); + videobuf_streamoff(&fh->vb_vidq); + res_free(fh); + mutex_unlock(&dev->lock); + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_I2C_DRIVER: + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); + return 0; + default: + return -EINVAL; + } +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_I2C_DRIVER: + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); + return 0; + default: + return -EINVAL; + } + return 0; +} +#endif + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_reqbufs(&fh->vb_vidq, rb); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_querybuf(&fh->vb_vidq, b); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_qbuf(&fh->vb_vidq, b); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + /* Workaround for a bug in the au0828 hardware design that sometimes + results in the colorspace being inverted */ + if (dev->greenscreen_detected == 1) { + dprintk(1, "Detected green frame. Resetting stream...\n"); + au0828_analog_stream_reset(dev); + dev->greenscreen_detected = 0; + } + + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); +} + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) +{ + struct au0828_fh *fh = priv; + + return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); +} +#endif + +static struct v4l2_file_operations au0828_v4l_fops = { + .owner = THIS_MODULE, + .open = au0828_v4l2_open, + .release = au0828_v4l2_close, + .read = au0828_v4l2_read, + .poll = au0828_v4l2_poll, + .mmap = au0828_v4l2_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, +#ifdef VBI_IS_WORKING + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, +#endif + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_cropcap = vidioc_cropcap, +#ifdef VBI_IS_WORKING + .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, + .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, + .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, +#endif + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif + .vidioc_g_chip_ident = vidioc_g_chip_ident, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +}; + +static const struct video_device au0828_video_template = { + .fops = &au0828_v4l_fops, + .release = video_device_release, + .ioctl_ops = &video_ioctl_ops, + .minor = -1, + .tvnorms = V4L2_STD_NTSC_M, + .current_norm = V4L2_STD_NTSC_M, +}; + +/**************************************************************************/ + +int au0828_analog_register(struct au0828_dev *dev, + struct usb_interface *interface) +{ + int retval = -ENOMEM; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + + dprintk(1, "au0828_analog_register called!\n"); + + /* set au0828 usb interface0 to as5 */ + retval = usb_set_interface(dev->usbdev, + interface->cur_altsetting->desc.bInterfaceNumber, 5); + if (retval != 0) { + printk(KERN_INFO "Failure setting usb interface0 to as5\n"); + return retval; + } + + /* Figure out which endpoint has the isoc interface */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_ISOC)) { + + /* we find our isoc in endpoint */ + u16 tmp = le16_to_cpu(endpoint->wMaxPacketSize); + dev->max_pkt_size = (tmp & 0x07ff) * + (((tmp & 0x1800) >> 11) + 1); + dev->isoc_in_endpointaddr = endpoint->bEndpointAddress; + } + } + if (!(dev->isoc_in_endpointaddr)) { + printk(KERN_INFO "Could not locate isoc endpoint\n"); + kfree(dev); + return -ENODEV; + } + + init_waitqueue_head(&dev->open); + spin_lock_init(&dev->slock); + mutex_init(&dev->lock); + + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + + dev->width = NTSC_STD_W; + dev->height = NTSC_STD_H; + dev->field_size = dev->width * dev->height; + dev->frame_size = dev->field_size << 1; + dev->bytesperline = dev->width << 1; + dev->ctrl_ainput = 0; + + /* allocate and fill v4l2 video struct */ + dev->vdev = video_device_alloc(); + if (NULL == dev->vdev) { + dprintk(1, "Can't allocate video_device.\n"); + return -ENOMEM; + } + +#ifdef VBI_IS_WORKING + dev->vbi_dev = video_device_alloc(); + if (NULL == dev->vbi_dev) { + dprintk(1, "Can't allocate vbi_device.\n"); + kfree(dev->vdev); + return -ENOMEM; + } +#endif + + /* Fill the video capture device struct */ + *dev->vdev = au0828_video_template; + dev->vdev->parent = &dev->usbdev->dev; + strcpy(dev->vdev->name, "au0828a video"); + +#ifdef VBI_IS_WORKING + /* Setup the VBI device */ + *dev->vbi_dev = au0828_video_template; + dev->vbi_dev->parent = &dev->usbdev->dev; + strcpy(dev->vbi_dev->name, "au0828a vbi"); +#endif + + list_add_tail(&dev->au0828list, &au0828_devlist); + + /* Register the v4l2 device */ + retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1); + if (retval != 0) { + dprintk(1, "unable to register video device (error = %d).\n", + retval); + list_del(&dev->au0828list); + video_device_release(dev->vdev); + return -ENODEV; + } + +#ifdef VBI_IS_WORKING + /* Register the vbi device */ + retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1); + if (retval != 0) { + dprintk(1, "unable to register vbi device (error = %d).\n", + retval); + list_del(&dev->au0828list); + video_device_release(dev->vbi_dev); + video_device_release(dev->vdev); + return -ENODEV; + } +#endif + + dprintk(1, "%s completed!\n", __func__); + + return 0; +} + diff --git a/drivers/media/video/au0828/au0828.h b/drivers/media/video/au0828/au0828.h index 9d6a116..6ed1a61 100644 --- a/drivers/media/video/au0828/au0828.h +++ b/drivers/media/video/au0828/au0828.h @@ -24,6 +24,11 @@ #include <linux/i2c-algo-bit.h> #include <media/tveeprom.h> +/* Analog */ +#include <linux/videodev2.h> +#include <media/videobuf-vmalloc.h> +#include <media/v4l2-device.h> + /* DVB */ #include "demux.h" #include "dmxdev.h" @@ -39,8 +44,45 @@ #define URB_COUNT 16 #define URB_BUFSIZE (0xe522) +/* Analog constants */ +#define NTSC_STD_W 720 +#define NTSC_STD_H 480 + +#define AU0828_INTERLACED_DEFAULT 1 +#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0) + +/* Defination for AU0828 USB transfer */ +#define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */ +#define AU0828_ISO_PACKETS_PER_URB 10 + +#define AU0828_MIN_BUF 4 +#define AU0828_DEF_BUF 8 + +#define AU0828_MAX_INPUT 4 + +enum au0828_itype { + AU0828_VMUX_UNDEFINED = 0, + AU0828_VMUX_COMPOSITE, + AU0828_VMUX_SVIDEO, + AU0828_VMUX_CABLE, + AU0828_VMUX_TELEVISION, + AU0828_VMUX_DVB, + AU0828_VMUX_DEBUG +}; + +struct au0828_input { + enum au0828_itype type; + unsigned int vmux; + unsigned int amux; + void (*audio_setup) (void *priv, int enable); +}; + struct au0828_board { char *name; + unsigned int tuner_type; + unsigned char tuner_addr; + struct au0828_input input[AU0828_MAX_INPUT]; + }; struct au0828_dvb { @@ -55,31 +97,143 @@ struct au0828_dvb { int feeding; }; +enum au0828_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON +}; + +#define AUVI_INPUT(nr) (dev->board.input[nr]) + +/* device state */ +enum au0828_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04 +}; + +struct au0828_fh { + struct au0828_dev *dev; + unsigned int stream_on:1; /* Locks streams */ + struct videobuf_queue vb_vidq; + enum v4l2_buf_type type; +}; + +struct au0828_usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct au0828_buffer *buf; + + /* Stores the number of received fields */ + int nfields; + + /* isoc urb callback */ + int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb); + +}; + +/* buffer for one video frame */ +struct au0828_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct list_head frame; + int top_field; + int receiving; +}; + +struct au0828_dmaqueue { + struct list_head active; + struct list_head queued; + + wait_queue_head_t wq; + + /* Counters to control buffer fill */ + int pos; +}; + struct au0828_dev { struct mutex mutex; struct usb_device *usbdev; - int board; + int boardnr; + struct au0828_board board; u8 ctrlmsg[64]; /* I2C */ struct i2c_adapter i2c_adap; - struct i2c_algo_bit_data i2c_algo; + struct i2c_algorithm i2c_algo; struct i2c_client i2c_client; u32 i2c_rc; /* Digital */ struct au0828_dvb dvb; + /* Analog */ + struct list_head au0828list; + struct v4l2_device v4l2_dev; + int users; + unsigned int stream_on:1; /* Locks streams */ + struct video_device *vdev; + struct video_device *vbi_dev; + int width; + int height; + u32 field_size; + u32 frame_size; + u32 bytesperline; + int type; + u8 ctrl_ainput; + __u8 isoc_in_endpointaddr; + u8 isoc_init_ok; + int greenscreen_detected; + unsigned int frame_count; + int ctrl_freq; + int input_type; + unsigned int ctrl_input; + enum au0828_dev_state dev_state; + enum au0828_stream_state stream_state; + wait_queue_head_t open; + + struct mutex lock; + + /* Isoc control struct */ + struct au0828_dmaqueue vidq; + struct au0828_usb_isoc_ctl isoc_ctl; + spinlock_t slock; + + /* usb transfer */ + int alt; /* alternate */ + int max_pkt_size; /* max packet size of isoc transaction */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ + struct urb *urb[AU0828_MAX_ISO_BUFS]; /* urb for isoc transfers */ + char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc + transfer */ + /* USB / URB Related */ int urb_streaming; struct urb *urbs[URB_COUNT]; - -}; - -struct au0828_buff { - struct au0828_dev *dev; - struct urb *purb; - struct list_head buff_list; }; /* ----------------------------------------------------------- */ @@ -111,8 +265,13 @@ extern void au0828_card_setup(struct au0828_dev *dev); /* au0828-i2c.c */ extern int au0828_i2c_register(struct au0828_dev *dev); extern int au0828_i2c_unregister(struct au0828_dev *dev); -extern void au0828_call_i2c_clients(struct au0828_dev *dev, - unsigned int cmd, void *arg); + +/* ----------------------------------------------------------- */ +/* au0828-video.c */ +int au0828_analog_register(struct au0828_dev *dev, + struct usb_interface *interface); +int au0828_analog_stream_disable(struct au0828_dev *d); +void au0828_analog_unregister(struct au0828_dev *dev); /* ----------------------------------------------------------- */ /* au0828-dvb.c */ diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index a07b7b8..df4516d 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -29,16 +29,16 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/types.h> #include <linux/ioctl.h> -#include <asm/uaccess.h> +#include <linux/delay.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> -#include <linux/video_decoder.h> -#include <media/v4l2-common.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> +#include <media/bt819.h> MODULE_DESCRIPTION("Brooktree-819 video decoder driver"); MODULE_AUTHOR("Mike Bernson & Dave Perks"); @@ -48,13 +48,15 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); + /* ----------------------------------------------------------------------- */ struct bt819 { + struct v4l2_subdev sd; unsigned char reg[32]; - int initialized; - int norm; + v4l2_std_id norm; + int ident; int input; int enable; int bright; @@ -63,6 +65,11 @@ struct bt819 { int sat; }; +static inline struct bt819 *to_bt819(struct v4l2_subdev *sd) +{ + return container_of(sd, struct bt819, sd); +} + struct timing { int hactive; int hdelay; @@ -80,24 +87,23 @@ static struct timing timing_data[] = { /* ----------------------------------------------------------------------- */ -static inline int bt819_write(struct i2c_client *client, u8 reg, u8 value) +static inline int bt819_write(struct bt819 *decoder, u8 reg, u8 value) { - struct bt819 *decoder = i2c_get_clientdata(client); + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); decoder->reg[reg] = value; return i2c_smbus_write_byte_data(client, reg, value); } -static inline int bt819_setbit(struct i2c_client *client, u8 reg, u8 bit, u8 value) +static inline int bt819_setbit(struct bt819 *decoder, u8 reg, u8 bit, u8 value) { - struct bt819 *decoder = i2c_get_clientdata(client); - - return bt819_write(client, reg, + return bt819_write(decoder, reg, (decoder->reg[reg] & ~(1 << bit)) | (value ? (1 << bit) : 0)); } -static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned int len) +static int bt819_write_block(struct bt819 *decoder, const u8 *data, unsigned int len) { + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); int ret = -1; u8 reg; @@ -105,7 +111,6 @@ static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned * the adapter understands raw I2C */ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { /* do raw I2C, not smbus compatible */ - struct bt819 *decoder = i2c_get_clientdata(client); u8 block_data[32]; int block_len; @@ -126,7 +131,8 @@ static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned /* do some slow I2C emulation kind of thing */ while (len >= 2) { reg = *data++; - if ((ret = bt819_write(client, reg, *data++)) < 0) + ret = bt819_write(decoder, reg, *data++); + if (ret < 0) break; len -= 2; } @@ -135,15 +141,15 @@ static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned return ret; } -static inline int bt819_read(struct i2c_client *client, u8 reg) +static inline int bt819_read(struct bt819 *decoder, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); + return i2c_smbus_read_byte_data(client, reg); } -static int bt819_init(struct i2c_client *client) +static int bt819_init(struct v4l2_subdev *sd) { - struct bt819 *decoder = i2c_get_clientdata(client); - static unsigned char init[] = { /*0x1f, 0x00,*/ /* Reset */ 0x01, 0x59, /* 0x01 input format */ @@ -178,7 +184,8 @@ static int bt819_init(struct i2c_client *client) 0x1a, 0x80, /* 0x1a ADC Interface */ }; - struct timing *timing = &timing_data[decoder->norm]; + struct bt819 *decoder = to_bt819(sd); + struct timing *timing = &timing_data[(decoder->norm & V4L2_STD_525_60) ? 1 : 0]; init[0x03 * 2 - 1] = (((timing->vdelay >> 8) & 0x03) << 6) | @@ -192,266 +199,306 @@ static int bt819_init(struct i2c_client *client) init[0x08 * 2 - 1] = timing->hscale >> 8; init[0x09 * 2 - 1] = timing->hscale & 0xff; /* 0x15 in array is address 0x19 */ - init[0x15 * 2 - 1] = (decoder->norm == 0) ? 115 : 93; /* Chroma burst delay */ + init[0x15 * 2 - 1] = (decoder->norm & V4L2_STD_625_50) ? 115 : 93; /* Chroma burst delay */ /* reset */ - bt819_write(client, 0x1f, 0x00); + bt819_write(decoder, 0x1f, 0x00); mdelay(1); /* init */ - return bt819_write_block(client, init, sizeof(init)); + return bt819_write_block(decoder, init, sizeof(init)); } /* ----------------------------------------------------------------------- */ -static int bt819_command(struct i2c_client *client, unsigned cmd, void *arg) +static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) { - int temp; + struct bt819 *decoder = to_bt819(sd); + int status = bt819_read(decoder, 0x00); + int res = V4L2_IN_ST_NO_SIGNAL; + v4l2_std_id std; - struct bt819 *decoder = i2c_get_clientdata(client); + if ((status & 0x80)) + res = 0; - if (!decoder->initialized) { /* First call to bt819_init could be */ - bt819_init(client); /* without #FRST = 0 */ - decoder->initialized = 1; - } + if ((status & 0x10)) + std = V4L2_STD_PAL; + else + std = V4L2_STD_NTSC; + if (pstd) + *pstd = std; + if (pstatus) + *pstatus = status; - switch (cmd) { - case 0: - /* This is just for testing!!! */ - bt819_init(client); - break; + v4l2_dbg(1, debug, sd, "get status %x\n", status); + return 0; +} - case DECODER_GET_CAPABILITIES: - { - struct video_decoder_capability *cap = arg; +static int bt819_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + return bt819_status(sd, NULL, std); +} - cap->flags = VIDEO_DECODER_PAL | - VIDEO_DECODER_NTSC | - VIDEO_DECODER_AUTO | - VIDEO_DECODER_CCIR; - cap->inputs = 8; - cap->outputs = 1; - break; +static int bt819_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + return bt819_status(sd, status, NULL); +} + +static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct bt819 *decoder = to_bt819(sd); + struct timing *timing = NULL; + + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); + + if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) + v4l2_err(sd, "no notify found!\n"); + + if (std & V4L2_STD_NTSC) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0); + bt819_setbit(decoder, 0x01, 0, 1); + bt819_setbit(decoder, 0x01, 1, 0); + bt819_setbit(decoder, 0x01, 5, 0); + bt819_write(decoder, 0x18, 0x68); + bt819_write(decoder, 0x19, 0x5d); + /* bt819_setbit(decoder, 0x1a, 5, 1); */ + timing = &timing_data[1]; + } else if (std & V4L2_STD_PAL) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0); + bt819_setbit(decoder, 0x01, 0, 1); + bt819_setbit(decoder, 0x01, 1, 1); + bt819_setbit(decoder, 0x01, 5, 1); + bt819_write(decoder, 0x18, 0x7f); + bt819_write(decoder, 0x19, 0x72); + /* bt819_setbit(decoder, 0x1a, 5, 0); */ + timing = &timing_data[0]; + } else { + v4l2_dbg(1, debug, sd, "unsupported norm %llx\n", + (unsigned long long)std); + return -EINVAL; } + bt819_write(decoder, 0x03, + (((timing->vdelay >> 8) & 0x03) << 6) | + (((timing->vactive >> 8) & 0x03) << 4) | + (((timing->hdelay >> 8) & 0x03) << 2) | + ((timing->hactive >> 8) & 0x03)); + bt819_write(decoder, 0x04, timing->vdelay & 0xff); + bt819_write(decoder, 0x05, timing->vactive & 0xff); + bt819_write(decoder, 0x06, timing->hdelay & 0xff); + bt819_write(decoder, 0x07, timing->hactive & 0xff); + bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff); + bt819_write(decoder, 0x09, timing->hscale & 0xff); + decoder->norm = std; + v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, 0); + return 0; +} - case DECODER_GET_STATUS: - { - int *iarg = arg; - int status; - int res; +static int bt819_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct bt819 *decoder = to_bt819(sd); - status = bt819_read(client, 0x00); - res = 0; - if ((status & 0x80)) - res |= DECODER_STATUS_GOOD; + v4l2_dbg(1, debug, sd, "set input %x\n", route->input); - switch (decoder->norm) { - case VIDEO_MODE_NTSC: - res |= DECODER_STATUS_NTSC; - break; - case VIDEO_MODE_PAL: - res |= DECODER_STATUS_PAL; - break; - default: - case VIDEO_MODE_AUTO: - if ((status & 0x10)) - res |= DECODER_STATUS_PAL; - else - res |= DECODER_STATUS_NTSC; - break; - } - res |= DECODER_STATUS_COLOR; - *iarg = res; + if (route->input < 0 || route->input > 7) + return -EINVAL; - v4l_dbg(1, debug, client, "get status %x\n", *iarg); - break; + if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) + v4l2_err(sd, "no notify found!\n"); + + if (decoder->input != route->input) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0); + decoder->input = route->input; + /* select mode */ + if (decoder->input == 0) { + bt819_setbit(decoder, 0x0b, 6, 0); + bt819_setbit(decoder, 0x1a, 1, 1); + } else { + bt819_setbit(decoder, 0x0b, 6, 1); + bt819_setbit(decoder, 0x1a, 1, 0); + } + v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, 0); } + return 0; +} - case DECODER_SET_NORM: - { - int *iarg = arg; - struct timing *timing = NULL; - - v4l_dbg(1, debug, client, "set norm %x\n", *iarg); - - switch (*iarg) { - case VIDEO_MODE_NTSC: - bt819_setbit(client, 0x01, 0, 1); - bt819_setbit(client, 0x01, 1, 0); - bt819_setbit(client, 0x01, 5, 0); - bt819_write(client, 0x18, 0x68); - bt819_write(client, 0x19, 0x5d); - /* bt819_setbit(client, 0x1a, 5, 1); */ - timing = &timing_data[VIDEO_MODE_NTSC]; - break; - case VIDEO_MODE_PAL: - bt819_setbit(client, 0x01, 0, 1); - bt819_setbit(client, 0x01, 1, 1); - bt819_setbit(client, 0x01, 5, 1); - bt819_write(client, 0x18, 0x7f); - bt819_write(client, 0x19, 0x72); - /* bt819_setbit(client, 0x1a, 5, 0); */ - timing = &timing_data[VIDEO_MODE_PAL]; - break; - case VIDEO_MODE_AUTO: - bt819_setbit(client, 0x01, 0, 0); - bt819_setbit(client, 0x01, 1, 0); - break; - default: - v4l_dbg(1, debug, client, "unsupported norm %x\n", *iarg); - return -EINVAL; - } +static int bt819_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct bt819 *decoder = to_bt819(sd); - if (timing) { - bt819_write(client, 0x03, - (((timing->vdelay >> 8) & 0x03) << 6) | - (((timing->vactive >> 8) & 0x03) << 4) | - (((timing->hdelay >> 8) & 0x03) << 2) | - ((timing->hactive >> 8) & 0x03) ); - bt819_write(client, 0x04, timing->vdelay & 0xff); - bt819_write(client, 0x05, timing->vactive & 0xff); - bt819_write(client, 0x06, timing->hdelay & 0xff); - bt819_write(client, 0x07, timing->hactive & 0xff); - bt819_write(client, 0x08, (timing->hscale >> 8) & 0xff); - bt819_write(client, 0x09, timing->hscale & 0xff); - } + v4l2_dbg(1, debug, sd, "enable output %x\n", enable); - decoder->norm = *iarg; - break; + if (decoder->enable != enable) { + decoder->enable = enable; + bt819_setbit(decoder, 0x16, 7, !enable); } + return 0; +} - case DECODER_SET_INPUT: - { - int *iarg = arg; - - v4l_dbg(1, debug, client, "set input %x\n", *iarg); - - if (*iarg < 0 || *iarg > 7) - return -EINVAL; - - if (decoder->input != *iarg) { - decoder->input = *iarg; - /* select mode */ - if (decoder->input == 0) { - bt819_setbit(client, 0x0b, 6, 0); - bt819_setbit(client, 0x1a, 1, 1); - } else { - bt819_setbit(client, 0x0b, 6, 1); - bt819_setbit(client, 0x1a, 1, 0); - } - } +static int bt819_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); break; - } - case DECODER_SET_OUTPUT: - { - int *iarg = arg; + case V4L2_CID_CONTRAST: + v4l2_ctrl_query_fill(qc, 0, 511, 1, 256); + break; - v4l_dbg(1, debug, client, "set output %x\n", *iarg); + case V4L2_CID_SATURATION: + v4l2_ctrl_query_fill(qc, 0, 511, 1, 256); + break; - /* not much choice of outputs */ - if (*iarg != 0) - return -EINVAL; + case V4L2_CID_HUE: + v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); break; - } - case DECODER_ENABLE_OUTPUT: - { - int *iarg = arg; - int enable = (*iarg != 0); + default: + return -EINVAL; + } + return 0; +} - v4l_dbg(1, debug, client, "enable output %x\n", *iarg); +static int bt819_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct bt819 *decoder = to_bt819(sd); + int temp; - if (decoder->enable != enable) { - decoder->enable = enable; - bt819_setbit(client, 0x16, 7, !enable); - } + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (decoder->bright == ctrl->value) + break; + decoder->bright = ctrl->value; + bt819_write(decoder, 0x0a, decoder->bright); break; - } - case DECODER_SET_PICTURE: - { - struct video_picture *pic = arg; - - v4l_dbg(1, debug, client, - "set picture brightness %d contrast %d colour %d\n", - pic->brightness, pic->contrast, pic->colour); + case V4L2_CID_CONTRAST: + if (decoder->contrast == ctrl->value) + break; + decoder->contrast = ctrl->value; + bt819_write(decoder, 0x0c, decoder->contrast & 0xff); + bt819_setbit(decoder, 0x0b, 2, ((decoder->contrast >> 8) & 0x01)); + break; + case V4L2_CID_SATURATION: + if (decoder->sat == ctrl->value) + break; + decoder->sat = ctrl->value; + bt819_write(decoder, 0x0d, (decoder->sat >> 7) & 0xff); + bt819_setbit(decoder, 0x0b, 1, ((decoder->sat >> 15) & 0x01)); + + /* Ratio between U gain and V gain must stay the same as + the ratio between the default U and V gain values. */ + temp = (decoder->sat * 180) / 254; + bt819_write(decoder, 0x0e, (temp >> 7) & 0xff); + bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01); + break; - if (decoder->bright != pic->brightness) { - /* We want -128 to 127 we get 0-65535 */ - decoder->bright = pic->brightness; - bt819_write(client, 0x0a, - (decoder->bright >> 8) - 128); - } + case V4L2_CID_HUE: + if (decoder->hue == ctrl->value) + break; + decoder->hue = ctrl->value; + bt819_write(decoder, 0x0f, decoder->hue); + break; - if (decoder->contrast != pic->contrast) { - /* We want 0 to 511 we get 0-65535 */ - decoder->contrast = pic->contrast; - bt819_write(client, 0x0c, - (decoder->contrast >> 7) & 0xff); - bt819_setbit(client, 0x0b, 2, - ((decoder->contrast >> 15) & 0x01)); - } + default: + return -EINVAL; + } + return 0; +} - if (decoder->sat != pic->colour) { - /* We want 0 to 511 we get 0-65535 */ - decoder->sat = pic->colour; - bt819_write(client, 0x0d, - (decoder->sat >> 7) & 0xff); - bt819_setbit(client, 0x0b, 1, - ((decoder->sat >> 15) & 0x01)); - - temp = (decoder->sat * 201) / 237; - bt819_write(client, 0x0e, (temp >> 7) & 0xff); - bt819_setbit(client, 0x0b, 0, (temp >> 15) & 0x01); - } +static int bt819_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct bt819 *decoder = to_bt819(sd); - if (decoder->hue != pic->hue) { - /* We want -128 to 127 we get 0-65535 */ - decoder->hue = pic->hue; - bt819_write(client, 0x0f, - 128 - (decoder->hue >> 8)); - } + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = decoder->bright; + break; + case V4L2_CID_CONTRAST: + ctrl->value = decoder->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = decoder->sat; + break; + case V4L2_CID_HUE: + ctrl->value = decoder->hue; break; - } - default: return -EINVAL; } - return 0; } +static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct bt819 *decoder = to_bt819(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); +} + /* ----------------------------------------------------------------------- */ -static unsigned short normal_i2c[] = { 0x8a >> 1, I2C_CLIENT_END }; +static const struct v4l2_subdev_core_ops bt819_core_ops = { + .g_chip_ident = bt819_g_chip_ident, + .g_ctrl = bt819_g_ctrl, + .s_ctrl = bt819_s_ctrl, + .queryctrl = bt819_queryctrl, +}; + +static const struct v4l2_subdev_tuner_ops bt819_tuner_ops = { + .s_std = bt819_s_std, +}; + +static const struct v4l2_subdev_video_ops bt819_video_ops = { + .s_routing = bt819_s_routing, + .s_stream = bt819_s_stream, + .querystd = bt819_querystd, + .g_input_status = bt819_g_input_status, +}; + +static const struct v4l2_subdev_ops bt819_ops = { + .core = &bt819_core_ops, + .tuner = &bt819_tuner_ops, + .video = &bt819_video_ops, +}; -I2C_CLIENT_INSMOD; +/* ----------------------------------------------------------------------- */ static int bt819_probe(struct i2c_client *client, const struct i2c_device_id *id) { int i, ver; struct bt819 *decoder; + struct v4l2_subdev *sd; const char *name; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - ver = bt819_read(client, 0x17); + decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &bt819_ops); + + ver = bt819_read(decoder, 0x17); switch (ver & 0xf0) { case 0x70: name = "bt819a"; + decoder->ident = V4L2_IDENT_BT819A; break; case 0x60: name = "bt817a"; + decoder->ident = V4L2_IDENT_BT817A; break; case 0x20: name = "bt815a"; + decoder->ident = V4L2_IDENT_BT815A; break; default: - v4l_dbg(1, debug, client, + v4l2_dbg(1, debug, sd, "unknown chip version 0x%02x\n", ver); return -ENODEV; } @@ -459,28 +506,26 @@ static int bt819_probe(struct i2c_client *client, v4l_info(client, "%s found @ 0x%x (%s)\n", name, client->addr << 1, client->adapter->name); - decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL); - if (decoder == NULL) - return -ENOMEM; - decoder->norm = VIDEO_MODE_NTSC; + decoder->norm = V4L2_STD_NTSC; decoder->input = 0; decoder->enable = 1; - decoder->bright = 32768; - decoder->contrast = 32768; - decoder->hue = 32768; - decoder->sat = 32768; - decoder->initialized = 0; - i2c_set_clientdata(client, decoder); - - i = bt819_init(client); + decoder->bright = 0; + decoder->contrast = 0xd8; /* 100% of original signal */ + decoder->hue = 0; + decoder->sat = 0xfe; /* 100% of original signal */ + + i = bt819_init(sd); if (i < 0) - v4l_dbg(1, debug, client, "init status %d\n", i); + v4l2_dbg(1, debug, sd, "init status %d\n", i); return 0; } static int bt819_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_bt819(sd)); return 0; } @@ -496,8 +541,6 @@ MODULE_DEVICE_TABLE(i2c, bt819_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "bt819", - .driverid = I2C_DRIVERID_BT819, - .command = bt819_command, .probe = bt819_probe, .remove = bt819_remove, .id_table = bt819_id, diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c index 4213867..78db395 100644 --- a/drivers/media/video/bt856.c +++ b/drivers/media/video/bt856.c @@ -34,10 +34,10 @@ #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> -#include <linux/video_encoder.h> -#include <media/v4l2-common.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("Brooktree-856A video encoder driver"); MODULE_AUTHOR("Mike Bernson & Dave Perks"); @@ -47,43 +47,46 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); + /* ----------------------------------------------------------------------- */ #define BT856_REG_OFFSET 0xDA #define BT856_NR_REG 6 struct bt856 { + struct v4l2_subdev sd; unsigned char reg[BT856_NR_REG]; - int norm; - int enable; + v4l2_std_id norm; }; +static inline struct bt856 *to_bt856(struct v4l2_subdev *sd) +{ + return container_of(sd, struct bt856, sd); +} + /* ----------------------------------------------------------------------- */ -static inline int bt856_write(struct i2c_client *client, u8 reg, u8 value) +static inline int bt856_write(struct bt856 *encoder, u8 reg, u8 value) { - struct bt856 *encoder = i2c_get_clientdata(client); + struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); encoder->reg[reg - BT856_REG_OFFSET] = value; return i2c_smbus_write_byte_data(client, reg, value); } -static inline int bt856_setbit(struct i2c_client *client, u8 reg, u8 bit, u8 value) +static inline int bt856_setbit(struct bt856 *encoder, u8 reg, u8 bit, u8 value) { - struct bt856 *encoder = i2c_get_clientdata(client); - - return bt856_write(client, reg, + return bt856_write(encoder, reg, (encoder->reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) | (value ? (1 << bit) : 0)); } -static void bt856_dump(struct i2c_client *client) +static void bt856_dump(struct bt856 *encoder) { int i; - struct bt856 *encoder = i2c_get_clientdata(client); - v4l_info(client, "register dump:\n"); + v4l2_info(&encoder->sd, "register dump:\n"); for (i = 0; i < BT856_NR_REG; i += 2) printk(KERN_CONT " %02x", encoder->reg[i]); printk(KERN_CONT "\n"); @@ -91,153 +94,120 @@ static void bt856_dump(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -static int bt856_command(struct i2c_client *client, unsigned cmd, void *arg) +static int bt856_init(struct v4l2_subdev *sd, u32 arg) { - struct bt856 *encoder = i2c_get_clientdata(client); - - switch (cmd) { - case 0: - /* This is just for testing!!! */ - v4l_dbg(1, debug, client, "init\n"); - bt856_write(client, 0xdc, 0x18); - bt856_write(client, 0xda, 0); - bt856_write(client, 0xde, 0); - - bt856_setbit(client, 0xdc, 3, 1); - //bt856_setbit(client, 0xdc, 6, 0); - bt856_setbit(client, 0xdc, 4, 1); - - switch (encoder->norm) { - case VIDEO_MODE_NTSC: - bt856_setbit(client, 0xdc, 2, 0); - break; - - case VIDEO_MODE_PAL: - bt856_setbit(client, 0xdc, 2, 1); - break; - } - - bt856_setbit(client, 0xdc, 1, 1); - bt856_setbit(client, 0xde, 4, 0); - bt856_setbit(client, 0xde, 3, 1); - if (debug != 0) - bt856_dump(client); - break; - - case ENCODER_GET_CAPABILITIES: - { - struct video_encoder_capability *cap = arg; - - v4l_dbg(1, debug, client, "get capabilities\n"); + struct bt856 *encoder = to_bt856(sd); + + /* This is just for testing!!! */ + v4l2_dbg(1, debug, sd, "init\n"); + bt856_write(encoder, 0xdc, 0x18); + bt856_write(encoder, 0xda, 0); + bt856_write(encoder, 0xde, 0); + + bt856_setbit(encoder, 0xdc, 3, 1); + /*bt856_setbit(encoder, 0xdc, 6, 0);*/ + bt856_setbit(encoder, 0xdc, 4, 1); + + if (encoder->norm & V4L2_STD_NTSC) + bt856_setbit(encoder, 0xdc, 2, 0); + else + bt856_setbit(encoder, 0xdc, 2, 1); + + bt856_setbit(encoder, 0xdc, 1, 1); + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); + if (debug != 0) + bt856_dump(encoder); + return 0; +} - cap->flags = VIDEO_ENCODER_PAL | - VIDEO_ENCODER_NTSC | - VIDEO_ENCODER_CCIR; - cap->inputs = 2; - cap->outputs = 1; - break; - } +static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct bt856 *encoder = to_bt856(sd); - case ENCODER_SET_NORM: - { - int *iarg = arg; - - v4l_dbg(1, debug, client, "set norm %d\n", *iarg); - - switch (*iarg) { - case VIDEO_MODE_NTSC: - bt856_setbit(client, 0xdc, 2, 0); - break; - - case VIDEO_MODE_PAL: - bt856_setbit(client, 0xdc, 2, 1); - bt856_setbit(client, 0xda, 0, 0); - //bt856_setbit(client, 0xda, 0, 1); - break; - - default: - return -EINVAL; - } - encoder->norm = *iarg; - if (debug != 0) - bt856_dump(client); - break; - } + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); - case ENCODER_SET_INPUT: - { - int *iarg = arg; - - v4l_dbg(1, debug, client, "set input %d\n", *iarg); - - /* We only have video bus. - * iarg = 0: input is from bt819 - * iarg = 1: input is from ZR36060 */ - switch (*iarg) { - case 0: - bt856_setbit(client, 0xde, 4, 0); - bt856_setbit(client, 0xde, 3, 1); - bt856_setbit(client, 0xdc, 3, 1); - bt856_setbit(client, 0xdc, 6, 0); - break; - case 1: - bt856_setbit(client, 0xde, 4, 0); - bt856_setbit(client, 0xde, 3, 1); - bt856_setbit(client, 0xdc, 3, 1); - bt856_setbit(client, 0xdc, 6, 1); - break; - case 2: // Color bar - bt856_setbit(client, 0xdc, 3, 0); - bt856_setbit(client, 0xde, 4, 1); - break; - default: - return -EINVAL; - } - - if (debug != 0) - bt856_dump(client); - break; + if (std & V4L2_STD_NTSC) { + bt856_setbit(encoder, 0xdc, 2, 0); + } else if (std & V4L2_STD_PAL) { + bt856_setbit(encoder, 0xdc, 2, 1); + bt856_setbit(encoder, 0xda, 0, 0); + /*bt856_setbit(encoder, 0xda, 0, 1);*/ + } else { + return -EINVAL; } + encoder->norm = std; + if (debug != 0) + bt856_dump(encoder); + return 0; +} - case ENCODER_SET_OUTPUT: - { - int *iarg = arg; +static int bt856_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct bt856 *encoder = to_bt856(sd); - v4l_dbg(1, debug, client, "set output %d\n", *iarg); + v4l2_dbg(1, debug, sd, "set input %d\n", route->input); - /* not much choice of outputs */ - if (*iarg != 0) - return -EINVAL; + /* We only have video bus. + * route->input= 0: input is from bt819 + * route->input= 1: input is from ZR36060 */ + switch (route->input) { + case 0: + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); + bt856_setbit(encoder, 0xdc, 3, 1); + bt856_setbit(encoder, 0xdc, 6, 0); break; - } - - case ENCODER_ENABLE_OUTPUT: - { - int *iarg = arg; - - encoder->enable = !!*iarg; - - v4l_dbg(1, debug, client, "enable output %d\n", encoder->enable); + case 1: + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); + bt856_setbit(encoder, 0xdc, 3, 1); + bt856_setbit(encoder, 0xdc, 6, 1); + break; + case 2: /* Color bar */ + bt856_setbit(encoder, 0xdc, 3, 0); + bt856_setbit(encoder, 0xde, 4, 1); break; - } - default: return -EINVAL; } + if (debug != 0) + bt856_dump(encoder); return 0; } +static int bt856_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT856, 0); +} + /* ----------------------------------------------------------------------- */ -static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; +static const struct v4l2_subdev_core_ops bt856_core_ops = { + .g_chip_ident = bt856_g_chip_ident, + .init = bt856_init, +}; + +static const struct v4l2_subdev_video_ops bt856_video_ops = { + .s_std_output = bt856_s_std_output, + .s_routing = bt856_s_routing, +}; -I2C_CLIENT_INSMOD; +static const struct v4l2_subdev_ops bt856_ops = { + .core = &bt856_core_ops, + .video = &bt856_video_ops, +}; + +/* ----------------------------------------------------------------------- */ static int bt856_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bt856 *encoder; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -249,41 +219,38 @@ static int bt856_probe(struct i2c_client *client, encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL); if (encoder == NULL) return -ENOMEM; - encoder->norm = VIDEO_MODE_NTSC; - encoder->enable = 1; - i2c_set_clientdata(client, encoder); + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &bt856_ops); + encoder->norm = V4L2_STD_NTSC; - bt856_write(client, 0xdc, 0x18); - bt856_write(client, 0xda, 0); - bt856_write(client, 0xde, 0); + bt856_write(encoder, 0xdc, 0x18); + bt856_write(encoder, 0xda, 0); + bt856_write(encoder, 0xde, 0); - bt856_setbit(client, 0xdc, 3, 1); - //bt856_setbit(client, 0xdc, 6, 0); - bt856_setbit(client, 0xdc, 4, 1); + bt856_setbit(encoder, 0xdc, 3, 1); + /*bt856_setbit(encoder, 0xdc, 6, 0);*/ + bt856_setbit(encoder, 0xdc, 4, 1); - switch (encoder->norm) { + if (encoder->norm & V4L2_STD_NTSC) + bt856_setbit(encoder, 0xdc, 2, 0); + else + bt856_setbit(encoder, 0xdc, 2, 1); - case VIDEO_MODE_NTSC: - bt856_setbit(client, 0xdc, 2, 0); - break; - - case VIDEO_MODE_PAL: - bt856_setbit(client, 0xdc, 2, 1); - break; - } - - bt856_setbit(client, 0xdc, 1, 1); - bt856_setbit(client, 0xde, 4, 0); - bt856_setbit(client, 0xde, 3, 1); + bt856_setbit(encoder, 0xdc, 1, 1); + bt856_setbit(encoder, 0xde, 4, 0); + bt856_setbit(encoder, 0xde, 3, 1); if (debug != 0) - bt856_dump(client); + bt856_dump(encoder); return 0; } static int bt856_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_bt856(sd)); return 0; } @@ -295,8 +262,6 @@ MODULE_DEVICE_TABLE(i2c, bt856_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "bt856", - .driverid = I2C_DRIVERID_BT856, - .command = bt856_command, .probe = bt856_probe, .remove = bt856_remove, .id_table = bt856_id, diff --git a/drivers/media/video/bt866.c b/drivers/media/video/bt866.c index 596f9e2..350cae4 100644 --- a/drivers/media/video/bt866.c +++ b/drivers/media/video/bt866.c @@ -34,10 +34,10 @@ #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> -#include <linux/video_encoder.h> -#include <media/v4l2-common.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("Brooktree-866 video encoder driver"); MODULE_AUTHOR("Mike Bernson & Dave Perks"); @@ -47,22 +47,22 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); + /* ----------------------------------------------------------------------- */ struct bt866 { + struct v4l2_subdev sd; u8 reg[256]; - - int norm; - int enable; - int bright; - int contrast; - int hue; - int sat; }; -static int bt866_write(struct i2c_client *client, u8 subaddr, u8 data) +static inline struct bt866 *to_bt866(struct v4l2_subdev *sd) { - struct bt866 *encoder = i2c_get_clientdata(client); + return container_of(sd, struct bt866, sd); +} + +static int bt866_write(struct bt866 *encoder, u8 subaddr, u8 data) +{ + struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd); u8 buffer[2]; int err; @@ -89,163 +89,120 @@ static int bt866_write(struct i2c_client *client, u8 subaddr, u8 data) return 0; } -static int bt866_command(struct i2c_client *client, unsigned cmd, void *arg) +static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) { - struct bt866 *encoder = i2c_get_clientdata(client); - - switch (cmd) { - case ENCODER_GET_CAPABILITIES: - { - struct video_encoder_capability *cap = arg; - - v4l_dbg(1, debug, client, "get capabilities\n"); - - cap->flags - = VIDEO_ENCODER_PAL - | VIDEO_ENCODER_NTSC - | VIDEO_ENCODER_CCIR; - cap->inputs = 2; - cap->outputs = 1; - break; - } - - case ENCODER_SET_NORM: - { - int *iarg = arg; - - v4l_dbg(1, debug, client, "set norm %d\n", *iarg); - - switch (*iarg) { - case VIDEO_MODE_NTSC: - break; - - case VIDEO_MODE_PAL: - break; - - default: - return -EINVAL; - } - encoder->norm = *iarg; - break; - } - - case ENCODER_SET_INPUT: - { - int *iarg = arg; - static const __u8 init[] = { - 0xc8, 0xcc, /* CRSCALE */ - 0xca, 0x91, /* CBSCALE */ - 0xcc, 0x24, /* YC16 | OSDNUM */ - 0xda, 0x00, /* */ - 0xdc, 0x24, /* SETMODE | PAL */ - 0xde, 0x02, /* EACTIVE */ - - /* overlay colors */ - 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */ - 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */ - 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */ - 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */ - 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */ - 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */ - 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */ - 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */ - - 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */ - 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */ - 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */ - 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */ - 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */ - 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */ - 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */ - 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */ - }; - int i; - u8 val; - - for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2) - bt866_write(client, init[i], init[i+1]); - - val = encoder->reg[0xdc]; - - if (*iarg == 0) - val |= 0x40; /* CBSWAP */ - else - val &= ~0x40; /* !CBSWAP */ - - bt866_write(client, 0xdc, val); - - val = encoder->reg[0xcc]; - if (*iarg == 2) - val |= 0x01; /* OSDBAR */ - else - val &= ~0x01; /* !OSDBAR */ - bt866_write(client, 0xcc, val); - - v4l_dbg(1, debug, client, "set input %d\n", *iarg); - - switch (*iarg) { - case 0: - break; - case 1: - break; - default: - return -EINVAL; - } - break; - } - - case ENCODER_SET_OUTPUT: - { - int *iarg = arg; - - v4l_dbg(1, debug, client, "set output %d\n", *iarg); - - /* not much choice of outputs */ - if (*iarg != 0) - return -EINVAL; - break; - } - - case ENCODER_ENABLE_OUTPUT: - { - int *iarg = arg; - encoder->enable = !!*iarg; + v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); - v4l_dbg(1, debug, client, "enable output %d\n", encoder->enable); - break; - } - - case 4711: - { - int *iarg = arg; - __u8 val; - - v4l_dbg(1, debug, client, "square %d\n", *iarg); + /* Only PAL supported by this driver at the moment! */ + if (!(std & V4L2_STD_NTSC)) + return -EINVAL; + return 0; +} - val = encoder->reg[0xdc]; - if (*iarg) - val |= 1; /* SQUARE */ - else - val &= ~1; /* !SQUARE */ - bt866_write(client, 0xdc, val); +static int bt866_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + static const __u8 init[] = { + 0xc8, 0xcc, /* CRSCALE */ + 0xca, 0x91, /* CBSCALE */ + 0xcc, 0x24, /* YC16 | OSDNUM */ + 0xda, 0x00, /* */ + 0xdc, 0x24, /* SETMODE | PAL */ + 0xde, 0x02, /* EACTIVE */ + + /* overlay colors */ + 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */ + 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */ + 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */ + 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */ + 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */ + 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */ + 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */ + 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */ + + 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */ + 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */ + 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */ + 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */ + 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */ + 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */ + 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */ + 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */ + }; + struct bt866 *encoder = to_bt866(sd); + u8 val; + int i; + + for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2) + bt866_write(encoder, init[i], init[i+1]); + + val = encoder->reg[0xdc]; + + if (route->input == 0) + val |= 0x40; /* CBSWAP */ + else + val &= ~0x40; /* !CBSWAP */ + + bt866_write(encoder, 0xdc, val); + + val = encoder->reg[0xcc]; + if (route->input == 2) + val |= 0x01; /* OSDBAR */ + else + val &= ~0x01; /* !OSDBAR */ + bt866_write(encoder, 0xcc, val); + + v4l2_dbg(1, debug, sd, "set input %d\n", route->input); + + switch (route->input) { + case 0: + case 1: + case 2: break; - } - default: return -EINVAL; } - return 0; } -static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; +#if 0 +/* Code to setup square pixels, might be of some use in the future, + but is currently unused. */ + val = encoder->reg[0xdc]; + if (*iarg) + val |= 1; /* SQUARE */ + else + val &= ~1; /* !SQUARE */ + bt866_write(client, 0xdc, val); +#endif + +static int bt866_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT866, 0); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops bt866_core_ops = { + .g_chip_ident = bt866_g_chip_ident, +}; + +static const struct v4l2_subdev_video_ops bt866_video_ops = { + .s_std_output = bt866_s_std_output, + .s_routing = bt866_s_routing, +}; -I2C_CLIENT_INSMOD; +static const struct v4l2_subdev_ops bt866_ops = { + .core = &bt866_core_ops, + .video = &bt866_video_ops, +}; static int bt866_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bt866 *encoder; + struct v4l2_subdev *sd; v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); @@ -253,20 +210,18 @@ static int bt866_probe(struct i2c_client *client, encoder = kzalloc(sizeof(*encoder), GFP_KERNEL); if (encoder == NULL) return -ENOMEM; - - i2c_set_clientdata(client, encoder); + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &bt866_ops); return 0; } static int bt866_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); - return 0; -} + struct v4l2_subdev *sd = i2c_get_clientdata(client); -static int bt866_legacy_probe(struct i2c_adapter *adapter) -{ - return adapter->id == I2C_HW_B_ZR36067; + v4l2_device_unregister_subdev(sd); + kfree(to_bt866(sd)); + return 0; } static const struct i2c_device_id bt866_id[] = { @@ -277,10 +232,7 @@ MODULE_DEVICE_TABLE(i2c, bt866_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "bt866", - .driverid = I2C_DRIVERID_BT866, - .command = bt866_command, .probe = bt866_probe, .remove = bt866_remove, - .legacy_probe = bt866_legacy_probe, .id_table = bt866_id, }; diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig index ce71e8e..3077c450 100644 --- a/drivers/media/video/bt8xx/Kconfig +++ b/drivers/media/video/bt8xx/Kconfig @@ -10,7 +10,7 @@ config VIDEO_BT848 select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TDA7432 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TDA9875 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO ---help--- Support for BT848 based frame grabber/overlay boards. This includes the Miro, Hauppauge and STB boards. Please read the material in diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index d24dcc0..b9c3ba5 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -73,6 +73,11 @@ static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input); static void geovision_muxsel(struct bttv *btv, unsigned int input); +static void phytec_muxsel(struct bttv *btv, unsigned int input); + +static void gv800s_muxsel(struct bttv *btv, unsigned int input); +static void gv800s_init(struct bttv *btv); + static int terratec_active_radio_upgrade(struct bttv *btv); static int tea5757_read(struct bttv *btv); static int tea5757_write(struct bttv *btv, int value); @@ -91,12 +96,10 @@ static unsigned int pll[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; static unsigned int tuner[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; static unsigned int svhs[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; static unsigned int remote[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; +static unsigned int audiodev[BTTV_MAX]; +static unsigned int saa6588[BTTV_MAX]; static struct bttv *master[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = NULL }; -#ifdef MODULE -static unsigned int autoload = 1; -#else -static unsigned int autoload; -#endif +static unsigned int autoload = UNSET; static unsigned int gpiomask = UNSET; static unsigned int audioall = UNSET; static unsigned int audiomux[5] = { [ 0 ... 4 ] = UNSET }; @@ -115,6 +118,7 @@ module_param_array(pll, int, NULL, 0444); module_param_array(tuner, int, NULL, 0444); module_param_array(svhs, int, NULL, 0444); module_param_array(remote, int, NULL, 0444); +module_param_array(audiodev, int, NULL, 0444); module_param_array(audiomux, int, NULL, 0444); MODULE_PARM_DESC(triton1,"set ETBF pci config bit " @@ -125,7 +129,14 @@ MODULE_PARM_DESC(latency,"pci latency timer"); MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list"); MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)"); MODULE_PARM_DESC(tuner,"specify installed tuner type"); -MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)"); +MODULE_PARM_DESC(autoload, "obsolete option, please do not use anymore"); +MODULE_PARM_DESC(audiodev, "specify audio device:\n" + "\t\t-1 = no audio\n" + "\t\t 0 = autodetect (default)\n" + "\t\t 1 = msp3400\n" + "\t\t 2 = tda7432\n" + "\t\t 3 = tvaudio"); +MODULE_PARM_DESC(saa6588, "if 1, then load the saa6588 RDS module, default (0) is to use the card definition."); MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)" " [some VIA/SIS chipsets are known to have problem with overlay]"); @@ -246,6 +257,10 @@ static struct CARD { { 0xa182ff0d, BTTV_BOARD_IVC120, "IVC-120G" }, { 0xa182ff0e, BTTV_BOARD_IVC120, "IVC-120G" }, { 0xa182ff0f, BTTV_BOARD_IVC120, "IVC-120G" }, + { 0xf0500000, BTTV_BOARD_IVCE8784, "IVCE-8784" }, + { 0xf0500001, BTTV_BOARD_IVCE8784, "IVCE-8784" }, + { 0xf0500002, BTTV_BOARD_IVCE8784, "IVCE-8784" }, + { 0xf0500003, BTTV_BOARD_IVCE8784, "IVCE-8784" }, { 0x41424344, BTTV_BOARD_GRANDTEC, "GrandTec Multi Capture" }, { 0x01020304, BTTV_BOARD_XGUARD, "Grandtec Grand X-Guard" }, @@ -289,6 +304,8 @@ static struct CARD { /* Duplicate PCI ID, reconfigure for this board during the eeprom read. * { 0x13eb0070, BTTV_BOARD_HAUPPAUGE_IMPACTVCB, "Hauppauge ImpactVCB" }, */ + { 0x109e036e, BTTV_BOARD_CONCEPTRONIC_CTVFMI2, "Conceptronic CTVFMi v2"}, + /* DVB cards (using pci function .1 for mpeg data xfer) */ { 0x001c11bd, BTTV_BOARD_PINNACLESAT, "Pinnacle PCTV Sat" }, { 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" }, @@ -305,6 +322,20 @@ static struct CARD { { 0xd200dbc0, BTTV_BOARD_DVICO_FUSIONHDTV_2, "DViCO FusionHDTV 2" }, { 0x763c008a, BTTV_BOARD_GEOVISION_GV600, "GeoVision GV-600" }, { 0x18011000, BTTV_BOARD_ENLTV_FM_2, "Encore ENL TV-FM-2" }, + { 0x763d800a, BTTV_BOARD_GEOVISION_GV800S, "GeoVision GV-800(S) (master)" }, + { 0x763d800b, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, + { 0x763d800c, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, + { 0x763d800d, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, + + { 0x15401830, BTTV_BOARD_PV183, "Provideo PV183-1" }, + { 0x15401831, BTTV_BOARD_PV183, "Provideo PV183-2" }, + { 0x15401832, BTTV_BOARD_PV183, "Provideo PV183-3" }, + { 0x15401833, BTTV_BOARD_PV183, "Provideo PV183-4" }, + { 0x15401834, BTTV_BOARD_PV183, "Provideo PV183-5" }, + { 0x15401835, BTTV_BOARD_PV183, "Provideo PV183-6" }, + { 0x15401836, BTTV_BOARD_PV183, "Provideo PV183-7" }, + { 0x15401837, BTTV_BOARD_PV183, "Provideo PV183-8" }, + { 0, -1, NULL } }; @@ -316,59 +347,50 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_UNKNOWN] = { .name = " *** UNKNOWN/GENERIC *** ", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, .svhs = 2, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_MIRO] = { .name = "MIRO PCTV", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 2, 0, 0, 0 }, .gpiomute = 10, .needs_tvaudio = 1, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_HAUPPAUGE] = { .name = "Hauppauge (bt848)", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 1, 2, 3 }, .gpiomute = 4, .needs_tvaudio = 1, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_STB] = { .name = "STB, Gateway P/N 6000699 (bt848)", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 4, 0, 2, 3 }, .gpiomute = 1, .no_msp34xx = 1, .needs_tvaudio = 1, .tuner_type = TUNER_PHILIPS_NTSC, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, .has_radio = 1, }, @@ -377,202 +399,177 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_INTEL] = { .name = "Intel Create and Share PCI/ Smart Video Recorder III", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 2, .gpiomask = 0, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0 }, .needs_tvaudio = 0, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_DIAMOND] = { .name = "Diamond DTV2000", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 3, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .gpiomux = { 0, 1, 0, 1 }, .gpiomute = 3, .needs_tvaudio = 1, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_AVERMEDIA] = { .name = "AVerMedia TVPhone", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 3, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomask = 0x0f, .gpiomux = { 0x0c, 0x04, 0x08, 0x04 }, /* 0x04 for some cards ?? */ .needs_tvaudio = 1, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .audio_mode_gpio= avermedia_tvphone_audio, .has_remote = 1, }, [BTTV_BOARD_MATRIX_VISION] = { .name = "MATRIX-Vision MV-Delta", .video_inputs = 5, - .audio_inputs = 1, - .tuner = UNSET, + /* .audio_inputs= 1, */ .svhs = 3, .gpiomask = 0, - .muxsel = { 2, 3, 1, 0, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0, 0), .gpiomux = { 0 }, .needs_tvaudio = 1, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x08 ---------------------------------- */ [BTTV_BOARD_FLYVIDEO] = { .name = "Lifeview FlyVideo II (Bt848) LR26 / MAXI TV Video PCI2 LR26", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0xc00, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0xc00, 0x800, 0x400 }, .gpiomute = 0xc00, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_TURBOTV] = { .name = "IMS/IXmicro TurboTV", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 3, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 1, 1, 2, 3 }, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_HAUPPAUGE878] = { .name = "Hauppauge (bt878)", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x0f, /* old: 7 */ - .muxsel = { 2, 0, 1, 1 }, + .muxsel = MUXSEL(2, 0, 1, 1), .gpiomux = { 0, 1, 2, 3 }, .gpiomute = 4, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_MIROPRO] = { .name = "MIRO PCTV pro", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x3014f, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x20001,0x10001, 0, 0 }, .gpiomute = 10, .needs_tvaudio = 1, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x0c ---------------------------------- */ [BTTV_BOARD_ADSTECH_TV] = { .name = "ADS Technologies Channel Surfer TV (bt848)", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 13, 14, 11, 7 }, .needs_tvaudio = 1, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_AVERMEDIA98] = { .name = "AVerMedia TVCapture 98", .video_inputs = 3, - .audio_inputs = 4, - .tuner = 0, + /* .audio_inputs= 4, */ .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 13, 14, 11, 7 }, .needs_tvaudio = 1, .msp34xx_alt = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .audio_mode_gpio= avermedia_tv_stereo_audio, .no_gpioirq = 1, }, [BTTV_BOARD_VHX] = { .name = "Aimslab Video Highway Xtreme (VHX)", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 2, 1, 3 }, /* old: {0, 1, 2, 3, 4} */ .gpiomute = 4, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_ZOLTRIX] = { .name = "Zoltrix TV-Max", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0, 1, 0 }, .gpiomute = 10, .needs_tvaudio = 1, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x10 ---------------------------------- */ [BTTV_BOARD_PIXVIEWPLAYTV] = { .name = "Prolink Pixelview PlayTV (bt878)", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x01fe00, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), /* 2003-10-20 by "Anton A. Arapov" <arapov@mail.ru> */ .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 }, .gpiomute = 0x002000, @@ -580,194 +577,170 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_WINVIEW_601] = { .name = "Leadtek WinView 601", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x8300f8, - .muxsel = { 2, 3, 1, 1,0 }, + .muxsel = MUXSEL(2, 3, 1, 1, 0), .gpiomux = { 0x4fa007,0xcfa007,0xcfa007,0xcfa007 }, .gpiomute = 0xcfa007, .needs_tvaudio = 1, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .volume_gpio = winview_volume, .has_radio = 1, }, [BTTV_BOARD_AVEC_INTERCAP] = { .name = "AVEC Intercapture", .video_inputs = 3, - .audio_inputs = 2, - .tuner = 0, + /* .audio_inputs= 2, */ .svhs = 2, .gpiomask = 0, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 1, 0, 0, 0 }, .needs_tvaudio = 1, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_LIFE_FLYKIT] = { .name = "Lifeview FlyVideo II EZ /FlyKit LR38 Bt848 (capture only)", .video_inputs = 4, - .audio_inputs = 1, - .tuner = UNSET, - .svhs = UNSET, + /* .audio_inputs= 1, */ + .svhs = NO_SVHS, .gpiomask = 0x8dff00, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0 }, .no_msp34xx = 1, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x14 ---------------------------------- */ [BTTV_BOARD_CEI_RAFFLES] = { .name = "CEI Raffles Card", .video_inputs = 3, - .audio_inputs = 3, - .tuner = 0, + /* .audio_inputs= 3, */ .svhs = 2, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_CONFERENCETV] = { .name = "Lifeview FlyVideo 98/ Lucky Star Image World ConferenceTV LR50", .video_inputs = 4, - .audio_inputs = 2, /* tuner, line in */ - .tuner = 0, + /* .audio_inputs= 2, tuner, line in */ .svhs = 2, .gpiomask = 0x1800, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_PHOEBE_TVMAS] = { .name = "Askey CPH050/ Phoebe Tv Master + FM", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0xc00, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 1, 0x800, 0x400 }, .gpiomute = 0xc00, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_MODTEC_205] = { .name = "Modular Technology MM201/MM202/MM205/MM210/MM215 PCTV, bt878", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, - .svhs = UNSET, + /* .audio_inputs= 1, */ + .svhs = NO_SVHS, + .has_dig_in = 1, .gpiomask = 7, - .muxsel = { 2, 3, -1 }, - .digital_mode = DIGITAL_MODE_CAMERA, + .muxsel = MUXSEL(2, 3, 0), /* input 2 is digital */ + /* .digital_mode= DIGITAL_MODE_CAMERA, */ .gpiomux = { 0, 0, 0, 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_ALPS_TSBB5_PAL_I, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x18 ---------------------------------- */ [BTTV_BOARD_MAGICTVIEW061] = { .name = "Askey CPH05X/06X (bt878) [many vendors]", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0xe00, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = {0x400, 0x400, 0x400, 0x400 }, .gpiomute = 0xc00, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_remote = 1, }, [BTTV_BOARD_VOBIS_BOOSTAR] = { .name = "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x1f0fff, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x20000, 0x30000, 0x10000, 0 }, .gpiomute = 0x40000, .needs_tvaudio = 0, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .audio_mode_gpio= terratv_audio, }, [BTTV_BOARD_HAUPPAUG_WCAM] = { .name = "Hauppauge WinCam newer (bt878)", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 3, .gpiomask = 7, - .muxsel = { 2, 0, 1, 1 }, + .muxsel = MUXSEL(2, 0, 1, 1), .gpiomux = { 0, 1, 2, 3 }, .gpiomute = 4, .needs_tvaudio = 1, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_MAXI] = { .name = "Lifeview FlyVideo 98/ MAXI TV Video PCI2 LR50", .video_inputs = 4, - .audio_inputs = 2, - .tuner = 0, + /* .audio_inputs= 2, */ .svhs = 2, .gpiomask = 0x1800, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_SECAM, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x1c ---------------------------------- */ [BTTV_BOARD_TERRATV] = { .name = "Terratec TerraTV+ Version 1.1 (bt878)", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x1f0fff, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x20000, 0x30000, 0x10000, 0x00000 }, .gpiomute = 0x40000, .needs_tvaudio = 0, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .audio_mode_gpio= terratv_audio, /* GPIO wiring: External 20 pin connector (for Active Radio Upgrade board) @@ -805,87 +778,77 @@ struct tvcard bttv_tvcards[] = { /* Jannik Fritsch <jannik@techfak.uni-bielefeld.de> */ .name = "Imagenation PXC200", .video_inputs = 5, - .audio_inputs = 1, - .tuner = UNSET, + /* .audio_inputs= 1, */ .svhs = 1, /* was: 4 */ .gpiomask = 0, - .muxsel = { 2, 3, 1, 0, 0}, + .muxsel = MUXSEL(2, 3, 1, 0, 0), .gpiomux = { 0 }, .needs_tvaudio = 1, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .muxsel_hook = PXC200_muxsel, }, [BTTV_BOARD_FLYVIDEO_98] = { .name = "Lifeview FlyVideo 98 LR50", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x1800, /* 0x8dfe00 */ - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0x0800, 0x1000, 0x1000 }, .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_IPROTV] = { .name = "Formac iProTV, Formac ProTV I (bt848)", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 3, .gpiomask = 1, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 1, 0, 0, 0 }, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x20 ---------------------------------- */ [BTTV_BOARD_INTEL_C_S_PCI] = { .name = "Intel Create and Share PCI/ Smart Video Recorder III", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 2, .gpiomask = 0, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0 }, .needs_tvaudio = 0, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_TERRATVALUE] = { .name = "Terratec TerraTValue Version Bt878", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0xffff00, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x500, 0, 0x300, 0x900 }, .gpiomute = 0x900, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_WINFAST2000] = { .name = "Leadtek WinFast 2000/ WinFast 2000 XP", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, - .muxsel = { 2, 3, 1, 1, 0 }, /* TV, CVid, SVid, CVid over SVid connector */ + /* TV, CVid, SVid, CVid over SVid connector */ + .muxsel = MUXSEL(2, 3, 1, 1, 0), /* Alexander Varakin <avarakin@hotmail.com> [stereo version] */ .gpiomask = 0xb33000, .gpiomux = { 0x122000,0x1000,0x0000,0x620000 }, @@ -906,217 +869,191 @@ struct tvcard bttv_tvcards[] = { .has_radio = 1, .tuner_type = TUNER_PHILIPS_PAL, /* default for now, gpio reads BFFF06 for Pal bg+dk */ .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .audio_mode_gpio= winfast2000_audio, .has_remote = 1, }, [BTTV_BOARD_CHRONOS_VS2] = { .name = "Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II", .video_inputs = 4, - .audio_inputs = 3, - .tuner = 0, + /* .audio_inputs= 3, */ .svhs = 2, .gpiomask = 0x1800, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x24 ---------------------------------- */ [BTTV_BOARD_TYPHOON_TVIEW] = { .name = "Lifeview FlyVideo 98FM LR50 / Typhoon TView TV/FM Tuner", .video_inputs = 4, - .audio_inputs = 3, - .tuner = 0, + /* .audio_inputs= 3, */ .svhs = 2, .gpiomask = 0x1800, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_radio = 1, }, [BTTV_BOARD_PXELVWPLTVPRO] = { .name = "Prolink PixelView PlayTV pro", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0xff, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x21, 0x20, 0x24, 0x2c }, .gpiomute = 0x29, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_MAGICTVIEW063] = { .name = "Askey CPH06X TView99", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x551e00, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .gpiomux = { 0x551400, 0x551200, 0, 0 }, .gpiomute = 0x551c00, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_remote = 1, }, [BTTV_BOARD_PINNACLE] = { .name = "Pinnacle PCTV Studio/Rave", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x03000F, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 2, 0xd0001, 0, 0 }, .gpiomute = 1, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x28 ---------------------------------- */ [BTTV_BOARD_STB2] = { .name = "STB TV PCI FM, Gateway P/N 6000704 (bt878), 3Dfx VoodooTV 100", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 4, 0, 2, 3 }, .gpiomute = 1, .no_msp34xx = 1, .needs_tvaudio = 1, .tuner_type = TUNER_PHILIPS_NTSC, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, .has_radio = 1, }, [BTTV_BOARD_AVPHONE98] = { .name = "AVerMedia TVPhone 98", .video_inputs = 3, - .audio_inputs = 4, - .tuner = 0, + /* .audio_inputs= 4, */ .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 13, 4, 11, 7 }, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_radio = 1, .audio_mode_gpio= avermedia_tvphone_audio, }, [BTTV_BOARD_PV951] = { .name = "ProVideo PV951", /* pic16c54 */ .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0, - .muxsel = { 2, 3, 1, 1}, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0, 0, 0}, .needs_tvaudio = 1, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_ONAIR_TV] = { .name = "Little OnAir TV", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0xe00b, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0xff9ff6, 0xff9ff6, 0xff1ff7, 0 }, .gpiomute = 0xff3ffc, .no_msp34xx = 1, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x2c ---------------------------------- */ [BTTV_BOARD_SIGMA_TVII_FM] = { .name = "Sigma TVII-FM", .video_inputs = 2, - .audio_inputs = 1, - .tuner = 0, - .svhs = UNSET, + /* .audio_inputs= 1, */ + .svhs = NO_SVHS, .gpiomask = 3, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 1, 1, 0, 2 }, .gpiomute = 3, .no_msp34xx = 1, .pll = PLL_NONE, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_MATRIX_VISION2] = { .name = "MATRIX-Vision MV-Delta 2", .video_inputs = 5, - .audio_inputs = 1, - .tuner = UNSET, + /* .audio_inputs= 1, */ .svhs = 3, .gpiomask = 0, - .muxsel = { 2, 3, 1, 0, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0, 0), .gpiomux = { 0 }, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_ZOLTRIX_GENIE] = { .name = "Zoltrix Genie TV/FM", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0xbcf03f, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0xbc803f, 0xbc903f, 0xbcb03f, 0 }, .gpiomute = 0xbcb03f, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_TEMIC_4039FR5_NTSC, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_TERRATVRADIO] = { .name = "Terratec TV/Radio+", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x70000, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x20000, 0x30000, 0x10000, 0 }, .gpiomute = 0x40000, .needs_tvaudio = 1, @@ -1124,7 +1061,6 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_35, .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_radio = 1, }, @@ -1132,51 +1068,46 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_DYNALINK] = { .name = "Askey CPH03x/ Dynalink Magic TView", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = {2,0,0,0 }, .gpiomute = 1, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_GVBCTV3PCI] = { .name = "IODATA GV-BCTV3/PCI", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x010f00, - .muxsel = {2, 3, 0, 0 }, + .muxsel = MUXSEL(2, 3, 0, 0), .gpiomux = {0x10000, 0, 0x10000, 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_ALPS_TSHC6_NTSC, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .audio_mode_gpio= gvbctv3pci_audio, }, [BTTV_BOARD_PXELVWPLTVPAK] = { .name = "Prolink PV-BT878P+4E / PixelView PlayTV PAK / Lenco MXTV-9578 CP", .video_inputs = 5, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 3, + .has_dig_in = 1, .gpiomask = 0xAA0000, - .muxsel = { 2,3,1,1,-1 }, - .digital_mode = DIGITAL_MODE_CAMERA, + .muxsel = MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */ + /* .digital_mode= DIGITAL_MODE_CAMERA, */ .gpiomux = { 0x20000, 0, 0x80000, 0x80000 }, .gpiomute = 0xa8000, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_remote = 1, /* GPIO wiring: (different from Rev.4C !) GPIO17: U4.A0 (first hef4052bt) @@ -1191,17 +1122,15 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_EAGLE] = { .name = "Eagle Wireless Capricorn2 (bt878A)", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 0, 1, 1 }, + .muxsel = MUXSEL(2, 0, 1, 1), .gpiomux = { 0, 1, 2, 3 }, .gpiomute = 4, .pll = PLL_28, .tuner_type = UNSET /* TUNER_ALPS_TMDH2_NTSC */, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x34 ---------------------------------- */ @@ -1209,11 +1138,10 @@ struct tvcard bttv_tvcards[] = { /* David Härdeman <david@2gen.com> */ .name = "Pinnacle PCTV Studio Pro", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 3, .gpiomask = 0x03000F, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 1, 0xd0001, 0, 0 }, .gpiomute = 10, /* sound path (5 sources): @@ -1229,25 +1157,22 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_TVIEW_RDS_FM] = { /* Claas Langbehn <claas@bigfoot.com>, Sven Grothklags <sven@upb.de> */ .name = "Typhoon TView RDS + FM Stereo / KNC1 TV Station RDS", .video_inputs = 4, - .audio_inputs = 3, - .tuner = 0, + /* .audio_inputs= 3, */ .svhs = 2, .gpiomask = 0x1c, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0, 0x10, 8 }, .gpiomute = 4, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_radio = 1, }, [BTTV_BOARD_LIFETEC_9415] = { @@ -1258,11 +1183,10 @@ struct tvcard bttv_tvcards[] = { options tuner type=5 */ .name = "Lifeview FlyVideo 2000 /FlyVideo A2/ Lifetec LT 9415 TV [LR90]", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x18e0, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x0000,0x0800,0x1000,0x1000 }, .gpiomute = 0x18e0, /* For cards with tda9820/tda9821: @@ -1272,25 +1196,22 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_BESTBUY_EASYTV] = { /* Miguel Angel Alvarez <maacruz@navegalia.com> old Easy TV BT848 version (model CPH031) */ .name = "Askey CPH031/ BESTBUY Easy TV", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0xF, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .gpiomux = { 2, 0, 0, 0 }, .gpiomute = 10, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x38 ---------------------------------- */ @@ -1298,17 +1219,15 @@ struct tvcard bttv_tvcards[] = { /* Gordon Heydon <gjheydon@bigfoot.com ('98) */ .name = "Lifeview FlyVideo 98FM LR50", .video_inputs = 4, - .audio_inputs = 3, - .tuner = 0, + /* .audio_inputs= 3, */ .svhs = 2, .gpiomask = 0x1800, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, .gpiomute = 0x1800, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* This is the ultimate cheapo capture card * just a BT848A on a small PCB! @@ -1316,51 +1235,45 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_GRANDTEC] = { .name = "GrandTec 'Grand Video Capture' (Bt848)", .video_inputs = 2, - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 1, .gpiomask = 0, - .muxsel = { 3, 1 }, + .muxsel = MUXSEL(3, 1), .gpiomux = { 0 }, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_35, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_ASKEY_CPH060] = { /* Daniel Herrington <daniel.herrington@home.com> */ .name = "Askey CPH060/ Phoebe TV Master Only (No FM)", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0xe00, - .muxsel = { 2, 3, 1, 1}, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x400, 0x400, 0x400, 0x400 }, .gpiomute = 0x800, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_TEMIC_4036FY5_NTSC, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_ASKEY_CPH03X] = { /* Matti Mottus <mottus@physic.ut.ee> */ .name = "Askey CPH03x TV Capturer", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x03000F, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .gpiomux = { 2, 0, 0, 0 }, .gpiomute = 1, .pll = PLL_28, .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x3c ---------------------------------- */ @@ -1368,34 +1281,30 @@ struct tvcard bttv_tvcards[] = { /* Philip Blundell <philb@gnu.org> */ .name = "Modular Technology MM100PCTV", .video_inputs = 2, - .audio_inputs = 2, - .tuner = 0, - .svhs = UNSET, + /* .audio_inputs= 2, */ + .svhs = NO_SVHS, .gpiomask = 11, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 2, 0, 0, 1 }, .gpiomute = 8, .pll = PLL_35, .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_GMV1] = { /* Adrian Cox <adrian@humboldt.co.uk */ .name = "AG Electronics GMV1", .video_inputs = 2, - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 1, .gpiomask = 0xF, - .muxsel = { 2, 2 }, + .muxsel = MUXSEL(2, 2), .gpiomux = { }, .no_msp34xx = 1, .needs_tvaudio = 0, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_BESTBUY_EASYTV2] = { /* Miguel Angel Alvarez <maacruz@navegalia.com> @@ -1403,34 +1312,30 @@ struct tvcard bttv_tvcards[] = { special thanks to Informatica Mieres for providing the card */ .name = "Askey CPH061/ BESTBUY Easy TV (bt878)", .video_inputs = 3, - .audio_inputs = 2, - .tuner = 0, + /* .audio_inputs= 2, */ .svhs = 2, .gpiomask = 0xFF, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .gpiomux = { 1, 0, 4, 4 }, .gpiomute = 9, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_ATI_TVWONDER] = { /* Lukas Gebauer <geby@volny.cz> */ .name = "ATI TV-Wonder", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0xf03f, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .gpiomux = { 0xbffe, 0, 0xbfff, 0 }, .gpiomute = 0xbffe, .pll = PLL_28, .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x40 ---------------------------------- */ @@ -1438,27 +1343,24 @@ struct tvcard bttv_tvcards[] = { /* Lukas Gebauer <geby@volny.cz> */ .name = "ATI TV-Wonder VE", .video_inputs = 2, - .audio_inputs = 1, - .tuner = 0, - .svhs = UNSET, + /* .audio_inputs= 1, */ + .svhs = NO_SVHS, .gpiomask = 1, - .muxsel = { 2, 3, 0, 1 }, + .muxsel = MUXSEL(2, 3, 0, 1), .gpiomux = { 0, 0, 1, 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_FLYVIDEO2000] = { /* DeeJay <deejay@westel900.net (2000S) */ .name = "Lifeview FlyVideo 2000S LR90", .video_inputs = 3, - .audio_inputs = 3, - .tuner = 0, + /* .audio_inputs= 3, */ .svhs = 2, .gpiomask = 0x18e0, - .muxsel = { 2, 3, 0, 1 }, + .muxsel = MUXSEL(2, 3, 0, 1), /* Radio changed from 1e80 to 0x800 to make FlyVideo2000S in .hu happy (gm)*/ /* -dk-???: set mute=0x1800 for tda9874h daughterboard */ @@ -1471,40 +1373,35 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_TERRATVALUER] = { .name = "Terratec TValueRadio", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0xffff00, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x500, 0x500, 0x300, 0x900 }, .gpiomute = 0x900, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_radio = 1, }, [BTTV_BOARD_GVBCTV4PCI] = { /* TANAKA Kei <peg00625@nifty.com> */ .name = "IODATA GV-BCTV4/PCI", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x010f00, - .muxsel = {2, 3, 0, 0 }, + .muxsel = MUXSEL(2, 3, 0, 0), .gpiomux = {0x10000, 0, 0x10000, 0 }, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_SHARP_2U5JF5540_NTSC, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .audio_mode_gpio= gvbctv3pci_audio, }, @@ -1514,9 +1411,8 @@ struct tvcard bttv_tvcards[] = { /* try "insmod msp3400 simple=0" if you have * sound problems with this card. */ .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, - .svhs = UNSET, + /* .audio_inputs= 1, */ + .svhs = NO_SVHS, .gpiomask = 0x4f8a00, /* 0x100000: 1=MSP enabled (0=disable again) * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */ @@ -1524,10 +1420,9 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 0x947fff, /* tvtuner, radio, external,internal, mute, stereo * tuner, Composit, SVid, Composit-on-Svid-adapter */ - .muxsel = { 2, 3 ,0 ,1 }, + .muxsel = MUXSEL(2, 3, 0, 1), .tuner_type = TUNER_MT2032, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, .has_radio = 1, }, @@ -1536,9 +1431,8 @@ struct tvcard bttv_tvcards[] = { /* try "insmod msp3400 simple=0" if you have * sound problems with this card. */ .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, - .svhs = UNSET, + /* .audio_inputs= 1, */ + .svhs = NO_SVHS, .gpiomask = 0x4f8a00, /* 0x100000: 1=MSP enabled (0=disable again) * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */ @@ -1546,10 +1440,9 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 0x947fff, /* tvtuner, radio, external,internal, mute, stereo * tuner, Composit, SVid, Composit-on-Svid-adapter */ - .muxsel = { 2, 3 ,0 ,1 }, + .muxsel = MUXSEL(2, 3, 0, 1), .tuner_type = TUNER_MT2032, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, .has_radio = 1, }, @@ -1557,31 +1450,27 @@ struct tvcard bttv_tvcards[] = { /* Philip Blundell <pb@nexus.co.uk> */ .name = "Active Imaging AIMMS", .video_inputs = 1, - .audio_inputs = 0, - .tuner = UNSET, - .tuner_type = UNSET, + /* .audio_inputs= 0, */ + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, - .muxsel = { 2 }, + .muxsel = MUXSEL(2), .gpiomask = 0 }, [BTTV_BOARD_PV_BT878P_PLUS] = { /* Tomasz Pyra <hellfire@sedez.iq.pl> */ .name = "Prolink Pixelview PV-BT878P+ (Rev.4C,8E)", .video_inputs = 3, - .audio_inputs = 4, - .tuner = 0, + /* .audio_inputs= 4, */ .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0, 11, 7 }, /* TV and Radio with same GPIO ! */ .gpiomute = 13, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_LG_PAL_I_FM, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_remote = 1, /* GPIO wiring: GPIO0: U4.A0 (hef4052bt) @@ -1594,15 +1483,14 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_FLYVIDEO98EZ] = { .name = "Lifeview FlyVideo 98EZ (capture only) LR51", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 2, - .muxsel = { 2, 3, 1, 1 }, /* AV1, AV2, SVHS, CVid adapter on SVHS */ + /* AV1, AV2, SVHS, CVid adapter on SVHS */ + .muxsel = MUXSEL(2, 3, 1, 1), .pll = PLL_28, .no_msp34xx = 1, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x48 ---------------------------------- */ @@ -1610,11 +1498,10 @@ struct tvcard bttv_tvcards[] = { /* Dariusz Kowalewski <darekk@automex.pl> */ .name = "Prolink Pixelview PV-BT878P+9B (PlayTV Pro rev.9B FM+NICAM)", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x3f, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x01, 0x00, 0x03, 0x03 }, .gpiomute = 0x09, .needs_tvaudio = 1, @@ -1623,7 +1510,6 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .audio_mode_gpio= pvbt878p9b_audio, /* Note: not all cards have stereo */ .has_radio = 1, /* Note: not all cards have radio */ .has_remote = 1, @@ -1640,49 +1526,42 @@ struct tvcard bttv_tvcards[] = { /* you must jumper JP5 for the card to work */ .name = "Sensoray 311", .video_inputs = 5, - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 4, .gpiomask = 0, - .muxsel = { 2, 3, 1, 0, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0, 0), .gpiomux = { 0 }, .needs_tvaudio = 0, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_RV605] = { /* Miguel Freitas <miguel@cetuc.puc-rio.br> */ .name = "RemoteVision MX (RV605)", .video_inputs = 16, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, .gpiomask = 0x00, .gpiomask2 = 0x07ff, - .muxsel = { 0x33, 0x13, 0x23, 0x43, 0xf3, 0x73, 0xe3, 0x03, - 0xd3, 0xb3, 0xc3, 0x63, 0x93, 0x53, 0x83, 0xa3 }, + .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3), .no_msp34xx = 1, .no_tda9875 = 1, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .muxsel_hook = rv605_muxsel, }, [BTTV_BOARD_POWERCLR_MTV878] = { .name = "Powercolor MTV878/ MTV878R/ MTV878F", .video_inputs = 3, - .audio_inputs = 2, - .tuner = 0, + /* .audio_inputs= 2, */ .svhs = 2, .gpiomask = 0x1C800F, /* Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset */ - .muxsel = { 2, 1, 1, }, + .muxsel = MUXSEL(2, 1, 1), .gpiomux = { 0, 1, 2, 2 }, .gpiomute = 4, .needs_tvaudio = 0, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, .has_radio = 1, }, @@ -1692,42 +1571,38 @@ struct tvcard bttv_tvcards[] = { /* Masaki Suzuki <masaki@btree.org> */ .name = "Canopus WinDVR PCI (COMPAQ Presario 3524JP, 5112JP)", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x140007, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 1, 2, 3 }, .gpiomute = 4, .tuner_type = TUNER_PHILIPS_NTSC, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .audio_mode_gpio= windvr_audio, }, [BTTV_BOARD_GRANDTEC_MULTI] = { .name = "GrandTec Multi Capture Card (Bt878)", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, .gpiomask = 0, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .gpiomux = { 0 }, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_KWORLD] = { .name = "Jetway TV/Capture JW-TV878-FBK, Kworld KW-TV878RF", .video_inputs = 4, - .audio_inputs = 3, - .tuner = 0, + /* .audio_inputs= 3, */ .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 3, 1, 1 }, /* Tuner, SVid, SVHS, SVid to SVHS connector */ + /* Tuner, SVid, SVHS, SVid to SVHS connector */ + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0, 4, 4 },/* Yes, this tuner uses the same audio output for TV and FM radio! * This card lacks external Audio In, so we mute it on Ext. & Int. * The PCB can take a sbx1637/sbx1673, wiring unknown. @@ -1741,7 +1616,6 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, /* Samsung TCPA9095PC27A (BG+DK), philips compatible, w/FM, stereo and radio signal strength indicators work fine. */ .has_radio = 1, @@ -1759,27 +1633,24 @@ struct tvcard bttv_tvcards[] = { /* Arthur Tetzlaff-Deas, DSP Design Ltd <software@dspdesign.com> */ .name = "DSP Design TCVIDEO", .video_inputs = 4, - .svhs = UNSET, - .muxsel = { 2, 3, 1, 0 }, + .svhs = NO_SVHS, + .muxsel = MUXSEL(2, 3, 1, 0), .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x50 ---------------------------------- */ [BTTV_BOARD_HAUPPAUGEPVR] = { .name = "Hauppauge WinTV PVR", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, - .muxsel = { 2, 0, 1, 1 }, + .muxsel = MUXSEL(2, 0, 1, 1), .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .gpiomask = 7, .gpiomux = {7}, @@ -1787,32 +1658,28 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_GVBCTV5PCI] = { .name = "IODATA GV-BCTV5/PCI", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x0f0f80, - .muxsel = {2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .gpiomux = {0x030000, 0x010000, 0, 0 }, .gpiomute = 0x020000, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_NTSC_M, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .audio_mode_gpio= gvbctv5pci_audio, .has_radio = 1, }, [BTTV_BOARD_OSPREY1x0] = { .name = "Osprey 100/150 (878)", /* 0x1(2|3)-45C6-C1 */ .video_inputs = 4, /* id-inputs-clock */ - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 3, - .muxsel = { 3, 2, 0, 1 }, + .muxsel = MUXSEL(3, 2, 0, 1), .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -1820,14 +1687,12 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_OSPREY1x0_848] = { .name = "Osprey 100/150 (848)", /* 0x04-54C0-C1 & older boards */ .video_inputs = 3, - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 2, - .muxsel = { 2, 3, 1 }, + .muxsel = MUXSEL(2, 3, 1), .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -1837,14 +1702,12 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_OSPREY101_848] = { .name = "Osprey 101 (848)", /* 0x05-40C0-C1 */ .video_inputs = 2, - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 1, - .muxsel = { 3, 1 }, + .muxsel = MUXSEL(3, 1), .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -1852,14 +1715,12 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_OSPREY1x1] = { .name = "Osprey 101/151", /* 0x1(4|5)-0004-C4 */ .video_inputs = 1, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, - .muxsel = { 0 }, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + .muxsel = MUXSEL(0), .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -1867,14 +1728,12 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_OSPREY1x1_SVID] = { .name = "Osprey 101/151 w/ svid", /* 0x(16|17|20)-00C4-C1 */ .video_inputs = 2, - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 1, - .muxsel = { 0, 1 }, + .muxsel = MUXSEL(0, 1), .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -1882,14 +1741,12 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_OSPREY2xx] = { .name = "Osprey 200/201/250/251", /* 0x1(8|9|E|F)-0004-C4 */ .video_inputs = 1, - .audio_inputs = 1, - .tuner = UNSET, - .svhs = UNSET, - .muxsel = { 0 }, + /* .audio_inputs= 1, */ + .svhs = NO_SVHS, + .muxsel = MUXSEL(0), .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -1899,14 +1756,12 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_OSPREY2x0_SVID] = { .name = "Osprey 200/250", /* 0x1(A|B)-00C4-C1 */ .video_inputs = 2, - .audio_inputs = 1, - .tuner = UNSET, + /* .audio_inputs= 1, */ .svhs = 1, - .muxsel = { 0, 1 }, + .muxsel = MUXSEL(0, 1), .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -1914,14 +1769,12 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_OSPREY2x0] = { .name = "Osprey 210/220/230", /* 0x1(A|B)-04C0-C1 */ .video_inputs = 2, - .audio_inputs = 1, - .tuner = UNSET, + /* .audio_inputs= 1, */ .svhs = 1, - .muxsel = { 2, 3 }, + .muxsel = MUXSEL(2, 3), .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -1929,14 +1782,12 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_OSPREY500] = { .name = "Osprey 500", /* 500 */ .video_inputs = 2, - .audio_inputs = 1, - .tuner = UNSET, + /* .audio_inputs= 1, */ .svhs = 1, - .muxsel = { 2, 3 }, + .muxsel = MUXSEL(2, 3), .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -1944,12 +1795,10 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_OSPREY540] = { .name = "Osprey 540", /* 540 */ .video_inputs = 4, - .audio_inputs = 1, - .tuner = UNSET, + /* .audio_inputs= 1, */ .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -1959,14 +1808,12 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_OSPREY2000] = { .name = "Osprey 2000", /* 2000 */ .video_inputs = 2, - .audio_inputs = 1, - .tuner = UNSET, + /* .audio_inputs= 1, */ .svhs = 1, - .muxsel = { 2, 3 }, + .muxsel = MUXSEL(2, 3), .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, /* must avoid, conflicts with the bt860 */ @@ -1975,14 +1822,12 @@ struct tvcard bttv_tvcards[] = { /* M G Berberich <berberic@forwiss.uni-passau.de> */ .name = "IDS Eagle", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, - .tuner_type = UNSET, + /* .audio_inputs= 0, */ + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .svhs = UNSET, + .svhs = NO_SVHS, .gpiomask = 0, - .muxsel = { 0, 1, 2, 3 }, + .muxsel = MUXSEL(2, 2, 2, 2), .muxsel_hook = eagle_muxsel, .no_msp34xx = 1, .no_tda9875 = 1, @@ -1991,16 +1836,14 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_PINNACLESAT] = { .name = "Pinnacle PCTV Sat", .video_inputs = 2, - .audio_inputs = 0, + /* .audio_inputs= 0, */ .svhs = 1, - .tuner = UNSET, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, - .muxsel = { 3, 1 }, + .muxsel = MUXSEL(3, 1), .pll = PLL_28, .no_gpioirq = 1, .has_dvb = 1, @@ -2008,18 +1851,16 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_FORMAC_PROTV] = { .name = "Formac ProTV II (bt878)", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 3, .gpiomask = 2, /* TV, Comp1, Composite over SVID con, SVID */ - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 2, 2, 0, 0 }, .pll = PLL_28, .has_radio = 1, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, /* sound routing: GPIO=0x00,0x01,0x03: mute (?) 0x02: both TV and radio (tuner: FM1216/I) @@ -2033,62 +1874,55 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_MACHTV] = { .name = "MachTV", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, - .svhs = UNSET, + /* .audio_inputs= 1, */ + .svhs = NO_SVHS, .gpiomask = 7, - .muxsel = { 2, 3, 1, 1}, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 1, 2, 3}, .gpiomute = 4, .needs_tvaudio = 1, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, }, [BTTV_BOARD_EURESYS_PICOLO] = { .name = "Euresys Picolo", .video_inputs = 3, - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 2, .gpiomask = 0, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, - .muxsel = { 2, 0, 1}, + .muxsel = MUXSEL(2, 0, 1), .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_PV150] = { /* Luc Van Hoeylandt <luc@e-magic.be> */ .name = "ProVideo PV150", /* 0x4f */ .video_inputs = 2, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, .gpiomask = 0, - .muxsel = { 2, 3 }, + .muxsel = MUXSEL(2, 3), .gpiomux = { 0 }, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_AD_TVK503] = { /* Hiroshi Takekawa <sian@big.or.jp> */ /* This card lacks subsystem ID */ .name = "AD-TVK503", /* 0x63 */ .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x001e8007, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), /* Tuner, Radio, external, internal, off, on */ .gpiomux = { 0x08, 0x0f, 0x0a, 0x08 }, .gpiomute = 0x0f, @@ -2097,7 +1931,6 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .tuner_type = TUNER_PHILIPS_NTSC, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .audio_mode_gpio= adtvk503_audio, }, @@ -2105,17 +1938,15 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_HERCULES_SM_TV] = { .name = "Hercules Smart TV Stereo", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x00, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .needs_tvaudio = 1, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, /* Notes: - card lacks subsystem ID - stereo variant w/ daughter board with tda9874a @0xb0 @@ -2129,16 +1960,15 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_PACETV] = { .name = "Pace TV & Radio Card", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, - .muxsel = { 2, 3, 1, 1 }, /* Tuner, CVid, SVid, CVid over SVid connector */ + /* Tuner, CVid, SVid, CVid over SVid connector */ + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomask = 0, .no_tda9875 = 1, .no_tda7432 = 1, .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_radio = 1, .pll = PLL_28, /* Bt878, Bt832, FI1246 tuner; no pci subsystem id @@ -2152,27 +1982,34 @@ struct tvcard bttv_tvcards[] = { /* Chris Willing <chris@vislab.usyd.edu.au> */ .name = "IVC-200", .video_inputs = 1, - .audio_inputs = 0, - .tuner = UNSET, - .tuner_type = UNSET, + /* .audio_inputs= 0, */ + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .svhs = UNSET, + .svhs = NO_SVHS, + .gpiomask = 0xdf, + .muxsel = MUXSEL(2), + .pll = PLL_28, + }, + [BTTV_BOARD_IVCE8784] = { + .name = "IVCE-8784", + .video_inputs = 1, + /* .audio_inputs= 0, */ + .tuner_type = TUNER_ABSENT, + .tuner_addr = ADDR_UNSET, + .svhs = NO_SVHS, .gpiomask = 0xdf, - .muxsel = { 2 }, + .muxsel = MUXSEL(2), .pll = PLL_28, }, [BTTV_BOARD_XGUARD] = { .name = "Grand X-Guard / Trust 814PCI", .video_inputs = 16, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .gpiomask2 = 0xff, - .muxsel = { 2,2,2,2, 3,3,3,3, 1,1,1,1, 0,0,0,0 }, + .muxsel = MUXSEL(2,2,2,2, 3,3,3,3, 1,1,1,1, 0,0,0,0), .muxsel_hook = xguard_muxsel, .no_msp34xx = 1, .no_tda9875 = 1, @@ -2184,16 +2021,14 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_NEBULA_DIGITV] = { .name = "Nebula Electronics DigiTV", .video_inputs = 1, - .tuner = UNSET, - .svhs = UNSET, - .muxsel = { 2, 3, 1, 0 }, + .svhs = NO_SVHS, + .muxsel = MUXSEL(2, 3, 1, 0), .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_dvb = 1, .has_remote = 1, .gpiomask = 0x1b, @@ -2203,118 +2038,101 @@ struct tvcard bttv_tvcards[] = { /* Jorge Boncompte - DTI2 <jorge@dti2.net> */ .name = "ProVideo PV143", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, .gpiomask = 0, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .gpiomux = { 0 }, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_VD009X1_VD011_MINIDIN] = { /* M.Klahr@phytec.de */ .name = "PHYTEC VD-009-X1 VD-011 MiniDIN (bt878)", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, /* card has no tuner */ + /* .audio_inputs= 0, */ .svhs = 3, .gpiomask = 0x00, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 0, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_VD009X1_VD011_COMBI] = { .name = "PHYTEC VD-009-X1 VD-011 Combi (bt878)", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, /* card has no tuner */ + /* .audio_inputs= 0, */ .svhs = 3, .gpiomask = 0x00, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 0, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x6c ---------------------------------- */ [BTTV_BOARD_VD009_MINIDIN] = { .name = "PHYTEC VD-009 MiniDIN (bt878)", .video_inputs = 10, - .audio_inputs = 0, - .tuner = UNSET, /* card has no tuner */ + /* .audio_inputs= 0, */ .svhs = 9, .gpiomask = 0x00, - .gpiomask2 = 0x03, /* gpiomask2 defines the bits used to switch audio - via the upper nibble of muxsel. here: used for - xternal video-mux */ - .muxsel = { 0x02, 0x12, 0x22, 0x32, 0x03, 0x13, 0x23, 0x33, 0x01, 0x00 }, + .gpiomask2 = 0x03, /* used for external vodeo mux */ + .muxsel = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 0), + .muxsel_hook = phytec_muxsel, .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_VD009_COMBI] = { .name = "PHYTEC VD-009 Combi (bt878)", .video_inputs = 10, - .audio_inputs = 0, - .tuner = UNSET, /* card has no tuner */ + /* .audio_inputs= 0, */ .svhs = 9, .gpiomask = 0x00, - .gpiomask2 = 0x03, /* gpiomask2 defines the bits used to switch audio - via the upper nibble of muxsel. here: used for - xternal video-mux */ - .muxsel = { 0x02, 0x12, 0x22, 0x32, 0x03, 0x13, 0x23, 0x33, 0x01, 0x01 }, + .gpiomask2 = 0x03, /* used for external vodeo mux */ + .muxsel = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 1), + .muxsel_hook = phytec_muxsel, .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_IVC100] = { .name = "IVC-100", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, - .tuner_type = UNSET, + /* .audio_inputs= 0, */ + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .svhs = UNSET, + .svhs = NO_SVHS, .gpiomask = 0xdf, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .pll = PLL_28, }, [BTTV_BOARD_IVC120] = { /* IVC-120G - Alan Garfield <alan@fromorbit.com> */ .name = "IVC-120G", .video_inputs = 16, - .audio_inputs = 0, /* card has no audio */ - .tuner = UNSET, /* card has no tuner */ - .tuner_type = UNSET, + /* .audio_inputs= 0, */ + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .svhs = UNSET, /* card has no svhs */ + .svhs = NO_SVHS, /* card has no svhs */ .needs_tvaudio = 0, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, .gpiomask = 0x00, - .muxsel = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, + .muxsel = MUXSEL(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), .muxsel_hook = ivc120_muxsel, .pll = PLL_28, }, @@ -2323,13 +2141,11 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_PC_HDTV] = { .name = "pcHDTV HD-2000 TV", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .tuner_type = TUNER_PHILIPS_FCV1236D, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_dvb = 1, }, [BTTV_BOARD_TWINHAN_DST] = { @@ -2339,38 +2155,34 @@ struct tvcard bttv_tvcards[] = { .no_tda7432 = 1, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_video = 1, .has_dvb = 1, }, [BTTV_BOARD_WINFASTVC100] = { .name = "Winfast VC100", .video_inputs = 3, - .audio_inputs = 0, + /* .audio_inputs= 0, */ .svhs = 1, - .tuner = UNSET, - .muxsel = { 3, 1, 1, 3 }, /* Vid In, SVid In, Vid over SVid in connector */ + /* Vid In, SVid In, Vid over SVid in connector */ + .muxsel = MUXSEL(3, 1, 1, 3), .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, }, [BTTV_BOARD_TEV560] = { .name = "Teppro TEV-560/InterVision IV-560", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 3, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 1, 1, 1, 1 }, .needs_tvaudio = 1, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_35, }, @@ -2378,14 +2190,12 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_SIMUS_GVC1100] = { .name = "SIMUS GVC1100", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, - .tuner_type = UNSET, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, - .muxsel = { 2, 2, 2, 2 }, + .muxsel = MUXSEL(2, 2, 2, 2), .gpiomask = 0x3F, .muxsel_hook = gvc1100_muxsel, }, @@ -2393,47 +2203,41 @@ struct tvcard bttv_tvcards[] = { /* Carlos Silva r3pek@r3pek.homelinux.org || card 0x75 */ .name = "NGS NGSTV+", .video_inputs = 3, - .tuner = 0, .svhs = 2, .gpiomask = 0x008007, - .muxsel = { 2, 3, 0, 0 }, + .muxsel = MUXSEL(2, 3, 0, 0), .gpiomux = { 0, 0, 0, 0 }, .gpiomute = 0x000003, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_remote = 1, }, [BTTV_BOARD_LMLBT4] = { /* http://linuxmedialabs.com */ .name = "LMLBT4", .video_inputs = 4, /* IN1,IN2,IN3,IN4 */ - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, - .muxsel = { 2, 3, 1, 0 }, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + .muxsel = MUXSEL(2, 3, 1, 0), .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, .needs_tvaudio = 0, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_TEKRAM_M205] = { /* Helmroos Harri <harri.helmroos@pp.inet.fi> */ .name = "Tekram M205 PRO", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .svhs = 2, .needs_tvaudio = 0, .gpiomask = 0x68, - .muxsel = { 2, 3, 1 }, + .muxsel = MUXSEL(2, 3, 1), .gpiomux = { 0x68, 0x68, 0x61, 0x61 }, .pll = PLL_28, }, @@ -2444,18 +2248,16 @@ struct tvcard bttv_tvcards[] = { /* bt878 TV + FM without subsystem ID */ .name = "Conceptronic CONTVFMi", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x008007, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 1, 2, 2 }, .gpiomute = 3, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_remote = 1, .has_radio = 1, }, @@ -2466,37 +2268,34 @@ struct tvcard bttv_tvcards[] = { /*0x79 in bttv.h*/ .name = "Euresys Picolo Tetra", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, .gpiomask = 0, .gpiomask2 = 0x3C<<16,/*Set the GPIO[18]->GPIO[21] as output pin.==> drive the video inputs through analog multiplexers*/ .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, - .muxsel = {2,2,2,2},/*878A input is always MUX0, see above.*/ + /*878A input is always MUX0, see above.*/ + .muxsel = MUXSEL(2, 2, 2, 2), .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .pll = PLL_28, .needs_tvaudio = 0, .muxsel_hook = picolo_tetra_muxsel,/*Required as it doesn't follow the classic input selection policy*/ - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_SPIRIT_TV] = { /* Spirit TV Tuner from http://spiritmodems.com.au */ /* Stafford Goodsell <surge@goliath.homeunix.org> */ .name = "Spirit TV Tuner", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x0000000f, - .muxsel = { 2, 1, 1 }, + .muxsel = MUXSEL(2, 1, 1), .gpiomux = { 0x02, 0x00, 0x00, 0x00 }, .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, }, @@ -2505,11 +2304,9 @@ struct tvcard bttv_tvcards[] = { .name = "AVerMedia AVerTV DVB-T 771", .video_inputs = 2, .svhs = 1, - .tuner = UNSET, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .muxsel = { 3 , 3 }, + .muxsel = MUXSEL(3, 3), .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -2524,54 +2321,47 @@ struct tvcard bttv_tvcards[] = { /* Based on the Nebula card data - added remote and new card number - BTTV_BOARD_AVDVBT_761, see also ir-kbd-gpio.c */ .name = "AverMedia AverTV DVB-T 761", .video_inputs = 2, - .tuner = UNSET, .svhs = 1, - .muxsel = { 3, 1, 2, 0 }, /* Comp0, S-Video, ?, ? */ + .muxsel = MUXSEL(3, 1, 2, 0), /* Comp0, S-Video, ?, ? */ .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_dvb = 1, .no_gpioirq = 1, .has_remote = 1, }, [BTTV_BOARD_MATRIX_VISIONSQ] = { /* andre.schwarz@matrix-vision.de */ - .name = "MATRIX Vision Sigma-SQ", - .video_inputs = 16, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, - .gpiomask = 0x0, - .muxsel = { 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3 }, - .muxsel_hook = sigmaSQ_muxsel, - .gpiomux = { 0 }, - .no_msp34xx = 1, - .pll = PLL_28, - .tuner_type = UNSET, - .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, + .name = "MATRIX Vision Sigma-SQ", + .video_inputs = 16, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + .gpiomask = 0x0, + .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3), + .muxsel_hook = sigmaSQ_muxsel, + .gpiomux = { 0 }, + .no_msp34xx = 1, + .pll = PLL_28, + .tuner_type = TUNER_ABSENT, + .tuner_addr = ADDR_UNSET, }, [BTTV_BOARD_MATRIX_VISIONSLC] = { /* andre.schwarz@matrix-vision.de */ - .name = "MATRIX Vision Sigma-SLC", - .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, - .gpiomask = 0x0, - .muxsel = { 2, 2, 2, 2 }, - .muxsel_hook = sigmaSLC_muxsel, - .gpiomux = { 0 }, - .no_msp34xx = 1, - .pll = PLL_28, - .tuner_type = UNSET, - .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, + .name = "MATRIX Vision Sigma-SLC", + .video_inputs = 4, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + .gpiomask = 0x0, + .muxsel = MUXSEL(2, 2, 2, 2), + .muxsel_hook = sigmaSLC_muxsel, + .gpiomux = { 0 }, + .no_msp34xx = 1, + .pll = PLL_28, + .tuner_type = TUNER_ABSENT, + .tuner_addr = ADDR_UNSET, }, /* BTTV_BOARD_APAC_VIEWCOMP */ [BTTV_BOARD_APAC_VIEWCOMP] = { @@ -2579,18 +2369,16 @@ struct tvcard bttv_tvcards[] = { /* bt878 TV + FM 0x00000000 subsystem ID */ .name = "APAC Viewcomp 878(AMAX)", .video_inputs = 2, - .audio_inputs = 1, - .tuner = 0, - .svhs = UNSET, + /* .audio_inputs= 1, */ + .svhs = NO_SVHS, .gpiomask = 0xFF, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 2, 0, 0, 0 }, .gpiomute = 10, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_remote = 1, /* miniremote works, see ir-kbd-gpio.c */ .has_radio = 1, /* not every card has radio */ }, @@ -2599,46 +2387,40 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_DVICO_DVBT_LITE] = { /* Chris Pascoe <c.pascoe@itee.uq.edu.au> */ .name = "DViCO FusionHDTV DVB-T Lite", - .tuner = UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, .no_video = 1, .has_dvb = 1, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_VGEAR_MYVCD] = { /* Steven <photon38@pchome.com.tw> */ .name = "V-Gear MyVCD", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x3f, - .muxsel = {2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .gpiomux = {0x31, 0x31, 0x31, 0x31 }, .gpiomute = 0x31, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_NTSC_M, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_radio = 0, }, [BTTV_BOARD_SUPER_TV] = { /* Rick C <cryptdragoon@gmail.com> */ .name = "Super TV Tuner", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .tuner_type = TUNER_PHILIPS_NTSC, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .gpiomask = 0x008007, .gpiomux = { 0, 0x000001,0,0 }, .needs_tvaudio = 1, @@ -2648,17 +2430,15 @@ struct tvcard bttv_tvcards[] = { /* Chris Fanning <video4linux@haydon.net> */ .name = "Tibet Systems 'Progress DVR' CS16", .video_inputs = 16, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, - .muxsel = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2), .pll = PLL_28, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .muxsel_hook = tibetCS16_muxsel, }, [BTTV_BOARD_KODICOM_4400R] = { @@ -2675,12 +2455,10 @@ struct tvcard bttv_tvcards[] = { */ .name = "Kodicom 4400R (master)", .video_inputs = 16, - .audio_inputs = 0, - .tuner = UNSET, - .tuner_type = UNSET, + /* .audio_inputs= 0, */ + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .svhs = UNSET, + .svhs = NO_SVHS, /* GPIO bits 0-9 used for analog switch: * 00 - 03: camera selector * 04 - 06: channel (controller) selector @@ -2691,7 +2469,7 @@ struct tvcard bttv_tvcards[] = { */ .gpiomask = 0x0003ff, .no_gpioirq = 1, - .muxsel = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, + .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3), .pll = PLL_28, .no_msp34xx = 1, .no_tda7432 = 1, @@ -2707,15 +2485,13 @@ struct tvcard bttv_tvcards[] = { */ .name = "Kodicom 4400R (slave)", .video_inputs = 16, - .audio_inputs = 0, - .tuner = UNSET, - .tuner_type = UNSET, + /* .audio_inputs= 0, */ + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .svhs = UNSET, + .svhs = NO_SVHS, .gpiomask = 0x010000, .no_gpioirq = 1, - .muxsel = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, + .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3), .pll = PLL_28, .no_msp34xx = 1, .no_tda7432 = 1, @@ -2728,27 +2504,23 @@ struct tvcard bttv_tvcards[] = { /* Adlink RTV24 with special unlock codes */ .name = "Adlink RTV24", .video_inputs = 4, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, - .muxsel = { 2, 3, 1, 0 }, + .muxsel = MUXSEL(2, 3, 1, 0), .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, }, /* ---- card 0x87---------------------------------- */ [BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE] = { /* Michael Krufky <mkrufky@m1k.net> */ .name = "DViCO FusionHDTV 5 Lite", - .tuner = 0, .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */ .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .video_inputs = 3, - .audio_inputs = 1, + /* .audio_inputs= 1, */ .svhs = 2, - .muxsel = { 2, 3, 1 }, + .muxsel = MUXSEL(2, 3, 1), .gpiomask = 0x00e00007, .gpiomux = { 0x00400005, 0, 0x00000001, 0 }, .gpiomute = 0x00c00007, @@ -2762,75 +2534,68 @@ struct tvcard bttv_tvcards[] = { /* Mauro Carvalho Chehab <mchehab@infradead.org> */ .name = "Acorp Y878F", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x01fe00, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 }, .gpiomute = 0x002000, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_YMEC_TVF66T5_B_DFF, .tuner_addr = 0xc1 >>1, - .radio_addr = 0xc1 >>1, .has_radio = 1, }, /* ---- card 0x89 ---------------------------------- */ [BTTV_BOARD_CONCEPTRONIC_CTVFMI2] = { .name = "Conceptronic CTVFMi v2", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x001c0007, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 1, 2, 2 }, .gpiomute = 3, .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = TUNER_TENA_9533_DI, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_remote = 1, .has_radio = 1, }, /* ---- card 0x8a ---------------------------------- */ [BTTV_BOARD_PV_BT878P_2E] = { - .name = "Prolink Pixelview PV-BT878P+ (Rev.2E)", - .video_inputs = 5, - .audio_inputs = 1, - .tuner = 0, - .svhs = 3, - .gpiomask = 0x01fe00, - .muxsel = { 2,3,1,1,-1 }, - .digital_mode = DIGITAL_MODE_CAMERA, - .gpiomux = { 0x00400, 0x10400, 0x04400, 0x80000 }, - .gpiomute = 0x12400, - .no_msp34xx = 1, - .pll = PLL_28, - .tuner_type = TUNER_LG_PAL_FM, - .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .has_remote = 1, + .name = "Prolink Pixelview PV-BT878P+ (Rev.2E)", + .video_inputs = 5, + /* .audio_inputs= 1, */ + .svhs = 3, + .has_dig_in = 1, + .gpiomask = 0x01fe00, + .muxsel = MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */ + /* .digital_mode= DIGITAL_MODE_CAMERA, */ + .gpiomux = { 0x00400, 0x10400, 0x04400, 0x80000 }, + .gpiomute = 0x12400, + .no_msp34xx = 1, + .pll = PLL_28, + .tuner_type = TUNER_LG_PAL_FM, + .tuner_addr = ADDR_UNSET, + .has_remote = 1, }, /* ---- card 0x8b ---------------------------------- */ [BTTV_BOARD_PV_M4900] = { /* Sérgio Fortier <sergiofortier@yahoo.com.br> */ .name = "Prolink PixelView PlayTV MPEG2 PV-M4900", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x3f, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x21, 0x20, 0x24, 0x2c }, .gpiomute = 0x29, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_YMEC_TVF_5533MF, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .has_radio = 1, .has_remote = 1, }, @@ -2850,17 +2615,15 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_OSPREY440] = { .name = "Osprey 440", .video_inputs = 4, - .audio_inputs = 2, /* this is meaningless */ - .tuner = UNSET, - .svhs = UNSET, - .muxsel = { 2, 3, 0, 1 }, /* 3,0,1 are guesses */ + /* .audio_inputs= 2, */ + .svhs = NO_SVHS, + .muxsel = MUXSEL(2, 3, 0, 1), /* 3,0,1 are guesses */ .gpiomask = 0x303, .gpiomute = 0x000, /* int + 32kHz */ .gpiomux = { 0, 0, 0x000, 0x100}, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, @@ -2869,28 +2632,25 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_ASOUND_SKYEYE] = { .name = "Asound Skyeye PCTV", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 15, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 2, 0, 0, 0 }, .gpiomute = 1, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_NTSC, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x8e ---------------------------------- */ [BTTV_BOARD_SABRENT_TVFM] = { .name = "Sabrent TV-FM (bttv version)", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x108007, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 100000, 100002, 100002, 100000 }, .no_msp34xx = 1, .no_tda9875 = 1, @@ -2904,17 +2664,15 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_HAUPPAUGE_IMPACTVCB] = { .name = "Hauppauge ImpactVCB (bt878)", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, .gpiomask = 0x0f, /* old: 7 */ - .muxsel = { 0, 1, 3, 2 }, /* Composite 0-3 */ + .muxsel = MUXSEL(0, 1, 3, 2), /* Composite 0-3 */ .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_MACHTV_MAGICTV] = { /* Julian Calaby <julian.calaby@gmail.com> @@ -2926,16 +2684,14 @@ struct tvcard bttv_tvcards[] = { .name = "MagicTV", /* rebranded MachTV */ .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 7, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 1, 2, 3 }, .gpiomute = 4, .tuner_type = TUNER_TEMIC_4009FR5_PAL, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, .has_radio = 1, .has_remote = 1, @@ -2943,36 +2699,30 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_SSAI_SECURITY] = { .name = "SSAI Security Video Interface", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, - .muxsel = { 0, 1, 2, 3 }, - .tuner_type = UNSET, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + .muxsel = MUXSEL(0, 1, 2, 3), + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_SSAI_ULTRASOUND] = { .name = "SSAI Ultrasound Video Interface", .video_inputs = 2, - .audio_inputs = 0, - .tuner = UNSET, + /* .audio_inputs= 0, */ .svhs = 1, - .muxsel = { 2, 0, 1, 3 }, - .tuner_type = UNSET, + .muxsel = MUXSEL(2, 0, 1, 3), + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, /* ---- card 0x94---------------------------------- */ [BTTV_BOARD_DVICO_FUSIONHDTV_2] = { .name = "DViCO FusionHDTV 2", - .tuner = 0, .tuner_type = TUNER_PHILIPS_FCV1236D, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .video_inputs = 3, - .audio_inputs = 1, + /* .audio_inputs= 1, */ .svhs = 2, - .muxsel = { 2, 3, 1 }, + .muxsel = MUXSEL(2, 3, 1), .gpiomask = 0x00e00007, .gpiomux = { 0x00400005, 0, 0x00000001, 0 }, .gpiomute = 0x00c00007, @@ -2984,36 +2734,31 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_TYPHOON_TVTUNERPCI] = { .name = "Typhoon TV-Tuner PCI (50684)", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x3014f, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0x20001,0x10001, 0, 0 }, .gpiomute = 10, .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_GEOVISION_GV600] = { /* emhn@usb.ve */ - .name = "Geovision GV-600", - .video_inputs = 16, - .audio_inputs = 0, - .tuner = UNSET, - .svhs = UNSET, - .gpiomask = 0x0, - .muxsel = { 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2 }, - .muxsel_hook = geovision_muxsel, - .gpiomux = { 0 }, - .no_msp34xx = 1, - .pll = PLL_28, - .tuner_type = UNSET, - .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, + .name = "Geovision GV-600", + .video_inputs = 16, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + .gpiomask = 0x0, + .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2), + .muxsel_hook = geovision_muxsel, + .gpiomux = { 0 }, + .no_msp34xx = 1, + .pll = PLL_28, + .tuner_type = TUNER_ABSENT, + .tuner_addr = ADDR_UNSET, }, [BTTV_BOARD_KOZUMI_KTV_01C] = { /* Mauro Lacy <mauro@lacy.com.ar> @@ -3021,17 +2766,15 @@ struct tvcard bttv_tvcards[] = { .name = "Kozumi KTV-01C", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, .gpiomask = 0x008007, - .muxsel = { 2, 3, 1, 1 }, + .muxsel = MUXSEL(2, 3, 1, 1), .gpiomux = { 0, 1, 2, 2 }, /* CONTVFMi */ .gpiomute = 3, /* CONTVFMi */ .needs_tvaudio = 0, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MK3 */ .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, .has_radio = 1, .has_remote = 1, @@ -3041,8 +2784,7 @@ struct tvcard bttv_tvcards[] = { Mauro Carvalho Chehab <mchehab@infradead.org */ .name = "Encore ENL TV-FM-2", .video_inputs = 3, - .audio_inputs = 1, - .tuner = 0, + /* .audio_inputs= 1, */ .svhs = 2, /* bit 6 -> IR disabled bit 18/17 = 00 -> mute @@ -3051,12 +2793,11 @@ struct tvcard bttv_tvcards[] = { 11 -> internal audio input */ .gpiomask = 0x060040, - .muxsel = { 2, 3, 3 }, + .muxsel = MUXSEL(2, 3, 3), .gpiomux = { 0x60000, 0x60000, 0x20000, 0x20000 }, .gpiomute = 0, .tuner_type = TUNER_TCL_MF02GIP_5N, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, .pll = PLL_28, .has_radio = 1, .has_remote = 1, @@ -3065,50 +2806,111 @@ struct tvcard bttv_tvcards[] = { /* D.Heer@Phytec.de */ .name = "PHYTEC VD-012 (bt878)", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, /* card has no tuner */ - .svhs = UNSET, /* card has no s-video */ + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, .gpiomask = 0x00, - .muxsel = { 0, 2, 3, 1 }, + .muxsel = MUXSEL(0, 2, 3, 1), .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 0, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_VD012_X1] = { /* D.Heer@Phytec.de */ .name = "PHYTEC VD-012-X1 (bt878)", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, /* card has no tuner */ + /* .audio_inputs= 0, */ .svhs = 3, .gpiomask = 0x00, - .muxsel = { 2, 3, 1 }, + .muxsel = MUXSEL(2, 3, 1), .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 0, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_VD012_X2] = { /* D.Heer@Phytec.de */ .name = "PHYTEC VD-012-X2 (bt878)", .video_inputs = 4, - .audio_inputs = 0, - .tuner = UNSET, /* card has no tuner */ + /* .audio_inputs= 0, */ .svhs = 3, .gpiomask = 0x00, - .muxsel = { 3, 2, 1 }, + .muxsel = MUXSEL(3, 2, 1), .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 0, .pll = PLL_28, - .tuner_type = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - } + }, + [BTTV_BOARD_GEOVISION_GV800S] = { + /* Bruno Christo <bchristo@inf.ufsm.br> + * + * GeoVision GV-800(S) has 4 Conexant Fusion 878A: + * 1 audio input per BT878A = 4 audio inputs + * 4 video inputs per BT878A = 16 video inputs + * This is the first BT878A chip of the GV-800(S). It's the + * "master" chip and it controls the video inputs through an + * analog multiplexer (a CD22M3494) via some GPIO pins. The + * slaves should use card type 0x9e (following this one). + * There is a EEPROM on the card which is currently not handled. + * The audio input is not working yet. + */ + .name = "Geovision GV-800(S) (master)", + .video_inputs = 4, + /* .audio_inputs= 1, */ + .tuner_type = TUNER_ABSENT, + .tuner_addr = ADDR_UNSET, + .svhs = NO_SVHS, + .gpiomask = 0xf107f, + .no_gpioirq = 1, + .muxsel = MUXSEL(2, 2, 2, 2), + .pll = PLL_28, + .no_msp34xx = 1, + .no_tda7432 = 1, + .no_tda9875 = 1, + .muxsel_hook = gv800s_muxsel, + }, + [BTTV_BOARD_GEOVISION_GV800S_SL] = { + /* Bruno Christo <bchristo@inf.ufsm.br> + * + * GeoVision GV-800(S) has 4 Conexant Fusion 878A: + * 1 audio input per BT878A = 4 audio inputs + * 4 video inputs per BT878A = 16 video inputs + * The 3 other BT878A chips are "slave" chips of the GV-800(S) + * and should use this card type. + * The audio input is not working yet. + */ + .name = "Geovision GV-800(S) (slave)", + .video_inputs = 4, + /* .audio_inputs= 1, */ + .tuner_type = TUNER_ABSENT, + .tuner_addr = ADDR_UNSET, + .svhs = NO_SVHS, + .gpiomask = 0x00, + .no_gpioirq = 1, + .muxsel = MUXSEL(2, 2, 2, 2), + .pll = PLL_28, + .no_msp34xx = 1, + .no_tda7432 = 1, + .no_tda9875 = 1, + .muxsel_hook = gv800s_muxsel, + }, + [BTTV_BOARD_PV183] = { + .name = "ProVideo PV183", /* 0x9f */ + .video_inputs = 2, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + .gpiomask = 0, + .muxsel = MUXSEL(2, 3), + .gpiomux = { 0 }, + .needs_tvaudio = 0, + .no_msp34xx = 1, + .pll = PLL_28, + .tuner_type = TUNER_ABSENT, + .tuner_addr = ADDR_UNSET, + }, }; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -3152,7 +2954,7 @@ void __devinit bttv_idcard(struct bttv *btv) btv->c.nr, btv->cardid & 0xffff, (btv->cardid >> 16) & 0xffff); printk(KERN_DEBUG "please mail id, board name and " - "the correct card= insmod option to video4linux-list@redhat.com\n"); + "the correct card= insmod option to linux-media@vger.kernel.org\n"); } } @@ -3403,8 +3205,7 @@ static void init_ids_eagle(struct bttv *btv) * has its own multiplexer */ static void eagle_muxsel(struct bttv *btv, unsigned int input) { - btaor((2)<<5, ~(3<<5), BT848_IFORM); - gpio_bits(3,bttv_tvcards[btv->c.type].muxsel[input&7]); + gpio_bits(3, input & 3); /* composite */ /* set chroma ADC to sleep */ @@ -3523,6 +3324,17 @@ void __devinit bttv_init_card1(struct bttv *btv) /* initialization part two -- after registering i2c bus */ void __devinit bttv_init_card2(struct bttv *btv) { + static const unsigned short tvaudio_addrs[] = { + I2C_ADDR_TDA8425 >> 1, + I2C_ADDR_TEA6300 >> 1, + I2C_ADDR_TEA6420 >> 1, + I2C_ADDR_TDA9840 >> 1, + I2C_ADDR_TDA985x_L >> 1, + I2C_ADDR_TDA985x_H >> 1, + I2C_ADDR_TDA9874 >> 1, + I2C_ADDR_PIC16C54 >> 1, + I2C_CLIENT_END + }; int addr=ADDR_UNSET; btv->tuner_type = UNSET; @@ -3629,6 +3441,9 @@ void __devinit bttv_init_card2(struct bttv *btv) case BTTV_BOARD_KODICOM_4400R: kodicom4400r_init(btv); break; + case BTTV_BOARD_GEOVISION_GV800S: + gv800s_init(btv); + break; } /* pll configuration */ @@ -3670,13 +3485,12 @@ void __devinit bttv_init_card2(struct bttv *btv) addr = bttv_tvcards[btv->c.type].tuner_addr; if (UNSET != bttv_tvcards[btv->c.type].tuner_type) - if(UNSET == btv->tuner_type) + if (UNSET == btv->tuner_type) btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type; if (UNSET != tuner[btv->c.nr]) btv->tuner_type = tuner[btv->c.nr]; - if (btv->tuner_type == TUNER_ABSENT || - bttv_tvcards[btv->c.type].tuner == UNSET) + if (btv->tuner_type == TUNER_ABSENT) printk(KERN_INFO "bttv%d: tuner absent\n", btv->c.nr); else if(btv->tuner_type == UNSET) printk(KERN_WARNING "bttv%d: tuner type unset\n", btv->c.nr); @@ -3684,14 +3498,35 @@ void __devinit bttv_init_card2(struct bttv *btv) printk(KERN_INFO "bttv%d: tuner type=%d\n", btv->c.nr, btv->tuner_type); - if (btv->tuner_type != UNSET) { + if (autoload != UNSET) { + printk(KERN_WARNING "bttv%d: the autoload option is obsolete.\n", btv->c.nr); + printk(KERN_WARNING "bttv%d: use option msp3400, tda7432 or tvaudio to\n", btv->c.nr); + printk(KERN_WARNING "bttv%d: override which audio module should be used.\n", btv->c.nr); + } + + if (UNSET == btv->tuner_type) + btv->tuner_type = TUNER_ABSENT; + + if (btv->tuner_type != TUNER_ABSENT) { struct tuner_setup tun_setup; + /* Load tuner module before issuing tuner config call! */ + if (bttv_tvcards[btv->c.type].has_radio) + v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, + "tuner", "tuner", v4l2_i2c_tuner_addrs(ADDRS_RADIO)); + v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, "tuner", + "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, "tuner", + "tuner", v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); + tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; tun_setup.type = btv->tuner_type; tun_setup.addr = addr; - bttv_call_i2c_clients(btv, TUNER_SET_TYPE_ADDR, &tun_setup); + if (bttv_tvcards[btv->c.type].has_radio) + tun_setup.mode_mask |= T_RADIO; + + bttv_call_all(btv, tuner, s_type_addr, &tun_setup); } if (btv->tda9887_conf) { @@ -3700,10 +3535,13 @@ void __devinit bttv_init_card2(struct bttv *btv) tda9887_cfg.tuner = TUNER_TDA9887; tda9887_cfg.priv = &btv->tda9887_conf; - bttv_call_i2c_clients(btv, TUNER_SET_CONFIG, &tda9887_cfg); + bttv_call_all(btv, tuner, s_config, &tda9887_cfg); } - btv->svhs = bttv_tvcards[btv->c.type].svhs; + btv->dig = bttv_tvcards[btv->c.type].has_dig_in ? + bttv_tvcards[btv->c.type].video_inputs - 1 : UNSET; + btv->svhs = bttv_tvcards[btv->c.type].svhs == NO_SVHS ? + UNSET : bttv_tvcards[btv->c.type].svhs; if (svhs[btv->c.nr] != UNSET) btv->svhs = svhs[btv->c.nr]; if (remote[btv->c.nr] != UNSET) @@ -3720,34 +3558,127 @@ void __devinit bttv_init_card2(struct bttv *btv) if (bttv_tvcards[btv->c.type].audio_mode_gpio) btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio; - if (!autoload) - return; - - if (bttv_tvcards[btv->c.type].tuner == UNSET) + if (btv->tuner_type == TUNER_ABSENT) return; /* no tuner or related drivers to load */ + if (btv->has_saa6588 || saa6588[btv->c.nr]) { + /* Probe for RDS receiver chip */ + static const unsigned short addrs[] = { + 0x20 >> 1, + 0x22 >> 1, + I2C_CLIENT_END + }; + struct v4l2_subdev *sd; + + sd = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, + "saa6588", "saa6588", addrs); + btv->has_saa6588 = (sd != NULL); + } + /* try to detect audio/fader chips */ - if (!bttv_tvcards[btv->c.type].no_msp34xx && - bttv_I2CRead(btv, I2C_ADDR_MSP3400, "MSP34xx") >=0) - request_module("msp3400"); - if (bttv_tvcards[btv->c.type].msp34xx_alt && - bttv_I2CRead(btv, I2C_ADDR_MSP3400_ALT, "MSP34xx (alternate address)") >=0) - request_module("msp3400"); + /* First check if the user specified the audio chip via a module + option. */ + + switch (audiodev[btv->c.nr]) { + case -1: + return; /* do not load any audio module */ + + case 0: /* autodetect */ + break; + + case 1: { + /* The user specified that we should probe for msp3400 */ + static const unsigned short addrs[] = { + I2C_ADDR_MSP3400 >> 1, + I2C_ADDR_MSP3400_ALT >> 1, + I2C_CLIENT_END + }; + + btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, + "msp3400", "msp3400", addrs); + if (btv->sd_msp34xx) + return; + goto no_audio; + } + + case 2: { + /* The user specified that we should probe for tda7432 */ + static const unsigned short addrs[] = { + I2C_ADDR_TDA7432 >> 1, + I2C_CLIENT_END + }; + + if (v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, + "tda7432", "tda7432", addrs)) + return; + goto no_audio; + } + + case 3: { + /* The user specified that we should probe for tvaudio */ + btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, + "tvaudio", "tvaudio", tvaudio_addrs); + if (btv->sd_tvaudio) + return; + goto no_audio; + } + + default: + printk(KERN_WARNING "bttv%d: unknown audiodev value!\n", + btv->c.nr); + return; + } - if (!bttv_tvcards[btv->c.type].no_tda9875 && - bttv_I2CRead(btv, I2C_ADDR_TDA9875, "TDA9875") >=0) - request_module("tda9875"); + /* There were no overrides, so now we try to discover this through the + card definition */ + + /* probe for msp3400 first: this driver can detect whether or not + it really is a msp3400, so it will return NULL when the device + found is really something else (e.g. a tea6300). */ + if (!bttv_tvcards[btv->c.type].no_msp34xx) { + static const unsigned short addrs[] = { + I2C_ADDR_MSP3400 >> 1, + I2C_CLIENT_END + }; + + btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, + "msp3400", "msp3400", addrs); + } else if (bttv_tvcards[btv->c.type].msp34xx_alt) { + static const unsigned short addrs[] = { + I2C_ADDR_MSP3400_ALT >> 1, + I2C_CLIENT_END + }; + + btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, + "msp3400", "msp3400", addrs); + } - if (!bttv_tvcards[btv->c.type].no_tda7432 && - bttv_I2CRead(btv, I2C_ADDR_TDA7432, "TDA7432") >=0) - request_module("tda7432"); + /* If we found a msp34xx, then we're done. */ + if (btv->sd_msp34xx) + return; - if (bttv_tvcards[btv->c.type].needs_tvaudio) - request_module("tvaudio"); + /* it might also be a tda7432. */ + if (!bttv_tvcards[btv->c.type].no_tda7432) { + static const unsigned short addrs[] = { + I2C_ADDR_TDA7432 >> 1, + I2C_CLIENT_END + }; - if (btv->tuner_type != UNSET && btv->tuner_type != TUNER_ABSENT) - request_module("tuner"); + if (v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, + "tda7432", "tda7432", addrs)) + return; + } + + /* Now see if we can find one of the tvaudio devices. */ + btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, + "tvaudio", "tvaudio", tvaudio_addrs); + if (btv->sd_tvaudio) + return; + +no_audio: + printk(KERN_WARNING "bttv%d: audio absent, no audio device found!\n", + btv->c.nr); } @@ -3819,6 +3750,7 @@ static int terratec_active_radio_upgrade(struct bttv *btv) printk("bttv%d: Terratec Active Radio Upgrade found.\n", btv->c.nr); btv->has_radio = 1; + btv->has_saa6588 = 1; btv->has_matchbox = 1; } else { btv->has_radio = 0; @@ -4067,27 +3999,26 @@ static void __devinit avermedia_eeprom(struct bttv *btv) btv->has_remote ? "yes" : "no"); } -/* used on Voodoo TV/FM (Voodoo 200), S0 wired to 0x10000 */ -void bttv_tda9880_setnorm(struct bttv *btv, int norm) +/* + * For Voodoo TV/FM and Voodoo 200. These cards' tuners use a TDA9880 + * analog demod, which is not I2C controlled like the newer and more common + * TDA9887 series. Instead is has two tri-state input pins, S0 and S1, + * that control the IF for the video and audio. Apparently, bttv GPIO + * 0x10000 is connected to S0. S0 low selects a 38.9 MHz VIF for B/G/D/K/I + * (i.e., PAL) while high selects 45.75 MHz for M/N (i.e., NTSC). + */ +u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits) { - /* fix up our card entry */ - if(norm==V4L2_STD_NTSC) { - bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomux[TVAUDIO_INPUT_TUNER]=0x957fff; - bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomute=0x957fff; - bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomux[TVAUDIO_INPUT_TUNER]=0x957fff; - bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomute=0x957fff; - dprintk("bttv_tda9880_setnorm to NTSC\n"); - } - else { - bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomux[TVAUDIO_INPUT_TUNER]=0x947fff; - bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomute=0x947fff; - bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomux[TVAUDIO_INPUT_TUNER]=0x947fff; - bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomute=0x947fff; - dprintk("bttv_tda9880_setnorm to PAL\n"); + + if (btv->audio == TVAUDIO_INPUT_TUNER) { + if (bttv_tvnorms[btv->tvnorm].v4l2_id & V4L2_STD_MN) + gpiobits |= 0x10000; + else + gpiobits &= ~0x10000; } - /* set GPIO according */ - gpio_bits(bttv_tvcards[btv->c.type].gpiomask, - bttv_tvcards[btv->c.type].gpiomux[btv->audio]); + + gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpiobits); + return gpiobits; } @@ -4463,6 +4394,11 @@ void tea5757_set_freq(struct bttv *btv, unsigned short freq) */ static void rv605_muxsel(struct bttv *btv, unsigned int input) { + static const u8 muxgpio[] = { 0x3, 0x1, 0x2, 0x4, 0xf, 0x7, 0xe, 0x0, + 0xd, 0xb, 0xc, 0x6, 0x9, 0x5, 0x8, 0xa }; + + gpio_bits(0x07f, muxgpio[input]); + /* reset all conections */ gpio_bits(0x200,0x200); mdelay(1); @@ -4470,7 +4406,6 @@ static void rv605_muxsel(struct bttv *btv, unsigned int input) mdelay(1); /* create a new connection */ - gpio_bits(0x480,0x080); gpio_bits(0x480,0x480); mdelay(1); gpio_bits(0x480,0x080); @@ -4729,8 +4664,7 @@ static void ivc120_muxsel(struct bttv *btv, unsigned int input) bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x02, ((matrix == 2) ? 0x03 : 0x00), 1); /* 9-12 */ - /* Selects MUX0 for input on the 878 */ - btaor((0)<<5, ~(3<<5), BT848_IFORM); + /* 878's MUX0 is already selected for input via muxsel values */ } @@ -4814,6 +4748,132 @@ static void PXC200_muxsel(struct bttv *btv, unsigned int input) printk(KERN_DEBUG "bttv%d: setting input channel to:%d\n", btv->c.nr,(int)mux); } +static void phytec_muxsel(struct bttv *btv, unsigned int input) +{ + unsigned int mux = input % 4; + + if (input == btv->svhs) + mux = 0; + + gpio_bits(0x3, mux); +} + +/* + * GeoVision GV-800(S) functions + * Bruno Christo <bchristo@inf.ufsm.br> +*/ + +/* This is a function to control the analog switch, which determines which + * camera is routed to which controller. The switch comprises an X-address + * (gpio bits 0-3, representing the camera, ranging from 0-15), and a + * Y-address (gpio bits 4-6, representing the controller, ranging from 0-3). + * A data value (gpio bit 18) of '1' enables the switch, and '0' disables + * the switch. A STROBE bit (gpio bit 17) latches the data value into the + * specified address. There is also a chip select (gpio bit 16). + * The idea is to set the address and chip select together, bring + * STROBE high, write the data, and finally bring STROBE back to low. + */ +static void gv800s_write(struct bttv *btv, + unsigned char xaddr, + unsigned char yaddr, + unsigned char data) { + /* On the "master" 878A: + * GPIO bits 0-9 are used for the analog switch: + * 00 - 03: camera selector + * 04 - 06: 878A (controller) selector + * 16: cselect + * 17: strobe + * 18: data (1->on, 0->off) + * 19: reset + */ + const u32 ADDRESS = ((xaddr&0xf) | (yaddr&3)<<4); + const u32 CSELECT = 1<<16; + const u32 STROBE = 1<<17; + const u32 DATA = data<<18; + + gpio_bits(0x1007f, ADDRESS | CSELECT); /* write ADDRESS and CSELECT */ + gpio_bits(0x20000, STROBE); /* STROBE high */ + gpio_bits(0x40000, DATA); /* write DATA */ + gpio_bits(0x20000, ~STROBE); /* STROBE low */ +} + +/* + * GeoVision GV-800(S) muxsel + * + * Each of the 4 cards (controllers) use this function. + * The controller using this function selects the input through the GPIO pins + * of the "master" card. A pointer to this card is stored in master[btv->c.nr]. + * + * The parameter 'input' is the requested camera number (0-4) on the controller. + * The map array has the address of each input. Note that the addresses in the + * array are in the sequence the original GeoVision driver uses, that is, set + * every controller to input 0, then to input 1, 2, 3, repeat. This means that + * the physical "camera 1" connector corresponds to controller 0 input 0, + * "camera 2" corresponds to controller 1 input 0, and so on. + * + * After getting the input address, the function then writes the appropriate + * data to the analog switch, and housekeeps the local copy of the switch + * information. + */ +static void gv800s_muxsel(struct bttv *btv, unsigned int input) +{ + struct bttv *mctlr; + char *sw_status; + int xaddr, yaddr; + static unsigned int map[4][4] = { { 0x0, 0x4, 0xa, 0x6 }, + { 0x1, 0x5, 0xb, 0x7 }, + { 0x2, 0x8, 0xc, 0xe }, + { 0x3, 0x9, 0xd, 0xf } }; + input = input%4; + mctlr = master[btv->c.nr]; + if (mctlr == NULL) { + /* do nothing until the "master" is detected */ + return; + } + yaddr = (btv->c.nr - mctlr->c.nr) & 3; + sw_status = (char *)(&mctlr->mbox_we); + xaddr = map[yaddr][input] & 0xf; + + /* Check if the controller/camera pair has changed, ignore otherwise */ + if (sw_status[yaddr] != xaddr) { + /* disable the old switch, enable the new one and save status */ + gv800s_write(mctlr, sw_status[yaddr], yaddr, 0); + sw_status[yaddr] = xaddr; + gv800s_write(mctlr, xaddr, yaddr, 1); + } +} + +/* GeoVision GV-800(S) "master" chip init */ +static void gv800s_init(struct bttv *btv) +{ + char *sw_status = (char *)(&btv->mbox_we); + int ix; + + gpio_inout(0xf107f, 0xf107f); + gpio_write(1<<19); /* reset the analog MUX */ + gpio_write(0); + + /* Preset camera 0 to the 4 controllers */ + for (ix = 0; ix < 4; ix++) { + sw_status[ix] = ix; + gv800s_write(btv, ix, ix, 1); + } + + /* Inputs on the "master" controller need this brightness fix */ + bttv_I2CWrite(btv, 0x18, 0x5, 0x90, 1); + + if (btv->c.nr > BTTV_MAX-4) + return; + /* + * Store the "master" controller pointer in the master + * array for later use in the muxsel function. + */ + master[btv->c.nr] = btv; + master[btv->c.nr+1] = btv; + master[btv->c.nr+2] = btv; + master[btv->c.nr+3] = btv; +} + /* ----------------------------------------------------------------------- */ /* motherboard chipset specific stuff */ diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index c71f394..7a8ca0d 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -58,7 +58,7 @@ unsigned int bttv_num; /* number of Bt848s in use */ -struct bttv bttvs[BTTV_MAX]; +struct bttv *bttvs[BTTV_MAX]; unsigned int bttv_debug; unsigned int bttv_verbose = 1; @@ -167,7 +167,7 @@ static ssize_t show_card(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vfd = container_of(cd, struct video_device, dev); - struct bttv *btv = dev_get_drvdata(vfd->parent); + struct bttv *btv = video_get_drvdata(vfd); return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET); } static DEVICE_ATTR(card, S_IRUGO, show_card, NULL); @@ -1040,7 +1040,7 @@ static void bt848A_set_timing(struct bttv *btv) int table_idx = bttv_tvnorms[btv->tvnorm].sram; int fsc = bttv_tvnorms[btv->tvnorm].Fsc; - if (UNSET == bttv_tvcards[btv->c.type].muxsel[btv->input]) { + if (btv->input == btv->dig) { dprintk("bttv%d: load digital timing table (table_idx=%d)\n", btv->c.nr,table_idx); @@ -1142,7 +1142,7 @@ video_mux(struct bttv *btv, unsigned int input) btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); } - mux = bttv_tvcards[btv->c.type].muxsel[input] & 3; + mux = bttv_muxsel(btv, input); btaor(mux<<5, ~(3<<5), BT848_IFORM); dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n", btv->c.nr,input,mux); @@ -1163,7 +1163,6 @@ audio_mux(struct bttv *btv, int input, int mute) { int gpio_val, signal; struct v4l2_control ctrl; - struct i2c_client *c; gpio_inout(bttv_tvcards[btv->c.type].gpiomask, bttv_tvcards[btv->c.type].gpiomask); @@ -1180,7 +1179,16 @@ audio_mux(struct bttv *btv, int input, int mute) else gpio_val = bttv_tvcards[btv->c.type].gpiomux[input]; - gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val); + switch (btv->c.type) { + case BTTV_BOARD_VOODOOTV_FM: + case BTTV_BOARD_VOODOOTV_200: + gpio_val = bttv_tda9880_setnorm(btv, gpio_val); + break; + + default: + gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val); + } + if (bttv_gpio) bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]); if (in_interrupt()) @@ -1188,9 +1196,8 @@ audio_mux(struct bttv *btv, int input, int mute) ctrl.id = V4L2_CID_AUDIO_MUTE; ctrl.value = btv->mute; - bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, &ctrl); - c = btv->i2c_msp34xx_client; - if (c) { + bttv_call_all(btv, core, s_ctrl, &ctrl); + if (btv->sd_msp34xx) { struct v4l2_routing route; /* Note: the inputs tuner/radio/extern/intern are translated @@ -1229,15 +1236,14 @@ audio_mux(struct bttv *btv, int input, int mute) break; } route.output = MSP_OUTPUT_DEFAULT; - c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route); + v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing, &route); } - c = btv->i2c_tvaudio_client; - if (c) { + if (btv->sd_tvaudio) { struct v4l2_routing route; route.input = input; route.output = 0; - c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route); + v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing, &route); } return 0; } @@ -1277,7 +1283,7 @@ bttv_crop_calc_limits(struct bttv_crop *c) } static void -bttv_crop_reset(struct bttv_crop *c, int norm) +bttv_crop_reset(struct bttv_crop *c, unsigned int norm) { c->rect = bttv_tvnorms[norm].cropcap.defrect; bttv_crop_calc_limits(c); @@ -1290,16 +1296,13 @@ set_tvnorm(struct bttv *btv, unsigned int norm) const struct bttv_tvnorm *tvnorm; v4l2_std_id id; - if (norm < 0 || norm >= BTTV_TVNORMS) - return -EINVAL; + BUG_ON(norm >= BTTV_TVNORMS); + BUG_ON(btv->tvnorm >= BTTV_TVNORMS); tvnorm = &bttv_tvnorms[norm]; - if (btv->tvnorm < 0 || - btv->tvnorm >= BTTV_TVNORMS || - 0 != memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, - &tvnorm->cropcap, - sizeof (tvnorm->cropcap))) { + if (!memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap, + sizeof (tvnorm->cropcap))) { bttv_crop_reset(&btv->crop[0], norm); btv->crop[1] = btv->crop[0]; /* current = default */ @@ -1322,11 +1325,11 @@ set_tvnorm(struct bttv *btv, unsigned int norm) switch (btv->c.type) { case BTTV_BOARD_VOODOOTV_FM: case BTTV_BOARD_VOODOOTV_200: - bttv_tda9880_setnorm(btv,norm); + bttv_tda9880_setnorm(btv, gpio_read()); break; } id = tvnorm->v4l2_id; - bttv_call_i2c_clients(btv, VIDIOC_S_STD, &id); + bttv_call_all(btv, tuner, s_std, id); return 0; } @@ -1350,8 +1353,8 @@ set_input(struct bttv *btv, unsigned int input, unsigned int norm) } else { video_mux(btv,input); } - audio_input(btv,(input == bttv_tvcards[btv->c.type].tuner ? - TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN)); + audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ? + TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN); set_tvnorm(btv, norm); } @@ -1470,7 +1473,7 @@ static int bttv_g_ctrl(struct file *file, void *priv, case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - bttv_call_i2c_clients(btv, VIDIOC_G_CTRL, c); + bttv_call_all(btv, core, g_ctrl, c); break; case V4L2_CID_PRIVATE_CHROMA_AGC: @@ -1544,12 +1547,12 @@ static int bttv_s_ctrl(struct file *file, void *f, if (btv->volume_gpio) btv->volume_gpio(btv, c->value); - bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, c); + bttv_call_all(btv, core, s_ctrl, c); break; case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, c); + bttv_call_all(btv, core, s_ctrl, c); break; case V4L2_CID_PRIVATE_CHROMA_AGC: @@ -1888,20 +1891,15 @@ static int bttv_enum_input(struct file *file, void *priv, { struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; - unsigned int n; - - n = i->index; + int n; - if (n >= bttv_tvcards[btv->c.type].video_inputs) + if (i->index >= bttv_tvcards[btv->c.type].video_inputs) return -EINVAL; - memset(i, 0, sizeof(*i)); - - i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; i->audioset = 1; - if (i->index == bttv_tvcards[btv->c.type].tuner) { + if (btv->tuner_type != TUNER_ABSENT && i->index == 0) { sprintf(i->name, "Television"); i->type = V4L2_INPUT_TYPE_TUNER; i->tuner = 0; @@ -1965,14 +1963,14 @@ static int bttv_s_tuner(struct file *file, void *priv, if (0 != err) return err; - if (UNSET == bttv_tvcards[btv->c.type].tuner) + if (btv->tuner_type == TUNER_ABSENT) return -EINVAL; if (0 != t->index) return -EINVAL; mutex_lock(&btv->lock); - bttv_call_i2c_clients(btv, VIDIOC_S_TUNER, t); + bttv_call_all(btv, tuner, s_tuner, t); if (btv->audio_mode_gpio) btv->audio_mode_gpio(btv, t, 1); @@ -2017,7 +2015,7 @@ static int bttv_s_frequency(struct file *file, void *priv, return -EINVAL; mutex_lock(&btv->lock); btv->freq = f->frequency; - bttv_call_i2c_clients(btv, VIDIOC_S_FREQUENCY, f); + bttv_call_all(btv, tuner, s_frequency, f); if (btv->has_matchbox && btv->radio_user) tea5757_set_freq(btv, btv->freq); mutex_unlock(&btv->lock); @@ -2031,7 +2029,7 @@ static int bttv_log_status(struct file *file, void *f) printk(KERN_INFO "bttv%d: ======== START STATUS CARD #%d ========\n", btv->c.nr, btv->c.nr); - bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, NULL); + bttv_call_all(btv, core, log_status); printk(KERN_INFO "bttv%d: ======== END STATUS CARD #%d ========\n", btv->c.nr, btv->c.nr); return 0; @@ -2659,8 +2657,7 @@ static int bttv_querycap(struct file *file, void *priv, if (no_overlay <= 0) cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; - if (bttv_tvcards[btv->c.type].tuner != UNSET && - bttv_tvcards[btv->c.type].tuner != TUNER_ABSENT) + if (btv->tuner_type != TUNER_ABSENT) cap->capabilities |= V4L2_CAP_TUNER; return 0; } @@ -2927,13 +2924,9 @@ static int bttv_g_parm(struct file *file, void *f, { struct bttv_fh *fh = f; struct bttv *btv = fh->btv; - struct v4l2_standard s; - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id, - bttv_tvnorms[btv->tvnorm].name); - parm->parm.capture.timeperframe = s.frameperiod; + v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id, + &parm->parm.capture.timeperframe); return 0; } @@ -2943,15 +2936,14 @@ static int bttv_g_tuner(struct file *file, void *priv, struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; - if (UNSET == bttv_tvcards[btv->c.type].tuner) + if (btv->tuner_type == TUNER_ABSENT) return -EINVAL; if (0 != t->index) return -EINVAL; mutex_lock(&btv->lock); - memset(t, 0, sizeof(*t)); t->rxsubchans = V4L2_TUNER_SUB_MONO; - bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t); + bttv_call_all(btv, tuner, g_tuner, t); strcpy(t->name, "Television"); t->capability = V4L2_TUNER_CAP_NORM; t->type = V4L2_TUNER_ANALOG_TV; @@ -3212,29 +3204,19 @@ err: static int bttv_open(struct file *file) { int minor = video_devdata(file)->minor; - struct bttv *btv = NULL; + struct bttv *btv = video_drvdata(file); struct bttv_fh *fh; enum v4l2_buf_type type = 0; - unsigned int i; dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor); lock_kernel(); - for (i = 0; i < bttv_num; i++) { - if (bttvs[i].video_dev && - bttvs[i].video_dev->minor == minor) { - btv = &bttvs[i]; - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - break; - } - if (bttvs[i].vbi_dev && - bttvs[i].vbi_dev->minor == minor) { - btv = &bttvs[i]; - type = V4L2_BUF_TYPE_VBI_CAPTURE; - break; - } - } - if (NULL == btv) { + if (btv->video_dev->minor == minor) { + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } else if (btv->vbi_dev->minor == minor) { + type = V4L2_BUF_TYPE_VBI_CAPTURE; + } else { + WARN_ON(1); unlock_kernel(); return -ENODEV; } @@ -3424,20 +3406,14 @@ static struct video_device bttv_video_template = { static int radio_open(struct file *file) { int minor = video_devdata(file)->minor; - struct bttv *btv = NULL; + struct bttv *btv = video_drvdata(file); struct bttv_fh *fh; - unsigned int i; dprintk("bttv: open minor=%d\n",minor); lock_kernel(); - for (i = 0; i < bttv_num; i++) { - if (bttvs[i].radio_dev && bttvs[i].radio_dev->minor == minor) { - btv = &bttvs[i]; - break; - } - } - if (NULL == btv) { + WARN_ON(btv->radio_dev && btv->radio_dev->minor != minor); + if (!btv->radio_dev || btv->radio_dev->minor != minor) { unlock_kernel(); return -ENODEV; } @@ -3458,7 +3434,7 @@ static int radio_open(struct file *file) btv->radio_user++; - bttv_call_i2c_clients(btv,AUDC_SET_RADIO,NULL); + bttv_call_all(btv, tuner, s_radio); audio_input(btv,TVAUDIO_INPUT_RADIO); mutex_unlock(&btv->lock); @@ -3478,7 +3454,7 @@ static int radio_release(struct file *file) btv->radio_user--; - bttv_call_i2c_clients(btv, RDS_CMD_CLOSE, &cmd); + bttv_call_all(btv, core, ioctl, RDS_CMD_CLOSE, &cmd); return 0; } @@ -3503,16 +3479,15 @@ static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) struct bttv_fh *fh = priv; struct bttv *btv = fh->btv; - if (UNSET == bttv_tvcards[btv->c.type].tuner) + if (btv->tuner_type == TUNER_ABSENT) return -EINVAL; if (0 != t->index) return -EINVAL; mutex_lock(&btv->lock); - memset(t, 0, sizeof(*t)); strcpy(t->name, "Radio"); t->type = V4L2_TUNER_RADIO; - bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t); + bttv_call_all(btv, tuner, g_tuner, t); if (btv->audio_mode_gpio) btv->audio_mode_gpio(btv, t, 0); @@ -3554,7 +3529,7 @@ static int radio_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; - bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t); + bttv_call_all(btv, tuner, g_tuner, t); return 0; } @@ -3615,7 +3590,7 @@ static ssize_t radio_read(struct file *file, char __user *data, cmd.instance = file; cmd.result = -ENODEV; - bttv_call_i2c_clients(btv, RDS_CMD_READ, &cmd); + bttv_call_all(btv, core, ioctl, RDS_CMD_READ, &cmd); return cmd.result; } @@ -3628,7 +3603,7 @@ static unsigned int radio_poll(struct file *file, poll_table *wait) cmd.instance = file; cmd.event_list = wait; cmd.result = -ENODEV; - bttv_call_i2c_clients(btv, RDS_CMD_POLL, &cmd); + bttv_call_all(btv, core, ioctl, RDS_CMD_POLL, &cmd); return cmd.result; } @@ -3712,14 +3687,14 @@ static void bttv_risc_disasm(struct bttv *btv, unsigned int i,j,n; printk("%s: risc disasm: %p [dma=0x%08lx]\n", - btv->c.name, risc->cpu, (unsigned long)risc->dma); + btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma); for (i = 0; i < (risc->size >> 2); i += n) { - printk("%s: 0x%lx: ", btv->c.name, + printk("%s: 0x%lx: ", btv->c.v4l2_dev.name, (unsigned long)(risc->dma + (i<<2))); n = bttv_risc_decode(le32_to_cpu(risc->cpu[i])); for (j = 1; j < n; j++) printk("%s: 0x%lx: 0x%08x [ arg #%d ]\n", - btv->c.name, (unsigned long)(risc->dma + ((i+j)<<2)), + btv->c.v4l2_dev.name, (unsigned long)(risc->dma + ((i+j)<<2)), risc->cpu[i+j], j); if (0 == risc->cpu[i]) break; @@ -4195,9 +4170,10 @@ static struct video_device *vdev_init(struct bttv *btv, return NULL; *vfd = *template; vfd->minor = -1; - vfd->parent = &btv->c.pci->dev; + vfd->v4l2_dev = &btv->c.v4l2_dev; vfd->release = video_device_release; vfd->debug = bttv_debug; + video_set_drvdata(vfd, btv); snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)", btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "", type_name, bttv_tvcards[btv->c.type].name); @@ -4307,10 +4283,14 @@ static int __devinit bttv_probe(struct pci_dev *dev, if (bttv_num == BTTV_MAX) return -ENOMEM; printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num); - btv=&bttvs[bttv_num]; - memset(btv,0,sizeof(*btv)); + bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL); + if (btv == NULL) { + printk(KERN_ERR "bttv: out of memory.\n"); + return -ENOMEM; + } btv->c.nr = bttv_num; - sprintf(btv->c.name,"bttv%d",btv->c.nr); + snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name), + "bttv%d", btv->c.nr); /* initialize structs / fill in defaults */ mutex_init(&btv->lock); @@ -4347,7 +4327,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, } if (!request_mem_region(pci_resource_start(dev,0), pci_resource_len(dev,0), - btv->c.name)) { + btv->c.v4l2_dev.name)) { printk(KERN_WARNING "bttv%d: can't request iomem (0x%llx).\n", btv->c.nr, (unsigned long long)pci_resource_start(dev,0)); @@ -4355,7 +4335,12 @@ static int __devinit bttv_probe(struct pci_dev *dev, } pci_set_master(dev); pci_set_command(dev); - pci_set_drvdata(dev,btv); + + result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev); + if (result < 0) { + printk(KERN_WARNING "bttv%d: v4l2_device_register() failed\n", btv->c.nr); + goto fail0; + } pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision); pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); @@ -4379,7 +4364,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, /* disable irqs, register irq handler */ btwrite(0, BT848_INT_MASK); result = request_irq(btv->c.pci->irq, bttv_irq, - IRQF_SHARED | IRQF_DISABLED,btv->c.name,(void *)btv); + IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv); if (result < 0) { printk(KERN_ERR "bttv%d: can't get IRQ %d\n", bttv_num,btv->c.pci->irq); @@ -4463,21 +4448,24 @@ static int __devinit bttv_probe(struct pci_dev *dev, bttv_num++; return 0; - fail2: +fail2: free_irq(btv->c.pci->irq,btv); - fail1: +fail1: + v4l2_device_unregister(&btv->c.v4l2_dev); + +fail0: if (btv->bt848_mmio) iounmap(btv->bt848_mmio); release_mem_region(pci_resource_start(btv->c.pci,0), pci_resource_len(btv->c.pci,0)); - pci_set_drvdata(dev,NULL); return result; } static void __devexit bttv_remove(struct pci_dev *pci_dev) { - struct bttv *btv = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct bttv *btv = to_bttv(v4l2_dev); if (bttv_verbose) printk("bttv%d: unloading\n",btv->c.nr); @@ -4511,14 +4499,18 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev) release_mem_region(pci_resource_start(btv->c.pci,0), pci_resource_len(btv->c.pci,0)); - pci_set_drvdata(pci_dev, NULL); + v4l2_device_unregister(&btv->c.v4l2_dev); + bttvs[btv->c.nr] = NULL; + kfree(btv); + return; } #ifdef CONFIG_PM static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state) { - struct bttv *btv = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct bttv *btv = to_bttv(v4l2_dev); struct bttv_buffer_set idle; unsigned long flags; @@ -4553,7 +4545,8 @@ static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state) static int bttv_resume(struct pci_dev *pci_dev) { - struct bttv *btv = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct bttv *btv = to_bttv(v4l2_dev); unsigned long flags; int err; diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index bcd2cd2..a99d92f 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -36,8 +36,6 @@ #include <linux/jiffies.h> #include <asm/io.h> -static int attach_inform(struct i2c_client *client); - static int i2c_debug; static int i2c_hw; static int i2c_scan; @@ -231,7 +229,8 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last) static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { - struct bttv *btv = i2c_get_adapdata(i2c_adap); + struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap); + struct bttv *btv = to_bttv(v4l2_dev); int retval = 0; int i; @@ -265,52 +264,6 @@ static const struct i2c_algorithm bttv_algo = { /* ----------------------------------------------------------------------- */ /* I2C functions - common stuff */ -static int attach_inform(struct i2c_client *client) -{ - struct bttv *btv = i2c_get_adapdata(client->adapter); - int addr=ADDR_UNSET; - - - if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr) - addr = bttv_tvcards[btv->c.type].tuner_addr; - - - if (bttv_debug) - printk(KERN_DEBUG "bttv%d: %s i2c attach [addr=0x%x,client=%s]\n", - btv->c.nr, client->driver->driver.name, client->addr, - client->name); - if (!client->driver->command) - return 0; - - if (client->driver->id == I2C_DRIVERID_MSP3400) - btv->i2c_msp34xx_client = client; - if (client->driver->id == I2C_DRIVERID_TVAUDIO) - btv->i2c_tvaudio_client = client; - if (btv->tuner_type != UNSET) { - struct tuner_setup tun_setup; - - if ((addr==ADDR_UNSET) || - (addr==client->addr)) { - - tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV | T_RADIO; - tun_setup.type = btv->tuner_type; - tun_setup.addr = addr; - bttv_call_i2c_clients(btv, TUNER_SET_TYPE_ADDR, &tun_setup); - } - - } - - return 0; -} - -void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg) -{ - if (0 != btv->i2c_rc) - return; - i2c_clients_command(&btv->c.i2c_adap, cmd, arg); -} - - /* read I2C */ int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) { @@ -417,21 +370,15 @@ int __devinit init_bttv_i2c(struct bttv *btv) btv->c.i2c_adap.algo_data = &btv->i2c_algo; } btv->c.i2c_adap.owner = THIS_MODULE; - btv->c.i2c_adap.class = I2C_CLASS_TV_ANALOG; - btv->c.i2c_adap.client_register = attach_inform; btv->c.i2c_adap.dev.parent = &btv->c.pci->dev; snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name), "bt%d #%d [%s]", btv->id, btv->c.nr, btv->use_i2c_hw ? "hw" : "sw"); - i2c_set_adapdata(&btv->c.i2c_adap, btv); + i2c_set_adapdata(&btv->c.i2c_adap, &btv->c.v4l2_dev); btv->i2c_client.adapter = &btv->c.i2c_adap; - if (bttv_tvcards[btv->c.type].no_video) - btv->c.i2c_adap.class &= ~I2C_CLASS_TV_ANALOG; - if (bttv_tvcards[btv->c.type].has_dvb) - btv->c.i2c_adap.class |= I2C_CLASS_TV_DIGITAL; if (btv->use_i2c_hw) { btv->i2c_rc = i2c_add_adapter(&btv->c.i2c_adap); @@ -441,7 +388,7 @@ int __devinit init_bttv_i2c(struct bttv *btv) btv->i2c_rc = i2c_bit_add_bus(&btv->c.i2c_adap); } if (0 == btv->i2c_rc && i2c_scan) - do_i2c_scan(btv->c.name,&btv->i2c_client); + do_i2c_scan(btv->c.v4l2_dev.name, &btv->i2c_client); return btv->i2c_rc; } diff --git a/drivers/media/video/bt8xx/bttv-if.c b/drivers/media/video/bt8xx/bttv-if.c index ecf0798..a6a540d 100644 --- a/drivers/media/video/bt8xx/bttv-if.c +++ b/drivers/media/video/bt8xx/bttv-if.c @@ -47,7 +47,10 @@ struct pci_dev* bttv_get_pcidev(unsigned int card) { if (card >= bttv_num) return NULL; - return bttvs[card].c.pci; + if (!bttvs[card]) + return NULL; + + return bttvs[card]->c.pci; } @@ -59,7 +62,10 @@ int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data) return -EINVAL; } - btv = &bttvs[card]; + btv = bttvs[card]; + if (!btv) + return -ENODEV; + gpio_inout(mask,data); if (bttv_gpio) bttv_gpio_tracking(btv,"extern enable"); @@ -74,7 +80,9 @@ int bttv_read_gpio(unsigned int card, unsigned long *data) return -EINVAL; } - btv = &bttvs[card]; + btv = bttvs[card]; + if (!btv) + return -ENODEV; if(btv->shutdown) { return -ENODEV; @@ -94,7 +102,9 @@ int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data) return -EINVAL; } - btv = &bttvs[card]; + btv = bttvs[card]; + if (!btv) + return -ENODEV; /* prior setting BT848_GPIO_REG_INP is (probably) not needed because direct input is set on init */ diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c index 5b1b8e4..d16af28 100644 --- a/drivers/media/video/bt8xx/bttv-risc.c +++ b/drivers/media/video/bt8xx/bttv-risc.c @@ -341,7 +341,7 @@ bttv_calc_geo_old(struct bttv *btv, struct bttv_geometry *geo, int totalwidth = tvnorm->totalwidth; int scaledtwidth = tvnorm->scaledtwidth; - if (bttv_tvcards[btv->c.type].muxsel[btv->input] < 0) { + if (btv->input == btv->dig) { swidth = 720; totalwidth = 858; scaledtwidth = 858; @@ -391,7 +391,7 @@ bttv_calc_geo (struct bttv * btv, && crop->width == tvnorm->cropcap.defrect.width && crop->height == tvnorm->cropcap.defrect.height && width <= tvnorm->swidth /* see PAL-Nc et al */) - || bttv_tvcards[btv->c.type].muxsel[btv->input] < 0) { + || btv->input == btv->dig) { bttv_calc_geo_old(btv, geo, width, height, both_fields, tvnorm); return; diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c index 6819e21..e79a402 100644 --- a/drivers/media/video/bt8xx/bttv-vbi.c +++ b/drivers/media/video/bt8xx/bttv-vbi.c @@ -411,7 +411,7 @@ int bttv_g_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt) return 0; } -void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, int norm) +void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm) { const struct bttv_tvnorm *tvnorm; unsigned int real_samples_per_line; diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index 529bf6c..3d36daf 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -14,8 +14,9 @@ #ifndef _BTTV_H_ #define _BTTV_H_ -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <linux/i2c.h> +#include <media/v4l2-device.h> #include <media/ir-common.h> #include <media/ir-kbd-i2c.h> #include <media/i2c-addr.h> @@ -180,6 +181,10 @@ #define BTTV_BOARD_VD012 0x99 #define BTTV_BOARD_VD012_X1 0x9a #define BTTV_BOARD_VD012_X2 0x9b +#define BTTV_BOARD_IVCE8784 0x9c +#define BTTV_BOARD_GEOVISION_GV800S 0x9d +#define BTTV_BOARD_GEOVISION_GV800S_SL 0x9e +#define BTTV_BOARD_PV183 0x9f /* more card-specific defines */ @@ -191,12 +196,9 @@ #define WINVIEW_PT2254_DATA 0x20 #define WINVIEW_PT2254_STROBE 0x80 -/* digital_mode */ -#define DIGITAL_MODE_VIDEO 1 -#define DIGITAL_MODE_CAMERA 2 - struct bttv_core { /* device structs */ + struct v4l2_device v4l2_dev; struct pci_dev *pci; struct i2c_adapter i2c_adap; struct list_head subs; /* struct bttv_sub_device */ @@ -204,59 +206,79 @@ struct bttv_core { /* device config */ unsigned int nr; /* dev nr (for printk("bttv%d: ..."); */ unsigned int type; /* card type (pointer into tvcards[]) */ - char name[8]; /* dev name */ }; struct bttv; - -struct tvcard -{ +struct tvcard { char *name; - unsigned int video_inputs; - unsigned int audio_inputs; - unsigned int tuner; - unsigned int svhs; - unsigned int digital_mode; // DIGITAL_MODE_CAMERA or DIGITAL_MODE_VIDEO + void (*volume_gpio)(struct bttv *btv, __u16 volume); + void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set); + void (*muxsel_hook)(struct bttv *btv, unsigned int input); + + /* MUX bits for each input, two bits per input starting with the LSB */ + u32 muxsel; /* Use MUXSEL() to set */ + u32 gpiomask; - u32 muxsel[16]; u32 gpiomux[4]; /* Tuner, Radio, external, internal */ u32 gpiomute; /* GPIO mute setting */ u32 gpiomask2; /* GPIO MUX mask */ + unsigned int tuner_type; + u8 tuner_addr; + u8 video_inputs; /* Number of inputs */ + unsigned int svhs:4; /* Which input is s-video */ +#define NO_SVHS 15 + unsigned int pll:2; +#define PLL_NONE 0 +#define PLL_28 1 +#define PLL_35 2 + /* i2c audio flags */ unsigned int no_msp34xx:1; unsigned int no_tda9875:1; unsigned int no_tda7432:1; unsigned int needs_tvaudio:1; unsigned int msp34xx_alt:1; + /* Note: currently no card definition needs to mark the presence + of a RDS saa6588 chip. If this is ever needed, then add a new + 'has_saa6588' bit here. */ - /* flag: video pci function is unused */ - unsigned int no_video:1; + unsigned int no_video:1; /* video pci function is unused */ unsigned int has_dvb:1; unsigned int has_remote:1; + unsigned int has_radio:1; + unsigned int has_dig_in:1; /* Has digital input (always last input) */ unsigned int no_gpioirq:1; - - /* other settings */ - unsigned int pll; -#define PLL_NONE 0 -#define PLL_28 1 -#define PLL_35 2 - - unsigned int tuner_type; - unsigned int tuner_addr; - unsigned int radio_addr; - - unsigned int has_radio; - - void (*volume_gpio)(struct bttv *btv, __u16 volume); - void (*audio_mode_gpio)(struct bttv *btv, struct v4l2_tuner *tuner, int set); - - void (*muxsel_hook)(struct bttv *btv, unsigned int input); }; extern struct tvcard bttv_tvcards[]; +/* + * This bit of cpp voodoo is used to create a macro with a variable number of + * arguments (1 to 16). It will pack each argument into a word two bits at a + * time. It can't be a function because it needs to be compile time constant to + * initialize structures. Since each argument must fit in two bits, it's ok + * that they are changed to octal. One should not use hex number, macros, or + * anything else with this macro. Just use plain integers from 0 to 3. + */ +#define _MUXSELf(a) 0##a << 30 +#define _MUXSELe(a, b...) 0##a << 28 | _MUXSELf(b) +#define _MUXSELd(a, b...) 0##a << 26 | _MUXSELe(b) +#define _MUXSELc(a, b...) 0##a << 24 | _MUXSELd(b) +#define _MUXSELb(a, b...) 0##a << 22 | _MUXSELc(b) +#define _MUXSELa(a, b...) 0##a << 20 | _MUXSELb(b) +#define _MUXSEL9(a, b...) 0##a << 18 | _MUXSELa(b) +#define _MUXSEL8(a, b...) 0##a << 16 | _MUXSEL9(b) +#define _MUXSEL7(a, b...) 0##a << 14 | _MUXSEL8(b) +#define _MUXSEL6(a, b...) 0##a << 12 | _MUXSEL7(b) +#define _MUXSEL5(a, b...) 0##a << 10 | _MUXSEL6(b) +#define _MUXSEL4(a, b...) 0##a << 8 | _MUXSEL5(b) +#define _MUXSEL3(a, b...) 0##a << 6 | _MUXSEL4(b) +#define _MUXSEL2(a, b...) 0##a << 4 | _MUXSEL3(b) +#define _MUXSEL1(a, b...) 0##a << 2 | _MUXSEL2(b) +#define MUXSEL(a, b...) (a | _MUXSEL1(b)) + /* identification / initialization of the card */ extern void bttv_idcard(struct bttv *btv); extern void bttv_init_card1(struct bttv *btv); @@ -264,7 +286,7 @@ extern void bttv_init_card2(struct bttv *btv); /* card-specific funtions */ extern void tea5757_set_freq(struct bttv *btv, unsigned short freq); -extern void bttv_tda9880_setnorm(struct bttv *btv, int norm); +extern u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits); /* extra tweaks for some chipsets */ extern void bttv_check_chipset(void); @@ -336,7 +358,9 @@ void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits); /* ---------------------------------------------------------- */ /* i2c */ -extern void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg); +#define bttv_call_all(btv, o, f, args...) \ + v4l2_device_call_all(&btv->c.v4l2_dev, 0, o, f, ##args) + extern int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for); extern int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, unsigned char b2, int both); diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index 199a4d2..9649848 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -32,7 +32,6 @@ #include <linux/wait.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> -#include <linux/videodev.h> #include <linux/pci.h> #include <linux/input.h> #include <linux/mutex.h> @@ -135,7 +134,7 @@ struct bttv_buffer { /* bttv specific */ const struct bttv_format *fmt; - int tvnorm; + unsigned int tvnorm; int btformat; int btswap; struct bttv_geometry geo; @@ -154,7 +153,7 @@ struct bttv_buffer_set { }; struct bttv_overlay { - int tvnorm; + unsigned int tvnorm; struct v4l2_rect w; enum v4l2_field field; struct v4l2_clip *clips; @@ -174,7 +173,7 @@ struct bttv_vbi_fmt { }; /* bttv-vbi.c */ -void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, int norm); +void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm); struct bttv_crop { /* A cropping rectangle in struct bttv_tvnorm.cropcap units. */ @@ -329,7 +328,8 @@ struct bttv { unsigned int cardid; /* pci subsystem id (bt878 based ones) */ unsigned int tuner_type; /* tuner chip type */ unsigned int tda9887_conf; - unsigned int svhs; + unsigned int svhs, dig; + unsigned int has_saa6588:1; struct bttv_pll_info pll; int triton1; int gpioirq; @@ -353,8 +353,8 @@ struct bttv { int i2c_state, i2c_rc; int i2c_done; wait_queue_head_t i2c_queue; - struct i2c_client *i2c_msp34xx_client; - struct i2c_client *i2c_tvaudio_client; + struct v4l2_subdev *sd_msp34xx; + struct v4l2_subdev *sd_tvaudio; /* video4linux (1) */ struct video_device *video_dev; @@ -378,7 +378,8 @@ struct bttv { unsigned int audio; unsigned int mute; unsigned long freq; - int tvnorm,hue,contrast,bright,saturation; + unsigned int tvnorm; + int hue, contrast, bright, saturation; struct v4l2_framebuffer fbuf; unsigned int field_count; @@ -458,10 +459,21 @@ struct bttv { __s32 crop_start; }; +static inline struct bttv *to_bttv(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct bttv, c.v4l2_dev); +} + /* our devices */ #define BTTV_MAX 32 extern unsigned int bttv_num; -extern struct bttv bttvs[BTTV_MAX]; +extern struct bttv *bttvs[BTTV_MAX]; + +static inline unsigned int bttv_muxsel(const struct bttv *btv, + unsigned int input) +{ + return (bttv_tvcards[btv->c.type].muxsel >> (input * 2)) & 3; +} #endif diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 46fd573..7abe94d 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -11,6 +11,12 @@ * * Written by Jonathan Corbet, corbet@lwn.net. * + * v4l2_device/v4l2_subdev conversion by: + * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl> + * + * Note: this conversion is untested! Please contact the linux-media + * mailinglist if you can test this, together with the test results. + * * This file may be distributed under the terms of the GNU General * Public License, version 2. */ @@ -25,7 +31,7 @@ #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-chip-ident.h> #include <linux/device.h> @@ -33,7 +39,6 @@ #include <linux/list.h> #include <linux/dma-mapping.h> #include <linux/delay.h> -#include <linux/debugfs.h> #include <linux/jiffies.h> #include <linux/vmalloc.h> @@ -136,6 +141,7 @@ struct cafe_sio_buffer { */ struct cafe_camera { + struct v4l2_device v4l2_dev; enum cafe_state state; unsigned long flags; /* Buffer status, mainly (dev_lock) */ int users; /* How many open FDs */ @@ -145,9 +151,10 @@ struct cafe_camera * Subsystem structures. */ struct pci_dev *pdev; - struct video_device v4ldev; + struct video_device vdev; struct i2c_adapter i2c_adapter; - struct i2c_client *sensor; + struct v4l2_subdev *sensor; + unsigned short sensor_addr; unsigned char __iomem *regs; struct list_head dev_list; /* link to other devices */ @@ -180,10 +187,6 @@ struct cafe_camera /* Misc */ wait_queue_head_t smbus_wait; /* Waiting on i2c events */ wait_queue_head_t iowait; /* Waiting on frame data */ -#ifdef CONFIG_VIDEO_ADV_DEBUG - struct dentry *dfs_regs; - struct dentry *dfs_cam_regs; -#endif }; /* @@ -195,6 +198,13 @@ struct cafe_camera #define CF_DMA_ACTIVE 3 /* A frame is incoming */ #define CF_CONFIG_NEEDED 4 /* Must configure hardware */ +#define sensor_call(cam, o, f, args...) \ + v4l2_subdev_call(cam->sensor, o, f, ##args) + +static inline struct cafe_camera *to_cam(struct v4l2_device *dev) +{ + return container_of(dev, struct cafe_camera, v4l2_dev); +} /* @@ -238,59 +248,7 @@ static void cafe_set_config_needed(struct cafe_camera *cam, int needed) /* ---------------------------------------------------------------------*/ -/* - * We keep a simple list of known devices to search at open time. - */ -static LIST_HEAD(cafe_dev_list); -static DEFINE_MUTEX(cafe_dev_list_lock); - -static void cafe_add_dev(struct cafe_camera *cam) -{ - mutex_lock(&cafe_dev_list_lock); - list_add_tail(&cam->dev_list, &cafe_dev_list); - mutex_unlock(&cafe_dev_list_lock); -} - -static void cafe_remove_dev(struct cafe_camera *cam) -{ - mutex_lock(&cafe_dev_list_lock); - list_del(&cam->dev_list); - mutex_unlock(&cafe_dev_list_lock); -} - -static struct cafe_camera *cafe_find_dev(int minor) -{ - struct cafe_camera *cam; - - mutex_lock(&cafe_dev_list_lock); - list_for_each_entry(cam, &cafe_dev_list, dev_list) { - if (cam->v4ldev.minor == minor) - goto done; - } - cam = NULL; - done: - mutex_unlock(&cafe_dev_list_lock); - return cam; -} - - -static struct cafe_camera *cafe_find_by_pdev(struct pci_dev *pdev) -{ - struct cafe_camera *cam; - mutex_lock(&cafe_dev_list_lock); - list_for_each_entry(cam, &cafe_dev_list, dev_list) { - if (cam->pdev == pdev) - goto done; - } - cam = NULL; - done: - mutex_unlock(&cafe_dev_list_lock); - return cam; -} - - -/* ------------------------------------------------------------------------ */ /* * Device register I/O */ @@ -481,18 +439,11 @@ static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char rw, u8 command, int size, union i2c_smbus_data *data) { - struct cafe_camera *cam = i2c_get_adapdata(adapter); + struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter); + struct cafe_camera *cam = to_cam(v4l2_dev); int ret = -EINVAL; /* - * Refuse to talk to anything but OV cam chips. We should - * never even see an attempt to do so, but one never knows. - */ - if (cam->sensor && addr != cam->sensor->addr) { - cam_err(cam, "funky smbus addr %d\n", addr); - return -EINVAL; - } - /* * This interface would appear to only do byte data ops. OK * it can do word too, but the cam chip has no use for that. */ @@ -530,38 +481,9 @@ static struct i2c_algorithm cafe_smbus_algo = { }; /* Somebody is on the bus */ -static int cafe_cam_init(struct cafe_camera *cam); static void cafe_ctlr_stop_dma(struct cafe_camera *cam); static void cafe_ctlr_power_down(struct cafe_camera *cam); -static int cafe_smbus_attach(struct i2c_client *client) -{ - struct cafe_camera *cam = i2c_get_adapdata(client->adapter); - - /* - * Don't talk to chips we don't recognize. - */ - if (client->driver->id == I2C_DRIVERID_OV7670) { - cam->sensor = client; - return cafe_cam_init(cam); - } - return -EINVAL; -} - -static int cafe_smbus_detach(struct i2c_client *client) -{ - struct cafe_camera *cam = i2c_get_adapdata(client->adapter); - - if (cam->sensor == client) { - cafe_ctlr_stop_dma(cam); - cafe_ctlr_power_down(cam); - cam_err(cam, "lost the sensor!\n"); - cam->sensor = NULL; /* Bummer, no camera */ - cam->state = S_NOTREADY; - } - return 0; -} - static int cafe_smbus_setup(struct cafe_camera *cam) { struct i2c_adapter *adap = &cam->i2c_adapter; @@ -570,12 +492,10 @@ static int cafe_smbus_setup(struct cafe_camera *cam) cafe_smbus_enable_irq(cam); adap->id = I2C_HW_SMBUS_CAFE; adap->owner = THIS_MODULE; - adap->client_register = cafe_smbus_attach; - adap->client_unregister = cafe_smbus_detach; adap->algo = &cafe_smbus_algo; strcpy(adap->name, "cafe_ccic"); adap->dev.parent = &cam->pdev->dev; - i2c_set_adapdata(adap, cam); + i2c_set_adapdata(adap, &cam->v4l2_dev); ret = i2c_add_adapter(adap); if (ret) printk(KERN_ERR "Unable to register cafe i2c adapter\n"); @@ -809,9 +729,9 @@ static void cafe_ctlr_power_up(struct cafe_camera *cam) * Control 1 is power down, set to 0 to operate. */ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */ -// mdelay(1); /* Marvell says 1ms will do it */ +/* mdelay(1); */ /* Marvell says 1ms will do it */ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0); -// mdelay(1); /* Enough? */ +/* mdelay(1); */ /* Enough? */ spin_unlock_irqrestore(&cam->dev_lock, flags); msleep(5); /* Just to be sure */ } @@ -833,23 +753,9 @@ static void cafe_ctlr_power_down(struct cafe_camera *cam) * Communications with the sensor. */ -static int __cafe_cam_cmd(struct cafe_camera *cam, int cmd, void *arg) -{ - struct i2c_client *sc = cam->sensor; - int ret; - - if (sc == NULL || sc->driver == NULL || sc->driver->command == NULL) - return -EINVAL; - ret = sc->driver->command(sc, cmd, arg); - if (ret == -EPERM) /* Unsupported command */ - return 0; - return ret; -} - static int __cafe_cam_reset(struct cafe_camera *cam) { - int zero = 0; - return __cafe_cam_cmd(cam, VIDIOC_INT_RESET, &zero); + return sensor_call(cam, core, reset, 0); } /* @@ -869,14 +775,13 @@ static int cafe_cam_init(struct cafe_camera *cam) if (ret) goto out; chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR; - chip.match.addr = cam->sensor->addr; - ret = __cafe_cam_cmd(cam, VIDIOC_DBG_G_CHIP_IDENT, &chip); + chip.match.addr = cam->sensor_addr; + ret = sensor_call(cam, core, g_chip_ident, &chip); if (ret) goto out; cam->sensor_type = chip.ident; -// if (cam->sensor->addr != OV7xx0_SID) { if (cam->sensor_type != V4L2_IDENT_OV7670) { - cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr); + cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type); ret = -EINVAL; goto out; } @@ -900,21 +805,21 @@ static int cafe_cam_set_flip(struct cafe_camera *cam) memset(&ctrl, 0, sizeof(ctrl)); ctrl.id = V4L2_CID_VFLIP; ctrl.value = flip; - return __cafe_cam_cmd(cam, VIDIOC_S_CTRL, &ctrl); + return sensor_call(cam, core, s_ctrl, &ctrl); } static int cafe_cam_configure(struct cafe_camera *cam) { struct v4l2_format fmt; - int ret, zero = 0; + int ret; if (cam->state != S_IDLE) return -EINVAL; fmt.fmt.pix = cam->pix_format; - ret = __cafe_cam_cmd(cam, VIDIOC_INT_INIT, &zero); + ret = sensor_call(cam, core, init, 0); if (ret == 0) - ret = __cafe_cam_cmd(cam, VIDIOC_S_FMT, &fmt); + ret = sensor_call(cam, video, s_fmt, &fmt); /* * OV7670 does weird things if flip is set *before* format... */ @@ -1246,8 +1151,6 @@ static int cafe_vidioc_reqbufs(struct file *filp, void *priv, * Make sure it's something we can do. User pointers could be * implemented without great pain, but that's not been done yet. */ - if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (req->memory != V4L2_MEMORY_MMAP) return -EINVAL; /* @@ -1311,9 +1214,7 @@ static int cafe_vidioc_querybuf(struct file *filp, void *priv, int ret = -EINVAL; mutex_lock(&cam->s_mutex); - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - goto out; - if (buf->index < 0 || buf->index >= cam->n_sbufs) + if (buf->index >= cam->n_sbufs) goto out; *buf = cam->sb_bufs[buf->index].v4lbuf; ret = 0; @@ -1331,9 +1232,7 @@ static int cafe_vidioc_qbuf(struct file *filp, void *priv, unsigned long flags; mutex_lock(&cam->s_mutex); - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - goto out; - if (buf->index < 0 || buf->index >= cam->n_sbufs) + if (buf->index >= cam->n_sbufs) goto out; sbuf = cam->sb_bufs + buf->index; if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) { @@ -1364,8 +1263,6 @@ static int cafe_vidioc_dqbuf(struct file *filp, void *priv, unsigned long flags; mutex_lock(&cam->s_mutex); - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - goto out_unlock; if (cam->state != S_STREAMING) goto out_unlock; if (list_empty(&cam->sb_full) && filp->f_flags & O_NONBLOCK) { @@ -1474,11 +1371,8 @@ static int cafe_v4l_mmap(struct file *filp, struct vm_area_struct *vma) static int cafe_v4l_open(struct file *filp) { - struct cafe_camera *cam; + struct cafe_camera *cam = video_drvdata(filp); - cam = cafe_find_dev(video_devdata(filp)->minor); - if (cam == NULL) - return -ENODEV; filp->private_data = cam; mutex_lock(&cam->s_mutex); @@ -1532,11 +1426,11 @@ static unsigned int cafe_v4l_poll(struct file *filp, static int cafe_vidioc_queryctrl(struct file *filp, void *priv, struct v4l2_queryctrl *qc) { - struct cafe_camera *cam = filp->private_data; + struct cafe_camera *cam = priv; int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_QUERYCTRL, qc); + ret = sensor_call(cam, core, queryctrl, qc); mutex_unlock(&cam->s_mutex); return ret; } @@ -1545,11 +1439,11 @@ static int cafe_vidioc_queryctrl(struct file *filp, void *priv, static int cafe_vidioc_g_ctrl(struct file *filp, void *priv, struct v4l2_control *ctrl) { - struct cafe_camera *cam = filp->private_data; + struct cafe_camera *cam = priv; int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_G_CTRL, ctrl); + ret = sensor_call(cam, core, g_ctrl, ctrl); mutex_unlock(&cam->s_mutex); return ret; } @@ -1558,11 +1452,11 @@ static int cafe_vidioc_g_ctrl(struct file *filp, void *priv, static int cafe_vidioc_s_ctrl(struct file *filp, void *priv, struct v4l2_control *ctrl) { - struct cafe_camera *cam = filp->private_data; + struct cafe_camera *cam = priv; int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_S_CTRL, ctrl); + ret = sensor_call(cam, core, s_ctrl, ctrl); mutex_unlock(&cam->s_mutex); return ret; } @@ -1601,10 +1495,8 @@ static int cafe_vidioc_enum_fmt_vid_cap(struct file *filp, struct cafe_camera *cam = priv; int ret; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_ENUM_FMT, fmt); + ret = sensor_call(cam, video, enum_fmt, fmt); mutex_unlock(&cam->s_mutex); return ret; } @@ -1617,7 +1509,7 @@ static int cafe_vidioc_try_fmt_vid_cap(struct file *filp, void *priv, int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_TRY_FMT, fmt); + ret = sensor_call(cam, video, try_fmt, fmt); mutex_unlock(&cam->s_mutex); return ret; } @@ -1726,7 +1618,7 @@ static int cafe_vidioc_g_parm(struct file *filp, void *priv, int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_G_PARM, parms); + ret = sensor_call(cam, video, g_parm, parms); mutex_unlock(&cam->s_mutex); parms->parm.capture.readbuffers = n_dma_bufs; return ret; @@ -1739,20 +1631,52 @@ static int cafe_vidioc_s_parm(struct file *filp, void *priv, int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_S_PARM, parms); + ret = sensor_call(cam, video, s_parm, parms); mutex_unlock(&cam->s_mutex); parms->parm.capture.readbuffers = n_dma_bufs; return ret; } +static int cafe_vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + struct cafe_camera *cam = priv; -static void cafe_v4l_dev_release(struct video_device *vd) + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (v4l2_chip_match_host(&chip->match)) { + chip->ident = V4L2_IDENT_CAFE; + return 0; + } + return sensor_call(cam, core, g_chip_ident, chip); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cafe_vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) { - struct cafe_camera *cam = container_of(vd, struct cafe_camera, v4ldev); + struct cafe_camera *cam = priv; - kfree(cam); + if (v4l2_chip_match_host(®->match)) { + reg->val = cafe_reg_read(cam, reg->reg); + reg->size = 4; + return 0; + } + return sensor_call(cam, core, g_register, reg); } +static int cafe_vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct cafe_camera *cam = priv; + + if (v4l2_chip_match_host(®->match)) { + cafe_reg_write(cam, reg->reg, reg->val); + return 0; + } + return sensor_call(cam, core, s_register, reg); +} +#endif /* * This template device holds all of those v4l2 methods; we @@ -1790,6 +1714,11 @@ static const struct v4l2_ioctl_ops cafe_v4l_ioctl_ops = { .vidioc_s_ctrl = cafe_vidioc_s_ctrl, .vidioc_g_parm = cafe_vidioc_g_parm, .vidioc_s_parm = cafe_vidioc_s_parm, + .vidioc_g_chip_ident = cafe_vidioc_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = cafe_vidioc_g_register, + .vidioc_s_register = cafe_vidioc_s_register, +#endif }; static struct video_device cafe_v4l_template = { @@ -1800,15 +1729,10 @@ static struct video_device cafe_v4l_template = { .fops = &cafe_v4l_fops, .ioctl_ops = &cafe_v4l_ioctl_ops, - .release = cafe_v4l_dev_release, + .release = video_device_release_empty, }; - - - - - /* ---------------------------------------------------------------------- */ /* * Interrupt handler stuff @@ -1962,127 +1886,6 @@ static irqreturn_t cafe_irq(int irq, void *data) /* -------------------------------------------------------------------------- */ -#ifdef CONFIG_VIDEO_ADV_DEBUG -/* - * Debugfs stuff. - */ - -static char cafe_debug_buf[1024]; -static struct dentry *cafe_dfs_root; - -static void cafe_dfs_setup(void) -{ - cafe_dfs_root = debugfs_create_dir("cafe_ccic", NULL); - if (IS_ERR(cafe_dfs_root)) { - cafe_dfs_root = NULL; /* Never mind */ - printk(KERN_NOTICE "cafe_ccic unable to set up debugfs\n"); - } -} - -static void cafe_dfs_shutdown(void) -{ - if (cafe_dfs_root) - debugfs_remove(cafe_dfs_root); -} - -static int cafe_dfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t cafe_dfs_read_regs(struct file *file, - char __user *buf, size_t count, loff_t *ppos) -{ - struct cafe_camera *cam = file->private_data; - char *s = cafe_debug_buf; - int offset; - - for (offset = 0; offset < 0x44; offset += 4) - s += sprintf(s, "%02x: %08x\n", offset, - cafe_reg_read(cam, offset)); - for (offset = 0x88; offset <= 0x90; offset += 4) - s += sprintf(s, "%02x: %08x\n", offset, - cafe_reg_read(cam, offset)); - for (offset = 0xb4; offset <= 0xbc; offset += 4) - s += sprintf(s, "%02x: %08x\n", offset, - cafe_reg_read(cam, offset)); - for (offset = 0x3000; offset <= 0x300c; offset += 4) - s += sprintf(s, "%04x: %08x\n", offset, - cafe_reg_read(cam, offset)); - return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf, - s - cafe_debug_buf); -} - -static const struct file_operations cafe_dfs_reg_ops = { - .owner = THIS_MODULE, - .read = cafe_dfs_read_regs, - .open = cafe_dfs_open -}; - -static ssize_t cafe_dfs_read_cam(struct file *file, - char __user *buf, size_t count, loff_t *ppos) -{ - struct cafe_camera *cam = file->private_data; - char *s = cafe_debug_buf; - int offset; - - if (! cam->sensor) - return -EINVAL; - for (offset = 0x0; offset < 0x8a; offset++) - { - u8 v; - - cafe_smbus_read_data(cam, cam->sensor->addr, offset, &v); - s += sprintf(s, "%02x: %02x\n", offset, v); - } - return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf, - s - cafe_debug_buf); -} - -static const struct file_operations cafe_dfs_cam_ops = { - .owner = THIS_MODULE, - .read = cafe_dfs_read_cam, - .open = cafe_dfs_open -}; - - - -static void cafe_dfs_cam_setup(struct cafe_camera *cam) -{ - char fname[40]; - - if (!cafe_dfs_root) - return; - sprintf(fname, "regs-%d", cam->v4ldev.num); - cam->dfs_regs = debugfs_create_file(fname, 0444, cafe_dfs_root, - cam, &cafe_dfs_reg_ops); - sprintf(fname, "cam-%d", cam->v4ldev.num); - cam->dfs_cam_regs = debugfs_create_file(fname, 0444, cafe_dfs_root, - cam, &cafe_dfs_cam_ops); -} - - -static void cafe_dfs_cam_shutdown(struct cafe_camera *cam) -{ - if (! IS_ERR(cam->dfs_regs)) - debugfs_remove(cam->dfs_regs); - if (! IS_ERR(cam->dfs_cam_regs)) - debugfs_remove(cam->dfs_cam_regs); -} - -#else - -#define cafe_dfs_setup() -#define cafe_dfs_shutdown() -#define cafe_dfs_cam_setup(cam) -#define cafe_dfs_cam_shutdown(cam) -#endif /* CONFIG_VIDEO_ADV_DEBUG */ - - - - -/* ------------------------------------------------------------------------*/ /* * PCI interface stuff. */ @@ -2100,6 +1903,10 @@ static int cafe_pci_probe(struct pci_dev *pdev, cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL); if (cam == NULL) goto out; + ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev); + if (ret) + goto out_free; + mutex_init(&cam->s_mutex); mutex_lock(&cam->s_mutex); spin_lock_init(&cam->dev_lock); @@ -2118,14 +1925,14 @@ static int cafe_pci_probe(struct pci_dev *pdev, */ ret = pci_enable_device(pdev); if (ret) - goto out_free; + goto out_unreg; pci_set_master(pdev); ret = -EIO; cam->regs = pci_iomap(pdev, 0, 0); if (! cam->regs) { printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n"); - goto out_free; + goto out_unreg; } ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam); if (ret) @@ -2145,17 +1952,31 @@ static int cafe_pci_probe(struct pci_dev *pdev, ret = cafe_smbus_setup(cam); if (ret) goto out_freeirq; + + cam->sensor_addr = 0x42; + cam->sensor = v4l2_i2c_new_subdev(&cam->i2c_adapter, + "ov7670", "ov7670", cam->sensor_addr); + if (cam->sensor == NULL) { + ret = -ENODEV; + goto out_smbus; + } + ret = cafe_cam_init(cam); + if (ret) + goto out_smbus; + /* * Get the v4l2 setup done. */ mutex_lock(&cam->s_mutex); - cam->v4ldev = cafe_v4l_template; - cam->v4ldev.debug = 0; -// cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG; - cam->v4ldev.parent = &pdev->dev; - ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1); + cam->vdev = cafe_v4l_template; + cam->vdev.debug = 0; +/* cam->vdev.debug = V4L2_DEBUG_IOCTL_ARG;*/ + cam->vdev.v4l2_dev = &cam->v4l2_dev; + ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); if (ret) goto out_smbus; + video_set_drvdata(&cam->vdev, cam); + /* * If so requested, try to get our DMA buffers now. */ @@ -2165,21 +1986,21 @@ static int cafe_pci_probe(struct pci_dev *pdev, " will try again later."); } - cafe_dfs_cam_setup(cam); mutex_unlock(&cam->s_mutex); - cafe_add_dev(cam); return 0; - out_smbus: +out_smbus: cafe_smbus_shutdown(cam); - out_freeirq: +out_freeirq: cafe_ctlr_power_down(cam); free_irq(pdev->irq, cam); - out_iounmap: +out_iounmap: pci_iounmap(pdev, cam->regs); - out_free: +out_free: + v4l2_device_unregister(&cam->v4l2_dev); +out_unreg: kfree(cam); - out: +out: return ret; } @@ -2190,25 +2011,23 @@ static int cafe_pci_probe(struct pci_dev *pdev, static void cafe_shutdown(struct cafe_camera *cam) { /* FIXME: Make sure we take care of everything here */ - cafe_dfs_cam_shutdown(cam); if (cam->n_sbufs > 0) /* What if they are still mapped? Shouldn't be, but... */ cafe_free_sio_buffers(cam); - cafe_remove_dev(cam); cafe_ctlr_stop_dma(cam); cafe_ctlr_power_down(cam); cafe_smbus_shutdown(cam); cafe_free_dma_bufs(cam); free_irq(cam->pdev->irq, cam); pci_iounmap(cam->pdev, cam->regs); - video_unregister_device(&cam->v4ldev); - /* kfree(cam); done in v4l_release () */ + video_unregister_device(&cam->vdev); } static void cafe_pci_remove(struct pci_dev *pdev) { - struct cafe_camera *cam = cafe_find_by_pdev(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct cafe_camera *cam = to_cam(v4l2_dev); if (cam == NULL) { printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev); @@ -2218,6 +2037,8 @@ static void cafe_pci_remove(struct pci_dev *pdev) if (cam->users > 0) cam_warn(cam, "Removing a device with users!\n"); cafe_shutdown(cam); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); /* No unlock - it no longer exists */ } @@ -2228,7 +2049,8 @@ static void cafe_pci_remove(struct pci_dev *pdev) */ static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) { - struct cafe_camera *cam = cafe_find_by_pdev(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct cafe_camera *cam = to_cam(v4l2_dev); int ret; enum cafe_state cstate; @@ -2246,7 +2068,8 @@ static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) static int cafe_pci_resume(struct pci_dev *pdev) { - struct cafe_camera *cam = cafe_find_by_pdev(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct cafe_camera *cam = to_cam(v4l2_dev); int ret = 0; ret = pci_restore_state(pdev); @@ -2307,13 +2130,11 @@ static int __init cafe_init(void) printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n", CAFE_VERSION); - cafe_dfs_setup(); ret = pci_register_driver(&cafe_pci_driver); if (ret) { printk(KERN_ERR "Unable to register cafe_ccic driver\n"); goto out; } - request_module("ov7670"); /* FIXME want something more general */ ret = 0; out: @@ -2324,7 +2145,6 @@ static int __init cafe_init(void) static void __exit cafe_exit(void) { pci_unregister_driver(&cafe_pci_driver); - cafe_dfs_shutdown(); } module_init(cafe_init); diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 9c25894..d4099f5 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -37,6 +37,7 @@ #include <linux/sched.h> #include <linux/slab.h> #include <linux/init.h> +#include <linux/videodev.h> #include <media/v4l2-ioctl.h> #include "cpia2.h" diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c index 87e9107..9714059 100644 --- a/drivers/media/video/cs5345.c +++ b/drivers/media/video/cs5345.c @@ -141,11 +141,6 @@ static int cs5345_log_status(struct v4l2_subdev *sd) return 0; } -static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops cs5345_core_ops = { @@ -214,8 +209,6 @@ MODULE_DEVICE_TABLE(i2c, cs5345_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cs5345", - .driverid = I2C_DRIVERID_CS5345, - .command = cs5345_command, .probe = cs5345_probe, .remove = cs5345_remove, .id_table = cs5345_id, diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index 7292a63..5aeb066 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -29,7 +29,7 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); MODULE_AUTHOR("Martin Vaughan"); @@ -41,9 +41,6 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); -static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; - -I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ @@ -122,11 +119,6 @@ static int cs53l32a_log_status(struct v4l2_subdev *sd) return 0; } -static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { @@ -218,8 +210,6 @@ MODULE_DEVICE_TABLE(i2c, cs53l32a_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cs53l32a", - .driverid = I2C_DRIVERID_CS53L32A, - .command = cs53l32a_command, .remove = cs53l32a_remove, .probe = cs53l32a_probe, .id_table = cs53l32a_id, diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index 8940b53..e8a50a6 100644 --- a/drivers/media/video/cx18/Kconfig +++ b/drivers/media/video/cx18/Kconfig @@ -9,7 +9,7 @@ config VIDEO_CX18 select VIDEO_CX2341X select VIDEO_CS5345 select DVB_S5H1409 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE ---help--- This is a video4linux driver for Conexant cx23418 based PCI combo video recorder devices. diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c index 57beddf..bb5c516 100644 --- a/drivers/media/video/cx18/cx18-audio.c +++ b/drivers/media/video/cx18/cx18-audio.c @@ -23,7 +23,6 @@ #include "cx18-driver.h" #include "cx18-io.h" -#include "cx18-i2c.h" #include "cx18-cards.h" #include "cx18-audio.h" @@ -33,55 +32,32 @@ settings. */ int cx18_audio_set_io(struct cx18 *cx) { + const struct cx18_card_audio_input *in; struct v4l2_routing route; - u32 audio_input; u32 val; - int mux_input; int err; /* Determine which input to use */ - if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { - audio_input = cx->card->radio_input.audio_input; - mux_input = cx->card->radio_input.muxer_input; - } else { - audio_input = - cx->card->audio_inputs[cx->audio_input].audio_input; - mux_input = - cx->card->audio_inputs[cx->audio_input].muxer_input; - } + if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) + in = &cx->card->radio_input; + else + in = &cx->card->audio_inputs[cx->audio_input]; /* handle muxer chips */ - route.input = mux_input; + route.input = in->muxer_input; route.output = 0; - cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route); + v4l2_subdev_call(cx->sd_extmux, audio, s_routing, &route); - route.input = audio_input; - err = cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, - VIDIOC_INT_S_AUDIO_ROUTING, &route); + route.input = in->audio_input; + err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl, + audio, s_routing, &route); if (err) return err; + /* FIXME - this internal mux should be abstracted to a subdev */ val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30; - val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 : - (audio_input << 4); - cx18_write_reg(cx, val | 0xb00, CX18_AUDIO_ENABLE); - cx18_vapi(cx, CX18_APU_RESETAI, 1, 0); + val |= (in->audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 : + (in->audio_input << 4); + cx18_write_reg_expect(cx, val | 0xb00, CX18_AUDIO_ENABLE, val, 0x30); return 0; } - -void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route) -{ - cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, - VIDIOC_INT_S_AUDIO_ROUTING, route); -} - -void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq) -{ - static u32 freqs[3] = { 44100, 48000, 32000 }; - - /* The audio clock of the digitizer must match the codec sample - rate otherwise you get some very strange effects. */ - if (freq > 2) - return; - cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]); -} diff --git a/drivers/media/video/cx18/cx18-audio.h b/drivers/media/video/cx18/cx18-audio.h index cb569a6..2731d29 100644 --- a/drivers/media/video/cx18/cx18-audio.h +++ b/drivers/media/video/cx18/cx18-audio.h @@ -22,5 +22,3 @@ */ int cx18_audio_set_io(struct cx18 *cx); -void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route); -void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq); diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c index a2f0ad5..9e30983 100644 --- a/drivers/media/video/cx18/cx18-av-audio.c +++ b/drivers/media/video/cx18/cx18-av-audio.c @@ -464,82 +464,76 @@ static void set_mute(struct cx18 *cx, int mute) } } -int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg) +int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { + struct cx18 *cx = v4l2_get_subdevdata(sd); struct cx18_av_state *state = &cx->av_state; - struct v4l2_control *ctrl = arg; int retval; + u8 v; - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - { - u8 v; - if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { - v = cx18_av_read(cx, 0x803) & ~0x10; - cx18_av_write_expect(cx, 0x803, v, v, 0x1f); - cx18_av_write(cx, 0x8d3, 0x1f); - } - v = cx18_av_read(cx, 0x810) | 0x1; - cx18_av_write_expect(cx, 0x810, v, v, 0x0f); + if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { + v = cx18_av_read(cx, 0x803) & ~0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); + cx18_av_write(cx, 0x8d3, 0x1f); + } + v = cx18_av_read(cx, 0x810) | 0x1; + cx18_av_write_expect(cx, 0x810, v, v, 0x0f); - retval = set_audclk_freq(cx, *(u32 *)arg); + retval = set_audclk_freq(cx, freq); - v = cx18_av_read(cx, 0x810) & ~0x1; - cx18_av_write_expect(cx, 0x810, v, v, 0x0f); - if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { - v = cx18_av_read(cx, 0x803) | 0x10; - cx18_av_write_expect(cx, 0x803, v, v, 0x1f); - } - return retval; + v = cx18_av_read(cx, 0x810) & ~0x1; + cx18_av_write_expect(cx, 0x810, v, v, 0x0f); + if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { + v = cx18_av_read(cx, 0x803) | 0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); } + return retval; +} - case VIDIOC_G_CTRL: - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = get_volume(cx); - break; - case V4L2_CID_AUDIO_BASS: - ctrl->value = get_bass(cx); - break; - case V4L2_CID_AUDIO_TREBLE: - ctrl->value = get_treble(cx); - break; - case V4L2_CID_AUDIO_BALANCE: - ctrl->value = get_balance(cx); - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = get_mute(cx); - break; - default: - return -EINVAL; - } +int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = get_volume(cx); break; - - case VIDIOC_S_CTRL: - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - set_volume(cx, ctrl->value); - break; - case V4L2_CID_AUDIO_BASS: - set_bass(cx, ctrl->value); - break; - case V4L2_CID_AUDIO_TREBLE: - set_treble(cx, ctrl->value); - break; - case V4L2_CID_AUDIO_BALANCE: - set_balance(cx, ctrl->value); - break; - case V4L2_CID_AUDIO_MUTE: - set_mute(cx, ctrl->value); - break; - default: - return -EINVAL; - } + case V4L2_CID_AUDIO_BASS: + ctrl->value = get_bass(cx); + break; + case V4L2_CID_AUDIO_TREBLE: + ctrl->value = get_treble(cx); + break; + case V4L2_CID_AUDIO_BALANCE: + ctrl->value = get_balance(cx); + break; + case V4L2_CID_AUDIO_MUTE: + ctrl->value = get_mute(cx); break; - default: return -EINVAL; } + return 0; +} +int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + set_volume(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_BASS: + set_bass(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_TREBLE: + set_treble(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_BALANCE: + set_balance(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_MUTE: + set_mute(cx, ctrl->value); + break; + default: + return -EINVAL; + } return 0; } diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 0b1c84b..f4dd9d7 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -22,8 +22,10 @@ * 02110-1301, USA. */ +#include <media/v4l2-chip-ident.h> #include "cx18-driver.h" #include "cx18-io.h" +#include "cx18-cards.h" int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) { @@ -97,15 +99,6 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, or_value); } -/* ----------------------------------------------------------------------- */ - -static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, - enum cx18_av_audio_input aud_input); -static void log_audio_status(struct cx18 *cx); -static void log_video_status(struct cx18 *cx); - -/* ----------------------------------------------------------------------- */ - static void cx18_av_initialize(struct cx18 *cx) { struct cx18_av_state *state = &cx->av_state; @@ -169,9 +162,14 @@ static void cx18_av_initialize(struct cx18 *cx) /* Set VGA_TRACK_RANGE to 0x20 */ cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000); - /* Enable VBI capture */ - cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253F); - /* cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253E); */ + /* + * Initial VBI setup + * VIP-1.1, 10 bit mode, enable Raw, disable sliced, + * don't clamp raw samples when codes are in use, 1 byte user D-words, + * IDID0 has line #, RP code V bit transition on VBLANK, data during + * blanking intervals + */ + cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4013252e); /* Set the video input. The setting in MODE_CTRL gets lost when we do the above setup */ @@ -195,11 +193,61 @@ static void cx18_av_initialize(struct cx18 *cx) state->default_volume = ((state->default_volume / 2) + 23) << 9; } -/* ----------------------------------------------------------------------- */ +static int cx18_av_reset(struct v4l2_subdev *sd, u32 val) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + + cx18_av_initialize(cx); + return 0; +} + +static int cx18_av_init(struct v4l2_subdev *sd, u32 val) +{ + struct cx18_av_state *state = to_cx18_av_state(sd); + struct cx18 *cx = v4l2_get_subdevdata(sd); + + switch (val) { + case CX18_AV_INIT_PLLS: + /* + * The crystal freq used in calculations in this driver will be + * 28.636360 MHz. + * Aim to run the PLLs' VCOs near 400 MHz to minimze errors. + */ + + /* + * VDCLK Integer = 0x0f, Post Divider = 0x04 + * AIMCLK Integer = 0x0e, Post Divider = 0x16 + */ + cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f); + + /* VDCLK Fraction = 0x2be2fe */ + /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */ + cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe); + + /* AIMCLK Fraction = 0x05227ad */ + /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/ + cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad); + + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ + cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56); + break; + + case CX18_AV_INIT_NORMAL: + default: + if (!state->is_initialized) { + /* initialize on first use */ + state->is_initialized = 1; + cx18_av_initialize(cx); + } + break; + } + return 0; +} void cx18_av_std_setup(struct cx18 *cx) { struct cx18_av_state *state = &cx->av_state; + struct v4l2_subdev *sd = &state->sd; v4l2_std_id std = state->std; int hblank, hactive, burst, vblank, vactive, sc; int vblank656, src_decimation; @@ -213,6 +261,7 @@ void cx18_av_std_setup(struct cx18 *cx) cx18_av_write(cx, 0x49f, 0x14); if (std & V4L2_STD_625_50) { + /* FIXME - revisit these for Sliced VBI */ hblank = 132; hactive = 720; burst = 93; @@ -236,13 +285,40 @@ void cx18_av_std_setup(struct cx18 *cx) sc = 672351; } } else { + /* + * The following relationships of half line counts should hold: + * 525 = vsync + vactive + vblank656 + * 12 = vblank656 - vblank + * + * vsync: always 6 half-lines of vsync pulses + * vactive: half lines of active video + * vblank656: half lines, after line 3/mid-266, of blanked video + * vblank: half lines, after line 9/272, of blanked video + * + * As far as I can tell: + * vblank656 starts counting from the falling edge of the first + * vsync pulse (start of line 4 or mid-266) + * vblank starts counting from the after the 6 vsync pulses and + * 6 or 5 equalization pulses (start of line 10 or 272) + * + * For 525 line systems the driver will extract VBI information + * from lines 10-21 and lines 273-284. + */ + vblank656 = 38; /* lines 4 - 22 & 266 - 284 */ + vblank = 26; /* lines 10 - 22 & 272 - 284 */ + vactive = 481; /* lines 23 - 263 & 285 - 525 */ + + /* + * For a 13.5 Mpps clock and 15,734.26 Hz line rate, a line is + * is 858 pixels = 720 active + 138 blanking. The Hsync leading + * edge should happen 1.2 us * 13.5 Mpps ~= 16 pixels after the + * end of active video, leaving 122 pixels of hblank to ignore + * before active video starts. + */ hactive = 720; hblank = 122; - vactive = 487; luma_lpf = 1; uv_lpf = 1; - vblank = 26; - vblank656 = 26; src_decimation = 0x21f; if (std == V4L2_STD_PAL_60) { @@ -265,33 +341,35 @@ void cx18_av_std_setup(struct cx18 *cx) pll_int = cx18_av_read(cx, 0x108); pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff; pll_post = cx18_av_read(cx, 0x109); - CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n", - pll_int, pll_frac, pll_post); + CX18_DEBUG_INFO_DEV(sd, "PLL regs = int: %u, frac: %u, post: %u\n", + pll_int, pll_frac, pll_post); if (pll_post) { int fin, fsc, pll; pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25; pll /= pll_post; - CX18_DEBUG_INFO("PLL = %d.%06d MHz\n", - pll / 1000000, pll % 1000000); - CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n", - pll / 8000000, (pll / 8) % 1000000); + CX18_DEBUG_INFO_DEV(sd, "PLL = %d.%06d MHz\n", + pll / 1000000, pll % 1000000); + CX18_DEBUG_INFO_DEV(sd, "PLL/8 = %d.%06d MHz\n", + pll / 8000000, (pll / 8) % 1000000); fin = ((u64)src_decimation * pll) >> 12; - CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n", - fin / 1000000, fin % 1000000); + CX18_DEBUG_INFO_DEV(sd, "ADC Sampling freq = %d.%06d MHz\n", + fin / 1000000, fin % 1000000); fsc = (((u64)sc) * pll) >> 24L; - CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n", - fsc / 1000000, fsc % 1000000); - - CX18_DEBUG_INFO("hblank %i, hactive %i, " - "vblank %i , vactive %i, vblank656 %i, src_dec %i," - "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x," - " sc 0x%06x\n", - hblank, hactive, vblank, vactive, vblank656, - src_decimation, burst, luma_lpf, uv_lpf, comb, sc); + CX18_DEBUG_INFO_DEV(sd, + "Chroma sub-carrier freq = %d.%06d MHz\n", + fsc / 1000000, fsc % 1000000); + + CX18_DEBUG_INFO_DEV(sd, "hblank %i, hactive %i, vblank %i, " + "vactive %i, vblank656 %i, src_dec %i, " + "burst 0x%02x, luma_lpf %i, uv_lpf %i, " + "comb 0x%02x, sc 0x%06x\n", + hblank, hactive, vblank, vactive, vblank656, + src_decimation, burst, luma_lpf, uv_lpf, + comb, sc); } /* Sets horizontal blanking delay and active lines */ @@ -325,18 +403,16 @@ void cx18_av_std_setup(struct cx18 *cx) cx18_av_write(cx, 0x47d, 0xff & sc >> 8); cx18_av_write(cx, 0x47e, 0xff & sc >> 16); - /* Sets VBI parameters */ if (std & V4L2_STD_625_50) { - cx18_av_write(cx, 0x47f, 0x01); - state->vbi_line_offset = 5; + state->slicer_line_delay = 1; + state->slicer_line_offset = (6 + state->slicer_line_delay - 2); } else { - cx18_av_write(cx, 0x47f, 0x00); - state->vbi_line_offset = 8; + state->slicer_line_delay = 0; + state->slicer_line_offset = (10 + state->slicer_line_delay - 2); } + cx18_av_write(cx, 0x47f, state->slicer_line_delay); } -/* ----------------------------------------------------------------------- */ - static void input_change(struct cx18 *cx) { struct cx18_av_state *state = &cx->av_state; @@ -382,17 +458,26 @@ static void input_change(struct cx18 *cx) } } +static int cx18_av_s_frequency(struct v4l2_subdev *sd, + struct v4l2_frequency *freq) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + input_change(cx); + return 0; +} + static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, enum cx18_av_audio_input aud_input) { struct cx18_av_state *state = &cx->av_state; + struct v4l2_subdev *sd = &state->sd; u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 && vid_input <= CX18_AV_COMPOSITE8); u8 reg; u8 v; - CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n", - vid_input, aud_input); + CX18_DEBUG_INFO_DEV(sd, "decoder set video input %d, audio input %d\n", + vid_input, aud_input); if (is_composite) { reg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); @@ -405,8 +490,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, luma > CX18_AV_SVIDEO_LUMA8 || chroma < CX18_AV_SVIDEO_CHROMA4 || chroma > CX18_AV_SVIDEO_CHROMA8) { - CX18_ERR("0x%04x is not a valid video input!\n", - vid_input); + CX18_ERR_DEV(sd, "0x%04x is not a valid video input!\n", + vid_input); return -EINVAL; } reg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4); @@ -431,7 +516,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, case CX18_AV_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; default: - CX18_ERR("0x%04x is not a valid audio input!\n", aud_input); + CX18_ERR_DEV(sd, "0x%04x is not a valid audio input!\n", + aud_input); return -EINVAL; } @@ -461,14 +547,118 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, return 0; } -/* ----------------------------------------------------------------------- */ +static int cx18_av_s_video_routing(struct v4l2_subdev *sd, + const struct v4l2_routing *route) +{ + struct cx18_av_state *state = to_cx18_av_state(sd); + struct cx18 *cx = v4l2_get_subdevdata(sd); + return set_input(cx, route->input, state->aud_input); +} + +static int cx18_av_s_audio_routing(struct v4l2_subdev *sd, + const struct v4l2_routing *route) +{ + struct cx18_av_state *state = to_cx18_av_state(sd); + struct cx18 *cx = v4l2_get_subdevdata(sd); + return set_input(cx, state->vid_input, route->input); +} -static int set_v4lstd(struct cx18 *cx) +static int cx18_av_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) { - struct cx18_av_state *state = &cx->av_state; + struct cx18_av_state *state = to_cx18_av_state(sd); + struct cx18 *cx = v4l2_get_subdevdata(sd); + u8 vpres; + u8 mode; + int val = 0; + + if (state->radio) + return 0; + + vpres = cx18_av_read(cx, 0x40e) & 0x20; + vt->signal = vpres ? 0xffff : 0x0; + + vt->capability |= + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + + mode = cx18_av_read(cx, 0x804); + + /* get rxsubchans and audmode */ + if ((mode & 0xf) == 1) + val |= V4L2_TUNER_SUB_STEREO; + else + val |= V4L2_TUNER_SUB_MONO; + + if (mode == 2 || mode == 4) + val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + + if (mode & 0x10) + val |= V4L2_TUNER_SUB_SAP; + + vt->rxsubchans = val; + vt->audmode = state->audmode; + return 0; +} + +static int cx18_av_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct cx18_av_state *state = to_cx18_av_state(sd); + struct cx18 *cx = v4l2_get_subdevdata(sd); + u8 v; + + if (state->radio) + return 0; + + v = cx18_av_read(cx, 0x809); + v &= ~0xf; + + switch (vt->audmode) { + case V4L2_TUNER_MODE_MONO: + /* mono -> mono + stereo -> mono + bilingual -> lang1 */ + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + /* mono -> mono + stereo -> stereo + bilingual -> lang1 */ + v |= 0x4; + break; + case V4L2_TUNER_MODE_LANG1_LANG2: + /* mono -> mono + stereo -> stereo + bilingual -> lang1/lang2 */ + v |= 0x7; + break; + case V4L2_TUNER_MODE_LANG2: + /* mono -> mono + stereo -> stereo + bilingual -> lang2 */ + v |= 0x1; + break; + default: + return -EINVAL; + } + cx18_av_write_expect(cx, 0x809, v, v, 0xff); + state->audmode = vt->audmode; + return 0; +} + +static int cx18_av_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct cx18_av_state *state = to_cx18_av_state(sd); + struct cx18 *cx = v4l2_get_subdevdata(sd); + u8 fmt = 0; /* zero is autodetect */ u8 pal_m = 0; + if (state->radio == 0 && state->std == norm) + return 0; + + state->radio = 0; + state->std = norm; + /* First tests should be against specific std */ if (state->std == V4L2_STD_NTSC_M_JP) { fmt = 0x2; @@ -493,7 +683,7 @@ static int set_v4lstd(struct cx18 *cx) fmt = 0xc; } - CX18_DEBUG_INFO("changing video std to fmt %i\n", fmt); + CX18_DEBUG_INFO_DEV(sd, "changing video std to fmt %i\n", fmt); /* Follow step 9 of section 3.16 in the cx18_av datasheet. Without this PAL may display a vertical ghosting effect. @@ -511,15 +701,22 @@ static int set_v4lstd(struct cx18 *cx) return 0; } -/* ----------------------------------------------------------------------- */ +static int cx18_av_s_radio(struct v4l2_subdev *sd) +{ + struct cx18_av_state *state = to_cx18_av_state(sd); + state->radio = 1; + return 0; +} -static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) +static int cx18_av_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { + struct cx18 *cx = v4l2_get_subdevdata(sd); + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { - CX18_ERR("invalid brightness setting %d\n", - ctrl->value); + CX18_ERR_DEV(sd, "invalid brightness setting %d\n", + ctrl->value); return -ERANGE; } @@ -528,8 +725,8 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 127) { - CX18_ERR("invalid contrast setting %d\n", - ctrl->value); + CX18_ERR_DEV(sd, "invalid contrast setting %d\n", + ctrl->value); return -ERANGE; } @@ -538,8 +735,8 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 127) { - CX18_ERR("invalid saturation setting %d\n", - ctrl->value); + CX18_ERR_DEV(sd, "invalid saturation setting %d\n", + ctrl->value); return -ERANGE; } @@ -548,8 +745,9 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) break; case V4L2_CID_HUE: - if (ctrl->value < -127 || ctrl->value > 127) { - CX18_ERR("invalid hue setting %d\n", ctrl->value); + if (ctrl->value < -128 || ctrl->value > 127) { + CX18_ERR_DEV(sd, "invalid hue setting %d\n", + ctrl->value); return -ERANGE; } @@ -561,17 +759,18 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: - return cx18_av_audio(cx, VIDIOC_S_CTRL, ctrl); + return cx18_av_audio_s_ctrl(cx, ctrl); default: return -EINVAL; } - return 0; } -static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) +static int cx18_av_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { + struct cx18 *cx = v4l2_get_subdevdata(sd); + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128; @@ -590,31 +789,57 @@ static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: - return cx18_av_audio(cx, VIDIOC_G_CTRL, ctrl); + return cx18_av_audio_g_ctrl(cx, ctrl); default: return -EINVAL; } - return 0; } -/* ----------------------------------------------------------------------- */ - -static int get_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) +static int cx18_av_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { - switch (fmt->type) { - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt); + struct cx18_av_state *state = to_cx18_av_state(sd); + + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); + default: + break; + } + + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, + 65535 / 100, state->default_volume); + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); default: return -EINVAL; } + return -EINVAL; +} - return 0; +static int cx18_av_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + + return cx18_av_vbi_g_fmt(cx, fmt); } -static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) +static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct cx18_av_state *state = &cx->av_state; + struct cx18_av_state *state = to_cx18_av_state(sd); + struct cx18 *cx = v4l2_get_subdevdata(sd); + struct v4l2_pix_format *pix; int HSC, VSC, Vsrc, Hsrc, filter, Vlines; int is_50Hz = !(state->std & V4L2_STD_525_60); @@ -629,12 +854,26 @@ static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4; Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4; - Vlines = pix->height + (is_50Hz ? 4 : 7); - + /* + * This adjustment reflects the excess of vactive, set in + * cx18_av_std_setup(), above standard values: + * + * 480 + 1 for 60 Hz systems + * 576 + 4 for 50 Hz systems + */ + Vlines = pix->height + (is_50Hz ? 4 : 1); + + /* + * Invalid height and width scaling requests are: + * 1. width less than 1/16 of the source width + * 2. width greater than the source width + * 3. height less than 1/8 of the source height + * 4. height greater than the source height + */ if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) || (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { - CX18_ERR("%dx%d is not a valid size!\n", - pix->width, pix->height); + CX18_ERR_DEV(sd, "%dx%d is not a valid size!\n", + pix->width, pix->height); return -ERANGE; } @@ -651,8 +890,9 @@ static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) else filter = 3; - CX18_DEBUG_INFO("decoder set size %dx%d -> scale %ux%u\n", - pix->width, pix->height, HSC, VSC); + CX18_DEBUG_INFO_DEV(sd, + "decoder set size %dx%d -> scale %ux%u\n", + pix->width, pix->height, HSC, VSC); /* HSCALE=HSC */ cx18_av_write(cx, 0x418, HSC & 0xff); @@ -666,231 +906,32 @@ static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt) break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt); + return cx18_av_vbi_s_fmt(cx, fmt); case V4L2_BUF_TYPE_VBI_CAPTURE: - return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt); + return cx18_av_vbi_s_fmt(cx, fmt); default: return -EINVAL; } - return 0; } -/* ----------------------------------------------------------------------- */ - -int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg) +static int cx18_av_s_stream(struct v4l2_subdev *sd, int enable) { - struct cx18_av_state *state = &cx->av_state; - struct v4l2_tuner *vt = arg; - struct v4l2_routing *route = arg; - - /* ignore these commands */ - switch (cmd) { - case TUNER_SET_TYPE_ADDR: - return 0; - } - - if (!state->is_initialized) { - CX18_DEBUG_INFO("cmd %08x triggered fw load\n", cmd); - /* initialize on first use */ - state->is_initialized = 1; - cx18_av_initialize(cx); - } + struct cx18 *cx = v4l2_get_subdevdata(sd); - switch (cmd) { - case VIDIOC_INT_DECODE_VBI_LINE: - return cx18_av_vbi(cx, cmd, arg); - - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return cx18_av_audio(cx, cmd, arg); - - case VIDIOC_STREAMON: - CX18_DEBUG_INFO("enable output\n"); + CX18_DEBUG_INFO_DEV(sd, "%s output\n", enable ? "enable" : "disable"); + if (enable) { cx18_av_write(cx, 0x115, 0x8c); cx18_av_write(cx, 0x116, 0x07); - break; - - case VIDIOC_STREAMOFF: - CX18_DEBUG_INFO("disable output\n"); + } else { cx18_av_write(cx, 0x115, 0x00); cx18_av_write(cx, 0x116, 0x00); - break; - - case VIDIOC_LOG_STATUS: - log_video_status(cx); - log_audio_status(cx); - break; - - case VIDIOC_G_CTRL: - return get_v4lctrl(cx, (struct v4l2_control *)arg); - - case VIDIOC_S_CTRL: - return set_v4lctrl(cx, (struct v4l2_control *)arg); - - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); - default: - break; - } - - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, - 65535 / 100, state->default_volume); - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); - default: - return -EINVAL; - } - return -EINVAL; - } - - case VIDIOC_G_STD: - *(v4l2_std_id *)arg = state->std; - break; - - case VIDIOC_S_STD: - if (state->radio == 0 && state->std == *(v4l2_std_id *)arg) - return 0; - state->radio = 0; - state->std = *(v4l2_std_id *)arg; - return set_v4lstd(cx); - - case AUDC_SET_RADIO: - state->radio = 1; - break; - - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = state->vid_input; - route->output = 0; - break; - - case VIDIOC_INT_S_VIDEO_ROUTING: - return set_input(cx, route->input, state->aud_input); - - case VIDIOC_INT_G_AUDIO_ROUTING: - route->input = state->aud_input; - route->output = 0; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - return set_input(cx, state->vid_input, route->input); - - case VIDIOC_S_FREQUENCY: - input_change(cx); - break; - - case VIDIOC_G_TUNER: - { - u8 vpres = cx18_av_read(cx, 0x40e) & 0x20; - u8 mode; - int val = 0; - - if (state->radio) - break; - - vt->signal = vpres ? 0xffff : 0x0; - - vt->capability |= - V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - - mode = cx18_av_read(cx, 0x804); - - /* get rxsubchans and audmode */ - if ((mode & 0xf) == 1) - val |= V4L2_TUNER_SUB_STEREO; - else - val |= V4L2_TUNER_SUB_MONO; - - if (mode == 2 || mode == 4) - val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - - if (mode & 0x10) - val |= V4L2_TUNER_SUB_SAP; - - vt->rxsubchans = val; - vt->audmode = state->audmode; - break; - } - - case VIDIOC_S_TUNER: - { - u8 v; - - if (state->radio) - break; - - v = cx18_av_read(cx, 0x809); - v &= ~0xf; - - switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - /* mono -> mono - stereo -> mono - bilingual -> lang1 */ - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1: - /* mono -> mono - stereo -> stereo - bilingual -> lang1 */ - v |= 0x4; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - /* mono -> mono - stereo -> stereo - bilingual -> lang1/lang2 */ - v |= 0x7; - break; - case V4L2_TUNER_MODE_LANG2: - /* mono -> mono - stereo -> stereo - bilingual -> lang2 */ - v |= 0x1; - break; - default: - return -EINVAL; - } - cx18_av_write_expect(cx, 0x809, v, v, 0xff); - state->audmode = vt->audmode; - break; } - - case VIDIOC_G_FMT: - return get_v4lfmt(cx, (struct v4l2_format *)arg); - - case VIDIOC_S_FMT: - return set_v4lfmt(cx, (struct v4l2_format *)arg); - - case VIDIOC_INT_RESET: - cx18_av_initialize(cx); - break; - - default: - return -EINVAL; - } - return 0; } -/* ----------------------------------------------------------------------- */ - -/* ----------------------------------------------------------------------- */ - static void log_video_status(struct cx18 *cx) { static const char *const fmt_strs[] = { @@ -903,36 +944,40 @@ static void log_video_status(struct cx18 *cx) }; struct cx18_av_state *state = &cx->av_state; + struct v4l2_subdev *sd = &state->sd; u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf; u8 gen_stat1 = cx18_av_read(cx, 0x40d); u8 gen_stat2 = cx18_av_read(cx, 0x40e); int vid_input = state->vid_input; - CX18_INFO("Video signal: %spresent\n", - (gen_stat2 & 0x20) ? "" : "not "); - CX18_INFO("Detected format: %s\n", - fmt_strs[gen_stat1 & 0xf]); + CX18_INFO_DEV(sd, "Video signal: %spresent\n", + (gen_stat2 & 0x20) ? "" : "not "); + CX18_INFO_DEV(sd, "Detected format: %s\n", + fmt_strs[gen_stat1 & 0xf]); - CX18_INFO("Specified standard: %s\n", - vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); + CX18_INFO_DEV(sd, "Specified standard: %s\n", + vidfmt_sel ? fmt_strs[vidfmt_sel] + : "automatic detection"); if (vid_input >= CX18_AV_COMPOSITE1 && vid_input <= CX18_AV_COMPOSITE8) { - CX18_INFO("Specified video input: Composite %d\n", - vid_input - CX18_AV_COMPOSITE1 + 1); + CX18_INFO_DEV(sd, "Specified video input: Composite %d\n", + vid_input - CX18_AV_COMPOSITE1 + 1); } else { - CX18_INFO("Specified video input: S-Video (Luma In%d, Chroma In%d)\n", - (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); + CX18_INFO_DEV(sd, "Specified video input: " + "S-Video (Luma In%d, Chroma In%d)\n", + (vid_input & 0xf0) >> 4, + (vid_input & 0xf00) >> 8); } - CX18_INFO("Specified audioclock freq: %d Hz\n", state->audclk_freq); + CX18_INFO_DEV(sd, "Specified audioclock freq: %d Hz\n", + state->audclk_freq); } -/* ----------------------------------------------------------------------- */ - static void log_audio_status(struct cx18 *cx) { struct cx18_av_state *state = &cx->av_state; + struct v4l2_subdev *sd = &state->sd; u8 download_ctl = cx18_av_read(cx, 0x803); u8 mod_det_stat0 = cx18_av_read(cx, 0x804); u8 mod_det_stat1 = cx18_av_read(cx, 0x805); @@ -955,7 +1000,7 @@ static void log_audio_status(struct cx18 *cx) case 0xfe: p = "forced mode"; break; default: p = "not defined"; break; } - CX18_INFO("Detected audio mode: %s\n", p); + CX18_INFO_DEV(sd, "Detected audio mode: %s\n", p); switch (mod_det_stat1) { case 0x00: p = "not defined"; break; @@ -980,11 +1025,11 @@ static void log_audio_status(struct cx18 *cx) case 0xff: p = "no detected audio standard"; break; default: p = "not defined"; break; } - CX18_INFO("Detected audio standard: %s\n", p); - CX18_INFO("Audio muted: %s\n", - (mute_ctl & 0x2) ? "yes" : "no"); - CX18_INFO("Audio microcontroller: %s\n", - (download_ctl & 0x10) ? "running" : "stopped"); + CX18_INFO_DEV(sd, "Detected audio standard: %s\n", p); + CX18_INFO_DEV(sd, "Audio muted: %s\n", + (mute_ctl & 0x2) ? "yes" : "no"); + CX18_INFO_DEV(sd, "Audio microcontroller: %s\n", + (download_ctl & 0x10) ? "running" : "stopped"); switch (audio_config >> 4) { case 0x00: p = "undefined"; break; @@ -1005,7 +1050,7 @@ static void log_audio_status(struct cx18 *cx) case 0x0f: p = "automatic detection"; break; default: p = "undefined"; break; } - CX18_INFO("Configured audio standard: %s\n", p); + CX18_INFO_DEV(sd, "Configured audio standard: %s\n", p); if ((audio_config >> 4) < 0xF) { switch (audio_config & 0xF) { @@ -1019,7 +1064,7 @@ static void log_audio_status(struct cx18 *cx) case 0x07: p = "DUAL3 (AB)"; break; default: p = "undefined"; } - CX18_INFO("Configured audio mode: %s\n", p); + CX18_INFO_DEV(sd, "Configured audio mode: %s\n", p); } else { switch (audio_config & 0xF) { case 0x00: p = "BG"; break; @@ -1037,14 +1082,14 @@ static void log_audio_status(struct cx18 *cx) case 0x0f: p = "automatic standard and mode detection"; break; default: p = "undefined"; break; } - CX18_INFO("Configured audio system: %s\n", p); + CX18_INFO_DEV(sd, "Configured audio system: %s\n", p); } if (aud_input) - CX18_INFO("Specified audio input: Tuner (In%d)\n", - aud_input); + CX18_INFO_DEV(sd, "Specified audio input: Tuner (In%d)\n", + aud_input); else - CX18_INFO("Specified audio input: External\n"); + CX18_INFO_DEV(sd, "Specified audio input: External\n"); switch (pref_mode & 0xf) { case 0: p = "mono/language A"; break; @@ -1057,14 +1102,14 @@ static void log_audio_status(struct cx18 *cx) case 7: p = "language AB"; break; default: p = "undefined"; break; } - CX18_INFO("Preferred audio mode: %s\n", p); + CX18_INFO_DEV(sd, "Preferred audio mode: %s\n", p); if ((audio_config & 0xf) == 0xf) { switch ((afc0 >> 3) & 0x1) { case 0: p = "system DK"; break; case 1: p = "system L"; break; } - CX18_INFO("Selected 65 MHz format: %s\n", p); + CX18_INFO_DEV(sd, "Selected 65 MHz format: %s\n", p); switch (afc0 & 0x7) { case 0: p = "Chroma"; break; @@ -1074,6 +1119,131 @@ static void log_audio_status(struct cx18 *cx) case 4: p = "autodetect"; break; default: p = "undefined"; break; } - CX18_INFO("Selected 45 MHz format: %s\n", p); + CX18_INFO_DEV(sd, "Selected 45 MHz format: %s\n", p); } } + +static int cx18_av_log_status(struct v4l2_subdev *sd) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + log_video_status(cx); + log_audio_status(cx); + return 0; +} + +static inline int cx18_av_dbg_match(const struct v4l2_dbg_match *match) +{ + return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 1; +} + +static int cx18_av_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct cx18_av_state *state = to_cx18_av_state(sd); + + if (cx18_av_dbg_match(&chip->match)) { + chip->ident = state->id; + chip->revision = state->rev; + } + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cx18_av_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + + if (!cx18_av_dbg_match(®->match)) + return -EINVAL; + if ((reg->reg & 0x3) != 0) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->size = 4; + reg->val = cx18_av_read4(cx, reg->reg & 0x00000ffc); + return 0; +} + +static int cx18_av_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + + if (!cx18_av_dbg_match(®->match)) + return -EINVAL; + if ((reg->reg & 0x3) != 0) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx18_av_write4(cx, reg->reg & 0x00000ffc, reg->val); + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops cx18_av_general_ops = { + .g_chip_ident = cx18_av_g_chip_ident, + .log_status = cx18_av_log_status, + .init = cx18_av_init, + .reset = cx18_av_reset, + .queryctrl = cx18_av_queryctrl, + .g_ctrl = cx18_av_g_ctrl, + .s_ctrl = cx18_av_s_ctrl, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cx18_av_g_register, + .s_register = cx18_av_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops cx18_av_tuner_ops = { + .s_radio = cx18_av_s_radio, + .s_frequency = cx18_av_s_frequency, + .g_tuner = cx18_av_g_tuner, + .s_tuner = cx18_av_s_tuner, + .s_std = cx18_av_s_std, +}; + +static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = { + .s_clock_freq = cx18_av_s_clock_freq, + .s_routing = cx18_av_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops cx18_av_video_ops = { + .s_routing = cx18_av_s_video_routing, + .decode_vbi_line = cx18_av_decode_vbi_line, + .s_stream = cx18_av_s_stream, + .g_fmt = cx18_av_g_fmt, + .s_fmt = cx18_av_s_fmt, +}; + +static const struct v4l2_subdev_ops cx18_av_ops = { + .core = &cx18_av_general_ops, + .tuner = &cx18_av_tuner_ops, + .audio = &cx18_av_audio_ops, + .video = &cx18_av_video_ops, +}; + +int cx18_av_probe(struct cx18 *cx) +{ + struct cx18_av_state *state = &cx->av_state; + struct v4l2_subdev *sd; + + state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff; + state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO) + ? V4L2_IDENT_CX23418_843 : V4L2_IDENT_UNKNOWN; + + state->vid_input = CX18_AV_COMPOSITE7; + state->aud_input = CX18_AV_AUDIO8; + state->audclk_freq = 48000; + state->audmode = V4L2_TUNER_MODE_LANG1; + state->slicer_line_delay = 0; + state->slicer_line_offset = (10 + state->slicer_line_delay - 2); + + sd = &state->sd; + v4l2_subdev_init(sd, &cx18_av_ops); + v4l2_set_subdevdata(sd, cx); + snprintf(sd->name, sizeof(sd->name), + "%s %03x", cx->v4l2_dev.name, (state->rev >> 4)); + sd->grp_id = CX18_HW_418_AV; + return v4l2_device_register_subdev(&cx->v4l2_dev, sd); +} diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h index cf68a60..c458120 100644 --- a/drivers/media/video/cx18/cx18-av-core.h +++ b/drivers/media/video/cx18/cx18-av-core.h @@ -25,6 +25,8 @@ #ifndef _CX18_AV_CORE_H_ #define _CX18_AV_CORE_H_ +#include <media/v4l2-device.h> + struct cx18; enum cx18_av_video_input { @@ -73,17 +75,40 @@ enum cx18_av_audio_input { }; struct cx18_av_state { + struct v4l2_subdev sd; int radio; v4l2_std_id std; enum cx18_av_video_input vid_input; enum cx18_av_audio_input aud_input; u32 audclk_freq; int audmode; - int vbi_line_offset; int default_volume; u32 id; u32 rev; int is_initialized; + + /* + * The VBI slicer starts operating and counting lines, begining at + * slicer line count of 1, at D lines after the deassertion of VRESET. + * This staring field line, S, is 6 (& 319) or 10 (& 273) for 625 or 525 + * line systems respectively. Sliced ancillary data captured on VBI + * slicer line M is inserted after the VBI slicer is done with line M, + * when VBI slicer line count is N = M+1. Thus when the VBI slicer + * reports a VBI slicer line number with ancillary data, the IDID0 byte + * indicates VBI slicer line N. The actual field line that the captured + * data comes from is + * + * L = M+(S+D-1) = N-1+(S+D-1) = N + (S+D-2). + * + * L is the line in the field, not frame, from which the VBI data came. + * N is the line reported by the slicer in the ancillary data. + * D is the slicer_line_delay value programmed into register 0x47f. + * S is 6 for 625 line systems or 10 for 525 line systems + * (S+D-2) is the slicer_line_offset used to convert slicer reported + * line counts to actual field lines. + */ + int slicer_line_delay; + int slicer_line_offset; }; @@ -298,6 +323,16 @@ struct cx18_av_state { #define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */ #define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */ +static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cx18_av_state, sd); +} + +enum cx18_av_subdev_init_arg { + CX18_AV_INIT_NORMAL = 0, + CX18_AV_INIT_PLLS = 1, +}; + /* ----------------------------------------------------------------------- */ /* cx18_av-core.c */ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); @@ -310,20 +345,26 @@ u8 cx18_av_read(struct cx18 *cx, u16 addr); u32 cx18_av_read4(struct cx18 *cx, u16 addr); int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value); int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value); -int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg); void cx18_av_std_setup(struct cx18 *cx); +int cx18_av_probe(struct cx18 *cx); + /* ----------------------------------------------------------------------- */ /* cx18_av-firmware.c */ int cx18_av_loadfw(struct cx18 *cx); /* ----------------------------------------------------------------------- */ /* cx18_av-audio.c */ -int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg); +int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl); +int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl); +int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq); void cx18_av_audio_set_path(struct cx18 *cx); /* ----------------------------------------------------------------------- */ /* cx18_av-vbi.c */ -int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg); +int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, + struct v4l2_decode_vbi_line *vbi); +int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt); +int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt); #endif diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c index c64fd0a..49a55cc8 100644 --- a/drivers/media/video/cx18/cx18-av-firmware.c +++ b/drivers/media/video/cx18/cx18-av-firmware.c @@ -29,6 +29,7 @@ int cx18_av_loadfw(struct cx18 *cx) { + struct v4l2_subdev *sd = &cx->av_state.sd; const struct firmware *fw = NULL; u32 size; u32 v; @@ -36,8 +37,8 @@ int cx18_av_loadfw(struct cx18 *cx) int i; int retries1 = 0; - if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) { - CX18_ERR("unable to open firmware %s\n", FWFILE); + if (request_firmware(&fw, FWFILE, &cx->pci_dev->dev) != 0) { + CX18_ERR_DEV(sd, "unable to open firmware %s\n", FWFILE); return -EINVAL; } @@ -88,7 +89,7 @@ int cx18_av_loadfw(struct cx18 *cx) retries1++; } if (retries1 >= 5) { - CX18_ERR("unable to load firmware %s\n", FWFILE); + CX18_ERR_DEV(sd, "unable to load firmware %s\n", FWFILE); release_firmware(fw); return -EIO; } @@ -115,9 +116,9 @@ int cx18_av_loadfw(struct cx18 *cx) are generated) */ cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0); - /* set alt I2s master clock to /16 and enable alt divider i2s + /* set alt I2s master clock to /0x16 and enable alt divider i2s passthrough */ - cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687); + cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5600B687); cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6, 0x3F00FFFF); @@ -131,7 +132,8 @@ int cx18_av_loadfw(struct cx18 *cx) v = cx18_read_reg(cx, CX18_AUDIO_ENABLE); /* If bit 11 is 1, clear bit 10 */ if (v & 0x800) - cx18_write_reg(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); + cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE, + 0, 0x400); /* Enable WW auto audio standard detection */ v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); @@ -142,6 +144,6 @@ int cx18_av_loadfw(struct cx18 *cx) release_firmware(fw); - CX18_INFO("loaded %s firmware (%d bytes)\n", FWFILE, size); + CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size); return 0; } diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c index 1527ea4..23b3167 100644 --- a/drivers/media/video/cx18/cx18-av-vbi.c +++ b/drivers/media/video/cx18/cx18-av-vbi.c @@ -24,6 +24,52 @@ #include "cx18-driver.h" +/* + * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode, + * NN counts 1 byte Dwords, an IDID with the VBI line # in it. + * Thus, according to the VIP-2 Spec, our VBI ancillary data lines + * (should!) look like: + * 4 byte EAV code: 0xff 0x00 0x00 0xRP + * unknown number of possible idle bytes + * 3 byte Anc data preamble: 0x00 0xff 0xff + * 1 byte data identifier: ne010iii (parity bits, 010, DID bits) + * 1 byte secondary data id: nessssss (parity bits, SDID bits) + * 1 byte data word count: necccccc (parity bits, NN Dword count) + * 2 byte Internal DID: VBI-line-# 0x80 + * NN data bytes + * 1 byte checksum + * Fill bytes needed to fil out to 4*NN bytes of payload + * + * The RP codes for EAVs when in VIP-1.1 mode, not in raw mode, & + * in the vertical blanking interval are: + * 0xb0 (Task 0 VerticalBlank HorizontalBlank 0 0 0 0) + * 0xf0 (Task EvenField VerticalBlank HorizontalBlank 0 0 0 0) + * + * Since the V bit is only allowed to toggle in the EAV RP code, just + * before the first active region line and for active lines, they are: + * 0x90 (Task 0 0 HorizontalBlank 0 0 0 0) + * 0xd0 (Task EvenField 0 HorizontalBlank 0 0 0 0) + * + * The user application DID bytes we care about are: + * 0x91 (1 0 010 0 !ActiveLine AncDataPresent) + * 0x55 (0 1 010 2ndField !ActiveLine AncDataPresent) + * + */ +static const u8 sliced_vbi_did[2] = { 0x91, 0x55 }; + +struct vbi_anc_data { + /* u8 eav[4]; */ + /* u8 idle[]; Variable number of idle bytes */ + u8 preamble[3]; + u8 did; + u8 sdid; + u8 data_count; + u8 idid[2]; + u8 payload[1]; /* data_count of payload */ + /* u8 checksum; */ + /* u8 fill[]; Variable number of fill bytes */ +}; + static int odd_parity(u8 c) { c ^= (c >> 4); @@ -83,188 +129,189 @@ static int decode_vps(u8 *dst, u8 *p) return err & 0xf0; } -int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) +int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt) { struct cx18_av_state *state = &cx->av_state; - struct v4l2_format *fmt; struct v4l2_sliced_vbi_format *svbi; + static const u16 lcr2vbi[] = { + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ + 0, V4L2_SLICED_WSS_625, 0, /* 4 */ + V4L2_SLICED_CAPTION_525, /* 6 */ + 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ + 0, 0, 0, 0 + }; + int is_pal = !(state->std & V4L2_STD_525_60); + int i; - switch (cmd) { - case VIDIOC_G_FMT: - { - static u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ - 0, V4L2_SLICED_WSS_625, 0, /* 4 */ - V4L2_SLICED_CAPTION_525, /* 6 */ - 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ - 0, 0, 0, 0 - }; - int is_pal = !(state->std & V4L2_STD_525_60); - int i; - - fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - memset(svbi, 0, sizeof(*svbi)); - /* we're done if raw VBI is active */ - if ((cx18_av_read(cx, 0x404) & 0x10) == 0) - break; - - if (is_pal) { - for (i = 7; i <= 23; i++) { - u8 v = cx18_av_read(cx, 0x424 + i - 7); - - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= svbi->service_lines[0][i] | - svbi->service_lines[1][i]; - } - } else { - for (i = 10; i <= 21; i++) { - u8 v = cx18_av_read(cx, 0x424 + i - 10); - - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= svbi->service_lines[0][i] | - svbi->service_lines[1][i]; - } - } - break; - } + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + memset(svbi, 0, sizeof(*svbi)); + /* we're done if raw VBI is active */ + if ((cx18_av_read(cx, 0x404) & 0x10) == 0) + return 0; - case VIDIOC_S_FMT: - { - int is_pal = !(state->std & V4L2_STD_525_60); - int vbi_offset = is_pal ? 1 : 0; - int i, x; - u8 lcr[24]; - - fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && - fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* raw VBI */ - memset(svbi, 0, sizeof(*svbi)); - - /* Setup standard */ - cx18_av_std_setup(cx); - - /* VBI Offset */ - cx18_av_write(cx, 0x47f, vbi_offset); - cx18_av_write(cx, 0x404, 0x2e); - break; + if (is_pal) { + for (i = 7; i <= 23; i++) { + u8 v = cx18_av_read(cx, 0x424 + i - 7); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; + } + } else { + for (i = 10; i <= 21; i++) { + u8 v = cx18_av_read(cx, 0x424 + i - 10); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; } + } + return 0; +} - for (x = 0; x <= 23; x++) - lcr[x] = 0x00; +int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt) +{ + struct cx18_av_state *state = &cx->av_state; + struct v4l2_sliced_vbi_format *svbi; + int is_pal = !(state->std & V4L2_STD_525_60); + int i, x; + u8 lcr[24]; + + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && + fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + /* raw VBI */ + memset(svbi, 0, sizeof(*svbi)); /* Setup standard */ cx18_av_std_setup(cx); - /* Sliced VBI */ - cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */ - cx18_av_write(cx, 0x406, 0x13); - cx18_av_write(cx, 0x47f, vbi_offset); - - if (is_pal) { - for (i = 0; i <= 6; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } else { - for (i = 0; i <= 9; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - - for (i = 22; i <= 23; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } + /* VBI Offset */ + cx18_av_write(cx, 0x47f, state->slicer_line_delay); + cx18_av_write(cx, 0x404, 0x2e); + return 0; + } - for (i = 7; i <= 23; i++) { - for (x = 0; x <= 1; x++) { - switch (svbi->service_lines[1-x][i]) { - case V4L2_SLICED_TELETEXT_B: - lcr[i] |= 1 << (4 * x); - break; - case V4L2_SLICED_WSS_625: - lcr[i] |= 4 << (4 * x); - break; - case V4L2_SLICED_CAPTION_525: - lcr[i] |= 6 << (4 * x); - break; - case V4L2_SLICED_VPS: - lcr[i] |= 9 << (4 * x); - break; - } - } - } + for (x = 0; x <= 23; x++) + lcr[x] = 0x00; + + /* Setup standard */ + cx18_av_std_setup(cx); + + /* Sliced VBI */ + cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */ + cx18_av_write(cx, 0x406, 0x13); + cx18_av_write(cx, 0x47f, state->slicer_line_delay); + + /* Force impossible lines to 0 */ + if (is_pal) { + for (i = 0; i <= 6; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } else { + for (i = 0; i <= 9; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + + for (i = 22; i <= 23; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } - if (is_pal) { - for (x = 1, i = 0x424; i <= 0x434; i++, x++) - cx18_av_write(cx, i, lcr[6 + x]); - } else { - for (x = 1, i = 0x424; i <= 0x430; i++, x++) - cx18_av_write(cx, i, lcr[9 + x]); - for (i = 0x431; i <= 0x434; i++) - cx18_av_write(cx, i, 0); + /* Build register values for requested service lines */ + for (i = 7; i <= 23; i++) { + for (x = 0; x <= 1; x++) { + switch (svbi->service_lines[1-x][i]) { + case V4L2_SLICED_TELETEXT_B: + lcr[i] |= 1 << (4 * x); + break; + case V4L2_SLICED_WSS_625: + lcr[i] |= 4 << (4 * x); + break; + case V4L2_SLICED_CAPTION_525: + lcr[i] |= 6 << (4 * x); + break; + case V4L2_SLICED_VPS: + lcr[i] |= 9 << (4 * x); + break; + } } + } - cx18_av_write(cx, 0x43c, 0x16); - cx18_av_write(cx, 0x474, is_pal ? 0x2a : 0x22); - break; + if (is_pal) { + for (x = 1, i = 0x424; i <= 0x434; i++, x++) + cx18_av_write(cx, i, lcr[6 + x]); + } else { + for (x = 1, i = 0x424; i <= 0x430; i++, x++) + cx18_av_write(cx, i, lcr[9 + x]); + for (i = 0x431; i <= 0x434; i++) + cx18_av_write(cx, i, 0); } - case VIDIOC_INT_DECODE_VBI_LINE: - { - struct v4l2_decode_vbi_line *vbi = arg; - u8 *p = vbi->p; - int id1, id2, l, err = 0; + cx18_av_write(cx, 0x43c, 0x16); + /* FIXME - should match vblank set in cx18_av_std_setup() */ + cx18_av_write(cx, 0x474, is_pal ? 0x2a : 26); + return 0; +} + +int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, + struct v4l2_decode_vbi_line *vbi) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + struct cx18_av_state *state = &cx->av_state; + struct vbi_anc_data *anc = (struct vbi_anc_data *)vbi->p; + u8 *p; + int did, sdid, l, err = 0; + + /* + * Check for the ancillary data header for sliced VBI + */ + if (anc->preamble[0] || + anc->preamble[1] != 0xff || anc->preamble[2] != 0xff || + (anc->did != sliced_vbi_did[0] && + anc->did != sliced_vbi_did[1])) { + vbi->line = vbi->type = 0; + return 0; + } - if (p[0] || p[1] != 0xff || p[2] != 0xff || - (p[3] != 0x55 && p[3] != 0x91)) { - vbi->line = vbi->type = 0; - break; - } + did = anc->did; + sdid = anc->sdid & 0xf; + l = anc->idid[0] & 0x3f; + l += state->slicer_line_offset; + p = anc->payload; - p += 4; - id1 = p[-1]; - id2 = p[0] & 0xf; - l = p[2] & 0x3f; - l += state->vbi_line_offset; - p += 4; - - switch (id2) { - case 1: - id2 = V4L2_SLICED_TELETEXT_B; - break; - case 4: - id2 = V4L2_SLICED_WSS_625; - break; - case 6: - id2 = V4L2_SLICED_CAPTION_525; - err = !odd_parity(p[0]) || !odd_parity(p[1]); - break; - case 9: - id2 = V4L2_SLICED_VPS; - if (decode_vps(p, p) != 0) - err = 1; - break; - default: - id2 = 0; + /* Decode the SDID set by the slicer */ + switch (sdid) { + case 1: + sdid = V4L2_SLICED_TELETEXT_B; + break; + case 4: + sdid = V4L2_SLICED_WSS_625; + break; + case 6: + sdid = V4L2_SLICED_CAPTION_525; + err = !odd_parity(p[0]) || !odd_parity(p[1]); + break; + case 9: + sdid = V4L2_SLICED_VPS; + if (decode_vps(p, p) != 0) err = 1; - break; - } - - vbi->type = err ? 0 : id2; - vbi->line = err ? 0 : l; - vbi->is_second_field = err ? 0 : (id1 == 0x55); - vbi->p = p; break; - } + default: + sdid = 0; + err = 1; + break; } + vbi->type = err ? 0 : sdid; + vbi->line = err ? 0 : l; + vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]); + vbi->p = p; return 0; } diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index e274043..9bc2218 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -51,12 +51,12 @@ static struct cx18_card_tuner_i2c cx18_i2c_std = { static const struct cx18_card cx18_card_hvr1600_esmt = { .type = CX18_CARD_HVR_1600_ESMT, .name = "Hauppauge HVR-1600", - .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", + .comment = "Simultaneous Digital and Analog TV capture supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, - .hw_audio_ctrl = CX18_HW_CX23418, + .hw_audio_ctrl = CX18_HW_418_AV, .hw_muxer = CX18_HW_CS5345, - .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | - CX18_HW_CS5345 | CX18_HW_DVB, + .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | + CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, @@ -97,12 +97,12 @@ static const struct cx18_card cx18_card_hvr1600_esmt = { static const struct cx18_card cx18_card_hvr1600_samsung = { .type = CX18_CARD_HVR_1600_SAMSUNG, .name = "Hauppauge HVR-1600 (Preproduction)", - .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", + .comment = "Simultaneous Digital and Analog TV capture supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, - .hw_audio_ctrl = CX18_HW_CX23418, + .hw_audio_ctrl = CX18_HW_418_AV, .hw_muxer = CX18_HW_CS5345, - .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | - CX18_HW_CS5345 | CX18_HW_DVB, + .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | + CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, @@ -152,10 +152,10 @@ static const struct cx18_card_pci_info cx18_pci_h900[] = { static const struct cx18_card cx18_card_h900 = { .type = CX18_CARD_COMPRO_H900, .name = "Compro VideoMate H900", - .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", + .comment = "Analog TV capture supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, - .hw_audio_ctrl = CX18_HW_CX23418, - .hw_all = CX18_HW_TUNER, + .hw_audio_ctrl = CX18_HW_418_AV, + .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, { CX18_CARD_INPUT_SVIDEO1, 1, @@ -201,8 +201,8 @@ static const struct cx18_card cx18_card_mpc718 = { .name = "Yuan MPC718", .comment = "Analog video capture works; some audio line in may not.\n", .v4l2_capabilities = CX18_CAP_ENCODER, - .hw_audio_ctrl = CX18_HW_CX23418, - .hw_all = CX18_HW_TUNER, + .hw_audio_ctrl = CX18_HW_418_AV, + .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, { CX18_CARD_INPUT_SVIDEO1, 1, @@ -249,11 +249,11 @@ static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = { static const struct cx18_card cx18_card_cnxt_raptor_pal = { .type = CX18_CARD_CNXT_RAPTOR_PAL, .name = "Conexant Raptor PAL/SECAM", - .comment = "Raw VBI supported; Sliced VBI is not yet supported\n", + .comment = "Analog TV capture supported\n", .v4l2_capabilities = CX18_CAP_ENCODER, - .hw_audio_ctrl = CX18_HW_CX23418, - .hw_muxer = CX18_HW_GPIO, - .hw_all = CX18_HW_TUNER | CX18_HW_GPIO, + .hw_audio_ctrl = CX18_HW_418_AV, + .hw_muxer = CX18_HW_GPIO_MUX, + .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, { CX18_CARD_INPUT_SVIDEO1, 1, @@ -306,8 +306,8 @@ static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = { .comment = "Experimenters and photos needed for device to work well.\n" "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", .v4l2_capabilities = CX18_CAP_ENCODER, - .hw_audio_ctrl = CX18_HW_CX23418, - .hw_all = CX18_HW_TUNER, + .hw_audio_ctrl = CX18_HW_418_AV, + .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE6 }, { CX18_CARD_INPUT_SVIDEO1, 1, @@ -339,19 +339,21 @@ static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = { /* Leadtek WinFast PVR2100 */ static const struct cx18_card_pci_info cx18_pci_leadtek_pvr2100[] = { - { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 }, + { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 }, /* PVR2100 */ + { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6690 }, /* DVR3100 H */ { 0, 0, 0 } }; static const struct cx18_card cx18_card_leadtek_pvr2100 = { .type = CX18_CARD_LEADTEK_PVR2100, - .name = "Leadtek WinFast PVR2100", + .name = "Leadtek WinFast PVR2100/DVR3100 H", .comment = "Experimenters and photos needed for device to work well.\n" "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", .v4l2_capabilities = CX18_CAP_ENCODER, - .hw_audio_ctrl = CX18_HW_CX23418, - .hw_muxer = CX18_HW_GPIO, - .hw_all = CX18_HW_TUNER | CX18_HW_GPIO, + .hw_audio_ctrl = CX18_HW_418_AV, + .hw_muxer = CX18_HW_GPIO_MUX, + .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX | + CX18_HW_GPIO_RESET_CTRL, .video_inputs = { { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, { CX18_CARD_INPUT_SVIDEO1, 1, diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index 6fa7bcb..3c552b6 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -22,12 +22,13 @@ */ /* hardware flags */ -#define CX18_HW_TUNER (1 << 0) -#define CX18_HW_TVEEPROM (1 << 1) -#define CX18_HW_CS5345 (1 << 2) -#define CX18_HW_GPIO (1 << 3) -#define CX18_HW_CX23418 (1 << 4) -#define CX18_HW_DVB (1 << 5) +#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) /* video inputs */ #define CX18_CARD_INPUT_VID_TUNER 1 @@ -49,8 +50,7 @@ /* V4L2 capability aliases */ #define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \ - V4L2_CAP_VBI_CAPTURE) -/* | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */ + V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) struct cx18_card_video_input { u8 video_type; /* video input type */ @@ -122,7 +122,7 @@ struct cx18_card { char *comment; u32 v4l2_capabilities; u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only - 1 dev allowed) */ + 1 dev allowed currently) */ u32 hw_muxer; /* hardware used to multiplex audio input */ u32 hw_all; /* all hardware used by the board */ struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS]; diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index 17edf30..82fc2f9 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c @@ -22,14 +22,13 @@ */ #include "cx18-driver.h" -#include "cx18-av-core.h" #include "cx18-cards.h" #include "cx18-ioctl.h" #include "cx18-audio.h" -#include "cx18-i2c.h" #include "cx18-mailbox.h" #include "cx18-controls.h" +/* Must be sorted from low to high control ID! */ static const u32 user_ctrls[] = { V4L2_CID_USER_CLASS, V4L2_CID_BRIGHTNESS, @@ -66,7 +65,7 @@ int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - if (cx18_av_cmd(cx, VIDIOC_QUERYCTRL, qctrl)) + if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl)) qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0; @@ -76,7 +75,7 @@ int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - if (cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl)) + if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl)) qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0; @@ -125,7 +124,7 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - return cx18_av_cmd(cx, VIDIOC_S_CTRL, vctrl); + return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl); case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_MUTE: @@ -133,7 +132,7 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl); + return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl); default: CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); @@ -150,7 +149,7 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - return cx18_av_cmd(cx, VIDIOC_G_CTRL, vctrl); + return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl); case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_MUTE: @@ -158,7 +157,8 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl); + return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl); + default: CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); return -EINVAL; @@ -166,38 +166,57 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl) return 0; } -static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt) +static int cx18_setup_vbi_fmt(struct cx18 *cx, + enum v4l2_mpeg_stream_vbi_fmt fmt, + enum v4l2_mpeg_stream_type type) { if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE)) return -EINVAL; if (atomic_read(&cx->ana_capturing) > 0) return -EBUSY; - /* First try to allocate sliced VBI buffers if needed. */ - if (fmt && cx->vbi.sliced_mpeg_data[0] == NULL) { + if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV || + type != V4L2_MPEG_STREAM_TYPE_MPEG2_PS) { + /* We don't do VBI insertion aside from IVTV format in a PS */ + cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE; + CX18_DEBUG_INFO("disabled insertion of sliced VBI data into " + "the MPEG stream\n"); + return 0; + } + + /* Allocate sliced VBI buffers if needed. */ + if (cx->vbi.sliced_mpeg_data[0] == NULL) { int i; for (i = 0; i < CX18_VBI_FRAMES; i++) { - /* Yuck, hardcoded. Needs to be a define */ - cx->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL); + cx->vbi.sliced_mpeg_data[i] = + kmalloc(CX18_SLICED_MPEG_DATA_BUFSZ, GFP_KERNEL); if (cx->vbi.sliced_mpeg_data[i] == NULL) { while (--i >= 0) { kfree(cx->vbi.sliced_mpeg_data[i]); cx->vbi.sliced_mpeg_data[i] = NULL; } + cx->vbi.insert_mpeg = + V4L2_MPEG_STREAM_VBI_FMT_NONE; + CX18_WARN("Unable to allocate buffers for " + "sliced VBI data insertion\n"); return -ENOMEM; } } } cx->vbi.insert_mpeg = fmt; + CX18_DEBUG_INFO("enabled insertion of sliced VBI data into the MPEG PS," + "when sliced VBI is enabled\n"); - if (cx->vbi.insert_mpeg == 0) - return 0; - /* Need sliced data for mpeg insertion */ + /* + * If our current settings have no lines set for capture, store a valid, + * default set of service lines to capture, in our current settings. + */ if (cx18_get_service_set(cx->vbi.sliced_in) == 0) { if (cx->is_60hz) - cx->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525; + cx->vbi.sliced_in->service_set = + V4L2_SLICED_CAPTION_525; else cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz); @@ -259,10 +278,12 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) return err; } if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + static u32 freqs[3] = { 44100, 48000, 32000 }; struct cx18_api_func_private priv; struct cx2341x_mpeg_params p = cx->params; int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing), c, VIDIOC_S_EXT_CTRLS); + unsigned int idx; if (err) return err; @@ -277,16 +298,23 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) fmt.fmt.pix.width = cx->params.width / (is_mpeg1 ? 2 : 1); fmt.fmt.pix.height = cx->params.height; - cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt); + v4l2_subdev_call(cx->sd_av, video, s_fmt, &fmt); } priv.cx = cx; priv.s = &cx->streams[id->type]; err = cx2341x_update(&priv, cx18_api_func, &cx->params, &p); - if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt) - err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt); + if (!err && + (cx->params.stream_vbi_fmt != p.stream_vbi_fmt || + cx->params.stream_type != p.stream_type)) + err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt, + p.stream_type); cx->params = p; cx->dualwatch_stereo_mode = p.audio_properties & 0x0300; - cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03); + idx = p.audio_properties & 0x03; + /* The audio clock of the digitizer must match the codec sample + rate otherwise you get some very strange effects. */ + if (idx < sizeof(freqs)) + cx18_call_all(cx, audio, s_clock_freq, freqs[idx]); return err; } return -EINVAL; diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index f50cf21..210c68a 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -39,10 +39,6 @@ #include <media/tveeprom.h> - -/* var to keep track of the number of array elements in use */ -int cx18_cards_active; - /* If you have already X v4l cards, then set this to X. This way the device numbers stay matched. Example: you have a WinTV card without radio and a Compro H900 with. Normally this would give a @@ -50,12 +46,6 @@ int cx18_cards_active; setting this to 1 you ensure that radio0 is now also radio1. */ int cx18_first_minor; -/* Master variable for all cx18 info */ -struct cx18 *cx18_cards[CX18_MAX_CARDS]; - -/* Protects cx18_cards_active */ -DEFINE_SPINLOCK(cx18_cards_lock); - /* add your revision and whatnot here */ static struct pci_device_id cx18_pci_tbl[] __devinitdata = { {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418, @@ -65,6 +55,8 @@ static struct pci_device_id cx18_pci_tbl[] __devinitdata = { MODULE_DEVICE_TABLE(pci, cx18_pci_tbl); +static atomic_t cx18_instance = ATOMIC_INIT(0); + /* Parameter declarations */ static int cardtype[CX18_MAX_CARDS]; static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, @@ -159,7 +151,7 @@ MODULE_PARM_DESC(cardtype, "\t\t\t 4 = Yuan MPC718\n" "\t\t\t 5 = Conexant Raptor PAL/SECAM\n" "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n" - "\t\t\t 7 = Leadtek WinFast PVR2100\n" + "\t\t\t 7 = Leadtek WinFast PVR2100/DVR3100 H\n" "\t\t\t 0 = Autodetect (default)\n" "\t\t\t-1 = Ignore this card\n\t\t"); MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); @@ -277,11 +269,16 @@ static void cx18_iounmap(struct cx18 *cx) /* Hauppauge card? get values from tveeprom */ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) { + struct i2c_client c; u8 eedata[256]; - cx->i2c_client[0].addr = 0xA0 >> 1; - tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata)); - tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata); + memset(&c, 0, sizeof(c)); + strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name)); + c.adapter = &cx->i2c_adap[0]; + c.addr = 0xA0 >> 1; + + tveeprom_read(&c, eedata, sizeof(eedata)); + tveeprom_hauppauge_analog(&c, tv, eedata); } static void cx18_process_eeprom(struct cx18 *cx) @@ -448,34 +445,38 @@ static void cx18_process_options(struct cx18 *cx) cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize; - cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = 0; /* computed later */ + cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize; cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */ - /* Except for VBI ensure stream_buffers & stream_buf_size are valid */ + /* Ensure stream_buffers & stream_buf_size are valid */ for (i = 0; i < CX18_MAX_STREAMS; i++) { - /* User said to use 0 buffers */ - if (cx->stream_buffers[i] == 0) { - cx->options.megabytes[i] = 0; - cx->stream_buf_size[i] = 0; - continue; - } - /* User said to use 0 MB total */ - if (cx->options.megabytes[i] <= 0) { + if (cx->stream_buffers[i] == 0 || /* User said 0 buffers */ + cx->options.megabytes[i] <= 0 || /* User said 0 MB total */ + cx->stream_buf_size[i] <= 0) { /* User said buf size 0 */ cx->options.megabytes[i] = 0; cx->stream_buffers[i] = 0; cx->stream_buf_size[i] = 0; continue; } - /* VBI is computed later or user said buffer has size 0 */ - if (cx->stream_buf_size[i] <= 0) { - if (i != CX18_ENC_STREAM_TYPE_VBI) { - cx->options.megabytes[i] = 0; - cx->stream_buffers[i] = 0; - cx->stream_buf_size[i] = 0; + /* + * VBI is a special case where the stream_buf_size is fixed + * and already in bytes + */ + if (i == CX18_ENC_STREAM_TYPE_VBI) { + if (cx->stream_buffers[i] < 0) { + cx->stream_buffers[i] = + cx->options.megabytes[i] * 1024 * 1024 + / cx->stream_buf_size[i]; + } else { + /* N.B. This might round down to 0 */ + cx->options.megabytes[i] = + cx->stream_buffers[i] + * cx->stream_buf_size[i]/(1024 * 1024); } continue; } + /* All other streams have stream_buf_size in kB at this point */ if (cx->stream_buffers[i] < 0) { cx->stream_buffers[i] = cx->options.megabytes[i] * 1024 / cx->stream_buf_size[i]; @@ -487,9 +488,9 @@ static void cx18_process_options(struct cx18 *cx) cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */ } - cx->options.cardtype = cardtype[cx->num]; - cx->options.tuner = tuner[cx->num]; - cx->options.radio = radio[cx->num]; + cx->options.cardtype = cardtype[cx->instance]; + cx->options.tuner = tuner[cx->instance]; + cx->options.radio = radio[cx->instance]; cx->std = cx18_parse_std(cx); if (cx->options.cardtype == -1) { @@ -502,7 +503,7 @@ static void cx18_process_options(struct cx18 *cx) else if (cx->options.cardtype != 0) CX18_ERR("Unknown user specified type, trying to autodetect card\n"); if (cx->card == NULL) { - if (cx->dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) { + if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) { cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); CX18_INFO("Autodetected Hauppauge card\n"); } @@ -512,13 +513,13 @@ static void cx18_process_options(struct cx18 *cx) if (cx->card->pci_list == NULL) continue; for (j = 0; cx->card->pci_list[j].device; j++) { - if (cx->dev->device != + if (cx->pci_dev->device != cx->card->pci_list[j].device) continue; - if (cx->dev->subsystem_vendor != + if (cx->pci_dev->subsystem_vendor != cx->card->pci_list[j].subsystem_vendor) continue; - if (cx->dev->subsystem_device != + if (cx->pci_dev->subsystem_device != cx->card->pci_list[j].subsystem_device) continue; CX18_INFO("Autodetected %s card\n", cx->card->name); @@ -531,9 +532,10 @@ done: if (cx->card == NULL) { cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n", - cx->dev->vendor, cx->dev->device); + cx->pci_dev->vendor, cx->pci_dev->device); CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n", - cx->dev->subsystem_vendor, cx->dev->subsystem_device); + cx->pci_dev->subsystem_vendor, + cx->pci_dev->subsystem_device); CX18_ERR("Defaulting to %s card\n", cx->card->name); CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n"); @@ -545,7 +547,7 @@ done: } /* Precondition: the cx18 structure has been memset to 0. Only - the dev and num fields have been filled in. + the dev and instance fields have been filled in. No assumptions on the card type may be made here (see cx18_init_struct2 for that). */ @@ -553,18 +555,14 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) { int i; - cx->base_addr = pci_resource_start(cx->dev, 0); + cx->base_addr = pci_resource_start(cx->pci_dev, 0); mutex_init(&cx->serialize_lock); - mutex_init(&cx->i2c_bus_lock[0]); - mutex_init(&cx->i2c_bus_lock[1]); mutex_init(&cx->gpio_lock); mutex_init(&cx->epu2apu_mb_lock); mutex_init(&cx->epu2cpu_mb_lock); - spin_lock_init(&cx->lock); - - cx->work_queue = create_singlethread_workqueue(cx->name); + cx->work_queue = create_singlethread_workqueue(cx->v4l2_dev.name); if (cx->work_queue == NULL) { CX18_ERR("Unable to create work hander thread\n"); return -ENOMEM; @@ -587,7 +585,8 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) (cx->params.video_temporal_filter_mode << 1) | (cx->params.video_median_filter_type << 2); cx->params.port = CX2341X_PORT_MEMORY; - cx->params.capabilities = CX2341X_CAP_HAS_TS; + cx->params.capabilities = + CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI; init_waitqueue_head(&cx->cap_w); init_waitqueue_head(&cx->mb_apu_waitq); init_waitqueue_head(&cx->mb_cpu_waitq); @@ -597,49 +596,6 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; - /* - * The VBI line sizes depend on the pixel clock and the horiz rate - * - * (1/Fh)*(2*Fp) = Samples/line - * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples - * - * Sliced VBI is sent as ancillary data during horizontal blanking - * Raw VBI is sent as active video samples during vertcal blanking - * - * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line - * length of 720 pixels @ 4:2:2 sampling. Thus... - * - * For systems that use a 15.734 kHz horizontal rate, such as - * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have: - * - * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line = - * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples - * - * For systems that use a 15.625 kHz horizontal rate, such as - * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have: - * - * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line = - * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples - * - */ - - /* FIXME: init these based on tuner std & modify when std changes */ - /* CX18-AV-Core number of VBI samples output per horizontal line */ - cx->vbi.raw_decoder_line_size = 1444; /* 4 byte SAV + 2 * 720 */ - cx->vbi.sliced_decoder_line_size = 272; /* 60 Hz: 268+4, 50 Hz: 280+4 */ - - /* CX18-AV-Core VBI samples/line possibly rounded up */ - cx->vbi.raw_size = 1444; /* Real max size is 1444 */ - cx->vbi.sliced_size = 284; /* Real max size is 284 */ - - /* - * CX18-AV-Core SAV/EAV RP codes in VIP 1.x mode - * Task Field VerticalBlank HorizontalBlank 0 0 0 0 - */ - cx->vbi.raw_decoder_sav_odd_field = 0x20; /* V */ - cx->vbi.raw_decoder_sav_even_field = 0x60; /* FV */ - cx->vbi.sliced_decoder_sav_odd_field = 0xB0; /* T VH - actually EAV */ - cx->vbi.sliced_decoder_sav_even_field = 0xF0; /* TFVH - actually EAV */ return 0; } @@ -668,15 +624,9 @@ static void __devinit cx18_init_struct2(struct cx18 *cx) i = 0; cx->active_input = i; cx->audio_input = cx->card->video_inputs[i].audio_index; - cx->av_state.vid_input = CX18_AV_COMPOSITE7; - cx->av_state.aud_input = CX18_AV_AUDIO8; - cx->av_state.audclk_freq = 48000; - cx->av_state.audmode = V4L2_TUNER_MODE_LANG1; - /* FIXME - 8 is NTSC value, investigate */ - cx->av_state.vbi_line_offset = 8; } -static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev, +static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { u16 cmd; @@ -684,124 +634,125 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev, CX18_DEBUG_INFO("Enabling pci device\n"); - if (pci_enable_device(dev)) { - CX18_ERR("Can't enable device %d!\n", cx->num); + if (pci_enable_device(pci_dev)) { + CX18_ERR("Can't enable device %d!\n", cx->instance); return -EIO; } - if (pci_set_dma_mask(dev, 0xffffffff)) { - CX18_ERR("No suitable DMA available on card %d.\n", cx->num); + if (pci_set_dma_mask(pci_dev, 0xffffffff)) { + CX18_ERR("No suitable DMA available, card %d\n", cx->instance); return -EIO; } if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) { - CX18_ERR("Cannot request encoder memory region on card %d.\n", cx->num); + CX18_ERR("Cannot request encoder memory region, card %d\n", + cx->instance); return -EIO; } /* Enable bus mastering and memory mapped IO for the CX23418 */ - pci_read_config_word(dev, PCI_COMMAND, &cmd); + pci_read_config_word(pci_dev, PCI_COMMAND, &cmd); cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; - pci_write_config_word(dev, PCI_COMMAND, cmd); + pci_write_config_word(pci_dev, PCI_COMMAND, cmd); - pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev); - pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &cx->card_rev); + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency); if (pci_latency < 64 && cx18_pci_latency) { CX18_INFO("Unreasonably low latency timer, " "setting to 64 (was %d)\n", pci_latency); - pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); - pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64); + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency); } CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, " "irq: %d, latency: %d, memory: 0x%lx\n", - cx->dev->device, cx->card_rev, dev->bus->number, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), - cx->dev->irq, pci_latency, (unsigned long)cx->base_addr); + cx->pci_dev->device, cx->card_rev, pci_dev->bus->number, + PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn), + cx->pci_dev->irq, pci_latency, (unsigned long)cx->base_addr); return 0; } -#ifdef MODULE -static u32 cx18_request_module(struct cx18 *cx, u32 hw, - const char *name, u32 id) -{ - if ((hw & id) == 0) - return hw; - if (request_module(name) != 0) { - CX18_ERR("Failed to load module %s\n", name); - return hw & ~id; - } - CX18_DEBUG_INFO("Loaded module %s\n", name); - return hw; -} -#endif - -static void cx18_load_and_init_modules(struct cx18 *cx) +static void cx18_init_subdevs(struct cx18 *cx) { u32 hw = cx->card->hw_all; + u32 device; int i; -#ifdef MODULE - /* load modules */ -#ifdef CONFIG_MEDIA_TUNER_MODULE - hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER); -#endif -#ifdef CONFIG_VIDEO_CS5345_MODULE - hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345); -#endif -#endif - - /* check which i2c devices are actually found */ - for (i = 0; i < 32; i++) { - u32 device = 1 << i; + for (i = 0, device = 1; i < 32; i++, device <<= 1) { if (!(device & hw)) continue; - if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM || - device == CX18_HW_CX23418 || device == CX18_HW_DVB) { - /* These 'devices' do not use i2c probing */ + + switch (device) { + case CX18_HW_DVB: + case CX18_HW_TVEEPROM: + /* These subordinate devices do not use probing */ cx->hw_flags |= device; - continue; - } - cx18_i2c_register(cx, i); - if (cx18_i2c_hw_addr(cx, device) > 0) + break; + case CX18_HW_418_AV: + /* The A/V decoder gets probed earlier to set PLLs */ + /* Just note that the card uses it (i.e. has analog) */ cx->hw_flags |= device; + break; + case CX18_HW_GPIO_RESET_CTRL: + /* + * The Reset Controller gets probed and added to + * hw_flags earlier for i2c adapter/bus initialization + */ + break; + case CX18_HW_GPIO_MUX: + if (cx18_gpio_register(cx, device) == 0) + cx->hw_flags |= device; + break; + default: + if (cx18_i2c_register(cx, i) == 0) + cx->hw_flags |= device; + break; + } } - hw = cx->hw_flags; + if (cx->hw_flags & CX18_HW_418_AV) + cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV); + + if (cx->card->hw_muxer != 0) + cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer); } -static int __devinit cx18_probe(struct pci_dev *dev, +static int __devinit cx18_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { int retval = 0; int i; - int vbi_buf_size; u32 devtype; struct cx18 *cx; - spin_lock(&cx18_cards_lock); - - /* Make sure we've got a place for this card */ - if (cx18_cards_active == CX18_MAX_CARDS) { - printk(KERN_ERR "cx18: Maximum number of cards detected (%d).\n", - cx18_cards_active); - spin_unlock(&cx18_cards_lock); + /* FIXME - module parameter arrays constrain max instances */ + i = atomic_inc_return(&cx18_instance) - 1; + if (i >= CX18_MAX_CARDS) { + printk(KERN_ERR "cx18: cannot manage card %d, driver has a " + "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1); return -ENOMEM; } cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC); - if (!cx) { - spin_unlock(&cx18_cards_lock); + if (cx == NULL) { + printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n", + i); return -ENOMEM; } - cx18_cards[cx18_cards_active] = cx; - cx->dev = dev; - cx->num = cx18_cards_active++; - snprintf(cx->name, sizeof(cx->name), "cx18-%d", cx->num); - CX18_INFO("Initializing card #%d\n", cx->num); + cx->pci_dev = pci_dev; + cx->instance = i; - spin_unlock(&cx18_cards_lock); + retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev); + if (retval) { + printk(KERN_ERR "cx18: v4l2_device_register of card %d failed" + "\n", cx->instance); + kfree(cx); + return retval; + } + snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d", + cx->instance); + CX18_INFO("Initializing card %d\n", cx->instance); cx18_process_options(cx); if (cx->options.cardtype == -1) { @@ -816,13 +767,10 @@ static int __devinit cx18_probe(struct pci_dev *dev, CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); /* PCI Device Setup */ - retval = cx18_setup_pci(cx, dev, pci_id); + retval = cx18_setup_pci(cx, pci_dev, pci_id); if (retval != 0) goto free_workqueue; - /* save cx in the pci struct for later use */ - pci_set_drvdata(dev, cx); - /* map io memory */ CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE); @@ -856,6 +804,23 @@ static int __devinit cx18_probe(struct pci_dev *dev, cx18_gpio_init(cx); + /* Initialize integrated A/V decoder early to set PLLs, just in case */ + retval = cx18_av_probe(cx); + if (retval) { + CX18_ERR("Could not register A/V decoder subdevice\n"); + goto free_map; + } + cx18_call_hw(cx, CX18_HW_418_AV, core, init, (u32) CX18_AV_INIT_PLLS); + + /* Initialize GPIO Reset Controller to do chip resets during i2c init */ + if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) { + if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0) + CX18_WARN("Could not register GPIO reset controller" + "subdevice; proceeding anyway.\n"); + else + cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL; + } + /* active i2c */ CX18_DEBUG_INFO("activating i2c...\n"); retval = init_cx18_i2c(cx); @@ -864,8 +829,6 @@ static int __devinit cx18_probe(struct pci_dev *dev, goto free_map; } - CX18_DEBUG_INFO("Active card count: %d.\n", cx18_cards_active); - if (cx->card->hw_all & CX18_HW_TVEEPROM) { /* Based on the model number the cardtype may be changed. The PCI IDs are not always reliable. */ @@ -881,8 +844,9 @@ static int __devinit cx18_probe(struct pci_dev *dev, cx18_init_scb(cx); /* Register IRQ */ - retval = request_irq(cx->dev->irq, cx18_irq_handler, - IRQF_SHARED | IRQF_DISABLED, cx->name, (void *)cx); + retval = request_irq(cx->pci_dev->irq, cx18_irq_handler, + IRQF_SHARED | IRQF_DISABLED, + cx->v4l2_dev.name, (void *)cx); if (retval) { CX18_ERR("Failed to register irq %d\n", retval); goto free_i2c; @@ -917,33 +881,14 @@ static int __devinit cx18_probe(struct pci_dev *dev, initialization. */ cx18_init_struct2(cx); - cx18_load_and_init_modules(cx); + cx18_init_subdevs(cx); - if (cx->std & V4L2_STD_525_60) { + if (cx->std & V4L2_STD_525_60) cx->is_60hz = 1; - cx->is_out_60hz = 1; - } else { + else cx->is_50hz = 1; - cx->is_out_50hz = 1; - } - cx->params.video_gop_size = cx->is_60hz ? 15 : 12; - - /* - * FIXME: setting the buffer size based on the tuner standard is - * suboptimal, as the CVBS and SVideo inputs could use a different std - * and the buffer could end up being too small in that case. - */ - vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2; - cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size; - if (cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] < 0) - cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = - cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] * 1024 * 1024 - / vbi_buf_size; - else - cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = - cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] * vbi_buf_size - / (1024 * 1024); + cx->params.video_gop_size = cx->is_60hz ? 15 : 12; if (cx->options.radio > 0) cx->v4l2_cap |= V4L2_CAP_RADIO; @@ -956,7 +901,7 @@ static int __devinit cx18_probe(struct pci_dev *dev, setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ setup.tuner_callback = (setup.type == TUNER_XC2028) ? cx18_reset_tuner_gpio : NULL; - cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup); + cx18_call_all(cx, tuner, s_type_addr, &setup); if (setup.type == TUNER_XC2028) { static struct xc2028_ctrl ctrl = { .fname = XC2028_DEFAULT_FIRMWARE, @@ -966,7 +911,7 @@ static int __devinit cx18_probe(struct pci_dev *dev, .tuner = cx->options.tuner, .priv = &ctrl, }; - cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg); + cx18_call_all(cx, tuner, s_config, &cfg); } } @@ -985,14 +930,13 @@ static int __devinit cx18_probe(struct pci_dev *dev, goto free_streams; } - CX18_INFO("Initialized card #%d: %s\n", cx->num, cx->card_name); - + CX18_INFO("Initialized card: %s\n", cx->card_name); return 0; free_streams: cx18_streams_cleanup(cx, 1); free_irq: - free_irq(cx->dev->irq, (void *)cx); + free_irq(cx->pci_dev->irq, (void *)cx); free_i2c: exit_cx18_i2c(cx); free_map: @@ -1006,11 +950,8 @@ err: retval = -ENODEV; CX18_ERR("Error %d on initialization\n", retval); - i = cx->num; - spin_lock(&cx18_cards_lock); - kfree(cx18_cards[i]); - cx18_cards[i] = NULL; - spin_unlock(&cx18_cards_lock); + v4l2_device_unregister(&cx->v4l2_dev); + kfree(cx); return retval; } @@ -1043,8 +984,21 @@ int cx18_init_on_first_open(struct cx18 *cx) } set_bit(CX18_F_I_LOADED_FW, &cx->i_flags); - /* Init the firmware twice to work around a silicon bug - * transport related. */ + /* + * Init the firmware twice to work around a silicon bug + * with the digital TS. + * + * The second firmware load requires us to normalize the APU state, + * or the audio for the first analog capture will be badly incorrect. + * + * I can't seem to call APU_RESETAI and have it succeed without the + * APU capturing audio, so we start and stop it here to do the reset + */ + + /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */ + cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0); + cx18_vapi(cx, CX18_APU_RESETAI, 0); + cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG); fw_retry_count = 3; while (--fw_retry_count > 0) { @@ -1060,6 +1014,22 @@ int cx18_init_on_first_open(struct cx18 *cx) return -ENXIO; } + /* + * The second firmware load requires us to normalize the APU state, + * or the audio for the first analog capture will be badly incorrect. + * + * I can't seem to call APU_RESETAI and have it succeed without the + * APU capturing audio, so we start and stop it here to do the reset + */ + + /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */ + cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0); + cx18_vapi(cx, CX18_APU_RESETAI, 0); + cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG); + + /* Init the A/V decoder, if it hasn't been already */ + v4l2_subdev_call(cx->sd_av, core, init, (u32) CX18_AV_INIT_NORMAL); + vf.tuner = 0; vf.type = V4L2_TUNER_ANALOG_TV; vf.frequency = 6400; /* the tuner 'baseline' frequency */ @@ -1092,9 +1062,11 @@ static void cx18_cancel_epu_work_orders(struct cx18 *cx) static void cx18_remove(struct pci_dev *pci_dev) { - struct cx18 *cx = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct cx18 *cx = to_cx18(v4l2_dev); + int i; - CX18_DEBUG_INFO("Removing Card #%d\n", cx->num); + CX18_DEBUG_INFO("Removing Card\n"); /* Stop all captures */ CX18_DEBUG_INFO("Stopping all streams\n"); @@ -1115,15 +1087,22 @@ static void cx18_remove(struct pci_dev *pci_dev) exit_cx18_i2c(cx); - free_irq(cx->dev->irq, (void *)cx); + free_irq(cx->pci_dev->irq, (void *)cx); cx18_iounmap(cx); release_mem_region(cx->base_addr, CX18_MEM_SIZE); - pci_disable_device(cx->dev); + pci_disable_device(cx->pci_dev); + + if (cx->vbi.sliced_mpeg_data[0] != NULL) + for (i = 0; i < CX18_VBI_FRAMES; i++) + kfree(cx->vbi.sliced_mpeg_data[i]); + + CX18_INFO("Removed %s\n", cx->card_name); - CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num); + v4l2_device_unregister(v4l2_dev); + kfree(cx); } /* define a pci_driver for card detection */ @@ -1138,8 +1117,6 @@ static int module_start(void) { printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION); - memset(cx18_cards, 0, sizeof(cx18_cards)); - /* Validate parameters */ if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) { printk(KERN_ERR "cx18: Exiting, cx18_first_minor must be between 0 and %d\n", @@ -1162,16 +1139,7 @@ static int module_start(void) static void module_cleanup(void) { - int i; - pci_unregister_driver(&cx18_pci_driver); - - for (i = 0; i < cx18_cards_active; i++) { - if (cx18_cards[i] == NULL) - continue; - kfree(cx18_cards[i]); - } - } module_init(module_start); diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 0d2edeb..ece4f28 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -48,6 +48,7 @@ #include <linux/dvb/audio.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> #include <media/tuner.h> #include "cx18-mailbox.h" #include "cx18-av-core.h" @@ -79,7 +80,7 @@ #define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */ #define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */ #define CX18_CARD_TOSHIBA_QOSMIO_DVBT 5 /* Toshiba Qosmio Interal DVB-T/Analog*/ -#define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100 */ +#define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100/DVR3100 H */ #define CX18_CARD_LAST 6 #define CX18_ENC_STREAM_TYPE_MPG 0 @@ -143,12 +144,12 @@ /* Flag to turn on high volume debugging */ #define CX18_DBGFLG_HIGHVOL (1 << 8) -/* NOTE: extra space before comma in 'cx->num , ## args' is required for +/* NOTE: extra space before comma in 'fmt , ## args' is required for gcc-2.95, otherwise it won't compile. */ #define CX18_DEBUG(x, type, fmt, args...) \ do { \ if ((x) & cx18_debug) \ - printk(KERN_INFO "cx18-%d " type ": " fmt, cx->num , ## args); \ + v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \ } while (0) #define CX18_DEBUG_WARN(fmt, args...) CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args) #define CX18_DEBUG_INFO(fmt, args...) CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args) @@ -162,7 +163,7 @@ #define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \ do { \ if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \ - printk(KERN_INFO "cx18%d " type ": " fmt, cx->num , ## args); \ + v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \ } while (0) #define CX18_DEBUG_HI_WARN(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args) #define CX18_DEBUG_HI_INFO(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args) @@ -174,9 +175,58 @@ #define CX18_DEBUG_HI_IRQ(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args) /* Standard kernel messages */ -#define CX18_ERR(fmt, args...) printk(KERN_ERR "cx18-%d: " fmt, cx->num , ## args) -#define CX18_WARN(fmt, args...) printk(KERN_WARNING "cx18-%d: " fmt, cx->num , ## args) -#define CX18_INFO(fmt, args...) printk(KERN_INFO "cx18-%d: " fmt, cx->num , ## args) +#define CX18_ERR(fmt, args...) v4l2_err(&cx->v4l2_dev, fmt , ## args) +#define CX18_WARN(fmt, args...) v4l2_warn(&cx->v4l2_dev, fmt , ## args) +#define CX18_INFO(fmt, args...) v4l2_info(&cx->v4l2_dev, fmt , ## args) + +/* Messages for internal subdevs to use */ +#define CX18_DEBUG_DEV(x, dev, type, fmt, args...) \ + do { \ + if ((x) & cx18_debug) \ + v4l2_info(dev, " " type ": " fmt , ## args); \ + } while (0) +#define CX18_DEBUG_WARN_DEV(dev, fmt, args...) \ + CX18_DEBUG_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args) +#define CX18_DEBUG_INFO_DEV(dev, fmt, args...) \ + CX18_DEBUG_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args) +#define CX18_DEBUG_API_DEV(dev, fmt, args...) \ + CX18_DEBUG_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args) +#define CX18_DEBUG_DMA_DEV(dev, fmt, args...) \ + CX18_DEBUG_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args) +#define CX18_DEBUG_IOCTL_DEV(dev, fmt, args...) \ + CX18_DEBUG_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args) +#define CX18_DEBUG_FILE_DEV(dev, fmt, args...) \ + CX18_DEBUG_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args) +#define CX18_DEBUG_I2C_DEV(dev, fmt, args...) \ + CX18_DEBUG_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args) +#define CX18_DEBUG_IRQ_DEV(dev, fmt, args...) \ + CX18_DEBUG_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args) + +#define CX18_DEBUG_HIGH_VOL_DEV(x, dev, type, fmt, args...) \ + do { \ + if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \ + v4l2_info(dev, " " type ": " fmt , ## args); \ + } while (0) +#define CX18_DEBUG_HI_WARN_DEV(dev, fmt, args...) \ + CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args) +#define CX18_DEBUG_HI_INFO_DEV(dev, fmt, args...) \ + CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args) +#define CX18_DEBUG_HI_API_DEV(dev, fmt, args...) \ + CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args) +#define CX18_DEBUG_HI_DMA_DEV(dev, fmt, args...) \ + CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args) +#define CX18_DEBUG_HI_IOCTL_DEV(dev, fmt, args...) \ + CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args) +#define CX18_DEBUG_HI_FILE_DEV(dev, fmt, args...) \ + CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args) +#define CX18_DEBUG_HI_I2C_DEV(dev, fmt, args...) \ + CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args) +#define CX18_DEBUG_HI_IRQ_DEV(dev, fmt, args...) \ + CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args) + +#define CX18_ERR_DEV(dev, fmt, args...) v4l2_err(dev, fmt , ## args) +#define CX18_WARN_DEV(dev, fmt, args...) v4l2_warn(dev, fmt , ## args) +#define CX18_INFO_DEV(dev, fmt, args...) v4l2_info(dev, fmt , ## args) /* Values for CX18_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */ #define MPEG_FRAME_TYPE_IFRAME 1 @@ -279,7 +329,7 @@ struct cx18_epu_work_order { struct cx18_stream { /* These first four fields are always set, even if the stream is not actually created. */ - struct video_device *v4l2dev; /* NULL when stream not created */ + struct video_device *video_dev; /* NULL when stream not created */ struct cx18 *cx; /* for ease of use */ const char *name; /* name of the stream */ int type; /* stream type */ @@ -292,7 +342,6 @@ struct cx18_stream { int dma; /* can be PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE or PCI_DMA_NONE */ - u64 dma_pts; wait_queue_head_t waitq; /* Buffer Stats */ @@ -318,59 +367,121 @@ struct cx18_open_id { /* forward declaration of struct defined in cx18-cards.h */ struct cx18_card; +/* + * A note about "sliced" VBI data as implemented in this driver: + * + * Currently we collect the sliced VBI in the form of Ancillary Data + * packets, inserted by the AV core decoder/digitizer/slicer in the + * horizontal blanking region of the VBI lines, in "raw" mode as far as + * the Encoder is concerned. We don't ever tell the Encoder itself + * to provide sliced VBI. (AV Core: sliced mode - Encoder: raw mode) + * + * We then process the ancillary data ourselves to send the sliced data + * to the user application directly or build up MPEG-2 private stream 1 + * packets to splice into (only!) MPEG-2 PS streams for the user app. + * + * (That's how ivtv essentially does it.) + * + * The Encoder should be able to extract certain sliced VBI data for + * us and provide it in a separate stream or splice it into any type of + * MPEG PS or TS stream, but this isn't implemented yet. + */ + +/* + * Number of "raw" VBI samples per horizontal line we tell the Encoder to + * grab from the decoder/digitizer/slicer output for raw or sliced VBI. + * It depends on the pixel clock and the horiz rate: + * + * (1/Fh)*(2*Fp) = Samples/line + * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples + * + * Sliced VBI data is sent as ancillary data during horizontal blanking + * Raw VBI is sent as active video samples during vertcal blanking + * + * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line + * length of 720 pixels @ 4:2:2 sampling. Thus... + * + * For systems that use a 15.734 kHz horizontal rate, such as + * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have: + * + * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line = + * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples + * + * For systems that use a 15.625 kHz horizontal rate, such as + * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have: + * + * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line = + * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples + */ +static const u32 vbi_active_samples = 1444; /* 4 byte SAV + 720 Y + 720 U/V */ +static const u32 vbi_hblank_samples_60Hz = 272; /* 4 byte EAV + 268 anc/fill */ +static const u32 vbi_hblank_samples_50Hz = 284; /* 4 byte EAV + 280 anc/fill */ #define CX18_VBI_FRAMES 32 -/* VBI data */ struct vbi_info { - u32 enc_size; - u32 frame; - u8 cc_data_odd[256]; - u8 cc_data_even[256]; - int cc_pos; - u8 cc_no_update; - u8 vps[5]; - u8 vps_found; - int wss; - u8 wss_found; - u8 wss_no_update; - u32 raw_decoder_line_size; - u8 raw_decoder_sav_odd_field; - u8 raw_decoder_sav_even_field; - u32 sliced_decoder_line_size; - u8 sliced_decoder_sav_odd_field; - u8 sliced_decoder_sav_even_field; + /* Current state of v4l2 VBI settings for this device */ struct v4l2_format in; - /* convenience pointer to sliced struct in vbi_in union */ - struct v4l2_sliced_vbi_format *sliced_in; - u32 service_set_in; - int insert_mpeg; + struct v4l2_sliced_vbi_format *sliced_in; /* pointer to in.fmt.sliced */ + u32 count; /* Count of VBI data lines: 60 Hz: 12 or 50 Hz: 18 */ + u32 start[2]; /* First VBI data line per field: 10 & 273 or 6 & 318 */ - /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. - One for /dev/vbi0 and one for /dev/vbi8 */ - struct v4l2_sliced_vbi_data sliced_data[36]; + u32 frame; /* Count of VBI buffers/frames received from Encoder */ - /* Buffer for VBI data inserted into MPEG stream. - The first byte is a dummy byte that's never used. - The next 16 bytes contain the MPEG header for the VBI data, - the remainder is the actual VBI data. - The max size accepted by the MPEG VBI reinsertion turns out - to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes, - where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is - a single line header byte and 2 * 18 is the number of VBI lines per frame. + /* + * Vars for creation and insertion of MPEG Private Stream 1 packets + * of sliced VBI data into an MPEG PS + */ - However, it seems that the data must be 1K aligned, so we have to - pad the data until the 1 or 2 K boundary. + /* Boolean: create and insert Private Stream 1 packets into the PS */ + int insert_mpeg; + + /* + * Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. + * Used in cx18-vbi.c only for collecting sliced data, and as a source + * during conversion of sliced VBI data into MPEG Priv Stream 1 packets. + * We don't need to save state here, but the array may have been a bit + * too big (2304 bytes) to alloc from the stack. + */ + struct v4l2_sliced_vbi_data sliced_data[36]; - This pointer array will allocate 2049 bytes to store each VBI frame. */ + /* + * A ring buffer of driver-generated MPEG-2 PS + * Program Pack/Private Stream 1 packets for sliced VBI data insertion + * into the MPEG PS stream. + * + * In each sliced_mpeg_data[] buffer is: + * 16 byte MPEG-2 PS Program Pack Header + * 16 byte MPEG-2 Private Stream 1 PES Header + * 4 byte magic number: "itv0" or "ITV0" + * 4 byte first field line mask, if "itv0" + * 4 byte second field line mask, if "itv0" + * 36 lines, if "ITV0"; or <36 lines, if "itv0"; of sliced VBI data + * + * Each line in the payload is + * 1 byte line header derived from the SDID (WSS, CC, VPS, etc.) + * 42 bytes of line data + * + * That's a maximum 1552 bytes of payload in the Private Stream 1 packet + * which is the payload size a PVR-350 (CX23415) MPEG decoder will + * accept for VBI data. So, including the headers, it's a maximum 1584 + * bytes total. + */ +#define CX18_SLICED_MPEG_DATA_MAXSZ 1584 + /* copy_vbi_buf() needs 8 temp bytes on the end for the worst case */ +#define CX18_SLICED_MPEG_DATA_BUFSZ (CX18_SLICED_MPEG_DATA_MAXSZ+8) u8 *sliced_mpeg_data[CX18_VBI_FRAMES]; u32 sliced_mpeg_size[CX18_VBI_FRAMES]; - struct cx18_buffer sliced_mpeg_buf; + + /* Count of Program Pack/Program Stream 1 packets inserted into PS */ u32 inserted_frame; - u32 start[2], count; - u32 raw_size; - u32 sliced_size; + /* + * A dummy driver stream transfer buffer with a copy of the next + * sliced_mpeg_data[] buffer for output to userland apps. + * Only used in cx18-fileops.c, but its state needs to persist at times. + */ + struct cx18_buffer sliced_mpeg_buf; }; /* Per cx23418, per I2C bus private algo callback data */ @@ -383,16 +494,17 @@ struct cx18_i2c_algo_callback_data { /* Struct to hold info about cx18 cards */ struct cx18 { - int num; /* board number, -1 during init! */ - char name[8]; /* board name for printk and interrupts (e.g. 'cx180') */ - struct pci_dev *dev; /* PCI device */ + int instance; + struct pci_dev *pci_dev; + struct v4l2_device v4l2_dev; + struct v4l2_subdev *sd_av; /* A/V decoder/digitizer sub-device */ + struct v4l2_subdev *sd_extmux; /* External multiplexer sub-dev */ + const struct cx18_card *card; /* card information */ const char *card_name; /* full name of the card */ const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */ u8 is_50hz; u8 is_60hz; - u8 is_out_50hz; - u8 is_out_60hz; u8 nof_inputs; /* number of video inputs */ u8 nof_audio_inputs; /* number of audio inputs */ u16 buffer_id; /* buffer ID counter */ @@ -413,10 +525,7 @@ struct cx18 { /* dualwatch */ unsigned long dualwatch_jiffies; - u16 dualwatch_stereo_mode; - - /* Digitizer type */ - int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */ + u32 dualwatch_stereo_mode; struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ struct cx18_options options; /* User options */ @@ -426,7 +535,6 @@ struct cx18 { unsigned long i_flags; /* global cx18 flags */ atomic_t ana_capturing; /* count number of active analog capture streams */ atomic_t tot_capturing; /* total count number of active capture streams */ - spinlock_t lock; /* lock access to this struct */ int search_pack_header; int open_id; /* incremented each time an open occurs, used as @@ -468,30 +576,30 @@ struct cx18 { struct i2c_adapter i2c_adap[2]; struct i2c_algo_bit_data i2c_algo[2]; struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2]; - struct i2c_client i2c_client[2]; - struct mutex i2c_bus_lock[2]; - struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; /* gpio */ u32 gpio_dir; u32 gpio_val; struct mutex gpio_lock; + struct v4l2_subdev sd_gpiomux; + struct v4l2_subdev sd_resetctrl; /* v4l2 and User settings */ /* codec settings */ u32 audio_input; u32 active_input; - u32 active_output; v4l2_std_id std; v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ }; +static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct cx18, v4l2_dev); +} + /* Globals */ -extern struct cx18 *cx18_cards[]; -extern int cx18_cards_active; extern int cx18_first_minor; -extern spinlock_t cx18_cards_lock; /*==============Prototypes==================*/ @@ -511,4 +619,22 @@ static inline int cx18_raw_vbi(const struct cx18 *cx) return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE; } +/* Call the specified callback for all subdevs with a grp_id bit matching the + * mask in hw (if 0, then match them all). Ignore any errors. */ +#define cx18_call_hw(cx, hw, o, f, args...) \ + __v4l2_device_call_subdevs(&(cx)->v4l2_dev, \ + !(hw) || (sd->grp_id & (hw)), o, f , ##args) + +#define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args) + +/* Call the specified callback for all subdevs with a grp_id bit matching the + * mask in hw (if 0, then match them all). If the callback returns an error + * other than 0 or -ENOIOCTLCMD, then return with that error code. */ +#define cx18_call_hw_err(cx, hw, o, f, args...) \ + __v4l2_device_call_subdevs_until_err( \ + &(cx)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args) + +#define cx18_call_all_err(cx, o, f, args...) \ + cx18_call_hw_err(cx, 0, o, f , ##args) + #endif /* CX18_DRIVER_H */ diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index bd5e6f3..3b86f57 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -167,7 +167,7 @@ int cx18_dvb_register(struct cx18_stream *stream) ret = dvb_register_adapter(&dvb->dvb_adapter, CX18_DRIVER_NAME, - THIS_MODULE, &cx->dev->dev, adapter_nr); + THIS_MODULE, &cx->pci_dev->dev, adapter_nr); if (ret < 0) goto err_out; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 055f6e0..4d7d6d5 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -128,15 +128,15 @@ static void cx18_release_stream(struct cx18_stream *s) static void cx18_dualwatch(struct cx18 *cx) { struct v4l2_tuner vt; - u16 new_bitmap; - u16 new_stereo_mode; - const u16 stereo_mask = 0x0300; - const u16 dual = 0x0200; + u32 new_bitmap; + u32 new_stereo_mode; + const u32 stereo_mask = 0x0300; + const u32 dual = 0x0200; u32 h; new_stereo_mode = cx->params.audio_properties & stereo_mask; memset(&vt, 0, sizeof(vt)); - cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, &vt); + cx18_call_all(cx, tuner, g_tuner, &vt); if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) new_stereo_mode = dual; @@ -176,6 +176,8 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, *err = 0; while (1) { if (s->type == CX18_ENC_STREAM_TYPE_MPG) { + /* Process pending program info updates and pending + VBI data */ if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) { cx->dualwatch_jiffies = jiffies; @@ -186,7 +188,6 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) { /* byteswap and process VBI data */ cx18_process_vbi_data(cx, buf, - s_vbi->dma_pts, s_vbi->type); cx18_stream_put_buf_fw(s_vbi, buf); } @@ -207,8 +208,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, cx18_buf_swap(buf); else { /* byteswap and process VBI data */ - cx18_process_vbi_data(cx, buf, - s->dma_pts, s->type); + cx18_process_vbi_data(cx, buf, s->type); } return buf; } @@ -260,6 +260,20 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, len = ucount; if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) { + /* + * Try to find a good splice point in the PS, just before + * an MPEG-2 Program Pack start code, and provide only + * up to that point to the user, so it's easy to insert VBI data + * the next time around. + */ + /* FIXME - This only works for an MPEG-2 PS, not a TS */ + /* + * An MPEG-2 Program Stream (PS) is a series of + * MPEG-2 Program Packs terminated by an + * MPEG Program End Code after the last Program Pack. + * A Program Pack may hold a PS System Header packet and any + * number of Program Elementary Stream (PES) Packets + */ const char *start = buf->buf + buf->readpos; const char *p = start + 1; const u8 *q; @@ -267,38 +281,54 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, int stuffing, i; while (start + len > p) { + /* Scan for a 0 to find a potential MPEG-2 start code */ q = memchr(p, 0, start + len - p); if (q == NULL) break; p = q + 1; + /* + * Keep looking if not a + * MPEG-2 Pack header start code: 0x00 0x00 0x01 0xba + * or MPEG-2 video PES start code: 0x00 0x00 0x01 0xe0 + */ if ((char *)q + 15 >= buf->buf + buf->bytesused || q[1] != 0 || q[2] != 1 || q[3] != ch) continue; + + /* If expecting the primary video PES */ if (!cx->search_pack_header) { + /* Continue if it couldn't be a PES packet */ if ((q[6] & 0xc0) != 0x80) continue; - if (((q[7] & 0xc0) == 0x80 && - (q[9] & 0xf0) == 0x20) || - ((q[7] & 0xc0) == 0xc0 && - (q[9] & 0xf0) == 0x30)) { - ch = 0xba; + /* Check if a PTS or PTS & DTS follow */ + if (((q[7] & 0xc0) == 0x80 && /* PTS only */ + (q[9] & 0xf0) == 0x20) || /* PTS only */ + ((q[7] & 0xc0) == 0xc0 && /* PTS & DTS */ + (q[9] & 0xf0) == 0x30)) { /* DTS follows */ + /* Assume we found the video PES hdr */ + ch = 0xba; /* next want a Program Pack*/ cx->search_pack_header = 1; - p = q + 9; + p = q + 9; /* Skip this video PES hdr */ } continue; } + + /* We may have found a Program Pack start code */ + + /* Get the count of stuffing bytes & verify them */ stuffing = q[13] & 7; /* all stuffing bytes must be 0xff */ for (i = 0; i < stuffing; i++) if (q[14 + i] != 0xff) break; - if (i == stuffing && - (q[4] & 0xc4) == 0x44 && - (q[12] & 3) == 3 && - q[14 + stuffing] == 0 && + if (i == stuffing && /* right number of stuffing bytes*/ + (q[4] & 0xc4) == 0x44 && /* marker check */ + (q[12] & 3) == 3 && /* marker check */ + q[14 + stuffing] == 0 && /* PES Pack or Sys Hdr */ q[15 + stuffing] == 0 && q[16 + stuffing] == 1) { - cx->search_pack_header = 0; + /* We declare we actually found a Program Pack*/ + cx->search_pack_header = 0; /* expect vid PES */ len = (char *)q - start; cx18_setup_sliced_vbi_buf(cx); break; @@ -578,7 +608,7 @@ int cx18_v4l2_close(struct file *filp) /* Mark that the radio is no longer in use */ clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags); /* Switch tuner to TV */ - cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std); + cx18_call_all(cx, tuner, s_std, cx->std); /* Select correct audio input (i.e. TV tuner or Line in) */ cx18_audio_set_io(cx); if (atomic_read(&cx->ana_capturing) > 0) { @@ -641,7 +671,7 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) /* We have the radio */ cx18_mute(cx); /* Switch tuner to radio */ - cx18_call_i2c_clients(cx, AUDC_SET_RADIO, NULL); + cx18_call_all(cx, tuner, s_radio); /* Select the correct audio input (i.e. radio tuner) */ cx18_audio_set_io(cx); /* Done! Unmute and continue. */ @@ -652,38 +682,15 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) int cx18_v4l2_open(struct file *filp) { - int res, x, y = 0; - struct cx18 *cx = NULL; - struct cx18_stream *s = NULL; - int minor = video_devdata(filp)->minor; - - /* Find which card this open was on */ - spin_lock(&cx18_cards_lock); - for (x = 0; cx == NULL && x < cx18_cards_active; x++) { - /* find out which stream this open was on */ - for (y = 0; y < CX18_MAX_STREAMS; y++) { - if (cx18_cards[x] == NULL) - continue; - s = &cx18_cards[x]->streams[y]; - if (s->v4l2dev && s->v4l2dev->minor == minor) { - cx = cx18_cards[x]; - break; - } - } - } - spin_unlock(&cx18_cards_lock); - - if (cx == NULL) { - /* Couldn't find a device registered - on that minor, shouldn't happen! */ - printk(KERN_WARNING "No cx18 device found on minor %d\n", - minor); - return -ENXIO; - } + int res; + struct video_device *video_dev = video_devdata(filp); + struct cx18_stream *s = video_get_drvdata(video_dev); + struct cx18 *cx = s->cx;; mutex_lock(&cx->serialize_lock); if (cx18_init_on_first_open(cx)) { - CX18_ERR("Failed to initialize on minor %d\n", minor); + CX18_ERR("Failed to initialize on minor %d\n", + video_dev->minor); mutex_unlock(&cx->serialize_lock); return -ENXIO; } diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c index 1fa95da..83cd559 100644 --- a/drivers/media/video/cx18/cx18-firmware.c +++ b/drivers/media/video/cx18/cx18-firmware.c @@ -26,7 +26,6 @@ #include "cx18-irq.h" #include "cx18-firmware.h" #include "cx18-cards.h" -#include "cx18-av-core.h" #include <linux/firmware.h> #define CX18_PROC_SOFT_RESET 0xc70010 @@ -107,7 +106,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) u32 __iomem *dst = (u32 __iomem *)mem; const u32 *src; - if (request_firmware(&fw, fn, &cx->dev->dev)) { + if (request_firmware(&fw, fn, &cx->pci_dev->dev)) { CX18_ERR("Unable to open firmware %s\n", fn); CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n"); return -ENOMEM; @@ -151,7 +150,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, u32 apu_version = 0; int sz; - if (request_firmware(&fw, fn, &cx->dev->dev)) { + if (request_firmware(&fw, fn, &cx->pci_dev->dev)) { CX18_ERR("unable to open firmware %s\n", fn); CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); cx18_setup_page(cx, 0); @@ -286,23 +285,6 @@ void cx18_init_power(struct cx18 *cx, int lowpwr) cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC); cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST); - /* - * VDCLK Integer = 0x0f, Post Divider = 0x04 - * AIMCLK Integer = 0x0e, Post Divider = 0x16 - */ - cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f); - - /* VDCLK Fraction = 0x2be2fe */ - /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */ - cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe); - - /* AIMCLK Fraction = 0x05227ad */ - /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz before post-divide */ - cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad); - - /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ - cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56); - /* Defaults */ /* APU = SC or SC/2 = 125/62.5 */ /* EPU = SC = 125 */ diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c index 1a99329..5518d14 100644 --- a/drivers/media/video/cx18/cx18-gpio.c +++ b/drivers/media/video/cx18/cx18-gpio.c @@ -46,6 +46,9 @@ * gpio13: cs5345 reset pin */ +/* + * File scope utility functions + */ static void gpio_write(struct cx18 *cx) { u32 dir_lo = cx->gpio_dir & 0xffff; @@ -63,73 +66,201 @@ static void gpio_write(struct cx18 *cx) CX18_REG_GPIO_OUT2, val_hi, dir_hi); } -void cx18_reset_i2c_slaves_gpio(struct cx18 *cx) +static void gpio_update(struct cx18 *cx, u32 mask, u32 data) { - const struct cx18_gpio_i2c_slave_reset *p; + if (mask == 0) + return; - p = &cx->card->gpio_i2c_slave_reset; + mutex_lock(&cx->gpio_lock); + cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask); + gpio_write(cx); + mutex_unlock(&cx->gpio_lock); +} + +static void gpio_reset_seq(struct cx18 *cx, u32 active_lo, u32 active_hi, + unsigned int assert_msecs, + unsigned int recovery_msecs) +{ + u32 mask; - if ((p->active_lo_mask | p->active_hi_mask) == 0) + mask = active_lo | active_hi; + if (mask == 0) return; - /* Assuming that the masks are a subset of the bits in gpio_dir */ + /* + * Assuming that active_hi and active_lo are a subsets of the bits in + * gpio_dir. Also assumes that active_lo and active_hi don't overlap + * in any bit position + */ /* Assert */ - mutex_lock(&cx->gpio_lock); - cx->gpio_val = - (cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask); - gpio_write(cx); - schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted)); + gpio_update(cx, mask, ~active_lo); + schedule_timeout_uninterruptible(msecs_to_jiffies(assert_msecs)); /* Deassert */ - cx->gpio_val = - (cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask); - gpio_write(cx); - schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery)); + gpio_update(cx, mask, ~active_hi); + schedule_timeout_uninterruptible(msecs_to_jiffies(recovery_msecs)); +} + +/* + * GPIO Multiplexer - logical device + */ +static int gpiomux_log_status(struct v4l2_subdev *sd) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + + mutex_lock(&cx->gpio_lock); + CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n", + cx->gpio_dir, cx->gpio_val); mutex_unlock(&cx->gpio_lock); + return 0; } -void cx18_reset_ir_gpio(void *data) +static int gpiomux_s_radio(struct v4l2_subdev *sd) { - struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; - const struct cx18_gpio_i2c_slave_reset *p; + struct cx18 *cx = v4l2_get_subdevdata(sd); - p = &cx->card->gpio_i2c_slave_reset; + /* + * FIXME - work out the cx->active/audio_input mess - this is + * intended to handle the switch to radio mode and set the + * audio routing, but we need to update the state in cx + */ + gpio_update(cx, cx->card->gpio_audio_input.mask, + cx->card->gpio_audio_input.radio); + return 0; +} - if (p->ir_reset_mask == 0) - return; +static int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + u32 data; - CX18_DEBUG_INFO("Resetting IR microcontroller\n"); + switch (cx->card->audio_inputs[cx->audio_input].muxer_input) { + case 1: + data = cx->card->gpio_audio_input.linein; + break; + case 0: + data = cx->card->gpio_audio_input.tuner; + break; + default: + /* + * FIXME - work out the cx->active/audio_input mess - this is + * intended to handle the switch from radio mode and set the + * audio routing, but we need to update the state in cx + */ + data = cx->card->gpio_audio_input.tuner; + break; + } + gpio_update(cx, cx->card->gpio_audio_input.mask, data); + return 0; +} - /* - Assert timing for the Z8F0811 on HVR-1600 boards: - 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to initiate - 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock cycles - (6,601,085 nanoseconds ~= 7 milliseconds) - 3. DBG pin must be high before chip exits reset for normal operation. - DBG is open drain and hopefully pulled high since we don't - normally drive it (GPIO 1?) for the HVR-1600 - 4. Z8F0811 won't exit reset until RESET is deasserted - */ - mutex_lock(&cx->gpio_lock); - cx->gpio_val = cx->gpio_val & ~p->ir_reset_mask; - gpio_write(cx); - mutex_unlock(&cx->gpio_lock); - schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted)); +static int gpiomux_s_audio_routing(struct v4l2_subdev *sd, + const struct v4l2_routing *route) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + u32 data; + + switch (route->input) { + case 0: + data = cx->card->gpio_audio_input.tuner; + break; + case 1: + data = cx->card->gpio_audio_input.linein; + break; + case 2: + data = cx->card->gpio_audio_input.radio; + break; + default: + return -EINVAL; + } + gpio_update(cx, cx->card->gpio_audio_input.mask, data); + return 0; +} + +static const struct v4l2_subdev_core_ops gpiomux_core_ops = { + .log_status = gpiomux_log_status, +}; + +static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = { + .s_std = gpiomux_s_std, + .s_radio = gpiomux_s_radio, +}; + +static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = { + .s_routing = gpiomux_s_audio_routing, +}; + +static const struct v4l2_subdev_ops gpiomux_ops = { + .core = &gpiomux_core_ops, + .tuner = &gpiomux_tuner_ops, + .audio = &gpiomux_audio_ops, +}; + +/* + * GPIO Reset Controller - logical device + */ +static int resetctrl_log_status(struct v4l2_subdev *sd) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); - /* - Zilog comes out of reset, loads reset vector address and executes - from there. Required recovery delay unknown. - */ mutex_lock(&cx->gpio_lock); - cx->gpio_val = cx->gpio_val | p->ir_reset_mask; - gpio_write(cx); + CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n", + cx->gpio_dir, cx->gpio_val); mutex_unlock(&cx->gpio_lock); - schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery)); + return 0; } -EXPORT_SYMBOL(cx18_reset_ir_gpio); -/* This symbol is exported for use by an infrared module for the IR-blaster */ +static int resetctrl_reset(struct v4l2_subdev *sd, u32 val) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + const struct cx18_gpio_i2c_slave_reset *p; + + p = &cx->card->gpio_i2c_slave_reset; + switch (val) { + case CX18_GPIO_RESET_I2C: + gpio_reset_seq(cx, p->active_lo_mask, p->active_hi_mask, + p->msecs_asserted, p->msecs_recovery); + break; + case CX18_GPIO_RESET_Z8F0811: + /* + * Assert timing for the Z8F0811 on HVR-1600 boards: + * 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to + * initiate + * 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock + * cycles (6,601,085 nanoseconds ~= 7 milliseconds) + * 3. DBG pin must be high before chip exits reset for normal + * operation. DBG is open drain and hopefully pulled high + * since we don't normally drive it (GPIO 1?) for the + * HVR-1600 + * 4. Z8F0811 won't exit reset until RESET is deasserted + * 5. Zilog comes out of reset, loads reset vector address and + * executes from there. Required recovery delay unknown. + */ + gpio_reset_seq(cx, p->ir_reset_mask, 0, + p->msecs_asserted, p->msecs_recovery); + break; + case CX18_GPIO_RESET_XC2028: + if (cx->card->tuners[0].tuner == TUNER_XC2028) + gpio_reset_seq(cx, (1 << cx->card->xceive_pin), 0, + 1, 1); + break; + } + return 0; +} + +static const struct v4l2_subdev_core_ops resetctrl_core_ops = { + .log_status = resetctrl_log_status, + .reset = resetctrl_reset, +}; + +static const struct v4l2_subdev_ops resetctrl_ops = { + .core = &resetctrl_core_ops, +}; + +/* + * External entry points + */ void cx18_gpio_init(struct cx18 *cx) { mutex_lock(&cx->gpio_lock); @@ -156,6 +287,49 @@ void cx18_gpio_init(struct cx18 *cx) mutex_unlock(&cx->gpio_lock); } +int cx18_gpio_register(struct cx18 *cx, u32 hw) +{ + struct v4l2_subdev *sd; + const struct v4l2_subdev_ops *ops; + char *str; + + switch (hw) { + case CX18_HW_GPIO_MUX: + sd = &cx->sd_gpiomux; + ops = &gpiomux_ops; + str = "gpio-mux"; + break; + case CX18_HW_GPIO_RESET_CTRL: + sd = &cx->sd_resetctrl; + ops = &resetctrl_ops; + str = "gpio-reset-ctrl"; + break; + default: + return -EINVAL; + } + + v4l2_subdev_init(sd, ops); + v4l2_set_subdevdata(sd, cx); + snprintf(sd->name, sizeof(sd->name), "%s %s", cx->v4l2_dev.name, str); + sd->grp_id = hw; + return v4l2_device_register_subdev(&cx->v4l2_dev, sd); +} + +void cx18_reset_ir_gpio(void *data) +{ + struct cx18 *cx = to_cx18((struct v4l2_device *)data); + + if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0) + return; + + CX18_DEBUG_INFO("Resetting IR microcontroller\n"); + + v4l2_subdev_call(&cx->sd_resetctrl, + core, reset, CX18_GPIO_RESET_Z8F0811); +} +EXPORT_SYMBOL(cx18_reset_ir_gpio); +/* This symbol is exported for use by lirc_pvr150 for the IR-blaster */ + /* Xceive tuner reset function */ int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value) { @@ -163,56 +337,11 @@ int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value) struct cx18_i2c_algo_callback_data *cb_data = algo->data; struct cx18 *cx = cb_data->cx; - if (cmd != XC2028_TUNER_RESET) + if (cmd != XC2028_TUNER_RESET || + cx->card->tuners[0].tuner != TUNER_XC2028) return 0; - CX18_DEBUG_INFO("Resetting tuner\n"); - mutex_lock(&cx->gpio_lock); - cx->gpio_val &= ~(1 << cx->card->xceive_pin); - gpio_write(cx); - mutex_unlock(&cx->gpio_lock); - schedule_timeout_interruptible(msecs_to_jiffies(1)); - - mutex_lock(&cx->gpio_lock); - cx->gpio_val |= 1 << cx->card->xceive_pin; - gpio_write(cx); - mutex_unlock(&cx->gpio_lock); - schedule_timeout_interruptible(msecs_to_jiffies(1)); - return 0; -} - -int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg) -{ - struct v4l2_routing *route = arg; - u32 mask, data; - - switch (command) { - case VIDIOC_INT_S_AUDIO_ROUTING: - if (route->input > 2) - return -EINVAL; - mask = cx->card->gpio_audio_input.mask; - switch (route->input) { - case 0: - data = cx->card->gpio_audio_input.tuner; - break; - case 1: - data = cx->card->gpio_audio_input.linein; - break; - case 2: - default: - data = cx->card->gpio_audio_input.radio; - break; - } - break; - - default: - return -EINVAL; - } - if (mask) { - mutex_lock(&cx->gpio_lock); - cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask); - gpio_write(cx); - mutex_unlock(&cx->gpio_lock); - } - return 0; + CX18_DEBUG_INFO("Resetting XCeive tuner\n"); + return v4l2_subdev_call(&cx->sd_resetctrl, + core, reset, CX18_GPIO_RESET_XC2028); } diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h index 39ffccc..f9a5ca3 100644 --- a/drivers/media/video/cx18/cx18-gpio.h +++ b/drivers/media/video/cx18/cx18-gpio.h @@ -22,7 +22,13 @@ */ void cx18_gpio_init(struct cx18 *cx); -void cx18_reset_i2c_slaves_gpio(struct cx18 *cx); +int cx18_gpio_register(struct cx18 *cx, u32 hw); + +enum cx18_gpio_reset_type { + CX18_GPIO_RESET_I2C = 0, + CX18_GPIO_RESET_Z8F0811 = 1, + CX18_GPIO_RESET_XC2028 = 2, +}; + void cx18_reset_ir_gpio(void *data); int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value); -int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg); diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 83e1c63..d092643 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -26,7 +26,6 @@ #include "cx18-io.h" #include "cx18-cards.h" #include "cx18-gpio.h" -#include "cx18-av-core.h" #include "cx18-i2c.h" #include "cx18-irq.h" @@ -43,31 +42,37 @@ #define CX18_CS5345_I2C_ADDR 0x4c /* This array should match the CX18_HW_ defines */ -static const u8 hw_driverids[] = { - I2C_DRIVERID_TUNER, - I2C_DRIVERID_TVEEPROM, - I2C_DRIVERID_CS5345, - 0, /* CX18_HW_GPIO dummy driver ID */ - 0 /* CX18_HW_CX23418 dummy driver ID */ -}; - -/* This array should match the CX18_HW_ defines */ static const u8 hw_addrs[] = { - 0, - 0, - CX18_CS5345_I2C_ADDR, - 0, /* CX18_HW_GPIO dummy driver ID */ - 0, /* CX18_HW_CX23418 dummy driver ID */ + 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 */ }; /* This array should match the CX18_HW_ defines */ /* This might well become a card-specific array */ static const u8 hw_bus[] = { - 0, - 0, - 0, - 0, /* CX18_HW_GPIO dummy driver ID */ - 0, /* CX18_HW_CX23418 dummy driver ID */ + 1, /* CX18_HW_TUNER */ + 0, /* CX18_HW_TVEEPROM */ + 0, /* CX18_HW_CS5345 */ + 0, /* CX18_HW_DVB */ + 0, /* CX18_HW_418_AV */ + 0, /* CX18_HW_GPIO_MUX */ + 0, /* CX18_HW_GPIO_RESET_CTRL */ +}; + +/* This array should match the CX18_HW_ defines */ +static const char * const hw_modules[] = { + "tuner", /* CX18_HW_TUNER */ + NULL, /* CX18_HW_TVEEPROM */ + "cs5345", /* CX18_HW_CS5345 */ + NULL, /* CX18_HW_DVB */ + NULL, /* CX18_HW_418_AV */ + NULL, /* CX18_HW_GPIO_MUX */ + NULL, /* CX18_HW_GPIO_RESET_CTRL */ }; /* This array should match the CX18_HW_ defines */ @@ -75,83 +80,67 @@ static const char * const hw_devicenames[] = { "tuner", "tveeprom", "cs5345", - "gpio", - "cx23418", + "cx23418_DTV", + "cx23418_AV", + "gpio_mux", + "gpio_reset_ctrl", }; int cx18_i2c_register(struct cx18 *cx, unsigned idx) { - struct i2c_board_info info; - struct i2c_client *c; - u8 id, bus; - int i; - - CX18_DEBUG_I2C("i2c client register\n"); - if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0) + struct v4l2_subdev *sd; + int bus = hw_bus[idx]; + struct i2c_adapter *adap = &cx->i2c_adap[bus]; + const char *mod = hw_modules[idx]; + const char *type = hw_devicenames[idx]; + u32 hw = 1 << idx; + + if (idx >= ARRAY_SIZE(hw_addrs)) return -1; - id = hw_driverids[idx]; - bus = hw_bus[idx]; - memset(&info, 0, sizeof(info)); - strlcpy(info.type, hw_devicenames[idx], sizeof(info.type)); - info.addr = hw_addrs[idx]; - for (i = 0; i < I2C_CLIENTS_MAX; i++) - if (cx->i2c_clients[i] == NULL) - break; - - if (i == I2C_CLIENTS_MAX) { - CX18_ERR("insufficient room for new I2C client!\n"); - return -ENOMEM; - } - if (id != I2C_DRIVERID_TUNER) { - c = i2c_new_device(&cx->i2c_adap[bus], &info); - if (c->driver == NULL) - i2c_unregister_device(c); - else - cx->i2c_clients[i] = c; - return cx->i2c_clients[i] ? 0 : -ENODEV; + if (hw == CX18_HW_TUNER) { + /* special tuner group handling */ + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + cx->card_i2c->radio); + if (sd != NULL) + sd->grp_id = hw; + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + cx->card_i2c->demod); + if (sd != NULL) + sd->grp_id = hw; + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + cx->card_i2c->tv); + if (sd != NULL) + sd->grp_id = hw; + return sd != NULL ? 0 : -1; } - /* special tuner handling */ - c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - cx->i2c_clients[i++] = c; - c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - cx->i2c_clients[i++] = c; - c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - cx->i2c_clients[i++] = c; - return 0; -} + /* Is it not an I2C device or one we do not wish to register? */ + if (!hw_addrs[idx]) + return -1; -static int attach_inform(struct i2c_client *client) -{ - return 0; + /* It's an I2C device other than an analog tuner */ + sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]); + if (sd != NULL) + sd->grp_id = hw; + return sd != NULL ? 0 : -1; } -static int detach_inform(struct i2c_client *client) +/* Find the first member of the subdev group id in hw */ +struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw) { - int i; - struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter); + struct v4l2_subdev *result = NULL; + struct v4l2_subdev *sd; - CX18_DEBUG_I2C("i2c client detach\n"); - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - if (cx->i2c_clients[i] == client) { - cx->i2c_clients[i] = NULL; + spin_lock(&cx->v4l2_dev.lock); + v4l2_device_for_each_subdev(sd, &cx->v4l2_dev) { + if (sd->grp_id == hw) { + result = sd; break; } } - CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n", - client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); - - return 0; + spin_unlock(&cx->v4l2_dev.lock); + return result; } static void cx18_setscl(void *data, int state) @@ -204,8 +193,6 @@ static struct i2c_adapter cx18_i2c_adap_template = { .id = I2C_HW_B_CX2341X, .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ - .client_register = attach_inform, - .client_unregister = detach_inform, .owner = THIS_MODULE, }; @@ -221,152 +208,28 @@ static struct i2c_algo_bit_data cx18_i2c_algo_template = { .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */ }; -static struct i2c_client cx18_i2c_client_template = { - .name = "cx18 internal", -}; - -int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg) -{ - struct i2c_client *client; - int retval; - int i; - - CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr); - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - client = cx->i2c_clients[i]; - if (client == NULL || client->driver == NULL || - client->driver->command == NULL) - continue; - if (addr == client->addr) { - retval = client->driver->command(client, cmd, arg); - return retval; - } - } - if (cmd != VIDIOC_DBG_G_CHIP_IDENT) - CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n", - addr, cmd); - return -ENODEV; -} - -/* Find the i2c device based on the driver ID and return - its i2c address or -ENODEV if no matching device was found. */ -static int cx18_i2c_id_addr(struct cx18 *cx, u32 id) -{ - struct i2c_client *client; - int retval = -ENODEV; - int i; - - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - client = cx->i2c_clients[i]; - if (client == NULL || client->driver == NULL) - continue; - if (id == client->driver->id) { - retval = client->addr; - break; - } - } - return retval; -} - -/* Find the i2c device name matching the CX18_HW_ flag */ -static const char *cx18_i2c_hw_name(u32 hw) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (1 << i == hw) - return hw_devicenames[i]; - return "unknown device"; -} - -/* Find the i2c device matching the CX18_HW_ flag and return - its i2c address or -ENODEV if no matching device was found. */ -int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (1 << i == hw) - return cx18_i2c_id_addr(cx, hw_driverids[i]); - return -ENODEV; -} - -/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing. - If hw == CX18_HW_GPIO then call the gpio handler. */ -int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg) -{ - int addr; - - if (hw == 0) - return 0; - - if (hw == CX18_HW_GPIO) - return cx18_gpio(cx, cmd, arg); - - if (hw == CX18_HW_CX23418) - return cx18_av_cmd(cx, cmd, arg); - - addr = cx18_i2c_hw_addr(cx, hw); - if (addr < 0) { - CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n", - hw, cx18_i2c_hw_name(hw), cmd); - return addr; - } - return cx18_call_i2c_client(cx, addr, cmd, arg); -} - -/* broadcast cmd for all I2C clients and for the gpio subsystem */ -void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg) -{ - if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) { - CX18_ERR("adapter is not set\n"); - return; - } - cx18_av_cmd(cx, cmd, arg); - i2c_clients_command(&cx->i2c_adap[0], cmd, arg); - i2c_clients_command(&cx->i2c_adap[1], cmd, arg); - if (cx->hw_flags & CX18_HW_GPIO) - cx18_gpio(cx, cmd, arg); -} - /* init + register i2c algo-bit adapter */ int init_cx18_i2c(struct cx18 *cx) { int i; CX18_DEBUG_I2C("i2c init\n"); - /* Sanity checks for the I2C hardware arrays. They must be the - * same size and GPIO/CX23418 must be the last entries. - */ - if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) || - ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || - CX18_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 2)) || - CX18_HW_CX23418 != (1 << (ARRAY_SIZE(hw_addrs) - 1)) || - hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) { - CX18_ERR("Mismatched I2C hardware arrays\n"); - return -ENODEV; - } - for (i = 0; i < 2; i++) { - memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, - sizeof(struct i2c_adapter)); + /* Setup algorithm for adapter */ memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template, sizeof(struct i2c_algo_bit_data)); cx->i2c_algo_cb_data[i].cx = cx; cx->i2c_algo_cb_data[i].bus_index = i; cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i]; - cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; + /* Setup adapter */ + memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, + sizeof(struct i2c_adapter)); + cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name), - " #%d-%d", cx->num, i); - i2c_set_adapdata(&cx->i2c_adap[i], cx); - - memcpy(&cx->i2c_client[i], &cx18_i2c_client_template, - sizeof(struct i2c_client)); - sprintf(cx->i2c_client[i].name + - strlen(cx->i2c_client[i].name), "%d", i); - cx->i2c_client[i].adapter = &cx->i2c_adap[i]; - cx->i2c_adap[i].dev.parent = &cx->dev->dev; + " #%d-%d", cx->instance, i); + i2c_set_adapdata(&cx->i2c_adap[i], &cx->v4l2_dev); + cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev; } if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) { @@ -402,7 +265,8 @@ int init_cx18_i2c(struct cx18 *cx) cx18_setscl(&cx->i2c_algo_cb_data[1], 1); cx18_setsda(&cx->i2c_algo_cb_data[1], 1); - cx18_reset_i2c_slaves_gpio(cx); + cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, + core, reset, (u32) CX18_GPIO_RESET_I2C); return i2c_bit_add_bus(&cx->i2c_adap[0]) || i2c_bit_add_bus(&cx->i2c_adap[1]); diff --git a/drivers/media/video/cx18/cx18-i2c.h b/drivers/media/video/cx18/cx18-i2c.h index 4869739..bdfd192 100644 --- a/drivers/media/video/cx18/cx18-i2c.h +++ b/drivers/media/video/cx18/cx18-i2c.h @@ -21,11 +21,8 @@ * 02111-1307 USA */ -int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw); -int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg); -int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg); -void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg); int cx18_i2c_register(struct cx18 *cx, unsigned idx); +struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw); /* init + register i2c algo-bit adapter */ int init_cx18_i2c(struct cx18 *cx); diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 7086aab..e4c9e3d 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -58,12 +58,21 @@ u16 cx18_service2vbi(int type) } } +/* Check if VBI services are allowed on the (field, line) for the video std */ static int valid_service_line(int field, int line, int is_pal) { - return (is_pal && line >= 6 && (line != 23 || field == 0)) || + return (is_pal && line >= 6 && + ((field == 0 && line <= 23) || (field == 1 && line <= 22))) || (!is_pal && line >= 10 && line < 22); } +/* + * For a (field, line, std) and inbound potential set of services for that line, + * return the first valid service of those passed in the incoming set for that + * line in priority order: + * CC, VPS, or WSS over TELETEXT for well known lines + * TELETEXT, before VPS, before CC, before WSS, for other lines + */ static u16 select_service_from_set(int field, int line, u16 set, int is_pal) { u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525); @@ -90,6 +99,10 @@ static u16 select_service_from_set(int field, int line, u16 set, int is_pal) return 0; } +/* + * Expand the service_set of *fmt into valid service_lines for the std, + * and clear the passed in fmt->service_set + */ void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) { u16 set = fmt->service_set; @@ -102,7 +115,25 @@ void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) } } +/* + * Sanitize the service_lines in *fmt per the video std, and return 1 + * if any service_line is left as valid after santization + */ +static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +{ + int f, l; + u16 set = 0; + + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal); + set |= fmt->service_lines[f][l]; + } + } + return set != 0; +} +/* Compute the service_set from the assumed valid service_lines of *fmt */ u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt) { int f, l; @@ -129,10 +160,8 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh, pixfmt->priv = 0; if (id->type == CX18_ENC_STREAM_TYPE_YUV) { pixfmt->pixelformat = V4L2_PIX_FMT_HM12; - /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ - pixfmt->sizeimage = - pixfmt->height * pixfmt->width + - pixfmt->height * (pixfmt->width / 2); + /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */ + pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2; pixfmt->bytesperline = 720; } else { pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; @@ -149,8 +178,8 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi; vbifmt->sampling_rate = 27000000; - vbifmt->offset = 248; - vbifmt->samples_per_line = cx->vbi.raw_decoder_line_size - 4; + vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */ + vbifmt->samples_per_line = vbi_active_samples - 4; vbifmt->sample_format = V4L2_PIX_FMT_GREY; vbifmt->start[0] = cx->vbi.start[0]; vbifmt->start[1] = cx->vbi.start[1]; @@ -164,7 +193,30 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh, static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - return -EINVAL; + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + + /* sane, V4L2 spec compliant, defaults */ + vbifmt->reserved[0] = 0; + vbifmt->reserved[1] = 0; + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); + vbifmt->service_set = 0; + + /* + * Fetch the configured service_lines and total service_set from the + * digitizer/slicer. Note, cx18_av_vbi() wipes the passed in + * fmt->fmt.sliced under valid calling conditions + */ + if (v4l2_subdev_call(cx->sd_av, video, g_fmt, fmt)) + return -EINVAL; + + /* Ensure V4L2 spec compliant output */ + vbifmt->reserved[0] = 0; + vbifmt->reserved[1] = 0; + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + vbifmt->service_set = cx18_get_service_set(vbifmt); + return 0; } static int cx18_try_fmt_vid_cap(struct file *file, void *fh, @@ -174,11 +226,18 @@ static int cx18_try_fmt_vid_cap(struct file *file, void *fh, struct cx18 *cx = id->cx; int w = fmt->fmt.pix.width; int h = fmt->fmt.pix.height; + int min_h = 2; w = min(w, 720); - w = max(w, 1); + w = max(w, 2); + if (id->type == CX18_ENC_STREAM_TYPE_YUV) { + /* YUV height must be a multiple of 32 */ + h &= ~0x1f; + min_h = 32; + } h = min(h, cx->is_50hz ? 576 : 480); - h = max(h, 2); + h = max(h, min_h); + cx18_g_fmt_vid_cap(file, fh, fmt); fmt->fmt.pix.width = w; fmt->fmt.pix.height = h; @@ -194,7 +253,20 @@ static int cx18_try_fmt_vbi_cap(struct file *file, void *fh, static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - return -EINVAL; + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + vbifmt->reserved[0] = 0; + vbifmt->reserved[1] = 0; + + /* If given a service set, expand it validly & clear passed in set */ + if (vbifmt->service_set) + cx18_expand_service_set(vbifmt, cx->is_50hz); + /* Sanitize the service_lines, and compute the new set if any valid */ + if (check_service_set(vbifmt, cx->is_50hz)) + vbifmt->service_set = cx18_get_service_set(vbifmt); + return 0; } static int cx18_s_fmt_vid_cap(struct file *file, void *fh, @@ -223,7 +295,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh, cx->params.width = w; cx->params.height = h; - cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); + v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt); return cx18_g_fmt_vid_cap(file, fh, fmt); } @@ -238,54 +310,131 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, if (ret) return ret; + /* + * Changing the Encoder's Raw VBI parameters won't have any effect + * if any analog capture is ongoing + */ if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) return -EBUSY; + /* + * Set the digitizer registers for raw active VBI. + * Note cx18_av_vbi_wipes out alot of the passed in fmt under valid + * calling conditions + */ + ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt); + if (ret) + return ret; + + /* Store our new v4l2 (non-)sliced VBI state */ cx->vbi.sliced_in->service_set = 0; cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; - cx18_av_cmd(cx, VIDIOC_S_FMT, fmt); + return cx18_g_fmt_vbi_cap(file, fh, fmt); } static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - return -EINVAL; + struct cx18_open_id *id = fh; + struct cx18 *cx = id->cx; + int ret; + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + + ret = v4l2_prio_check(&cx->prio, &id->prio); + if (ret) + return ret; + + cx18_try_fmt_sliced_vbi_cap(file, fh, fmt); + + /* + * Changing the Encoder's Raw VBI parameters won't have any effect + * if any analog capture is ongoing + */ + if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) + return -EBUSY; + + /* + * Set the service_lines requested in the digitizer/slicer registers. + * Note, cx18_av_vbi() wipes some "impossible" service lines in the + * passed in fmt->fmt.sliced under valid calling conditions + */ + ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt); + if (ret) + return ret; + /* Store our current v4l2 sliced VBI settings */ + cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in)); + return 0; } static int cx18_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *chip) { struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + int err = 0; chip->ident = V4L2_IDENT_NONE; chip->revision = 0; - if (v4l2_chip_match_host(&chip->match)) { - chip->ident = V4L2_IDENT_CX23418; - return 0; + switch (chip->match.type) { + case V4L2_CHIP_MATCH_HOST: + switch (chip->match.addr) { + case 0: + chip->ident = V4L2_IDENT_CX23418; + chip->revision = cx18_read_reg(cx, 0xC72028); + break; + case 1: + /* + * The A/V decoder is always present, but in the rare + * case that the card doesn't have analog, we don't + * use it. We find it w/o using the cx->sd_av pointer + */ + cx18_call_hw(cx, CX18_HW_418_AV, + core, g_chip_ident, chip); + break; + default: + /* + * Could return ident = V4L2_IDENT_UNKNOWN if we had + * other host chips at higher addresses, but we don't + */ + err = -EINVAL; /* per V4L2 spec */ + break; + } + break; + case V4L2_CHIP_MATCH_I2C_DRIVER: + /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */ + cx18_call_all(cx, core, g_chip_ident, chip); + break; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* + * We could return V4L2_IDENT_UNKNOWN, but we don't do the work + * to look if a chip is at the address with no driver. That's a + * dangerous thing to do with EEPROMs anyway. + */ + cx18_call_all(cx, core, g_chip_ident, chip); + break; + default: + err = -EINVAL; + break; } - cx18_call_i2c_clients(cx, VIDIOC_DBG_G_CHIP_IDENT, chip); - return 0; + return err; } #ifdef CONFIG_VIDEO_ADV_DEBUG static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) { struct v4l2_dbg_register *regs = arg; - unsigned long flags; if (!capable(CAP_SYS_ADMIN)) return -EPERM; if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE) return -EINVAL; - spin_lock_irqsave(&cx18_cards_lock, flags); regs->size = 4; - if (cmd == VIDIOC_DBG_G_REGISTER) - regs->val = cx18_read_enc(cx, regs->reg); - else + if (cmd == VIDIOC_DBG_S_REGISTER) cx18_write_enc(cx, regs->val, regs->reg); - spin_unlock_irqrestore(&cx18_cards_lock, flags); + else + regs->val = cx18_read_enc(cx, regs->reg); return 0; } @@ -296,7 +445,8 @@ static int cx18_g_register(struct file *file, void *fh, if (v4l2_chip_match_host(®->match)) return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg); - cx18_call_i2c_clients(cx, VIDIOC_DBG_G_REGISTER, reg); + /* FIXME - errors shouldn't be ignored */ + cx18_call_all(cx, core, g_register, reg); return 0; } @@ -307,7 +457,8 @@ static int cx18_s_register(struct file *file, void *fh, if (v4l2_chip_match_host(®->match)) return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg); - cx18_call_i2c_clients(cx, VIDIOC_DBG_S_REGISTER, reg); + /* FIXME - errors shouldn't be ignored */ + cx18_call_all(cx, core, s_register, reg); return 0; } #endif @@ -335,7 +486,8 @@ static int cx18_querycap(struct file *file, void *fh, strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver)); strlcpy(vcap->card, cx->card_name, sizeof(vcap->card)); - snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(cx->dev)); + snprintf(vcap->bus_info, sizeof(vcap->bus_info), + "PCI:%s", pci_name(cx->pci_dev)); vcap->version = CX18_DRIVER_VERSION; /* version */ vcap->capabilities = cx->v4l2_cap; /* capabilities */ return 0; @@ -403,7 +555,8 @@ static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - return cx18_av_cmd(cx, VIDIOC_S_CROP, crop); + CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n"); + return -EINVAL; } static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) @@ -412,7 +565,8 @@ static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - return cx18_av_cmd(cx, VIDIOC_G_CROP, crop); + CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n"); + return -EINVAL; } static int cx18_enum_fmt_vid_cap(struct file *file, void *fh, @@ -483,7 +637,7 @@ static int cx18_g_frequency(struct file *file, void *fh, if (vf->tuner != 0) return -EINVAL; - cx18_call_i2c_clients(cx, VIDIOC_G_FREQUENCY, vf); + cx18_call_all(cx, tuner, g_frequency, vf); return 0; } @@ -502,7 +656,7 @@ int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) cx18_mute(cx); CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); - cx18_call_i2c_clients(cx, VIDIOC_S_FREQUENCY, vf); + cx18_call_all(cx, tuner, s_frequency, vf); cx18_unmute(cx); return 0; } @@ -547,12 +701,11 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) cx->vbi.count = cx->is_50hz ? 18 : 12; cx->vbi.start[0] = cx->is_50hz ? 6 : 10; cx->vbi.start[1] = cx->is_50hz ? 318 : 273; - cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284; CX18_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long) cx->std); /* Tuner */ - cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std); + cx18_call_all(cx, tuner, s_std, cx->std); return 0; } @@ -569,9 +722,7 @@ static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) if (vt->index != 0) return -EINVAL; - /* Setting tuner can only set audio mode */ - cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt); - + cx18_call_all(cx, tuner, s_tuner, vt); return 0; } @@ -582,7 +733,7 @@ static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) if (vt->index != 0) return -EINVAL; - cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt); + cx18_call_all(cx, tuner, g_tuner, vt); if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name)); @@ -598,7 +749,30 @@ static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) static int cx18_g_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_sliced_vbi_cap *cap) { - return -EINVAL; + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; + int f, l; + + if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + + cap->service_set = 0; + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + if (valid_service_line(f, l, cx->is_50hz)) { + /* + * We can find all v4l2 supported vbi services + * for the standard, on a valid line for the std + */ + cap->service_lines[f][l] = set; + cap->service_set |= set; + } else + cap->service_lines[f][l] = 0; + } + } + for (f = 0; f < 3; f++) + cap->reserved[f] = 0; + return 0; } static int cx18_g_enc_index(struct file *file, void *fh, @@ -708,13 +882,15 @@ static int cx18_log_status(struct file *file, void *fh) struct v4l2_audio audin; int i; - CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num); + CX18_INFO("================= START STATUS CARD #%d " + "=================\n", cx->instance); + CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name); if (cx->hw_flags & CX18_HW_TVEEPROM) { struct tveeprom tv; cx18_read_eeprom(cx, &tv); } - cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL); + cx18_call_all(cx, core, log_status); cx18_get_input(cx, cx->active_input, &vidin); cx18_get_audio_input(cx, cx->audio_input, &audin); CX18_INFO("Video Input: %s\n", vidin.name); @@ -725,12 +901,12 @@ static int cx18_log_status(struct file *file, void *fh) mutex_unlock(&cx->gpio_lock); CX18_INFO("Tuner: %s\n", test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV"); - cx2341x_log_status(&cx->params, cx->name); + cx2341x_log_status(&cx->params, cx->v4l2_dev.name); CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags); for (i = 0; i < CX18_MAX_STREAMS; i++) { struct cx18_stream *s = &cx->streams[i]; - if (s->v4l2dev == NULL || s->buffers == 0) + if (s->video_dev == NULL || s->buffers == 0) continue; CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, @@ -740,7 +916,8 @@ static int cx18_log_status(struct file *file, void *fh) CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", (long long)cx->mpg_data_received, (long long)cx->vbi_data_inserted); - CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num); + CX18_INFO("================== END STATUS CARD #%d " + "==================\n", cx->instance); return 0; } @@ -754,7 +931,8 @@ static long cx18_default(struct file *file, void *fh, int cmd, void *arg) CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n", route->input, route->output); - cx18_audio_set_route(cx, route); + cx18_call_hw(cx, cx->card->hw_audio_ctrl, audio, s_routing, + route); break; } @@ -762,7 +940,8 @@ static long cx18_default(struct file *file, void *fh, int cmd, void *arg) u32 val = *(u32 *)arg; if ((val == 0) || (val & 0x01)) - cx18_reset_ir_gpio(&cx->i2c_algo_cb_data[0]); + cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset, + (u32) CX18_GPIO_RESET_Z8F0811); break; } @@ -782,6 +961,8 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd, mutex_lock(&cx->serialize_lock); + /* FIXME - consolidate v4l2_prio_check()'s here */ + if (cx18_debug & CX18_DBGFLG_IOCTL) vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; res = video_ioctl2(filp, cmd, arg); diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index de5e723..2226e57 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -83,6 +83,8 @@ static const struct cx18_api_info api_info[] = { API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), + API_ENTRY(APU, CX18_APU_START, 0), + API_ENTRY(APU, CX18_APU_STOP, 0), API_ENTRY(APU, CX18_APU_RESETAI, 0), API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0), API_ENTRY(0, 0, 0), @@ -98,21 +100,30 @@ static const struct cx18_api_info *find_api_info(u32 cmd) return NULL; } -static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) +/* Call with buf of n*11+1 bytes */ +static char *u32arr2hex(u32 data[], int n, char *buf) { - char argstr[MAX_MB_ARGUMENTS*11+1]; char *p; int i; + for (i = 0, p = buf; i < n; i++, p += 11) { + /* kernel snprintf() appends '\0' always */ + snprintf(p, 12, " %#010x", data[i]); + } + *p = '\0'; + return buf; +} + +static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) +{ + char argstr[MAX_MB_ARGUMENTS*11+1]; + if (!(cx18_debug & CX18_DBGFLG_API)) return; - for (i = 0, p = argstr; i < MAX_MB_ARGUMENTS; i++, p += 11) { - /* kernel snprintf() appends '\0' always */ - snprintf(p, 12, " %#010x", mb->args[i]); - } CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s" - "\n", name, mb->request, mb->ack, mb->cmd, mb->error, argstr); + "\n", name, mb->request, mb->ack, mb->cmd, mb->error, + u32arr2hex(mb->args, MAX_MB_ARGUMENTS, argstr)); } @@ -439,7 +450,8 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) "incoming %s to EPU mailbox (sequence no. %u)" "\n", rpu_str[rpu], rpu_str[rpu], order_mb->request); - dump_mb(cx, order_mb, "incoming"); + if (cx18_debug & CX18_DBGFLG_WARN) + dump_mb(cx, order_mb, "incoming"); order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT; } @@ -468,16 +480,24 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) struct mutex *mb_lock; long int timeout, ret; int i; + char argstr[MAX_MB_ARGUMENTS*11+1]; if (info == NULL) { CX18_WARN("unknown cmd %x\n", cmd); return -EINVAL; } - if (cmd == CX18_CPU_DE_SET_MDL) - CX18_DEBUG_HI_API("%s\n", info->name); - else - CX18_DEBUG_API("%s\n", info->name); + if (cx18_debug & CX18_DBGFLG_API) { /* only call u32arr2hex if needed */ + if (cmd == CX18_CPU_DE_SET_MDL) { + if (cx18_debug & CX18_DBGFLG_HIGHVOL) + CX18_DEBUG_HI_API("%s\tcmd %#010x args%s\n", + info->name, cmd, + u32arr2hex(data, args, argstr)); + } else + CX18_DEBUG_API("%s\tcmd %#010x args%s\n", + info->name, cmd, + u32arr2hex(data, args, argstr)); + } switch (info->rpu) { case APU: diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 8d9441e..3046b8e 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -204,7 +204,7 @@ int cx18_stream_alloc(struct cx18_stream *s) } buf->id = cx->buffer_id++; INIT_LIST_HEAD(&buf->list); - buf->dma_handle = pci_map_single(s->cx->dev, + buf->dma_handle = pci_map_single(s->cx->pci_dev, buf->buf, s->buf_size, s->dma); cx18_buf_sync_for_cpu(s, buf); cx18_enqueue(s, buf, &s->q_free); @@ -227,7 +227,7 @@ void cx18_stream_free(struct cx18_stream *s) /* empty q_free */ while ((buf = cx18_dequeue(s, &s->q_free))) { - pci_unmap_single(s->cx->dev, buf->dma_handle, + pci_unmap_single(s->cx->pci_dev, buf->dma_handle, s->buf_size, s->dma); kfree(buf->buf); kfree(buf); diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h index 456cec3..4de0626 100644 --- a/drivers/media/video/cx18/cx18-queue.h +++ b/drivers/media/video/cx18/cx18-queue.h @@ -29,14 +29,14 @@ static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s, struct cx18_buffer *buf) { - pci_dma_sync_single_for_cpu(s->cx->dev, buf->dma_handle, + pci_dma_sync_single_for_cpu(s->cx->pci_dev, buf->dma_handle, s->buf_size, s->dma); } static inline void cx18_buf_sync_for_device(struct cx18_stream *s, struct cx18_buffer *buf) { - pci_dma_sync_single_for_device(s->cx->dev, buf->dma_handle, + pci_dma_sync_single_for_device(s->cx->pci_dev, buf->dma_handle, s->buf_size, s->dma); } diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 89c1ec9..0932b76 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -32,7 +32,6 @@ #include "cx18-streams.h" #include "cx18-cards.h" #include "cx18-scb.h" -#include "cx18-av-core.h" #include "cx18-dvb.h" #define CX18_DSP0_INTERRUPT_MASK 0xd0004C @@ -101,11 +100,11 @@ static struct { static void cx18_stream_init(struct cx18 *cx, int type) { struct cx18_stream *s = &cx->streams[type]; - struct video_device *dev = s->v4l2dev; + struct video_device *video_dev = s->video_dev; - /* we need to keep v4l2dev, so restore it afterwards */ + /* we need to keep video_dev, so restore it afterwards */ memset(s, 0, sizeof(*s)); - s->v4l2dev = dev; + s->video_dev = video_dev; /* initialize cx18_stream fields */ s->cx = cx; @@ -130,12 +129,12 @@ static int cx18_prep_dev(struct cx18 *cx, int type) struct cx18_stream *s = &cx->streams[type]; u32 cap = cx->v4l2_cap; int num_offset = cx18_stream_info[type].num_offset; - int num = cx->num + cx18_first_minor + num_offset; + int num = cx->instance + cx18_first_minor + num_offset; - /* These four fields are always initialized. If v4l2dev == NULL, then + /* These four fields are always initialized. If video_dev == NULL, then this stream is not in use. In that case no other fields but these four can be used. */ - s->v4l2dev = NULL; + s->video_dev = NULL; s->cx = cx; s->type = type; s->name = cx18_stream_info[type].name; @@ -163,22 +162,22 @@ static int cx18_prep_dev(struct cx18 *cx, int type) return 0; /* allocate and initialize the v4l2 video device structure */ - s->v4l2dev = video_device_alloc(); - if (s->v4l2dev == NULL) { + s->video_dev = video_device_alloc(); + if (s->video_dev == NULL) { CX18_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name); return -ENOMEM; } - snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d", - cx->num); + snprintf(s->video_dev->name, sizeof(s->video_dev->name), "%s %s", + cx->v4l2_dev.name, s->name); - s->v4l2dev->num = num; - s->v4l2dev->parent = &cx->dev->dev; - s->v4l2dev->fops = &cx18_v4l2_enc_fops; - s->v4l2dev->release = video_device_release; - s->v4l2dev->tvnorms = V4L2_STD_ALL; - cx18_set_funcs(s->v4l2dev); + s->video_dev->num = num; + s->video_dev->v4l2_dev = &cx->v4l2_dev; + s->video_dev->fops = &cx18_v4l2_enc_fops; + s->video_dev->release = video_device_release; + s->video_dev->tvnorms = V4L2_STD_ALL; + cx18_set_funcs(s->video_dev); return 0; } @@ -227,28 +226,30 @@ static int cx18_reg_dev(struct cx18 *cx, int type) } } - if (s->v4l2dev == NULL) + if (s->video_dev == NULL) return 0; - num = s->v4l2dev->num; + num = s->video_dev->num; /* card number + user defined offset + device offset */ if (type != CX18_ENC_STREAM_TYPE_MPG) { struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; - if (s_mpg->v4l2dev) - num = s_mpg->v4l2dev->num + cx18_stream_info[type].num_offset; + if (s_mpg->video_dev) + num = s_mpg->video_dev->num + + cx18_stream_info[type].num_offset; } + video_set_drvdata(s->video_dev, s); /* Register device. First try the desired minor, then any free one. */ - ret = video_register_device(s->v4l2dev, vfl_type, num); + ret = video_register_device(s->video_dev, vfl_type, num); if (ret < 0) { CX18_ERR("Couldn't register v4l2 device for %s kernel number %d\n", s->name, num); - video_device_release(s->v4l2dev); - s->v4l2dev = NULL; + video_device_release(s->video_dev); + s->video_dev = NULL; return ret; } - num = s->v4l2dev->num; + num = s->video_dev->num; switch (vfl_type) { case VFL_TYPE_GRABBER: @@ -312,9 +313,9 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister) cx->streams[type].dvb.enabled = false; } - vdev = cx->streams[type].v4l2dev; + vdev = cx->streams[type].video_dev; - cx->streams[type].v4l2dev = NULL; + cx->streams[type].video_dev = NULL; if (vdev == NULL) continue; @@ -346,46 +347,88 @@ static void cx18_vbi_setup(struct cx18_stream *s) } /* setup VBI registers */ - cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in); - - /* determine number of lines and total number of VBI bytes. - A raw line takes 1444 bytes: 4 byte SAV code + 2 * 720 - A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal - header, 42 data bytes + checksum (to be confirmed) */ + v4l2_subdev_call(cx->sd_av, video, s_fmt, &cx->vbi.in); + + /* + * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw + * VBI when the first analog capture channel starts, as once it starts + * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup + * (i.e. for the VBI capture channels). We also send it for each + * analog capture channel anyway just to make sure we get the proper + * behavior + */ if (raw) { lines = cx->vbi.count * 2; } else { - lines = cx->is_60hz ? 24 : 38; - if (cx->is_60hz) - lines += 2; + /* + * For 525/60 systems, according to the VIP 2 & BT.656 std: + * The EAV RP code's Field bit toggles on line 4, a few lines + * after the Vertcal Blank bit has already toggled. + * Tell the encoder to capture 21-4+1=18 lines per field, + * since we want lines 10 through 21. + * + * FIXME - revisit for 625/50 systems + */ + lines = cx->is_60hz ? (21 - 4 + 1) * 2 : 38; } - cx->vbi.enc_size = lines * - (raw ? cx->vbi.raw_size : cx->vbi.sliced_size); - data[0] = s->handle; /* Lines per field */ data[1] = (lines / 2) | ((lines / 2) << 16); /* bytes per line */ - data[2] = (raw ? cx->vbi.raw_decoder_line_size - : cx->vbi.sliced_decoder_line_size); + data[2] = (raw ? vbi_active_samples + : (cx->is_60hz ? vbi_hblank_samples_60Hz + : vbi_hblank_samples_50Hz)); /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ data[3] = 1; - /* Setup VBI for the cx25840 digitizer */ + /* + * Set the SAV/EAV RP codes to look for as start/stop points + * when in VIP-1.1 mode + */ if (raw) { + /* + * Start codes for beginning of "active" line in vertical blank + * 0x20 ( VerticalBlank ) + * 0x60 ( EvenField VerticalBlank ) + */ data[4] = 0x20602060; + /* + * End codes for end of "active" raw lines and regular lines + * 0x30 ( VerticalBlank HorizontalBlank) + * 0x70 ( EvenField VerticalBlank HorizontalBlank) + * 0x90 (Task HorizontalBlank) + * 0xd0 (Task EvenField HorizontalBlank) + */ data[5] = 0x307090d0; } else { + /* + * End codes for active video, we want data in the hblank region + * 0xb0 (Task 0 VerticalBlank HorizontalBlank) + * 0xf0 (Task EvenField VerticalBlank HorizontalBlank) + * + * Since the V bit is only allowed to toggle in the EAV RP code, + * just before the first active region line, these two + * are problematic: + * 0x90 (Task HorizontalBlank) + * 0xd0 (Task EvenField HorizontalBlank) + * + * We have set the digitzer such that we don't have to worry + * about these problem codes. + */ data[4] = 0xB0F0B0F0; + /* + * Start codes for beginning of active line in vertical blank + * 0xa0 (Task VerticalBlank ) + * 0xe0 (Task EvenField VerticalBlank ) + */ data[5] = 0xA0E0A0E0; } CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n", data[0], data[1], data[2], data[3], data[4], data[5]); - if (s->type == CX18_ENC_STREAM_TYPE_VBI) - cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); + cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); } struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, @@ -434,10 +477,10 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) u32 data[MAX_MB_ARGUMENTS]; struct cx18 *cx = s->cx; struct cx18_buffer *buf; - int ts = 0; int captype = 0; + struct cx18_api_func_private priv; - if (s->v4l2dev == NULL && s->dvb.enabled == 0) + if (s->video_dev == NULL && s->dvb.enabled == 0) return -EINVAL; CX18_DEBUG_INFO("Start encoder stream %s\n", s->name); @@ -453,7 +496,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) case CX18_ENC_STREAM_TYPE_TS: captype = CAPTURE_CHANNEL_TYPE_TS; - ts = 1; break; case CX18_ENC_STREAM_TYPE_YUV: captype = CAPTURE_CHANNEL_TYPE_YUV; @@ -462,8 +504,16 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) captype = CAPTURE_CHANNEL_TYPE_PCM; break; case CX18_ENC_STREAM_TYPE_VBI: +#ifdef CX18_ENCODER_PARSES_SLICED captype = cx18_raw_vbi(cx) ? CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI; +#else + /* + * Currently we set things up so that Sliced VBI from the + * digitizer is handled as Raw VBI by the encoder + */ + captype = CAPTURE_CHANNEL_TYPE_VBI; +#endif cx->vbi.frame = 0; cx->vbi.inserted_frame = 0; memset(cx->vbi.sliced_mpeg_size, @@ -473,10 +523,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) return -EINVAL; } - /* mute/unmute video */ - cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, - s->handle, !!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)); - /* Clear Streamoff flags in case left from last capture */ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); @@ -484,31 +530,63 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) s->handle = data[0]; cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); - if (atomic_read(&cx->ana_capturing) == 0 && !ts) { - struct cx18_api_func_private priv; - - /* Stuff from Windows, we don't know what it is */ + /* + * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and + * set up all the parameters, as it is not obvious which parameters the + * firmware shares across capture channel types and which it does not. + * + * Some of the cx18_vapi() calls below apply to only certain capture + * channel types. We're hoping there's no harm in calling most of them + * anyway, as long as the values are all consistent. Setting some + * shared parameters will have no effect once an analog capture channel + * has started streaming. + */ + if (captype != CAPTURE_CHANNEL_TYPE_TS) { cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0); cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1); - cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12); + /* + * Audio related reset according to + * Documentation/video4linux/cx2341x/fw-encoder-api.txt + */ + if (atomic_read(&cx->ana_capturing) == 0) + cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, + s->handle, 12); + + /* + * Number of lines for Field 1 & Field 2 according to + * Documentation/video4linux/cx2341x/fw-encoder-api.txt + * Field 1 is 312 for 625 line systems in BT.656 + * Field 2 is 313 for 625 line systems in BT.656 + */ cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3, - s->handle, cx->digitizer, cx->digitizer); + s->handle, 312, 313); - /* Setup VBI */ if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE) cx18_vbi_setup(s); - /* assign program index info. - Mask 7: select I/P/B, Num_req: 400 max */ + /* + * assign program index info. + * Mask 7: select I/P/B, Num_req: 400 max + * FIXME - currently we have this hardcoded as disabled + */ cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0); - /* Setup API for Stream */ + /* Call out to the common CX2341x API setup for user controls */ priv.cx = cx; priv.s = s; cx2341x_update(&priv, cx18_api_func, NULL, &cx->params); + + /* + * When starting a capture and we're set for radio, + * ensure the video is muted, despite the user control. + */ + if (!cx->params.video_mute && + test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) + cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, + (cx->params.video_mute_yuv << 8) | 1); } if (atomic_read(&cx->tot_capturing) == 0) { @@ -552,7 +630,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) } /* you're live! sit back and await interrupts :) */ - if (!ts) + if (captype != CAPTURE_CHANNEL_TYPE_TS) atomic_inc(&cx->ana_capturing); atomic_inc(&cx->tot_capturing); return 0; @@ -565,7 +643,7 @@ void cx18_stop_all_captures(struct cx18 *cx) for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) { struct cx18_stream *s = &cx->streams[i]; - if (s->v4l2dev == NULL && s->dvb.enabled == 0) + if (s->video_dev == NULL && s->dvb.enabled == 0) continue; if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) cx18_stop_v4l2_encode_stream(s, 0); @@ -577,7 +655,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) struct cx18 *cx = s->cx; unsigned long then; - if (s->v4l2dev == NULL && s->dvb.enabled == 0) + if (s->video_dev == NULL && s->dvb.enabled == 0) return -EINVAL; /* This function assumes that you are allowed to stop the capture @@ -629,7 +707,7 @@ u32 cx18_find_handle(struct cx18 *cx) for (i = 0; i < CX18_MAX_STREAMS; i++) { struct cx18_stream *s = &cx->streams[i]; - if (s->v4l2dev && (s->handle != CX18_INVALID_TASK_HANDLE)) + if (s->video_dev && (s->handle != CX18_INVALID_TASK_HANDLE)) return s->handle; } return CX18_INVALID_TASK_HANDLE; @@ -647,7 +725,7 @@ struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle) s = &cx->streams[i]; if (s->handle != handle) continue; - if (s->v4l2dev || s->dvb.enabled) + if (s->video_dev || s->dvb.enabled) return s; } return NULL; diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c index fb595bd..c2aef4a 100644 --- a/drivers/media/video/cx18/cx18-vbi.c +++ b/drivers/media/video/cx18/cx18-vbi.c @@ -25,7 +25,16 @@ #include "cx18-vbi.h" #include "cx18-ioctl.h" #include "cx18-queue.h" -#include "cx18-av-core.h" + +/* + * Raster Reference/Protection (RP) bytes, used in Start/End Active + * Video codes emitted from the digitzer in VIP 1.x mode, that flag the start + * of VBI sample or VBI ancilliary data regions in the digitial ratser line. + * + * Task FieldEven VerticalBlank HorizontalBlank 0 0 0 0 + */ +static const u8 raw_vbi_sav_rp[2] = { 0x20, 0x60 }; /* __V_, _FV_ */ +static const u8 sliced_vbi_eav_rp[2] = { 0xb0, 0xf0 }; /* T_VH, TFVH */ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) { @@ -34,10 +43,17 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) u32 linemask[2] = { 0, 0 }; unsigned short size; static const u8 mpeg_hdr_data[] = { - 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66, - 0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff, - 0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80, - 0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff + /* MPEG-2 Program Pack */ + 0x00, 0x00, 0x01, 0xba, /* Prog Pack start code */ + 0x44, 0x00, 0x0c, 0x66, 0x24, 0x01, /* SCR, SCR Ext, markers */ + 0x01, 0xd1, 0xd3, /* Mux Rate, markers */ + 0xfa, 0xff, 0xff, /* Res, Suff cnt, Stuff */ + /* MPEG-2 Private Stream 1 PES Packet */ + 0x00, 0x00, 0x01, 0xbd, /* Priv Stream 1 start */ + 0x00, 0x1a, /* length */ + 0x84, 0x80, 0x07, /* flags, hdr data len */ + 0x21, 0x00, 0x5d, 0x63, 0xa7, /* PTS, markers */ + 0xff, 0xff /* stuffing */ }; const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */ int idx = cx->vbi.frame % CX18_VBI_FRAMES; @@ -71,7 +87,9 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) memcpy(dst + sd + 4, dst + sd + 12, line * 43); size = 4 + ((43 * line + 3) & ~3); } else { - memcpy(dst + sd, "cx0", 4); + memcpy(dst + sd, "itv0", 4); + cpu_to_le32s(&linemask[0]); + cpu_to_le32s(&linemask[1]); memcpy(dst + sd + 4, &linemask[0], 8); size = 12 + ((43 * line + 3) & ~3); } @@ -86,58 +104,76 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) } /* Compress raw VBI format, removes leading SAV codes and surplus space - after the field. - Returns new compressed size. */ -static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size) + after the frame. Returns new compressed size. */ +static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size) { - u32 line_size = cx->vbi.raw_decoder_line_size; - u32 lines = cx->vbi.count; - u8 sav1 = cx->vbi.raw_decoder_sav_odd_field; - u8 sav2 = cx->vbi.raw_decoder_sav_even_field; + u32 line_size = vbi_active_samples; + u32 lines = cx->vbi.count * 2; u8 *q = buf; u8 *p; int i; + /* Skip the header */ + buf += hdr_size; + for (i = 0; i < lines; i++) { p = buf + i * line_size; /* Look for SAV code */ if (p[0] != 0xff || p[1] || p[2] || - (p[3] != sav1 && p[3] != sav2)) + (p[3] != raw_vbi_sav_rp[0] && + p[3] != raw_vbi_sav_rp[1])) break; - memcpy(q, p + 4, line_size - 4); - q += line_size - 4; + if (i == lines - 1) { + /* last line is hdr_size bytes short - extrapolate it */ + memcpy(q, p + 4, line_size - 4 - hdr_size); + q += line_size - 4 - hdr_size; + p += line_size - hdr_size - 1; + memset(q, (int) *p, hdr_size); + } else { + memcpy(q, p + 4, line_size - 4); + q += line_size - 4; + } } return lines * (line_size - 4); } - -/* Compressed VBI format, all found sliced blocks put next to one another - Returns new compressed size */ -static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf, - u32 size, u8 sav) +static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size, + const u32 hdr_size) { - u32 line_size = cx->vbi.sliced_decoder_line_size; struct v4l2_decode_vbi_line vbi; int i; + u32 line = 0; + u32 line_size = cx->is_60hz ? vbi_hblank_samples_60Hz + : vbi_hblank_samples_50Hz; /* find the first valid line */ - for (i = 0; i < size; i++, buf++) { - if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav) + for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) { + if (buf[0] == 0xff && !buf[1] && !buf[2] && + (buf[3] == sliced_vbi_eav_rp[0] || + buf[3] == sliced_vbi_eav_rp[1])) break; } - size -= i; + /* + * The last line is short by hdr_size bytes, but for the remaining + * checks against size, we pretend that it is not, by counting the + * header bytes we knowingly skipped + */ + size -= (i - hdr_size); if (size < line_size) return line; + for (i = 0; i < size / line_size; i++) { u8 *p = buf + i * line_size; - /* Look for SAV code */ - if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) + /* Look for EAV code */ + if (p[0] != 0xff || p[1] || p[2] || + (p[3] != sliced_vbi_eav_rp[0] && + p[3] != sliced_vbi_eav_rp[1])) continue; vbi.p = p + 4; - cx18_av_cmd(cx, VIDIOC_INT_DECODE_VBI_LINE, &vbi); + v4l2_subdev_call(cx->sd_av, video, decode_vbi_line, &vbi); if (vbi.type) { cx->vbi.sliced_data[line].id = vbi.type; cx->vbi.sliced_data[line].field = vbi.is_second_field; @@ -150,51 +186,56 @@ static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf, } void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, - u64 pts_stamp, int streamtype) + int streamtype) { + /* + * The CX23418 provides a 12 byte header in its raw VBI buffers to us: + * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp] + */ + struct vbi_data_hdr { + __be32 magic; + __be32 unknown; + __be32 pts; + } *hdr = (struct vbi_data_hdr *) buf->buf; + u8 *p = (u8 *) buf->buf; u32 size = buf->bytesused; + u32 pts; int lines; if (streamtype != CX18_ENC_STREAM_TYPE_VBI) return; + /* + * The CX23418 sends us data that is 32 bit little-endian swapped, + * but we want the raw VBI bytes in the order they were in the raster + * line. This has a side effect of making the header big endian + */ + cx18_buf_swap(buf); + /* Raw VBI data */ if (cx18_raw_vbi(cx)) { - u8 type; - - cx18_buf_swap(buf); - - /* Skip 12 bytes of header that gets stuffed in */ - size -= 12; - memcpy(p, &buf->buf[12], size); - type = p[3]; - size = buf->bytesused = compress_raw_buf(cx, p, size); + size = buf->bytesused = + compress_raw_buf(cx, p, size, sizeof(struct vbi_data_hdr)); - /* second field of the frame? */ - if (type == cx->vbi.raw_decoder_sav_even_field) { - /* Dirty hack needed for backwards - compatibility of old VBI software. */ - p += size - 4; - memcpy(p, &cx->vbi.frame, 4); - cx->vbi.frame++; - } + /* + * Hack needed for compatibility with old VBI software. + * Write the frame # at the last 4 bytes of the frame + */ + p += size - 4; + memcpy(p, &cx->vbi.frame, 4); + cx->vbi.frame++; return; } /* Sliced VBI data with data insertion */ - cx18_buf_swap(buf); - /* first field */ - lines = compress_sliced_buf(cx, 0, p, size / 2, - cx->vbi.sliced_decoder_sav_odd_field); - /* second field */ - /* experimentation shows that the second half does not always - begin at the exact address. So start a bit earlier - (hence 32). */ - lines = compress_sliced_buf(cx, lines, p + size / 2 - 32, - size / 2 + 32, cx->vbi.sliced_decoder_sav_even_field); + pts = (be32_to_cpu(hdr->magic) == 0x3fffffff) ? be32_to_cpu(hdr->pts) + : 0; + + lines = compress_sliced_buf(cx, p, size, sizeof(struct vbi_data_hdr)); + /* always return at least one empty line */ if (lines == 0) { cx->vbi.sliced_data[0].id = 0; @@ -206,6 +247,6 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, memcpy(p, &cx->vbi.sliced_data[0], size); if (cx->vbi.insert_mpeg) - copy_vbi_data(cx, lines, pts_stamp); + copy_vbi_data(cx, lines, pts); cx->vbi.frame++; } diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h index c56ff7d..e7e1ae4 100644 --- a/drivers/media/video/cx18/cx18-vbi.h +++ b/drivers/media/video/cx18/cx18-vbi.h @@ -22,5 +22,5 @@ */ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, - u64 pts_stamp, int streamtype); + int streamtype); int cx18_used_line(struct cx18 *cx, int line, int field); diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index 84c0ff1..bd9bd44 100644 --- a/drivers/media/video/cx18/cx18-version.h +++ b/drivers/media/video/cx18/cx18-version.h @@ -24,8 +24,8 @@ #define CX18_DRIVER_NAME "cx18" #define CX18_DRIVER_VERSION_MAJOR 1 -#define CX18_DRIVER_VERSION_MINOR 0 -#define CX18_DRIVER_VERSION_PATCHLEVEL 4 +#define CX18_DRIVER_VERSION_MINOR 1 +#define CX18_DRIVER_VERSION_PATCHLEVEL 0 #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) #define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \ diff --git a/drivers/media/video/cx18/cx18-video.c b/drivers/media/video/cx18/cx18-video.c index 2e5c419..6fdaded 100644 --- a/drivers/media/video/cx18/cx18-video.c +++ b/drivers/media/video/cx18/cx18-video.c @@ -21,7 +21,6 @@ #include "cx18-driver.h" #include "cx18-video.h" -#include "cx18-av-core.h" #include "cx18-cards.h" void cx18_video_set_io(struct cx18 *cx) @@ -32,7 +31,7 @@ void cx18_video_set_io(struct cx18 *cx) route.input = cx->card->video_inputs[inp].video_input; route.output = 0; - cx18_av_cmd(cx, VIDIOC_INT_S_VIDEO_ROUTING, &route); + v4l2_subdev_call(cx->sd_av, video, s_routing, &route); type = cx->card->video_inputs[inp].video_type; diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h index 601f3a2..9956abf 100644 --- a/drivers/media/video/cx18/cx23418.h +++ b/drivers/media/video/cx18/cx23418.h @@ -56,6 +56,22 @@ #define APU_CMD_MASK 0x10000000 #define APU_CMD_MASK_ACK (APU_CMD_MASK | 0x80000000) +#define CX18_APU_ENCODING_METHOD_MPEG (0 << 28) +#define CX18_APU_ENCODING_METHOD_AC3 (1 << 28) + +/* Description: Command APU to start audio + IN[0] - audio parameters (same as CX18_CPU_SET_AUDIO_PARAMETERS?) + IN[1] - caller buffer address, or 0 + ReturnCode - ??? */ +#define CX18_APU_START (APU_CMD_MASK | 0x01) + +/* Description: Command APU to stop audio + IN[0] - encoding method to stop + ReturnCode - ??? */ +#define CX18_APU_STOP (APU_CMD_MASK | 0x02) + +/* Description: Command APU to reset the AI + ReturnCode - ??? */ #define CX18_APU_RESETAI (APU_CMD_MASK | 0x05) /* Description: This command indicates that a Memory Descriptor List has been diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index cbbe47f..8ded529 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -1,5 +1,5 @@ /* - * cx2341x - generic code for cx23415/6 based devices + * cx2341x - generic code for cx23415/6/8 based devices * * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl> * @@ -30,7 +30,7 @@ #include <media/cx2341x.h> #include <media/v4l2-common.h> -MODULE_DESCRIPTION("cx23415/6 driver"); +MODULE_DESCRIPTION("cx23415/6/8 driver"); MODULE_AUTHOR("Hans Verkuil"); MODULE_LICENSE("GPL"); @@ -38,6 +38,7 @@ static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); +/* Must be sorted from low to high control ID! */ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_CLASS, V4L2_CID_MPEG_STREAM_TYPE, @@ -50,6 +51,7 @@ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_AUDIO_EMPHASIS, V4L2_CID_MPEG_AUDIO_CRC, V4L2_CID_MPEG_AUDIO_MUTE, + V4L2_CID_MPEG_AUDIO_AC3_BITRATE, V4L2_CID_MPEG_VIDEO_ENCODING, V4L2_CID_MPEG_VIDEO_ASPECT, V4L2_CID_MPEG_VIDEO_B_FRAMES, @@ -94,6 +96,7 @@ static const struct cx2341x_mpeg_params default_params = { .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K, + .audio_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_224K, .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO, .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, @@ -148,6 +151,9 @@ static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_AUDIO_L2_BITRATE: ctrl->value = params->audio_l2_bitrate; break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + ctrl->value = params->audio_ac3_bitrate; + break; case V4L2_CID_MPEG_AUDIO_MODE: ctrl->value = params->audio_mode; break; @@ -256,6 +262,12 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy, params->audio_sampling_freq = ctrl->value; break; case V4L2_CID_MPEG_AUDIO_ENCODING: + if (busy) + return -EBUSY; + if (params->capabilities & CX2341X_CAP_HAS_AC3) + if (ctrl->value != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && + ctrl->value != V4L2_MPEG_AUDIO_ENCODING_AC3) + return -ERANGE; params->audio_encoding = ctrl->value; break; case V4L2_CID_MPEG_AUDIO_L2_BITRATE: @@ -263,6 +275,13 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy, return -EBUSY; params->audio_l2_bitrate = ctrl->value; break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (busy) + return -EBUSY; + if (!(params->capabilities & CX2341X_CAP_HAS_AC3)) + return -EINVAL; + params->audio_ac3_bitrate = ctrl->value; + break; case V4L2_CID_MPEG_AUDIO_MODE: params->audio_mode = ctrl->value; break; @@ -481,29 +500,106 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, int err; switch (qctrl->id) { + case V4L2_CID_MPEG_STREAM_TYPE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS, + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1, + V4L2_MPEG_STREAM_TYPE_MPEG2_PS); + + case V4L2_CID_MPEG_STREAM_VBI_FMT: + if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI) + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1, + V4L2_MPEG_STREAM_VBI_FMT_NONE); + return cx2341x_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_VBI_FMT_NONE, + V4L2_MPEG_STREAM_VBI_FMT_NONE, 1, + default_params.stream_vbi_fmt); + + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (params->capabilities & CX2341X_CAP_HAS_AC3) { + /* + * The state of L2 & AC3 bitrate controls can change + * when this control changes, but v4l2_ctrl_query_fill() + * already sets V4L2_CTRL_FLAG_UPDATE for + * V4L2_CID_MPEG_AUDIO_ENCODING, so we don't here. + */ + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_AC3, 1, + default_params.audio_encoding); + } + return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_ENCODING_LAYER_2, V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1, default_params.audio_encoding); case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - return v4l2_ctrl_query_fill(qctrl, + err = v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_AUDIO_L2_BITRATE_192K, V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, default_params.audio_l2_bitrate); + if (err) + return err; + if (params->capabilities & CX2341X_CAP_HAS_AC3 && + params->audio_encoding != V4L2_MPEG_AUDIO_ENCODING_LAYER_2) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return 0; - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: - return -EINVAL; + case V4L2_CID_MPEG_AUDIO_MODE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_MODE_STEREO, + V4L2_MPEG_AUDIO_MODE_MONO, 1, + V4L2_MPEG_AUDIO_MODE_STEREO); case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); if (err == 0 && params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return err; + case V4L2_CID_MPEG_AUDIO_EMPHASIS: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_EMPHASIS_NONE, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1, + V4L2_MPEG_AUDIO_EMPHASIS_NONE); + + case V4L2_CID_MPEG_AUDIO_CRC: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_CRC_NONE, + V4L2_MPEG_AUDIO_CRC_CRC16, 1, + V4L2_MPEG_AUDIO_CRC_NONE); + + case V4L2_CID_MPEG_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_AC3_BITRATE_48K, + V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 1, + default_params.audio_ac3_bitrate); + if (err) + return err; + if (params->capabilities & CX2341X_CAP_HAS_AC3) { + if (params->audio_encoding != + V4L2_MPEG_AUDIO_ENCODING_AC3) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + } else + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + case V4L2_CID_MPEG_VIDEO_ENCODING: /* this setting is read-only for the cx2341x since the V4L2_CID_MPEG_STREAM_TYPE really determines the @@ -516,32 +612,51 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; return err; + case V4L2_CID_MPEG_VIDEO_ASPECT: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ASPECT_1x1, + V4L2_MPEG_VIDEO_ASPECT_221x100, 1, + V4L2_MPEG_VIDEO_ASPECT_4x3); + + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2); + + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, + params->is_50hz ? 12 : 15); + + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); if (err == 0 && params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return err; + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); if (err == 0 && params->video_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return err; - case V4L2_CID_MPEG_STREAM_VBI_FMT: - if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI) - return v4l2_ctrl_query_fill_std(qctrl); - return cx2341x_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_VBI_FMT_NONE, - V4L2_MPEG_STREAM_VBI_FMT_NONE, 1, - default_params.stream_vbi_fmt); + case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: + return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, - params->is_50hz ? 12 : 15); + case V4L2_CID_MPEG_VIDEO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ + return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); /* CX23415/6 specific */ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE: @@ -643,7 +758,7 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, default_params.stream_insert_nav_packets); default: - return v4l2_ctrl_query_fill_std(qctrl); + return -EINVAL; } } @@ -671,6 +786,15 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) NULL }; + static const char *mpeg_audio_encoding_l2_ac3[] = { + "", + "MPEG-1/2 Layer II", + "", + "", + "AC-3", + NULL + }; + static const char *cx2341x_video_spatial_filter_mode_menu[] = { "Manual", "Auto", @@ -711,6 +835,9 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) case V4L2_CID_MPEG_STREAM_TYPE: return (p->capabilities & CX2341X_CAP_HAS_TS) ? mpeg_stream_type_with_ts : mpeg_stream_type_without_ts; + case V4L2_CID_MPEG_AUDIO_ENCODING: + return (p->capabilities & CX2341X_CAP_HAS_AC3) ? + mpeg_audio_encoding_l2_ac3 : v4l2_ctrl_get_menu(id); case V4L2_CID_MPEG_AUDIO_L1_BITRATE: case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return NULL; @@ -730,16 +857,34 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id) } EXPORT_SYMBOL(cx2341x_ctrl_get_menu); +/* definitions for audio properties bits 29-28 */ +#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0 +#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1 +#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2 + static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params) { - params->audio_properties = (params->audio_sampling_freq << 0) | - ((3 - params->audio_encoding) << 2) | - ((1 + params->audio_l2_bitrate) << 4) | + params->audio_properties = + (params->audio_sampling_freq << 0) | (params->audio_mode << 8) | (params->audio_mode_extension << 10) | (((params->audio_emphasis == V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17) ? 3 : params->audio_emphasis) << 12) | (params->audio_crc << 14); + + if ((params->capabilities & CX2341X_CAP_HAS_AC3) && + params->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { + params->audio_properties |= + /* Not sure if this MPEG Layer II setting is required */ + ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) | + (params->audio_ac3_bitrate << 4) | + (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28); + } else { + /* Assuming MPEG Layer II */ + params->audio_properties |= + ((3 - params->audio_encoding) << 2) | + ((1 + params->audio_l2_bitrate) << 4); + } } int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy, @@ -1022,7 +1167,10 @@ void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix) prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE), + cx2341x_menu_item(p, + p->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3 + ? V4L2_CID_MPEG_AUDIO_AC3_BITRATE + : V4L2_CID_MPEG_AUDIO_L2_BITRATE), cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE), p->audio_mute ? " (muted)" : ""); if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index 00f1e2e..fd3fc3e 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -15,12 +15,15 @@ config VIDEO_CX23885 select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE - select DVB_TDA10048 if !DVB_FE_CUSTOMIZE - select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE - select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE - select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE - select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE + select DVB_TDA10048 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_STV6110 if !DVB_FE_CUSTOMISE + select DVB_STV0900 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE ---help--- This is a video4linux driver for Conexant 23885 based TV cards. diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile index 29c23b4..ab8ea35 100644 --- a/drivers/media/video/cx23885/Makefile +++ b/drivers/media/video/cx23885/Makefile @@ -1,4 +1,6 @@ -cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o +cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ + cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \ + netup-init.o cimax2.o netup-eeprom.o obj-$(CONFIG_VIDEO_CX23885) += cx23885.o diff --git a/drivers/media/video/cx23885/cimax2.c b/drivers/media/video/cx23885/cimax2.c new file mode 100644 index 0000000..9a65369 --- /dev/null +++ b/drivers/media/video/cx23885/cimax2.c @@ -0,0 +1,472 @@ +/* + * cimax2.c + * + * CIMax2(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card + * + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx23885.h" +#include "dvb_ca_en50221.h" +/**** Bit definitions for MC417_RWD and MC417_OEN registers *** + bits 31-16 ++-----------+ +| Reserved | ++-----------+ + bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 ++-------+-------+-------+-------+-------+-------+-------+-------+ +| WR# | RD# | | ACK# | ADHI | ADLO | CS1# | CS0# | ++-------+-------+-------+-------+-------+-------+-------+-------+ + bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 ++-------+-------+-------+-------+-------+-------+-------+-------+ +| DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0| ++-------+-------+-------+-------+-------+-------+-------+-------+ +***/ +/* MC417 */ +#define NETUP_DATA 0x000000ff +#define NETUP_WR 0x00008000 +#define NETUP_RD 0x00004000 +#define NETUP_ACK 0x00001000 +#define NETUP_ADHI 0x00000800 +#define NETUP_ADLO 0x00000400 +#define NETUP_CS1 0x00000200 +#define NETUP_CS0 0x00000100 +#define NETUP_EN_ALL 0x00001000 +#define NETUP_CTRL_OFF (NETUP_CS1 | NETUP_CS0 | NETUP_WR | NETUP_RD) +#define NETUP_CI_CTL 0x04 +#define NETUP_CI_RD 1 + + +static unsigned int ci_dbg; +module_param(ci_dbg, int, 0644); +MODULE_PARM_DESC(ci_dbg, "Enable CI debugging"); + +#define ci_dbg_print(args...) \ + do { \ + if (ci_dbg) \ + printk(KERN_DEBUG args); \ + } while (0) + +/* stores all private variables for communication with CI */ +struct netup_ci_state { + struct dvb_ca_en50221 ca; + struct mutex ca_mutex; + struct i2c_adapter *i2c_adap; + u8 ci_i2c_addr; + int status; + struct work_struct work; + void *priv; +}; + +struct mutex gpio_mutex;/* Two CiMax's uses same GPIO lines */ + +int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg, + u8 *buf, int len) +{ + int ret; + struct i2c_msg msg[] = { + { + .addr = addr, + .flags = 0, + .buf = ®, + .len = 1 + }, { + .addr = addr, + .flags = I2C_M_RD, + .buf = buf, + .len = len + } + }; + + ret = i2c_transfer(i2c_adap, msg, 2); + + if (ret != 2) { + ci_dbg_print("%s: i2c read error, Reg = 0x%02x, Status = %d\n", + __func__, reg, ret); + + return -1; + } + + ci_dbg_print("%s: i2c read Addr=0x%04x, Reg = 0x%02x, data = %02x\n", + __func__, addr, reg, buf[0]); + + return 0; +} + +int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg, + u8 *buf, int len) +{ + int ret; + u8 buffer[len + 1]; + + struct i2c_msg msg = { + .addr = addr, + .flags = 0, + .buf = &buffer[0], + .len = len + 1 + }; + + buffer[0] = reg; + memcpy(&buffer[1], buf, len); + + ret = i2c_transfer(i2c_adap, &msg, 1); + + if (ret != 1) { + ci_dbg_print("%s: i2c write error, Reg=[0x%02x], Status=%d\n", + __func__, reg, ret); + return -1; + } + + return 0; +} + +int netup_ci_get_mem(struct cx23885_dev *dev) +{ + int mem; + unsigned long timeout = jiffies + msecs_to_jiffies(1); + + for (;;) { + mem = cx_read(MC417_RWD); + if ((mem & NETUP_ACK) == 0) + break; + if (time_after(jiffies, timeout)) + break; + udelay(1); + } + + cx_set(MC417_RWD, NETUP_CTRL_OFF); + + return mem & 0xff; +} + +int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, + u8 flag, u8 read, int addr, u8 data) +{ + struct netup_ci_state *state = en50221->data; + struct cx23885_tsport *port = state->priv; + struct cx23885_dev *dev = port->dev; + + u8 store; + int mem; + int ret; + + if (0 != slot) + return -EINVAL; + + ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr, + 0, &store, 1); + if (ret != 0) + return ret; + + store &= ~0x0c; + store |= flag; + + ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, + 0, &store, 1); + if (ret != 0) + return ret; + + mutex_lock(&gpio_mutex); + + /* write addr */ + cx_write(MC417_OEN, NETUP_EN_ALL); + cx_write(MC417_RWD, NETUP_CTRL_OFF | + NETUP_ADLO | (0xff & addr)); + cx_clear(MC417_RWD, NETUP_ADLO); + cx_write(MC417_RWD, NETUP_CTRL_OFF | + NETUP_ADHI | (0xff & (addr >> 8))); + cx_clear(MC417_RWD, NETUP_ADHI); + + if (read) /* data in */ + cx_write(MC417_OEN, NETUP_EN_ALL | NETUP_DATA); + else /* data out */ + cx_write(MC417_RWD, NETUP_CTRL_OFF | data); + + /* choose chip */ + cx_clear(MC417_RWD, + (state->ci_i2c_addr == 0x40) ? NETUP_CS0 : NETUP_CS1); + /* read/write */ + cx_clear(MC417_RWD, (read) ? NETUP_RD : NETUP_WR); + mem = netup_ci_get_mem(dev); + + mutex_unlock(&gpio_mutex); + + if (!read) + if (mem < 0) + return -EREMOTEIO; + + ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__, + (read) ? "read" : "write", addr, + (flag == NETUP_CI_CTL) ? "ctl" : "mem", + (read) ? mem : data); + + if (read) + return mem; + + return 0; +} + +int netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr) +{ + return netup_ci_op_cam(en50221, slot, 0, NETUP_CI_RD, addr, 0); +} + +int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr, u8 data) +{ + return netup_ci_op_cam(en50221, slot, 0, 0, addr, data); +} + +int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr) +{ + return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL, + NETUP_CI_RD, addr, 0); +} + +int netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, + u8 addr, u8 data) +{ + return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL, 0, addr, data); +} + +int netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) +{ + struct netup_ci_state *state = en50221->data; + u8 buf = 0x80; + int ret; + + if (0 != slot) + return -EINVAL; + + udelay(500); + ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, + 0, &buf, 1); + + if (ret != 0) + return ret; + + udelay(500); + + buf = 0x00; + ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, + 0, &buf, 1); + + msleep(1000); + dvb_ca_en50221_camready_irq(&state->ca, 0); + + return 0; + +} + +int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) +{ + /* not implemented */ + return 0; +} + +int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot) +{ + struct netup_ci_state *state = en50221->data; + u8 buf = 0x60; + + if (0 != slot) + return -EINVAL; + + return netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, + 0, &buf, 1); +} + +/* work handler */ +static void netup_read_ci_status(struct work_struct *work) +{ + struct netup_ci_state *state = + container_of(work, struct netup_ci_state, work); + u8 buf[33]; + int ret; + + ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr, + 0, &buf[0], 33); + + if (ret != 0) + return; + + ci_dbg_print("%s: Slot Status Addr=[0x%04x], Reg=[0x%02x], data=%02x, " + "TS config = %02x\n", __func__, state->ci_i2c_addr, 0, buf[0], + buf[32]); + + if (buf[0] && 1) + state->status = DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + else + state->status = 0; +} + +/* CI irq handler */ +int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status) +{ + struct cx23885_tsport *port = NULL; + struct netup_ci_state *state = NULL; + + if (pci_status & PCI_MSK_GPIO0) + port = &dev->ts1; + else if (pci_status & PCI_MSK_GPIO1) + port = &dev->ts2; + else /* who calls ? */ + return 0; + + state = port->port_priv; + + schedule_work(&state->work); + + return 1; +} + +int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open) +{ + struct netup_ci_state *state = en50221->data; + + if (0 != slot) + return -EINVAL; + + return state->status; +} + +int netup_ci_init(struct cx23885_tsport *port) +{ + struct netup_ci_state *state; + u8 cimax_init[34] = { + 0x00, /* module A control*/ + 0x00, /* auto select mask high A */ + 0x00, /* auto select mask low A */ + 0x00, /* auto select pattern high A */ + 0x00, /* auto select pattern low A */ + 0x44, /* memory access time A */ + 0x00, /* invert input A */ + 0x00, /* RFU */ + 0x00, /* RFU */ + 0x00, /* module B control*/ + 0x00, /* auto select mask high B */ + 0x00, /* auto select mask low B */ + 0x00, /* auto select pattern high B */ + 0x00, /* auto select pattern low B */ + 0x44, /* memory access time B */ + 0x00, /* invert input B */ + 0x00, /* RFU */ + 0x00, /* RFU */ + 0x00, /* auto select mask high Ext */ + 0x00, /* auto select mask low Ext */ + 0x00, /* auto select pattern high Ext */ + 0x00, /* auto select pattern low Ext */ + 0x00, /* RFU */ + 0x02, /* destination - module A */ + 0x01, /* power on (use it like store place) */ + 0x00, /* RFU */ + 0x00, /* int status read only */ + 0x01, /* all int unmasked */ + 0x04, /* int config */ + 0x00, /* USCG1 */ + 0x04, /* ack active low */ + 0x00, /* LOCK = 0 */ + 0x33, /* serial mode, rising in, rising out, MSB first*/ + 0x31, /* syncronization */ + }; + int ret; + + ci_dbg_print("%s\n", __func__); + state = kzalloc(sizeof(struct netup_ci_state), GFP_KERNEL); + if (!state) { + ci_dbg_print("%s: Unable create CI structure!\n", __func__); + ret = -ENOMEM; + goto err; + } + + port->port_priv = state; + + switch (port->nr) { + case 1: + state->ci_i2c_addr = 0x40; + mutex_init(&gpio_mutex); + break; + case 2: + state->ci_i2c_addr = 0x41; + break; + } + + state->i2c_adap = &port->dev->i2c_bus[0].i2c_adap; + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = netup_ci_read_attribute_mem; + state->ca.write_attribute_mem = netup_ci_write_attribute_mem; + state->ca.read_cam_control = netup_ci_read_cam_ctl; + state->ca.write_cam_control = netup_ci_write_cam_ctl; + state->ca.slot_reset = netup_ci_slot_reset; + state->ca.slot_shutdown = netup_ci_slot_shutdown; + state->ca.slot_ts_enable = netup_ci_slot_ts_ctl; + state->ca.poll_slot_status = netup_poll_ci_slot_status; + state->ca.data = state; + state->priv = port; + + ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, + 0, &cimax_init[0], 34); + /* lock registers */ + ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, + 0x1f, &cimax_init[0x18], 1); + /* power on slots */ + ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, + 0x18, &cimax_init[0x18], 1); + + if (0 != ret) + goto err; + + ret = dvb_ca_en50221_init(&port->frontends.adapter, + &state->ca, + /* flags */ 0, + /* n_slots */ 1); + if (0 != ret) + goto err; + + INIT_WORK(&state->work, netup_read_ci_status); + + ci_dbg_print("%s: CI initialized!\n", __func__); + + return 0; +err: + ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret); + kfree(state); + return ret; +} + +void netup_ci_exit(struct cx23885_tsport *port) +{ + struct netup_ci_state *state; + + if (NULL == port) + return; + + state = (struct netup_ci_state *)port->port_priv; + if (NULL == state) + return; + + if (NULL == state->ca.data) + return; + + dvb_ca_en50221_release(&state->ca); + kfree(state); +} diff --git a/drivers/media/video/cx23885/cimax2.h b/drivers/media/video/cx23885/cimax2.h new file mode 100644 index 0000000..518744a --- /dev/null +++ b/drivers/media/video/cx23885/cimax2.h @@ -0,0 +1,47 @@ +/* + * cimax2.h + * + * CIMax(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card + * + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef CIMAX2_H +#define CIMAX2_H +#include "dvb_ca_en50221.h" + +extern int netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr); +extern int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, + int slot, int addr, u8 data); +extern int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, + int slot, u8 addr); +extern int netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, + int slot, u8 addr, u8 data); +extern int netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot); +extern int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot); +extern int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot); +extern int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status); +extern int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, + int slot, int open); +extern int netup_ci_init(struct cx23885_tsport *port); +extern void netup_ci_exit(struct cx23885_tsport *port); + +#endif diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index bfe2584..6f5df90 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -896,7 +896,7 @@ static int cx23885_load_firmware(struct cx23885_dev *dev) if (retval != 0) { printk(KERN_ERR "ERROR: Hotplug firmware request failed (%s).\n", - CX2341X_FIRM_ENC_FILENAME); + CX23885_FIRM_IMAGE_NAME); printk(KERN_ERR "Please fix your hotplug setup, the board will " "not work without firmware loaded!\n"); return -1; @@ -1198,21 +1198,16 @@ static int vidioc_enum_input(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; struct cx23885_input *input; - unsigned int n; + int n; - n = i->index; - - if (n >= 4) + if (i->index >= 4) return -EINVAL; - input = &cx23885_boards[dev->board].input[n]; + input = &cx23885_boards[dev->board].input[i->index]; if (input->type == 0) return -EINVAL; - memset(i, 0, sizeof(*i)); - i->index = n; - /* FIXME * strcpy(i->name, input->name); */ strcpy(i->name, "unset"); @@ -1255,10 +1250,8 @@ static int vidioc_g_tuner(struct file *file, void *priv, return -EINVAL; if (0 != t->index) return -EINVAL; - memset(t, 0, sizeof(*t)); strcpy(t->name, "Television"); - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_TUNER, t); - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t); + call_all(dev, tuner, g_tuner, t); dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type); @@ -1275,7 +1268,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, return -EINVAL; /* Update the A/V core */ - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_TUNER, t); + call_all(dev, tuner, s_tuner, t); return 0; } @@ -1286,14 +1279,12 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; - memset(f, 0, sizeof(*f)); if (UNSET == dev->tuner_type) return -EINVAL; f->type = V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; - /* Assumption that tuner is always on bus 1 */ - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_FREQUENCY, f); + call_all(dev, tuner, g_frequency, f); return 0; } @@ -1320,8 +1311,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, return -EINVAL; dev->freq = f->frequency; - /* Assumption that tuner is always on bus 1 */ - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, f); + call_all(dev, tuner, s_frequency, f); cx23885_initialize_codec(dev); @@ -1335,7 +1325,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, struct cx23885_dev *dev = fh->dev; /* Update the A/V core */ - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_CTRL, ctl); + call_all(dev, core, s_ctrl, ctl); return 0; } @@ -1346,7 +1336,6 @@ static int vidioc_querycap(struct file *file, void *priv, struct cx23885_dev *dev = fh->dev; struct cx23885_tsport *tsport = &dev->ts1; - memset(cap, 0, sizeof(*cap)); strcpy(cap->driver, dev->name); strlcpy(cap->card, cx23885_boards[tsport->dev->board].name, sizeof(cap->card)); @@ -1366,16 +1355,10 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - int index; - - index = f->index; - if (index != 0) + if (f->index != 0) return -EINVAL; - memset(f, 0, sizeof(*f)); - f->index = index; strlcpy(f->description, "MPEG", sizeof(f->description)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->pixelformat = V4L2_PIX_FMT_MPEG; return 0; @@ -1387,8 +1370,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; - memset(f, 0, sizeof(*f)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = @@ -1408,12 +1389,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; - f->fmt.pix.sizeimage = f->fmt.pix.colorspace = 0; dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", dev->ts1.width, dev->ts1.height, fh->mpegq.field); @@ -1426,7 +1405,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = @@ -1543,12 +1521,7 @@ static int vidioc_log_status(struct file *file, void *priv) printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", dev->name); - cx23885_call_i2c_clients(&dev->i2c_bus[0], VIDIOC_LOG_STATUS, - NULL); - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_LOG_STATUS, - NULL); - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_LOG_STATUS, - NULL); + call_all(dev, core, log_status); cx2341x_log_status(&dev->mpeg_params, name); printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index caa098b..5e4b7e7 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -27,6 +27,7 @@ #include "cx23885.h" #include "tuner-xc2028.h" +#include "netup-init.h" /* ------------------------------------------------------------------ */ /* board config info */ @@ -162,6 +163,24 @@ struct cx23885_board cx23885_boards[] = { .name = "Compro VideoMate E650F", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_TBS_6920] = { + .name = "TurboSight TBS 6920", + .portb = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_TEVII_S470] = { + .name = "TeVii S470", + .portb = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_DVBWORLD_2005] = { + .name = "DVBWorld DVB-S2 2005", + .portb = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_NETUP_DUAL_DVBS2_CI] = { + .cimax = 1, + .name = "NetUP Dual DVB-S2 CI", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -245,6 +264,22 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x185b, .subdevice = 0xe800, .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F, + }, { + .subvendor = 0x6920, + .subdevice = 0x8888, + .card = CX23885_BOARD_TBS_6920, + }, { + .subvendor = 0xd470, + .subdevice = 0x9022, + .card = CX23885_BOARD_TEVII_S470, + }, { + .subvendor = 0x0001, + .subdevice = 0x2005, + .card = CX23885_BOARD_DVBWORLD_2005, + }, { + .subvendor = 0x1b55, + .subdevice = 0x2a2c, + .card = CX23885_BOARD_NETUP_DUAL_DVBS2_CI, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -552,6 +587,38 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) mdelay(20); cx_set(GP0_IO, 0x00040004); break; + case CX23885_BOARD_TBS_6920: + case CX23885_BOARD_TEVII_S470: + cx_write(MC417_CTL, 0x00000036); + cx_write(MC417_OEN, 0x00001000); + cx_write(MC417_RWD, 0x00001800); + break; + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + /* GPIO-0 INTA from CiMax1 + GPIO-1 INTB from CiMax2 + GPIO-2 reset chips + GPIO-3 to GPIO-10 data/addr for CA + GPIO-11 ~CS0 to CiMax1 + GPIO-12 ~CS1 to CiMax2 + GPIO-13 ADL0 load LSB addr + GPIO-14 ADL1 load MSB addr + GPIO-15 ~RDY from CiMax + GPIO-17 ~RD to CiMax + GPIO-18 ~WR to CiMax + */ + cx_set(GP0_IO, 0x00040000); /* GPIO as out */ + /* GPIO1 and GPIO2 as INTA and INTB from CiMaxes, reset low */ + cx_clear(GP0_IO, 0x00030004); + mdelay(100);/* reset delay */ + cx_set(GP0_IO, 0x00040004); /* GPIO as out, reset high */ + cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */ + /* GPIO-15 IN as ~ACK, rest as OUT */ + cx_write(MC417_OEN, 0x00001000); + /* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */ + cx_write(MC417_RWD, 0x0000c300); + /* enable irq */ + cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ + break; } } @@ -632,6 +699,21 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_TBS_6920: + case CX23885_BOARD_DVBWORLD_2005: + 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; + break; + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: @@ -656,7 +738,17 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1700: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: - request_module("cx25840"); + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->i2c_bus[2].i2c_adap, + "cx25840", "cx25840", 0x88 >> 1); + v4l2_subdev_call(dev->sd_cx25840, core, init, 0); + break; + } + + /* AUX-PLL 27MHz CLK */ + switch (dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + netup_initialize(dev); break; } } diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index 8f6fb2a..dc7fff2 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -31,6 +31,7 @@ #include <asm/div64.h> #include "cx23885.h" +#include "cimax2.h" MODULE_DESCRIPTION("Driver for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); @@ -791,6 +792,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) dev->pci_bus = dev->pci->bus->number; dev->pci_slot = PCI_SLOT(dev->pci->devfn); dev->pci_irqmask = 0x001f00; + if (cx23885_boards[dev->board].cimax > 0) + dev->pci_irqmask |= 0x01800000; /* for CiMaxes */ /* External Master 1 Bus */ dev->i2c_bus[0].nr = 0; @@ -872,7 +875,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_i2c_register(&dev->i2c_bus[1]); cx23885_i2c_register(&dev->i2c_bus[2]); cx23885_card_setup(dev); - cx23885_call_i2c_clients(&dev->i2c_bus[0], TUNER_SET_STANDBY, NULL); + call_all(dev, core, s_standby, 0); cx23885_ir_init(dev); if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) { @@ -1643,7 +1646,9 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) (pci_status & PCI_MSK_VID_B) || (pci_status & PCI_MSK_VID_A) || (pci_status & PCI_MSK_AUD_INT) || - (pci_status & PCI_MSK_AUD_EXT)) { + (pci_status & PCI_MSK_AUD_EXT) || + (pci_status & PCI_MSK_GPIO0) || + (pci_status & PCI_MSK_GPIO1)) { if (pci_status & PCI_MSK_RISC_RD) dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n", @@ -1685,8 +1690,20 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) dprintk(7, " (PCI_MSK_AUD_EXT 0x%08x)\n", PCI_MSK_AUD_EXT); + if (pci_status & PCI_MSK_GPIO0) + dprintk(7, " (PCI_MSK_GPIO0 0x%08x)\n", + PCI_MSK_GPIO0); + + if (pci_status & PCI_MSK_GPIO1) + dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n", + PCI_MSK_GPIO1); } + if (cx23885_boards[dev->board].cimax > 0 && + ((pci_status & PCI_MSK_GPIO0) || (pci_status & PCI_MSK_GPIO1))) + /* handled += cx23885_irq_gpio(dev, pci_status); */ + handled += netup_ci_slot_status(dev, pci_status); + if (ts1_status) { if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) handled += cx23885_irq_ts(ts1, ts1_status); @@ -1722,16 +1739,20 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, if (NULL == dev) return -ENOMEM; + err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); + if (err < 0) + goto fail_free; + /* pci init */ dev->pci = pci_dev; if (pci_enable_device(pci_dev)) { err = -EIO; - goto fail_free; + goto fail_unreg; } if (cx23885_dev_setup(dev) < 0) { err = -EINVAL; - goto fail_free; + goto fail_unreg; } /* print pci info */ @@ -1758,11 +1779,18 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, goto fail_irq; } - pci_set_drvdata(pci_dev, dev); + switch (dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */ + break; + } + return 0; fail_irq: cx23885_dev_unregister(dev); +fail_unreg: + v4l2_device_unregister(&dev->v4l2_dev); fail_free: kfree(dev); return err; @@ -1770,7 +1798,8 @@ fail_free: static void __devexit cx23885_finidev(struct pci_dev *pci_dev) { - struct cx23885_dev *dev = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct cx23885_dev *dev = to_cx23885(v4l2_dev); cx23885_shutdown(dev); @@ -1778,13 +1807,13 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev) /* unregister stuff */ free_irq(pci_dev->irq, dev); - pci_set_drvdata(pci_dev, NULL); mutex_lock(&devlist); list_del(&dev->devlist); mutex_unlock(&devlist); cx23885_dev_unregister(dev); + v4l2_device_unregister(v4l2_dev); kfree(dev); } diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 1c45412..d43c743 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -30,6 +30,7 @@ #include "cx23885.h" #include <media/v4l2-common.h> +#include "dvb_ca_en50221.h" #include "s5h1409.h" #include "s5h1411.h" #include "mt2131.h" @@ -43,6 +44,13 @@ #include "dib7000p.h" #include "dibx000_common.h" #include "zl10353.h" +#include "stv0900.h" +#include "stv6110.h" +#include "lnbh24.h" +#include "cx24116.h" +#include "cimax2.h" +#include "netup-eeprom.h" +#include "netup-init.h" static unsigned int debug; @@ -308,11 +316,63 @@ static struct zl10353_config dvico_fusionhdtv_xc3028 = { .no_tuner = 1, }; +static struct stv0900_config netup_stv0900_config = { + .demod_address = 0x68, + .xtal = 27000000, + .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ + .diseqc_mode = 2,/* 2/3 PWM */ + .path1_mode = 2,/*Serial continues clock */ + .path2_mode = 2,/*Serial continues clock */ + .tun1_maddress = 0,/* 0x60 */ + .tun2_maddress = 3,/* 0x63 */ + .tun1_adc = 1,/* 1 Vpp */ + .tun2_adc = 1,/* 1 Vpp */ +}; + +static struct stv6110_config netup_stv6110_tunerconfig_a = { + .i2c_address = 0x60, + .mclk = 27000000, + .iq_wiring = 0, +}; + +static struct stv6110_config netup_stv6110_tunerconfig_b = { + .i2c_address = 0x63, + .mclk = 27000000, + .iq_wiring = 1, +}; + +static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct cx23885_tsport *port = fe->dvb->priv; + struct cx23885_dev *dev = port->dev; + + if (voltage == SEC_VOLTAGE_18) + cx_write(MC417_RWD, 0x00001e00);/* GPIO-13 high */ + else if (voltage == SEC_VOLTAGE_13) + cx_write(MC417_RWD, 0x00001a00);/* GPIO-13 low */ + else + cx_write(MC417_RWD, 0x00001800);/* GPIO-12 low */ + return 0; +} + +static struct cx24116_config tbs_cx24116_config = { + .demod_address = 0x05, +}; + +static struct cx24116_config tevii_cx24116_config = { + .demod_address = 0x55, +}; + +static struct cx24116_config dvbworld_cx24116_config = { + .demod_address = 0x05, +}; + static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; struct cx23885_i2c *i2c_bus = NULL; struct videobuf_dvb_frontend *fe0; + int ret; /* Get the first frontend */ fe0 = videobuf_dvb_get_frontend(&port->frontends, 1); @@ -526,6 +586,78 @@ static int dvb_register(struct cx23885_tsport *port) fe->ops.tuner_ops.set_config(fe, &ctl); } break; + case CX23885_BOARD_TBS_6920: + i2c_bus = &dev->i2c_bus[0]; + + fe0->dvb.frontend = dvb_attach(cx24116_attach, + &tbs_cx24116_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) + fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; + + break; + case CX23885_BOARD_TEVII_S470: + i2c_bus = &dev->i2c_bus[1]; + + fe0->dvb.frontend = dvb_attach(cx24116_attach, + &tevii_cx24116_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) + fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; + + break; + case CX23885_BOARD_DVBWORLD_2005: + i2c_bus = &dev->i2c_bus[1]; + + fe0->dvb.frontend = dvb_attach(cx24116_attach, + &dvbworld_cx24116_config, + &i2c_bus->i2c_adap); + break; + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + i2c_bus = &dev->i2c_bus[0]; + switch (port->nr) { + /* port B */ + case 1: + fe0->dvb.frontend = dvb_attach(stv0900_attach, + &netup_stv0900_config, + &i2c_bus->i2c_adap, 0); + if (fe0->dvb.frontend != NULL) { + if (dvb_attach(stv6110_attach, + fe0->dvb.frontend, + &netup_stv6110_tunerconfig_a, + &i2c_bus->i2c_adap)) { + if (!dvb_attach(lnbh24_attach, + fe0->dvb.frontend, + &i2c_bus->i2c_adap, + LNBH24_PCL, 0, 0x09)) + printk(KERN_ERR + "No LNBH24 found!\n"); + + } + } + break; + /* port C */ + case 2: + fe0->dvb.frontend = dvb_attach(stv0900_attach, + &netup_stv0900_config, + &i2c_bus->i2c_adap, 1); + if (fe0->dvb.frontend != NULL) { + if (dvb_attach(stv6110_attach, + fe0->dvb.frontend, + &netup_stv6110_tunerconfig_b, + &i2c_bus->i2c_adap)) { + if (!dvb_attach(lnbh24_attach, + fe0->dvb.frontend, + &i2c_bus->i2c_adap, + LNBH24_PCL, 0, 0x0a)) + printk(KERN_ERR + "No LNBH24 found!\n"); + + } + } + break; + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", @@ -541,15 +673,39 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->callback = cx23885_tuner_callback; /* Put the analog decoder in standby to keep it quiet */ - cx23885_call_i2c_clients(i2c_bus, TUNER_SET_STANDBY, NULL); + call_all(dev, core, s_standby, 0); if (fe0->dvb.frontend->ops.analog_ops.standby) fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend); /* register everything */ - return videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, + ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, &dev->pci->dev, adapter_nr, 0); + /* init CI & MAC */ + switch (dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: { + static struct netup_card_info cinfo; + + netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo); + memcpy(port->frontends.adapter.proposed_mac, + cinfo.port[port->nr - 1].mac, 6); + printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=" + "%02X:%02X:%02X:%02X:%02X:%02X\n", + port->nr, + port->frontends.adapter.proposed_mac[0], + port->frontends.adapter.proposed_mac[1], + port->frontends.adapter.proposed_mac[2], + port->frontends.adapter.proposed_mac[3], + port->frontends.adapter.proposed_mac[4], + port->frontends.adapter.proposed_mac[5]); + + netup_ci_init(port); + break; + } + } + + return ret; } int cx23885_dvb_register(struct cx23885_tsport *port) @@ -622,6 +778,12 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port) if (fe0->dvb.frontend) videobuf_dvb_unregister_bus(&port->frontends); + switch (port->dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + netup_ci_exit(port); + break; + } + return 0; } diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c index bb7f71a..3421bd1 100644 --- a/drivers/media/video/cx23885/cx23885-i2c.c +++ b/drivers/media/video/cx23885/cx23885-i2c.c @@ -268,64 +268,6 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap, return retval; } -static int attach_inform(struct i2c_client *client) -{ - struct cx23885_i2c *bus = i2c_get_adapdata(client->adapter); - struct cx23885_dev *dev = bus->dev; - struct tuner_setup tun_setup; - - dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, client->addr, client->name); - - if (!client->driver->command) - return 0; - - if (dev->tuner_type != UNSET) { - - dprintk(1, "%s (tuner) i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, client->addr, - client->name); - - if ((dev->tuner_addr == ADDR_UNSET) || - (dev->tuner_addr == client->addr)) { - - dprintk(1, "%s (tuner || addr UNSET)\n", - client->driver->driver.name); - - dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, - client->addr, client->name); - - tun_setup.mode_mask = T_ANALOG_TV; - tun_setup.type = dev->tuner_type; - tun_setup.addr = dev->tuner_addr; - - client->driver->command(client, TUNER_SET_TYPE_ADDR, - &tun_setup); - } - } - - return 0; -} - -static int detach_inform(struct i2c_client *client) -{ - struct cx23885_dev *dev = i2c_get_adapdata(client->adapter); - - dprintk(1, "i2c detach [client=%s]\n", client->name); - - return 0; -} - -void cx23885_call_i2c_clients(struct cx23885_i2c *bus, - unsigned int cmd, void *arg) -{ - if (bus->i2c_rc != 0) - return; - - i2c_clients_command(&bus->i2c_adap, cmd, arg); -} - static u32 cx23885_functionality(struct i2c_adapter *adap) { return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; @@ -343,9 +285,6 @@ static struct i2c_adapter cx23885_i2c_adap_template = { .owner = THIS_MODULE, .id = I2C_HW_B_CX23885, .algo = &cx23885_i2c_algo_template, - .class = I2C_CLASS_TV_ANALOG, - .client_register = attach_inform, - .client_unregister = detach_inform, }; static struct i2c_client cx23885_i2c_client_template = { @@ -402,15 +341,18 @@ int cx23885_i2c_register(struct cx23885_i2c *bus) bus->i2c_algo.data = bus; bus->i2c_adap.algo_data = bus; - i2c_set_adapdata(&bus->i2c_adap, bus); + i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev); i2c_add_adapter(&bus->i2c_adap); bus->i2c_client.adapter = &bus->i2c_adap; if (0 == bus->i2c_rc) { dprintk(1, "%s: i2c bus %d registered\n", dev->name, bus->nr); - if (i2c_scan) + if (i2c_scan) { + printk(KERN_INFO "%s: scan bus %d:\n", + dev->name, bus->nr); do_i2c_scan(dev->name, &bus->i2c_client); + } } else printk(KERN_WARNING "%s: i2c bus %d register FAILED\n", dev->name, bus->nr); diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h index 20b68a2..eafbe52 100644 --- a/drivers/media/video/cx23885/cx23885-reg.h +++ b/drivers/media/video/cx23885/cx23885-reg.h @@ -212,6 +212,8 @@ Channel manager Data Structure entry = 20 DWORD #define DEV_CNTRL2 0x00040000 +#define PCI_MSK_GPIO1 (1 << 24) +#define PCI_MSK_GPIO0 (1 << 23) #define PCI_MSK_APB_DMA (1 << 12) #define PCI_MSK_AL_WR (1 << 11) #define PCI_MSK_AL_RD (1 << 10) diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index eaa1189..f0ac62c 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -35,11 +35,6 @@ #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#ifdef CONFIG_VIDEO_V4L1_COMPAT -/* Include V4L1 specific functions. Should be removed soon */ -#include <linux/videodev.h> -#endif - MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); MODULE_LICENSE("GPL"); @@ -244,6 +239,7 @@ static struct cx23885_ctrl cx23885_ctls[] = { }; static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls); +/* Must be sorted from low to high control ID! */ static const u32 cx23885_user_ctrls[] = { V4L2_CID_USER_CLASS, V4L2_CID_BRIGHTNESS, @@ -303,11 +299,7 @@ static int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) dev->tvnorm = norm; - /* Tell the analog tuner/demods */ - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_STD, &norm); - - /* Tell the internal A/V decoder */ - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_STD, &norm); + call_all(dev, tuner, s_std, norm); return 0; } @@ -324,8 +316,8 @@ static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, if (NULL == vfd) return NULL; *vfd = *template; - vfd->minor = -1; - vfd->parent = &pci->dev; + vfd->minor = -1; + vfd->v4l2_dev = &dev->v4l2_dev; vfd->release = video_device_release; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, cx23885_boards[dev->board].name); @@ -414,8 +406,7 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) route.input = INPUT(input)->vmux; /* Tell the internal A/V decoder */ - cx23885_call_i2c_clients(&dev->i2c_bus[2], - VIDIOC_INT_S_VIDEO_ROUTING, &route); + v4l2_subdev_call(dev->sd_cx25840, video, s_routing, &route); return 0; } @@ -891,7 +882,7 @@ static int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl) { dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__); - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_CTRL, ctl); + call_all(dev, core, g_ctrl, ctl); return 0; } @@ -1005,7 +996,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->vidq.field = f->fmt.pix.field; dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field); - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_FMT, f); + call_all(dev, video, s_fmt, f); return 0; } @@ -1285,7 +1276,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_FREQUENCY, f); + call_all(dev, tuner, g_frequency, f); return 0; } @@ -1300,7 +1291,7 @@ static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f) mutex_lock(&dev->lock); dev->freq = f->frequency; - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, f); + call_all(dev, tuner, s_frequency, f); /* When changing channels it is required to reset TVAUDIO */ msleep(10); @@ -1334,7 +1325,7 @@ static int vidioc_g_register(struct file *file, void *fh, if (!v4l2_chip_match_host(®->match)) return -EINVAL; - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_G_REGISTER, reg); + call_all(dev, core, g_register, reg); return 0; } @@ -1347,7 +1338,7 @@ static int vidioc_s_register(struct file *file, void *fh, if (!v4l2_chip_match_host(®->match)) return -EINVAL; - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_S_REGISTER, reg); + call_all(dev, core, s_register, reg); return 0; } @@ -1528,6 +1519,26 @@ int cx23885_video_register(struct cx23885_dev *dev) /* Don't enable VBI yet */ cx_set(PCI_INT_MSK, 1); + if (TUNER_ABSENT != dev->tuner_type) { + struct v4l2_subdev *sd = NULL; + + if (dev->tuner_addr) + sd = v4l2_i2c_new_subdev(&dev->i2c_bus[1].i2c_adap, + "tuner", "tuner", dev->tuner_addr); + else + sd = v4l2_i2c_new_probed_subdev(&dev->i2c_bus[1].i2c_adap, + "tuner", "tuner", v4l2_i2c_tuner_addrs(ADDRS_TV)); + if (sd) { + struct tuner_setup tun_setup; + + tun_setup.mode_mask = T_ANALOG_TV; + tun_setup.type = dev->tuner_type; + tun_setup.addr = v4l2_i2c_subdev_addr(sd); + + v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup); + } + } + /* register v4l devices */ dev->video_dev = cx23885_vdev_init(dev, dev->pci, diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 6782802..02d980a 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -24,7 +24,7 @@ #include <linux/i2c-algo-bit.h> #include <linux/kdev_t.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/tuner.h> #include <media/tveeprom.h> #include <media/videobuf-dma-sg.h> @@ -67,6 +67,10 @@ #define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11 #define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12 #define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13 +#define CX23885_BOARD_TBS_6920 14 +#define CX23885_BOARD_TEVII_S470 15 +#define CX23885_BOARD_DVBWORLD_2005 16 +#define CX23885_BOARD_NETUP_DUAL_DVBS2_CI 17 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ @@ -184,6 +188,7 @@ struct cx23885_board { */ u32 clk_freq; struct cx23885_input input[MAX_CX23885_INPUT]; + int cimax; /* for NetUP */ }; struct cx23885_subid { @@ -266,11 +271,13 @@ struct cx23885_tsport { /* Allow a single tsport to have multiple frontends */ u32 num_frontends; + void *port_priv; }; struct cx23885_dev { struct list_head devlist; atomic_t refcount; + struct v4l2_device v4l2_dev; /* pci stuff */ struct pci_dev *pci; @@ -316,6 +323,7 @@ struct cx23885_dev { unsigned int radio_type; unsigned char radio_addr; unsigned int has_radio; + struct v4l2_subdev *sd_cx25840; /* V4l */ u32 freq; @@ -336,6 +344,14 @@ struct cx23885_dev { }; +static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct cx23885_dev, v4l2_dev); +} + +#define call_all(dev, o, f, args...) \ + v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) + extern struct list_head cx23885_devlist; #define SRAM_CH01 0 /* Video A */ @@ -452,8 +468,6 @@ extern struct videobuf_queue_ops cx23885_vbi_qops; /* cx23885-i2c.c */ extern int cx23885_i2c_register(struct cx23885_i2c *bus); extern int cx23885_i2c_unregister(struct cx23885_i2c *bus); -extern void cx23885_call_i2c_clients(struct cx23885_i2c *bus, unsigned int cmd, - void *arg); extern void cx23885_av_clk(struct cx23885_dev *dev, int enable); /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/cx23885/netup-eeprom.c b/drivers/media/video/cx23885/netup-eeprom.c new file mode 100644 index 0000000..042bbbb --- /dev/null +++ b/drivers/media/video/cx23885/netup-eeprom.c @@ -0,0 +1,107 @@ + +/* + * netup-eeprom.c + * + * 24LC02 EEPROM driver in conjunction with NetUP Dual DVB-S2 CI card + * + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +# +#include "cx23885.h" +#include "netup-eeprom.h" + +#define EEPROM_I2C_ADDR 0x50 + +int netup_eeprom_read(struct i2c_adapter *i2c_adap, u8 addr) +{ + int ret; + unsigned char buf[2]; + + /* Read from EEPROM */ + struct i2c_msg msg[] = { + { + .addr = EEPROM_I2C_ADDR, + .flags = 0, + .buf = &buf[0], + .len = 1 + }, { + .addr = EEPROM_I2C_ADDR, + .flags = I2C_M_RD, + .buf = &buf[1], + .len = 1 + } + + }; + + buf[0] = addr; + buf[1] = 0x0; + + ret = i2c_transfer(i2c_adap, msg, 2); + + if (ret != 2) { + printk(KERN_ERR "eeprom i2c read error, status=%d\n", ret); + return -1; + } + + return buf[1]; +}; + +int netup_eeprom_write(struct i2c_adapter *i2c_adap, u8 addr, u8 data) +{ + int ret; + unsigned char bufw[2]; + + /* Write into EEPROM */ + struct i2c_msg msg[] = { + { + .addr = EEPROM_I2C_ADDR, + .flags = 0, + .buf = &bufw[0], + .len = 2 + } + }; + + bufw[0] = addr; + bufw[1] = data; + + ret = i2c_transfer(i2c_adap, msg, 1); + + if (ret != 1) { + printk(KERN_ERR "eeprom i2c write error, status=%d\n", ret); + return -1; + } + + mdelay(10); /* prophylactic delay, datasheet write cycle time = 5 ms */ + return 0; +}; + +void netup_get_card_info(struct i2c_adapter *i2c_adap, + struct netup_card_info *cinfo) +{ + int i, j; + + cinfo->rev = netup_eeprom_read(i2c_adap, 13); + + for (i = 0, j = 0; i < 6; i++, j++) + cinfo->port[0].mac[j] = netup_eeprom_read(i2c_adap, i); + + for (i = 6, j = 0; i < 12; i++, j++) + cinfo->port[1].mac[j] = netup_eeprom_read(i2c_adap, i); +}; diff --git a/drivers/media/video/cx23885/netup-eeprom.h b/drivers/media/video/cx23885/netup-eeprom.h new file mode 100644 index 0000000..13926e1 --- /dev/null +++ b/drivers/media/video/cx23885/netup-eeprom.h @@ -0,0 +1,42 @@ +/* + * netup-eeprom.h + * + * 24LC02 EEPROM driver in conjunction with NetUP Dual DVB-S2 CI card + * + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef NETUP_EEPROM_H +#define NETUP_EEPROM_H + +struct netup_port_info { + u8 mac[6];/* card MAC address */ +}; + +struct netup_card_info { + struct netup_port_info port[2];/* ports - 1,2 */ + u8 rev;/* card revision */ +}; + +extern int netup_eeprom_read(struct i2c_adapter *i2c_adap, u8 addr); +extern int netup_eeprom_write(struct i2c_adapter *i2c_adap, u8 addr, u8 data); +extern void netup_get_card_info(struct i2c_adapter *i2c_adap, + struct netup_card_info *cinfo); + +#endif diff --git a/drivers/media/video/cx23885/netup-init.c b/drivers/media/video/cx23885/netup-init.c new file mode 100644 index 0000000..f4893e6 --- /dev/null +++ b/drivers/media/video/cx23885/netup-init.c @@ -0,0 +1,125 @@ +/* + * netup-init.c + * + * NetUP Dual DVB-S2 CI driver + * + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx23885.h" + +static void i2c_av_write(struct i2c_adapter *i2c, u16 reg, u8 val) +{ + int ret; + u8 buf[3]; + struct i2c_msg msg = { + .addr = 0x88 >> 1, + .flags = 0, + .buf = buf, + .len = 3 + }; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + buf[2] = val; + + ret = i2c_transfer(i2c, &msg, 1); + + if (ret != 1) + printk(KERN_ERR "%s: i2c write error!\n", __func__); +} + +static void i2c_av_write4(struct i2c_adapter *i2c, u16 reg, u32 val) +{ + int ret; + u8 buf[6]; + struct i2c_msg msg = { + .addr = 0x88 >> 1, + .flags = 0, + .buf = buf, + .len = 6 + }; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + buf[2] = val & 0xff; + buf[3] = (val >> 8) & 0xff; + buf[4] = (val >> 16) & 0xff; + buf[5] = val >> 24; + + ret = i2c_transfer(i2c, &msg, 1); + + if (ret != 1) + printk(KERN_ERR "%s: i2c write error!\n", __func__); +} + +static u8 i2c_av_read(struct i2c_adapter *i2c, u16 reg) +{ + int ret; + u8 buf[2]; + struct i2c_msg msg = { + .addr = 0x88 >> 1, + .flags = 0, + .buf = buf, + .len = 2 + }; + + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + ret = i2c_transfer(i2c, &msg, 1); + + if (ret != 1) + printk(KERN_ERR "%s: i2c write error!\n", __func__); + + msg.flags = I2C_M_RD; + msg.len = 1; + + ret = i2c_transfer(i2c, &msg, 1); + + if (ret != 1) + printk(KERN_ERR "%s: i2c read error!\n", __func__); + + return buf[0]; +} + +static void i2c_av_and_or(struct i2c_adapter *i2c, u16 reg, unsigned and_mask, + u8 or_value) +{ + i2c_av_write(i2c, reg, (i2c_av_read(i2c, reg) & and_mask) | or_value); +} +/* set 27MHz on AUX_CLK */ +void netup_initialize(struct cx23885_dev *dev) +{ + struct cx23885_i2c *i2c_bus = &dev->i2c_bus[2]; + struct i2c_adapter *i2c = &i2c_bus->i2c_adap; + + /* Stop microcontroller */ + i2c_av_and_or(i2c, 0x803, ~0x10, 0x00); + + /* Aux PLL frac for 27 MHz */ + i2c_av_write4(i2c, 0x114, 0xea0eb3); + + /* Aux PLL int for 27 MHz */ + i2c_av_write4(i2c, 0x110, 0x090319); + + /* start microcontroller */ + i2c_av_and_or(i2c, 0x803, ~0x10, 0x10); +} diff --git a/drivers/media/video/cx23885/netup-init.h b/drivers/media/video/cx23885/netup-init.h new file mode 100644 index 0000000..d26ae4b --- /dev/null +++ b/drivers/media/video/cx23885/netup-init.h @@ -0,0 +1,25 @@ +/* + * netup-init.h + * + * NetUP Dual DVB-S2 CI driver + * + * Copyright (C) 2009 NetUP Inc. + * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> + * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru> + * + * 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. + */ +extern void netup_initialize(struct cx23885_dev *dev); diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c index d199d80..93d74be 100644 --- a/drivers/media/video/cx25840/cx25840-audio.c +++ b/drivers/media/video/cx25840/cx25840-audio.c @@ -363,75 +363,74 @@ static void set_mute(struct i2c_client *client, int mute) } } -int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg) +int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - struct v4l2_control *ctrl = arg; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); int retval; - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - if (!state->is_cx25836) - cx25840_and_or(client, 0x810, ~0x1, 1); - if (state->aud_input != CX25840_AUDIO_SERIAL) { - cx25840_and_or(client, 0x803, ~0x10, 0); - cx25840_write(client, 0x8d3, 0x1f); - } - retval = set_audclk_freq(client, *(u32 *)arg); - if (state->aud_input != CX25840_AUDIO_SERIAL) { - cx25840_and_or(client, 0x803, ~0x10, 0x10); - } - if (!state->is_cx25836) - cx25840_and_or(client, 0x810, ~0x1, 0); - return retval; - - case VIDIOC_G_CTRL: - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = get_volume(client); - break; - case V4L2_CID_AUDIO_BASS: - ctrl->value = get_bass(client); - break; - case V4L2_CID_AUDIO_TREBLE: - ctrl->value = get_treble(client); - break; - case V4L2_CID_AUDIO_BALANCE: - ctrl->value = get_balance(client); - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = get_mute(client); - break; - default: - return -EINVAL; - } - break; + if (!state->is_cx25836) + cx25840_and_or(client, 0x810, ~0x1, 1); + if (state->aud_input != CX25840_AUDIO_SERIAL) { + cx25840_and_or(client, 0x803, ~0x10, 0); + cx25840_write(client, 0x8d3, 0x1f); + } + retval = set_audclk_freq(client, freq); + if (state->aud_input != CX25840_AUDIO_SERIAL) + cx25840_and_or(client, 0x803, ~0x10, 0x10); + if (!state->is_cx25836) + cx25840_and_or(client, 0x810, ~0x1, 0); + return retval; +} - case VIDIOC_S_CTRL: - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - set_volume(client, ctrl->value); - break; - case V4L2_CID_AUDIO_BASS: - set_bass(client, ctrl->value); - break; - case V4L2_CID_AUDIO_TREBLE: - set_treble(client, ctrl->value); - break; - case V4L2_CID_AUDIO_BALANCE: - set_balance(client, ctrl->value); - break; - case V4L2_CID_AUDIO_MUTE: - set_mute(client, ctrl->value); - break; - default: - return -EINVAL; - } - break; +int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = get_volume(client); + break; + case V4L2_CID_AUDIO_BASS: + ctrl->value = get_bass(client); + break; + case V4L2_CID_AUDIO_TREBLE: + ctrl->value = get_treble(client); + break; + case V4L2_CID_AUDIO_BALANCE: + ctrl->value = get_balance(client); + break; + case V4L2_CID_AUDIO_MUTE: + ctrl->value = get_mute(client); + break; default: return -EINVAL; } + return 0; +} +int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + set_volume(client, ctrl->value); + break; + case V4L2_CID_AUDIO_BASS: + set_bass(client, ctrl->value); + break; + case V4L2_CID_AUDIO_TREBLE: + set_treble(client, ctrl->value); + break; + case V4L2_CID_AUDIO_BALANCE: + set_balance(client, ctrl->value); + break; + case V4L2_CID_AUDIO_MUTE: + set_mute(client, ctrl->value); + break; + default: + return -EINVAL; + } return 0; } diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 25eb3bec..737ee4e 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -39,7 +39,7 @@ #include <linux/delay.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include <media/cx25840.h> #include "cx25840-core.h" @@ -48,15 +48,12 @@ MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver"); MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford"); MODULE_LICENSE("GPL"); -static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; - static int cx25840_debug; module_param_named(debug,cx25840_debug, int, 0644); MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]"); -I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ @@ -763,7 +760,7 @@ static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) break; case V4L2_CID_HUE: - if (ctrl->value < -127 || ctrl->value > 127) { + if (ctrl->value < -128 || ctrl->value > 127) { v4l_err(client, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } @@ -778,7 +775,7 @@ static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_MUTE: if (state->is_cx25836) return -EINVAL; - return cx25840_audio(client, VIDIOC_S_CTRL, ctrl); + return cx25840_audio_s_ctrl(sd, ctrl); default: return -EINVAL; @@ -815,7 +812,7 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_MUTE: if (state->is_cx25836) return -EINVAL; - return cx25840_audio(client, VIDIOC_G_CTRL, ctrl); + return cx25840_audio_g_ctrl(sd, ctrl); default: return -EINVAL; } @@ -827,11 +824,9 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) static int cx25840_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - switch (fmt->type) { case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx25840_vbi(client, VIDIOC_G_FMT, fmt); + return cx25840_vbi_g_fmt(sd, fmt); default: return -EINVAL; } @@ -893,10 +888,10 @@ static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx25840_vbi(client, VIDIOC_S_FMT, fmt); + return cx25840_vbi_s_fmt(sd, fmt); case V4L2_BUF_TYPE_VBI_CAPTURE: - return cx25840_vbi(client, VIDIOC_S_FMT, fmt); + return cx25840_vbi_s_fmt(sd, fmt); default: return -EINVAL; @@ -1101,6 +1096,16 @@ static void log_audio_status(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +/* This init operation must be called to load the driver's firmware. + Without this the audio standard detection will fail and you will + only get mono. + + Since loading the firmware is often problematic when the driver is + compiled into the kernel I recommend postponing calling this function + until the first open of the video device. Another reason for + postponing it is that loading this firmware takes a long time (seconds) + due to the slow i2c bus speed. So it will speed up the boot process if + you can avoid loading the fw as long as the video device isn't used. */ static int cx25840_init(struct v4l2_subdev *sd, u32 val) { struct cx25840_state *state = to_state(sd); @@ -1146,20 +1151,6 @@ static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * } #endif -static int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return cx25840_vbi(client, VIDIOC_INT_DECODE_VBI_LINE, vbi); -} - -static int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return cx25840_audio(client, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freq); -} - static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) { struct cx25840_state *state = to_state(sd); @@ -1195,10 +1186,12 @@ static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) switch (qc->id) { case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); default: break; } @@ -1210,10 +1203,11 @@ static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, state->default_volume); case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); default: return -EINVAL; } @@ -1380,19 +1374,6 @@ static int cx25840_log_status(struct v4l2_subdev *sd) return 0; } -static int cx25840_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - /* ignore this command */ - if (cmd == TUNER_SET_TYPE_ADDR || cmd == TUNER_SET_CONFIG) - return 0; - - /* Old-style drivers rely on initialization on first use, so - call the init whenever a command is issued to this driver. - New-style drivers using v4l2_subdev should call init explicitly. */ - cx25840_init(i2c_get_clientdata(client), 0); - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops cx25840_core_ops = { @@ -1528,8 +1509,6 @@ MODULE_DEVICE_TABLE(i2c, cx25840_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cx25840", - .driverid = I2C_DRIVERID_CX25840, - .command = cx25840_command, .probe = cx25840_probe, .remove = cx25840_remove, .id_table = cx25840_id, diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index be05582..9ad0eb8 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -75,11 +75,15 @@ int cx25840_loadfw(struct i2c_client *client); /* ----------------------------------------------------------------------- */ /* cx25850-audio.c */ -int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg); void cx25840_audio_set_path(struct i2c_client *client); +int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq); +int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); +int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); /* ----------------------------------------------------------------------- */ /* cx25850-vbi.c */ -int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg); +int cx25840_vbi_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt); +int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt); +int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); #endif diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c index 03f09b2..35f6592 100644 --- a/drivers/media/video/cx25840/cx25840-vbi.c +++ b/drivers/media/video/cx25840/cx25840-vbi.c @@ -82,199 +82,181 @@ static int decode_vps(u8 * dst, u8 * p) return err & 0xf0; } -int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) +int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - struct v4l2_format *fmt; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); struct v4l2_sliced_vbi_format *svbi; + static const u16 lcr2vbi[] = { + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ + 0, V4L2_SLICED_WSS_625, 0, /* 4 */ + V4L2_SLICED_CAPTION_525, /* 6 */ + 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ + 0, 0, 0, 0 + }; + int is_pal = !(state->std & V4L2_STD_525_60); + int i; - switch (cmd) { - case VIDIOC_G_FMT: - { - static u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ - 0, V4L2_SLICED_WSS_625, 0, /* 4 */ - V4L2_SLICED_CAPTION_525, /* 6 */ - 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ - 0, 0, 0, 0 - }; - int is_pal = !(state->std & V4L2_STD_525_60); - int i; - - fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - memset(svbi, 0, sizeof(*svbi)); - /* we're done if raw VBI is active */ - if ((cx25840_read(client, 0x404) & 0x10) == 0) - break; + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + memset(svbi, 0, sizeof(*svbi)); + /* we're done if raw VBI is active */ + if ((cx25840_read(client, 0x404) & 0x10) == 0) + return 0; - if (is_pal) { - for (i = 7; i <= 23; i++) { - u8 v = cx25840_read(client, 0x424 + i - 7); + if (is_pal) { + for (i = 7; i <= 23; i++) { + u8 v = cx25840_read(client, 0x424 + i - 7); - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= - svbi->service_lines[0][i] | svbi->service_lines[1][i]; - } + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; } - else { - for (i = 10; i <= 21; i++) { - u8 v = cx25840_read(client, 0x424 + i - 10); - - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= - svbi->service_lines[0][i] | svbi->service_lines[1][i]; - } + } else { + for (i = 10; i <= 21; i++) { + u8 v = cx25840_read(client, 0x424 + i - 10); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; } - break; } + return 0; +} - case VIDIOC_S_FMT: - { - int is_pal = !(state->std & V4L2_STD_525_60); - int vbi_offset = is_pal ? 1 : 0; - int i, x; - u8 lcr[24]; - - fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && - fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* raw VBI */ - memset(svbi, 0, sizeof(*svbi)); - - /* Setup standard */ - cx25840_std_setup(client); - - /* VBI Offset */ - cx25840_write(client, 0x47f, vbi_offset); - cx25840_write(client, 0x404, 0x2e); - break; - } - - for (x = 0; x <= 23; x++) - lcr[x] = 0x00; +int cx25840_vbi_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + struct v4l2_sliced_vbi_format *svbi; + int is_pal = !(state->std & V4L2_STD_525_60); + int vbi_offset = is_pal ? 1 : 0; + int i, x; + u8 lcr[24]; + + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && + fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + /* raw VBI */ + memset(svbi, 0, sizeof(*svbi)); /* Setup standard */ cx25840_std_setup(client); - /* Sliced VBI */ - cx25840_write(client, 0x404, 0x32); /* Ancillary data */ - cx25840_write(client, 0x406, 0x13); + /* VBI Offset */ cx25840_write(client, 0x47f, vbi_offset); + cx25840_write(client, 0x404, 0x2e); + return 0; + } - if (is_pal) { - for (i = 0; i <= 6; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } else { - for (i = 0; i <= 9; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - - for (i = 22; i <= 23; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } - - for (i = 7; i <= 23; i++) { - for (x = 0; x <= 1; x++) { - switch (svbi->service_lines[1-x][i]) { - case V4L2_SLICED_TELETEXT_B: - lcr[i] |= 1 << (4 * x); - break; - case V4L2_SLICED_WSS_625: - lcr[i] |= 4 << (4 * x); - break; - case V4L2_SLICED_CAPTION_525: - lcr[i] |= 6 << (4 * x); - break; - case V4L2_SLICED_VPS: - lcr[i] |= 9 << (4 * x); - break; - } - } - } + for (x = 0; x <= 23; x++) + lcr[x] = 0x00; + + /* Setup standard */ + cx25840_std_setup(client); + + /* Sliced VBI */ + cx25840_write(client, 0x404, 0x32); /* Ancillary data */ + cx25840_write(client, 0x406, 0x13); + cx25840_write(client, 0x47f, vbi_offset); + + if (is_pal) { + for (i = 0; i <= 6; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } else { + for (i = 0; i <= 9; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + + for (i = 22; i <= 23; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } - if (is_pal) { - for (x = 1, i = 0x424; i <= 0x434; i++, x++) { - cx25840_write(client, i, lcr[6 + x]); - } - } - else { - for (x = 1, i = 0x424; i <= 0x430; i++, x++) { - cx25840_write(client, i, lcr[9 + x]); - } - for (i = 0x431; i <= 0x434; i++) { - cx25840_write(client, i, 0); + for (i = 7; i <= 23; i++) { + for (x = 0; x <= 1; x++) { + switch (svbi->service_lines[1-x][i]) { + case V4L2_SLICED_TELETEXT_B: + lcr[i] |= 1 << (4 * x); + break; + case V4L2_SLICED_WSS_625: + lcr[i] |= 4 << (4 * x); + break; + case V4L2_SLICED_CAPTION_525: + lcr[i] |= 6 << (4 * x); + break; + case V4L2_SLICED_VPS: + lcr[i] |= 9 << (4 * x); + break; } } + } - cx25840_write(client, 0x43c, 0x16); - - if (is_pal) { - cx25840_write(client, 0x474, 0x2a); - } else { - cx25840_write(client, 0x474, 0x22); - } - break; + if (is_pal) { + for (x = 1, i = 0x424; i <= 0x434; i++, x++) + cx25840_write(client, i, lcr[6 + x]); + } else { + for (x = 1, i = 0x424; i <= 0x430; i++, x++) + cx25840_write(client, i, lcr[9 + x]); + for (i = 0x431; i <= 0x434; i++) + cx25840_write(client, i, 0); } - case VIDIOC_INT_DECODE_VBI_LINE: - { - struct v4l2_decode_vbi_line *vbi = arg; - u8 *p = vbi->p; - int id1, id2, l, err = 0; + cx25840_write(client, 0x43c, 0x16); + cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22); + return 0; +} - if (p[0] || p[1] != 0xff || p[2] != 0xff || - (p[3] != 0x55 && p[3] != 0x91)) { - vbi->line = vbi->type = 0; - break; - } +int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) +{ + struct cx25840_state *state = to_state(sd); + u8 *p = vbi->p; + int id1, id2, l, err = 0; + + if (p[0] || p[1] != 0xff || p[2] != 0xff || + (p[3] != 0x55 && p[3] != 0x91)) { + vbi->line = vbi->type = 0; + return 0; + } - p += 4; - id1 = p[-1]; - id2 = p[0] & 0xf; - l = p[2] & 0x3f; - l += state->vbi_line_offset; - p += 4; + p += 4; + id1 = p[-1]; + id2 = p[0] & 0xf; + l = p[2] & 0x3f; + l += state->vbi_line_offset; + p += 4; - switch (id2) { - case 1: - id2 = V4L2_SLICED_TELETEXT_B; - break; - case 4: - id2 = V4L2_SLICED_WSS_625; - break; - case 6: - id2 = V4L2_SLICED_CAPTION_525; - err = !odd_parity(p[0]) || !odd_parity(p[1]); - break; - case 9: - id2 = V4L2_SLICED_VPS; - if (decode_vps(p, p) != 0) { - err = 1; - } - break; - default: - id2 = 0; + switch (id2) { + case 1: + id2 = V4L2_SLICED_TELETEXT_B; + break; + case 4: + id2 = V4L2_SLICED_WSS_625; + break; + case 6: + id2 = V4L2_SLICED_CAPTION_525; + err = !odd_parity(p[0]) || !odd_parity(p[1]); + break; + case 9: + id2 = V4L2_SLICED_VPS; + if (decode_vps(p, p) != 0) err = 1; - break; - } - - vbi->type = err ? 0 : id2; - vbi->line = err ? 0 : l; - vbi->is_second_field = err ? 0 : (id1 == 0x55); - vbi->p = p; break; - } + default: + id2 = 0; + err = 1; + break; } + vbi->type = err ? 0 : id2; + vbi->line = err ? 0 : l; + vbi->is_second_field = err ? 0 : (id1 == 0x55); + vbi->p = p; return 0; } diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index 2d250a2..4995298 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -61,7 +61,7 @@ config VIDEO_CX88_DVB select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_STV0288 if !DVB_FE_CUSTOMISE select DVB_STB6000 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE ---help--- This adds support for DVB/ATSC cards based on the Conexant 2388x chip. diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 66c755c..ce98d95 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -803,9 +803,10 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci, return (-ENOENT); } - card = snd_card_new(index[devno], id[devno], THIS_MODULE, sizeof(snd_cx88_card_t)); - if (!card) - return (-ENOMEM); + err = snd_card_create(index[devno], id[devno], THIS_MODULE, + sizeof(snd_cx88_card_t), &card); + if (err < 0) + return err; card->private_free = snd_cx88_dev_free; diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 7f5b8bf..44eacfb 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -746,7 +746,6 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, return -EINVAL; strlcpy(f->description, "MPEG", sizeof(f->description)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->pixelformat = V4L2_PIX_FMT_MPEG; return 0; } @@ -757,7 +756,6 @@ static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, struct cx8802_fh *fh = priv; struct cx8802_dev *dev = fh->dev; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */ @@ -776,7 +774,6 @@ static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, struct cx8802_fh *fh = priv; struct cx8802_dev *dev = fh->dev; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; @@ -793,7 +790,6 @@ static int vidioc_s_fmt_vid_cap (struct file *file, void *priv, struct cx8802_dev *dev = fh->dev; struct cx88_core *core = dev->core; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; @@ -919,7 +915,7 @@ static int vidioc_log_status (struct file *file, void *priv) snprintf(name, sizeof(name), "%s/2", core->name); printk("%s/2: ============ START LOG STATUS ============\n", core->name); - cx88_call_i2c_clients(core, VIDIOC_LOG_STATUS, NULL); + call_all(core, core, log_status); cx2341x_log_status(&dev->params, name); printk("%s/2: ============= END LOG STATUS =============\n", core->name); @@ -974,7 +970,7 @@ static int vidioc_g_frequency (struct file *file, void *priv, f->type = V4L2_TUNER_ANALOG_TV; f->frequency = core->freq; - cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f); + call_all(core, tuner, g_frequency, f); return 0; } diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 733ede3..0363971 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -732,6 +732,8 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + /* Some variants use a tda9874 and so need the tvaudio module. */ + .audio_chip = V4L2_IDENT_TVAUDIO, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -1934,6 +1936,39 @@ static const struct cx88_board cx88_boards[] = { } }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII] = { + .name = "Terratec Cinergy HT PCI MKII", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .radio_type = TUNER_XC2028, + .radio_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x00001, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x004fb, + .gpio1 = 0x010ef, + .audioroute = 1, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x004fb, + .gpio1 = 0x010ef, + .audioroute = 1, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x0ff, + }, + .mpeg = CX88_MPEG_DVB, + }, }; /* ------------------------------------------------------------------ */ @@ -2343,6 +2378,10 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0xb200, .subdevice = 0x4200, .card = CX88_BOARD_SATTRADE_ST4200, + }, { + .subvendor = 0x153b, + .subdevice = 0x1177, + .card = CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII, }, }; @@ -2819,6 +2858,7 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) */ break; case CX88_BOARD_PINNACLE_HYBRID_PCTV: + case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: ctl->demod = XC3028_FE_ZARLINK456; ctl->mts = 1; break; @@ -2947,7 +2987,7 @@ static void cx88_card_setup(struct cx88_core *core) tea5767_cfg.tuner = TUNER_TEA5767; tea5767_cfg.priv = &ctl; - cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tea5767_cfg); + call_all(core, tuner, s_config, &tea5767_cfg); break; } case CX88_BOARD_TEVII_S420: @@ -2972,7 +3012,7 @@ static void cx88_card_setup(struct cx88_core *core) tun_setup.type = core->board.radio_type; tun_setup.addr = core->board.radio_addr; tun_setup.tuner_callback = cx88_tuner_callback; - cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup); + call_all(core, tuner, s_type_addr, &tun_setup); mode_mask &= ~T_RADIO; } @@ -2982,7 +3022,7 @@ static void cx88_card_setup(struct cx88_core *core) tun_setup.addr = core->board.tuner_addr; tun_setup.tuner_callback = cx88_tuner_callback; - cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup); + call_all(core, tuner, s_type_addr, &tun_setup); } if (core->board.tda9887_conf) { @@ -2991,7 +3031,7 @@ static void cx88_card_setup(struct cx88_core *core) tda9887_cfg.tuner = TUNER_TDA9887; tda9887_cfg.priv = &core->board.tda9887_conf; - cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tda9887_cfg); + call_all(core, tuner, s_config, &tda9887_cfg); } if (core->board.tuner_type == TUNER_XC2028) { @@ -3007,9 +3047,9 @@ static void cx88_card_setup(struct cx88_core *core) xc2028_cfg.priv = &ctl; info_printk(core, "Asking xc2028/3028 to load firmware %s\n", ctl.fname); - cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &xc2028_cfg); + call_all(core, tuner, s_config, &xc2028_cfg); } - cx88_call_i2c_clients (core, TUNER_SET_STANDBY, NULL); + call_all(core, core, s_standby, 0); } /* ------------------------------------------------------------------ */ @@ -3089,6 +3129,8 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) int i; core = kzalloc(sizeof(*core), GFP_KERNEL); + if (core == NULL) + return NULL; atomic_inc(&core->refcount); core->pci_bus = pci->bus->number; @@ -3100,7 +3142,15 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) core->nr = nr; sprintf(core->name, "cx88[%d]", core->nr); + + strcpy(core->v4l2_dev.name, core->name); + if (v4l2_device_register(NULL, &core->v4l2_dev)) { + kfree(core); + return NULL; + } + if (0 != cx88_get_resources(core, pci)) { + v4l2_device_unregister(&core->v4l2_dev); kfree(core); return NULL; } @@ -3111,6 +3161,11 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) pci_resource_len(pci, 0)); core->bmmio = (u8 __iomem *)core->lmmio; + if (core->lmmio == NULL) { + kfree(core); + return NULL; + } + /* board config */ core->boardnr = UNSET; if (card[core->nr] < ARRAY_SIZE(cx88_boards)) @@ -3149,8 +3204,36 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) cx88_i2c_init(core, pci); /* load tuner module, if needed */ - if (TUNER_ABSENT != core->board.tuner_type) - request_module("tuner"); + if (TUNER_ABSENT != core->board.tuner_type) { + /* Ignore 0x6b and 0x6f on cx88 boards. + * FusionHDTV5 RT Gold has an ir receiver at 0x6b + * and an RTC at 0x6f which can get corrupted if probed. */ + static const unsigned short tv_addrs[] = { + 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, + I2C_CLIENT_END + }; + int has_demod = (core->board.tda9887_conf & TDA9887_PRESENT); + + /* I don't trust the radio_type as is stored in the card + definitions, so we just probe for it. + The radio_type is sometimes missing, or set to UNSET but + later code configures a tea5767. + */ + v4l2_i2c_new_probed_subdev(&core->i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_RADIO)); + if (has_demod) + v4l2_i2c_new_probed_subdev(&core->i2c_adap, "tuner", + "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + if (core->board.tuner_addr == ADDR_UNSET) { + v4l2_i2c_new_probed_subdev(&core->i2c_adap, "tuner", + "tuner", has_demod ? tv_addrs + 4 : tv_addrs); + } else { + v4l2_i2c_new_subdev(&core->i2c_adap, + "tuner", "tuner", core->board.tuner_addr); + } + } cx88_card_setup(core); cx88_ir_init(core, pci); diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index b045874..f2fb9f3 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -991,7 +991,7 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm) set_tvaudio(core); // tell i2c chips - cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm); + call_all(core, tuner, s_std, norm); // done return 0; @@ -1011,7 +1011,8 @@ struct video_device *cx88_vdev_init(struct cx88_core *core, return NULL; *vfd = *template; vfd->minor = -1; - vfd->parent = &pci->dev; + vfd->v4l2_dev = &core->v4l2_dev; + vfd->parent = &pci->dev; vfd->release = video_device_release; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", core->name, type, core->board.name); @@ -1058,12 +1059,16 @@ void cx88_core_put(struct cx88_core *core, struct pci_dev *pci) mutex_lock(&devlist); cx88_ir_fini(core); - if (0 == core->i2c_rc) + if (0 == core->i2c_rc) { + if (core->i2c_rtc) + i2c_unregister_device(core->i2c_rtc); i2c_del_adapter(&core->i2c_adap); + } list_del(&core->devlist); iounmap(core->lmmio); cx88_devcount--; mutex_unlock(&devlist); + v4l2_device_unregister(&core->v4l2_dev); kfree(core); } diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index aef5297..4ff4d9f 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -241,6 +241,12 @@ static struct mt352_config dvico_fusionhdtv_dual = { .demod_init = dvico_dual_demod_init, }; +static struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .if2 = 45600, +}; + #if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) { @@ -1131,6 +1137,16 @@ static int dvb_register(struct cx8802_dev *dev) if (fe0->dvb.frontend != NULL) fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; break; + case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &cx88_terratec_cinergy_ht_pci_mkii_config, + &core->i2c_adap); + if (fe0->dvb.frontend) { + fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; + if (attach_xc3028(0x61, dev) < 0) + goto frontend_detach; + } + break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", core->name); @@ -1152,7 +1168,7 @@ static int dvb_register(struct cx8802_dev *dev) fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; /* Put the analog decoder in standby to keep it quiet */ - cx88_call_i2c_clients(core, TUNER_SET_STANDBY, NULL); + call_all(core, core, s_standby, 0); /* register everything */ return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index c0ff230..996b4ed 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -97,37 +97,6 @@ static int cx8800_bit_getsda(void *data) /* ----------------------------------------------------------------------- */ -static int attach_inform(struct i2c_client *client) -{ - struct cx88_core *core = i2c_get_adapdata(client->adapter); - - dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, client->addr, client->name); - return 0; -} - -static int detach_inform(struct i2c_client *client) -{ - struct cx88_core *core = i2c_get_adapdata(client->adapter); - - dprintk(1, "i2c detach [client=%s]\n", client->name); - return 0; -} - -void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg) -{ - if (0 != core->i2c_rc) - return; - - if (core->gate_ctrl) - core->gate_ctrl(core, 1); - - i2c_clients_command(&core->i2c_adap, cmd, arg); - - if (core->gate_ctrl) - core->gate_ctrl(core, 0); -} - static const struct i2c_algo_bit_data cx8800_i2c_algo_template = { .setsda = cx8800_bit_setsda, .setscl = cx8800_bit_setscl, @@ -173,20 +142,14 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) memcpy(&core->i2c_algo, &cx8800_i2c_algo_template, sizeof(core->i2c_algo)); - if (core->board.tuner_type != TUNER_ABSENT) - core->i2c_adap.class |= I2C_CLASS_TV_ANALOG; - if (core->board.mpeg & CX88_MPEG_DVB) - core->i2c_adap.class |= I2C_CLASS_TV_DIGITAL; core->i2c_adap.dev.parent = &pci->dev; strlcpy(core->i2c_adap.name,core->name,sizeof(core->i2c_adap.name)); core->i2c_adap.owner = THIS_MODULE; core->i2c_adap.id = I2C_HW_B_CX2388x; - core->i2c_adap.client_register = attach_inform; - core->i2c_adap.client_unregister = detach_inform; core->i2c_algo.udelay = i2c_udelay; core->i2c_algo.data = core; - i2c_set_adapdata(&core->i2c_adap,core); + i2c_set_adapdata(&core->i2c_adap, &core->v4l2_dev); core->i2c_adap.algo_data = &core->i2c_algo; core->i2c_client.adapter = &core->i2c_adap; strlcpy(core->i2c_client.name, "cx88xx internal", I2C_NAME_SIZE); @@ -222,8 +185,6 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) /* ----------------------------------------------------------------------- */ -EXPORT_SYMBOL(cx88_call_i2c_clients); - /* * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 8683d10..ec05312 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -48,8 +48,7 @@ struct cx88_IR { /* poll external decoder */ int polling; - struct work_struct work; - struct timer_list timer; + struct delayed_work work; u32 gpio_addr; u32 last_gpio; u32 mask_keycode; @@ -143,27 +142,19 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) } } -static void ir_timer(unsigned long data) -{ - struct cx88_IR *ir = (struct cx88_IR *)data; - - schedule_work(&ir->work); -} - static void cx88_ir_work(struct work_struct *work) { - struct cx88_IR *ir = container_of(work, struct cx88_IR, work); + struct cx88_IR *ir = container_of(work, struct cx88_IR, work.work); cx88_ir_handle_key(ir); - mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); } void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) { if (ir->polling) { - setup_timer(&ir->timer, ir_timer, (unsigned long)ir); - INIT_WORK(&ir->work, cx88_ir_work); - schedule_work(&ir->work); + INIT_DELAYED_WORK(&ir->work, cx88_ir_work); + schedule_delayed_work(&ir->work, 0); } if (ir->sampling) { core->pci_irqmask |= PCI_INT_IR_SMPINT; @@ -179,10 +170,8 @@ void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) core->pci_irqmask &= ~PCI_INT_IR_SMPINT; } - if (ir->polling) { - del_timer_sync(&ir->timer); - flush_scheduled_work(); - } + if (ir->polling) + cancel_delayed_work_sync(&ir->work); } /* ---------------------------------------------------------------------- */ @@ -226,6 +215,8 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_HAUPPAUGE_HVR3000: case CX88_BOARD_HAUPPAUGE_HVR4000: case CX88_BOARD_HAUPPAUGE_HVR4000LITE: + case CX88_BOARD_PCHDTV_HD3000: + case CX88_BOARD_PCHDTV_HD5500: ir_codes = ir_codes_hauppauge_new; ir_type = IR_TYPE_RC5; ir->sampling = 1; @@ -466,6 +457,8 @@ void cx88_ir_irq(struct cx88_core *core) case CX88_BOARD_HAUPPAUGE_HVR3000: case CX88_BOARD_HAUPPAUGE_HVR4000: case CX88_BOARD_HAUPPAUGE_HVR4000LITE: + case CX88_BOARD_PCHDTV_HD3000: + case CX88_BOARD_PCHDTV_HD5500: ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7); ir_dprintk("biphase decoded: %x\n", ircode); /* diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 791e69d..434237a 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -41,11 +41,6 @@ #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#ifdef CONFIG_VIDEO_V4L1_COMPAT -/* Include V4L1 specific functions. Should be removed soon */ -#include <linux/videodev.h> -#endif - MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); @@ -298,6 +293,7 @@ static struct cx88_ctrl cx8800_ctls[] = { }; static const int CX8800_CTLS = ARRAY_SIZE(cx8800_ctls); +/* Must be sorted from low to high control ID! */ const u32 cx88_user_ctrls[] = { V4L2_CID_USER_CLASS, V4L2_CID_BRIGHTNESS, @@ -435,8 +431,7 @@ int cx88_video_mux(struct cx88_core *core, unsigned int input) struct v4l2_routing route; route.input = INPUT(input).audioroute; - cx88_call_i2c_clients(core, - VIDIOC_INT_S_AUDIO_ROUTING, &route); + call_all(core, audio, s_routing, &route); } /* cx2388's C-ADC is connected to the tuner only. When used with S-Video, that ADC is busy dealing with @@ -831,8 +826,7 @@ static int video_open(struct file *file) struct v4l2_routing route; route.input = core->board.radio.audioroute; - cx88_call_i2c_clients(core, - VIDIOC_INT_S_AUDIO_ROUTING, &route); + call_all(core, audio, s_routing, &route); } /* "I2S ADC mode" */ core->tvaudio = WW_I2SADC; @@ -843,7 +837,7 @@ static int video_open(struct file *file) cx88_set_tvaudio(core); cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1); } - cx88_call_i2c_clients(core,AUDC_SET_RADIO,NULL); + call_all(core, tuner, s_radio); } unlock_kernel(); @@ -937,7 +931,7 @@ static int video_release(struct file *file) kfree(fh); if(atomic_dec_and_test(&dev->core->users)) - cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); + call_all(dev->core, core, s_standby, 0); return 0; } @@ -1276,15 +1270,12 @@ int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i) [ CX88_VMUX_DVB ] = "DVB", [ CX88_VMUX_DEBUG ] = "for debug only", }; - unsigned int n; + unsigned int n = i->index; - n = i->index; if (n >= 4) return -EINVAL; if (0 == INPUT(n).type) return -EINVAL; - memset(i,0,sizeof(*i)); - i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; strcpy(i->name,iname[INPUT(n).type]); if ((CX88_VMUX_TELEVISION == INPUT(n).type) || @@ -1402,7 +1393,7 @@ static int vidioc_g_frequency (struct file *file, void *priv, f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = core->freq; - cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f); + call_all(core, tuner, g_frequency, f); return 0; } @@ -1418,7 +1409,7 @@ int cx88_set_freq (struct cx88_core *core, mutex_lock(&core->lock); core->freq = f->frequency; cx88_newstation(core); - cx88_call_i2c_clients(core,VIDIOC_S_FREQUENCY,f); + call_all(core, tuner, s_frequency, f); /* When changing channels it is required to reset TVAUDIO */ msleep (10); @@ -1500,7 +1491,7 @@ static int radio_g_tuner (struct file *file, void *priv, strcpy(t->name, "Radio"); t->type = V4L2_TUNER_RADIO; - cx88_call_i2c_clients(core,VIDIOC_G_TUNER,t); + call_all(core, tuner, g_tuner, t); return 0; } @@ -1520,7 +1511,6 @@ static int radio_g_audio (struct file *file, void *priv, struct v4l2_audio *a) if (unlikely(a->index)) return -EINVAL; - memset(a,0,sizeof(*a)); strcpy(a->name,"Radio"); return 0; } @@ -1535,7 +1525,7 @@ static int radio_s_tuner (struct file *file, void *priv, if (0 != t->index) return -EINVAL; - cx88_call_i2c_clients(core,VIDIOC_S_TUNER,t); + call_all(core, tuner, s_tuner, t); return 0; } @@ -1892,12 +1882,30 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, /* load and configure helper modules */ if (core->board.audio_chip == V4L2_IDENT_WM8775) - request_module("wm8775"); + v4l2_i2c_new_subdev(&core->i2c_adap, + "wm8775", "wm8775", 0x36 >> 1); + + if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) { + /* This probes for a tda9874 as is used on some + Pixelview Ultra boards. */ + static const unsigned short i2c_addr[] = { + 0xb0 >> 1, I2C_CLIENT_END + }; + + v4l2_i2c_new_probed_subdev(&core->i2c_adap, + "tvaudio", "tvaudio", i2c_addr); + } switch (core->boardnr) { case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: - case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: + case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: { + static struct i2c_board_info rtc_info = { + I2C_BOARD_INFO("isl1208", 0x6f) + }; + request_module("rtc-isl1208"); + core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info); + } /* break intentionally omitted */ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: request_module("ir-kbd-i2c"); diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 6025fdd..9a43fdf 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -25,7 +25,7 @@ #include <linux/videodev2.h> #include <linux/kdev_t.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/tuner.h> #include <media/tveeprom.h> #include <media/videobuf-dma-sg.h> @@ -231,6 +231,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_SATTRADE_ST4200 76 #define CX88_BOARD_TBS_8910 77 #define CX88_BOARD_PROF_6200 78 +#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, @@ -302,7 +303,6 @@ struct cx88_dmaqueue { struct btcx_riscmem stopper; u32 count; }; -struct cx88_core; struct cx88_core { struct list_head devlist; @@ -327,6 +327,8 @@ struct cx88_core { u32 i2c_state, i2c_rc; /* config info -- analog */ + struct v4l2_device v4l2_dev; + struct i2c_client *i2c_rtc; unsigned int boardnr; struct cx88_board board; @@ -365,6 +367,22 @@ struct cx88_core { int active_fe_id; }; +static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct cx88_core, v4l2_dev); +} + +#define call_all(core, o, f, args...) \ + do { \ + if (!core->i2c_rc) { \ + if (core->gate_ctrl) \ + core->gate_ctrl(core, 1); \ + v4l2_device_call_all(&core->v4l2_dev, 0, o, f, ##args); \ + if (core->gate_ctrl) \ + core->gate_ctrl(core, 0); \ + } \ + } while (0) + struct cx8800_dev; struct cx8802_dev; @@ -610,8 +628,6 @@ extern struct videobuf_queue_ops cx8800_vbi_qops; /* cx88-i2c.c */ extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci); -extern void cx88_call_i2c_clients(struct cx88_core *core, - unsigned int cmd, void *arg); /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c index 298810d..ba3709b 100644 --- a/drivers/media/video/dabusb.c +++ b/drivers/media/video/dabusb.c @@ -189,17 +189,20 @@ static void dabusb_iso_complete (struct urb *purb) dst += len; } else - err("dabusb_iso_complete: invalid len %d", len); + dev_err(&purb->dev->dev, + "dabusb_iso_complete: invalid len %d\n", len); } else dev_warn(&purb->dev->dev, "dabusb_iso_complete: corrupted packet status: %d\n", purb->iso_frame_desc[i].status); if (dst != purb->actual_length) - err("dst!=purb->actual_length:%d!=%d", dst, purb->actual_length); + dev_err(&purb->dev->dev, + "dst!=purb->actual_length:%d!=%d\n", + dst, purb->actual_length); } if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) { s->overruns++; - err("overrun (%d)", s->overruns); + dev_err(&purb->dev->dev, "overrun (%d)\n", s->overruns); } wake_up (&s->wait); } @@ -220,13 +223,14 @@ static int dabusb_alloc_buffers (pdabusb_t s) while (transfer_len < (s->total_buffer_size << 10)) { b = kzalloc(sizeof (buff_t), GFP_KERNEL); if (!b) { - err("kzalloc(sizeof(buff_t))==NULL"); + dev_err(&s->usbdev->dev, + "kzalloc(sizeof(buff_t))==NULL\n"); goto err; } b->s = s; b->purb = usb_alloc_urb(packets, GFP_KERNEL); if (!b->purb) { - err("usb_alloc_urb == NULL"); + dev_err(&s->usbdev->dev, "usb_alloc_urb == NULL\n"); kfree (b); goto err; } @@ -235,7 +239,8 @@ static int dabusb_alloc_buffers (pdabusb_t s) if (!b->purb->transfer_buffer) { kfree (b->purb); kfree (b); - err("kmalloc(%d)==NULL", transfer_buffer_length); + dev_err(&s->usbdev->dev, + "kmalloc(%d)==NULL\n", transfer_buffer_length); goto err; } @@ -279,10 +284,11 @@ static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb) ret=usb_bulk_msg(s->usbdev, pipe, pb->data, pb->size, &actual_length, 100); if(ret<0) { - err("dabusb: usb_bulk_msg failed(%d)",ret); + dev_err(&s->usbdev->dev, + "usb_bulk_msg failed(%d)\n", ret); if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { - err("set_interface failed"); + dev_err(&s->usbdev->dev, "set_interface failed\n"); return -EINVAL; } @@ -291,7 +297,7 @@ static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb) if( ret == -EPIPE ) { dev_warn(&s->usbdev->dev, "CLEAR_FEATURE request to remove STALL condition.\n"); if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe))) - err("request failed"); + dev_err(&s->usbdev->dev, "request failed\n"); } pb->size = actual_length; @@ -305,7 +311,8 @@ static int dabusb_writemem (pdabusb_t s, int pos, const unsigned char *data, unsigned char *transfer_buffer = kmalloc (len, GFP_KERNEL); if (!transfer_buffer) { - err("dabusb_writemem: kmalloc(%d) failed.", len); + dev_err(&s->usbdev->dev, + "dabusb_writemem: kmalloc(%d) failed.\n", len); return -ENOMEM; } @@ -327,13 +334,14 @@ static int dabusb_loadmem (pdabusb_t s, const char *fname) { int ret; const struct ihex_binrec *rec; - const struct firmware *fw; + const struct firmware *uninitialized_var(fw); dbg("Enter dabusb_loadmem (internal)"); ret = request_ihex_firmware(&fw, "dabusb/firmware.fw", &s->usbdev->dev); if (ret) { - err("Failed to load \"dabusb/firmware.fw\": %d\n", ret); + dev_err(&s->usbdev->dev, + "Failed to load \"dabusb/firmware.fw\": %d\n", ret); goto out; } ret = dabusb_8051_reset (s, 1); @@ -346,9 +354,10 @@ static int dabusb_loadmem (pdabusb_t s, const char *fname) ret = dabusb_writemem(s, be32_to_cpu(rec->addr), rec->data, be16_to_cpu(rec->len)); if (ret < 0) { - err("dabusb_writemem failed (%d %04X %p %d)", ret, - be32_to_cpu(rec->addr), rec->data, - be16_to_cpu(rec->len)); + dev_err(&s->usbdev->dev, + "dabusb_writemem failed (%d %04X %p %d)\n", + ret, be32_to_cpu(rec->addr), + rec->data, be16_to_cpu(rec->len)); break; } } @@ -396,13 +405,15 @@ static int dabusb_fpga_download (pdabusb_t s, const char *fname) dbg("Enter dabusb_fpga_download (internal)"); if (!b) { - err("kmalloc(sizeof(bulk_transfer_t))==NULL"); + dev_err(&s->usbdev->dev, + "kmalloc(sizeof(bulk_transfer_t))==NULL\n"); return -ENOMEM; } ret = request_firmware(&fw, "dabusb/bitstream.bin", &s->usbdev->dev); if (ret) { - err("Failed to load \"dabusb/bitstream.bin\": %d\n", ret); + dev_err(&s->usbdev->dev, + "Failed to load \"dabusb/bitstream.bin\": %d\n", ret); kfree(b); return ret; } @@ -425,7 +436,7 @@ static int dabusb_fpga_download (pdabusb_t s, const char *fname) memcpy (b->data + 4, fw->data + 74 + n, 60); ret = dabusb_bulk (s, b); if (ret < 0) { - err("dabusb_bulk failed."); + dev_err(&s->usbdev->dev, "dabusb_bulk failed.\n"); break; } mdelay (1); @@ -478,9 +489,11 @@ static int dabusb_startrek (pdabusb_t s) ret = usb_submit_urb (end->purb, GFP_KERNEL); if (ret) { - err("usb_submit_urb returned:%d", ret); + dev_err(&s->usbdev->dev, + "usb_submit_urb returned:%d\n", ret); if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) - err("startrek: dabusb_add_buf_tail failed"); + dev_err(&s->usbdev->dev, + "startrek: dabusb_add_buf_tail failed\n"); break; } else @@ -523,7 +536,8 @@ static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, l spin_unlock_irqrestore(&s->lock, flags); - err("error: rec_buf_list is empty"); + dev_err(&s->usbdev->dev, + "error: rec_buf_list is empty\n"); goto err; } @@ -552,7 +566,8 @@ static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, l if (list_empty (&s->rec_buff_list)) { spin_unlock_irqrestore(&s->lock, flags); - err("error: still no buffer available."); + dev_err(&s->usbdev->dev, + "error: still no buffer available.\n"); goto err; } spin_unlock_irqrestore(&s->lock, flags); @@ -573,7 +588,7 @@ static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, l dbg("copy_to_user:%p %p %d",buf, purb->transfer_buffer + s->readptr, cnt); if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) { - err("read: copy_to_user failed"); + dev_err(&s->usbdev->dev, "read: copy_to_user failed\n"); if (!ret) ret = -EFAULT; goto err; @@ -587,7 +602,8 @@ static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, l if (s->readptr == purb->actual_length) { // finished, take next buffer if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) - err("read: dabusb_add_buf_tail failed"); + dev_err(&s->usbdev->dev, + "read: dabusb_add_buf_tail failed\n"); s->readptr = 0; } } @@ -623,7 +639,7 @@ static int dabusb_open (struct inode *inode, struct file *file) } if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { mutex_unlock(&s->mutex); - err("set_interface failed"); + dev_err(&s->usbdev->dev, "set_interface failed\n"); return -EINVAL; } s->opened = 1; @@ -648,7 +664,7 @@ static int dabusb_release (struct inode *inode, struct file *file) if (!s->remove_pending) { if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) - err("set_interface failed"); + dev_err(&s->usbdev->dev, "set_interface failed\n"); } else wake_up (&s->remove_ok); @@ -657,7 +673,7 @@ static int dabusb_release (struct inode *inode, struct file *file) return 0; } -static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long dabusb_ioctl (struct file *file, unsigned int cmd, unsigned long arg) { pdabusb_t s = (pdabusb_t) file->private_data; pbulk_transfer_t pbulk; @@ -666,13 +682,17 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm dbg("dabusb_ioctl"); - if (s->remove_pending) + lock_kernel(); + if (s->remove_pending) { + unlock_kernel(); return -EIO; + } mutex_lock(&s->mutex); if (!s->usbdev) { mutex_unlock(&s->mutex); + unlock_kernel(); return -EIO; } @@ -713,6 +733,7 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm break; } mutex_unlock(&s->mutex); + unlock_kernel(); return ret; } @@ -721,7 +742,7 @@ static const struct file_operations dabusb_fops = .owner = THIS_MODULE, .llseek = no_llseek, .read = dabusb_read, - .ioctl = dabusb_ioctl, + .unlocked_ioctl = dabusb_ioctl, .open = dabusb_open, .release = dabusb_release, }; @@ -764,7 +785,7 @@ static int dabusb_probe (struct usb_interface *intf, s->devnum = intf->minor; if (usb_reset_configuration (usbdev) < 0) { - err("reset_configuration failed"); + dev_err(&intf->dev, "reset_configuration failed\n"); goto reject; } if (le16_to_cpu(usbdev->descriptor.idProduct) == 0x2131) { @@ -775,7 +796,7 @@ static int dabusb_probe (struct usb_interface *intf, dabusb_fpga_download (s, NULL); if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) { - err("set_interface failed"); + dev_err(&intf->dev, "set_interface failed\n"); goto reject; } } diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index 2ac738f..0131322 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -56,7 +56,7 @@ MODULE_PARM_DESC(debug, "activates debug info"); static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static int em28xx_isoc_audio_deinit(struct em28xx *dev) +static int em28xx_deinit_isoc_audio(struct em28xx *dev) { int i; @@ -66,6 +66,7 @@ static int em28xx_isoc_audio_deinit(struct em28xx *dev) usb_kill_urb(dev->adev.urb[i]); else usb_unlink_urb(dev->adev.urb[i]); + usb_free_urb(dev->adev.urb[i]); dev->adev.urb[i] = NULL; @@ -87,6 +88,20 @@ static void em28xx_audio_isocirq(struct urb *urb) unsigned int stride; struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dprintk("urb completition error %d.\n", urb->status); + break; + } + if (dev->adev.capture_pcm_substream) { substream = dev->adev.capture_pcm_substream; runtime = substream->runtime; @@ -137,9 +152,6 @@ static void em28xx_audio_isocirq(struct urb *urb) } urb->status = 0; - if (dev->adev.shutdown) - return; - status = usb_submit_urb(urb, GFP_ATOMIC); if (status < 0) { em28xx_errdev("resubmit of audio urb failed (error=%i)\n", @@ -197,8 +209,7 @@ static int em28xx_init_audio_isoc(struct em28xx *dev) for (i = 0; i < EM28XX_AUDIO_BUFS; i++) { errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); if (errCode) { - em28xx_isoc_audio_deinit(dev); - + em28xx_deinit_isoc_audio(dev); return errCode; } } @@ -213,14 +224,16 @@ static int em28xx_cmd(struct em28xx *dev, int cmd, int arg) switch (cmd) { case EM28XX_CAPTURE_STREAM_EN: - if (dev->adev.capture_stream == STREAM_OFF && arg == 1) { + if (dev->adev.capture_stream == STREAM_OFF && + arg == EM28XX_START_AUDIO) { dev->adev.capture_stream = STREAM_ON; em28xx_init_audio_isoc(dev); - } else if (dev->adev.capture_stream == STREAM_ON && arg == 0) { + } else if (dev->adev.capture_stream == STREAM_ON && + arg == EM28XX_STOP_AUDIO) { dev->adev.capture_stream = STREAM_OFF; - em28xx_isoc_audio_deinit(dev); + em28xx_deinit_isoc_audio(dev); } else { - printk(KERN_ERR "An underrun very likely occurred. " + em28xx_errdev("An underrun very likely occurred. " "Ignoring it.\n"); } return 0; @@ -234,7 +247,7 @@ static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, { struct snd_pcm_runtime *runtime = subs->runtime; - dprintk("Alocating vbuffer\n"); + dprintk("Allocating vbuffer\n"); if (runtime->dma_area) { if (runtime->dma_bytes > size) return 0; @@ -302,7 +315,9 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) dprintk("changing alternate number to 7\n"); } + mutex_lock(&dev->lock); dev->adev.users++; + mutex_unlock(&dev->lock); snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); dev->adev.capture_pcm_substream = substream; @@ -317,22 +332,15 @@ err: static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) { struct em28xx *dev = snd_pcm_substream_chip(substream); - dev->adev.users--; dprintk("closing device\n"); dev->mute = 1; mutex_lock(&dev->lock); + dev->adev.users--; em28xx_audio_analog_set(dev); mutex_unlock(&dev->lock); - if (dev->adev.users == 0 && dev->adev.shutdown == 1) { - dprintk("audio users: %d\n", dev->adev.users); - dprintk("disabling audio stream!\n"); - dev->adev.shutdown = 0; - dprintk("released lock\n"); - em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0); - } return 0; } @@ -363,7 +371,7 @@ static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream) dprintk("Stop capture, if needed\n"); if (dev->adev.capture_stream == STREAM_ON) - em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0); + em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_STOP_AUDIO); return 0; } @@ -377,33 +385,40 @@ static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct em28xx *dev = snd_pcm_substream_chip(substream); + int retval; + + dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START) ? + "start" : "stop"); - dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START)? - "start": "stop"); + spin_lock(&dev->adev.slock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 1); - return 0; + em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_START_AUDIO); + retval = 0; + break; case SNDRV_PCM_TRIGGER_STOP: - dev->adev.shutdown = 1; - return 0; + em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_STOP_AUDIO); + retval = 0; + break; default: - return -EINVAL; + retval = -EINVAL; } + + spin_unlock(&dev->adev.slock); + return retval; } static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream *substream) { - unsigned long flags; - + unsigned long flags; struct em28xx *dev; snd_pcm_uframes_t hwptr_done; dev = snd_pcm_substream_chip(substream); - spin_lock_irqsave(&dev->adev.slock, flags); + spin_lock_irqsave(&dev->adev.slock, flags); hwptr_done = dev->adev.hwptr_done_capture; - spin_unlock_irqrestore(&dev->adev.slock, flags); + spin_unlock_irqrestore(&dev->adev.slock, flags); return hwptr_done; } @@ -448,9 +463,10 @@ static int em28xx_audio_init(struct em28xx *dev) printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " "Rechberger\n"); - card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0); - if (card == NULL) - return -ENOMEM; + err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0, + &card); + if (err < 0) + return err; spin_lock_init(&adev->slock); err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 3b3ca3f..0f48c0f 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -122,6 +122,22 @@ static struct em28xx_reg_seq default_tuner_gpio[] = { { -1, -1, -1, -1}, }; +/* Mute/unmute */ +static struct em28xx_reg_seq compro_unmute_tv_gpio[] = { + {EM28XX_R08_GPIO, 5, 7, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq compro_unmute_svid_gpio[] = { + {EM28XX_R08_GPIO, 4, 7, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq compro_mute_gpio[] = { + {EM28XX_R08_GPIO, 6, 7, 10}, + { -1, -1, -1, -1}, +}; + /* * Board definitions */ @@ -183,6 +199,25 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2820_BOARD_GADMEI_TVR200] = { + .name = "Gadmei TVR200", + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, [EM2820_BOARD_TERRATEC_CINERGY_250] = { .name = "Terratec Cinergy 250 USB", .tuner_type = TUNER_LG_PAL_NEW_TAPC, @@ -225,7 +260,7 @@ struct em28xx_board em28xx_boards[] = { .name = "Hauppauge WinTV USB 2", .tuner_type = TUNER_PHILIPS_FM1236_MK3, .tda9887_conf = TDA9887_PRESENT | - TDA9887_PORT1_ACTIVE| + TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, .decoder = EM28XX_TVP5150, .has_msp34xx = 1, @@ -350,26 +385,6 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_VIDEO, } }, }, - [EM2821_BOARD_PROLINK_PLAYTV_USB2] = { - .name = "SIIG AVTuner-PVR/Prolink PlayTV USB 2.0", - .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_LG_PAL_NEW_TAPC, /* unknown? */ - .tda9887_conf = TDA9887_PRESENT, /* unknown? */ - .decoder = EM28XX_SAA711X, - .input = { { - .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, - .amux = EM28XX_AMUX_LINE_IN, - }, { - .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_LINE_IN, - } }, - }, [EM2821_BOARD_SUPERCOMP_USB_2] = { .name = "Supercomp USB 2.0 TV", .valid = EM28XX_BOARD_NOT_VALIDATED, @@ -498,7 +513,7 @@ struct em28xx_board em28xx_boards[] = { }, [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = { .name = "Yakumo MovieMixer", - .tuner_type = TUNER_ABSENT, /* Capture only device */ + .tuner_type = TUNER_ABSENT, /* Capture only device */ .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -604,6 +619,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = ir_codes_hauppauge_new, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -628,6 +644,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, + .ir_codes = ir_codes_hauppauge_new, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -842,11 +859,11 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2800_BOARD_GRABBEEX_USB2800] = { - .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder", - .is_em2800 = 1, - .decoder = EM28XX_SAA711X, - .tuner_type = TUNER_ABSENT, /* capture only board */ - .input = { { + .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder", + .is_em2800 = 1, + .decoder = EM28XX_SAA711X, + .tuner_type = TUNER_ABSENT, /* capture only board */ + .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = SAA7115_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, @@ -897,7 +914,7 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_PINNACLE_DVC_90] = { - .name = "Pinnacle Dazzle DVC 90/DVC 100", + .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker", .tuner_type = TUNER_ABSENT, /* capture only board */ .decoder = EM28XX_SAA711X, .input = { { @@ -952,7 +969,7 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_PROLINK_PLAYTV_USB2] = { - .name = "Pixelview Prolink PlayTV USB 2.0", + .name = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0", .has_snapshot_button = 1, .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_YMEC_TVF_5533MF, @@ -1198,7 +1215,9 @@ struct em28xx_board em28xx_boards[] = { .has_dvb = 1, .dvb_gpio = kworld_330u_digital, .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, - .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_EEPROM_ON_BOARD | EM28XX_I2C_EEPROM_KEY_VALID, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_EEPROM_ON_BOARD | + EM28XX_I2C_EEPROM_KEY_VALID, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1223,21 +1242,88 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_TVP5150, + .adecoder = EM28XX_TVAUDIO, + .mute_gpio = compro_mute_gpio, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = compro_unmute_tv_gpio, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = compro_unmute_svid_gpio, + } }, + }, + [EM2860_BOARD_KAIOMY_TVNPC_U2] = { + .name = "Kaiomy TVnPC U2", + .vchannels = 3, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .mts_firmware = 1, + .decoder = EM28XX_TVP5150, + .tuner_gpio = default_tuner_gpio, + .ir_codes = ir_codes_kaiomy, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, } }, + .radio = { + .type = EM28XX_RADIO, + .amux = EM28XX_AMUX_LINE_IN, + } + }, + [EM2860_BOARD_EASYCAP] = { + .name = "Easy Cap Capture DC-60", + .vchannels = 2, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, + [EM2820_BOARD_IODATA_GVMVP_SZ] = { + .name = "IO-DATA GV-MVP/SZ", + .tuner_type = TUNER_PHILIPS_FM1236_MK3, + .tuner_gpio = default_tuner_gpio, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + }, { /* Composite has not been tested yet */ + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_VIDEO, + }, { /* S-video has not been tested yet */ + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_VIDEO, + } }, }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); /* table of devices that work with this driver */ -struct usb_device_id em28xx_id_table [] = { +struct usb_device_id em28xx_id_table[] = { { USB_DEVICE(0xeb1a, 0x2750), .driver_info = EM2750_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2751), @@ -1260,6 +1346,8 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0xe300), .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U }, + { USB_DEVICE(0xeb1a, 0xe303), + .driver_info = EM2860_BOARD_KAIOMY_TVNPC_U2 }, { USB_DEVICE(0xeb1a, 0xe305), .driver_info = EM2880_BOARD_KWORLD_DVB_305U }, { USB_DEVICE(0xeb1a, 0xe310), @@ -1278,6 +1366,8 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2800_BOARD_GRABBEEX_USB2800 }, { USB_DEVICE(0xeb1a, 0xe357), .driver_info = EM2870_BOARD_KWORLD_355U }, + { USB_DEVICE(0x1b80, 0xe302), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */ { USB_DEVICE(0x0ccd, 0x0036), .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 }, { USB_DEVICE(0x0ccd, 0x004c), @@ -1330,6 +1420,8 @@ struct usb_device_id em28xx_id_table [] = { .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII }, { USB_DEVICE(0x093b, 0xa005), .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U }, + { USB_DEVICE(0x04bb, 0x0515), + .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -1337,7 +1429,7 @@ MODULE_DEVICE_TABLE(usb, em28xx_id_table); /* * EEPROM hash table for devices with generic USB IDs */ -static struct em28xx_hash_table em28xx_eeprom_hash [] = { +static struct em28xx_hash_table em28xx_eeprom_hash[] = { /* P/N: SA 60002070465 Tuner: TVF7533-MF */ {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, @@ -1349,6 +1441,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC}, {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, {0x1ba50080, EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA, TUNER_ABSENT}, + {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, }; int em28xx_tuner_callback(void *ptr, int component, int command, int arg) @@ -1368,7 +1461,7 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg) } EXPORT_SYMBOL_GPL(em28xx_tuner_callback); -static void inline em28xx_set_model(struct em28xx *dev) +static inline void em28xx_set_model(struct em28xx *dev) { memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board)); @@ -1504,6 +1597,34 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* enables audio for that devices */ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); break; + + case EM2860_BOARD_KAIOMY_TVNPC_U2: + em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1); + em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); + em28xx_write_regs(dev, 0x0d, "\x42", 1); + em28xx_write_regs(dev, 0x08, "\xfd", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\xff", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\x7f", 1); + msleep(10); + em28xx_write_regs(dev, 0x08, "\x6b", 1); + + break; + case EM2860_BOARD_EASYCAP: + em28xx_write_regs(dev, 0x08, "\xf8", 1); + break; + + case EM2820_BOARD_IODATA_GVMVP_SZ: + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); + msleep(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7); + msleep(10); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); + msleep(70); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); + msleep(70); + break; } em28xx_gpio_set(dev, dev->board.tuner_gpio); @@ -1610,7 +1731,7 @@ static int em28xx_hint_board(struct em28xx *dev) em28xx_errdev("If the board were missdetected, " "please email this log to:\n"); em28xx_errdev("\tV4L Mailing List " - " <video4linux-list@redhat.com>\n"); + " <linux-media@vger.kernel.org>\n"); em28xx_errdev("Board detected as %s\n", em28xx_boards[dev->model].name); @@ -1642,7 +1763,7 @@ static int em28xx_hint_board(struct em28xx *dev) em28xx_errdev("If the board were missdetected, " "please email this log to:\n"); em28xx_errdev("\tV4L Mailing List " - " <video4linux-list@redhat.com>\n"); + " <linux-media@vger.kernel.org>\n"); em28xx_errdev("Board detected as %s\n", em28xx_boards[dev->model].name); @@ -1655,7 +1776,7 @@ static int em28xx_hint_board(struct em28xx *dev) em28xx_errdev("You may try to use card=<n> insmod option to " "workaround that.\n"); em28xx_errdev("Please send an email with this log to:\n"); - em28xx_errdev("\tV4L Mailing List <video4linux-list@redhat.com>\n"); + em28xx_errdev("\tV4L Mailing List <linux-media@vger.kernel.org>\n"); em28xx_errdev("Board eeprom hash is 0x%08lx\n", dev->hash); em28xx_errdev("Board i2c devicelist hash is 0x%08lx\n", dev->i2c_hash); @@ -1800,6 +1921,8 @@ void em28xx_card_setup(struct em28xx *dev) request_module("tvp5150"); if (dev->board.tuner_type != TUNER_ABSENT) request_module("tuner"); + if (dev->board.adecoder == EM28XX_TVAUDIO) + request_module("tvaudio"); #endif em28xx_config_tuner(dev); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 94fb1b6..8f1999c 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -33,8 +33,8 @@ /* #define ENABLE_DEBUG_ISOC_FRAMES */ static unsigned int core_debug; -module_param(core_debug,int,0644); -MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); +module_param(core_debug, int, 0644); +MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); #define em28xx_coredbg(fmt, arg...) do {\ if (core_debug) \ @@ -42,8 +42,8 @@ MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); dev->name, __func__ , ##arg); } while (0) static unsigned int reg_debug; -module_param(reg_debug,int,0644); -MODULE_PARM_DESC(reg_debug,"enable debug messages [URB reg]"); +module_param(reg_debug, int, 0644); +MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); #define em28xx_regdbg(fmt, arg...) do {\ if (reg_debug) \ @@ -77,7 +77,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg, return -EINVAL; if (reg_debug) { - printk( KERN_DEBUG "(pipe 0x%08x): " + printk(KERN_DEBUG "(pipe 0x%08x): " "IN: %02x %02x %02x %02x %02x %02x %02x %02x ", pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, @@ -154,7 +154,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf, if (reg_debug) { int byte; - printk( KERN_DEBUG "(pipe 0x%08x): " + printk(KERN_DEBUG "(pipe 0x%08x): " "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>", pipe, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, @@ -378,6 +378,11 @@ static int em28xx_set_audio_source(struct em28xx *dev) } } + if (dev->board.mute_gpio && dev->mute) + em28xx_gpio_set(dev, dev->board.mute_gpio); + else + em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio); + ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0); if (ret < 0) return ret; @@ -424,7 +429,7 @@ int em28xx_audio_analog_set(struct em28xx *dev) xclk = dev->board.xclk & 0x7f; if (!dev->mute) - xclk |= 0x80; + xclk |= EM28XX_XCLK_AUDIO_UNMUTE; ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk); if (ret < 0) @@ -462,7 +467,8 @@ int em28xx_audio_analog_set(struct em28xx *dev) if (dev->ctl_aoutput & EM28XX_AOUT_PCM_IN) { int sel = ac97_return_record_select(dev->ctl_aoutput); - /* Use the same input for both left and right channels */ + /* Use the same input for both left and right + channels */ sel |= (sel << 8); em28xx_write_ac97(dev, AC97_RECORD_SELECT, sel); @@ -698,7 +704,7 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2); /* it seems that both H and V scalers must be active to work correctly */ - mode = (h || v)? 0x30: 0x00; + mode = (h || v) ? 0x30 : 0x00; } return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30); } @@ -827,6 +833,19 @@ static void em28xx_irq_callback(struct urb *urb) struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); int rc, i; + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + em28xx_isocdbg("urb completition error %d.\n", urb->status); + break; + } + /* Copy data from URB */ spin_lock(&dev->slock); rc = dev->isoc_ctl.isoc_copy(dev, urb); @@ -945,7 +964,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, em28xx_err("unable to allocate %i bytes for transfer" " buffer %i%s\n", sb_size, i, - in_interrupt()?" while in int":""); + in_interrupt() ? " while in int" : ""); em28xx_uninit_isoc(dev); return -ENOMEM; } @@ -963,7 +982,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, em28xx_irq_callback, dma_q, 1); urb->number_of_packets = max_packets; - urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; k = 0; for (j = 0; j < max_packets; j++) { diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 9ad8527..fcd2551 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -29,9 +29,6 @@ #include "lgdt330x.h" #include "zl10353.h" #include "s5h1409.h" -#ifdef EM28XX_DRX397XD_SUPPORT -#include "drx397xD.h" -#endif MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index d69f0ef..02c12fe 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -402,10 +402,12 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len) dev->name); break; case 2: - printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n", dev->name); + printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n", + dev->name); break; case 3: - printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n", dev->name); + printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n", + dev->name); break; } @@ -508,12 +510,17 @@ static int attach_inform(struct i2c_client *client) dprintk1(1, "attach_inform: tvp5150 detected.\n"); break; + case 0xb0: + dprintk1(1, "attach_inform: tda9874 detected\n"); + break; + default: if (!dev->tuner_addr) dev->tuner_addr = client->addr; dprintk1(1, "attach inform: detected I2C address %x\n", client->addr << 1); + dprintk1(1, "driver id %d\n", client->driver->id); } @@ -552,6 +559,7 @@ static char *i2c_devs[128] = { [0x80 >> 1] = "msp34xx", [0x88 >> 1] = "msp34xx", [0xa0 >> 1] = "eeprom", + [0xb0 >> 1] = "tda9874", [0xb8 >> 1] = "tvp5150a", [0xba >> 1] = "tvp5150a", [0xc0 >> 1] = "tuner (analog)", diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 0443afe..a5abfd7 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -68,8 +68,7 @@ struct em28xx_IR { /* poll external decoder */ int polling; - struct work_struct work; - struct timer_list timer; + struct delayed_work work; unsigned int last_toggle:1; unsigned int last_readcount; unsigned int repeat_interval; @@ -292,32 +291,23 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) return; } -static void ir_timer(unsigned long data) -{ - struct em28xx_IR *ir = (struct em28xx_IR *)data; - - schedule_work(&ir->work); -} - static void em28xx_ir_work(struct work_struct *work) { - struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work); + struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); em28xx_ir_handle_key(ir); - mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); } static void em28xx_ir_start(struct em28xx_IR *ir) { - setup_timer(&ir->timer, ir_timer, (unsigned long)ir); - INIT_WORK(&ir->work, em28xx_ir_work); - schedule_work(&ir->work); + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); + schedule_delayed_work(&ir->work, 0); } static void em28xx_ir_stop(struct em28xx_IR *ir) { - del_timer_sync(&ir->timer); - flush_scheduled_work(); + cancel_delayed_work_sync(&ir->work); } int em28xx_ir_init(struct em28xx *dev) diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 8e61b2c..575472f 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -186,7 +186,8 @@ static void em28xx_copy_video(struct em28xx *dev, em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", ((char *)startwrite + lencopy) - ((char *)outp + buf->vb.size)); - lencopy = remain = (char *)outp + buf->vb.size - (char *)startwrite; + remain = (char *)outp + buf->vb.size - (char *)startwrite; + lencopy = remain; } if (lencopy <= 0) return; @@ -202,7 +203,8 @@ static void em28xx_copy_video(struct em28xx *dev, else lencopy = bytesperline; - if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + if ((char *)startwrite + lencopy > (char *)outp + + buf->vb.size) { em28xx_isocdbg("Overflow of %zi bytes past buffer end (2)\n", ((char *)startwrite + lencopy) - ((char *)outp + buf->vb.size)); @@ -347,7 +349,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) } if (p[0] == 0x22 && p[1] == 0x5a) { em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], - len, (p[2] & 1)? "odd" : "even"); + len, (p[2] & 1) ? "odd" : "even"); if (!(p[2] & 1)) { if (buf != NULL) @@ -476,7 +478,9 @@ fail: static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx_buffer *buf = container_of(vb, + struct em28xx_buffer, + vb); struct em28xx_fh *fh = vq->priv_data; struct em28xx *dev = fh->dev; struct em28xx_dmaqueue *vidq = &dev->vidq; @@ -489,7 +493,9 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) { - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx_buffer *buf = container_of(vb, + struct em28xx_buffer, + vb); struct em28xx_fh *fh = vq->priv_data; struct em28xx *dev = (struct em28xx *)fh->dev; @@ -534,6 +540,13 @@ static void video_mux(struct em28xx *dev, int index) &route); } + if (dev->board.adecoder != EM28XX_NOADECODER) { + route.input = dev->ctl_ainput; + route.output = dev->ctl_aoutput; + em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, + &route); + } + em28xx_audio_analog_set(dev); } @@ -557,7 +570,7 @@ static int res_get(struct em28xx_fh *fh) static int res_check(struct em28xx_fh *fh) { - return (fh->stream_on); + return fh->stream_on; } static void res_free(struct em28xx_fh *fh) @@ -791,7 +804,7 @@ out: return rc; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; @@ -1008,8 +1021,13 @@ static int vidioc_g_ctrl(struct file *file, void *priv, if (dev->board.has_msp34xx) em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl); - else + else { rc = em28xx_get_ctrl(dev, ctrl); + if (rc < 0) { + em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl); + rc = 0; + } + } mutex_unlock(&dev->lock); return rc; @@ -1345,7 +1363,7 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); cap->version = EM28XX_VERSION_CODE; @@ -1431,7 +1449,7 @@ static int vidioc_reqbufs(struct file *file, void *priv, if (rc < 0) return rc; - return (videobuf_reqbufs(&fh->vb_vidq, rb)); + return videobuf_reqbufs(&fh->vb_vidq, rb); } static int vidioc_querybuf(struct file *file, void *priv, @@ -1445,7 +1463,7 @@ static int vidioc_querybuf(struct file *file, void *priv, if (rc < 0) return rc; - return (videobuf_querybuf(&fh->vb_vidq, b)); + return videobuf_querybuf(&fh->vb_vidq, b); } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) @@ -1458,7 +1476,7 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) if (rc < 0) return rc; - return (videobuf_qbuf(&fh->vb_vidq, b)); + return videobuf_qbuf(&fh->vb_vidq, b); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) @@ -1471,8 +1489,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) if (rc < 0) return rc; - return (videobuf_dqbuf(&fh->vb_vidq, b, - file->f_flags & O_NONBLOCK)); + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); } #ifdef CONFIG_VIDEO_V4L1_COMPAT @@ -1496,7 +1513,7 @@ static int radio_querycap(struct file *file, void *priv, strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); cap->version = EM28XX_VERSION_CODE; cap->capabilities = V4L2_CAP_TUNER; @@ -1781,7 +1798,7 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, * em28xx_v4l2_poll() * will allocate buffers when called for the first time */ -static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait) +static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) { struct em28xx_fh *fh = filp->private_data; struct em28xx *dev = fh->dev; @@ -1934,8 +1951,8 @@ static struct video_device em28xx_radio_template = { static struct video_device *em28xx_vdev_init(struct em28xx *dev, - const struct video_device *template, - const char *type_name) + const struct video_device *template, + const char *type_name) { struct video_device *vfd; @@ -1984,8 +2001,9 @@ int em28xx_register_analog_devices(struct em28xx *dev) /* enable vbi capturing */ /* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ - val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK); - em28xx_write_reg(dev, EM28XX_R0F_XCLK, (EM28XX_XCLK_AUDIO_UNMUTE | val)); + val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK); + em28xx_write_reg(dev, EM28XX_R0F_XCLK, + (EM28XX_XCLK_AUDIO_UNMUTE | val)); em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51); em28xx_set_outfmt(dev); @@ -2020,7 +2038,8 @@ int em28xx_register_analog_devices(struct em28xx *dev) } if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { - dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, "radio"); + dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, + "radio"); if (!dev->radio_dev) { em28xx_errdev("cannot allocate video_device.\n"); return -ENODEV; diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index dd2cd36..a33a58d 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -70,7 +70,6 @@ #define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30 #define EM2821_BOARD_USBGEAR_VD204 31 #define EM2821_BOARD_SUPERCOMP_USB_2 32 -#define EM2821_BOARD_PROLINK_PLAYTV_USB2 33 #define EM2860_BOARD_TERRATEC_HYBRID_XS 34 #define EM2860_BOARD_TYPHOON_DVD_MAKER 35 #define EM2860_BOARD_NETGMBH_CAM 36 @@ -98,6 +97,10 @@ #define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58 #define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60 #define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61 +#define EM2820_BOARD_GADMEI_TVR200 62 +#define EM2860_BOARD_KAIOMY_TVNPC_U2 63 +#define EM2860_BOARD_EASYCAP 64 +#define EM2820_BOARD_IODATA_GVMVP_SZ 65 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -110,6 +113,10 @@ #define EM28XX_BOARD_NOT_VALIDATED 1 #define EM28XX_BOARD_VALIDATED 0 +/* Params for em28xx_cmd() audio */ +#define EM28XX_START_AUDIO 1 +#define EM28XX_STOP_AUDIO 0 + /* maximum number of em28xx boards */ #define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */ @@ -154,7 +161,8 @@ */ /* time to wait when stopping the isoc transfer */ -#define EM28XX_URB_TIMEOUT msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS) +#define EM28XX_URB_TIMEOUT \ + msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS) /* time in msecs to wait for i2c writes to finish */ #define EM2800_I2C_WRITE_TIMEOUT 20 @@ -348,6 +356,11 @@ enum em28xx_decoder { EM28XX_SAA711X, }; +enum em28xx_adecoder { + EM28XX_NOADECODER = 0, + EM28XX_TVAUDIO, +}; + struct em28xx_board { char *name; int vchannels; @@ -361,6 +374,7 @@ struct em28xx_board { struct em28xx_reg_seq *dvb_gpio; struct em28xx_reg_seq *suspend_gpio; struct em28xx_reg_seq *tuner_gpio; + struct em28xx_reg_seq *mute_gpio; unsigned int is_em2800:1; unsigned int has_msp34xx:1; @@ -373,6 +387,7 @@ struct em28xx_board { unsigned char xclk, i2c_speed; enum em28xx_decoder decoder; + enum em28xx_adecoder adecoder; struct em28xx_input input[MAX_EM28XX_INPUT]; struct em28xx_input radio; @@ -420,7 +435,7 @@ struct em28xx_audio { unsigned int hwptr_done_capture; struct snd_card *sndcard; - int users, shutdown; + int users; enum em28xx_stream_state capture_stream; spinlock_t slock; }; @@ -523,7 +538,8 @@ struct em28xx { int num_alt; /* Number of alternative settings */ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ struct urb *urb[EM28XX_NUM_BUFS]; /* urb for isoc transfers */ - char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc transfer */ + char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc + transfer */ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ /* helper funcs that call usb_control_msg */ diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index ee6a691..578dc4f 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -56,6 +56,15 @@ config USB_GSPCA_MARS To compile this driver as a module, choose M here: the module will be called gspca_mars. +config USB_GSPCA_MR97310A + tristate "Mars-Semi MR97310A USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the MR97310A chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_mr97310a. + config USB_GSPCA_OV519 tristate "OV519 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -167,6 +176,24 @@ config USB_GSPCA_SPCA561 To compile this driver as a module, choose M here: the module will be called gspca_spca561. +config USB_GSPCA_SQ905 + tristate "SQ Technologies SQ905 based USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SQ905 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_sq905. + +config USB_GSPCA_SQ905C + tristate "SQ Technologies SQ905C based USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SQ905C chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_sq905c. + config USB_GSPCA_STK014 tristate "Syntek DV4000 (STK014) USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index bd8d9ee..8a6643e 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -1,50 +1,56 @@ -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_MARS) += gspca_mars.o -obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o -obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o -obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o -obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o -obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o -obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o -obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o -obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o -obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o -obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o -obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o -obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o -obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o -obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o -obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o -obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o -obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o -obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o +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_MARS) += gspca_mars.o +obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o +obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o +obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o +obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o +obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o +obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o +obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o +obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o +obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o +obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o +obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o +obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o +obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o +obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o +obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o +obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o +obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o +obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o +obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o +obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o +obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o -gspca_main-objs := gspca.o -gspca_conex-objs := conex.o -gspca_etoms-objs := etoms.o -gspca_finepix-objs := finepix.o -gspca_mars-objs := mars.o -gspca_ov519-objs := ov519.o -gspca_ov534-objs := ov534.o -gspca_pac207-objs := pac207.o -gspca_pac7311-objs := pac7311.o -gspca_sonixb-objs := sonixb.o -gspca_sonixj-objs := sonixj.o -gspca_spca500-objs := spca500.o -gspca_spca501-objs := spca501.o -gspca_spca505-objs := spca505.o -gspca_spca506-objs := spca506.o -gspca_spca508-objs := spca508.o -gspca_spca561-objs := spca561.o -gspca_stk014-objs := stk014.o -gspca_sunplus-objs := sunplus.o -gspca_t613-objs := t613.o -gspca_tv8532-objs := tv8532.o -gspca_vc032x-objs := vc032x.o -gspca_zc3xx-objs := zc3xx.o +gspca_main-objs := gspca.o +gspca_conex-objs := conex.o +gspca_etoms-objs := etoms.o +gspca_finepix-objs := finepix.o +gspca_mars-objs := mars.o +gspca_mr97310a-objs := mr97310a.o +gspca_ov519-objs := ov519.o +gspca_ov534-objs := ov534.o +gspca_pac207-objs := pac207.o +gspca_pac7311-objs := pac7311.o +gspca_sonixb-objs := sonixb.o +gspca_sonixj-objs := sonixj.o +gspca_spca500-objs := spca500.o +gspca_spca501-objs := spca501.o +gspca_spca505-objs := spca505.o +gspca_spca506-objs := spca506.o +gspca_spca508-objs := spca508.o +gspca_spca561-objs := spca561.o +gspca_sq905-objs := sq905.o +gspca_sq905c-objs := sq905c.o +gspca_stk014-objs := stk014.o +gspca_sunplus-objs := sunplus.o +gspca_t613-objs := t613.o +gspca_tv8532-objs := tv8532.o +gspca_vc032x-objs := vc032x.o +gspca_zc3xx-objs := zc3xx.o -obj-$(CONFIG_USB_M5602) += m5602/ -obj-$(CONFIG_USB_STV06XX) += stv06xx/ +obj-$(CONFIG_USB_M5602) += m5602/ +obj-$(CONFIG_USB_STV06XX) += stv06xx/ diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index 1753f5b..219cfa6 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -36,8 +36,12 @@ struct sd { unsigned char brightness; unsigned char contrast; unsigned char colors; + u8 quality; +#define QUALITY_MIN 30 +#define QUALITY_MAX 60 +#define QUALITY_DEF 40 - unsigned char qindex; + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -815,14 +819,13 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->epaddr = 0x01; cam->cam_mode = vga_mode; cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; - sd->qindex = 0; /* set the quantization */ sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; + sd->quality = QUALITY_DEF; return 0; } @@ -839,6 +842,14 @@ static int sd_init(struct gspca_dev *gspca_dev) static int sd_start(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + cx11646_initsize(gspca_dev); cx11646_fw(gspca_dev); cx_sensor(gspca_dev); @@ -849,8 +860,11 @@ static int sd_start(struct gspca_dev *gspca_dev) /* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int retry = 50; + kfree(sd->jpeg_hdr); + if (!gspca_dev->present) return; reg_w_val(gspca_dev, 0x0000, 0x00); @@ -876,6 +890,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; + if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ @@ -883,9 +899,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data, 0); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, - ((struct sd *) gspca_dev)->qindex, - 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); data += 2; len -= 2; } @@ -988,6 +1003,34 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + if (gspca_dev->streaming) + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + /* sub-driver description */ static struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -998,6 +1041,8 @@ static struct sd_desc sd_desc = { .start = sd_start, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ @@ -1029,8 +1074,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c index f3cd8ff..2c20d06 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -472,19 +472,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) reg_w_val(gspca_dev, ET_O_RED + i, brightness); } -static void getbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - int brightness = 0; - - for (i = 0; i < 4; i++) { - reg_r(gspca_dev, ET_O_RED + i, 1); - brightness += gspca_dev->usb_buf[0]; - } - sd->brightness = brightness >> 3; -} - static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -495,19 +482,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) reg_w(gspca_dev, ET_G_RED, RGBG, 6); } -static void getcontrast(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - int contrast = 0; - - for (i = 0; i < 4; i++) { - reg_r(gspca_dev, ET_G_RED + i, 1); - contrast += gspca_dev->usb_buf[0]; - } - sd->contrast = contrast >> 2; -} - static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -658,7 +632,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->epaddr = 1; sd->sensor = id->driver_info; if (sd->sensor == SENSOR_PAS106) { cam->cam_mode = sif_mode; @@ -821,7 +794,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -840,7 +812,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcontrast(gspca_dev); *val = sd->contrast; return 0; } @@ -859,7 +830,6 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcolors(gspca_dev); *val = sd->colors; return 0; } @@ -928,8 +898,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index afc8b2d..00e6863 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -27,7 +27,7 @@ MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver"); MODULE_LICENSE("GPL"); /* Default timeout, in ms */ -#define FPIX_TIMEOUT (HZ / 10) +#define FPIX_TIMEOUT 250 /* Maximum transfer size to use. The windows driver reads by chunks of * 0x2000 bytes, so do the same. Note: reading more seems to work @@ -38,38 +38,15 @@ MODULE_LICENSE("GPL"); struct usb_fpix { struct gspca_dev gspca_dev; /* !! must be the first item */ - /* - * USB stuff - */ - struct usb_ctrlrequest ctrlreq; - struct urb *control_urb; - struct timer_list bulk_timer; - - enum { - FPIX_NOP, /* inactive, else streaming */ - FPIX_RESET, /* must reset */ - FPIX_REQ_FRAME, /* requesting a frame */ - FPIX_READ_FRAME, /* reading frame */ - } state; - - /* - * Driver stuff - */ - struct delayed_work wqe; - struct completion can_close; - int streaming; + struct work_struct work_struct; + struct workqueue_struct *work_thread; }; /* Delay after which claim the next frame. If the delay is too small, * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms - * will fail every 4 or 5 frames, but 30ms is perfect. */ -#define NEXT_FRAME_DELAY (((HZ * 30) + 999) / 1000) - -#define dev_new_state(new_state) { \ - PDEBUG(D_STREAM, "new state from %d to %d at %s:%d", \ - dev->state, new_state, __func__, __LINE__); \ - dev->state = new_state; \ -} + * will fail every 4 or 5 frames, but 30ms is perfect. On the A210, + * 30ms is bad while 35ms is perfect. */ +#define NEXT_FRAME_DELAY 35 /* These cameras only support 320x200. */ static const struct v4l2_pix_format fpix_mode[1] = { @@ -80,316 +57,183 @@ static const struct v4l2_pix_format fpix_mode[1] = { .priv = 0} }; -/* Reads part of a frame */ -static void read_frame_part(struct usb_fpix *dev) +/* send a command to the webcam */ +static int command(struct gspca_dev *gspca_dev, + int order) /* 0: reset, 1: frame request */ { - int ret; + static u8 order_values[2][12] = { + {0xc6, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0}, /* reset */ + {0xd3, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0}, /* fr req */ + }; - PDEBUG(D_STREAM, "read_frame_part"); - - /* Reads part of a frame */ - ret = usb_submit_urb(dev->gspca_dev.urb[0], GFP_ATOMIC); - if (ret) { - dev_new_state(FPIX_RESET); - schedule_delayed_work(&dev->wqe, 1); - PDEBUG(D_STREAM, "usb_submit_urb failed with %d", - ret); - } else { - /* Sometimes we never get a callback, so use a timer. - * Is this masking a bug somewhere else? */ - dev->bulk_timer.expires = jiffies + msecs_to_jiffies(150); - add_timer(&dev->bulk_timer); - } + memcpy(gspca_dev->usb_buf, order_values[order], 12); + return usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_GET_STATUS, + USB_DIR_OUT | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf, + 12, FPIX_TIMEOUT); } -/* Callback for URBs. */ -static void urb_callback(struct urb *urb) +/* workqueue */ +static void dostream(struct work_struct *work) { - struct gspca_dev *gspca_dev = urb->context; - struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - - PDEBUG(D_PACK, - "enter urb_callback - status=%d, length=%d", - urb->status, urb->actual_length); - - if (dev->state == FPIX_READ_FRAME) - del_timer(&dev->bulk_timer); - - if (urb->status != 0) { - /* We kill a stuck urb every 50 frames on average, so don't - * display a log message for that. */ - if (urb->status != -ECONNRESET) - PDEBUG(D_STREAM, "bad URB status %d", urb->status); - dev_new_state(FPIX_RESET); - schedule_delayed_work(&dev->wqe, 1); - } - - switch (dev->state) { - case FPIX_REQ_FRAME: - dev_new_state(FPIX_READ_FRAME); - read_frame_part(dev); - break; - - case FPIX_READ_FRAME: { - unsigned char *data = urb->transfer_buffer; - struct gspca_frame *frame; - - frame = gspca_get_i_frame(&dev->gspca_dev); - if (frame == NULL) - gspca_dev->last_packet_type = DISCARD_PACKET; - if (urb->actual_length < FPIX_MAX_TRANSFER || - (data[urb->actual_length-2] == 0xff && - data[urb->actual_length-1] == 0xd9)) { - - /* If the result is less than what was asked - * for, then it's the end of the - * frame. Sometime the jpeg is not complete, - * but there's nothing we can do. We also end - * here if the the jpeg ends right at the end - * of the frame. */ - if (frame) - gspca_frame_add(gspca_dev, LAST_PACKET, - frame, - data, urb->actual_length); - dev_new_state(FPIX_REQ_FRAME); - schedule_delayed_work(&dev->wqe, NEXT_FRAME_DELAY); - } else { + struct usb_fpix *dev = container_of(work, struct usb_fpix, work_struct); + struct gspca_dev *gspca_dev = &dev->gspca_dev; + struct urb *urb = gspca_dev->urb[0]; + u8 *data = urb->transfer_buffer; + struct gspca_frame *frame; + int ret = 0; + int len; + + /* synchronize with the main driver */ + mutex_lock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->usb_lock); + PDEBUG(D_STREAM, "dostream started"); + + /* loop reading a frame */ +again: + while (gspca_dev->present && gspca_dev->streaming) { + + /* request a frame */ + mutex_lock(&gspca_dev->usb_lock); + ret = command(gspca_dev, 1); + mutex_unlock(&gspca_dev->usb_lock); + if (ret < 0) + break; + if (!gspca_dev->present || !gspca_dev->streaming) + break; + + /* the frame comes in parts */ + for (;;) { + ret = usb_bulk_msg(gspca_dev->dev, + urb->pipe, + data, + FPIX_MAX_TRANSFER, + &len, FPIX_TIMEOUT); + if (ret < 0) { + /* Most of the time we get a timeout + * error. Just restart. */ + goto again; + } + if (!gspca_dev->present || !gspca_dev->streaming) + goto out; + frame = gspca_get_i_frame(&dev->gspca_dev); + if (frame == NULL) + gspca_dev->last_packet_type = DISCARD_PACKET; + + if (len < FPIX_MAX_TRANSFER || + (data[len - 2] == 0xff && + data[len - 1] == 0xd9)) { + + /* If the result is less than what was asked + * for, then it's the end of the + * frame. Sometimes the jpeg is not complete, + * but there's nothing we can do. We also end + * here if the the jpeg ends right at the end + * of the frame. */ + if (frame) + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + data, len); + break; + } /* got a partial image */ if (frame) gspca_frame_add(gspca_dev, gspca_dev->last_packet_type - == LAST_PACKET + == LAST_PACKET ? FIRST_PACKET : INTER_PACKET, - frame, - data, urb->actual_length); - read_frame_part(dev); + frame, data, len); } - break; - } - - case FPIX_NOP: - case FPIX_RESET: - PDEBUG(D_STREAM, "invalid state %d", dev->state); - break; - } -} -/* Request a new frame */ -static void request_frame(struct usb_fpix *dev) -{ - int ret; - struct gspca_dev *gspca_dev = &dev->gspca_dev; - - /* Setup command packet */ - memset(gspca_dev->usb_buf, 0, 12); - gspca_dev->usb_buf[0] = 0xd3; - gspca_dev->usb_buf[7] = 0x01; - - /* Request a frame */ - dev->ctrlreq.bRequestType = - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - dev->ctrlreq.bRequest = USB_REQ_GET_STATUS; - dev->ctrlreq.wValue = 0; - dev->ctrlreq.wIndex = 0; - dev->ctrlreq.wLength = cpu_to_le16(12); - - usb_fill_control_urb(dev->control_urb, - gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - (unsigned char *) &dev->ctrlreq, - gspca_dev->usb_buf, - 12, urb_callback, gspca_dev); - - ret = usb_submit_urb(dev->control_urb, GFP_ATOMIC); - if (ret) { - dev_new_state(FPIX_RESET); - schedule_delayed_work(&dev->wqe, 1); - PDEBUG(D_STREAM, "usb_submit_urb failed with %d", ret); - } -} - -/*--------------------------------------------------------------------------*/ - -/* State machine. */ -static void fpix_sm(struct work_struct *work) -{ - struct usb_fpix *dev = container_of(work, struct usb_fpix, wqe.work); - - PDEBUG(D_STREAM, "fpix_sm state %d", dev->state); - - /* verify that the device wasn't unplugged */ - if (!dev->gspca_dev.present) { - PDEBUG(D_STREAM, "device is gone"); - dev_new_state(FPIX_NOP); - complete(&dev->can_close); - return; - } - - if (!dev->streaming) { - PDEBUG(D_STREAM, "stopping state machine"); - dev_new_state(FPIX_NOP); - complete(&dev->can_close); - return; + /* We must wait before trying reading the next + * frame. If we don't, or if the delay is too short, + * the camera will disconnect. */ + msleep(NEXT_FRAME_DELAY); } - switch (dev->state) { - case FPIX_RESET: - dev_new_state(FPIX_REQ_FRAME); - schedule_delayed_work(&dev->wqe, HZ / 10); - break; - - case FPIX_REQ_FRAME: - /* get an image */ - request_frame(dev); - break; - - case FPIX_NOP: - case FPIX_READ_FRAME: - PDEBUG(D_STREAM, "invalid state %d", dev->state); - break; - } +out: + PDEBUG(D_STREAM, "dostream stopped"); } /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { + struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; struct cam *cam = &gspca_dev->cam; cam->cam_mode = fpix_mode; cam->nmodes = 1; - cam->epaddr = 0x01; /* todo: correct for all cams? */ cam->bulk_size = FPIX_MAX_TRANSFER; -/* gspca_dev->nbalt = 1; * use bulk transfer */ - return 0; -} - -/* Stop streaming and free the ressources allocated by sd_start. */ -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - - dev->streaming = 0; - - /* Stop the state machine */ - if (dev->state != FPIX_NOP) - wait_for_completion(&dev->can_close); -} - -/* called on streamoff with alt 0 and disconnect */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - - usb_free_urb(dev->control_urb); - dev->control_urb = NULL; -} - -/* Kill an URB that hasn't completed. */ -static void timeout_kill(unsigned long data) -{ - struct urb *urb = (struct urb *) data; + INIT_WORK(&dev->work_struct, dostream); - usb_unlink_urb(urb); + return 0; } /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - - INIT_DELAYED_WORK(&dev->wqe, fpix_sm); - - init_timer(&dev->bulk_timer); - dev->bulk_timer.function = timeout_kill; - return 0; } +/* start the camera */ static int sd_start(struct gspca_dev *gspca_dev) { struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - int ret; - int size_ret; + int ret, len; /* Init the device */ - memset(gspca_dev->usb_buf, 0, 12); - gspca_dev->usb_buf[0] = 0xc6; - gspca_dev->usb_buf[8] = 0x20; - - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - USB_REQ_GET_STATUS, - USB_DIR_OUT | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf, - 12, FPIX_TIMEOUT); - - if (ret != 12) { - PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret); - ret = -EIO; - goto error; + ret = command(gspca_dev, 0); + if (ret < 0) { + PDEBUG(D_STREAM, "init failed %d", ret); + return ret; } /* Read the result of the command. Ignore the result, for it * varies with the device. */ ret = usb_bulk_msg(gspca_dev->dev, - usb_rcvbulkpipe(gspca_dev->dev, - gspca_dev->cam.epaddr), - gspca_dev->usb_buf, FPIX_MAX_TRANSFER, &size_ret, + gspca_dev->urb[0]->pipe, + gspca_dev->urb[0]->transfer_buffer, + FPIX_MAX_TRANSFER, &len, FPIX_TIMEOUT); - if (ret != 0) { - PDEBUG(D_STREAM, "usb_bulk_msg failed (%d)", ret); - ret = -EIO; - goto error; + if (ret < 0) { + PDEBUG(D_STREAM, "usb_bulk_msg failed %d", ret); + return ret; } /* Request a frame, but don't read it */ - memset(gspca_dev->usb_buf, 0, 12); - gspca_dev->usb_buf[0] = 0xd3; - gspca_dev->usb_buf[7] = 0x01; - - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - USB_REQ_GET_STATUS, - USB_DIR_OUT | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf, - 12, FPIX_TIMEOUT); - if (ret != 12) { - PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret); - ret = -EIO; - goto error; + ret = command(gspca_dev, 1); + if (ret < 0) { + PDEBUG(D_STREAM, "frame request failed %d", ret); + return ret; } /* Again, reset bulk in endpoint */ - usb_clear_halt(gspca_dev->dev, gspca_dev->cam.epaddr); - - /* Allocate a control URB */ - dev->control_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->control_urb) { - PDEBUG(D_STREAM, "No free urbs available"); - ret = -EIO; - goto error; - } - - /* Various initializations. */ - init_completion(&dev->can_close); - dev->bulk_timer.data = (unsigned long)dev->gspca_dev.urb[0]; - dev->gspca_dev.urb[0]->complete = urb_callback; - dev->streaming = 1; + usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); - /* Schedule a frame request. */ - dev_new_state(FPIX_REQ_FRAME); - schedule_delayed_work(&dev->wqe, 1); + /* 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; +} + +/* 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 usb_fpix *dev = (struct usb_fpix *) gspca_dev; -error: - /* Free the ressources */ - sd_stopN(gspca_dev); - sd_stop0(gspca_dev); - return ret; + /* wait for the work queue to terminate */ + mutex_unlock(&gspca_dev->usb_lock); + destroy_workqueue(dev->work_thread); + mutex_lock(&gspca_dev->usb_lock); + dev->work_thread = NULL; } /* Table of supported USB devices */ @@ -424,12 +268,11 @@ MODULE_DEVICE_TABLE(usb, device_table); /* sub-driver description */ static const struct sd_desc sd_desc = { - .name = MODULE_NAME, + .name = MODULE_NAME, .config = sd_config, - .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, + .init = sd_init, + .start = sd_start, + .stop0 = sd_stop0, }; /* -- device connect -- */ @@ -443,24 +286,28 @@ static int sd_probe(struct usb_interface *intf, } static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, .disconnect = gspca_disconnect, #ifdef CONFIG_PM .suspend = gspca_suspend, - .resume = gspca_resume, + .resume = gspca_resume, #endif }; /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + 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); diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 65e4901..a75c1ca 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -38,15 +38,16 @@ #include "gspca.h" /* global values */ -#define DEF_NURBS 2 /* default number of URBs */ +#define DEF_NURBS 3 /* default number of URBs */ +#if DEF_NURBS > MAX_NURBS +#error "DEF_NURBS too big" +#endif 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, 4, 0) - -static int video_nr = -1; +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 5, 0) #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; @@ -126,16 +127,18 @@ static void fill_frame(struct gspca_dev *gspca_dev, struct urb *urb) { struct gspca_frame *frame; - __u8 *data; /* address of data in the iso message */ + u8 *data; /* address of data in the iso message */ int i, len, st; cam_pkt_op pkt_scan; if (urb->status != 0) { + if (urb->status == -ESHUTDOWN) + return; /* disconnection */ #ifdef CONFIG_PM if (!gspca_dev->frozen) #endif PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); - return; /* disconnection ? */ + return; } pkt_scan = gspca_dev->sd_desc->pkt_scan; for (i = 0; i < urb->number_of_packets; i++) { @@ -166,7 +169,7 @@ static void fill_frame(struct gspca_dev *gspca_dev, /* let the packet be analyzed by the subdriver */ PDEBUG(D_PACK, "packet [%d] o:%d l:%d", i, urb->iso_frame_desc[i].offset, len); - data = (__u8 *) urb->transfer_buffer + data = (u8 *) urb->transfer_buffer + urb->iso_frame_desc[i].offset; pkt_scan(gspca_dev, frame, data, len); } @@ -182,8 +185,7 @@ static void fill_frame(struct gspca_dev *gspca_dev, * * Analyse each packet and call the subdriver for copy to the frame buffer. */ -static void isoc_irq(struct urb *urb -) +static void isoc_irq(struct urb *urb) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; @@ -196,8 +198,7 @@ static void isoc_irq(struct urb *urb /* * bulk message interrupt from the USB device */ -static void bulk_irq(struct urb *urb -) +static void bulk_irq(struct urb *urb) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; struct gspca_frame *frame; @@ -209,6 +210,8 @@ static void bulk_irq(struct urb *urb switch (urb->status) { case 0: break; + case -ESHUTDOWN: + return; /* disconnection */ case -ECONNRESET: urb->status = 0; break; @@ -217,7 +220,7 @@ static void bulk_irq(struct urb *urb if (!gspca_dev->frozen) #endif PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); - return; /* disconnection ? */ + return; } /* check the availability of the frame buffer */ @@ -322,6 +325,7 @@ static int gspca_is_compressed(__u32 format) case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_SPCA561: case V4L2_PIX_FMT_PAC207: + case V4L2_PIX_FMT_MR97310A: return 1; } return 0; @@ -422,10 +426,8 @@ static void destroy_urbs(struct gspca_dev *gspca_dev) if (urb == NULL) break; - BUG_ON(!gspca_dev->dev); gspca_dev->urb[i] = NULL; - if (!gspca_dev->present) - usb_kill_urb(urb); + usb_kill_urb(urb); if (urb->transfer_buffer != NULL) usb_buffer_free(gspca_dev->dev, urb->transfer_buffer_length, @@ -439,22 +441,16 @@ static void destroy_urbs(struct gspca_dev *gspca_dev) * look for an input transfer endpoint in an alternate setting */ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, - __u8 epaddr, __u8 xfer) { struct usb_host_endpoint *ep; int i, attr; - epaddr |= USB_DIR_IN; for (i = 0; i < alt->desc.bNumEndpoints; i++) { ep = &alt->endpoint[i]; - if (ep->desc.bEndpointAddress == epaddr) { - attr = ep->desc.bmAttributes - & USB_ENDPOINT_XFERTYPE_MASK; - if (attr == xfer) - return ep; - break; - } + attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (attr == xfer) + return ep; } return NULL; } @@ -478,23 +474,23 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) i = gspca_dev->alt; /* previous alt setting */ /* try isoc */ - while (--i > 0) { /* alt 0 is unusable */ + while (--i >= 0) { ep = alt_xfer(&intf->altsetting[i], - gspca_dev->cam.epaddr, USB_ENDPOINT_XFER_ISOC); if (ep) break; } - /* if no isoc, try bulk */ + /* if no isoc, try bulk (alt 0 only) */ if (ep == NULL) { ep = alt_xfer(&intf->altsetting[0], - gspca_dev->cam.epaddr, USB_ENDPOINT_XFER_BULK); if (ep == NULL) { err("no transfer endpoint found"); return NULL; } + i = 0; + gspca_dev->bulk = 1; } PDEBUG(D_STREAM, "use alt %d ep 0x%02x", i, ep->desc.bEndpointAddress); @@ -521,7 +517,7 @@ static int create_urbs(struct gspca_dev *gspca_dev, /* calculate the packet size and the number of packets */ psize = le16_to_cpu(ep->desc.wMaxPacketSize); - if (gspca_dev->alt != 0) { /* isoc */ + if (!gspca_dev->bulk) { /* isoc */ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); @@ -601,6 +597,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; + if (!gspca_dev->present) { + ret = -ENODEV; + goto out; + } + /* set the higher alternate setting and * loop until urb submit succeeds */ gspca_dev->alt = gspca_dev->nbalt; @@ -616,10 +617,9 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) goto out; /* clear the bulk endpoint */ - if (gspca_dev->alt == 0) /* if bulk transfer */ + if (gspca_dev->bulk) usb_clear_halt(gspca_dev->dev, - usb_rcvintpipe(gspca_dev->dev, - gspca_dev->cam.epaddr)); + gspca_dev->urb[0]->pipe); /* start the cam */ ret = gspca_dev->sd_desc->start(gspca_dev); @@ -630,7 +630,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->streaming = 1; /* some bulk transfers are started by the subdriver */ - if (gspca_dev->alt == 0 && gspca_dev->cam.bulk_nurbs == 0) + if (gspca_dev->bulk && gspca_dev->cam.bulk_nurbs == 0) break; /* submit the URBs */ @@ -671,11 +671,14 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) static void gspca_stream_off(struct gspca_dev *gspca_dev) { gspca_dev->streaming = 0; - if (gspca_dev->present - && gspca_dev->sd_desc->stopN) - gspca_dev->sd_desc->stopN(gspca_dev); - destroy_urbs(gspca_dev); - gspca_set_alt0(gspca_dev); + if (gspca_dev->present) { + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_set_alt0(gspca_dev); + } + + /* always call stop0 to free the subdriver's resources */ if (gspca_dev->sd_desc->stop0) gspca_dev->sd_desc->stop0(gspca_dev); PDEBUG(D_STREAM, "stream off OK"); @@ -762,7 +765,6 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, fmtdesc->pixelformat = fmt_tb[index]; if (gspca_is_compressed(fmt_tb[index])) fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; - fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmtdesc->description[0] = fmtdesc->pixelformat & 0xff; fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff; fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff; @@ -957,8 +959,15 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct gspca_dev *gspca_dev = priv; + int ret; - memset(cap, 0, sizeof *cap); + /* protect the access to the usb device */ + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (!gspca_dev->present) { + ret = -ENODEV; + goto out; + } strncpy(cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver); if (gspca_dev->dev->product != NULL) { strncpy(cap->card, gspca_dev->dev->product, @@ -969,13 +978,15 @@ static int vidioc_querycap(struct file *file, void *priv, le16_to_cpu(gspca_dev->dev->descriptor.idVendor), le16_to_cpu(gspca_dev->dev->descriptor.idProduct)); } - strncpy(cap->bus_info, gspca_dev->dev->bus->bus_name, - sizeof cap->bus_info); + usb_make_path(gspca_dev->dev, cap->bus_info, sizeof(cap->bus_info)); cap->version = DRIVER_VERSION_NUMBER; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - return 0; + ret = 0; +out: + mutex_unlock(&gspca_dev->usb_lock); + return ret; } static int vidioc_queryctrl(struct file *file, void *priv, @@ -1038,7 +1049,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv, PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; - ret = ctrls->set(gspca_dev, ctrl->value); + if (gspca_dev->present) + ret = ctrls->set(gspca_dev, ctrl->value); + else + ret = -ENODEV; mutex_unlock(&gspca_dev->usb_lock); return ret; } @@ -1062,7 +1076,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv, return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; - ret = ctrls->get(gspca_dev, &ctrl->value); + if (gspca_dev->present) + ret = ctrls->get(gspca_dev, &ctrl->value); + else + ret = -ENODEV; mutex_unlock(&gspca_dev->usb_lock); return ret; } @@ -1081,7 +1098,6 @@ static int vidioc_s_audio(struct file *file, void *priv, static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *audio) { - memset(audio, 0, sizeof *audio); strcpy(audio->name, "Microphone"); return 0; } @@ -1115,7 +1131,6 @@ static int vidioc_enum_input(struct file *file, void *priv, if (input->index != 0) return -EINVAL; - memset(input, 0, sizeof *input); input->type = V4L2_INPUT_TYPE_CAMERA; strncpy(input->name, gspca_dev->sd_desc->name, sizeof input->name); @@ -1224,10 +1239,7 @@ static int vidioc_streamon(struct file *file, void *priv, return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } + if (gspca_dev->nframes == 0 || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) { ret = -EINVAL; @@ -1295,7 +1307,10 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; - ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); + if (gspca_dev->present) + ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); + else + ret = -ENODEV; mutex_unlock(&gspca_dev->usb_lock); return ret; } @@ -1310,7 +1325,10 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; - ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); + if (gspca_dev->present) + ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); + else + ret = -ENODEV; mutex_unlock(&gspca_dev->usb_lock); return ret; } @@ -1320,8 +1338,6 @@ static int vidioc_g_parm(struct file *filp, void *priv, { struct gspca_dev *gspca_dev = priv; - memset(parm, 0, sizeof *parm); - parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm->parm.capture.readbuffers = gspca_dev->nbufread; if (gspca_dev->sd_desc->get_streamparm) { @@ -1329,7 +1345,11 @@ static int vidioc_g_parm(struct file *filp, void *priv, if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; - ret = gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); + if (gspca_dev->present) + ret = gspca_dev->sd_desc->get_streamparm(gspca_dev, + parm); + else + ret = -ENODEV; mutex_unlock(&gspca_dev->usb_lock); return ret; } @@ -1354,7 +1374,11 @@ static int vidioc_s_parm(struct file *filp, void *priv, if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; - ret = gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); + if (gspca_dev->present) + ret = gspca_dev->sd_desc->set_streamparm(gspca_dev, + parm); + else + ret = -ENODEV; mutex_unlock(&gspca_dev->usb_lock); return ret; } @@ -1382,7 +1406,6 @@ static int vidiocgmbuf(struct file *file, void *priv, { struct v4l2_format fmt; - memset(&fmt, 0, sizeof fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; i = gspca_dev->cam.nmodes - 1; /* highest mode */ fmt.fmt.pix.width = gspca_dev->cam.cam_mode[i].width; @@ -1528,7 +1551,8 @@ static int frame_wait(struct gspca_dev *gspca_dev, if (gspca_dev->sd_desc->dq_callback) { mutex_lock(&gspca_dev->usb_lock); - gspca_dev->sd_desc->dq_callback(gspca_dev); + if (gspca_dev->present) + gspca_dev->sd_desc->dq_callback(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); } return j; @@ -1550,6 +1574,9 @@ static int vidioc_dqbuf(struct file *file, void *priv, if (v4l2_buf->memory != gspca_dev->memory) return -EINVAL; + if (!gspca_dev->present) + return -ENODEV; + /* if not streaming, be sure the application will not loop forever */ if (!(file->f_flags & O_NONBLOCK) && !gspca_dev->streaming && gspca_dev->users == 1) @@ -1700,8 +1727,6 @@ static unsigned int dev_poll(struct file *file, poll_table *wait) PDEBUG(D_FRAM, "poll"); poll_wait(file, &gspca_dev->wq, wait); - if (!gspca_dev->present) - return POLLERR; /* if reqbufs is not done, the user would use read() */ if (gspca_dev->nframes == 0) { @@ -1714,10 +1739,6 @@ static unsigned int dev_poll(struct file *file, poll_table *wait) if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) return POLLERR; - if (!gspca_dev->present) { - ret = POLLERR; - goto out; - } /* check the next incoming buffer */ i = gspca_dev->fr_o; @@ -1726,8 +1747,9 @@ static unsigned int dev_poll(struct file *file, poll_table *wait) ret = POLLIN | POLLRDNORM; /* something to read */ else ret = 0; -out: mutex_unlock(&gspca_dev->queue_lock); + if (!gspca_dev->present) + return POLLHUP; return ret; } @@ -1925,7 +1947,7 @@ int gspca_dev_probe(struct usb_interface *intf, gspca_dev->present = 1; ret = video_register_device(&gspca_dev->vdev, VFL_TYPE_GRABBER, - video_nr); + -1); if (ret < 0) { err("video_register_device err %d", ret); goto out; @@ -1953,10 +1975,16 @@ void gspca_disconnect(struct usb_interface *intf) mutex_lock(&gspca_dev->usb_lock); gspca_dev->present = 0; - mutex_unlock(&gspca_dev->usb_lock); - destroy_urbs(gspca_dev); + if (gspca_dev->streaming) { + destroy_urbs(gspca_dev); + wake_up_interruptible(&gspca_dev->wq); + } + + /* the device is freed at exit of this function */ gspca_dev->dev = NULL; + mutex_unlock(&gspca_dev->usb_lock); + usb_set_intfdata(intf, NULL); /* release the device */ diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index c90af9c..e4d4cf6 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -33,19 +33,13 @@ extern int gspca_debug; #endif #undef err #define err(fmt, args...) \ - do {\ - printk(KERN_ERR MODULE_NAME ": " fmt "\n", ## args); \ - } while (0) + printk(KERN_ERR MODULE_NAME ": " fmt "\n", ## args) #undef info #define info(fmt, args...) \ - do {\ - printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args); \ - } while (0) + printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args) #undef warn #define warn(fmt, args...) \ - do {\ - printk(KERN_WARNING MODULE_NAME ": " fmt "\n", ## args); \ - } while (0) + printk(KERN_WARNING MODULE_NAME ": " fmt "\n", ## args) #define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */ /* image transfers */ @@ -62,7 +56,6 @@ struct cam { * - cannot be > MAX_NURBS * - when 0 and bulk_size != 0 means * 1 URB and submit done by subdriver */ - __u8 epaddr; }; struct gspca_dev; @@ -174,6 +167,7 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ __u8 nbalt; /* number of USB alternate settings */ + u8 bulk; /* image transfer by 0:isoc / 1:bulk */ }; int gspca_dev_probe(struct usb_interface *intf, diff --git a/drivers/media/video/gspca/jpeg.h b/drivers/media/video/gspca/jpeg.h index d823b47..de63c36 100644 --- a/drivers/media/video/gspca/jpeg.h +++ b/drivers/media/video/gspca/jpeg.h @@ -24,171 +24,39 @@ * */ -/* start of jpeg frame + quantization table */ -static const unsigned char quant[][0x88] = { -/* index 0 - Q40*/ - { +/* + * generation options + * CONEX_CAM Conexant if present + */ + +/* JPEG header */ +static const u8 jpeg_head[] = { 0xff, 0xd8, /* jpeg */ - 0xff, 0xdb, 0x00, 0x84, /* DQT */ -0, /* quantization table part 1 */ - 20, 14, 15, 18, 15, 13, 20, 18, 16, 18, 23, 21, 20, 24, 30, 50, - 33, 30, 28, 28, 30, 61, 44, 46, 36, 50, 73, 64, 76, 75, 71, 64, - 70, 69, 80, 90, 115, 98, 80, 85, 109, 86, 69, 70, 100, 136, 101, - 109, - 119, 123, 129, 130, 129, 78, 96, 141, 151, 140, 125, 150, 115, - 126, 129, 124, -1, /* quantization table part 2 */ - 21, 23, 23, 30, 26, 30, 59, 33, 33, 59, 124, 83, 70, 83, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124}, -/* index 1 - Q50 */ - { - 0xff, 0xd8, - 0xff, 0xdb, 0x00, 0x84, /* DQT */ -0, - 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, 40, - 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, 57, 51, - 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, 109, 81, 87, - 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, 100, 120, 92, 101, - 103, 99, -1, - 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}, -/* index 2 Q60 */ - { - 0xff, 0xd8, - 0xff, 0xdb, 0x00, 0x84, /* DQT */ -0, - 13, 9, 10, 11, 10, 8, 13, 11, 10, 11, 14, 14, 13, 15, 19, 32, - 21, 19, 18, 18, 19, 39, 28, 30, 23, 32, 46, 41, 49, 48, 46, 41, - 45, 44, 51, 58, 74, 62, 51, 54, 70, 55, 44, 45, 64, 87, 65, 70, - 76, 78, 82, 83, 82, 50, 62, 90, 97, 90, 80, 96, 74, 81, 82, 79, -1, - 14, 14, 14, 19, 17, 19, 38, 21, 21, 38, 79, 53, 45, 53, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, - 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79}, -/* index 3 - Q70 */ - { - 0xff, 0xd8, - 0xff, 0xdb, 0x00, 0x84, /* DQT */ -0, - 10, 7, 7, 8, 7, 6, 10, 8, 8, 8, 11, 10, 10, 11, 14, 24, - 16, 14, 13, 13, 14, 29, 21, 22, 17, 24, 35, 31, 37, 36, 34, 31, - 34, 33, 38, 43, 55, 47, 38, 41, 52, 41, 33, 34, 48, 65, 49, 52, - 57, 59, 62, 62, 62, 37, 46, 68, 73, 67, 60, 72, 55, 61, 62, 59, -1, - 10, 11, 11, 14, 13, 14, 28, 16, 16, 28, 59, 40, 34, 40, 59, 59, - 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, - 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, - 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59}, -/* index 4 - Q80 */ - { - 0xff, 0xd8, - 0xff, 0xdb, 0x00, 0x84, /* DQT */ -0, - 6, 4, 5, 6, 5, 4, 6, 6, 5, 6, 7, 7, 6, 8, 10, 16, - 10, 10, 9, 9, 10, 20, 14, 15, 12, 16, 23, 20, 24, 24, 23, 20, - 22, 22, 26, 29, 37, 31, 26, 27, 35, 28, 22, 22, 32, 44, 32, 35, - 38, 39, 41, 42, 41, 25, 31, 45, 48, 45, 40, 48, 37, 40, 41, 40, -1, - 7, 7, 7, 10, 8, 10, 19, 10, 10, 19, 40, 26, 22, 26, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}, -/* index 5 - Q85 */ - { - 0xff, 0xd8, - 0xff, 0xdb, 0x00, 0x84, /* DQT */ -0, - 5, 3, 4, 4, 4, 3, 5, 4, 4, 4, 5, 5, 5, 6, 7, 12, - 8, 7, 7, 7, 7, 15, 11, 11, 9, 12, 17, 15, 18, 18, 17, 15, - 17, 17, 19, 22, 28, 23, 19, 20, 26, 21, 17, 17, 24, 33, 24, 26, - 29, 29, 31, 31, 31, 19, 23, 34, 36, 34, 30, 36, 28, 30, 31, 30, -1, - 5, 5, 5, 7, 6, 7, 14, 8, 8, 14, 30, 20, 17, 20, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, - 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, -/* index 6 - 86 */ -{ - 0xff, 0xd8, - 0xff, 0xdb, 0x00, 0x84, /* DQT */ -0, - 0x04, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x04, - 0x04, 0x04, 0x05, 0x05, 0x04, 0x05, 0x07, 0x0B, - 0x07, 0x07, 0x06, 0x06, 0x07, 0x0E, 0x0A, 0x0A, - 0x08, 0x0B, 0x10, 0x0E, 0x11, 0x11, 0x10, 0x0E, - 0x10, 0x0F, 0x12, 0x14, 0x1A, 0x16, 0x12, 0x13, - 0x18, 0x13, 0x0F, 0x10, 0x16, 0x1F, 0x17, 0x18, - 0x1B, 0x1B, 0x1D, 0x1D, 0x1D, 0x11, 0x16, 0x20, - 0x22, 0x1F, 0x1C, 0x22, 0x1A, 0x1C, 0x1D, 0x1C, -1, - 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0D, 0x07, - 0x07, 0x0D, 0x1C, 0x12, 0x10, 0x12, 0x1C, 0x1C, - 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, - 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, - 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, - 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, - 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, - 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, - }, -/* index 7 - 88 */ -{ - 0xff, 0xd8, - 0xff, 0xdb, 0x00, 0x84, /* DQT */ -0, - 0x04, 0x03, 0x03, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x06, 0x0A, - 0x06, 0x06, 0x05, 0x05, 0x06, 0x0C, 0x08, 0x09, - 0x07, 0x0A, 0x0E, 0x0C, 0x0F, 0x0E, 0x0E, 0x0C, - 0x0D, 0x0D, 0x0F, 0x11, 0x16, 0x13, 0x0F, 0x10, - 0x15, 0x11, 0x0D, 0x0D, 0x13, 0x1A, 0x13, 0x15, - 0x17, 0x18, 0x19, 0x19, 0x19, 0x0F, 0x12, 0x1B, - 0x1D, 0x1B, 0x18, 0x1D, 0x16, 0x18, 0x19, 0x18, -1, - 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0B, 0x06, - 0x06, 0x0B, 0x18, 0x10, 0x0D, 0x10, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -}, -/* index 8 - ?? */ -{ - 0xff, 0xd8, + +/* quantization table quality 50% */ 0xff, 0xdb, 0x00, 0x84, /* DQT */ 0, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x04, 0x05, - 0x04, 0x05, 0x07, 0x06, 0x08, 0x08, 0x07, 0x06, - 0x07, 0x07, 0x08, 0x09, 0x0C, 0x0A, 0x08, 0x09, - 0x0B, 0x09, 0x07, 0x07, 0x0A, 0x0E, 0x0A, 0x0B, - 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x08, 0x0A, 0x0E, - 0x0F, 0x0E, 0x0D, 0x0F, 0x0C, 0x0D, 0x0D, 0x0C, +#define JPEG_QT0_OFFSET 7 + 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, + 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, + 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, + 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33, + 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44, + 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57, + 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, + 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, 1, - 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x06, 0x03, - 0x03, 0x06, 0x0C, 0x08, 0x07, 0x08, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C -} -}; +#define JPEG_QT1_OFFSET 72 + 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, + 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, -/* huffman table + start of SOF0 */ -static unsigned char huffman[] = { +/* huffman table */ 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -244,58 +112,57 @@ static unsigned char huffman[] = { 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, #ifdef CONEX_CAM /* the Conexant frames start with SOF0 */ +#define JPEG_HDR_SZ 556 #else 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */ 0x08, /* data precision */ -#endif -}; - -#ifndef CONEX_CAM -/* variable part: - * 0x01, 0xe0, height - * 0x02, 0x80, width - * 0x03, component number - * 0x01, - * 0x21, samples Y - */ - -/* end of header */ -static unsigned char eoh[] = { +#define JPEG_HEIGHT_OFFSET 561 + 0x01, 0xe0, /* height */ + 0x02, 0x80, /* width */ + 0x03, /* component number */ + 0x01, + 0x21, /* samples Y */ 0x00, /* quant Y */ 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */ 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */ 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 -}; +#define JPEG_HDR_SZ 589 #endif +}; -/* -- output the JPEG header -- */ -static void jpeg_put_header(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - int qindex, - int samplesY) +/* define the JPEG header */ +static void jpeg_define(u8 *jpeg_hdr, + int height, + int width, + int samplesY) { + memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head); #ifndef CONEX_CAM - unsigned char tmpbuf[8]; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height & 0xff; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width & 0xff; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 6] = samplesY; #endif +} - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - (unsigned char *) quant[qindex], sizeof quant[0]); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - (unsigned char *) huffman, sizeof huffman); -#ifndef CONEX_CAM - tmpbuf[0] = gspca_dev->height >> 8; - tmpbuf[1] = gspca_dev->height & 0xff; - tmpbuf[2] = gspca_dev->width >> 8; - tmpbuf[3] = gspca_dev->width & 0xff; - tmpbuf[4] = 0x03; /* component number */ - tmpbuf[5] = 0x01; /* first component */ - tmpbuf[6] = samplesY; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - tmpbuf, 7); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - eoh, sizeof eoh); -#endif +/* set the JPEG quality */ +static void jpeg_set_qual(u8 *jpeg_hdr, + int quality) +{ + int i, sc; + + if (quality < 50) + sc = 5000 / quality; + else + sc = 200 - quality * 2; + for (i = 0; i < 64; i++) { + jpeg_hdr[JPEG_QT0_OFFSET + i] = + (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; + jpeg_hdr[JPEG_QT1_OFFSET + i] = + (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; + } } #endif diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index ed906fe..b35e483 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/drivers/media/video/gspca/m5602/m5602_core.c @@ -332,7 +332,6 @@ static int m5602_configure(struct gspca_dev *gspca_dev, int err; cam = &gspca_dev->cam; - cam->epaddr = M5602_ISOC_ENDPOINT_ADDR; sd->desc = &sd_desc; if (dump_bridge) @@ -374,8 +373,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init mod_m5602_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index e564a61..48892b5 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -102,7 +102,11 @@ int s5k4aa_probe(struct sd *sd) } /* Test some registers, but we don't know their exact meaning yet */ - if (m5602_read_sensor(sd, 0x00, prod_id, sizeof(prod_id))) + if (m5602_read_sensor(sd, 0x00, prod_id, 2)) + return -ENODEV; + if (m5602_read_sensor(sd, 0x02, prod_id+2, 2)) + return -ENODEV; + if (m5602_read_sensor(sd, 0x04, prod_id+4, 2)) return -ENODEV; if (memcmp(prod_id, expected_prod_id, sizeof(prod_id))) diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index 3d2090e..75e8d14 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -32,17 +32,91 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - char qindex; + u8 brightness; + u8 colors; + u8 gamma; + u8 sharpness; + u8 quality; +#define QUALITY_MIN 40 +#define QUALITY_MAX 70 +#define QUALITY_DEF 50 + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val); +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[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 30, + .step = 1, +#define BRIGHTNESS_DEF 15 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Color", + .minimum = 1, + .maximum = 255, + .step = 1, +#define COLOR_DEF 200 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, + { + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 0, + .maximum = 3, + .step = 1, +#define GAMMA_DEF 1 + .default_value = GAMMA_DEF, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 2, + .step = 1, +#define SHARPNESS_DEF 1 + .default_value = SHARPNESS_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, }; static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 589, + .sizeimage = 320 * 240 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 2}, {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, @@ -52,65 +126,45 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 1}, }; -/* MI Register table //elvis */ -enum { - REG_HW_MI_0, - REG_HW_MI_1, - REG_HW_MI_2, - REG_HW_MI_3, - REG_HW_MI_4, - REG_HW_MI_5, - REG_HW_MI_6, - REG_HW_MI_7, - REG_HW_MI_9 = 0x09, - REG_HW_MI_B = 0x0B, - REG_HW_MI_C, - REG_HW_MI_D, - REG_HW_MI_1E = 0x1E, - REG_HW_MI_20 = 0x20, - REG_HW_MI_2B = 0x2B, - REG_HW_MI_2C, - REG_HW_MI_2D, - REG_HW_MI_2E, - REG_HW_MI_35 = 0x35, - REG_HW_MI_5F = 0x5f, - REG_HW_MI_60, - REG_HW_MI_61, - REG_HW_MI_62, - REG_HW_MI_63, - REG_HW_MI_64, - REG_HW_MI_F1 = 0xf1, - ATTR_TOTAL_MI_REG = 0xf2 +static const __u8 mi_data[0x20] = { +/* 01 02 03 04 05 06 07 08 */ + 0x48, 0x22, 0x01, 0x47, 0x10, 0x00, 0x00, 0x00, +/* 09 0a 0b 0c 0d 0e 0f 10 */ + 0x00, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01, +/* 11 12 13 14 15 16 17 18 */ + 0x30, 0x00, 0x04, 0x00, 0x06, 0x01, 0xe2, 0x02, +/* 19 1a 1b 1c 1d 1e 1f 20 */ + 0x82, 0x00, 0x20, 0x17, 0x80, 0x08, 0x0c, 0x00 }; -/* the bytes to write are in gspca_dev->usb_buf */ +/* write <len> bytes from gspca_dev->usb_buf */ static int reg_w(struct gspca_dev *gspca_dev, - __u16 index, int len) + int len) { - int rc; - - rc = usb_control_msg(gspca_dev->dev, - usb_sndbulkpipe(gspca_dev->dev, 4), - 0x12, - 0xc8, /* ?? */ - 0, /* value */ - index, gspca_dev->usb_buf, len, 500); - if (rc < 0) - PDEBUG(D_ERR, "reg write [%02x] error %d", index, rc); - return rc; + int alen, ret; + + ret = usb_bulk_msg(gspca_dev->dev, + usb_sndbulkpipe(gspca_dev->dev, 4), + gspca_dev->usb_buf, + len, + &alen, + 500); /* timeout in milliseconds */ + if (ret < 0) + PDEBUG(D_ERR, "reg write [%02x] error %d", + gspca_dev->usb_buf[0], ret); + return ret; } -static void bulk_w(struct gspca_dev *gspca_dev, - __u16 *pch, - __u16 Address) +static void mi_w(struct gspca_dev *gspca_dev, + u8 addr, + u8 value) { gspca_dev->usb_buf[0] = 0x1f; gspca_dev->usb_buf[1] = 0; /* control byte */ - gspca_dev->usb_buf[2] = Address; - gspca_dev->usb_buf[3] = *pch >> 8; /* high byte */ - gspca_dev->usb_buf[4] = *pch; /* low byte */ + gspca_dev->usb_buf[2] = addr; + gspca_dev->usb_buf[3] = value; - reg_w(gspca_dev, Address, 5); + reg_w(gspca_dev, 4); } /* this function is called at probe time */ @@ -121,10 +175,14 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->epaddr = 0x01; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); - sd->qindex = 1; /* set the quantization table */ + sd->brightness = BRIGHTNESS_DEF; + sd->colors = COLOR_DEF; + sd->gamma = GAMMA_DEF; + sd->sharpness = SHARPNESS_DEF; + sd->quality = QUALITY_DEF; + gspca_dev->nbalt = 9; /* use the altsetting 08 */ return 0; } @@ -136,24 +194,22 @@ static int sd_init(struct gspca_dev *gspca_dev) static int sd_start(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int err_code; - __u8 *data; - __u16 *MI_buf; - int h_size, v_size; - int intpipe; - - PDEBUG(D_STREAM, "camera start, iface %d, alt 8", gspca_dev->iface); - err_code = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 8); - if (err_code < 0) { - PDEBUG(D_ERR|D_STREAM, "Set packet size: set interface error"); - return err_code; - } + u8 *data; + int i; + + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); data = gspca_dev->usb_buf; + data[0] = 0x01; /* address */ data[1] = 0x01; - - err_code = reg_w(gspca_dev, data[0], 2); + err_code = reg_w(gspca_dev, 2); if (err_code < 0) return err_code; @@ -163,30 +219,28 @@ static int sd_start(struct gspca_dev *gspca_dev) data[0] = 0x00; /* address */ data[1] = 0x0c | 0x01; /* reg 0 */ data[2] = 0x01; /* reg 1 */ - h_size = gspca_dev->width; - v_size = gspca_dev->height; - data[3] = h_size / 8; /* h_size , reg 2 */ - data[4] = v_size / 8; /* v_size , reg 3 */ + data[3] = gspca_dev->width / 8; /* h_size , reg 2 */ + data[4] = gspca_dev->height / 8; /* v_size , reg 3 */ data[5] = 0x30; /* reg 4, MI, PAS5101 : * 0x30 for 24mhz , 0x28 for 12mhz */ - data[6] = 4; /* reg 5, H start */ - data[7] = 0xc0; /* reg 6, gamma 1.5 */ - data[8] = 3; /* reg 7, V start */ + data[6] = 0x02; /* reg 5, H start - was 0x04 */ + data[7] = sd->gamma * 0x40; /* reg 0x06: gamma */ + data[8] = 0x01; /* reg 7, V start - was 0x03 */ /* if (h_size == 320 ) */ /* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */ /* else */ data[9] = 0x52; /* reg 8, 24MHz, no scale down */ - data[10] = 0x5d; /* reg 9, I2C device address - * [for PAS5101 (0x40)] [for MI (0x5d)] */ +/*jfm: from win trace*/ + data[10] = 0x18; - err_code = reg_w(gspca_dev, data[0], 11); + err_code = reg_w(gspca_dev, 11); if (err_code < 0) return err_code; data[0] = 0x23; /* address */ data[1] = 0x09; /* reg 35, append frame header */ - err_code = reg_w(gspca_dev, data[0], 2); + err_code = reg_w(gspca_dev, 2); if (err_code < 0) return err_code; @@ -197,137 +251,57 @@ static int sd_start(struct gspca_dev *gspca_dev) /* else */ data[1] = 50; /* 50 reg 60, pc-cam frame size * (unit: 4KB) 200KB */ - err_code = reg_w(gspca_dev, data[0], 2); + err_code = reg_w(gspca_dev, 2); if (err_code < 0) return err_code; - if (0) { /* fixed dark-gain */ - data[1] = 0; /* reg 94, Y Gain (1.75) */ - data[2] = 0; /* reg 95, UV Gain (1.75) */ - data[3] = 0x3f; /* reg 96, Y Gain/UV Gain/disable - * auto dark-gain */ - data[4] = 0; /* reg 97, set fixed dark level */ - data[5] = 0; /* reg 98, don't care */ - } else { /* auto dark-gain */ - data[1] = 0; /* reg 94, Y Gain (auto) */ - data[2] = 0; /* reg 95, UV Gain (1.75) */ - data[3] = 0x78; /* reg 96, Y Gain/UV Gain/disable - * auto dark-gain */ - switch (gspca_dev->width) { -/* case 1280: */ -/* data[4] = 154; - * reg 97, %3 shadow point (unit: 256 pixel) */ -/* data[5] = 51; - * reg 98, %1 highlight point - * (uint: 256 pixel) */ -/* break; */ - default: -/* case 640: */ - data[4] = 36; /* reg 97, %3 shadow point - * (unit: 256 pixel) */ - data[5] = 12; /* reg 98, %1 highlight point - * (uint: 256 pixel) */ - break; - case 320: - data[4] = 9; /* reg 97, %3 shadow point - * (unit: 256 pixel) */ - data[5] = 3; /* reg 98, %1 highlight point - * (uint: 256 pixel) */ - break; - } - } /* auto dark-gain */ data[0] = 0x5e; /* address */ - - err_code = reg_w(gspca_dev, data[0], 6); + data[1] = 0; /* reg 94, Y Gain (auto) */ +/*jfm: from win trace*/ + /* reg 0x5f/0x60 (LE) = saturation */ + /* h (60): xxxx x100 + * l (5f): xxxx x000 */ + data[2] = sd->colors << 3; + data[3] = ((sd->colors >> 2) & 0xf8) | 0x04; + data[4] = sd->brightness; /* reg 0x61 = brightness */ + data[5] = 0x00; + + err_code = reg_w(gspca_dev, 6); if (err_code < 0) return err_code; data[0] = 0x67; - data[1] = 0x13; /* reg 103, first pixel B, disable sharpness */ - err_code = reg_w(gspca_dev, data[0], 2); +/*jfm: from win trace*/ + data[1] = sd->sharpness * 4 + 3; + data[2] = 0x14; + err_code = reg_w(gspca_dev, 3); if (err_code < 0) return err_code; - /* - * initialize the value of MI sensor... - */ - MI_buf = kzalloc(ATTR_TOTAL_MI_REG * sizeof *MI_buf, GFP_KERNEL); - MI_buf[REG_HW_MI_1] = 0x000a; - MI_buf[REG_HW_MI_2] = 0x000c; - MI_buf[REG_HW_MI_3] = 0x0405; - MI_buf[REG_HW_MI_4] = 0x0507; - /* mi_Attr_Reg_[REG_HW_MI_5] = 0x01ff;//13 */ - MI_buf[REG_HW_MI_5] = 0x0013; /* 13 */ - MI_buf[REG_HW_MI_6] = 0x001f; /* vertical blanking */ - /* mi_Attr_Reg_[REG_HW_MI_6] = 0x0400; // vertical blanking */ - MI_buf[REG_HW_MI_7] = 0x0002; - /* mi_Attr_Reg_[REG_HW_MI_9] = 0x015f; */ - /* mi_Attr_Reg_[REG_HW_MI_9] = 0x030f; */ - MI_buf[REG_HW_MI_9] = 0x0374; - MI_buf[REG_HW_MI_B] = 0x0000; - MI_buf[REG_HW_MI_C] = 0x0000; - MI_buf[REG_HW_MI_D] = 0x0000; - MI_buf[REG_HW_MI_1E] = 0x8000; -/* mi_Attr_Reg_[REG_HW_MI_20] = 0x1104; */ - MI_buf[REG_HW_MI_20] = 0x1104; /* 0x111c; */ - MI_buf[REG_HW_MI_2B] = 0x0008; -/* mi_Attr_Reg_[REG_HW_MI_2C] = 0x000f; */ - MI_buf[REG_HW_MI_2C] = 0x001f; /* lita suggest */ - MI_buf[REG_HW_MI_2D] = 0x0008; - MI_buf[REG_HW_MI_2E] = 0x0008; - MI_buf[REG_HW_MI_35] = 0x0051; - MI_buf[REG_HW_MI_5F] = 0x0904; /* fail to write */ - MI_buf[REG_HW_MI_60] = 0x0000; - MI_buf[REG_HW_MI_61] = 0x0000; - MI_buf[REG_HW_MI_62] = 0x0498; - MI_buf[REG_HW_MI_63] = 0x0000; - MI_buf[REG_HW_MI_64] = 0x0000; - MI_buf[REG_HW_MI_F1] = 0x0001; - /* changing while setting up the different value of dx/dy */ - - if (gspca_dev->width != 1280) { - MI_buf[0x01] = 0x010a; - MI_buf[0x02] = 0x014c; - MI_buf[0x03] = 0x01e5; - MI_buf[0x04] = 0x0287; - } - MI_buf[0x20] = 0x1104; - - bulk_w(gspca_dev, MI_buf + 1, 1); - bulk_w(gspca_dev, MI_buf + 2, 2); - bulk_w(gspca_dev, MI_buf + 3, 3); - bulk_w(gspca_dev, MI_buf + 4, 4); - bulk_w(gspca_dev, MI_buf + 5, 5); - bulk_w(gspca_dev, MI_buf + 6, 6); - bulk_w(gspca_dev, MI_buf + 7, 7); - bulk_w(gspca_dev, MI_buf + 9, 9); - bulk_w(gspca_dev, MI_buf + 0x0b, 0x0b); - bulk_w(gspca_dev, MI_buf + 0x0c, 0x0c); - bulk_w(gspca_dev, MI_buf + 0x0d, 0x0d); - bulk_w(gspca_dev, MI_buf + 0x1e, 0x1e); - bulk_w(gspca_dev, MI_buf + 0x20, 0x20); - bulk_w(gspca_dev, MI_buf + 0x2b, 0x2b); - bulk_w(gspca_dev, MI_buf + 0x2c, 0x2c); - bulk_w(gspca_dev, MI_buf + 0x2d, 0x2d); - bulk_w(gspca_dev, MI_buf + 0x2e, 0x2e); - bulk_w(gspca_dev, MI_buf + 0x35, 0x35); - bulk_w(gspca_dev, MI_buf + 0x5f, 0x5f); - bulk_w(gspca_dev, MI_buf + 0x60, 0x60); - bulk_w(gspca_dev, MI_buf + 0x61, 0x61); - bulk_w(gspca_dev, MI_buf + 0x62, 0x62); - bulk_w(gspca_dev, MI_buf + 0x63, 0x63); - bulk_w(gspca_dev, MI_buf + 0x64, 0x64); - bulk_w(gspca_dev, MI_buf + 0xf1, 0xf1); - kfree(MI_buf); - - intpipe = usb_sndintpipe(gspca_dev->dev, 0); - err_code = usb_clear_halt(gspca_dev->dev, intpipe); + data[0] = 0x69; + data[1] = 0x2f; + data[2] = 0x28; + data[3] = 0x42; + err_code = reg_w(gspca_dev, 4); + if (err_code < 0) + return err_code; + + data[0] = 0x63; + data[1] = 0x07; + err_code = reg_w(gspca_dev, 2); +/*jfm: win trace - many writes here to reg 0x64*/ + if (err_code < 0) + return err_code; + + /* initialize the MI sensor */ + for (i = 0; i < sizeof mi_data; i++) + mi_w(gspca_dev, i + 1, mi_data[i]); data[0] = 0x00; data[1] = 0x4d; /* ISOC transfering enable... */ - reg_w(gspca_dev, data[0], 2); - return err_code; + reg_w(gspca_dev, 2); + return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) @@ -336,11 +310,18 @@ static void sd_stopN(struct gspca_dev *gspca_dev) gspca_dev->usb_buf[0] = 1; gspca_dev->usb_buf[1] = 0; - result = reg_w(gspca_dev, gspca_dev->usb_buf[0], 2); + result = reg_w(gspca_dev, 2); if (result < 0) PDEBUG(D_ERR, "Camera Stop failed"); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -363,16 +344,16 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, || data[5 + p] == 0x65 || data[5 + p] == 0x66 || data[5 + p] == 0x67) { - PDEBUG(D_PACK, "sof offset: %d leng: %d", + PDEBUG(D_PACK, "sof offset: %d len: %d", p, len); frame = gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, 0); + frame, data, p); /* put the JPEG header */ - jpeg_put_header(gspca_dev, frame, - sd->qindex, 0x21); - data += 16; - len -= 16; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); + data += p + 16; + len -= p + 16; break; } } @@ -380,6 +361,121 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) { + gspca_dev->usb_buf[0] = 0x61; + gspca_dev->usb_buf[1] = val; + reg_w(gspca_dev, 2); + } + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) { + + /* see sd_start */ + gspca_dev->usb_buf[0] = 0x5f; + gspca_dev->usb_buf[1] = sd->colors << 3; + gspca_dev->usb_buf[2] = ((sd->colors >> 2) & 0xf8) | 0x04; + reg_w(gspca_dev, 3); + } + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) { + gspca_dev->usb_buf[0] = 0x06; + gspca_dev->usb_buf[1] = val * 0x40; + reg_w(gspca_dev, 2); + } + return 0; +} + +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gamma; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) { + gspca_dev->usb_buf[0] = 0x67; + gspca_dev->usb_buf[1] = val * 4 + 3; + reg_w(gspca_dev, 2); + } + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + if (gspca_dev->streaming) + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -389,7 +485,10 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ @@ -421,8 +520,11 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c new file mode 100644 index 0000000..2a901a4 --- /dev/null +++ b/drivers/media/video/gspca/mr97310a.c @@ -0,0 +1,362 @@ +/* + * Mars MR97310A library + * + * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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 "mr97310a" + +#include "gspca.h" + +MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>"); +MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + u8 sof_read; +}; + +/* V4L2 controls supported by the driver */ +static struct ctrl sd_ctrls[] = { +}; + +static const struct v4l2_pix_format vga_mode[] = { + {160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 4}, + {176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* the bytes to write are in gspca_dev->usb_buf */ +static int reg_w(struct gspca_dev *gspca_dev, int len) +{ + int rc; + + rc = usb_bulk_msg(gspca_dev->dev, + usb_sndbulkpipe(gspca_dev->dev, 4), + gspca_dev->usb_buf, len, NULL, 500); + if (rc < 0) + PDEBUG(D_ERR, "reg write [%02x] error %d", + gspca_dev->usb_buf[0], rc); + return rc; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam; + + cam = &gspca_dev->cam; + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u8 *data = gspca_dev->usb_buf; + int err_code; + + sd->sof_read = 0; + + /* Note: register descriptions guessed from MR97113A driver */ + + data[0] = 0x01; + data[1] = 0x01; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x00; + data[1] = 0x0d; + data[2] = 0x01; + data[5] = 0x2b; + data[7] = 0x00; + data[9] = 0x50; /* reg 8, no scale down */ + data[10] = 0xc0; + + switch (gspca_dev->width) { + case 160: + data[9] |= 0x0c; /* reg 8, 4:1 scale down */ + /* fall thru */ + case 320: + data[9] |= 0x04; /* reg 8, 2:1 scale down */ + /* fall thru */ + case 640: + default: + data[3] = 0x50; /* reg 2, H size */ + data[4] = 0x78; /* reg 3, V size */ + data[6] = 0x04; /* reg 5, H start */ + data[8] = 0x03; /* reg 7, V start */ + break; + + case 176: + data[9] |= 0x04; /* reg 8, 2:1 scale down */ + /* fall thru */ + case 352: + data[3] = 0x2c; /* reg 2, H size */ + data[4] = 0x48; /* reg 3, V size */ + data[6] = 0x94; /* reg 5, H start */ + data[8] = 0x63; /* reg 7, V start */ + break; + } + + err_code = reg_w(gspca_dev, 11); + if (err_code < 0) + return err_code; + + data[0] = 0x0a; + data[1] = 0x80; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x14; + data[1] = 0x0a; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x1b; + data[1] = 0x00; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x15; + data[1] = 0x16; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x16; + data[1] = 0x10; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x17; + data[1] = 0x3a; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x18; + data[1] = 0x68; + err_code = reg_w(gspca_dev, 2); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x00; + data[2] = 0x02; + data[3] = 0x06; + data[4] = 0x59; + data[5] = 0x0c; + data[6] = 0x16; + data[7] = 0x00; + data[8] = 0x07; + data[9] = 0x00; + data[10] = 0x01; + err_code = reg_w(gspca_dev, 11); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x04; + data[2] = 0x11; + data[3] = 0x01; + err_code = reg_w(gspca_dev, 4); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x00; + data[2] = 0x0a; + data[3] = 0x00; + data[4] = 0x01; + data[5] = 0x00; + data[6] = 0x00; + data[7] = 0x01; + data[8] = 0x00; + data[9] = 0x0a; + err_code = reg_w(gspca_dev, 10); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x04; + data[2] = 0x11; + data[3] = 0x01; + err_code = reg_w(gspca_dev, 4); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x00; + data[2] = 0x12; + data[3] = 0x00; + data[4] = 0x63; + data[5] = 0x00; + data[6] = 0x70; + data[7] = 0x00; + data[8] = 0x00; + err_code = reg_w(gspca_dev, 9); + if (err_code < 0) + return err_code; + + data[0] = 0x1f; + data[1] = 0x04; + data[2] = 0x11; + data[3] = 0x01; + err_code = reg_w(gspca_dev, 4); + if (err_code < 0) + return err_code; + + data[0] = 0x00; + data[1] = 0x4d; /* ISOC transfering enable... */ + err_code = reg_w(gspca_dev, 2); + return err_code; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int result; + + gspca_dev->usb_buf[0] = 1; + gspca_dev->usb_buf[1] = 0; + result = reg_w(gspca_dev, 2); + if (result < 0) + PDEBUG(D_ERR, "Camera Stop failed"); +} + +/* Include pac common sof detection functions */ +#include "pac_common.h" + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + unsigned char *sof; + + sof = pac_find_sof(gspca_dev, data, len); + if (sof) { + int n; + + /* finish decoding current frame */ + n = sof - data; + if (n > sizeof pac_sof_marker) + n -= sizeof pac_sof_marker; + else + n = 0; + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data, n); + /* Start next frame. */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + pac_sof_marker, sizeof pac_sof_marker); + len -= sof - data; + data = sof; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0111)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- 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) +{ + if (usb_register(&sd_driver) < 0) + return -1; + 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/ov519.c b/drivers/media/video/gspca/ov519.c index ee23295..1fff37b 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -1360,7 +1360,6 @@ static int sd_config(struct gspca_dev *gspca_dev, } cam = &gspca_dev->cam; - cam->epaddr = OV511_ENDPOINT_ADDRESS; if (!sd->sif) { cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); @@ -2177,8 +2176,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 3bf15e4..19e0bc6 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -1,7 +1,8 @@ /* - * ov534/ov772x gspca driver + * ov534 gspca driver * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it> * Copyright (C) 2008 Jim Paris <jim@jtan.com> + * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr * * Based on a prototype written by Mark Ferrell <majortrips@gmail.com> * USB protocol reverse engineered by Jim Paris <jim@jtan.com> @@ -26,7 +27,7 @@ #include "gspca.h" -#define OV534_REG_ADDRESS 0xf1 /* ? */ +#define OV534_REG_ADDRESS 0xf1 /* sensor address */ #define OV534_REG_SUBADDR 0xf2 #define OV534_REG_WRITE 0xf3 #define OV534_REG_READ 0xf4 @@ -46,9 +47,13 @@ MODULE_LICENSE("GPL"); /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u32 last_fid; __u32 last_pts; - int frame_rate; + u16 last_fid; + u8 frame_rate; + + u8 sensor; +#define SENSOR_OV772X 0 +#define SENSOR_OV965X 1 }; /* V4L2 controls supported by the driver */ @@ -63,114 +68,7 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; -static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) -{ - struct usb_device *udev = gspca_dev->dev; - int ret; - - PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val); - gspca_dev->usb_buf[0] = val; - ret = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - 0x1, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); - if (ret < 0) - PDEBUG(D_ERR, "write failed"); -} - -static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) -{ - struct usb_device *udev = gspca_dev->dev; - int ret; - - ret = usb_control_msg(udev, - usb_rcvctrlpipe(udev, 0), - 0x1, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); - PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]); - if (ret < 0) - PDEBUG(D_ERR, "read failed"); - return gspca_dev->usb_buf[0]; -} - -/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. - * (direction and output)? */ -static void ov534_set_led(struct gspca_dev *gspca_dev, int status) -{ - u8 data; - - PDEBUG(D_CONF, "led status: %d", status); - - data = ov534_reg_read(gspca_dev, 0x21); - data |= 0x80; - ov534_reg_write(gspca_dev, 0x21, data); - - data = ov534_reg_read(gspca_dev, 0x23); - if (status) - data |= 0x80; - else - data &= ~(0x80); - - ov534_reg_write(gspca_dev, 0x23, data); -} - -static int sccb_check_status(struct gspca_dev *gspca_dev) -{ - u8 data; - int i; - - for (i = 0; i < 5; i++) { - data = ov534_reg_read(gspca_dev, OV534_REG_STATUS); - - switch (data) { - case 0x00: - return 1; - case 0x04: - return 0; - case 0x03: - break; - default: - PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5", - data, i + 1); - } - } - return 0; -} - -static void sccb_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) -{ - PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%02x", reg, val); - ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); - ov534_reg_write(gspca_dev, OV534_REG_WRITE, val); - ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); - - if (!sccb_check_status(gspca_dev)) - PDEBUG(D_ERR, "sccb_reg_write failed"); -} - -#ifdef GSPCA_DEBUG -static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg) -{ - ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); - ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); - if (!sccb_check_status(gspca_dev)) - PDEBUG(D_ERR, "sccb_reg_read failed 1"); - - ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); - if (!sccb_check_status(gspca_dev)) - PDEBUG(D_ERR, "sccb_reg_read failed 2"); - - return ov534_reg_read(gspca_dev, OV534_REG_READ); -} -#endif - -static const __u8 ov534_reg_initdata[][2] = { - { 0xe7, 0x3a }, - - { OV534_REG_ADDRESS, 0x42 }, /* select OV772x sensor */ - +static const u8 bridge_init_ov722x[][2] = { { 0xc2, 0x0c }, { 0x88, 0xf8 }, { 0xc3, 0x69 }, @@ -228,7 +126,7 @@ static const __u8 ov534_reg_initdata[][2] = { { 0xc2, 0x0c }, }; -static const __u8 ov772x_reg_initdata[][2] = { +static const u8 sensor_init_ov722x[][2] = { { 0x12, 0x80 }, { 0x11, 0x01 }, @@ -311,6 +209,456 @@ static const __u8 ov772x_reg_initdata[][2] = { { 0x0c, 0xd0 } }; +static const u8 bridge_init_ov965x[][2] = { + {0x88, 0xf8}, + {0x89, 0xff}, + {0x76, 0x03}, + {0x92, 0x03}, + {0x95, 0x10}, + {0xe2, 0x00}, + {0xe7, 0x3e}, + {0x8d, 0x1c}, + {0x8e, 0x00}, + {0x8f, 0x00}, + {0x1f, 0x00}, + {0xc3, 0xf9}, + {0x89, 0xff}, + {0x88, 0xf8}, + {0x76, 0x03}, + {0x92, 0x01}, + {0x93, 0x18}, + {0x1c, 0x0a}, + {0x1d, 0x48}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x34, 0x05}, + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0x34, 0x05}, + {0xe7, 0x2e}, + {0x31, 0xf9}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x25, 0x42}, + {0x94, 0x11}, +}; + +static const u8 sensor_init_ov965x[][2] = { + {0x12, 0x80}, /* com7 - reset */ + {0x00, 0x00}, /* gain */ + {0x01, 0x80}, /* blue */ + {0x02, 0x80}, /* red */ + {0x03, 0x1b}, /* vref */ + {0x04, 0x03}, /* com1 - exposure low bits */ + {0x0b, 0x57}, /* ver */ + {0x0e, 0x61}, /* com5 */ + {0x0f, 0x42}, /* com6 */ + {0x11, 0x00}, /* clkrc */ + {0x12, 0x02}, /* com7 */ + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x14, 0x28}, /* com9 */ + {0x16, 0x24}, /* rsvd16 */ + {0x17, 0x1d}, /* hstart*/ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop*/ + {0x1e, 0x04}, /* mvfp */ + {0x24, 0x3c}, /* aew */ + {0x25, 0x36}, /* aeb */ + {0x26, 0x71}, /* vpt */ + {0x27, 0x08}, /* bbias */ + {0x28, 0x08}, /* gbbias */ + {0x29, 0x15}, /* gr com */ + {0x2a, 0x00}, + {0x2b, 0x00}, + {0x2c, 0x08}, /* rbias */ + {0x32, 0xff}, /* href */ + {0x33, 0x00}, /* chlf */ + {0x34, 0x3f}, /* arblm */ + {0x35, 0x00}, /* rsvd35 */ + {0x36, 0xf8}, /* rsvd36 */ + {0x38, 0x72}, /* acom38 */ + {0x39, 0x57}, /* ofon */ + {0x3a, 0x80}, /* tslb */ + {0x3b, 0xc4}, + {0x3d, 0x99}, /* com13 */ + {0x3f, 0xc1}, + {0x40, 0xc0}, /* com15 */ + {0x41, 0x40}, /* com16 */ + {0x42, 0xc0}, + {0x43, 0x0a}, + {0x44, 0xf0}, + {0x45, 0x46}, + {0x46, 0x62}, + {0x47, 0x2a}, + {0x48, 0x3c}, + {0x4a, 0xfc}, + {0x4b, 0xfc}, + {0x4c, 0x7f}, + {0x4d, 0x7f}, + {0x4e, 0x7f}, + {0x4f, 0x98}, + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {0x58, 0x1a}, + {0x59, 0x85}, + {0x5a, 0xa9}, + {0x5b, 0x64}, + {0x5c, 0x84}, + {0x5d, 0x53}, + {0x5e, 0x0e}, + {0x5f, 0xf0}, + {0x60, 0xf0}, + {0x61, 0xf0}, + {0x62, 0x00}, /* lcc1 */ + {0x63, 0x00}, /* lcc2 */ + {0x64, 0x02}, /* lcc3 */ + {0x65, 0x16}, /* lcc4 */ + {0x66, 0x01}, /* lcc5 */ + {0x69, 0x02}, /* hv */ + {0x6b, 0x5a}, /* dbvl */ + {0x6c, 0x04}, + {0x6d, 0x55}, + {0x6e, 0x00}, + {0x6f, 0x9d}, + {0x70, 0x21}, + {0x71, 0x78}, + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0x77, 0x02}, + {0x7a, 0x12}, + {0x7b, 0x08}, + {0x7c, 0x16}, + {0x7d, 0x30}, + {0x7e, 0x5e}, + {0x7f, 0x72}, + {0x80, 0x82}, + {0x81, 0x8e}, + {0x82, 0x9a}, + {0x83, 0xa4}, + {0x84, 0xac}, + {0x85, 0xb8}, + {0x86, 0xc3}, + {0x87, 0xd6}, + {0x88, 0xe6}, + {0x89, 0xf2}, + {0x8a, 0x03}, + {0x8c, 0x89}, + {0x14, 0x28}, /* com9 */ + {0x90, 0x7d}, + {0x91, 0x7b}, + {0x9d, 0x03}, + {0x9e, 0x04}, + {0x9f, 0x7a}, + {0xa0, 0x79}, + {0xa1, 0x40}, /* aechm */ + {0xa4, 0x50}, + {0xa5, 0x68}, /* com26 */ + {0xa6, 0x4a}, + {0xa8, 0xc1}, /* acoma8 */ + {0xa9, 0xef}, /* acoma9 */ + {0xaa, 0x92}, + {0xab, 0x04}, + {0xac, 0x80}, + {0xad, 0x80}, + {0xae, 0x80}, + {0xaf, 0x80}, + {0xb2, 0xf2}, + {0xb3, 0x20}, + {0xb4, 0x20}, + {0xb5, 0x00}, + {0xb6, 0xaf}, + {0xbb, 0xae}, + {0xbc, 0x7f}, + {0xdb, 0x7f}, + {0xbe, 0x7f}, + {0xbf, 0x7f}, + {0xc0, 0xe2}, + {0xc1, 0xc0}, + {0xc2, 0x01}, + {0xc3, 0x4e}, + {0xc6, 0x85}, + {0xc7, 0x80}, + {0xc9, 0xe0}, + {0xca, 0xe8}, + {0xcb, 0xf0}, + {0xcc, 0xd8}, + {0xcd, 0xf1}, + {0x4f, 0x98}, + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {0x58, 0x1a}, + {0xff, 0x41}, /* read 41, write ff 00 */ + {0x41, 0x40}, /* com16 */ + {0xc5, 0x03}, + {0x6a, 0x02}, + + {0x12, 0x62}, /* com7 - VGA + CIF */ + {0x36, 0xfa}, /* rsvd36 */ + {0x69, 0x0a}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x00}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, + {0x03, 0x12}, /* vref */ + {0x17, 0x16}, /* hstart */ + {0x18, 0x02}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x3d}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xaa}, +}; + +static const u8 bridge_init_ov965x_2[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0xda, 0x01}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x3c}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0xa0}, + {0x5b, 0x78}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, +}; + +static const u8 sensor_init_ov965x_2[][2] = { + {0x3b, 0xc4}, + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, /* gain */ + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x03}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x05}, + {0xc5, 0x07}, + {0xa2, 0x4b}, + {0xa3, 0x3e}, + {0x2d, 0x00}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc0}, + {0x2d, 0x00}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc1}, + {0x3f, 0x01}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc1}, + {0x4f, 0x98}, + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {0x58, 0x1a}, + {0xff, 0x41}, /* read 41, write ff 00 */ + {0x41, 0x40}, /* com16 */ + {0x56, 0x40}, + {0x55, 0x8f}, + {0x10, 0x25}, /* aech - exposure high bits */ + {0xff, 0x13}, /* read 13, write ff 00 */ + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ +}; + +static const u8 bridge_start_ov965x[][2] = { + {0xc2, 0x4c}, + {0xc3, 0xf9}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x78}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0x28}, + {0x5b, 0x1e}, + {0x35, 0x00}, + {0xd9, 0x21}, + {0x94, 0x11}, +}; + +static const u8 sensor_start_ov965x[][2] = { + {0x3b, 0xe4}, + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x01}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x02}, + {0xc5, 0x03}, + {0xa2, 0x96}, + {0xa3, 0x7d}, + {0xff, 0x13}, /* read 13, write ff 00 */ + {0x13, 0xe7}, + {0x3a, 0x80}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc1}, +}; + + +static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val); + gspca_dev->usb_buf[0] = val; + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + if (ret < 0) + PDEBUG(D_ERR, "write failed"); +} + +static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + 0x01, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]); + if (ret < 0) + PDEBUG(D_ERR, "read failed"); + return gspca_dev->usb_buf[0]; +} + +/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. + * (direction and output)? */ +static void ov534_set_led(struct gspca_dev *gspca_dev, int status) +{ + u8 data; + + PDEBUG(D_CONF, "led status: %d", status); + + data = ov534_reg_read(gspca_dev, 0x21); + data |= 0x80; + ov534_reg_write(gspca_dev, 0x21, data); + + data = ov534_reg_read(gspca_dev, 0x23); + if (status) + data |= 0x80; + else + data &= ~0x80; + + ov534_reg_write(gspca_dev, 0x23, data); + + if (!status) { + data = ov534_reg_read(gspca_dev, 0x21); + data &= ~0x80; + ov534_reg_write(gspca_dev, 0x21, data); + } +} + +static int sccb_check_status(struct gspca_dev *gspca_dev) +{ + u8 data; + int i; + + for (i = 0; i < 5; i++) { + data = ov534_reg_read(gspca_dev, OV534_REG_STATUS); + + switch (data) { + case 0x00: + return 1; + case 0x04: + return 0; + case 0x03: + break; + default: + PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5", + data, i + 1); + } + } + return 0; +} + +static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + PDEBUG(D_USBO, "reg: 0x%02x, val: 0x%02x", reg, val); + ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); + ov534_reg_write(gspca_dev, OV534_REG_WRITE, val); + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); + + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_reg_write failed"); +} + +static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg) +{ + ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_reg_read failed 1"); + + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_reg_read failed 2"); + + return ov534_reg_read(gspca_dev, OV534_REG_READ); +} + +/* output a bridge sequence (reg - val) */ +static void reg_w_array(struct gspca_dev *gspca_dev, + const u8 (*data)[2], int len) +{ + while (--len >= 0) { + ov534_reg_write(gspca_dev, (*data)[0], (*data)[1]); + data++; + } +} + +/* output a sensor sequence (reg - val) */ +static void sccb_w_array(struct gspca_dev *gspca_dev, + const u8 (*data)[2], int len) +{ + while (--len >= 0) { + if ((*data)[0] != 0xff) { + sccb_reg_write(gspca_dev, (*data)[0], (*data)[1]); + } else { + sccb_reg_read(gspca_dev, (*data)[1]); + sccb_reg_write(gspca_dev, 0xff, 0x00); + } + data++; + } +} + /* set framerate */ static void ov534_set_frame_rate(struct gspca_dev *gspca_dev) { @@ -346,40 +694,17 @@ static void ov534_set_frame_rate(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "frame_rate: %d", fr); } -/* setup method */ -static void ov534_setup(struct gspca_dev *gspca_dev) -{ - int i; - - /* Initialize bridge chip */ - for (i = 0; i < ARRAY_SIZE(ov534_reg_initdata); i++) - ov534_reg_write(gspca_dev, ov534_reg_initdata[i][0], - ov534_reg_initdata[i][1]); - - PDEBUG(D_PROBE, "sensor is ov%02x%02x", - sccb_reg_read(gspca_dev, 0x0a), - sccb_reg_read(gspca_dev, 0x0b)); - - ov534_set_led(gspca_dev, 1); - - /* Initialize sensor */ - for (i = 0; i < ARRAY_SIZE(ov772x_reg_initdata); i++) - sccb_reg_write(gspca_dev, ov772x_reg_initdata[i][0], - ov772x_reg_initdata[i][1]); - - ov534_reg_write(gspca_dev, 0xe0, 0x09); - ov534_set_led(gspca_dev, 0); -} - /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { + struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; + sd->sensor = id->driver_info; + cam = &gspca_dev->cam; - cam->epaddr = 0x01; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); @@ -392,26 +717,102 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - ov534_setup(gspca_dev); - ov534_set_frame_rate(gspca_dev); + struct sd *sd = (struct sd *) gspca_dev; + u16 sensor_id; + static const u8 sensor_addr[2] = { + 0x42, /* 0 SENSOR_OV772X */ + 0x60, /* 1 SENSOR_OV965X */ + }; + + /* reset bridge */ + ov534_reg_write(gspca_dev, 0xe7, 0x3a); + ov534_reg_write(gspca_dev, 0xe0, 0x08); + msleep(100); + + /* initialize the sensor address */ + ov534_reg_write(gspca_dev, OV534_REG_ADDRESS, + sensor_addr[sd->sensor]); + + /* reset sensor */ + sccb_reg_write(gspca_dev, 0x12, 0x80); + msleep(10); + + /* probe the sensor */ + sccb_reg_read(gspca_dev, 0x0a); + sensor_id = sccb_reg_read(gspca_dev, 0x0a) << 8; + sccb_reg_read(gspca_dev, 0x0b); + sensor_id |= sccb_reg_read(gspca_dev, 0x0b); + PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id); + + /* initialize */ + switch (sd->sensor) { + case SENSOR_OV772X: + reg_w_array(gspca_dev, bridge_init_ov722x, + ARRAY_SIZE(bridge_init_ov722x)); + ov534_set_led(gspca_dev, 1); + sccb_w_array(gspca_dev, sensor_init_ov722x, + ARRAY_SIZE(sensor_init_ov722x)); + ov534_reg_write(gspca_dev, 0xe0, 0x09); + ov534_set_led(gspca_dev, 0); + ov534_set_frame_rate(gspca_dev); + break; + default: +/* case SENSOR_OV965X: */ + reg_w_array(gspca_dev, bridge_init_ov965x, + ARRAY_SIZE(bridge_init_ov965x)); + sccb_w_array(gspca_dev, sensor_init_ov965x, + ARRAY_SIZE(sensor_init_ov965x)); + reg_w_array(gspca_dev, bridge_init_ov965x_2, + ARRAY_SIZE(bridge_init_ov965x_2)); + sccb_w_array(gspca_dev, sensor_init_ov965x_2, + ARRAY_SIZE(sensor_init_ov965x_2)); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + ov534_reg_write(gspca_dev, 0xe0, 0x01); + ov534_set_led(gspca_dev, 0); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + } return 0; } static int sd_start(struct gspca_dev *gspca_dev) { - /* start streaming data */ - ov534_set_led(gspca_dev, 1); - ov534_reg_write(gspca_dev, 0xe0, 0x00); + struct sd *sd = (struct sd *) gspca_dev; + switch (sd->sensor) { + case SENSOR_OV772X: + ov534_set_led(gspca_dev, 1); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + break; + default: +/* case SENSOR_OV965X: */ + reg_w_array(gspca_dev, bridge_start_ov965x, + ARRAY_SIZE(bridge_start_ov965x)); + sccb_w_array(gspca_dev, sensor_start_ov965x, + ARRAY_SIZE(sensor_start_ov965x)); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + ov534_set_led(gspca_dev, 1); +/*fixme: other sensor start omitted*/ + } return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) { - /* stop streaming data */ - ov534_reg_write(gspca_dev, 0xe0, 0x09); - ov534_set_led(gspca_dev, 0); + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_OV772X: + ov534_reg_write(gspca_dev, 0xe0, 0x09); + ov534_set_led(gspca_dev, 0); + break; + default: +/* case SENSOR_OV965X: */ + ov534_reg_write(gspca_dev, 0xe0, 0x01); + ov534_set_led(gspca_dev, 0); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + break; + } } /* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ @@ -429,75 +830,75 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, { struct sd *sd = (struct sd *) gspca_dev; __u32 this_pts; - int this_fid; + u16 this_fid; int remaining_len = len; - __u8 *next_data = data; -scan_next: - if (remaining_len <= 0) - return; - - data = next_data; - len = min(remaining_len, 2048); - remaining_len -= len; - next_data += len; - - /* Payloads are prefixed with a UVC-style header. We - consider a frame to start when the FID toggles, or the PTS - changes. A frame ends when EOF is set, and we've received - the correct number of bytes. */ - - /* Verify UVC header. Header length is always 12 */ - if (data[0] != 12 || len < 12) { - PDEBUG(D_PACK, "bad header"); - goto discard; - } - - /* Check errors */ - if (data[1] & UVC_STREAM_ERR) { - PDEBUG(D_PACK, "payload error"); - goto discard; - } + do { + len = min(remaining_len, 2040); /*fixme: was 2048*/ - /* Extract PTS and FID */ - if (!(data[1] & UVC_STREAM_PTS)) { - PDEBUG(D_PACK, "PTS not present"); - goto discard; - } - this_pts = (data[5] << 24) | (data[4] << 16) | (data[3] << 8) | data[2]; - this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0; - - /* If PTS or FID has changed, start a new frame. */ - if (this_pts != sd->last_pts || this_fid != sd->last_fid) { - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); - sd->last_pts = this_pts; - sd->last_fid = this_fid; - } + /* Payloads are prefixed with a UVC-style header. We + consider a frame to start when the FID toggles, or the PTS + changes. A frame ends when EOF is set, and we've received + the correct number of bytes. */ - /* Add the data from this payload */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data + 12, len - 12); + /* Verify UVC header. Header length is always 12 */ + if (data[0] != 12 || len < 12) { + PDEBUG(D_PACK, "bad header"); + goto discard; + } - /* If this packet is marked as EOF, end the frame */ - if (data[1] & UVC_STREAM_EOF) { - sd->last_pts = 0; + /* Check errors */ + if (data[1] & UVC_STREAM_ERR) { + PDEBUG(D_PACK, "payload error"); + goto discard; + } - if ((frame->data_end - frame->data) != - (gspca_dev->width * gspca_dev->height * 2)) { - PDEBUG(D_PACK, "short frame"); + /* Extract PTS and FID */ + if (!(data[1] & UVC_STREAM_PTS)) { + PDEBUG(D_PACK, "PTS not present"); goto discard; } + this_pts = (data[5] << 24) | (data[4] << 16) + | (data[3] << 8) | data[2]; + this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0; + + /* If PTS or FID has changed, start a new frame. */ + if (this_pts != sd->last_pts || this_fid != sd->last_fid) { + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + NULL, 0); + sd->last_pts = this_pts; + sd->last_fid = this_fid; + } - gspca_frame_add(gspca_dev, LAST_PACKET, frame, NULL, 0); - } + /* Add the data from this payload */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data + 12, len - 12); - /* Done this payload */ - goto scan_next; + /* If this packet is marked as EOF, end the frame */ + if (data[1] & UVC_STREAM_EOF) { + sd->last_pts = 0; + + if (frame->data_end - frame->data != + gspca_dev->width * gspca_dev->height * 2) { + PDEBUG(D_PACK, "short frame"); + goto discard; + } + + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + NULL, 0); + } + + /* Done this payload */ + goto scan_next; discard: - /* Discard data until a new frame starts. */ - gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0); - goto scan_next; + /* Discard data until a new frame starts. */ + gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0); + +scan_next: + remaining_len -= len; + data += len; + } while (remaining_len > 0); } /* get stream parameters (framerate) */ @@ -556,9 +957,8 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x06f8, 0x3002)}, /* Hercules Blog Webcam */ - {USB_DEVICE(0x06f8, 0x3003)}, /* Hercules Dualpix HD Weblog */ - {USB_DEVICE(0x1415, 0x2000)}, /* Sony HD Eye for PS3 (SLEH 00201) */ + {USB_DEVICE(0x06f8, 0x3003), .driver_info = SENSOR_OV965X}, + {USB_DEVICE(0x1415, 0x2000), .driver_info = SENSOR_OV772X}, {} }; @@ -585,8 +985,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index c90ac85..95a97ab 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -256,7 +256,6 @@ static int sd_config(struct gspca_dev *gspca_dev, " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); cam = &gspca_dev->cam; - cam->epaddr = 0x05; cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); sd->brightness = PAC207_BRIGHTNESS_DEFAULT; @@ -536,6 +535,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2470)}, {USB_DEVICE(0x093a, 0x2471)}, {USB_DEVICE(0x093a, 0x2472)}, + {USB_DEVICE(0x093a, 0x2474)}, {USB_DEVICE(0x093a, 0x2476)}, {USB_DEVICE(0x145f, 0x013a)}, {USB_DEVICE(0x2001, 0xf115)}, @@ -565,8 +565,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index a9c95cb..e1e3a3a 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -498,7 +498,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->epaddr = 0x05; sd->sensor = id->driver_info; if (sd->sensor == SENSOR_PAC7302) { @@ -1097,8 +1096,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index b3e4e06..153d0a9 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -870,7 +870,6 @@ static int sd_config(struct gspca_dev *gspca_dev, gspca_dev->ctrl_dis = sensor_data[sd->sensor].ctrl_dis; cam = &gspca_dev->cam; - cam->epaddr = 0x01; if (!(sensor_data[sd->sensor].flags & F_SIF)) { cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); @@ -1272,8 +1271,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 3373b8d..c72e19d 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -35,36 +35,47 @@ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ atomic_t avg_lum; - unsigned int exposure; - - __u16 brightness; - __u8 contrast; - __u8 colors; - __u8 autogain; - __u8 blue; - __u8 red; - __u8 vflip; /* ov7630 only */ - __u8 infrared; /* mi0360 only */ - - __s8 ag_cnt; + u32 exposure; + + u16 brightness; + u8 contrast; + u8 colors; + u8 autogain; + u8 blue; + u8 red; + u8 gamma; + u8 vflip; /* ov7630/ov7648 only */ + u8 infrared; /* mt9v111 only */ + u8 quality; /* image quality */ +#define QUALITY_MIN 60 +#define QUALITY_MAX 95 +#define QUALITY_DEF 80 + u8 jpegqual; /* webcam quality */ + + u8 reg18; + + s8 ag_cnt; #define AG_CNT_START 13 - __u8 qindex; - __u8 bridge; + u8 bridge; #define BRIDGE_SN9C102P 0 #define BRIDGE_SN9C105 1 #define BRIDGE_SN9C110 2 #define BRIDGE_SN9C120 3 #define BRIDGE_SN9C325 4 - __u8 sensor; /* Type of image sensor chip */ + u8 sensor; /* Type of image sensor chip */ #define SENSOR_HV7131R 0 #define SENSOR_MI0360 1 #define SENSOR_MO4000 2 -#define SENSOR_OM6802 3 -#define SENSOR_OV7630 4 -#define SENSOR_OV7648 5 -#define SENSOR_OV7660 6 - __u8 i2c_base; +#define SENSOR_MT9V111 3 +#define SENSOR_OM6802 4 +#define SENSOR_OV7630 5 +#define SENSOR_OV7648 6 +#define SENSOR_OV7660 7 +#define SENSOR_SP80708 8 + u8 i2c_base; + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -78,6 +89,8 @@ static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val); static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val); static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); @@ -158,6 +171,20 @@ static struct ctrl sd_ctrls[] = { .set = sd_setred_balance, .get = sd_getred_balance, }, + { + { + .id = V4L2_CID_GAMMA, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gamma", + .minimum = 0, + .maximum = 40, + .step = 1, +#define GAMMA_DEF 20 + .default_value = GAMMA_DEF, + }, + .set = sd_setgamma, + .get = sd_getgamma, + }, #define AUTOGAIN_IDX 5 { { @@ -173,7 +200,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, -/* ov7630 only */ +/* ov7630/ov7648 only */ #define VFLIP_IDX 6 { { @@ -183,13 +210,13 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define VFLIP_DEF 1 +#define VFLIP_DEF 0 /* vflip def = 1 for ov7630 */ .default_value = VFLIP_DEF, }, .set = sd_setvflip, .get = sd_getvflip, }, -/* mi0360 only */ +/* mt9v111 only */ #define INFRARED_IDX 7 { { @@ -211,18 +238,22 @@ static struct ctrl sd_ctrls[] = { static __u32 ctrl_dis[] = { (1 << INFRARED_IDX) | (1 << VFLIP_IDX), /* SENSOR_HV7131R 0 */ - (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX), /* SENSOR_MI0360 1 */ (1 << INFRARED_IDX) | (1 << VFLIP_IDX), /* SENSOR_MO4000 2 */ + (1 << VFLIP_IDX), + /* SENSOR_MT9V111 3 */ (1 << INFRARED_IDX) | (1 << VFLIP_IDX), - /* SENSOR_OM6802 3 */ + /* SENSOR_OM6802 4 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX), - /* SENSOR_OV7630 4 */ + /* SENSOR_OV7630 5 */ + (1 << INFRARED_IDX), + /* SENSOR_OV7648 6 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), - /* SENSOR_OV7648 5 */ + /* SENSOR_OV7660 7 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), - /* SENSOR_OV7660 6 */ + /* SENSOR_SP80708 8 */ }; static const struct v4l2_pix_format vga_mode[] = { @@ -243,196 +274,228 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; -/*Data from sn9c102p+hv71331r */ -static const __u8 sn_hv7131[] = { +/*Data from sn9c102p+hv7131r */ +static const u8 sn_hv7131[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, -/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +/* reg18 reg19 reg1a reg1b */ + 0x0a, 0x00, 0x00, 0x00 }; -static const __u8 sn_mi0360[] = { +static const u8 sn_mi0360[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, -/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +/* reg18 reg19 reg1a reg1b */ + 0x06, 0x00, 0x00, 0x00 }; -static const __u8 sn_mo4000[] = { +static const u8 sn_mo4000[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x12, 0x23, 0x60, 0x00, 0x1a, 0x00, 0x20, 0x18, + 0x00, 0x23, 0x60, 0x00, 0x1a, 0x00, 0x20, 0x18, /* reg8 reg9 rega regb regc regd rege regf */ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x0b, 0x0f, 0x14, 0x28, 0x1e, 0x40, -/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +/* reg18 reg19 reg1a reg1b */ + 0x08, 0x00, 0x00, 0x00 }; -static const __u8 sn_om6802[] = { +static const u8 sn_mt9v111[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x5c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40, +/* reg18 reg19 reg1a reg1b */ + 0x06, 0x00, 0x00, 0x00 +}; + +static const u8 sn_om6802[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x23, 0x72, 0x00, 0x1a, 0x34, 0x27, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x51, 0x01, 0x00, 0x28, 0x1e, 0x40, -/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x22, 0x44, 0x63, 0x7d, 0x92, 0xa3, 0xaf, - 0xbc, 0xc4, 0xcd, 0xd5, 0xdc, 0xe1, 0xe8, 0xef, - 0xf7 +/* reg18 reg19 reg1a reg1b */ + 0x05, 0x00, 0x00, 0x00 }; -static const __u8 sn_ov7630[] = { +static const u8 sn_ov7630[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x21, 0x40, 0x00, 0x1a, 0x20, 0x1f, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ 0xa1, 0x21, 0x76, 0x21, 0x00, 0x00, 0x00, 0x10, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2, -/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ - 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00 +/* reg18 reg19 reg1a reg1b */ + 0x0b, 0x00, 0x00, 0x00 }; -static const __u8 sn_ov7648[] = { +static const u8 sn_ov7648[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00, -/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ - 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00 +/* reg18 reg19 reg1a reg1b */ + 0x0b, 0x00, 0x00, 0x00 }; -static const __u8 sn_ov7660[] = { +static const u8 sn_ov7660[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ 0x81, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20, -/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */ - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + +static const u8 sn_sp80708[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 }; /* sequence specific to the sensors - !! index = SENSOR_xxx */ -static const __u8 *sn_tb[] = { +static const u8 *sn_tb[] = { sn_hv7131, sn_mi0360, sn_mo4000, + sn_mt9v111, sn_om6802, sn_ov7630, sn_ov7648, - sn_ov7660 + sn_ov7660, + sn_sp80708 }; -static const __u8 gamma_def[] = { +/* default gamma table */ +static const u8 gamma_def[17] = { 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99, 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff }; +/* gamma for sensors HV7131R and MT9V111 */ +static const u8 gamma_spec_1[17] = { + 0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d, + 0xa9, 0xb4, 0xbe, 0xc8, 0xd2, 0xdb, 0xe4, 0xed, 0xf5 +}; +/* gamma for sensor SP80708 */ +static const u8 gamma_spec_2[17] = { + 0x0a, 0x2d, 0x4e, 0x68, 0x7d, 0x8f, 0x9f, 0xab, + 0xb7, 0xc2, 0xcc, 0xd3, 0xd8, 0xde, 0xe2, 0xe5, 0xe6 +}; /* color matrix and offsets */ -static const __u8 reg84[] = { +static const u8 reg84[] = { 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, /* YR YG YB gains */ 0xe8, 0x0f, 0xda, 0x0f, 0x40, 0x00, /* UR UG UB */ 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */ 0x00, 0x00, 0x00 /* YUV offsets */ }; -static const __u8 hv7131r_sensor_init[][8] = { - {0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, - {0xB1, 0x11, 0x34, 0x17, 0x7F, 0x00, 0x00, 0x10}, - {0xD1, 0x11, 0x40, 0xFF, 0x7F, 0x7F, 0x7F, 0x10}, - {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x11, 0x14, 0x01, 0xE2, 0x02, 0x82, 0x10}, - {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, - - {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xC1, 0x11, 0x25, 0x00, 0x61, 0xA8, 0x00, 0x10}, - {0xA1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10}, - {0xC1, 0x11, 0x31, 0x20, 0x2E, 0x20, 0x00, 0x10}, - {0xC1, 0x11, 0x25, 0x00, 0xC3, 0x50, 0x00, 0x10}, - {0xA1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */ - {0xC1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */ - - {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10}, - - {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xA1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, +static const u8 hv7131r_sensor_init[][8] = { + {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, + {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10}, + {0xd1, 0x11, 0x40, 0xff, 0x7f, 0x7f, 0x7f, 0x10}, +/* {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, */ + {0xd1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x11, 0x14, 0x01, 0xe2, 0x02, 0x82, 0x10}, +/* {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, */ + + {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x11, 0x25, 0x00, 0x61, 0xa8, 0x00, 0x10}, + {0xa1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x11, 0x31, 0x20, 0x2e, 0x20, 0x00, 0x10}, + {0xc1, 0x11, 0x25, 0x00, 0xc3, 0x50, 0x00, 0x10}, + {0xa1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */ + {0xc1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */ + + {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10}, + + {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, {} }; -static const __u8 mi0360_sensor_init[][8] = { - {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, - {0xB1, 0x5D, 0x0D, 0x00, 0x01, 0x00, 0x00, 0x10}, - {0xB1, 0x5D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10}, - {0xD1, 0x5D, 0x03, 0x01, 0xE2, 0x02, 0x82, 0x10}, - {0xD1, 0x5D, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10}, - {0xB1, 0x5D, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xB1, 0x5D, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10}, - {0xD1, 0x5D, 0x2F, 0xF7, 0xB0, 0x00, 0x04, 0x10}, - {0xD1, 0x5D, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10}, - {0xB1, 0x5D, 0x3D, 0x06, 0x8F, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x40, 0x01, 0xE0, 0x00, 0xD1, 0x10}, - {0xB1, 0x5D, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10}, - {0xD1, 0x5D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x5E, 0x00, 0x00, 0xA3, 0x1D, 0x10}, - {0xB1, 0x5D, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10}, - - {0xB1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, - {0xB1, 0x5D, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, - {0xB1, 0x5D, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10}, - {0xD1, 0x5D, 0x2B, 0x00, 0xA0, 0x00, 0xB0, 0x10}, - {0xD1, 0x5D, 0x2D, 0x00, 0xA0, 0x00, 0xA0, 0x10}, - - {0xB1, 0x5D, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */ - {0xB1, 0x5D, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10}, - {0xB1, 0x5D, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x10}, - {0xB1, 0x5D, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */ - - {0xD1, 0x5D, 0x2B, 0x00, 0xB9, 0x00, 0xE3, 0x10}, - {0xD1, 0x5D, 0x2D, 0x00, 0x5f, 0x00, 0xB9, 0x10}, /* 42 */ -/* {0xB1, 0x5D, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */ -/* {0xB1, 0x5D, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */ - {0xB1, 0x5D, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */ - {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */ +static const u8 mi0360_sensor_init[][8] = { + {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10}, + {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10}, + {0xd1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10}, + {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10}, + {0xd1, 0x5d, 0x2f, 0xf7, 0xB0, 0x00, 0x04, 0x10}, + {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10}, + {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10}, + {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10}, + {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10}, + {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10}, + + {0xb1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10}, + {0xd1, 0x5d, 0x2b, 0x00, 0xa0, 0x00, 0xb0, 0x10}, + {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0xa0, 0x10}, + + {0xb1, 0x5d, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */ + {0xb1, 0x5d, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x10}, + {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */ + + {0xd1, 0x5d, 0x2b, 0x00, 0xb9, 0x00, 0xe3, 0x10}, + {0xd1, 0x5d, 0x2d, 0x00, 0x5f, 0x00, 0xb9, 0x10}, /* 42 */ +/* {0xb1, 0x5d, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */ +/* {0xb1, 0x5d, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */ + {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */ + {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */ {} }; -static const __u8 mo4000_sensor_init[][8] = { +static const u8 mo4000_sensor_init[][8] = { {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10}, @@ -455,7 +518,49 @@ static const __u8 mo4000_sensor_init[][8] = { {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10}, {} }; -static __u8 om6802_sensor_init[][8] = { +static const u8 mt9v111_sensor_init[][8] = { + {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */ + /* delay 20 ms */ + {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */ + {0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */ + {0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, /* op mode ctrl */ + {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x03, 0x01, 0xe1, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x04, 0x02, 0x81, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x05, 0x00, 0x04, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, /* sensor select */ + {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x03, 0x01, 0xe6, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x04, 0x02, 0x86, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x05, 0x00, 0x04, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x08, 0x00, 0x08, 0x00, 0x00, 0x10}, /* row start */ + {0xb1, 0x5c, 0x0e, 0x00, 0x08, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, /* col start */ + {0xb1, 0x5c, 0x03, 0x01, 0xe7, 0x00, 0x00, 0x10}, /* window height */ + {0xb1, 0x5c, 0x04, 0x02, 0x87, 0x00, 0x00, 0x10}, /* window width */ + {0xb1, 0x5c, 0x07, 0x30, 0x02, 0x00, 0x00, 0x10}, /* output ctrl */ + {0xb1, 0x5c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, /* shutter delay */ + {0xb1, 0x5c, 0x12, 0x00, 0xb0, 0x00, 0x00, 0x10}, /* zoom col start */ + {0xb1, 0x5c, 0x13, 0x00, 0x7c, 0x00, 0x00, 0x10}, /* zoom row start */ + {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */ + {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */ + {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + /*******/ + {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x5c, 0x09, 0x01, 0x2c, 0x00, 0x00, 0x10}, + {0xd1, 0x5c, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10}, /* green1 gain */ + {0xd1, 0x5c, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10}, /* red gain */ + /*******/ + {0xb1, 0x5c, 0x06, 0x00, 0x1e, 0x00, 0x00, 0x10}, /* vert blanking */ + {0xb1, 0x5c, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x10}, /* horiz blanking */ + {0xd1, 0x5c, 0x2c, 0x00, 0xad, 0x00, 0xad, 0x10}, /* blue gain */ + {0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */ + {} +}; +static const u8 om6802_sensor_init[][8] = { {0xa0, 0x34, 0x90, 0x05, 0x00, 0x00, 0x00, 0x10}, {0xa0, 0x34, 0x49, 0x85, 0x00, 0x00, 0x00, 0x10}, {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10}, @@ -489,7 +594,7 @@ static __u8 om6802_sensor_init[][8] = { /* {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, */ {} }; -static const __u8 ov7630_sensor_init[][8] = { +static const u8 ov7630_sensor_init[][8] = { {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, /* win: delay 20ms */ @@ -543,7 +648,7 @@ static const __u8 ov7630_sensor_init[][8] = { {} }; -static const __u8 ov7648_sensor_init[][8] = { +static const u8 ov7648_sensor_init[][8] = { {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, @@ -572,7 +677,8 @@ static const __u8 ov7648_sensor_init[][8] = { {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, /*...*/ /* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */ -/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, jfm done */ +/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN + * set by setvflip */ {0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10}, /* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */ @@ -589,7 +695,7 @@ static const __u8 ov7648_sensor_init[][8] = { {} }; -static const __u8 ov7660_sensor_init[][8] = { +static const u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ /* (delay 20ms) */ {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, @@ -678,28 +784,92 @@ static const __u8 ov7660_sensor_init[][8] = { {} }; -static const __u8 qtable4[] = { - 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06, - 0x06, 0x08, 0x0A, 0x11, - 0x0A, 0x0A, 0x08, 0x08, 0x0A, 0x15, 0x0F, 0x0F, 0x0C, 0x11, 0x19, 0x15, - 0x19, 0x19, 0x17, 0x15, - 0x17, 0x17, 0x1B, 0x1D, 0x25, 0x21, 0x1B, 0x1D, 0x23, 0x1D, 0x17, 0x17, - 0x21, 0x2E, 0x21, 0x23, - 0x27, 0x29, 0x2C, 0x2C, 0x2C, 0x19, 0x1F, 0x30, 0x32, 0x2E, 0x29, 0x32, - 0x25, 0x29, 0x2C, 0x29, - 0x06, 0x08, 0x08, 0x0A, 0x08, 0x0A, 0x13, 0x0A, 0x0A, 0x13, 0x29, 0x1B, - 0x17, 0x1B, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29 +static const u8 sp80708_sensor_init[][8] = { + {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x0d, 0xc0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x10, 0x40, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x12, 0x53, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x15, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x19, 0x18, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x1a, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x1c, 0x28, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x1d, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x1e, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x26, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x27, 0x1e, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x28, 0x5a, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x29, 0x28, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2a, 0x78, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2b, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2c, 0xf7, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2d, 0x2d, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2e, 0xd5, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x39, 0x42, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3a, 0x67, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3b, 0x87, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3c, 0xa3, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3d, 0xb0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3e, 0xbc, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x3f, 0xc8, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x40, 0xd4, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x41, 0xdf, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x42, 0xea, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x43, 0xf5, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x45, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x46, 0x60, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x47, 0x50, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x48, 0x30, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x49, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x4d, 0xae, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x4e, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x4f, 0x66, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x50, 0x1c, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x44, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x4a, 0x30, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x51, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x52, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x53, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x54, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x55, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x56, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x57, 0xe0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x58, 0xc0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x59, 0xab, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5a, 0xa0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5b, 0x99, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5c, 0x90, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5e, 0x24, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x61, 0x73, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x63, 0x42, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x64, 0x42, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x65, 0x42, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x66, 0x24, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10}, + /********/ + {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x04, 0xa4, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x14, 0x3f, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x18, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x18, 0x11, 0x40, 0x40, 0x00, 0x00, 0x10}, + {} }; /* read <len> bytes to gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, - __u16 value, int len) + u16 value, int len) { #ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { @@ -718,10 +888,10 @@ static void reg_r(struct gspca_dev *gspca_dev, } static void reg_w1(struct gspca_dev *gspca_dev, - __u16 value, - __u8 data) + u16 value, + u8 data) { - PDEBUG(D_USBO, "reg_w1 [%02x] = %02x", value, data); + PDEBUG(D_USBO, "reg_w1 [%04x] = %02x", value, data); gspca_dev->usb_buf[0] = data; usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -733,11 +903,11 @@ static void reg_w1(struct gspca_dev *gspca_dev, 500); } static void reg_w(struct gspca_dev *gspca_dev, - __u16 value, - const __u8 *buffer, + u16 value, + const u8 *buffer, int len) { - PDEBUG(D_USBO, "reg_w [%02x] = %02x %02x ..", + PDEBUG(D_USBO, "reg_w [%04x] = %02x %02x ..", value, buffer[0], buffer[1]); #ifdef GSPCA_DEBUG if (len > USB_BUF_SZ) { @@ -756,7 +926,7 @@ static void reg_w(struct gspca_dev *gspca_dev, } /* I2C write 1 byte */ -static void i2c_w1(struct gspca_dev *gspca_dev, __u8 reg, __u8 val) +static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -781,7 +951,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, __u8 reg, __u8 val) /* I2C write 8 bytes */ static void i2c_w8(struct gspca_dev *gspca_dev, - const __u8 *buffer) + const u8 *buffer) { memcpy(gspca_dev->usb_buf, buffer, 8); usb_control_msg(gspca_dev->dev, @@ -795,10 +965,10 @@ static void i2c_w8(struct gspca_dev *gspca_dev, } /* read 5 bytes in gspca_dev->usb_buf */ -static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg) +static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg) { struct sd *sd = (struct sd *) gspca_dev; - __u8 mode[8]; + u8 mode[8]; mode[0] = 0x81 | 0x10; mode[1] = sd->i2c_base; @@ -817,7 +987,7 @@ static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg) reg_r(gspca_dev, 0x0a, 5); } -static int probesensor(struct gspca_dev *gspca_dev) +static int hv7131r_probe(struct gspca_dev *gspca_dev) { i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */ msleep(10); @@ -839,16 +1009,66 @@ static int probesensor(struct gspca_dev *gspca_dev) return -ENODEV; } +static void mi0360_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, j; + u16 val = 0; + static const u8 probe_tb[][4][8] = { + { /* mi0360 */ + {0xb0, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, + {0x90, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa2, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10} + }, + { /* mt9v111 */ + {0xb0, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, + {0x90, 0x5c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa2, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, + {} + }, + }; + + for (i = 0; i < ARRAY_SIZE(probe_tb); i++) { + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + for (j = 0; j < 3; j++) + i2c_w8(gspca_dev, probe_tb[i][j]); + msleep(2); + reg_r(gspca_dev, 0x0a, 5); + val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4]; + if (probe_tb[i][3][0] != 0) + i2c_w8(gspca_dev, probe_tb[i][3]); + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + if (val != 0xffff) + break; + } + switch (val) { + case 0x823a: + PDEBUG(D_PROBE, "Sensor mt9v111"); + sd->sensor = SENSOR_MT9V111; + sd->i2c_base = 0x5c; + break; + case 0x8243: + PDEBUG(D_PROBE, "Sensor mi0360"); + break; + default: + PDEBUG(D_PROBE, "Unknown sensor %04x - forced to mi0360", val); + break; + } +} + static int configure_gpio(struct gspca_dev *gspca_dev, - const __u8 *sn9c1xx) + const u8 *sn9c1xx) { struct sd *sd = (struct sd *) gspca_dev; - const __u8 *reg9a; - static const __u8 reg9a_def[] = + const u8 *reg9a; + static const u8 reg9a_def[] = {0x08, 0x40, 0x20, 0x10, 0x00, 0x04}; - static const __u8 reg9a_sn9c325[] = + static const u8 reg9a_sn9c325[] = {0x0a, 0x40, 0x38, 0x30, 0x00, 0x20}; - static const __u8 regd4[] = {0x60, 0x00, 0x00}; + static const u8 regd4[] = {0x60, 0x00, 0x00}; reg_w1(gspca_dev, 0xf1, 0x00); reg_w1(gspca_dev, 0x01, sn9c1xx[1]); @@ -872,6 +1092,12 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f); switch (sd->sensor) { + case SENSOR_MT9V111: + reg_w1(gspca_dev, 0x01, 0x61); + reg_w1(gspca_dev, 0x17, 0x61); + reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); + break; case SENSOR_OM6802: reg_w1(gspca_dev, 0x02, 0x71); reg_w1(gspca_dev, 0x01, 0x42); @@ -900,12 +1126,20 @@ static int configure_gpio(struct gspca_dev *gspca_dev, break; } /* fall thru */ + case SENSOR_SP80708: + reg_w1(gspca_dev, 0x01, 0x63); + reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x01, 0x62); + reg_w1(gspca_dev, 0x01, 0x42); + mdelay(100); + reg_w1(gspca_dev, 0x02, 0x62); + break; default: reg_w1(gspca_dev, 0x01, 0x43); reg_w1(gspca_dev, 0x17, 0x61); reg_w1(gspca_dev, 0x01, 0x42); if (sd->sensor == SENSOR_HV7131R) { - if (probesensor(gspca_dev) < 0) + if (hv7131r_probe(gspca_dev) < 0) return -ENODEV; } break; @@ -916,7 +1150,7 @@ static int configure_gpio(struct gspca_dev *gspca_dev, static void hv7131R_InitSensor(struct gspca_dev *gspca_dev) { int i = 0; - static const __u8 SetSensorClk[] = /* 0x08 Mclk */ + static const u8 SetSensorClk[] = /* 0x08 Mclk */ { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 }; while (hv7131r_sensor_init[i][0]) { @@ -946,6 +1180,19 @@ static void mo4000_InitSensor(struct gspca_dev *gspca_dev) } } +static void mt9v111_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + + i2c_w8(gspca_dev, mt9v111_sensor_init[i]); + i++; + msleep(20); + while (mt9v111_sensor_init[i][0]) { + i2c_w8(gspca_dev, mt9v111_sensor_init[i]); + i++; + } +} + static void om6802_InitSensor(struct gspca_dev *gspca_dev) { int i = 0; @@ -1010,6 +1257,19 @@ static void ov7660_InitSensor(struct gspca_dev *gspca_dev) } } +static void sp80708_InitSensor(struct gspca_dev *gspca_dev) +{ + int i = 0; + + i2c_w8(gspca_dev, sp80708_sensor_init[i]); /* reset SCCB */ + i++; + msleep(20); + while (sp80708_sensor_init[i][0]) { + i2c_w8(gspca_dev, sp80708_sensor_init[i]); + i++; + } +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -1018,7 +1278,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->epaddr = 0x01; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); @@ -1026,16 +1285,21 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = id->driver_info >> 8; sd->i2c_base = id->driver_info; - sd->qindex = 4; /* set the quantization table */ sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; sd->blue = BLUE_BALANCE_DEF; sd->red = RED_BALANCE_DEF; + sd->gamma = GAMMA_DEF; sd->autogain = AUTOGAIN_DEF; sd->ag_cnt = -1; - sd->vflip = VFLIP_DEF; + if (sd->sensor != SENSOR_OV7630) + sd->vflip = 0; + else + sd->vflip = 1; sd->infrared = INFRARED_DEF; + sd->quality = QUALITY_DEF; + sd->jpegqual = 80; gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; return 0; @@ -1045,8 +1309,8 @@ static int sd_config(struct gspca_dev *gspca_dev, static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 regGpio[] = { 0x29, 0x74 }; - __u8 regF1; + u8 regGpio[] = { 0x29, 0x74 }; + u8 regF1; /* setup a selector by bridge */ reg_w1(gspca_dev, 0xf1, 0x01); @@ -1064,11 +1328,15 @@ static int sd_init(struct gspca_dev *gspca_dev) case BRIDGE_SN9C105: if (regF1 != 0x11) return -ENODEV; + if (sd->sensor == SENSOR_MI0360) + mi0360_probe(gspca_dev); reg_w(gspca_dev, 0x01, regGpio, 2); break; case BRIDGE_SN9C120: if (regF1 != 0x12) return -ENODEV; + if (sd->sensor == SENSOR_MI0360) + mi0360_probe(gspca_dev); regGpio[1] = 0x70; reg_w(gspca_dev, 0x01, regGpio, 2); break; @@ -1086,20 +1354,14 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static unsigned int setexposure(struct gspca_dev *gspca_dev, - unsigned int expo) +static u32 setexposure(struct gspca_dev *gspca_dev, + u32 expo) { struct sd *sd = (struct sd *) gspca_dev; - static const __u8 doit[] = /* update sensor */ - { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; - static const __u8 sensorgo[] = /* sensor on */ - { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 }; - static const __u8 gainMo[] = - { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d }; switch (sd->sensor) { case SENSOR_HV7131R: { - __u8 Expodoit[] = + u8 Expodoit[] = { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 }; Expodoit[3] = expo >> 16; @@ -1109,8 +1371,12 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev, break; } case SENSOR_MI0360: { - __u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */ + u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */ { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 }; + static const u8 doit[] = /* update sensor */ + { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; + static const u8 sensorgo[] = /* sensor on */ + { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 }; if (expo > 0x0635) expo = 0x0635; @@ -1124,10 +1390,12 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev, break; } case SENSOR_MO4000: { - __u8 expoMof[] = + u8 expoMof[] = { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 }; - __u8 expoMo10[] = + u8 expoMo10[] = { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 }; + static const u8 gainMo[] = + { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d }; if (expo > 0x1fff) expo = 0x1fff; @@ -1139,14 +1407,27 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev, | ((expo & 0x0003) << 4); i2c_w8(gspca_dev, expoMo10); i2c_w8(gspca_dev, gainMo); - PDEBUG(D_CONF, "set exposure %d", + PDEBUG(D_FRAM, "set exposure %d", ((expoMo10[3] & 0x07) << 10) | (expoMof[3] << 2) | ((expoMo10[3] & 0x30) >> 4)); break; } + case SENSOR_MT9V111: { + u8 expo_c1[] = + { 0xb1, 0x5c, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10 }; + + if (expo > 0x0280) + expo = 0x0280; + else if (expo < 0x0040) + expo = 0x0040; + expo_c1[3] = expo >> 8; + expo_c1[4] = expo; + i2c_w8(gspca_dev, expo_c1); + break; + } case SENSOR_OM6802: { - __u8 gainOm[] = + u8 gainOm[] = { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 }; if (expo > 0x03ff) @@ -1156,7 +1437,7 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev, gainOm[3] = expo >> 2; i2c_w8(gspca_dev, gainOm); reg_w1(gspca_dev, 0x96, (expo >> 5) & 0x1f); - PDEBUG(D_CONF, "set exposure %d", gainOm[3]); + PDEBUG(D_FRAM, "set exposure %d", gainOm[3]); break; } } @@ -1167,7 +1448,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; unsigned int expo; - __u8 k2; + u8 k2; k2 = ((int) sd->brightness - 0x8000) >> 10; switch (sd->sensor) { @@ -1184,6 +1465,10 @@ static void setbrightness(struct gspca_dev *gspca_dev) expo = sd->brightness >> 4; sd->exposure = setexposure(gspca_dev, expo); break; + case SENSOR_MT9V111: + expo = sd->brightness >> 8; + sd->exposure = setexposure(gspca_dev, expo); + break; case SENSOR_OM6802: expo = sd->brightness >> 6; sd->exposure = setexposure(gspca_dev, expo); @@ -1191,14 +1476,15 @@ static void setbrightness(struct gspca_dev *gspca_dev) break; } - reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ + if (sd->sensor != SENSOR_MT9V111) + reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ } static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 k2; - __u8 contrast[6]; + u8 k2; + u8 contrast[6]; k2 = sd->contrast * 0x30 / (CONTRAST_MAX + 1) + 0x10; /* 10..40 */ contrast[0] = (k2 + 1) / 2; /* red */ @@ -1214,8 +1500,8 @@ static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i, v; - __u8 reg8a[12]; /* U & V gains */ - static __s16 uv[6] = { /* same as reg84 in signed decimal */ + u8 reg8a[12]; /* U & V gains */ + static s16 uv[6] = { /* same as reg84 in signed decimal */ -24, -38, 64, /* UR UG UB */ 62, -51, -9 /* VR VG VB */ }; @@ -1236,22 +1522,75 @@ static void setredblue(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x06, sd->blue); } +static void setgamma(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + u8 gamma[17]; + const u8 *gamma_base; + static const u8 delta[17] = { + 0x00, 0x14, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a, + 0x18, 0x13, 0x10, 0x0e, 0x08, 0x07, 0x04, 0x02, 0x00 + }; + + switch (sd->sensor) { + case SENSOR_HV7131R: + case SENSOR_MT9V111: + gamma_base = gamma_spec_1; + break; + case SENSOR_SP80708: + gamma_base = gamma_spec_2; + break; + default: + gamma_base = gamma_def; + break; + } + + for (i = 0; i < sizeof gamma; i++) + gamma[i] = gamma_base[i] + + delta[i] * (sd->gamma - GAMMA_DEF) / 32; + reg_w(gspca_dev, 0x20, gamma, sizeof gamma); +} + static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX)) return; + switch (sd->sensor) { + case SENSOR_OV7630: + case SENSOR_OV7648: { + u8 comb; + + if (sd->sensor == SENSOR_OV7630) + comb = 0xc0; + else + comb = 0xa0; + if (sd->autogain) + comb |= 0x02; + i2c_w1(&sd->gspca_dev, 0x13, comb); + return; + } + } if (sd->autogain) sd->ag_cnt = AG_CNT_START; else sd->ag_cnt = -1; } +/* ov7630/ov7648 only */ static void setvflip(struct sd *sd) { - i2c_w1(&sd->gspca_dev, 0x75, /* COMN */ - sd->vflip ? 0x82 : 0x02); + u8 comn; + + if (sd->sensor == SENSOR_OV7630) + comn = 0x02; + else + comn = 0x06; + if (sd->vflip) + comn |= 0x80; + i2c_w1(&sd->gspca_dev, 0x75, comn); } static void setinfrared(struct sd *sd) @@ -1262,20 +1601,63 @@ static void setinfrared(struct sd *sd) sd->infrared ? 0x66 : 0x64); } +static void setjpegqual(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, sc; + + if (sd->jpegqual < 50) + sc = 5000 / sd->jpegqual; + else + sc = 200 - sd->jpegqual * 2; +#if USB_BUF_SZ < 64 +#error "No room enough in usb_buf for quantization table" +#endif + for (i = 0; i < 64; i++) + gspca_dev->usb_buf[i] = + (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x0100, 0, + gspca_dev->usb_buf, 64, + 500); + for (i = 0; i < 64; i++) + gspca_dev->usb_buf[i] = + (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x0140, 0, + gspca_dev->usb_buf, 64, + 500); + + sd->reg18 ^= 0x40; + reg_w1(gspca_dev, 0x18, sd->reg18); +} + /* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; - __u8 reg1, reg17, reg18; - const __u8 *sn9c1xx; + u8 reg1, reg17; + const u8 *sn9c1xx; int mode; - static const __u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; - static const __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; - static const __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ - static const __u8 CE_ov76xx[] = + static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; + static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; + static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ + static const u8 CE_ov76xx[] = { 0x32, 0xdd, 0x32, 0xdd }; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + sn9c1xx = sn_tb[(int) sd->sensor]; configure_gpio(gspca_dev, sn9c1xx); @@ -1292,6 +1674,9 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0xc9, 0x3c); reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); switch (sd->sensor) { + case SENSOR_MT9V111: + reg17 = 0xe0; + break; case SENSOR_OV7630: reg17 = 0xe2; break; @@ -1315,14 +1700,24 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */ reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */ reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]); - reg_w(gspca_dev, 0x20, gamma_def, sizeof gamma_def); + + setgamma(gspca_dev); + for (i = 0; i < 8; i++) reg_w(gspca_dev, 0x84, reg84, sizeof reg84); switch (sd->sensor) { + case SENSOR_MT9V111: + reg_w1(gspca_dev, 0x9a, 0x07); + reg_w1(gspca_dev, 0x99, 0x59); + break; case SENSOR_OV7648: reg_w1(gspca_dev, 0x9a, 0x0a); reg_w1(gspca_dev, 0x99, 0x60); break; + case SENSOR_SP80708: + reg_w1(gspca_dev, 0x9a, 0x05); + reg_w1(gspca_dev, 0x99, 0x59); + break; case SENSOR_OV7660: if (sd->bridge == BRIDGE_SN9C120) { reg_w1(gspca_dev, 0x9a, 0x05); @@ -1358,6 +1753,15 @@ static int sd_start(struct gspca_dev *gspca_dev) /* reg1 = 0x06; * 640 clk 24Mz (done) */ } break; + case SENSOR_MT9V111: + mt9v111_InitSensor(gspca_dev); + if (mode) { + reg1 = 0x04; /* 320 clk 48Mhz */ + } else { +/* reg1 = 0x06; * 640 clk 24Mz (done) */ + reg17 = 0xc2; + } + break; case SENSOR_OM6802: om6802_InitSensor(gspca_dev); reg17 = 0x64; /* 640 MCKSIZE */ @@ -1373,8 +1777,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg17 = 0x21; /* reg1 = 0x42; * 42 - 46? */ break; - default: -/* case SENSOR_OV7660: */ + case SENSOR_OV7660: ov7660_InitSensor(gspca_dev); if (sd->bridge == BRIDGE_SN9C120) { if (mode) { /* 320x240 - 160x120 */ @@ -1387,6 +1790,16 @@ static int sd_start(struct gspca_dev *gspca_dev) * inverse power down */ } break; + default: +/* case SENSOR_SP80708: */ + sp80708_InitSensor(gspca_dev); + if (mode) { +/*?? reg1 = 0x04; * 320 clk 48Mhz */ + } else { + reg1 = 0x46; /* 640 clk 48Mz */ + reg17 = 0xa2; + } + break; } reg_w(gspca_dev, 0xc0, C0, 6); reg_w(gspca_dev, 0xca, CA, 4); @@ -1403,20 +1816,13 @@ static int sd_start(struct gspca_dev *gspca_dev) } /* here change size mode 0 -> VGA; 1 -> CIF */ - reg18 = sn9c1xx[0x18] | (mode << 4); - reg_w1(gspca_dev, 0x18, reg18 | 0x40); - - reg_w(gspca_dev, 0x100, qtable4, 0x40); - reg_w(gspca_dev, 0x140, qtable4 + 0x40, 0x40); - - reg_w1(gspca_dev, 0x18, reg18); + sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40; + reg_w1(gspca_dev, 0x18, sd->reg18); + setjpegqual(gspca_dev); reg_w1(gspca_dev, 0x17, reg17); reg_w1(gspca_dev, 0x01, reg1); switch (sd->sensor) { - case SENSOR_MI0360: - setinfrared(sd); - break; case SENSOR_OV7630: setvflip(sd); break; @@ -1430,14 +1836,14 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - static const __u8 stophv7131[] = + static const u8 stophv7131[] = { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 }; - static const __u8 stopmi0360[] = + static const u8 stopmi0360[] = { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 }; - static const __u8 stopov7648[] = + static const u8 stopov7648[] = { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 }; - __u8 data; - const __u8 *sn9c1xx; + u8 data; + const u8 *sn9c1xx; data = 0x0b; switch (sd->sensor) { @@ -1452,6 +1858,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) case SENSOR_OV7648: i2c_w8(gspca_dev, stopov7648); /* fall thru */ + case SENSOR_MT9V111: case SENSOR_OV7630: data = 0x29; break; @@ -1468,13 +1875,20 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0xf1, 0x00); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int delta; int expotimes; - __u8 luma_mean = 130; - __u8 luma_delta = 20; + u8 luma_mean = 130; + u8 luma_delta = 20; /* Thanks S., without your advice, autobright should not work :) */ if (sd->ag_cnt < 0) @@ -1499,6 +1913,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) default: /* case SENSOR_MO4000: */ /* case SENSOR_MI0360: */ +/* case SENSOR_MT9V111: */ /* case SENSOR_OM6802: */ expotimes = sd->exposure; expotimes += (luma_mean - delta) >> 6; @@ -1516,7 +1931,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) /* This function is run at interrupt level. */ 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 packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -1550,7 +1965,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (gspca_dev->last_packet_type == LAST_PACKET) { /* put the JPEG 422 header */ - jpeg_put_header(gspca_dev, frame, sd->qindex, 0x21); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); } gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } @@ -1645,6 +2061,24 @@ static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gamma = val; + if (gspca_dev->streaming) + setgamma(gspca_dev); + return 0; +} + +static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gamma; + return 0; +} + static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1699,6 +2133,34 @@ static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + if (gspca_dev->streaming) + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -1708,8 +2170,11 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ @@ -1724,9 +2189,7 @@ static const __devinitdata struct usb_device_id device_table[] = { #endif {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)}, -#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)}, -#endif {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)}, {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)}, {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)}, @@ -1764,10 +2227,10 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, +#endif {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, /* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */ -#endif - {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, MI0360, 0x5d)}, + {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -1794,8 +2257,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; info("registered"); return 0; } diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index 942f04c..6f38fa6 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -38,8 +38,11 @@ struct sd { unsigned char brightness; unsigned char contrast; unsigned char colors; + u8 quality; +#define QUALITY_MIN 70 +#define QUALITY_MAX 95 +#define QUALITY_DEF 85 - char qindex; char subtype; #define AgfaCl20 0 #define AiptekPocketDV 1 @@ -56,6 +59,8 @@ struct sd { #define Optimedia 12 #define PalmPixDC85 13 #define ToptroIndus 14 + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -629,7 +634,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->epaddr = 0x01; sd->subtype = id->driver_info; if (sd->subtype != LogitechClickSmart310) { cam->cam_mode = vga_mode; @@ -638,10 +642,10 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); } - sd->qindex = 5; sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; + sd->quality = QUALITY_DEF; return 0; } @@ -667,6 +671,12 @@ static int sd_start(struct gspca_dev *gspca_dev) __u8 Data; __u8 xmult, ymult; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + if (sd->subtype == LogitechClickSmart310) { xmult = 0x16; ymult = 0x12; @@ -713,7 +723,8 @@ static int sd_start(struct gspca_dev *gspca_dev) write_vector(gspca_dev, spca500_visual_defaults); spca500_setmode(gspca_dev, xmult, ymult); /* enable drop packet */ - reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001); + if (err < 0) PDEBUG(D_ERR, "failed to enable drop packet"); reg_w(gspca_dev, 0x00, 0x8880, 3); err = spca50x_setup_qtable(gspca_dev, @@ -881,6 +892,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) gspca_dev->usb_buf[0]); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -901,7 +919,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, ffd9, 2); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, sd->qindex, 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); data += SPCA500_OFFSET_DATA; len -= SPCA500_OFFSET_DATA; @@ -937,16 +956,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) (__u8) (sd->brightness - 128)); } -static void getbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret; - - ret = reg_r_12(gspca_dev, 0x00, 0x8167, 1); - if (ret >= 0) - sd->brightness = ret + 128; -} - static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -954,16 +963,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x00, 0x8168, sd->contrast); } -static void getcontrast(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret; - - ret = reg_r_12(gspca_dev, 0x0, 0x8168, 1); - if (ret >= 0) - sd->contrast = ret; -} - static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -971,16 +970,6 @@ static void setcolors(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x00, 0x8169, sd->colors); } -static void getcolors(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int ret; - - ret = reg_r_12(gspca_dev, 0x0, 0x8169, 1); - if (ret >= 0) - sd->colors = ret; -} - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -995,7 +984,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -1014,7 +1002,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcontrast(gspca_dev); *val = sd->contrast; return 0; } @@ -1033,11 +1020,38 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcolors(gspca_dev); *val = sd->colors; return 0; } +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + if (gspca_dev->streaming) + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + /* sub-driver description */ static struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -1047,7 +1061,10 @@ static struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ @@ -1093,8 +1110,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index 82e3e3e..d48b27c 100644 --- a/drivers/media/video/gspca/spca501.c +++ b/drivers/media/video/gspca/spca501.c @@ -1883,10 +1883,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness); } -static void getbrightness(struct gspca_dev *gspca_dev) -{ -} - static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1897,10 +1893,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) sd->contrast & 0xff); } -static void getcontrast(struct gspca_dev *gspca_dev) -{ -} - static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1908,10 +1900,6 @@ static void setcolors(struct gspca_dev *gspca_dev) reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, sd->colors); } -static void getcolors(struct gspca_dev *gspca_dev) -{ -} - static void setblue_balance(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1934,7 +1922,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->epaddr = 0x01; cam->cam_mode = vga_mode; cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; sd->subtype = id->driver_info; @@ -2084,7 +2071,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -2103,7 +2089,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcontrast(gspca_dev); *val = sd->contrast; return 0; } @@ -2122,7 +2107,6 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcolors(gspca_dev); *val = sd->colors; return 0; } @@ -2211,8 +2195,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index 2a33a29..2acec58 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/drivers/media/video/gspca/spca505.c @@ -31,9 +31,9 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; + u8 brightness; - char subtype; + u8 subtype; #define IntelPCCameraPro 0 #define Nxultra 1 }; @@ -43,7 +43,6 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { -#define SD_BRIGHTNESS 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -52,7 +51,8 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 255, .step = 1, - .default_value = 127, +#define BRIGHTNESS_DEF 127 + .default_value = BRIGHTNESS_DEF, }, .set = sd_setbrightness, .get = sd_getbrightness, @@ -64,12 +64,12 @@ static const struct v4l2_pix_format vga_mode[] = { .bytesperline = 160, .sizeimage = 160 * 120 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 5}, + .priv = 4}, {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144 * 3 / 2, .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 4}, + .priv = 3}, {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 2, @@ -93,6 +93,7 @@ static const struct v4l2_pix_format vga_mode[] = { #define SPCA50X_USB_CTRL 0x00 /* spca505 */ #define SPCA50X_CUSB_ENABLE 0x01 /* spca505 */ + #define SPCA50X_REG_GLOBAL 0x03 /* spca505 */ #define SPCA50X_GMISC0_IDSEL 0x01 /* Global control device ID select spca505 */ #define SPCA50X_GLOBAL_MISC0 0x00 /* Global control miscellaneous 0 spca505 */ @@ -101,230 +102,230 @@ static const struct v4l2_pix_format vga_mode[] = { #define SPCA50X_GLOBAL_MISC3 0x03 /* 505 */ #define SPCA50X_GMISC3_SAA7113RST 0x20 /* Not sure about this one spca505 */ +/* Image format and compression control */ +#define SPCA50X_REG_COMPRESS 0x04 + /* * Data to initialize a SPCA505. Common to the CCD and external modes */ -static const __u16 spca505_init_data[][3] = { - /* line bmRequest,value,index */ - /* 1819 */ +static const u8 spca505_init_data[][3] = { + /* bmRequest,value,index */ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC3_SAA7113RST, SPCA50X_GLOBAL_MISC3}, /* Sensor reset */ - /* 1822 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3}, - /* 1825 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1}, + {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3}, + {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1}, /* Block USB reset */ - /* 1828 */ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL, - SPCA50X_GLOBAL_MISC0}, + {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL, SPCA50X_GLOBAL_MISC0}, - /* 1831 */ {0x5, 0x01, 0x10}, + {0x05, 0x01, 0x10}, /* Maybe power down some stuff */ - /* 1834 */ {0x5, 0x0f, 0x11}, + {0x05, 0x0f, 0x11}, /* Setup internal CCD ? */ - /* 1837 */ {0x6, 0x10, 0x08}, - /* 1840 */ {0x6, 0x00, 0x09}, - /* 1843 */ {0x6, 0x00, 0x0a}, - /* 1846 */ {0x6, 0x00, 0x0b}, - /* 1849 */ {0x6, 0x10, 0x0c}, - /* 1852 */ {0x6, 0x00, 0x0d}, - /* 1855 */ {0x6, 0x00, 0x0e}, - /* 1858 */ {0x6, 0x00, 0x0f}, - /* 1861 */ {0x6, 0x10, 0x10}, - /* 1864 */ {0x6, 0x02, 0x11}, - /* 1867 */ {0x6, 0x00, 0x12}, - /* 1870 */ {0x6, 0x04, 0x13}, - /* 1873 */ {0x6, 0x02, 0x14}, - /* 1876 */ {0x6, 0x8a, 0x51}, - /* 1879 */ {0x6, 0x40, 0x52}, - /* 1882 */ {0x6, 0xb6, 0x53}, - /* 1885 */ {0x6, 0x3d, 0x54}, + {0x06, 0x10, 0x08}, + {0x06, 0x00, 0x09}, + {0x06, 0x00, 0x0a}, + {0x06, 0x00, 0x0b}, + {0x06, 0x10, 0x0c}, + {0x06, 0x00, 0x0d}, + {0x06, 0x00, 0x0e}, + {0x06, 0x00, 0x0f}, + {0x06, 0x10, 0x10}, + {0x06, 0x02, 0x11}, + {0x06, 0x00, 0x12}, + {0x06, 0x04, 0x13}, + {0x06, 0x02, 0x14}, + {0x06, 0x8a, 0x51}, + {0x06, 0x40, 0x52}, + {0x06, 0xb6, 0x53}, + {0x06, 0x3d, 0x54}, {} }; /* * Data to initialize the camera using the internal CCD */ -static const __u16 spca505_open_data_ccd[][3] = { - /* line bmRequest,value,index */ +static const u8 spca505_open_data_ccd[][3] = { + /* bmRequest,value,index */ /* Internal CCD data set */ - /* 1891 */ {0x3, 0x04, 0x01}, + {0x03, 0x04, 0x01}, /* This could be a reset */ - /* 1894 */ {0x3, 0x00, 0x01}, + {0x03, 0x00, 0x01}, /* Setup compression and image registers. 0x6 and 0x7 seem to be related to H&V hold, and are resolution mode specific */ - /* 1897 */ {0x4, 0x10, 0x01}, + {0x04, 0x10, 0x01}, /* DIFF(0x50), was (0x10) */ - /* 1900 */ {0x4, 0x00, 0x04}, - /* 1903 */ {0x4, 0x00, 0x05}, - /* 1906 */ {0x4, 0x20, 0x06}, - /* 1909 */ {0x4, 0x20, 0x07}, + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x20, 0x06}, + {0x04, 0x20, 0x07}, - /* 1912 */ {0x8, 0x0a, 0x00}, + {0x08, 0x0a, 0x00}, /* DIFF (0x4a), was (0xa) */ - /* 1915 */ {0x5, 0x00, 0x10}, - /* 1918 */ {0x5, 0x00, 0x11}, - /* 1921 */ {0x5, 0x00, 0x00}, + {0x05, 0x00, 0x10}, + {0x05, 0x00, 0x11}, + {0x05, 0x00, 0x00}, /* DIFF not written */ - /* 1924 */ {0x5, 0x00, 0x01}, + {0x05, 0x00, 0x01}, /* DIFF not written */ - /* 1927 */ {0x5, 0x00, 0x02}, + {0x05, 0x00, 0x02}, /* DIFF not written */ - /* 1930 */ {0x5, 0x00, 0x03}, + {0x05, 0x00, 0x03}, /* DIFF not written */ - /* 1933 */ {0x5, 0x00, 0x04}, + {0x05, 0x00, 0x04}, /* DIFF not written */ - /* 1936 */ {0x5, 0x80, 0x05}, + {0x05, 0x80, 0x05}, /* DIFF not written */ - /* 1939 */ {0x5, 0xe0, 0x06}, + {0x05, 0xe0, 0x06}, /* DIFF not written */ - /* 1942 */ {0x5, 0x20, 0x07}, + {0x05, 0x20, 0x07}, /* DIFF not written */ - /* 1945 */ {0x5, 0xa0, 0x08}, + {0x05, 0xa0, 0x08}, /* DIFF not written */ - /* 1948 */ {0x5, 0x0, 0x12}, + {0x05, 0x0, 0x12}, /* DIFF not written */ - /* 1951 */ {0x5, 0x02, 0x0f}, + {0x05, 0x02, 0x0f}, /* DIFF not written */ - /* 1954 */ {0x5, 0x10, 0x46}, + {0x05, 0x10, 0x46}, /* DIFF not written */ - /* 1957 */ {0x5, 0x8, 0x4a}, + {0x05, 0x8, 0x4a}, /* DIFF not written */ - /* 1960 */ {0x3, 0x08, 0x03}, + {0x03, 0x08, 0x03}, /* DIFF (0x3,0x28,0x3) */ - /* 1963 */ {0x3, 0x08, 0x01}, - /* 1966 */ {0x3, 0x0c, 0x03}, + {0x03, 0x08, 0x01}, + {0x03, 0x0c, 0x03}, /* DIFF not written */ - /* 1969 */ {0x3, 0x21, 0x00}, + {0x03, 0x21, 0x00}, /* DIFF (0x39) */ /* Extra block copied from init to hopefully ensure CCD is in a sane state */ - /* 1837 */ {0x6, 0x10, 0x08}, - /* 1840 */ {0x6, 0x00, 0x09}, - /* 1843 */ {0x6, 0x00, 0x0a}, - /* 1846 */ {0x6, 0x00, 0x0b}, - /* 1849 */ {0x6, 0x10, 0x0c}, - /* 1852 */ {0x6, 0x00, 0x0d}, - /* 1855 */ {0x6, 0x00, 0x0e}, - /* 1858 */ {0x6, 0x00, 0x0f}, - /* 1861 */ {0x6, 0x10, 0x10}, - /* 1864 */ {0x6, 0x02, 0x11}, - /* 1867 */ {0x6, 0x00, 0x12}, - /* 1870 */ {0x6, 0x04, 0x13}, - /* 1873 */ {0x6, 0x02, 0x14}, - /* 1876 */ {0x6, 0x8a, 0x51}, - /* 1879 */ {0x6, 0x40, 0x52}, - /* 1882 */ {0x6, 0xb6, 0x53}, - /* 1885 */ {0x6, 0x3d, 0x54}, + {0x06, 0x10, 0x08}, + {0x06, 0x00, 0x09}, + {0x06, 0x00, 0x0a}, + {0x06, 0x00, 0x0b}, + {0x06, 0x10, 0x0c}, + {0x06, 0x00, 0x0d}, + {0x06, 0x00, 0x0e}, + {0x06, 0x00, 0x0f}, + {0x06, 0x10, 0x10}, + {0x06, 0x02, 0x11}, + {0x06, 0x00, 0x12}, + {0x06, 0x04, 0x13}, + {0x06, 0x02, 0x14}, + {0x06, 0x8a, 0x51}, + {0x06, 0x40, 0x52}, + {0x06, 0xb6, 0x53}, + {0x06, 0x3d, 0x54}, /* End of extra block */ - /* 1972 */ {0x6, 0x3f, 0x1}, + {0x06, 0x3f, 0x1}, /* Block skipped */ - /* 1975 */ {0x6, 0x10, 0x02}, - /* 1978 */ {0x6, 0x64, 0x07}, - /* 1981 */ {0x6, 0x10, 0x08}, - /* 1984 */ {0x6, 0x00, 0x09}, - /* 1987 */ {0x6, 0x00, 0x0a}, - /* 1990 */ {0x6, 0x00, 0x0b}, - /* 1993 */ {0x6, 0x10, 0x0c}, - /* 1996 */ {0x6, 0x00, 0x0d}, - /* 1999 */ {0x6, 0x00, 0x0e}, - /* 2002 */ {0x6, 0x00, 0x0f}, - /* 2005 */ {0x6, 0x10, 0x10}, - /* 2008 */ {0x6, 0x02, 0x11}, - /* 2011 */ {0x6, 0x00, 0x12}, - /* 2014 */ {0x6, 0x04, 0x13}, - /* 2017 */ {0x6, 0x02, 0x14}, - /* 2020 */ {0x6, 0x8a, 0x51}, - /* 2023 */ {0x6, 0x40, 0x52}, - /* 2026 */ {0x6, 0xb6, 0x53}, - /* 2029 */ {0x6, 0x3d, 0x54}, - /* 2032 */ {0x6, 0x60, 0x57}, - /* 2035 */ {0x6, 0x20, 0x58}, - /* 2038 */ {0x6, 0x15, 0x59}, - /* 2041 */ {0x6, 0x05, 0x5a}, - - /* 2044 */ {0x5, 0x01, 0xc0}, - /* 2047 */ {0x5, 0x10, 0xcb}, - /* 2050 */ {0x5, 0x80, 0xc1}, + {0x06, 0x10, 0x02}, + {0x06, 0x64, 0x07}, + {0x06, 0x10, 0x08}, + {0x06, 0x00, 0x09}, + {0x06, 0x00, 0x0a}, + {0x06, 0x00, 0x0b}, + {0x06, 0x10, 0x0c}, + {0x06, 0x00, 0x0d}, + {0x06, 0x00, 0x0e}, + {0x06, 0x00, 0x0f}, + {0x06, 0x10, 0x10}, + {0x06, 0x02, 0x11}, + {0x06, 0x00, 0x12}, + {0x06, 0x04, 0x13}, + {0x06, 0x02, 0x14}, + {0x06, 0x8a, 0x51}, + {0x06, 0x40, 0x52}, + {0x06, 0xb6, 0x53}, + {0x06, 0x3d, 0x54}, + {0x06, 0x60, 0x57}, + {0x06, 0x20, 0x58}, + {0x06, 0x15, 0x59}, + {0x06, 0x05, 0x5a}, + + {0x05, 0x01, 0xc0}, + {0x05, 0x10, 0xcb}, + {0x05, 0x80, 0xc1}, /* */ - /* 2053 */ {0x5, 0x0, 0xc2}, + {0x05, 0x0, 0xc2}, /* 4 was 0 */ - /* 2056 */ {0x5, 0x00, 0xca}, - /* 2059 */ {0x5, 0x80, 0xc1}, + {0x05, 0x00, 0xca}, + {0x05, 0x80, 0xc1}, /* */ - /* 2062 */ {0x5, 0x04, 0xc2}, - /* 2065 */ {0x5, 0x00, 0xca}, - /* 2068 */ {0x5, 0x0, 0xc1}, + {0x05, 0x04, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x0, 0xc1}, /* */ - /* 2071 */ {0x5, 0x00, 0xc2}, - /* 2074 */ {0x5, 0x00, 0xca}, - /* 2077 */ {0x5, 0x40, 0xc1}, + {0x05, 0x00, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x40, 0xc1}, /* */ - /* 2080 */ {0x5, 0x17, 0xc2}, - /* 2083 */ {0x5, 0x00, 0xca}, - /* 2086 */ {0x5, 0x80, 0xc1}, + {0x05, 0x17, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x80, 0xc1}, /* */ - /* 2089 */ {0x5, 0x06, 0xc2}, - /* 2092 */ {0x5, 0x00, 0xca}, - /* 2095 */ {0x5, 0x80, 0xc1}, + {0x05, 0x06, 0xc2}, + {0x05, 0x00, 0xca}, + {0x05, 0x80, 0xc1}, /* */ - /* 2098 */ {0x5, 0x04, 0xc2}, - /* 2101 */ {0x5, 0x00, 0xca}, + {0x05, 0x04, 0xc2}, + {0x05, 0x00, 0xca}, - /* 2104 */ {0x3, 0x4c, 0x3}, - /* 2107 */ {0x3, 0x18, 0x1}, + {0x03, 0x4c, 0x3}, + {0x03, 0x18, 0x1}, - /* 2110 */ {0x6, 0x70, 0x51}, - /* 2113 */ {0x6, 0xbe, 0x53}, - /* 2116 */ {0x6, 0x71, 0x57}, - /* 2119 */ {0x6, 0x20, 0x58}, - /* 2122 */ {0x6, 0x05, 0x59}, - /* 2125 */ {0x6, 0x15, 0x5a}, + {0x06, 0x70, 0x51}, + {0x06, 0xbe, 0x53}, + {0x06, 0x71, 0x57}, + {0x06, 0x20, 0x58}, + {0x06, 0x05, 0x59}, + {0x06, 0x15, 0x5a}, - /* 2128 */ {0x4, 0x00, 0x08}, + {0x04, 0x00, 0x08}, /* Compress = OFF (0x1 to turn on) */ - /* 2131 */ {0x4, 0x12, 0x09}, - /* 2134 */ {0x4, 0x21, 0x0a}, - /* 2137 */ {0x4, 0x10, 0x0b}, - /* 2140 */ {0x4, 0x21, 0x0c}, - /* 2143 */ {0x4, 0x05, 0x00}, + {0x04, 0x12, 0x09}, + {0x04, 0x21, 0x0a}, + {0x04, 0x10, 0x0b}, + {0x04, 0x21, 0x0c}, + {0x04, 0x05, 0x00}, /* was 5 (Image Type ? ) */ - /* 2146 */ {0x4, 0x00, 0x01}, - - /* 2149 */ {0x6, 0x3f, 0x01}, - - /* 2152 */ {0x4, 0x00, 0x04}, - /* 2155 */ {0x4, 0x00, 0x05}, - /* 2158 */ {0x4, 0x40, 0x06}, - /* 2161 */ {0x4, 0x40, 0x07}, - - /* 2164 */ {0x6, 0x1c, 0x17}, - /* 2167 */ {0x6, 0xe2, 0x19}, - /* 2170 */ {0x6, 0x1c, 0x1b}, - /* 2173 */ {0x6, 0xe2, 0x1d}, - /* 2176 */ {0x6, 0xaa, 0x1f}, - /* 2179 */ {0x6, 0x70, 0x20}, - - /* 2182 */ {0x5, 0x01, 0x10}, - /* 2185 */ {0x5, 0x00, 0x11}, - /* 2188 */ {0x5, 0x01, 0x00}, - /* 2191 */ {0x5, 0x05, 0x01}, - /* 2194 */ {0x5, 0x00, 0xc1}, + {0x04, 0x00, 0x01}, + + {0x06, 0x3f, 0x01}, + + {0x04, 0x00, 0x04}, + {0x04, 0x00, 0x05}, + {0x04, 0x40, 0x06}, + {0x04, 0x40, 0x07}, + + {0x06, 0x1c, 0x17}, + {0x06, 0xe2, 0x19}, + {0x06, 0x1c, 0x1b}, + {0x06, 0xe2, 0x1d}, + {0x06, 0xaa, 0x1f}, + {0x06, 0x70, 0x20}, + + {0x05, 0x01, 0x10}, + {0x05, 0x00, 0x11}, + {0x05, 0x01, 0x00}, + {0x05, 0x05, 0x01}, + {0x05, 0x00, 0xc1}, /* */ - /* 2197 */ {0x5, 0x00, 0xc2}, - /* 2200 */ {0x5, 0x00, 0xca}, + {0x05, 0x00, 0xc2}, + {0x05, 0x00, 0xca}, - /* 2203 */ {0x6, 0x70, 0x51}, - /* 2206 */ {0x6, 0xbe, 0x53}, + {0x06, 0x70, 0x51}, + {0x06, 0xbe, 0x53}, {} }; /* - Made by Tomasz Zablocki (skalamandra@poczta.onet.pl) + * Made by Tomasz Zablocki (skalamandra@poczta.onet.pl) * SPCA505b chip based cameras initialization data - * */ /* jfm */ #define initial_brightness 0x7f /* 0x0(white)-0xff(black) */ @@ -332,7 +333,7 @@ static const __u16 spca505_open_data_ccd[][3] = { /* * Data to initialize a SPCA505. Common to the CCD and external modes */ -static const __u16 spca505b_init_data[][3] = { +static const u8 spca505b_init_data[][3] = { /* start */ {0x02, 0x00, 0x00}, /* init */ {0x02, 0x00, 0x01}, @@ -396,7 +397,7 @@ static const __u16 spca505b_init_data[][3] = { /* * Data to initialize the camera using the internal CCD */ -static const __u16 spca505b_open_data_ccd[][3] = { +static const u8 spca505b_open_data_ccd[][3] = { /* {0x02,0x00,0x00}, */ {0x03, 0x04, 0x01}, /* rst */ @@ -426,7 +427,7 @@ static const __u16 spca505b_open_data_ccd[][3] = { {0x05, 0x00, 0x12}, {0x05, 0x6f, 0x00}, {0x05, initial_brightness >> 6, 0x00}, - {0x05, initial_brightness << 2, 0x01}, + {0x05, (initial_brightness << 2) & 0xff, 0x01}, {0x05, 0x00, 0x02}, {0x05, 0x01, 0x03}, {0x05, 0x00, 0x04}, @@ -436,7 +437,7 @@ static const __u16 spca505b_open_data_ccd[][3] = { {0x05, 0xa0, 0x08}, {0x05, 0x00, 0x12}, {0x05, 0x02, 0x0f}, - {0x05, 128, 0x14}, /* max exposure off (0=on) */ + {0x05, 0x80, 0x14}, /* max exposure off (0=on) */ {0x05, 0x01, 0xb0}, {0x05, 0x01, 0xbf}, {0x03, 0x02, 0x06}, @@ -560,26 +561,26 @@ static const __u16 spca505b_open_data_ccd[][3] = { {0x06, 0x32, 0x20}, {0x05, initial_brightness >> 6, 0x00}, - {0x05, initial_brightness << 2, 0x01}, + {0x05, (initial_brightness << 2) & 0xff, 0x01}, {0x05, 0x06, 0xc1}, {0x05, 0x58, 0xc2}, - {0x05, 0x0, 0xca}, - {0x05, 0x0, 0x11}, + {0x05, 0x00, 0xca}, + {0x05, 0x00, 0x11}, {} }; static int reg_write(struct usb_device *dev, - __u16 reg, __u16 index, __u16 value) + u16 req, u16 index, u16 value) { int ret; ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - reg, + req, USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, index, NULL, 0, 500); - PDEBUG(D_PACK, "reg write: 0x%02x,0x%02x:0x%02x, 0x%x", - reg, index, value, ret); + PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d", + req, index, value, ret); if (ret < 0) PDEBUG(D_ERR, "reg write: error %d", ret); return ret; @@ -587,42 +588,34 @@ static int reg_write(struct usb_device *dev, /* returns: negative is error, pos or zero is data */ static int reg_read(struct gspca_dev *gspca_dev, - __u16 reg, /* bRequest */ - __u16 index, /* wIndex */ - __u16 length) /* wLength (1 or 2 only) */ + u16 req, /* bRequest */ + u16 index) /* wIndex */ { int ret; - gspca_dev->usb_buf[1] = 0; ret = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), - reg, + req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - (__u16) 0, /* value */ - (__u16) index, - gspca_dev->usb_buf, length, + 0, /* value */ + index, + gspca_dev->usb_buf, 2, 500); /* timeout */ - if (ret < 0) { - PDEBUG(D_ERR, "reg_read err %d", ret); - return -1; - } + if (ret < 0) + return ret; return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0]; } static int write_vector(struct gspca_dev *gspca_dev, - const __u16 data[][3]) + const u8 data[][3]) { struct usb_device *dev = gspca_dev->dev; int ret, i = 0; - while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) { + while (data[i][0] != 0) { ret = reg_write(dev, data[i][0], data[i][2], data[i][1]); - if (ret < 0) { - PDEBUG(D_ERR, - "Register write failed for 0x%x,0x%x,0x%x", - data[i][0], data[i][1], data[i][2]); + if (ret < 0) return ret; - } i++; } return 0; @@ -636,14 +629,13 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->epaddr = 0x01; cam->cam_mode = vga_mode; sd->subtype = id->driver_info; if (sd->subtype != IntelPCCameraPro) - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; + cam->nmodes = ARRAY_SIZE(vga_mode); else /* no 640x480 for IntelPCCameraPro */ - cam->nmodes = sizeof vga_mode / sizeof vga_mode[0] - 1; - sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; + cam->nmodes = ARRAY_SIZE(vga_mode) - 1; + sd->brightness = BRIGHTNESS_DEF; if (sd->subtype == Nxultra) { if (write_vector(gspca_dev, spca505b_init_data)) @@ -658,81 +650,71 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { + return 0; +} + +static void setbrightness(struct gspca_dev *gspca_dev) +{ struct sd *sd = (struct sd *) gspca_dev; - int ret; + u8 brightness = sd->brightness; + + reg_write(gspca_dev->dev, 0x05, 0x00, (255 - brightness) >> 6); + reg_write(gspca_dev->dev, 0x05, 0x01, (255 - brightness) << 2); +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct usb_device *dev = gspca_dev->dev; + int ret, mode; + static u8 mode_tb[][3] = { + /* r00 r06 r07 */ + {0x00, 0x10, 0x10}, /* 640x480 */ + {0x01, 0x1a, 0x1a}, /* 352x288 */ + {0x02, 0x1c, 0x1d}, /* 320x240 */ + {0x04, 0x34, 0x34}, /* 176x144 */ + {0x05, 0x40, 0x40} /* 160x120 */ + }; - PDEBUG(D_STREAM, "Initializing SPCA505"); if (sd->subtype == Nxultra) write_vector(gspca_dev, spca505b_open_data_ccd); else write_vector(gspca_dev, spca505_open_data_ccd); - ret = reg_read(gspca_dev, 6, 0x16, 2); + ret = reg_read(gspca_dev, 0x06, 0x16); if (ret < 0) { - PDEBUG(D_ERR|D_STREAM, - "register read failed for after vector read err = %d", + PDEBUG(D_ERR|D_CONF, + "register read failed err: %d", ret); - return -EIO; + return ret; } - PDEBUG(D_STREAM, - "After vector read returns : 0x%x should be 0x0101", - ret & 0xffff); - - ret = reg_write(gspca_dev->dev, 6, 0x16, 0x0a); - if (ret < 0) { - PDEBUG(D_ERR, "register write failed for (6,0xa,0x16) err=%d", - ret); - return -EIO; + if (ret != 0x0101) { + PDEBUG(D_ERR|D_CONF, + "After vector read returns 0x%04x should be 0x0101", + ret); } - reg_write(gspca_dev->dev, 5, 0xc2, 18); - return 0; -} -static int sd_start(struct gspca_dev *gspca_dev) -{ - struct usb_device *dev = gspca_dev->dev; - int ret; + ret = reg_write(gspca_dev->dev, 0x06, 0x16, 0x0a); + if (ret < 0) + return ret; + reg_write(gspca_dev->dev, 0x05, 0xc2, 0x12); /* necessary because without it we can see stream * only once after loading module */ /* stopping usb registers Tomasz change */ - reg_write(dev, 0x02, 0x0, 0x0); - switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - case 0: - reg_write(dev, 0x04, 0x00, 0x00); - reg_write(dev, 0x04, 0x06, 0x10); - reg_write(dev, 0x04, 0x07, 0x10); - break; - case 1: - reg_write(dev, 0x04, 0x00, 0x01); - reg_write(dev, 0x04, 0x06, 0x1a); - reg_write(dev, 0x04, 0x07, 0x1a); - break; - case 2: - reg_write(dev, 0x04, 0x00, 0x02); - reg_write(dev, 0x04, 0x06, 0x1c); - reg_write(dev, 0x04, 0x07, 0x1d); - break; - case 4: - reg_write(dev, 0x04, 0x00, 0x04); - reg_write(dev, 0x04, 0x06, 0x34); - reg_write(dev, 0x04, 0x07, 0x34); - break; - default: -/* case 5: */ - reg_write(dev, 0x04, 0x00, 0x05); - reg_write(dev, 0x04, 0x06, 0x40); - reg_write(dev, 0x04, 0x07, 0x40); - break; - } -/* Enable ISO packet machine - should we do this here or in ISOC init ? */ + reg_write(dev, 0x02, 0x00, 0x00); + + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + reg_write(dev, SPCA50X_REG_COMPRESS, 0x00, mode_tb[mode][0]); + reg_write(dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]); + reg_write(dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]); + ret = reg_write(dev, SPCA50X_REG_USB, SPCA50X_USB_CTRL, SPCA50X_CUSB_ENABLE); -/* reg_write(dev, 0x5, 0x0, 0x0); */ -/* reg_write(dev, 0x5, 0x0, 0x1); */ -/* reg_write(dev, 0x5, 0x11, 0x2); */ + setbrightness(gspca_dev); + return ret; } @@ -750,15 +732,15 @@ static void sd_stop0(struct gspca_dev *gspca_dev) /* This maybe reset or power control */ reg_write(gspca_dev->dev, 0x03, 0x03, 0x20); - reg_write(gspca_dev->dev, 0x03, 0x01, 0x0); - reg_write(gspca_dev->dev, 0x03, 0x00, 0x1); - reg_write(gspca_dev->dev, 0x05, 0x10, 0x1); - reg_write(gspca_dev->dev, 0x05, 0x11, 0xf); + reg_write(gspca_dev->dev, 0x03, 0x01, 0x00); + reg_write(gspca_dev->dev, 0x03, 0x00, 0x01); + reg_write(gspca_dev->dev, 0x05, 0x10, 0x01); + reg_write(gspca_dev->dev, 0x05, 0x11, 0x0f); } 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 packet length */ { switch (data[0]) { @@ -771,7 +753,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data, len); break; case 0xff: /* drop */ -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ break; default: data += 1; @@ -782,24 +763,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } } -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - __u8 brightness = sd->brightness; - reg_write(gspca_dev->dev, 5, 0x00, (255 - brightness) >> 6); - reg_write(gspca_dev->dev, 5, 0x01, (255 - brightness) << 2); - -} -static void getbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = 255 - - ((reg_read(gspca_dev, 5, 0x01, 1) >> 2) - + (reg_read(gspca_dev, 5, 0x0, 1) << 6)); -} - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -814,7 +777,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -863,8 +825,11 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c index 96e2512..3a0c893 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -193,24 +193,6 @@ static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur, } } -static int spca506_ReadI2c(struct gspca_dev *gspca_dev, __u16 reg) -{ - int retry = 60; - - reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004); - reg_w(gspca_dev->dev, 0x07, reg, 0x0001); - reg_w(gspca_dev->dev, 0x07, 0x01, 0x0002); - while (--retry) { - reg_r(gspca_dev, 0x07, 0x0003, 2); - if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00) - break; - } - if (retry == 0) - return -1; - reg_r(gspca_dev, 0x07, 0x0000, 1); - return gspca_dev->usb_buf[0]; -} - static void spca506_SetNormeInput(struct gspca_dev *gspca_dev, __u16 norme, __u16 channel) @@ -303,7 +285,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->epaddr = 0x01; cam->cam_mode = vga_mode; cam->nmodes = sizeof vga_mode / sizeof vga_mode[0]; sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; @@ -596,13 +577,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) spca506_WriteI2c(gspca_dev, 0x01, 0x09); } -static void getbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = spca506_ReadI2c(gspca_dev, SAA7113_bright); -} - static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -612,13 +586,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) spca506_WriteI2c(gspca_dev, 0x01, 0x09); } -static void getcontrast(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->contrast = spca506_ReadI2c(gspca_dev, SAA7113_contrast); -} - static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -628,13 +595,6 @@ static void setcolors(struct gspca_dev *gspca_dev) spca506_WriteI2c(gspca_dev, 0x01, 0x09); } -static void getcolors(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->colors = spca506_ReadI2c(gspca_dev, SAA7113_saturation); -} - static void sethue(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -644,13 +604,6 @@ static void sethue(struct gspca_dev *gspca_dev) spca506_WriteI2c(gspca_dev, 0x01, 0x09); } -static void gethue(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->hue = spca506_ReadI2c(gspca_dev, SAA7113_hue); -} - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -665,7 +618,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -684,7 +636,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcontrast(gspca_dev); *val = sd->contrast; return 0; } @@ -703,7 +654,6 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcolors(gspca_dev); *val = sd->colors; return 0; } @@ -722,7 +672,6 @@ static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - gethue(gspca_dev); *val = sd->hue; return 0; } @@ -772,8 +721,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index be5d740a..adacf84 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -101,8 +101,7 @@ static const struct v4l2_pix_format sif_mode[] = { * Initialization data: this is the first set-up data written to the * device (before the open data). */ -static const __u16 spca508_init_data[][3] = -#define IGN(x) /* nothing */ +static const u16 spca508_init_data[][2] = { /* line URB value, index */ /* 44274 1804 */ {0x0000, 0x870b}, @@ -589,11 +588,10 @@ static const __u16 spca508_init_data[][3] = {} }; - /* * Initialization data for Intel EasyPC Camera CS110 */ -static const __u16 spca508cs110_init_data[][3] = { +static const u16 spca508cs110_init_data[][2] = { {0x0000, 0x870b}, /* Reset CTL3 */ {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ {0x0000, 0x8111}, /* Normal operation on reset */ @@ -677,7 +675,7 @@ static const __u16 spca508cs110_init_data[][3] = { {} }; -static const __u16 spca508_sightcam_init_data[][3] = { +static const u16 spca508_sightcam_init_data[][2] = { /* This line seems to setup the frame/canvas */ /*368 */ {0x000f, 0x8402}, @@ -760,7 +758,7 @@ static const __u16 spca508_sightcam_init_data[][3] = { {} }; -static const __u16 spca508_sightcam2_init_data[][3] = { +static const u16 spca508_sightcam2_init_data[][2] = { /* 35 */ {0x0020, 0x8112}, /* 36 */ {0x000f, 0x8402}, @@ -1107,7 +1105,7 @@ static const __u16 spca508_sightcam2_init_data[][3] = { /* * Initialization data for Creative Webcam Vista */ -static const __u16 spca508_vista_init_data[][3] = { +static const u16 spca508_vista_init_data[][2] = { {0x0008, 0x8200}, /* Clear register */ {0x0000, 0x870b}, /* Reset CTL3 */ {0x0020, 0x8112}, /* Video Drop packet enable */ @@ -1309,18 +1307,18 @@ static const __u16 spca508_vista_init_data[][3] = { {0x0050, 0x8703}, {0x0002, 0x8704}, /* External input CKIx1 */ - {0x0001, 0x870C}, /* Select CKOx2 output */ - {0x009A, 0x8600}, /* Line memory Read Counter (L) */ + {0x0001, 0x870c}, /* Select CKOx2 output */ + {0x009a, 0x8600}, /* Line memory Read Counter (L) */ {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ {0x0023, 0x8601}, {0x0010, 0x8602}, - {0x000A, 0x8603}, + {0x000a, 0x8603}, {0x009A, 0x8600}, - {0x0001, 0x865B}, /* 1 Horizontal Offset for Valid Pixel(L) */ - {0x0003, 0x865C}, /* Vertical offset for valid lines (L) */ - {0x0058, 0x865D}, /* Horizontal valid pixels window (L) */ - {0x0048, 0x865E}, /* Vertical valid lines window (L) */ - {0x0000, 0x865F}, + {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ + {0x0003, 0x865c}, /* Vertical offset for valid lines (L) */ + {0x0058, 0x865d}, /* Horizontal valid pixels window (L) */ + {0x0048, 0x865e}, /* Vertical valid lines window (L) */ + {0x0000, 0x865f}, {0x0006, 0x8660}, /* Enable nibble data input, select nibble input order */ @@ -1328,63 +1326,63 @@ static const __u16 spca508_vista_init_data[][3] = { {0x0013, 0x8608}, /* A11 Coeficients for color correction */ {0x0028, 0x8609}, /* Note: these values are confirmed at the end of array */ - {0x0005, 0x860A}, /* ... */ - {0x0025, 0x860B}, - {0x00E1, 0x860C}, - {0x00FA, 0x860D}, - {0x00F4, 0x860E}, - {0x00E8, 0x860F}, + {0x0005, 0x860a}, /* ... */ + {0x0025, 0x860b}, + {0x00e1, 0x860c}, + {0x00fa, 0x860D}, + {0x00f4, 0x860e}, + {0x00e8, 0x860f}, {0x0025, 0x8610}, /* A33 Coef. */ - {0x00FC, 0x8611}, /* White balance offset: R */ + {0x00fc, 0x8611}, /* White balance offset: R */ {0x0001, 0x8612}, /* White balance offset: Gr */ - {0x00FE, 0x8613}, /* White balance offset: B */ + {0x00fe, 0x8613}, /* White balance offset: B */ {0x0000, 0x8614}, /* White balance offset: Gb */ {0x0064, 0x8651}, /* R gain for white balance (L) */ {0x0040, 0x8652}, /* Gr gain for white balance (L) */ {0x0066, 0x8653}, /* B gain for white balance (L) */ {0x0040, 0x8654}, /* Gb gain for white balance (L) */ - {0x0001, 0x863F}, /* Enable fixed gamma correction */ + {0x0001, 0x863f}, /* Enable fixed gamma correction */ - {0x00A1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128 */ + {0x00a1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128 */ /* UV division: UV no change, Enable New edge enhancement */ {0x0018, 0x8657}, /* Edge gain high threshold */ {0x0020, 0x8658}, /* Edge gain low threshold */ {0x000A, 0x8659}, /* Edge bandwidth high threshold */ - {0x0005, 0x865A}, /* Edge bandwidth low threshold */ + {0x0005, 0x865a}, /* Edge bandwidth low threshold */ {0x0064, 0x8607}, /* UV filter enable */ {0x0016, 0x8660}, - {0x0000, 0x86B0}, /* Bad pixels compensation address */ - {0x00DC, 0x86B1}, /* X coord for bad pixels compensation (L) */ - {0x0000, 0x86B2}, - {0x0009, 0x86B3}, /* Y coord for bad pixels compensation (L) */ - {0x0000, 0x86B4}, - - {0x0001, 0x86B0}, - {0x00F5, 0x86B1}, - {0x0000, 0x86B2}, - {0x00C6, 0x86B3}, - {0x0000, 0x86B4}, - - {0x0002, 0x86B0}, - {0x001C, 0x86B1}, - {0x0001, 0x86B2}, - {0x00D7, 0x86B3}, - {0x0000, 0x86B4}, - - {0x0003, 0x86B0}, - {0x001C, 0x86B1}, - {0x0001, 0x86B2}, - {0x00D8, 0x86B3}, - {0x0000, 0x86B4}, - - {0x0004, 0x86B0}, - {0x001D, 0x86B1}, - {0x0001, 0x86B2}, - {0x00D8, 0x86B3}, - {0x0000, 0x86B4}, - {0x001E, 0x8660}, + {0x0000, 0x86b0}, /* Bad pixels compensation address */ + {0x00dc, 0x86b1}, /* X coord for bad pixels compensation (L) */ + {0x0000, 0x86b2}, + {0x0009, 0x86b3}, /* Y coord for bad pixels compensation (L) */ + {0x0000, 0x86b4}, + + {0x0001, 0x86b0}, + {0x00f5, 0x86b1}, + {0x0000, 0x86b2}, + {0x00c6, 0x86b3}, + {0x0000, 0x86b4}, + + {0x0002, 0x86b0}, + {0x001c, 0x86b1}, + {0x0001, 0x86b2}, + {0x00d7, 0x86b3}, + {0x0000, 0x86b4}, + + {0x0003, 0x86b0}, + {0x001c, 0x86b1}, + {0x0001, 0x86b2}, + {0x00d8, 0x86b3}, + {0x0000, 0x86b4}, + + {0x0004, 0x86b0}, + {0x001d, 0x86b1}, + {0x0001, 0x86b2}, + {0x00d8, 0x86b3}, + {0x0000, 0x86b4}, + {0x001e, 0x8660}, /* READ { 0, 0x0000, 0x8608 } -> 0000: 13 */ @@ -1449,7 +1447,7 @@ static int reg_read(struct gspca_dev *gspca_dev, } static int write_vector(struct gspca_dev *gspca_dev, - const __u16 data[][3]) + const u16 data[][2]) { struct usb_device *dev = gspca_dev->dev; int ret, i = 0; @@ -1487,7 +1485,6 @@ static int sd_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "Window 1 average luminance: %d", data1); cam = &gspca_dev->cam; - cam->epaddr = 0x01; cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); @@ -1593,13 +1590,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) reg_write(gspca_dev->dev, 0x8654, brightness); } -static void getbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = reg_read(gspca_dev, 0x8651); -} - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1614,7 +1604,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -1666,8 +1655,11 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index 3c92880..c99c5e3 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -141,38 +141,38 @@ static const struct v4l2_pix_format sif_072a_mode[] = { #define SPCA561_OFFSET_WIN1GBAVE 14 #define SPCA561_OFFSET_FREQ 15 #define SPCA561_OFFSET_VSYNC 16 -#define SPCA561_OFFSET_DATA 1 #define SPCA561_INDEX_I2C_BASE 0x8800 #define SPCA561_SNAPBIT 0x20 #define SPCA561_SNAPCTRL 0x40 -static const __u16 rev72a_init_data1[][2] = { +static const u16 rev72a_reset[][2] = { {0x0000, 0x8114}, /* Software GPIO output data */ {0x0001, 0x8114}, /* Software GPIO output data */ {0x0000, 0x8112}, /* Some kind of reset */ + {} +}; +static const __u16 rev72a_init_data1[][2] = { {0x0003, 0x8701}, /* PCLK clock delay adjustment */ {0x0001, 0x8703}, /* HSYNC from cmos inverted */ {0x0011, 0x8118}, /* Enable and conf sensor */ {0x0001, 0x8118}, /* Conf sensor */ {0x0092, 0x8804}, /* I know nothing about these */ {0x0010, 0x8802}, /* 0x88xx registers, so I won't */ - {0x000d, 0x8805}, /* sensor default setting */ {} }; -static const __u16 rev72a_init_sensor1[][2] = { - /* ms-win values */ - {0x0001, 0x0018}, /* 0x01 <- 0x0d */ - {0x0002, 0x0065}, /* 0x02 <- 0x18 */ - {0x0004, 0x0121}, /* 0x04 <- 0x0165 */ - {0x0005, 0x00aa}, /* 0x05 <- 0x21 */ - {0x0007, 0x0004}, /* 0x07 <- 0xaa */ - {0x0020, 0x1502}, /* 0x20 <- 0x1504 */ - {0x0039, 0x0010}, /* 0x39 <- 0x02 */ - {0x0035, 0x0049}, /* 0x35 <- 0x10 */ - {0x0009, 0x100b}, /* 0x09 <- 0x1049 */ - {0x0028, 0x000f}, /* 0x28 <- 0x0b */ - {0x003b, 0x003c}, /* 0x3b <- 0x0f */ - {0x003c, 0x0000}, /* 0x3c <- 0x00 */ +static const u16 rev72a_init_sensor1[][2] = { + {0x0001, 0x000d}, + {0x0002, 0x0018}, + {0x0004, 0x0165}, + {0x0005, 0x0021}, + {0x0007, 0x00aa}, + {0x0020, 0x1504}, + {0x0039, 0x0002}, + {0x0035, 0x0010}, + {0x0009, 0x1049}, + {0x0028, 0x000b}, + {0x003b, 0x000f}, + {0x003c, 0x0000}, {} }; static const __u16 rev72a_init_data2[][2] = { @@ -190,15 +190,10 @@ static const __u16 rev72a_init_data2[][2] = { {0x0002, 0x8201}, /* Output address for r/w serial EEPROM */ {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ {0x0001, 0x8200}, /* OprMode to be executed by hardware */ - {0x0007, 0x8201}, /* Output address for r/w serial EEPROM */ - {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */ - {0x0001, 0x8200}, /* OprMode to be executed by hardware */ - {0x0010, 0x8660}, /* Compensation memory stuff */ - {0x0018, 0x8660}, /* Compensation memory stuff */ - - {0x0004, 0x8611}, /* R offset for white balance */ - {0x0004, 0x8612}, /* Gr offset for white balance */ - {0x0007, 0x8613}, /* B offset for white balance */ +/* from ms-win */ + {0x0000, 0x8611}, /* R offset for white balance */ + {0x00fd, 0x8612}, /* Gr offset for white balance */ + {0x0003, 0x8613}, /* B offset for white balance */ {0x0000, 0x8614}, /* Gb offset for white balance */ /* from ms-win */ {0x0035, 0x8651}, /* R gain for white balance */ @@ -206,8 +201,8 @@ static const __u16 rev72a_init_data2[][2] = { {0x005f, 0x8653}, /* B gain for white balance */ {0x0040, 0x8654}, /* Gb gain for white balance */ {0x0002, 0x8502}, /* Maximum average bit rate stuff */ - {0x0011, 0x8802}, + {0x0087, 0x8700}, /* Set master clock (96Mhz????) */ {0x0081, 0x8702}, /* Master clock output enable */ @@ -218,104 +213,15 @@ static const __u16 rev72a_init_data2[][2] = { {0x0003, 0x865c}, /* Vertical offset for valid lines */ {} }; -static const __u16 rev72a_init_sensor2[][2] = { - /* ms-win values */ - {0x0003, 0x0121}, /* 0x03 <- 0x01 0x21 //289 */ - {0x0004, 0x0165}, /* 0x04 <- 0x01 0x65 //357 */ - {0x0005, 0x002f}, /* 0x05 <- 0x2f */ - {0x0006, 0x0000}, /* 0x06 <- 0 */ - {0x000a, 0x0002}, /* 0x0a <- 2 */ - {0x0009, 0x1061}, /* 0x09 <- 0x1061 */ - {0x0035, 0x0014}, /* 0x35 <- 0x14 */ - {} -}; -static const __u16 rev72a_init_data3[][2] = { - {0x0030, 0x8112}, /* ISO and drop packet enable */ -/*fixme: should stop here*/ - {0x0000, 0x8112}, /* Some kind of reset ???? */ - {0x0009, 0x8118}, /* Enable sensor and set standby */ - {0x0000, 0x8114}, /* Software GPIO output data */ - {0x0000, 0x8114}, /* Software GPIO output data */ - {0x0001, 0x8114}, /* Software GPIO output data */ - {0x0000, 0x8112}, /* Some kind of reset ??? */ - {0x0003, 0x8701}, - {0x0001, 0x8703}, - {0x0011, 0x8118}, - {0x0001, 0x8118}, - /***************/ - {0x0092, 0x8804}, - {0x0010, 0x8802}, - {0x000d, 0x8805}, - {0x0001, 0x8801}, - {0x0000, 0x8800}, - {0x0018, 0x8805}, - {0x0002, 0x8801}, - {0x0000, 0x8800}, - {0x0065, 0x8805}, - {0x0004, 0x8801}, - {0x0001, 0x8800}, - {0x0021, 0x8805}, - {0x0005, 0x8801}, - {0x0000, 0x8800}, - {0x00aa, 0x8805}, - {0x0007, 0x8801}, /* mode 0xaa */ - {0x0000, 0x8800}, - {0x0004, 0x8805}, - {0x0020, 0x8801}, - {0x0015, 0x8800}, /* mode 0x0415 */ - {0x0002, 0x8805}, - {0x0039, 0x8801}, - {0x0000, 0x8800}, - {0x0010, 0x8805}, - {0x0035, 0x8801}, - {0x0000, 0x8800}, - {0x0049, 0x8805}, - {0x0009, 0x8801}, - {0x0010, 0x8800}, - {0x000b, 0x8805}, - {0x0028, 0x8801}, - {0x0000, 0x8800}, - {0x000f, 0x8805}, - {0x003b, 0x8801}, - {0x0000, 0x8800}, - {0x0000, 0x8805}, - {0x003c, 0x8801}, - {0x0000, 0x8800}, - {0x0002, 0x8502}, - {0x0039, 0x8801}, - {0x0000, 0x8805}, - {0x0000, 0x8800}, - - {0x0087, 0x8700}, /* overwrite by start */ - {0x0081, 0x8702}, - {0x0000, 0x8500}, -/* {0x0010, 0x8500}, -- Previous line was this */ - {0x0002, 0x865b}, - {0x0003, 0x865c}, - /***************/ - {0x0003, 0x8801}, /* 0x121-> 289 */ - {0x0021, 0x8805}, - {0x0001, 0x8800}, - {0x0004, 0x8801}, /* 0x165 -> 357 */ - {0x0065, 0x8805}, - {0x0001, 0x8800}, - {0x0005, 0x8801}, /* 0x2f //blanking control colonne */ - {0x002f, 0x8805}, - {0x0000, 0x8800}, - {0x0006, 0x8801}, /* 0x00 //blanking mode row */ - {0x0000, 0x8805}, - {0x0000, 0x8800}, - {0x000a, 0x8801}, /* 0x01 //0x02 */ - {0x0001, 0x8805}, - {0x0000, 0x8800}, - {0x0009, 0x8801}, /* 0x1061 - setexposure times && pixel clock +static const u16 rev72a_init_sensor2[][2] = { + {0x0003, 0x0121}, + {0x0004, 0x0165}, + {0x0005, 0x002f}, /* blanking control column */ + {0x0006, 0x0000}, /* blanking mode row*/ + {0x000a, 0x0002}, + {0x0009, 0x1061}, /* setexposure times && pixel clock * 0001 0 | 000 0110 0001 */ - {0x0061, 0x8805}, /* 61 31 */ - {0x0008, 0x8800}, /* 08 */ - {0x0035, 0x8801}, /* 0x14 - set gain general */ - {0x001f, 0x8805}, /* 0x14 */ - {0x0000, 0x8800}, - {0x000e, 0x8112}, /* white balance - was 30 */ + {0x0035, 0x0014}, {} }; @@ -460,6 +366,7 @@ static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg) reg_r(gspca_dev, 0x8803, 1); if (!gspca_dev->usb_buf[0]) return; + msleep(10); } while (--retry); } @@ -479,6 +386,7 @@ static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode) reg_r(gspca_dev, 0x8805, 1); return ((int) value << 8) | gspca_dev->usb_buf[0]; } + msleep(10); } while (--retry); return -1; } @@ -541,7 +449,6 @@ static int sd_config(struct gspca_dev *gspca_dev, } cam = &gspca_dev->cam; - cam->epaddr = 0x01; gspca_dev->nbalt = 7 + 1; /* choose alternate 7 first */ sd->chip_revision = id->driver_info; @@ -572,11 +479,13 @@ static int sd_init_12a(struct gspca_dev *gspca_dev) static int sd_init_72a(struct gspca_dev *gspca_dev) { PDEBUG(D_STREAM, "Chip revision: 072a"); + write_vector(gspca_dev, rev72a_reset); + msleep(200); write_vector(gspca_dev, rev72a_init_data1); write_sensor_72a(gspca_dev, rev72a_init_sensor1); write_vector(gspca_dev, rev72a_init_data2); write_sensor_72a(gspca_dev, rev72a_init_sensor2); - write_vector(gspca_dev, rev72a_init_data3); + reg_w_val(gspca_dev->dev, 0x8112, 0x30); return 0; } @@ -731,11 +640,18 @@ static int sd_start_72a(struct gspca_dev *gspca_dev) int Clck; int mode; + write_vector(gspca_dev, rev72a_reset); + msleep(200); + write_vector(gspca_dev, rev72a_init_data1); + write_sensor_72a(gspca_dev, rev72a_init_sensor1); + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; switch (mode) { default: -/* case 0: - case 1: */ + case 0: + Clck = 0x27; /* ms-win 0x87 */ + break; + case 1: Clck = 0x25; break; case 2: @@ -745,13 +661,14 @@ static int sd_start_72a(struct gspca_dev *gspca_dev) Clck = 0x21; break; } - reg_w_val(dev, 0x8500, mode); /* mode */ reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */ - reg_w_val(dev, 0x8112, 0x10 | 0x20); + reg_w_val(dev, 0x8702, 0x81); + reg_w_val(dev, 0x8500, mode); /* mode */ + write_sensor_72a(gspca_dev, rev72a_init_sensor2); setcontrast(gspca_dev); /* setbrightness(gspca_dev); * fixme: bad values */ - setwhite(gspca_dev); setautogain(gspca_dev); + reg_w_val(dev, 0x8112, 0x10 | 0x20); return 0; } @@ -867,12 +784,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; - switch (data[0]) { /* sequence number */ + len--; + switch (*data++) { /* sequence number */ case 0: /* start of frame */ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); - data += SPCA561_OFFSET_DATA; - len -= SPCA561_OFFSET_DATA; if (data[1] & 0x10) { /* compressed bayer */ gspca_frame_add(gspca_dev, FIRST_PACKET, @@ -893,8 +809,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, case 0xff: /* drop (empty mpackets) */ return; } - data++; - len--; gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } @@ -1197,8 +1111,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c new file mode 100644 index 0000000..04e3ae5 --- /dev/null +++ b/drivers/media/video/gspca/sq905.c @@ -0,0 +1,456 @@ +/* + * SQ905 subdriver + * + * Copyright (C) 2008, 2009 Adam Baker and 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 + */ + +/* + * History and Acknowledgments + * + * The original Linux driver for SQ905 based cameras was written by + * Marcell Lengyel and furter developed by many other contributers + * and is available from http://sourceforge.net/projects/sqcam/ + * + * This driver takes advantage of the reverse engineering work done for + * that driver and for libgphoto2 but shares no code with them. + * + * This driver has used as a base the finepix driver and other gspca + * based drivers and may still contain code fragments taken from those + * drivers. + */ + +#define MODULE_NAME "sq905" + +#include <linux/workqueue.h> +#include "gspca.h" + +MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>, " + "Theodore Kilgore <kilgota@auburn.edu>"); +MODULE_DESCRIPTION("GSPCA/SQ905 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* Default timeouts, in ms */ +#define SQ905_CMD_TIMEOUT 500 +#define SQ905_DATA_TIMEOUT 1000 + +/* Maximum transfer size to use. */ +#define SQ905_MAX_TRANSFER 0x8000 +#define FRAME_HEADER_LEN 64 + +/* The known modes, or registers. These go in the "value" slot. */ + +/* 00 is "none" obviously */ + +#define SQ905_BULK_READ 0x03 /* precedes any bulk read */ +#define SQ905_COMMAND 0x06 /* precedes the command codes below */ +#define SQ905_PING 0x07 /* when reading an "idling" command */ +#define SQ905_READ_DONE 0xc0 /* ack bulk read completed */ + +/* Any non-zero value in the bottom 2 bits of the 2nd byte of + * the ID appears to indicate the camera can do 640*480. If the + * LSB of that byte is set the image is just upside down, otherwise + * it is rotated 180 degrees. */ +#define SQ905_HIRES_MASK 0x00000300 +#define SQ905_ORIENTATION_MASK 0x00000100 + +/* Some command codes. These go in the "index" slot. */ + +#define SQ905_ID 0xf0 /* asks for model string */ +#define SQ905_CONFIG 0x20 /* gets photo alloc. table, not used here */ +#define SQ905_DATA 0x30 /* accesses photo data, not used here */ +#define SQ905_CLEAR 0xa0 /* clear everything */ +#define SQ905_CAPTURE_LOW 0x60 /* Starts capture at 160x120 */ +#define SQ905_CAPTURE_MED 0x61 /* Starts capture at 320x240 */ +#define SQ905_CAPTURE_HIGH 0x62 /* Starts capture at 640x480 (some cams only) */ +/* note that the capture command also controls the output dimensions */ + +/* Structure to hold all of our device specific stuff */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + /* + * Driver stuff + */ + struct work_struct work_struct; + struct workqueue_struct *work_thread; +}; + +static struct v4l2_pix_format sq905_mode[] = { + { 160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + { 320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + { 640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0} +}; + +/* + * Send a command to the camera. + */ +static int sq905_command(struct gspca_dev *gspca_dev, u16 index) +{ + int ret; + + gspca_dev->usb_buf[0] = '\0'; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + SQ905_COMMAND, index, gspca_dev->usb_buf, 1, + SQ905_CMD_TIMEOUT); + if (ret < 0) { + PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", + __func__, ret); + return ret; + } + + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + SQ905_PING, 0, gspca_dev->usb_buf, 1, + SQ905_CMD_TIMEOUT); + if (ret < 0) { + PDEBUG(D_ERR, "%s: usb_control_msg failed 2 (%d)", + __func__, ret); + return ret; + } + + return 0; +} + +/* + * Acknowledge the end of a frame - see warning on sq905_command. + */ +static int sq905_ack_frame(struct gspca_dev *gspca_dev) +{ + int ret; + + gspca_dev->usb_buf[0] = '\0'; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1, + SQ905_CMD_TIMEOUT); + if (ret < 0) { + PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret); + return ret; + } + + return 0; +} + +/* + * request and read a block of data - see warning on sq905_command. + */ +static int +sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size) +{ + int ret; + int act_len; + + gspca_dev->usb_buf[0] = '\0'; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + SQ905_BULK_READ, size, gspca_dev->usb_buf, + 1, SQ905_CMD_TIMEOUT); + if (ret < 0) { + PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret); + return ret; + } + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x81), + data, size, &act_len, SQ905_DATA_TIMEOUT); + + /* successful, it returns 0, otherwise negative */ + if (ret < 0 || act_len != size) { + PDEBUG(D_ERR, "bulk read fail (%d) len %d/%d", + ret, act_len, size); + return -EIO; + } + return 0; +} + +/* 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 we take the gspca + * usb_lock when performing USB operations. In practice the only thing we need + * to protect against is the usb_set_interface call that gspca makes during + * stream_off as the camera doesn't provide any controls that the user could try + * to change. + */ +static void sq905_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 bytes_left; /* bytes remaining in current frame. */ + int data_len; /* size to use for the next read. */ + int header_read; /* true if we have already read the frame header. */ + int discarding; /* true if we failed to get space for frame. */ + int packet_type; + int frame_sz; + int ret; + u8 *data; + u8 *buffer; + + buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); + mutex_lock(&gspca_dev->usb_lock); + if (!buffer) { + PDEBUG(D_ERR, "Couldn't allocate USB buffer"); + goto quit_stream; + } + + frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + + FRAME_HEADER_LEN; + + while (gspca_dev->present && gspca_dev->streaming) { + /* Need a short delay to ensure streaming flag was set by + * gspca and to make sure gspca can grab the mutex. */ + mutex_unlock(&gspca_dev->usb_lock); + msleep(1); + + /* request some data and then read it until we have + * a complete frame. */ + bytes_left = frame_sz; + header_read = 0; + discarding = 0; + + while (bytes_left > 0) { + data_len = bytes_left > SQ905_MAX_TRANSFER ? + SQ905_MAX_TRANSFER : bytes_left; + mutex_lock(&gspca_dev->usb_lock); + if (!gspca_dev->present) + goto quit_stream; + ret = sq905_read_data(gspca_dev, buffer, data_len); + if (ret < 0) + goto quit_stream; + mutex_unlock(&gspca_dev->usb_lock); + PDEBUG(D_STREAM, + "Got %d bytes out of %d for frame", + data_len, bytes_left); + bytes_left -= data_len; + data = buffer; + if (!header_read) { + packet_type = FIRST_PACKET; + /* The first 64 bytes of each frame are + * a header full of FF 00 bytes */ + data += FRAME_HEADER_LEN; + data_len -= FRAME_HEADER_LEN; + header_read = 1; + } else if (bytes_left == 0) { + packet_type = LAST_PACKET; + } else { + packet_type = INTER_PACKET; + } + frame = gspca_get_i_frame(gspca_dev); + if (frame && !discarding) { + frame = gspca_frame_add(gspca_dev, packet_type, + frame, data, data_len); + /* If entire frame fits in one packet we still + need to add a LAST_PACKET */ + if (packet_type == FIRST_PACKET && + bytes_left == 0) + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, data, 0); + } else { + discarding = 1; + } + } + /* acknowledge the frame */ + mutex_lock(&gspca_dev->usb_lock); + if (!gspca_dev->present) + goto quit_stream; + ret = sq905_ack_frame(gspca_dev); + if (ret < 0) + goto quit_stream; + } +quit_stream: + /* the usb_lock is already acquired */ + if (gspca_dev->present) + sq905_command(gspca_dev, SQ905_CLEAR); + 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; + + /* We don't use the buffer gspca allocates so make it small. */ + cam->bulk_size = 64; + + INIT_WORK(&dev->work_struct, sq905_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 sq905_dostream to finish */ + destroy_workqueue(dev->work_thread); + dev->work_thread = NULL; + mutex_lock(&gspca_dev->usb_lock); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + u32 ident; + int ret; + + /* connect to the camera and read + * the model ID and process that and put it away. + */ + ret = sq905_command(gspca_dev, SQ905_CLEAR); + if (ret < 0) + return ret; + ret = sq905_command(gspca_dev, SQ905_ID); + if (ret < 0) + return ret; + ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4); + if (ret < 0) + return ret; + /* usb_buf is allocated with kmalloc so is aligned. + * Camera model number is the right way round if we assume this + * reverse engineered ID is supposed to be big endian. */ + ident = be32_to_cpup((__be32 *)gspca_dev->usb_buf); + ret = sq905_command(gspca_dev, SQ905_CLEAR); + if (ret < 0) + return ret; + PDEBUG(D_CONF, "SQ905 camera ID %08x detected", ident); + gspca_dev->cam.cam_mode = sq905_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode); + if (!(ident & SQ905_HIRES_MASK)) + gspca_dev->cam.nmodes--; + 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; + + /* "Open the shutter" and set size, to start capture */ + switch (gspca_dev->curr_mode) { + default: +/* case 2: */ + PDEBUG(D_STREAM, "Start streaming at high resolution"); + ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH); + break; + case 1: + PDEBUG(D_STREAM, "Start streaming at medium resolution"); + ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED); + break; + case 0: + PDEBUG(D_STREAM, "Start streaming at low resolution"); + ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW); + } + + 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(0x2770, 0x9120)}, + {} +}; + +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/sq905c.c b/drivers/media/video/gspca/sq905c.c new file mode 100644 index 0000000..0bcb74a --- /dev/null +++ b/drivers/media/video/gspca/sq905c.c @@ -0,0 +1,328 @@ +/* + * SQ905C subdriver + * + * 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 + */ + +/* + * + * This driver uses work done in + * libgphoto2/camlibs/digigr8, Copyright (C) Theodore Kilgore. + * + * This driver has also used as a base the sq905c driver + * and may contain code fragments from it. + */ + +#define MODULE_NAME "sq905c" + +#include <linux/workqueue.h> +#include "gspca.h" + +MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>"); +MODULE_DESCRIPTION("GSPCA/SQ905C USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* Default timeouts, in ms */ +#define SQ905C_CMD_TIMEOUT 500 +#define SQ905C_DATA_TIMEOUT 1000 + +/* Maximum transfer size to use. */ +#define SQ905C_MAX_TRANSFER 0x8000 + +#define FRAME_HEADER_LEN 0x50 + +/* Commands. These go in the "value" slot. */ +#define SQ905C_CLEAR 0xa0 /* clear everything */ +#define SQ905C_CAPTURE_LOW 0xa040 /* Starts capture at 160x120 */ +#define SQ905C_CAPTURE_MED 0x1440 /* Starts capture at 320x240 */ +#define SQ905C_CAPTURE_HI 0x2840 /* Starts capture at 320x240 */ + +/* For capture, this must go in the "index" slot. */ +#define SQ905C_CAPTURE_INDEX 0x110f + +/* 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; +}; + +/* + * Most of these cameras will do 640x480 and 320x240. 160x120 works + * in theory but gives very poor output. Therefore, not supported. + * The 0x2770:0x9050 cameras have max resolution of 320x240. + */ +static struct v4l2_pix_format sq905c_mode[] = { + { 320, 240, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + { 640, 480, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0} +}; + +/* Send a command to the camera. */ +static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index) +{ + int ret; + + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + command, index, NULL, 0, + SQ905C_CMD_TIMEOUT); + if (ret < 0) { + PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", + __func__, ret); + return ret; + } + + return 0; +} + +/* 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 sq905c_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 bytes_left; /* bytes remaining in current frame. */ + int data_len; /* size to use for the next read. */ + int act_len; + int discarding = 0; /* true if we failed to get space for frame. */ + int packet_type; + int ret; + u8 *buffer; + + buffer = kmalloc(SQ905C_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; + /* Request the header, which tells the size to download */ + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x81), + buffer, FRAME_HEADER_LEN, &act_len, + SQ905C_DATA_TIMEOUT); + PDEBUG(D_STREAM, + "Got %d bytes out of %d for header", + act_len, FRAME_HEADER_LEN); + if (ret < 0 || act_len < FRAME_HEADER_LEN) + goto quit_stream; + /* size is read from 4 bytes starting 0x40, little endian */ + bytes_left = buffer[0x40]|(buffer[0x41]<<8)|(buffer[0x42]<<16) + |(buffer[0x43]<<24); + PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left); + /* We keep the header. It has other information, too. */ + packet_type = FIRST_PACKET; + frame = gspca_get_i_frame(gspca_dev); + if (frame && !discarding) { + gspca_frame_add(gspca_dev, packet_type, + frame, buffer, FRAME_HEADER_LEN); + } else + discarding = 1; + while (bytes_left > 0) { + data_len = bytes_left > SQ905C_MAX_TRANSFER ? + SQ905C_MAX_TRANSFER : bytes_left; + if (!gspca_dev->present) + goto quit_stream; + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x81), + buffer, data_len, &act_len, + SQ905C_DATA_TIMEOUT); + if (ret < 0 || act_len < data_len) + goto quit_stream; + PDEBUG(D_STREAM, + "Got %d bytes out of %d for frame", + data_len, bytes_left); + bytes_left -= data_len; + if (bytes_left == 0) + packet_type = LAST_PACKET; + else + packet_type = INTER_PACKET; + frame = gspca_get_i_frame(gspca_dev); + if (frame && !discarding) + gspca_frame_add(gspca_dev, packet_type, + frame, buffer, data_len); + else + discarding = 1; + } + } +quit_stream: + mutex_lock(&gspca_dev->usb_lock); + if (gspca_dev->present) + sq905c_command(gspca_dev, SQ905C_CLEAR, 0); + 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; + + PDEBUG(D_PROBE, + "SQ9050 camera detected" + " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + cam->cam_mode = sq905c_mode; + cam->nmodes = 2; + if (id->idProduct == 0x9050) + cam->nmodes = 1; + /* We don't use the buffer gspca allocates so make it small. */ + cam->bulk_size = 32; + INIT_WORK(&dev->work_struct, sq905c_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 sq905c_dostream to finish */ + destroy_workqueue(dev->work_thread); + dev->work_thread = NULL; + mutex_lock(&gspca_dev->usb_lock); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + int ret; + + /* connect to the camera and reset it. */ + ret = sq905c_command(gspca_dev, SQ905C_CLEAR, 0); + return ret; +} + +/* Set up for getting frames. */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + int ret; + + dev->cap_mode = gspca_dev->cam.cam_mode; + /* "Open the shutter" and set size, to start capture */ + switch (gspca_dev->width) { + case 640: + PDEBUG(D_STREAM, "Start streaming at high resolution"); + dev->cap_mode++; + ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_HI, + SQ905C_CAPTURE_INDEX); + break; + default: /* 320 */ + PDEBUG(D_STREAM, "Start streaming at medium resolution"); + ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_MED, + SQ905C_CAPTURE_INDEX); + } + + 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(0x2770, 0x905c)}, + {USB_DEVICE(0x2770, 0x9050)}, + {USB_DEVICE(0x2770, 0x913d)}, + {} +}; + +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/stk014.c b/drivers/media/video/gspca/stk014.c index 60de9af..f25be20 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -35,10 +35,13 @@ struct sd { unsigned char contrast; unsigned char colors; unsigned char lightfreq; -}; + u8 quality; +#define QUALITY_MIN 60 +#define QUALITY_MAX 95 +#define QUALITY_DEF 80 -/* global parameters */ -static int sd_quant = 7; /* <= 4 KO - 7: good (enough!) */ + u8 *jpeg_hdr; +}; /* V4L2 controls supported by the driver */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); @@ -180,7 +183,7 @@ static int rcv_val(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 0x63b, 0); reg_w(gspca_dev, 0x630, 5); ret = usb_bulk_msg(dev, - usb_rcvbulkpipe(dev, 5), + usb_rcvbulkpipe(dev, 0x05), gspca_dev->usb_buf, 4, /* length */ &alen, @@ -294,15 +297,14 @@ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam = &gspca_dev->cam; - cam->epaddr = 0x02; gspca_dev->cam.cam_mode = vga_mode; gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; sd->lightfreq = FREQ_DEF; + sd->quality = QUALITY_DEF; return 0; } @@ -326,8 +328,15 @@ static int sd_init(struct gspca_dev *gspca_dev) /* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int ret, value; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + /* work on alternate 1 */ usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); @@ -399,11 +408,19 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "camera stopped"); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; static unsigned char ffd9[] = {0xff, 0xd9}; /* a frame starts with: @@ -420,7 +437,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, ffd9, 2); /* put the JPEG 411 header */ - jpeg_put_header(gspca_dev, frame, sd_quant, 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); /* beginning of the frame */ #define STKHDRSZ 12 @@ -520,6 +538,34 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + if (gspca_dev->streaming) + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -529,8 +575,11 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ @@ -562,8 +611,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; info("registered"); return 0; } @@ -575,6 +626,3 @@ static void __exit sd_mod_exit(void) module_init(sd_mod_init); module_exit(sd_mod_exit); - -module_param_named(quant, sd_quant, int, 0644); -MODULE_PARM_DESC(quant, "Quantization index (0..8)"); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 13a021e..9dff2e6 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -429,7 +429,6 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "Configuring camera"); cam = &gspca_dev->cam; - cam->epaddr = STV_ISOC_ENDPOINT_ADDR; sd->desc = sd_desc; gspca_dev->sd_desc = &sd->desc; @@ -501,8 +500,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index 14335a9..b169038 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -30,6 +30,66 @@ #include "stv06xx_hdcs.h" +static const struct ctrl hdcs1x00_ctrl[] = { + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0xffff, + .step = 0x1, + .default_value = HDCS_DEFAULT_EXPOSURE, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = hdcs_set_exposure, + .get = hdcs_get_exposure + }, { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = HDCS_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = hdcs_set_gain, + .get = hdcs_get_gain + } +}; + +static struct v4l2_pix_format hdcs1x00_mode[] = { + { + HDCS_1X00_DEF_WIDTH, + HDCS_1X00_DEF_HEIGHT, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT, + .bytesperline = HDCS_1X00_DEF_WIDTH, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + } +}; + +static const struct ctrl hdcs1020_ctrl[] = {}; + +static struct v4l2_pix_format hdcs1020_mode[] = { + { + HDCS_1020_DEF_WIDTH, + HDCS_1020_DEF_HEIGHT, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT, + .bytesperline = HDCS_1020_DEF_WIDTH, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + } +}; + enum hdcs_power_state { HDCS_STATE_SLEEP, HDCS_STATE_IDLE, @@ -353,10 +413,10 @@ static int hdcs_probe_1x00(struct sd *sd) info("HDCS-1000/1100 sensor detected"); - sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1x00.modes; - sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1x00.nmodes; - sd->desc.ctrls = stv06xx_sensor_hdcs1x00.ctrls; - sd->desc.nctrls = stv06xx_sensor_hdcs1x00.nctrls; + sd->gspca_dev.cam.cam_mode = hdcs1x00_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode); + sd->desc.ctrls = hdcs1x00_ctrl; + sd->desc.nctrls = ARRAY_SIZE(hdcs1x00_ctrl); hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); if (!hdcs) @@ -412,10 +472,10 @@ static int hdcs_probe_1020(struct sd *sd) info("HDCS-1020 sensor detected"); - sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1020.modes; - sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1020.nmodes; - sd->desc.ctrls = stv06xx_sensor_hdcs1020.ctrls; - sd->desc.nctrls = stv06xx_sensor_hdcs1020.nctrls; + sd->gspca_dev.cam.cam_mode = hdcs1020_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode); + sd->desc.ctrls = hdcs1020_ctrl; + sd->desc.nctrls = ARRAY_SIZE(hdcs1020_ctrl); hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); if (!hdcs) diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h index 9c7279a..412f06c 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h @@ -152,53 +152,6 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = { .stop = hdcs_stop, .disconnect = hdcs_disconnect, .dump = hdcs_dump, - - .nctrls = 2, - .ctrls = { - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xffff, - .step = 0x1, - .default_value = HDCS_DEFAULT_EXPOSURE, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_exposure, - .get = hdcs_get_exposure - }, - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = HDCS_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_gain, - .get = hdcs_get_gain - } - }, - - .nmodes = 1, - .modes = { - { - HDCS_1X00_DEF_WIDTH, - HDCS_1X00_DEF_HEIGHT, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT, - .bytesperline = HDCS_1X00_DEF_WIDTH, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - } - } }; const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = { @@ -207,29 +160,11 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = { .i2c_addr = (0x55 << 1), .i2c_len = 1, - .nctrls = 0, - .ctrls = {}, - .init = hdcs_init, .probe = hdcs_probe_1020, .start = hdcs_start, .stop = hdcs_stop, .dump = hdcs_dump, - - .nmodes = 1, - .modes = { - { - HDCS_1020_DEF_WIDTH, - HDCS_1020_DEF_HEIGHT, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT, - .bytesperline = HDCS_1020_DEF_WIDTH, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - } - } }; static const u16 stv_bridge_init[][2] = { diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c index d0a0f85..285221e 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c @@ -46,6 +46,132 @@ #include "stv06xx_pb0100.h" +static const struct ctrl pb0100_ctrl[] = { +#define GAIN_IDX 0 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 128 + }, + .set = pb0100_set_gain, + .get = pb0100_get_gain + }, +#define RED_BALANCE_IDX 1 + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = -255, + .maximum = 255, + .step = 1, + .default_value = 0 + }, + .set = pb0100_set_red_balance, + .get = pb0100_get_red_balance + }, +#define BLUE_BALANCE_IDX 2 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = -255, + .maximum = 255, + .step = 1, + .default_value = 0 + }, + .set = pb0100_set_blue_balance, + .get = pb0100_get_blue_balance + }, +#define EXPOSURE_IDX 3 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 511, + .step = 1, + .default_value = 12 + }, + .set = pb0100_set_exposure, + .get = pb0100_get_exposure + }, +#define AUTOGAIN_IDX 4 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Automatic Gain and Exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = pb0100_set_autogain, + .get = pb0100_get_autogain + }, +#define AUTOGAIN_TARGET_IDX 5 + { + { + .id = V4L2_CTRL_CLASS_USER + 0x1000, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Automatic Gain Target", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 128 + }, + .set = pb0100_set_autogain_target, + .get = pb0100_get_autogain_target + }, +#define NATURAL_IDX 6 + { + { + .id = V4L2_CTRL_CLASS_USER + 0x1001, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Natural Light Source", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = pb0100_set_natural, + .get = pb0100_get_natural + } +}; + +static struct v4l2_pix_format pb0100_mode[] = { +/* low res / subsample modes disabled as they are only half res horizontal, + halving the vertical resolution does not seem to work */ + { + 320, + 240, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 320 * 240, + .bytesperline = 320, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = PB0100_CROP_TO_VGA + }, + { + 352, + 288, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 352 * 288, + .bytesperline = 352, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + static int pb0100_probe(struct sd *sd) { u16 sensor; @@ -59,20 +185,19 @@ static int pb0100_probe(struct sd *sd) if ((sensor >> 8) == 0x64) { sensor_settings = kmalloc( - stv06xx_sensor_pb0100.nctrls * sizeof(s32), + ARRAY_SIZE(pb0100_ctrl) * sizeof(s32), GFP_KERNEL); if (!sensor_settings) return -ENOMEM; info("Photobit pb0100 sensor detected"); - sd->gspca_dev.cam.cam_mode = stv06xx_sensor_pb0100.modes; - sd->gspca_dev.cam.nmodes = stv06xx_sensor_pb0100.nmodes; - sd->desc.ctrls = stv06xx_sensor_pb0100.ctrls; - sd->desc.nctrls = stv06xx_sensor_pb0100.nctrls; - for (i = 0; i < stv06xx_sensor_pb0100.nctrls; i++) - sensor_settings[i] = stv06xx_sensor_pb0100. - ctrls[i].qctrl.default_value; + sd->gspca_dev.cam.cam_mode = pb0100_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); + sd->desc.ctrls = pb0100_ctrl; + sd->desc.nctrls = ARRAY_SIZE(pb0100_ctrl); + for (i = 0; i < sd->desc.nctrls; i++) + sensor_settings[i] = pb0100_ctrl[i].qctrl.default_value; sd->sensor_priv = sensor_settings; return 0; @@ -143,6 +268,12 @@ out: return (err < 0) ? err : 0; } +static void pb0100_disconnect(struct sd *sd) +{ + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + /* FIXME: Sort the init commands out and put them into tables, this is only for getting the camera to work */ /* FIXME: No error handling for now, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h index 5ea21a1..4de4fa5 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h @@ -114,6 +114,7 @@ static int pb0100_start(struct sd *sd); static int pb0100_init(struct sd *sd); static int pb0100_stop(struct sd *sd); static int pb0100_dump(struct sd *sd); +static void pb0100_disconnect(struct sd *sd); /* V4L2 controls supported by the driver */ static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val); @@ -137,139 +138,12 @@ const struct stv06xx_sensor stv06xx_sensor_pb0100 = { .i2c_addr = 0xba, .i2c_len = 2, - .nctrls = 7, - .ctrls = { -#define GAIN_IDX 0 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128 - }, - .set = pb0100_set_gain, - .get = pb0100_get_gain - }, -#define RED_BALANCE_IDX 1 - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = -255, - .maximum = 255, - .step = 1, - .default_value = 0 - }, - .set = pb0100_set_red_balance, - .get = pb0100_get_red_balance - }, -#define BLUE_BALANCE_IDX 2 - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = -255, - .maximum = 255, - .step = 1, - .default_value = 0 - }, - .set = pb0100_set_blue_balance, - .get = pb0100_get_blue_balance - }, -#define EXPOSURE_IDX 3 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 511, - .step = 1, - .default_value = 12 - }, - .set = pb0100_set_exposure, - .get = pb0100_get_exposure - }, -#define AUTOGAIN_IDX 4 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Automatic Gain and Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = pb0100_set_autogain, - .get = pb0100_get_autogain - }, -#define AUTOGAIN_TARGET_IDX 5 - { - { - .id = V4L2_CTRL_CLASS_USER + 0x1000, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Automatic Gain Target", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128 - }, - .set = pb0100_set_autogain_target, - .get = pb0100_get_autogain_target - }, -#define NATURAL_IDX 6 - { - { - .id = V4L2_CTRL_CLASS_USER + 0x1001, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Natural Light Source", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = pb0100_set_natural, - .get = pb0100_get_natural - }, - }, - .init = pb0100_init, .probe = pb0100_probe, .start = pb0100_start, .stop = pb0100_stop, .dump = pb0100_dump, - - .nmodes = 2, - .modes = { -/* low res / subsample modes disabled as they are only half res horizontal, - halving the vertical resolution does not seem to work */ - { - 320, - 240, - V4L2_PIX_FMT_SGRBG8, - V4L2_FIELD_NONE, - .sizeimage = 320 * 240, - .bytesperline = 320, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = PB0100_CROP_TO_VGA - }, - { - 352, - 288, - V4L2_PIX_FMT_SGRBG8, - V4L2_FIELD_NONE, - .sizeimage = 352 * 288, - .bytesperline = 352, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - }, - } + .disconnect = pb0100_disconnect, }; #endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h index c726dac..e88c42f 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h @@ -41,8 +41,6 @@ extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00; extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020; extern const struct stv06xx_sensor stv06xx_sensor_pb0100; -#define STV06XX_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10) - struct stv06xx_sensor { /* Defines the name of a sensor */ char name[32]; @@ -81,12 +79,6 @@ struct stv06xx_sensor { /* Instructs the sensor to dump all its contents */ int (*dump)(struct sd *sd); - - int nctrls; - struct ctrl ctrls[STV06XX_MAX_CTRLS]; - - char nmodes; - struct v4l2_pix_format modes[]; }; #endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c index 1ca91f2..69c77c9 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c @@ -29,26 +29,92 @@ #include "stv06xx_vv6410.h" +static struct v4l2_pix_format vv6410_mode[] = { + { + 356, + 292, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 356 * 292, + .bytesperline = 356, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + +static const struct ctrl vv6410_ctrl[] = { +#define HFLIP_IDX 0 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = vv6410_set_hflip, + .get = vv6410_get_hflip + }, +#define VFLIP_IDX 1 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = vv6410_set_vflip, + .get = vv6410_get_vflip + }, +#define GAIN_IDX 2 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "analog gain", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = 0 + }, + .set = vv6410_set_analog_gain, + .get = vv6410_get_analog_gain + } +}; + static int vv6410_probe(struct sd *sd) { u16 data; - int err; + int err, i; + s32 *sensor_settings; err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data); - if (err < 0) return -ENODEV; if (data == 0x19) { info("vv6410 sensor detected"); - sd->gspca_dev.cam.cam_mode = stv06xx_sensor_vv6410.modes; - sd->gspca_dev.cam.nmodes = stv06xx_sensor_vv6410.nmodes; - sd->desc.ctrls = stv06xx_sensor_vv6410.ctrls; - sd->desc.nctrls = stv06xx_sensor_vv6410.nctrls; + sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32), + GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = vv6410_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode); + sd->desc.ctrls = vv6410_ctrl; + sd->desc.nctrls = ARRAY_SIZE(vv6410_ctrl); + + for (i = 0; i < sd->desc.nctrls; i++) + sensor_settings[i] = vv6410_ctrl[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; return 0; } - return -ENODEV; } @@ -80,6 +146,12 @@ static int vv6410_init(struct sd *sd) return (err < 0) ? err : 0; } +static void vv6410_disconnect(struct sd *sd) +{ + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + static int vv6410_start(struct sd *sd) { int err; @@ -156,17 +228,13 @@ static int vv6410_dump(struct sd *sd) static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u16 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); - - *val = (i2c_data & VV6410_HFLIP) ? 1 : 0; - + *val = sensor_settings[HFLIP_IDX]; PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - return (err < 0) ? err : 0; + return 0; } static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) @@ -174,6 +242,9 @@ static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) int err; u16 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[HFLIP_IDX] = val; err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); if (err < 0) return err; @@ -191,17 +262,13 @@ static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u16 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); - - *val = (i2c_data & VV6410_VFLIP) ? 1 : 0; - + *val = sensor_settings[VFLIP_IDX]; PDEBUG(D_V4L2, "Read vertical flip %d", *val); - return (err < 0) ? err : 0; + return 0; } static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) @@ -209,6 +276,9 @@ static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) int err; u16 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[VFLIP_IDX] = val; err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); if (err < 0) return err; @@ -226,24 +296,23 @@ static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u16 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = stv06xx_read_sensor(sd, VV6410_ANALOGGAIN, &i2c_data); - - *val = i2c_data & 0xf; + *val = sensor_settings[GAIN_IDX]; PDEBUG(D_V4L2, "Read analog gain %d", *val); - return (err < 0) ? err : 0; + return 0; } static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + sensor_settings[GAIN_IDX] = val; PDEBUG(D_V4L2, "Set analog gain to %d", val); err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf)); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h index 3ff8c4e..95ac558 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h @@ -178,6 +178,7 @@ static int vv6410_start(struct sd *sd); static int vv6410_init(struct sd *sd); static int vv6410_stop(struct sd *sd); static int vv6410_dump(struct sd *sd); +static void vv6410_disconnect(struct sd *sd); /* V4L2 controls supported by the driver */ static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); @@ -197,62 +198,7 @@ const struct stv06xx_sensor stv06xx_sensor_vv6410 = { .start = vv6410_start, .stop = vv6410_stop, .dump = vv6410_dump, - - .nctrls = 3, - .ctrls = { - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = vv6410_set_hflip, - .get = vv6410_get_hflip - }, { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = vv6410_set_vflip, - .get = vv6410_get_vflip - }, { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "analog gain", - .minimum = 0, - .maximum = 15, - .step = 1, - .default_value = 0 - }, - .set = vv6410_set_analog_gain, - .get = vv6410_get_analog_gain - } - }, - - .nmodes = 1, - .modes = { - { - 356, - 292, - V4L2_PIX_FMT_SGRBG8, - V4L2_FIELD_NONE, - .sizeimage = - 356 * 292, - .bytesperline = 356, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - } - } + .disconnect = vv6410_disconnect, }; /* If NULL, only single value to write, stored in len */ diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 6d904d5..c2b8c10 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -39,8 +39,11 @@ struct sd { unsigned char contrast; unsigned char colors; unsigned char autogain; + u8 quality; +#define QUALITY_MIN 70 +#define QUALITY_MAX 95 +#define QUALITY_DEF 85 - char qindex; char bridge; #define BRIDGE_SPCA504 0 #define BRIDGE_SPCA504B 1 @@ -52,6 +55,8 @@ struct sd { #define LogitechClickSmart420 2 #define LogitechClickSmart820 3 #define MegapixV4 4 + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -812,7 +817,6 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->epaddr = 0x01; sd->bridge = id->driver_info >> 8; sd->subtype = id->driver_info; @@ -850,10 +854,10 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->nmodes = sizeof vga_mode2 / sizeof vga_mode2[0]; break; } - sd->qindex = 5; /* set the quantization table */ 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; + sd->quality = QUALITY_DEF; return 0; } @@ -970,6 +974,12 @@ static int sd_start(struct gspca_dev *gspca_dev) __u8 i; __u8 info[6]; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + if (sd->bridge == BRIDGE_SPCA504B) spca504B_setQtable(gspca_dev); spca504B_SetSizeType(gspca_dev); @@ -1079,6 +1089,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -1155,9 +1172,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, ffd9, 2); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, - ((struct sd *) gspca_dev)->qindex, - 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); } /* add 0x00 after 0xff */ @@ -1198,26 +1214,6 @@ static void setbrightness(struct gspca_dev *gspca_dev) } } -static void getbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - __u16 brightness = 0; - - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA504: */ -/* case BRIDGE_SPCA504C: */ - brightness = reg_r_12(gspca_dev, 0x00, 0x21a7, 2); - break; - case BRIDGE_SPCA536: - brightness = reg_r_12(gspca_dev, 0x00, 0x20f0, 2); - break; - } - sd->brightness = ((brightness & 0xff) - 128) % 255; -} - static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1237,24 +1233,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) } } -static void getcontrast(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA504: */ -/* case BRIDGE_SPCA504C: */ - sd->contrast = reg_r_12(gspca_dev, 0x00, 0x21a8, 2); - break; - case BRIDGE_SPCA536: - sd->contrast = reg_r_12(gspca_dev, 0x00, 0x20f1, 2); - break; - } -} - static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1274,24 +1252,6 @@ static void setcolors(struct gspca_dev *gspca_dev) } } -static void getcolors(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - switch (sd->bridge) { - default: -/* case BRIDGE_SPCA533: */ -/* case BRIDGE_SPCA504B: */ -/* case BRIDGE_SPCA504: */ -/* case BRIDGE_SPCA504C: */ - sd->colors = reg_r_12(gspca_dev, 0x00, 0x21ae, 2) >> 1; - break; - case BRIDGE_SPCA536: - sd->colors = reg_r_12(gspca_dev, 0x00, 0x20f6, 2) >> 1; - break; - } -} - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1306,7 +1266,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getbrightness(gspca_dev); *val = sd->brightness; return 0; } @@ -1325,7 +1284,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcontrast(gspca_dev); *val = sd->contrast; return 0; } @@ -1344,7 +1302,6 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - getcolors(gspca_dev); *val = sd->colors; return 0; } @@ -1365,6 +1322,34 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + if (gspca_dev->streaming) + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -1374,7 +1359,10 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ @@ -1465,8 +1453,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 6ee111a..f63e37e 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -37,20 +37,21 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; - unsigned char contrast; - unsigned char colors; - unsigned char autogain; - unsigned char gamma; - unsigned char sharpness; - unsigned char freq; - unsigned char whitebalance; - unsigned char mirror; - unsigned char effect; - - __u8 sensor; -#define SENSOR_TAS5130A 0 -#define SENSOR_OM6802 1 + u8 brightness; + u8 contrast; + u8 colors; + u8 autogain; + u8 gamma; + u8 sharpness; + u8 freq; + u8 whitebalance; + u8 mirror; + u8 effect; + + u8 sensor; +#define SENSOR_OM6802 0 +#define SENSOR_OTHER 1 +#define SENSOR_TAS5130A 2 }; /* V4L2 controls supported by the driver */ @@ -78,7 +79,6 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu); static struct ctrl sd_ctrls[] = { -#define SD_BRIGHTNESS 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -87,12 +87,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 14, .step = 1, - .default_value = 8, +#define BRIGHTNESS_DEF 8 + .default_value = BRIGHTNESS_DEF, }, .set = sd_setbrightness, .get = sd_getbrightness, }, -#define SD_CONTRAST 1 { { .id = V4L2_CID_CONTRAST, @@ -101,12 +101,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0x0d, .step = 1, - .default_value = 0x07, +#define CONTRAST_DEF 0x07 + .default_value = CONTRAST_DEF, }, .set = sd_setcontrast, .get = sd_getcontrast, }, -#define SD_COLOR 2 { { .id = V4L2_CID_SATURATION, @@ -115,7 +115,8 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 0x0f, .step = 1, - .default_value = 0x05, +#define COLORS_DEF 0x05 + .default_value = COLORS_DEF, }, .set = sd_setcolors, .get = sd_getcolors, @@ -135,7 +136,6 @@ static struct ctrl sd_ctrls[] = { .set = sd_setgamma, .get = sd_getgamma, }, -#define SD_AUTOGAIN 4 { { .id = V4L2_CID_GAIN, /* here, i activate only the lowlight, @@ -146,12 +146,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, - .default_value = 0x01, +#define AUTOGAIN_DEF 0x01 + .default_value = AUTOGAIN_DEF, }, .set = sd_setlowlight, .get = sd_getlowlight, }, -#define SD_MIRROR 5 { { .id = V4L2_CID_HFLIP, @@ -160,12 +160,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, - .default_value = 0, +#define MIRROR_DEF 0 + .default_value = MIRROR_DEF, }, .set = sd_setflip, .get = sd_getflip }, -#define SD_LIGHTFREQ 6 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -174,12 +174,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 1, /* 1 -> 0x50, 2->0x60 */ .maximum = 2, .step = 1, - .default_value = 1, +#define FREQ_DEF 1 + .default_value = FREQ_DEF, }, .set = sd_setfreq, .get = sd_getfreq}, -#define SD_WHITE_BALANCE 7 { { .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, @@ -188,12 +188,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, - .default_value = 0, +#define WHITE_BALANCE_DEF 0 + .default_value = WHITE_BALANCE_DEF, }, .set = sd_setwhitebalance, .get = sd_getwhitebalance }, -#define SD_SHARPNESS 8 /* (aka definition on win) */ { { .id = V4L2_CID_SHARPNESS, @@ -202,12 +202,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 15, .step = 1, - .default_value = 0x06, +#define SHARPNESS_DEF 0x06 + .default_value = SHARPNESS_DEF, }, .set = sd_setsharpness, .get = sd_getsharpness, }, -#define SD_EFFECTS 9 { { .id = V4L2_CID_EFFECTS, @@ -216,7 +216,8 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 4, .step = 1, - .default_value = 0, +#define EFFECTS_DEF 0 + .default_value = EFFECTS_DEF, }, .set = sd_seteffect, .get = sd_geteffect @@ -263,28 +264,50 @@ static const struct v4l2_pix_format vga_mode_t16[] = { /* sensor specific data */ struct additional_sensor_data { - const __u8 data1[20]; - const __u8 data2[18]; - const __u8 data3[18]; - const __u8 data4[4]; - const __u8 data5[6]; - const __u8 stream[4]; + const u8 data1[10]; + const u8 data2[9]; + const u8 data3[9]; + const u8 data4[4]; + const u8 data5[6]; + const u8 stream[4]; }; -const static struct additional_sensor_data sensor_data[] = { +static const struct additional_sensor_data sensor_data[] = { + { /* OM6802 */ + .data1 = + {0xc2, 0x28, 0x0f, 0x22, 0xcd, 0x27, 0x2c, 0x06, + 0xb3, 0xfc}, + .data2 = + {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 */ + {0x0c, 0x03, 0xab, 0x13, 0x81, 0x23}, + .stream = + {0x0b, 0x04, 0x0a, 0x78}, + }, + { /* OTHER */ + .data1 = + {0xc1, 0x48, 0x04, 0x1b, 0xca, 0x2e, 0x33, 0x3a, + 0xe8, 0xfc}, + .data2 = + {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96, + 0xd9}, + .data4 = + {0x66, 0x00, 0xa8, 0xa8}, + .data5 = + {0x0c, 0x03, 0xab, 0x29, 0x81, 0x69}, + .stream = + {0x0b, 0x04, 0x0a, 0x00}, + }, { /* TAS5130A */ .data1 = - {0xd0, 0xbb, 0xd1, 0x28, 0xd2, 0x10, 0xd3, 0x10, - 0xd4, 0xbb, 0xd5, 0x28, 0xd6, 0x1e, 0xd7, 0x27, - 0xd8, 0xc8, 0xd9, 0xfc}, + {0xbb, 0x28, 0x10, 0x10, 0xbb, 0x28, 0x1e, 0x27, + 0xc8, 0xfc}, .data2 = - {0xe0, 0x60, 0xe1, 0xa8, 0xe2, 0xe0, 0xe3, 0x60, - 0xe4, 0xa8, 0xe5, 0xe0, 0xe6, 0x60, 0xe7, 0xa8, - 0xe8, 0xe0}, - .data3 = - {0xc7, 0x60, 0xc8, 0xa8, 0xc9, 0xe0, 0xca, 0x60, - 0xcb, 0xa8, 0xcc, 0xe0, 0xcd, 0x60, 0xce, 0xa8, - 0xcf, 0xe0}, + {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8, + 0xe0}, .data4 = /* Freq (50/60Hz). Splitted for test purpose */ {0x66, 0x00, 0xa8, 0xe8}, .data5 = @@ -292,32 +315,12 @@ const static struct additional_sensor_data sensor_data[] = { .stream = {0x0b, 0x04, 0x0a, 0x40}, }, - { /* OM6802 */ - .data1 = - {0xd0, 0xc2, 0xd1, 0x28, 0xd2, 0x0f, 0xd3, 0x22, - 0xd4, 0xcd, 0xd5, 0x27, 0xd6, 0x2c, 0xd7, 0x06, - 0xd8, 0xb3, 0xd9, 0xfc}, - .data2 = - {0xe0, 0x80, 0xe1, 0xff, 0xe2, 0xff, 0xe3, 0x80, - 0xe4, 0xff, 0xe5, 0xff, 0xe6, 0x80, 0xe7, 0xff, - 0xe8, 0xff}, - .data3 = - {0xc7, 0x80, 0xc8, 0xff, 0xc9, 0xff, 0xca, 0x80, - 0xcb, 0xff, 0xcc, 0xff, 0xcd, 0x80, 0xce, 0xff, - 0xcf, 0xff}, - .data4 = /*Freq (50/60Hz). Splitted for test purpose */ - {0x66, 0xca, 0xa8, 0xf0 }, - .data5 = /* this could be removed later */ - {0x0c, 0x03, 0xab, 0x13, 0x81, 0x23}, - .stream = - {0x0b, 0x04, 0x0a, 0x78}, - } }; #define MAX_EFFECTS 7 /* easily done by soft, this table could be removed, * i keep it here just in case */ -static const __u8 effects_table[MAX_EFFECTS][6] = { +static const u8 effects_table[MAX_EFFECTS][6] = { {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */ {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */ {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x20}, /* Monochrome */ @@ -327,90 +330,58 @@ static const __u8 effects_table[MAX_EFFECTS][6] = { {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40}, /* Negative */ }; -static const __u8 gamma_table[GAMMA_MAX][34] = { - {0x90, 0x00, 0x91, 0x3e, 0x92, 0x69, 0x93, 0x85, /* 0 */ - 0x94, 0x95, 0x95, 0xa1, 0x96, 0xae, 0x97, 0xb9, - 0x98, 0xc2, 0x99, 0xcb, 0x9a, 0xd4, 0x9b, 0xdb, - 0x9c, 0xe3, 0x9d, 0xea, 0x9e, 0xf1, 0x9f, 0xf8, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x33, 0x92, 0x5a, 0x93, 0x75, /* 1 */ - 0x94, 0x85, 0x95, 0x93, 0x96, 0xa1, 0x97, 0xad, - 0x98, 0xb7, 0x99, 0xc2, 0x9a, 0xcb, 0x9b, 0xd4, - 0x9c, 0xde, 0x9D, 0xe7, 0x9e, 0xf0, 0x9f, 0xf7, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x2f, 0x92, 0x51, 0x93, 0x6b, /* 2 */ - 0x94, 0x7c, 0x95, 0x8a, 0x96, 0x99, 0x97, 0xa6, - 0x98, 0xb1, 0x99, 0xbc, 0x9a, 0xc6, 0x9b, 0xd0, - 0x9c, 0xdb, 0x9d, 0xe4, 0x9e, 0xed, 0x9f, 0xf6, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x29, 0x92, 0x48, 0x93, 0x60, /* 3 */ - 0x94, 0x72, 0x95, 0x81, 0x96, 0x90, 0x97, 0x9e, - 0x98, 0xaa, 0x99, 0xb5, 0x9a, 0xbf, 0x9b, 0xcb, - 0x9c, 0xd6, 0x9d, 0xe1, 0x9e, 0xeb, 0x9f, 0xf5, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x23, 0x92, 0x3f, 0x93, 0x55, /* 4 */ - 0x94, 0x68, 0x95, 0x77, 0x96, 0x86, 0x97, 0x95, - 0x98, 0xa2, 0x99, 0xad, 0x9a, 0xb9, 0x9b, 0xc6, - 0x9c, 0xd2, 0x9d, 0xde, 0x9e, 0xe9, 0x9f, 0xf4, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x1b, 0x92, 0x33, 0x93, 0x48, /* 5 */ - 0x94, 0x59, 0x95, 0x69, 0x96, 0x79, 0x97, 0x87, - 0x98, 0x96, 0x99, 0xa3, 0x9a, 0xb1, 0x9b, 0xbe, - 0x9c, 0xcc, 0x9d, 0xda, 0x9e, 0xe7, 0x9f, 0xf3, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x02, 0x92, 0x10, 0x93, 0x20, /* 6 */ - 0x94, 0x32, 0x95, 0x40, 0x96, 0x57, 0x97, 0x67, - 0x98, 0x77, 0x99, 0x88, 0x9a, 0x99, 0x9b, 0xaa, - 0x9c, 0xbb, 0x9d, 0xcc, 0x9e, 0xdd, 0x9f, 0xee, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x02, 0x92, 0x14, 0x93, 0x26, /* 7 */ - 0x94, 0x38, 0x95, 0x4a, 0x96, 0x60, 0x97, 0x70, - 0x98, 0x80, 0x99, 0x90, 0x9a, 0xa0, 0x9b, 0xb0, - 0x9c, 0xc0, 0x9D, 0xd0, 0x9e, 0xe0, 0x9f, 0xf0, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x10, 0x92, 0x22, 0x93, 0x35, /* 8 */ - 0x94, 0x47, 0x95, 0x5a, 0x96, 0x69, 0x97, 0x79, - 0x98, 0x88, 0x99, 0x97, 0x9a, 0xa7, 0x9b, 0xb6, - 0x9c, 0xc4, 0x9d, 0xd3, 0x9e, 0xe0, 0x9f, 0xf0, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x10, 0x92, 0x26, 0x93, 0x40, /* 9 */ - 0x94, 0x54, 0x95, 0x65, 0x96, 0x75, 0x97, 0x84, - 0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd, - 0x9c, 0xca, 0x9d, 0xd6, 0x9e, 0xe0, 0x9f, 0xf0, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x18, 0x92, 0x2b, 0x93, 0x44, /* 10 */ - 0x94, 0x60, 0x95, 0x70, 0x96, 0x80, 0x97, 0x8e, - 0x98, 0x9c, 0x99, 0xaa, 0x9a, 0xb7, 0x9b, 0xc4, - 0x9c, 0xd0, 0x9d, 0xd8, 0x9e, 0xe2, 0x9f, 0xf0, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x1a, 0x92, 0x34, 0x93, 0x52, /* 11 */ - 0x94, 0x66, 0x95, 0x7e, 0x96, 0x8D, 0x97, 0x9B, - 0x98, 0xa8, 0x99, 0xb4, 0x9a, 0xc0, 0x9b, 0xcb, - 0x9c, 0xd6, 0x9d, 0xe1, 0x9e, 0xeb, 0x9f, 0xf5, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x3f, 0x92, 0x5a, 0x93, 0x6e, /* 12 */ - 0x94, 0x7f, 0x95, 0x8e, 0x96, 0x9c, 0x97, 0xa8, - 0x98, 0xb4, 0x99, 0xbf, 0x9a, 0xc9, 0x9b, 0xd3, - 0x9c, 0xdc, 0x9d, 0xe5, 0x9e, 0xee, 0x9f, 0xf6, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x54, 0x92, 0x6f, 0x93, 0x83, /* 13 */ - 0x94, 0x93, 0x95, 0xa0, 0x96, 0xad, 0x97, 0xb7, - 0x98, 0xc2, 0x99, 0xcb, 0x9a, 0xd4, 0x9b, 0xdc, - 0x9c, 0xe4, 0x9d, 0xeb, 0x9e, 0xf2, 0x9f, 0xf9, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x6e, 0x92, 0x88, 0x93, 0x9a, /* 14 */ - 0x94, 0xa8, 0x95, 0xb3, 0x96, 0xbd, 0x97, 0xc6, - 0x98, 0xcf, 0x99, 0xd6, 0x9a, 0xdd, 0x9b, 0xe3, - 0x9c, 0xe9, 0x9d, 0xef, 0x9e, 0xf4, 0x9f, 0xfa, - 0xa0, 0xff}, - {0x90, 0x00, 0x91, 0x93, 0x92, 0xa8, 0x93, 0xb7, /* 15 */ - 0x94, 0xc1, 0x95, 0xca, 0x96, 0xd2, 0x97, 0xd8, - 0x98, 0xde, 0x99, 0xe3, 0x9a, 0xe8, 0x9b, 0xed, - 0x9c, 0xf1, 0x9d, 0xf5, 0x9e, 0xf8, 0x9f, 0xfc, - 0xa0, 0xff} +static const u8 gamma_table[GAMMA_MAX][17] = { + {0x00, 0x3e, 0x69, 0x85, 0x95, 0xa1, 0xae, 0xb9, /* 0 */ + 0xc2, 0xcb, 0xd4, 0xdb, 0xe3, 0xea, 0xf1, 0xf8, + 0xff}, + {0x00, 0x33, 0x5a, 0x75, 0x85, 0x93, 0xa1, 0xad, /* 1 */ + 0xb7, 0xc2, 0xcb, 0xd4, 0xde, 0xe7, 0xf0, 0xf7, + 0xff}, + {0x00, 0x2f, 0x51, 0x6b, 0x7c, 0x8a, 0x99, 0xa6, /* 2 */ + 0xb1, 0xbc, 0xc6, 0xd0, 0xdb, 0xe4, 0xed, 0xf6, + 0xff}, + {0x00, 0x29, 0x48, 0x60, 0x72, 0x81, 0x90, 0x9e, /* 3 */ + 0xaa, 0xb5, 0xbf, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5, + 0xff}, + {0x00, 0x23, 0x3f, 0x55, 0x68, 0x77, 0x86, 0x95, /* 4 */ + 0xa2, 0xad, 0xb9, 0xc6, 0xd2, 0xde, 0xe9, 0xf4, + 0xff}, + {0x00, 0x1b, 0x33, 0x48, 0x59, 0x69, 0x79, 0x87, /* 5 */ + 0x96, 0xa3, 0xb1, 0xbe, 0xcc, 0xda, 0xe7, 0xf3, + 0xff}, + {0x00, 0x02, 0x10, 0x20, 0x32, 0x40, 0x57, 0x67, /* 6 */ + 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, + 0xff}, + {0x00, 0x02, 0x14, 0x26, 0x38, 0x4a, 0x60, 0x70, /* 7 */ + 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0, + 0xff}, + {0x00, 0x10, 0x22, 0x35, 0x47, 0x5a, 0x69, 0x79, /* 8 */ + 0x88, 0x97, 0xa7, 0xb6, 0xc4, 0xd3, 0xe0, 0xf0, + 0xff}, + {0x00, 0x10, 0x26, 0x40, 0x54, 0x65, 0x75, 0x84, /* 9 */ + 0x93, 0xa1, 0xb0, 0xbd, 0xca, 0xd6, 0xe0, 0xf0, + 0xff}, + {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 */ + 0xa8, 0xb4, 0xc0, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5, + 0xff}, + {0x00, 0x3f, 0x5a, 0x6e, 0x7f, 0x8e, 0x9c, 0xa8, /* 12 */ + 0xb4, 0xbf, 0xc9, 0xd3, 0xdc, 0xe5, 0xee, 0xf6, + 0xff}, + {0x00, 0x54, 0x6f, 0x83, 0x93, 0xa0, 0xad, 0xb7, /* 13 */ + 0xc2, 0xcb, 0xd4, 0xdc, 0xe4, 0xeb, 0xf2, 0xf9, + 0xff}, + {0x00, 0x6e, 0x88, 0x9a, 0xa8, 0xb3, 0xbd, 0xc6, /* 14 */ + 0xcf, 0xd6, 0xdd, 0xe3, 0xe9, 0xef, 0xf4, 0xfa, + 0xff}, + {0x00, 0x93, 0xa8, 0xb7, 0xc1, 0xca, 0xd2, 0xd8, /* 15 */ + 0xde, 0xe3, 0xe8, 0xed, 0xf1, 0xf5, 0xf8, 0xfc, + 0xff} }; -static const __u8 tas5130a_sensor_init[][8] = { +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}, @@ -418,11 +389,11 @@ static const __u8 tas5130a_sensor_init[][8] = { {}, }; -static __u8 sensor_reset[] = {0x61, 0x68, 0x62, 0xff, 0x60, 0x07}; +static u8 sensor_reset[] = {0x61, 0x68, 0x62, 0xff, 0x60, 0x07}; /* read 1 byte */ -static int reg_r(struct gspca_dev *gspca_dev, - __u16 index) +static u8 reg_r(struct gspca_dev *gspca_dev, + u16 index) { usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), @@ -435,7 +406,7 @@ static int reg_r(struct gspca_dev *gspca_dev, } static void reg_w(struct gspca_dev *gspca_dev, - __u16 index) + u16 index) { usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -446,7 +417,7 @@ static void reg_w(struct gspca_dev *gspca_dev, } static void reg_w_buf(struct gspca_dev *gspca_dev, - const __u8 *buffer, __u16 len) + const u8 *buffer, u16 len) { if (len <= USB_BUF_SZ) { memcpy(gspca_dev->usb_buf, buffer, len); @@ -457,7 +428,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, 0x01, 0, gspca_dev->usb_buf, len, 500); } else { - __u8 *tmpbuf; + u8 *tmpbuf; tmpbuf = kmalloc(len, GFP_KERNEL); memcpy(tmpbuf, buffer, len); @@ -471,14 +442,41 @@ static void reg_w_buf(struct gspca_dev *gspca_dev, } } +/* write values to consecutive registers */ +static void reg_w_ixbuf(struct gspca_dev *gspca_dev, + u8 reg, + const u8 *buffer, u16 len) +{ + int i; + u8 *p, *tmpbuf; + + if (len * 2 <= USB_BUF_SZ) + p = tmpbuf = gspca_dev->usb_buf; + else + p = tmpbuf = kmalloc(len * 2, GFP_KERNEL); + i = len; + while (--i >= 0) { + *p++ = reg++; + *p++ = *buffer++; + } + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x01, 0, + tmpbuf, len * 2, 500); + if (len * 2 > USB_BUF_SZ) + kfree(tmpbuf); +} + /* Reported as OM6802*/ static void om6802_sensor_init(struct gspca_dev *gspca_dev) { int i; - const __u8 *p; - __u8 byte; - __u8 val[6] = {0x62, 0, 0x64, 0, 0x60, 0x05}; - static const __u8 sensor_init[] = { + const u8 *p; + u8 byte; + u8 val[6] = {0x62, 0, 0x64, 0, 0x60, 0x05}; + static const u8 sensor_init[] = { 0xdf, 0x6d, 0xdd, 0x18, 0x5a, 0xe0, @@ -497,7 +495,7 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev) }; reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset); - msleep(5); + msleep(100); i = 4; while (--i > 0) { byte = reg_r(gspca_dev, 0x0060); @@ -538,20 +536,20 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->epaddr = 0x01; cam->cam_mode = vga_mode_t16; cam->nmodes = ARRAY_SIZE(vga_mode_t16); - 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; + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLORS_DEF; sd->gamma = GAMMA_DEF; - sd->mirror = sd_ctrls[SD_MIRROR].qctrl.default_value; - sd->freq = sd_ctrls[SD_LIGHTFREQ].qctrl.default_value; - sd->whitebalance = sd_ctrls[SD_WHITE_BALANCE].qctrl.default_value; - sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value; - sd->effect = sd_ctrls[SD_EFFECTS].qctrl.default_value; + sd->autogain = AUTOGAIN_DEF; + sd->mirror = MIRROR_DEF; + sd->freq = FREQ_DEF; + sd->whitebalance = WHITE_BALANCE_DEF; + sd->sharpness = SHARPNESS_DEF; + sd->effect = EFFECTS_DEF; return 0; } @@ -559,7 +557,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; unsigned int brightness; - __u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 }; + u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 }; brightness = sd->brightness; if (brightness < 7) { @@ -576,7 +574,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; unsigned int contrast = sd->contrast; - __u16 reg_to_write; + u16 reg_to_write; if (contrast < 7) reg_to_write = 0x8ea9 - contrast * 0x200; @@ -589,7 +587,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u16 reg_to_write; + u16 reg_to_write; reg_to_write = 0x80bb + sd->colors * 0x100; /* was 0xc0 */ reg_w(gspca_dev, reg_to_write); @@ -600,14 +598,15 @@ static void setgamma(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; PDEBUG(D_CONF, "Gamma: %d", sd->gamma); - reg_w_buf(gspca_dev, gamma_table[sd->gamma], sizeof gamma_table[0]); + reg_w_ixbuf(gspca_dev, 0x90, + gamma_table[sd->gamma], sizeof gamma_table[0]); } static void setwhitebalance(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 white_balance[8] = + u8 white_balance[8] = {0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38}; if (sd->whitebalance) @@ -619,7 +618,7 @@ static void setwhitebalance(struct gspca_dev *gspca_dev) static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u16 reg_to_write; + u16 reg_to_write; reg_to_write = 0x0aa6 + 0x1000 * sd->sharpness; @@ -635,18 +634,22 @@ static int sd_init(struct gspca_dev *gspca_dev) * to see the initial parameters.*/ struct sd *sd = (struct sd *) gspca_dev; int i; - __u8 byte, test_byte; - - static const __u8 read_indexs[] = - { 0x06, 0x07, 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5, - 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00, 0x00 }; - static const __u8 n1[] = + u16 sensor_id; + u8 test_byte = 0; + u16 reg80, reg8e; + + static const u8 read_indexs[] = + { 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5, + 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00 }; + static const u8 n1[] = {0x08, 0x03, 0x09, 0x03, 0x12, 0x04}; - static const __u8 n2[] = + static const u8 n2[] = {0x08, 0x00}; - static const __u8 n3[] = + static const u8 n3[6] = {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04}; - static const __u8 n4[] = + 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, @@ -656,40 +659,61 @@ static int sd_init(struct gspca_dev *gspca_dev) 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 nset9[4] = - { 0x0b, 0x04, 0x0a, 0x78 }; - static const __u8 nset8[6] = + 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 }; - - byte = reg_r(gspca_dev, 0x06); - test_byte = reg_r(gspca_dev, 0x07); - if (byte == 0x08 && test_byte == 0x07) { - PDEBUG(D_CONF, "sensor om6802"); - sd->sensor = SENSOR_OM6802; - } else if (byte == 0x08 && test_byte == 0x01) { - PDEBUG(D_CONF, "sensor tas5130a"); - sd->sensor = SENSOR_TAS5130A; - } else { - PDEBUG(D_CONF, "unknown sensor %02x %02x", byte, test_byte); + 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); + switch (sensor_id & 0xff0f) { + case 0x0801: + PDEBUG(D_PROBE, "sensor tas5130a"); sd->sensor = SENSOR_TAS5130A; + break; + case 0x0803: + PDEBUG(D_PROBE, "sensor 'other'"); + sd->sensor = SENSOR_OTHER; + break; + case 0x0807: + PDEBUG(D_PROBE, "sensor om6802"); + sd->sensor = SENSOR_OM6802; + break; + default: + PDEBUG(D_ERR|D_PROBE, "unknown sensor %04x", sensor_id); + return -EINVAL; } - reg_w_buf(gspca_dev, n1, sizeof n1); - test_byte = 0; - i = 5; - while (--i >= 0) { - reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset); - test_byte = reg_r(gspca_dev, 0x0063); - msleep(100); - if (test_byte == 0x17) - break; /* OK */ - } - if (i < 0) { - err("Bad sensor reset %02x", test_byte); -/* return -EIO; */ + if (sd->sensor != SENSOR_OTHER) { + reg_w_buf(gspca_dev, n1, sizeof n1); + i = 5; + while (--i >= 0) { + reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset); + test_byte = reg_r(gspca_dev, 0x0063); + msleep(100); + if (test_byte == 0x17) + break; /* OK */ + } + if (i < 0) { + err("Bad sensor reset %02x", test_byte); +/* return -EIO; */ /*fixme: test - continue */ + } + reg_w_buf(gspca_dev, n2, sizeof n2); } - reg_w_buf(gspca_dev, n2, sizeof n2); i = 0; while (read_indexs[i] != 0x00) { @@ -699,21 +723,31 @@ static int sd_init(struct gspca_dev *gspca_dev) i++; } - 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); + 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; + } - reg_w_buf(gspca_dev, sensor_data[sd->sensor].data1, + reg_w_ixbuf(gspca_dev, 0xd0, sensor_data[sd->sensor].data1, sizeof sensor_data[sd->sensor].data1); - reg_w_buf(gspca_dev, sensor_data[sd->sensor].data3, - sizeof sensor_data[sd->sensor].data3); - reg_w_buf(gspca_dev, sensor_data[sd->sensor].data2, + 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(gspca_dev, 0x3880); - reg_w(gspca_dev, 0x3880); - reg_w(gspca_dev, 0x338e); + reg_w(gspca_dev, reg80); + reg_w(gspca_dev, reg80); + reg_w(gspca_dev, reg8e); setbrightness(gspca_dev); setcontrast(gspca_dev); @@ -730,16 +764,20 @@ static int sd_init(struct gspca_dev *gspca_dev) sizeof sensor_data[sd->sensor].data4); reg_w_buf(gspca_dev, sensor_data[sd->sensor].data5, sizeof sensor_data[sd->sensor].data5); - reg_w_buf(gspca_dev, nset8, sizeof nset8); - reg_w_buf(gspca_dev, nset9, sizeof nset9); - - reg_w(gspca_dev, 0x2880); + 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_data[sd->sensor].data1, + reg_w_ixbuf(gspca_dev, 0xd0, sensor_data[sd->sensor].data1, sizeof sensor_data[sd->sensor].data1); - reg_w_buf(gspca_dev, sensor_data[sd->sensor].data3, - sizeof sensor_data[sd->sensor].data3); - reg_w_buf(gspca_dev, sensor_data[sd->sensor].data2, + 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); return 0; @@ -748,7 +786,7 @@ static int sd_init(struct gspca_dev *gspca_dev) static void setflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 flipcmd[8] = + u8 flipcmd[8] = {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09}; if (sd->mirror) @@ -778,7 +816,7 @@ static void seteffect(struct gspca_dev *gspca_dev) static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 freq[4] = { 0x66, 0x40, 0xa8, 0xe8 }; + u8 freq[4] = { 0x66, 0x40, 0xa8, 0xe8 }; if (sd->freq == 2) /* 60hz */ freq[1] = 0x00; @@ -791,22 +829,22 @@ static void setlightfreq(struct gspca_dev *gspca_dev) static void poll_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - static const __u8 poll1[] = + static const u8 poll1[] = {0x67, 0x05, 0x68, 0x81, 0x69, 0x80, 0x6a, 0x82, 0x6b, 0x68, 0x6c, 0x69, 0x72, 0xd9, 0x73, 0x34, 0x74, 0x32, 0x75, 0x92, 0x76, 0x00, 0x09, 0x01, 0x60, 0x14}; - static const __u8 poll2[] = + static const u8 poll2[] = {0x67, 0x02, 0x68, 0x71, 0x69, 0x72, 0x72, 0xa9, 0x73, 0x02, 0x73, 0x02, 0x60, 0x14}; - static const __u8 poll3[] = + static const u8 poll3[] = {0x87, 0x3f, 0x88, 0x20, 0x89, 0x2d}; - static const __u8 poll4[] = + static const u8 poll4[] = {0xa6, 0x0a, 0xea, 0xcf, 0xbe, 0x26, 0xb1, 0x5f, 0xa1, 0xb1, 0xda, 0x6b, 0xdb, 0x98, 0xdf, 0x0c, 0xc2, 0x80, 0xc3, 0x10}; - if (sd->sensor != SENSOR_TAS5130A) { + 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); @@ -819,13 +857,14 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i, mode; - __u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 }; - static const __u8 t3[] = - { 0xb3, 0x07, 0xb4, 0x00, 0xb5, 0x88, 0xb6, 0x02, 0xb7, 0x06, - 0xb8, 0x00, 0xb9, 0xe7, 0xba, 0x01 }; + 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; switch (mode) { + case 0: /* 640x480 (0x00) */ + break; case 1: /* 352x288 */ t2[1] = 0x40; break; @@ -835,14 +874,20 @@ static int sd_start(struct gspca_dev *gspca_dev) case 3: /* 176x144 */ t2[1] = 0x50; break; - case 4: /* 160x120 */ + default: +/* case 4: * 160x120 */ t2[1] = 0x20; break; - default: /* 640x480 (0x00) */ - break; } - if (sd->sensor == SENSOR_TAS5130A) { + switch (sd->sensor) { + case SENSOR_OM6802: + om6802_sensor_init(gspca_dev); + break; + case SENSOR_OTHER: + break; + default: +/* case SENSOR_TAS5130A: */ i = 0; while (tas5130a_sensor_init[i][0] != 0) { reg_w_buf(gspca_dev, tas5130a_sensor_init[i], @@ -854,14 +899,13 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w_buf(gspca_dev, tas5130a_sensor_init[3], sizeof tas5130a_sensor_init[0]); reg_w(gspca_dev, 0x3c80); - } else { - om6802_sensor_init(gspca_dev); + break; } reg_w_buf(gspca_dev, sensor_data[sd->sensor].data4, sizeof sensor_data[sd->sensor].data4); reg_r(gspca_dev, 0x0012); reg_w_buf(gspca_dev, t2, sizeof t2); - reg_w_buf(gspca_dev, t3, sizeof t3); + 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, @@ -885,16 +929,18 @@ static void sd_stopN(struct gspca_dev *gspca_dev) msleep(20); reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream, sizeof sensor_data[sd->sensor].stream); - msleep(20); - reg_w(gspca_dev, 0x0309); + if (sd->sensor != SENSOR_OTHER) { + msleep(20); + reg_w(gspca_dev, 0x0309); + } } 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 packet length */ { - static __u8 ffd9[] = { 0xff, 0xd9 }; + static u8 ffd9[] = { 0xff, 0xd9 }; if (data[0] == 0x5a) { /* Control Packet, after this came the header again, @@ -1172,8 +1218,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index 94163cc..9f243d7 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -31,7 +31,6 @@ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ __u16 brightness; - __u16 contrast; __u8 packet; }; @@ -39,38 +38,22 @@ struct sd { /* V4L2 controls supported by the driver */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { -#define SD_BRIGHTNESS 0 { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Brightness", .minimum = 1, - .maximum = 0x2ff, + .maximum = 0x15f, /* = 352 - 1 */ .step = 1, - .default_value = 0x18f, +#define BRIGHTNESS_DEF 0x14c + .default_value = BRIGHTNESS_DEF, }, .set = sd_setbrightness, .get = sd_getbrightness, }, -#define SD_CONTRAST 1 - { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 0xffff, - .step = 1, - .default_value = 0x7fff, - }, - .set = sd_setcontrast, - .get = sd_getcontrast, - }, }; static const struct v4l2_pix_format sif_mode[] = { @@ -86,78 +69,64 @@ static const struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; -/* - * Initialization data: this is the first set-up data written to the - * device (before the open data). - */ -#define TESTCLK 0x10 /* reg 0x2c -> 0x12 //10 */ -#define TESTCOMP 0x90 /* reg 0x28 -> 0x80 */ -#define TESTLINE 0x81 /* reg 0x29 -> 0x81 */ -#define QCIFLINE 0x41 /* reg 0x29 -> 0x81 */ -#define TESTPTL 0x14 /* reg 0x2D -> 0x14 */ -#define TESTPTH 0x01 /* reg 0x2E -> 0x01 */ -#define TESTPTBL 0x12 /* reg 0x2F -> 0x0a */ -#define TESTPTBH 0x01 /* reg 0x30 -> 0x01 */ -#define ADWIDTHL 0xe8 /* reg 0x0c -> 0xe8 */ -#define ADWIDTHH 0x03 /* reg 0x0d -> 0x03 */ -#define ADHEIGHL 0x90 /* reg 0x0e -> 0x91 //93 */ -#define ADHEIGHH 0x01 /* reg 0x0f -> 0x01 */ -#define EXPOL 0x8f /* reg 0x1c -> 0x8f */ -#define EXPOH 0x01 /* reg 0x1d -> 0x01 */ -#define ADCBEGINL 0x44 /* reg 0x10 -> 0x46 //47 */ -#define ADCBEGINH 0x00 /* reg 0x11 -> 0x00 */ -#define ADRBEGINL 0x0a /* reg 0x14 -> 0x0b //0x0c */ -#define ADRBEGINH 0x00 /* reg 0x15 -> 0x00 */ -#define TV8532_CMD_UPDATE 0x84 - -#define TV8532_EEprom_Add 0x03 -#define TV8532_EEprom_DataL 0x04 -#define TV8532_EEprom_DataM 0x05 -#define TV8532_EEprom_DataH 0x06 -#define TV8532_EEprom_TableLength 0x07 -#define TV8532_EEprom_Write 0x08 -#define TV8532_PART_CTRL 0x00 -#define TV8532_CTRL 0x01 -#define TV8532_CMD_EEprom_Open 0x30 -#define TV8532_CMD_EEprom_Close 0x29 -#define TV8532_UDP_UPDATE 0x31 -#define TV8532_GPIO 0x39 -#define TV8532_GPIO_OE 0x3B -#define TV8532_REQ_RegWrite 0x02 -#define TV8532_REQ_RegRead 0x03 - -#define TV8532_ADWIDTH_L 0x0C -#define TV8532_ADWIDTH_H 0x0D -#define TV8532_ADHEIGHT_L 0x0E -#define TV8532_ADHEIGHT_H 0x0F -#define TV8532_EXPOSURE 0x1C -#define TV8532_QUANT_COMP 0x28 -#define TV8532_MODE_PACKET 0x29 -#define TV8532_SETCLK 0x2C -#define TV8532_POINT_L 0x2D -#define TV8532_POINT_H 0x2E -#define TV8532_POINTB_L 0x2F -#define TV8532_POINTB_H 0x30 -#define TV8532_BUDGET_L 0x2A -#define TV8532_BUDGET_H 0x2B -#define TV8532_VID_L 0x34 -#define TV8532_VID_H 0x35 -#define TV8532_PID_L 0x36 -#define TV8532_PID_H 0x37 -#define TV8532_DeviceID 0x83 -#define TV8532_AD_SLOPE 0x91 -#define TV8532_AD_BITCTRL 0x94 -#define TV8532_AD_COLBEGIN_L 0x10 -#define TV8532_AD_COLBEGIN_H 0x11 -#define TV8532_AD_ROWBEGIN_L 0x14 -#define TV8532_AD_ROWBEGIN_H 0x15 - -static const __u32 tv_8532_eeprom_data[] = { -/* add dataL dataM dataH */ - 0x00010001, 0x01018011, 0x02050014, 0x0305001c, - 0x040d001e, 0x0505001f, 0x06050519, 0x0705011b, - 0x0805091e, 0x090d892e, 0x0a05892f, 0x0b050dd9, - 0x0c0509f1, 0 +/* TV-8532A (ICM532A) registers (LE) */ +#define R00_PART_CONTROL 0x00 +#define LATENT_CHANGE 0x80 +#define EXPO_CHANGE 0x04 +#define R01_TIMING_CONTROL_LOW 0x01 +#define CMD_EEprom_Open 0x30 +#define CMD_EEprom_Close 0x29 +#define R03_TABLE_ADDR 0x03 +#define R04_WTRAM_DATA_L 0x04 +#define R05_WTRAM_DATA_M 0x05 +#define R06_WTRAM_DATA_H 0x06 +#define R07_TABLE_LEN 0x07 +#define R08_RAM_WRITE_ACTION 0x08 +#define R0C_AD_WIDTHL 0x0c +#define R0D_AD_WIDTHH 0x0d +#define R0E_AD_HEIGHTL 0x0e +#define R0F_AD_HEIGHTH 0x0f +#define R10_AD_COL_BEGINL 0x10 +#define R11_AD_COL_BEGINH 0x11 +#define MIRROR 0x04 /* [10] */ +#define R14_AD_ROW_BEGINL 0x14 +#define R15_AD_ROWBEGINH 0x15 +#define R1C_AD_EXPOSE_TIMEL 0x1c +#define R28_QUANT 0x28 +#define R29_LINE 0x29 +#define R2C_POLARITY 0x2c +#define R2D_POINT 0x2d +#define R2E_POINTH 0x2e +#define R2F_POINTB 0x2f +#define R30_POINTBH 0x30 +#define R31_UPD 0x31 +#define R2A_HIGH_BUDGET 0x2a +#define R2B_LOW_BUDGET 0x2b +#define R34_VID 0x34 +#define R35_VIDH 0x35 +#define R36_PID 0x36 +#define R37_PIDH 0x37 +#define R39_Test1 0x39 /* GPIO */ +#define R3B_Test3 0x3B /* GPIO */ +#define R83_AD_IDH 0x83 +#define R91_AD_SLOPEREG 0x91 +#define R94_AD_BITCONTROL 0x94 + +static const u8 eeprom_data[][3] = { +/* dataH dataM dataL */ + {0x01, 0x00, 0x01}, + {0x01, 0x80, 0x11}, + {0x05, 0x00, 0x14}, + {0x05, 0x00, 0x1c}, + {0x0d, 0x00, 0x1e}, + {0x05, 0x00, 0x1f}, + {0x05, 0x05, 0x19}, + {0x05, 0x01, 0x1b}, + {0x05, 0x09, 0x1e}, + {0x0d, 0x89, 0x2e}, + {0x05, 0x89, 0x2f}, + {0x05, 0x0d, 0xd9}, + {0x05, 0x09, 0xf1}, }; static int reg_r(struct gspca_dev *gspca_dev, @@ -165,7 +134,7 @@ static int reg_r(struct gspca_dev *gspca_dev, { usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), - TV8532_REQ_RegRead, + 0x03, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, /* value */ index, gspca_dev->usb_buf, 1, @@ -174,27 +143,27 @@ static int reg_r(struct gspca_dev *gspca_dev, } /* write 1 byte */ -static void reg_w_1(struct gspca_dev *gspca_dev, +static void reg_w1(struct gspca_dev *gspca_dev, __u16 index, __u8 value) { gspca_dev->usb_buf[0] = value; usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), - TV8532_REQ_RegWrite, + 0x02, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, /* value */ index, gspca_dev->usb_buf, 1, 500); } /* write 2 bytes */ -static void reg_w_2(struct gspca_dev *gspca_dev, - __u16 index, __u8 val1, __u8 val2) +static void reg_w2(struct gspca_dev *gspca_dev, + u16 index, u16 value) { - gspca_dev->usb_buf[0] = val1; - gspca_dev->usb_buf[1] = val2; + gspca_dev->usb_buf[0] = value; + gspca_dev->usb_buf[1] = value >> 8; usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), - TV8532_REQ_RegWrite, + 0x02, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, /* value */ index, gspca_dev->usb_buf, 2, 500); @@ -202,32 +171,18 @@ static void reg_w_2(struct gspca_dev *gspca_dev, static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev) { - int i = 0; - __u8 reg, data0, data1, data2; - - reg_w_1(gspca_dev, TV8532_GPIO, 0xb0); - reg_w_1(gspca_dev, TV8532_CTRL, TV8532_CMD_EEprom_Open); -/* msleep(1); */ - while (tv_8532_eeprom_data[i]) { - reg = (tv_8532_eeprom_data[i] & 0xff000000) >> 24; - reg_w_1(gspca_dev, TV8532_EEprom_Add, reg); - /* msleep(1); */ - data0 = (tv_8532_eeprom_data[i] & 0x000000ff); - reg_w_1(gspca_dev, TV8532_EEprom_DataL, data0); - /* msleep(1); */ - data1 = (tv_8532_eeprom_data[i] & 0x0000ff00) >> 8; - reg_w_1(gspca_dev, TV8532_EEprom_DataM, data1); - /* msleep(1); */ - data2 = (tv_8532_eeprom_data[i] & 0x00ff0000) >> 16; - reg_w_1(gspca_dev, TV8532_EEprom_DataH, data2); - /* msleep(1); */ - reg_w_1(gspca_dev, TV8532_EEprom_Write, 0); - /* msleep(10); */ - i++; + int i; + + reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Open); + for (i = 0; i < ARRAY_SIZE(eeprom_data); i++) { + reg_w1(gspca_dev, R03_TABLE_ADDR, i); + reg_w1(gspca_dev, R04_WTRAM_DATA_L, eeprom_data[i][2]); + reg_w1(gspca_dev, R05_WTRAM_DATA_M, eeprom_data[i][1]); + reg_w1(gspca_dev, R06_WTRAM_DATA_H, eeprom_data[i][0]); + reg_w1(gspca_dev, R08_RAM_WRITE_ACTION, 0); } - reg_w_1(gspca_dev, TV8532_EEprom_TableLength, i); -/* msleep(1); */ - reg_w_1(gspca_dev, TV8532_CTRL, TV8532_CMD_EEprom_Close); + reg_w1(gspca_dev, R07_TABLE_LEN, i); + reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close); msleep(10); } @@ -238,79 +193,76 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - tv_8532WriteEEprom(gspca_dev); - cam = &gspca_dev->cam; - cam->epaddr = 1; cam->cam_mode = sif_mode; - cam->nmodes = sizeof sif_mode / sizeof sif_mode[0]; + cam->nmodes = ARRAY_SIZE(sif_mode); - sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; - sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; + sd->brightness = BRIGHTNESS_DEF; return 0; } static void tv_8532ReadRegisters(struct gspca_dev *gspca_dev) { - __u8 data; - - data = reg_r(gspca_dev, 0x0001); - PDEBUG(D_USBI, "register 0x01-> %x", data); - data = reg_r(gspca_dev, 0x0002); - PDEBUG(D_USBI, "register 0x02-> %x", data); - reg_r(gspca_dev, TV8532_ADWIDTH_L); - reg_r(gspca_dev, TV8532_ADWIDTH_H); - reg_r(gspca_dev, TV8532_QUANT_COMP); - reg_r(gspca_dev, TV8532_MODE_PACKET); - reg_r(gspca_dev, TV8532_SETCLK); - reg_r(gspca_dev, TV8532_POINT_L); - reg_r(gspca_dev, TV8532_POINT_H); - reg_r(gspca_dev, TV8532_POINTB_L); - reg_r(gspca_dev, TV8532_POINTB_H); - reg_r(gspca_dev, TV8532_BUDGET_L); - reg_r(gspca_dev, TV8532_BUDGET_H); - reg_r(gspca_dev, TV8532_VID_L); - reg_r(gspca_dev, TV8532_VID_H); - reg_r(gspca_dev, TV8532_PID_L); - reg_r(gspca_dev, TV8532_PID_H); - reg_r(gspca_dev, TV8532_DeviceID); - reg_r(gspca_dev, TV8532_AD_COLBEGIN_L); - reg_r(gspca_dev, TV8532_AD_COLBEGIN_H); - reg_r(gspca_dev, TV8532_AD_ROWBEGIN_L); - reg_r(gspca_dev, TV8532_AD_ROWBEGIN_H); + int i; + static u8 reg_tb[] = { + R0C_AD_WIDTHL, + R0D_AD_WIDTHH, + R28_QUANT, + R29_LINE, + R2C_POLARITY, + R2D_POINT, + R2E_POINTH, + R2F_POINTB, + R30_POINTBH, + R2A_HIGH_BUDGET, + R2B_LOW_BUDGET, + R34_VID, + R35_VIDH, + R36_PID, + R37_PIDH, + R83_AD_IDH, + R10_AD_COL_BEGINL, + R11_AD_COL_BEGINH, + R14_AD_ROW_BEGINL, + R15_AD_ROWBEGINH, + 0 + }; + + i = 0; + do { + reg_r(gspca_dev, reg_tb[i]); + i++; + } while (reg_tb[i] != 0); } static void tv_8532_setReg(struct gspca_dev *gspca_dev) { - reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_L, - ADCBEGINL); /* 0x10 */ - reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_H, - ADCBEGINH); /* also digital gain */ - reg_w_1(gspca_dev, TV8532_PART_CTRL, - TV8532_CMD_UPDATE); /* 0x00<-0x84 */ - - reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0a); + reg_w1(gspca_dev, R10_AD_COL_BEGINL, 0x44); + /* begin active line */ + reg_w1(gspca_dev, R11_AD_COL_BEGINH, 0x00); + /* mirror and digital gain */ + reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); + /* = 0x84 */ + + reg_w1(gspca_dev, R3B_Test3, 0x0a); /* Test0Sel = 10 */ /******************************************************/ - reg_w_1(gspca_dev, TV8532_ADHEIGHT_L, ADHEIGHL); /* 0e */ - reg_w_1(gspca_dev, TV8532_ADHEIGHT_H, ADHEIGHH); /* 0f */ - reg_w_2(gspca_dev, TV8532_EXPOSURE, - EXPOL, EXPOH); /* 350d 0x014c; 1c */ - reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_L, - ADCBEGINL); /* 0x10 */ - reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_H, - ADCBEGINH); /* also digital gain */ - reg_w_1(gspca_dev, TV8532_AD_ROWBEGIN_L, - ADRBEGINL); /* 0x14 */ - - reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x00); /* 0x91 */ - reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x02); /* 0x94 */ - - reg_w_1(gspca_dev, TV8532_CTRL, - TV8532_CMD_EEprom_Close); /* 0x01 */ - - reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x00); /* 0x91 */ - reg_w_1(gspca_dev, TV8532_PART_CTRL, - TV8532_CMD_UPDATE); /* 0x00<-0x84 */ + reg_w1(gspca_dev, R0E_AD_HEIGHTL, 0x90); + reg_w1(gspca_dev, R0F_AD_HEIGHTH, 0x01); + reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, 0x018f); + reg_w1(gspca_dev, R10_AD_COL_BEGINL, 0x44); + /* begin active line */ + reg_w1(gspca_dev, R11_AD_COL_BEGINH, 0x00); + /* mirror and digital gain */ + reg_w1(gspca_dev, R14_AD_ROW_BEGINL, 0x0a); + + reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00); + reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x02); + + reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close); + + reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00); + reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); + /* = 0x84 */ } static void tv_8532_PollReg(struct gspca_dev *gspca_dev) @@ -319,54 +271,55 @@ static void tv_8532_PollReg(struct gspca_dev *gspca_dev) /* strange polling from tgc */ for (i = 0; i < 10; i++) { - reg_w_1(gspca_dev, TV8532_SETCLK, - TESTCLK); /* 0x48; //0x08; 0x2c */ - reg_w_1(gspca_dev, TV8532_PART_CTRL, TV8532_CMD_UPDATE); - reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01); /* 0x31 */ + reg_w1(gspca_dev, R2C_POLARITY, 0x10); + reg_w1(gspca_dev, R00_PART_CONTROL, + LATENT_CHANGE | EXPO_CHANGE); + reg_w1(gspca_dev, R31_UPD, 0x01); } } /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32); - reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00); + tv_8532WriteEEprom(gspca_dev); + + reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x32); /* slope begin 1,7V, + * slope rate 2 */ + reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x00); tv_8532ReadRegisters(gspca_dev); - reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); - reg_w_2(gspca_dev, TV8532_ADHEIGHT_L, ADHEIGHL, - ADHEIGHH); /* 401d 0x0169; 0e */ - reg_w_2(gspca_dev, TV8532_EXPOSURE, EXPOL, - EXPOH); /* 350d 0x014c; 1c */ - reg_w_1(gspca_dev, TV8532_ADWIDTH_L, ADWIDTHL); /* 0x20; 0x0c */ - reg_w_1(gspca_dev, TV8532_ADWIDTH_H, ADWIDTHH); /* 0x0d */ + reg_w1(gspca_dev, R3B_Test3, 0x0b); + reg_w2(gspca_dev, R0E_AD_HEIGHTL, 0x0190); + reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, 0x018f); + reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8); + reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03); /*******************************************************************/ - reg_w_1(gspca_dev, TV8532_QUANT_COMP, - TESTCOMP); /* 0x72 compressed mode 0x28 */ - reg_w_1(gspca_dev, TV8532_MODE_PACKET, - TESTLINE); /* 0x84; // CIF | 4 packet 0x29 */ + reg_w1(gspca_dev, R28_QUANT, 0x90); + /* no compress - fixed Q - quant 0 */ + reg_w1(gspca_dev, R29_LINE, 0x81); + /* 0x84; // CIF | 4 packet 0x29 */ /************************************************/ - reg_w_1(gspca_dev, TV8532_SETCLK, - TESTCLK); /* 0x48; //0x08; 0x2c */ - reg_w_1(gspca_dev, TV8532_POINT_L, - TESTPTL); /* 0x38; 0x2d */ - reg_w_1(gspca_dev, TV8532_POINT_H, - TESTPTH); /* 0x04; 0x2e */ - reg_w_1(gspca_dev, TV8532_POINTB_L, - TESTPTBL); /* 0x04; 0x2f */ - reg_w_1(gspca_dev, TV8532_POINTB_H, - TESTPTBH); /* 0x04; 0x30 */ - reg_w_1(gspca_dev, TV8532_PART_CTRL, - TV8532_CMD_UPDATE); /* 0x00<-0x84 */ + reg_w1(gspca_dev, R2C_POLARITY, 0x10); + /* 0x48; //0x08; 0x2c */ + reg_w1(gspca_dev, R2D_POINT, 0x14); + /* 0x38; 0x2d */ + reg_w1(gspca_dev, R2E_POINTH, 0x01); + /* 0x04; 0x2e */ + reg_w1(gspca_dev, R2F_POINTB, 0x12); + /* 0x04; 0x2f */ + reg_w1(gspca_dev, R30_POINTBH, 0x01); + /* 0x04; 0x30 */ + reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); + /* 0x00<-0x84 */ /*************************************************/ - reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01); /* 0x31 */ + reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */ msleep(200); - reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */ + reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */ /*************************************************/ tv_8532_setReg(gspca_dev); /*************************************************/ - reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); + reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */ /*************************************************/ tv_8532_setReg(gspca_dev); /*************************************************/ @@ -377,11 +330,10 @@ static int sd_init(struct gspca_dev *gspca_dev) static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int brightness = sd->brightness; - reg_w_2(gspca_dev, TV8532_EXPOSURE, - brightness >> 8, brightness); /* 1c */ - reg_w_1(gspca_dev, TV8532_PART_CTRL, TV8532_CMD_UPDATE); + reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, sd->brightness); + reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); + /* 0x84 */ } /* -- start the camera -- */ @@ -389,57 +341,50 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32); - reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00); + reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x32); /* slope begin 1,7V, + * slope rate 2 */ + reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x00); tv_8532ReadRegisters(gspca_dev); - reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); - reg_w_2(gspca_dev, TV8532_ADHEIGHT_L, - ADHEIGHL, ADHEIGHH); /* 401d 0x0169; 0e */ -/* reg_w_2(gspca_dev, TV8532_EXPOSURE, - EXPOL, EXPOH); * 350d 0x014c; 1c */ + reg_w1(gspca_dev, R3B_Test3, 0x0b); + + reg_w2(gspca_dev, R0E_AD_HEIGHTL, 0x0190); setbrightness(gspca_dev); - reg_w_1(gspca_dev, TV8532_ADWIDTH_L, ADWIDTHL); /* 0x20; 0x0c */ - reg_w_1(gspca_dev, TV8532_ADWIDTH_H, ADWIDTHH); /* 0x0d */ + reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8); /* 0x20; 0x0c */ + reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03); /************************************************/ - reg_w_1(gspca_dev, TV8532_QUANT_COMP, - TESTCOMP); /* 0x72 compressed mode 0x28 */ + reg_w1(gspca_dev, R28_QUANT, 0x90); + /* 0x72 compressed mode 0x28 */ if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { /* 176x144 */ - reg_w_1(gspca_dev, TV8532_MODE_PACKET, - QCIFLINE); /* 0x84; // CIF | 4 packet 0x29 */ + reg_w1(gspca_dev, R29_LINE, 0x41); + /* CIF - 2 lines/packet */ } else { /* 352x288 */ - reg_w_1(gspca_dev, TV8532_MODE_PACKET, - TESTLINE); /* 0x84; // CIF | 4 packet 0x29 */ + reg_w1(gspca_dev, R29_LINE, 0x81); + /* CIF - 2 lines/packet */ } /************************************************/ - reg_w_1(gspca_dev, TV8532_SETCLK, - TESTCLK); /* 0x48; //0x08; 0x2c */ - reg_w_1(gspca_dev, TV8532_POINT_L, - TESTPTL); /* 0x38; 0x2d */ - reg_w_1(gspca_dev, TV8532_POINT_H, - TESTPTH); /* 0x04; 0x2e */ - reg_w_1(gspca_dev, TV8532_POINTB_L, - TESTPTBL); /* 0x04; 0x2f */ - reg_w_1(gspca_dev, TV8532_POINTB_H, - TESTPTBH); /* 0x04; 0x30 */ - reg_w_1(gspca_dev, TV8532_PART_CTRL, - TV8532_CMD_UPDATE); /* 0x00<-0x84 */ + reg_w1(gspca_dev, R2C_POLARITY, 0x10); /* slow clock */ + reg_w1(gspca_dev, R2D_POINT, 0x14); + reg_w1(gspca_dev, R2E_POINTH, 0x01); + reg_w1(gspca_dev, R2F_POINTB, 0x12); + reg_w1(gspca_dev, R30_POINTBH, 0x01); + reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE); /************************************************/ - reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01); /* 0x31 */ + reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */ msleep(200); - reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */ + reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */ /************************************************/ tv_8532_setReg(gspca_dev); /************************************************/ - reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); + reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */ /************************************************/ tv_8532_setReg(gspca_dev); /************************************************/ tv_8532_PollReg(gspca_dev); - reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */ + reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */ gspca_dev->empty_packet = 0; /* check the empty packets */ sd->packet = 0; /* ignore the first packets */ @@ -449,7 +394,7 @@ static int sd_start(struct gspca_dev *gspca_dev) static void sd_stopN(struct gspca_dev *gspca_dev) { - reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b); + reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */ } static void sd_pkt_scan(struct gspca_dev *gspca_dev, @@ -473,9 +418,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* each packet contains: * - header 2 bytes - * - RG line + * - RGRG line * - 4 bytes - * - GB line + * - GBGB line * - 4 bytes */ gspca_frame_add(gspca_dev, packet_type0, @@ -484,10 +429,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, frame, data + gspca_dev->width + 6, gspca_dev->width); } -static void setcontrast(struct gspca_dev *gspca_dev) -{ -} - static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -506,24 +447,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->contrast = val; - if (gspca_dev->streaming) - setcontrast(gspca_dev); - return 0; -} - -static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->contrast; - return 0; -} - /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -570,8 +493,10 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 0525ea5..4c802fb 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -37,18 +37,21 @@ struct sd { __u8 lightfreq; __u8 sharpness; + u8 image_offset; + char bridge; #define BRIDGE_VC0321 0 #define BRIDGE_VC0323 1 char sensor; #define SENSOR_HV7131R 0 #define SENSOR_MI0360 1 -#define SENSOR_MI1320 2 -#define SENSOR_MI1310_SOC 3 -#define SENSOR_OV7660 4 -#define SENSOR_OV7670 5 -#define SENSOR_PO1200 6 -#define SENSOR_PO3130NC 7 +#define SENSOR_MI1310_SOC 2 +#define SENSOR_MI1320 3 +#define SENSOR_MI1320_SOC 4 +#define SENSOR_OV7660 5 +#define SENSOR_OV7670 6 +#define SENSOR_PO1200 7 +#define SENSOR_PO3130NC 8 }; /* V4L2 controls supported by the driver */ @@ -149,8 +152,50 @@ 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 */ + .bytesperline = 1280, + .sizeimage = 1280 * 1024 * 1 / 4 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, +}; +static const struct v4l2_pix_format bi_mode[] = { +/*fixme: jeg does not work + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 5}, +*/ + {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 4}, +/* + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, +*/ + {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, +/* + {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024 * 1 / 4 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, +*/ + {1280, 1024, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, }; - static const struct v4l2_pix_format svga_mode[] = { {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 800, @@ -400,92 +445,208 @@ static const __u8 mi0360_initQVGA_JPG[][4] = { static const __u8 mi1310_socinitVGA_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, 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, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, - {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x04, 0x0d, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, - {0xb3, 0x22, 0x03, 0xcc}, - {0xb3, 0x23, 0xc0, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 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, 0xd0, 0xcc}, - {0xbc, 0x01, 0x01, 0xcc}, - {0xf0, 0x00, 0x02, 0xbb}, - {0xc8, 0x9f, 0x0b, 0xbb}, - {0x5b, 0x00, 0x01, 0xbb}, - {0x2f, 0xde, 0x20, 0xbb}, + {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}, {0xf0, 0x00, 0x00, 0xbb}, - {0x20, 0x03, 0x02, 0xbb}, + {0x00, 0x01, 0x00, 0xdd}, + {0x0d, 0x00, 0x09, 0xbb}, + {0x0d, 0x00, 0x08, 0xbb}, {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}, + {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}, - {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}, + {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, 0x02, 0xcc}, {0xb6, 0x02, 0x80, 0xcc}, {0xb6, 0x05, 0x01, 0xcc}, {0xb6, 0x04, 0xe0, 0xcc}, - {0xb6, 0x12, 0xf8, 0xcc}, - {0xb6, 0x13, 0x25, 0xcc}, + {0xb6, 0x12, 0x78, 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, 0x00, 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}, {0xbc, 0x02, 0x18, 0xcc}, {0xbc, 0x03, 0x50, 0xcc}, {0xbc, 0x04, 0x18, 0xcc}, @@ -496,131 +657,123 @@ static const __u8 mi1310_socinitVGA_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}, + {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, 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}, {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}, - {}, +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, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x01, 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, 0x04, 0xcc}, + {0xb3, 0x23, 0x00, 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}, + {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, 0x03, 0xbb}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x05, 0xcc}, + {0xb6, 0x02, 0x00, 0xcc}, + {0xb6, 0x05, 0x04, 0xcc}, + {0xb6, 0x04, 0x00, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x18, 0x0a, 0xcc}, + {0xb6, 0x17, 0x00, 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}, + {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}, + {} }; static const __u8 mi1320_gamma[17] = { @@ -778,6 +931,722 @@ static const __u8 mi1320_initQVGA_data[][4] = { {} }; +static const u8 mi1320_soc_InitVGA[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 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}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x07, 0x00, 0xe0, 0xbb}, + {0x08, 0x00, 0x0b, 0xbb}, + {0x21, 0x00, 0x0c, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {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}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +static const u8 mi1320_soc_InitVGA_JPG[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 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}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x07, 0x00, 0xe0, 0xbb}, + {0x08, 0x00, 0x0b, 0xbb}, + {0x21, 0x00, 0x0c, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x05, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 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, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {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}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +static const u8 mi1320_soc_InitQVGA[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 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}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd1, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x07, 0x00, 0xe0, 0xbb}, + {0x08, 0x00, 0x0b, 0xbb}, + {0x21, 0x00, 0x0c, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {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}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +static const u8 mi1320_soc_InitQVGA_JPG[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 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}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd1, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x07, 0x00, 0xe0, 0xbb}, + {0x08, 0x00, 0x0b, 0xbb}, + {0x21, 0x00, 0x0c, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {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, 0x05, 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}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {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}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +static const u8 mi1320_soc_InitSXGA_JPG[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x33, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x04, 0xcc}, + {0xb3, 0x23, 0x00, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0x20, 0x01, 0x03, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x05, 0xcc}, + {0xb6, 0x02, 0x00, 0xcc}, + {0xb6, 0x05, 0x04, 0xcc}, + {0xb6, 0x04, 0x00, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x29, 0xcc}, + {0xb6, 0x18, 0x0a, 0xcc}, + {0xb6, 0x17, 0x00, 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}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {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}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x13, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x00, 0x85, 0xbb}, + {0x08, 0x00, 0x27, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0x0d, 0xbb}, + {0x3a, 0x06, 0x1b, 0xbb}, + {0x3b, 0x00, 0x95, 0xbb}, + {0x3c, 0x04, 0xdb, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x58, 0x02, 0x66, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0x5a, 0x01, 0x33, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x64, 0x5e, 0x1c, 0xbb}, + {0x2f, 0x90, 0x00, 0xbb}, + {} +}; +static const u8 mi1320_soc_InitSXGA[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x04, 0xcc}, + {0xb3, 0x23, 0x00, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0x20, 0x01, 0x03, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {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}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x13, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x00, 0x85, 0xbb}, + {0x08, 0x00, 0x27, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0x0d, 0xbb}, + {0x3a, 0x06, 0x1b, 0xbb}, + {0x3b, 0x00, 0x95, 0xbb}, + {0x3c, 0x04, 0xdb, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x58, 0x02, 0x66, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0x5a, 0x01, 0x33, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x64, 0x5e, 0x1c, 0xbb}, + {} +}; static const __u8 po3130_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff @@ -1764,26 +2633,43 @@ static const __u8 po1200_initVGA_data[][4] = { }; struct sensor_info { - int sensorId; - __u8 I2cAdd; - __u8 IdAdd; - __u16 VpId; - __u8 m1; - __u8 m2; - __u8 op; - }; + s8 sensorId; + u8 I2cAdd; + u8 IdAdd; + u16 VpId; + u8 m1; + u8 m2; + u8 op; +}; static const struct sensor_info sensor_info_data[] = { /* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ - {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, - {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, + {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, + {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01}, +/* (tested in vc032x_probe_sensor) */ +/* {-1, 0x80 | 0x20, 0x83, 0x0000, 0x24, 0x25, 0x01}, */ {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, - {SENSOR_MI1320, 0x80 | 0xc8, 0x00, 0x148c, 0x64, 0x65, 0x01}, - {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, /* (tested in vc032x_probe_sensor) */ /* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ + {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, + {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05}, + {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05}, + {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, +/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, */ + {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, */ +/* {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, */ + {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01}, {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01}, + {-1, 0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01}, + {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, + {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01}, + {SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01}, +/*fixme: previously detected?*/ + {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01}, +/*fixme: not in the ms-win probe - may be found before?*/ + {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, }; /* read 'len' bytes in gspca_dev->usb_buf */ @@ -1814,51 +2700,49 @@ static void reg_w(struct usb_device *dev, 500); } -static void read_sensor_register(struct gspca_dev *gspca_dev, - __u16 address, __u16 *value) +static u16 read_sensor_register(struct gspca_dev *gspca_dev, + u16 address) { struct usb_device *dev = gspca_dev->dev; - __u8 ldata, mdata, hdata; + u8 ldata, mdata, hdata; int retry = 50; - *value = 0; - reg_r(gspca_dev, 0xa1, 0xb33f, 1); - /*PDEBUG(D_PROBE, " I2c Bus Busy Wait 0x%02X ", tmpvalue); */ if (!(gspca_dev->usb_buf[0] & 0x02)) { - PDEBUG(D_ERR, "I2c Bus Busy Wait %d", - gspca_dev->usb_buf[0] & 0x02); - return; + PDEBUG(D_ERR, "I2c Bus Busy Wait %02x", + gspca_dev->usb_buf[0]); + return 0; } reg_w(dev, 0xa0, address, 0xb33a); reg_w(dev, 0xa0, 0x02, 0xb339); - reg_r(gspca_dev, 0xa1, 0xb33b, 1); - while (retry-- && gspca_dev->usb_buf[0]) { + do { reg_r(gspca_dev, 0xa1, 0xb33b, 1); -/* PDEBUG(D_PROBE, "Read again 0xb33b %d", tmpvalue); */ - msleep(1); - } + if (gspca_dev->usb_buf[0] == 0x00) + break; + msleep(40); + } while (--retry >= 0); + reg_r(gspca_dev, 0xa1, 0xb33e, 1); ldata = gspca_dev->usb_buf[0]; reg_r(gspca_dev, 0xa1, 0xb33d, 1); mdata = gspca_dev->usb_buf[0]; reg_r(gspca_dev, 0xa1, 0xb33c, 1); hdata = gspca_dev->usb_buf[0]; - PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x", - hdata, mdata, ldata); + if (hdata != 0 && mdata != 0 && ldata != 0) + PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x", + hdata, mdata, ldata); reg_r(gspca_dev, 0xa1, 0xb334, 1); if (gspca_dev->usb_buf[0] == 0x02) - *value = (hdata << 8) + mdata; - else - *value = hdata; + return (hdata << 8) + mdata; + return hdata; } static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; int i; - __u16 value; + u16 value; const struct sensor_info *ptsensor_info; reg_r(gspca_dev, 0xa1, 0xbfcf, 1); @@ -1872,48 +2756,51 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) reg_w(dev, 0xa0, 0x0c, 0xb309); reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); reg_w(dev, 0xa0, ptsensor_info->op, 0xb301); - read_sensor_register(gspca_dev, ptsensor_info->IdAdd, &value); - if (value == ptsensor_info->VpId) - return ptsensor_info->sensorId; - - /* special case for MI0360 */ - if (ptsensor_info->sensorId == SENSOR_MI1310_SOC - && value == 0x8243) - return SENSOR_MI0360; + value = read_sensor_register(gspca_dev, ptsensor_info->IdAdd); + if (value == 0 && ptsensor_info->IdAdd == 0x82) + value = read_sensor_register(gspca_dev, 0x83); + if (value != 0) { + PDEBUG(D_ERR|D_PROBE, "Sensor ID %04x (%d)", + value, i); + if (value == ptsensor_info->VpId) + return ptsensor_info->sensorId; + + switch (value) { + case 0x7673: + return SENSOR_OV7670; + case 0x8243: + return SENSOR_MI0360; + } +/*fixme: should return here*/ + } } return -1; } -static __u8 i2c_write(struct gspca_dev *gspca_dev, - __u8 reg, const __u8 *val, __u8 size) +static void i2c_write(struct gspca_dev *gspca_dev, + u8 reg, const u8 *val, + u8 size) /* 1 or 2 */ { struct usb_device *dev = gspca_dev->dev; + int retry; - if (size > 3 || size < 1) - return -EINVAL; reg_r(gspca_dev, 0xa1, 0xb33f, 1); +/*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/ reg_w(dev, 0xa0, size, 0xb334); reg_w(dev, 0xa0, reg, 0xb33a); - switch (size) { - case 1: - reg_w(dev, 0xa0, val[0], 0xb336); - break; - case 2: - reg_w(dev, 0xa0, val[0], 0xb336); + reg_w(dev, 0xa0, val[0], 0xb336); + if (size > 1) reg_w(dev, 0xa0, val[1], 0xb337); - break; - case 3: - reg_w(dev, 0xa0, val[0], 0xb336); - reg_w(dev, 0xa0, val[1], 0xb337); - reg_w(dev, 0xa0, val[2], 0xb338); - break; - default: - reg_w(dev, 0xa0, 0x01, 0xb334); - return -EINVAL; - } reg_w(dev, 0xa0, 0x01, 0xb339); - reg_r(gspca_dev, 0xa1, 0xb33b, 1); - return gspca_dev->usb_buf[0] == 0; + retry = 4; + do { + reg_r(gspca_dev, 0xa1, 0xb33b, 1); + if (gspca_dev->usb_buf[0] == 0) + break; + msleep(20); + } while (--retry > 0); + if (retry <= 0) + PDEBUG(D_ERR, "i2c_write failed"); } static void put_tab_to_reg(struct gspca_dev *gspca_dev, @@ -1938,7 +2825,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev, return; case 0xcc: /* normal write */ reg_w(dev, 0xa0, data[i][2], - ((data[i][0])<<8) | data[i][1]); + (data[i][0]) << 8 | data[i][1]); break; case 0xaa: /* i2c op */ i2c_write(gspca_dev, data[i][1], &data[i][2], 1); @@ -1955,19 +2842,6 @@ static void usb_exchange(struct gspca_dev *gspca_dev, /*not reached*/ } -/* - "GammaT"=hex:04,17,31,4f,6a,83,99,ad,bf,ce,da,e5,ee,f5,fb,ff,ff - "MatrixT"=hex:60,f9,e5,e7,50,05,f3,e6,66 - */ - -static void vc0321_reset(struct gspca_dev *gspca_dev) -{ - reg_w(gspca_dev->dev, 0xa0, 0x00, 0xb04d); - reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb301); - msleep(100); - reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb003); - msleep(100); -} /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, @@ -1979,10 +2853,7 @@ static int sd_config(struct gspca_dev *gspca_dev, int sensor; cam = &gspca_dev->cam; - cam->epaddr = 0x02; sd->bridge = id->driver_info; - - vc0321_reset(gspca_dev); sensor = vc032x_probe_sensor(gspca_dev); switch (sensor) { case -1: @@ -2001,6 +2872,9 @@ static int sd_config(struct gspca_dev *gspca_dev, case SENSOR_MI1320: PDEBUG(D_PROBE, "Find Sensor MI1320"); break; + case SENSOR_MI1320_SOC: + PDEBUG(D_PROBE, "Find Sensor MI1320_SOC"); + break; case SENSOR_OV7660: PDEBUG(D_PROBE, "Find Sensor OV7660"); break; @@ -2020,12 +2894,23 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = vc0321_mode; cam->nmodes = ARRAY_SIZE(vc0321_mode); } else { - if (sensor != SENSOR_PO1200) { - cam->cam_mode = vc0323_mode; - cam->nmodes = ARRAY_SIZE(vc0323_mode); - } else { + switch (sensor) { + case SENSOR_PO1200: cam->cam_mode = svga_mode; cam->nmodes = ARRAY_SIZE(svga_mode); + break; + case SENSOR_MI1310_SOC: + cam->cam_mode = vc0323_mode; + cam->nmodes = ARRAY_SIZE(vc0323_mode); + break; + case SENSOR_MI1320_SOC: + cam->cam_mode = bi_mode; + cam->nmodes = ARRAY_SIZE(bi_mode); + break; + default: + cam->cam_mode = vc0323_mode; + cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1; + break; } } @@ -2061,7 +2946,7 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at probe and time */ +/* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { return 0; @@ -2124,9 +3009,18 @@ 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; int mode; + static const u8 (*mi1320_soc_init[])[4] = { + mi1320_soc_InitSXGA, + mi1320_soc_InitSXGA_JPG, + mi1320_soc_InitVGA, + mi1320_soc_InitVGA_JPG, + mi1320_soc_InitQVGA, + mi1320_soc_InitQVGA_JPG + }; /* Assume start use the good resolution from gspca_dev->mode */ if (sd->bridge == BRIDGE_VC0321) { @@ -2134,6 +3028,13 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfed); reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfee); reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfef); + sd->image_offset = 46; + } else { + if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].pixelformat + == V4L2_PIX_FMT_JPEG) + sd->image_offset = 0; + else + sd->image_offset = 32; } mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; @@ -2141,115 +3042,87 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_HV7131R: GammaT = hv7131r_gamma; MatrixT = hv7131r_matrix; - if (mode) { - /* 320x240 */ - usb_exchange(gspca_dev, hv7131r_initQVGA_data); - } else { - /* 640x480 */ - usb_exchange(gspca_dev, hv7131r_initVGA_data); - } + if (mode) + init = hv7131r_initQVGA_data; /* 320x240 */ + else + init = hv7131r_initVGA_data; /* 640x480 */ break; case SENSOR_OV7660: GammaT = ov7660_gamma; MatrixT = ov7660_matrix; - if (mode) { - /* 320x240 */ - usb_exchange(gspca_dev, ov7660_initQVGA_data); - } else { - /* 640x480 */ - usb_exchange(gspca_dev, ov7660_initVGA_data); - } + if (mode) + init = ov7660_initQVGA_data; /* 320x240 */ + else + init = ov7660_initVGA_data; /* 640x480 */ break; case SENSOR_OV7670: /*GammaT = ov7660_gamma; */ /*MatrixT = ov7660_matrix; */ - if (mode) { - /* 320x240 */ - usb_exchange(gspca_dev, ov7670_initQVGA_JPG); - } else { - /* 640x480 */ - usb_exchange(gspca_dev, ov7670_initVGA_JPG); - } + if (mode) + init = ov7670_initQVGA_JPG; /* 320x240 */ + else + init = ov7670_initVGA_JPG; /* 640x480 */ break; case SENSOR_MI0360: GammaT = mi1320_gamma; MatrixT = mi0360_matrix; - if (mode) { - /* 320x240 */ - usb_exchange(gspca_dev, mi0360_initQVGA_JPG); - } else { - /* 640x480 */ - usb_exchange(gspca_dev, mi0360_initVGA_JPG); - } + if (mode) + init = mi0360_initQVGA_JPG; /* 320x240 */ + else + init = mi0360_initVGA_JPG; /* 640x480 */ break; case SENSOR_MI1310_SOC: - if (mode) { - /* 320x240 */ - usb_exchange(gspca_dev, mi1310_socinitQVGA_JPG); - } else { - /* 640x480 */ - usb_exchange(gspca_dev, mi1310_socinitVGA_JPG); + GammaT = mi1320_gamma; + MatrixT = mi1320_matrix; + switch (mode) { + case 1: + init = mi1310_socinitQVGA_JPG; /* 320x240 */ + break; + case 0: + init = mi1310_socinitVGA_JPG; /* 640x480 */ + break; + default: + init = mi1310_soc_InitSXGA_JPG; /* 1280x1024 */ + break; } break; case SENSOR_MI1320: GammaT = mi1320_gamma; MatrixT = mi1320_matrix; - if (mode) { - /* 320x240 */ - usb_exchange(gspca_dev, mi1320_initQVGA_data); - } else { - /* 640x480 */ - usb_exchange(gspca_dev, mi1320_initVGA_data); - } + if (mode) + init = mi1320_initQVGA_data; /* 320x240 */ + else + init = mi1320_initVGA_data; /* 640x480 */ + break; + case SENSOR_MI1320_SOC: + GammaT = mi1320_gamma; + MatrixT = mi1320_matrix; + init = mi1320_soc_init[mode]; break; case SENSOR_PO3130NC: GammaT = po3130_gamma; MatrixT = po3130_matrix; - if (mode) { - /* 320x240 */ - usb_exchange(gspca_dev, po3130_initQVGA_data); - } else { - /* 640x480 */ - usb_exchange(gspca_dev, po3130_initVGA_data); - } - usb_exchange(gspca_dev, po3130_rundata); + if (mode) + init = po3130_initQVGA_data; /* 320x240 */ + else + init = po3130_initVGA_data; /* 640x480 */ + usb_exchange(gspca_dev, init); + init = po3130_rundata; break; - case SENSOR_PO1200: + default: +/* case SENSOR_PO1200: */ GammaT = po1200_gamma; MatrixT = po1200_matrix; - usb_exchange(gspca_dev, po1200_initVGA_data); + init = po1200_initVGA_data; break; - default: - PDEBUG(D_PROBE, "Damned !! no sensor found Bye"); - return -EMEDIUMTYPE; } + usb_exchange(gspca_dev, init); if (GammaT && MatrixT) { put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a); put_tab_to_reg(gspca_dev, GammaT, 17, 0xb85b); put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c); put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c); - /* Seem SHARPNESS */ - /* - reg_w(gspca_dev->dev, 0xa0, 0x80, 0xb80a); - reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80b); - reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80e); - */ - /* all 0x40 ??? do nothing - reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb822); - reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb823); - reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb824); - */ - /* Only works for HV7131R ?? - reg_r (gspca_dev, 0xa1, 0xb881, 1); - reg_w(gspca_dev->dev, 0xa0, 0xfe01, 0xb881); - reg_w(gspca_dev->dev, 0xa0, 0x79, 0xb801); - */ - /* only hv7131r et ov7660 - reg_w(gspca_dev->dev, 0xa0, 0x20, 0xb827); - reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb826); * ISP_GAIN 80 - reg_w(gspca_dev->dev, 0xa0, 0x23, 0xb800); * ISP CTRL_BAS - */ /* set the led on 0x0892 0x0896 */ if (sd->sensor != SENSOR_PO1200) { reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); @@ -2296,12 +3169,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, "vc032x header packet found len %d", len); frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); - if (sd->bridge == BRIDGE_VC0321) { -#define VCHDRSZ 46 - data += VCHDRSZ; - len -= VCHDRSZ; -#undef VCHDRSZ - } + data += sd->image_offset; + len -= sd->image_offset; gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); return; @@ -2399,7 +3268,8 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ strcpy((char *) menu->name, "50 Hz"); return 0; - case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + default: +/* case 2: * V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ strcpy((char *) menu->name, "60 Hz"); return 0; } @@ -2424,6 +3294,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ 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}, @@ -2432,6 +3303,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {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}, {} @@ -2460,8 +3332,11 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index ec2a53d..4fe01d8 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -23,6 +23,7 @@ #define MODULE_NAME "zc3xx" #include "gspca.h" +#include "jpeg.h" MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>, " "Serge A. Suchkov <Serge.A.S@tochka.ru>"); @@ -31,7 +32,7 @@ MODULE_LICENSE("GPL"); static int force_sensor = -1; -#include "jpeg.h" +#define QUANT_VAL 1 /* quantization table */ #include "zc3xx-reg.h" /* specific webcam descriptor */ @@ -44,30 +45,36 @@ struct sd { __u8 autogain; __u8 lightfreq; __u8 sharpness; + u8 quality; /* image quality */ +#define QUALITY_MIN 40 +#define QUALITY_MAX 60 +#define QUALITY_DEF 50 - char qindex; signed char sensor; /* Type of image sensor chip */ /* !! values used in different tables */ -#define SENSOR_CS2102 0 -#define SENSOR_CS2102K 1 -#define SENSOR_GC0305 2 -#define SENSOR_HDCS2020b 3 -#define SENSOR_HV7131B 4 -#define SENSOR_HV7131C 5 -#define SENSOR_ICM105A 6 -#define SENSOR_MC501CB 7 -#define SENSOR_OV7620 8 -/*#define SENSOR_OV7648 8 - same values */ -#define SENSOR_OV7630C 9 -#define SENSOR_PAS106 10 -#define SENSOR_PAS202B 11 -#define SENSOR_PB0330 12 -#define SENSOR_PO2030 13 -#define SENSOR_TAS5130CK 14 -#define SENSOR_TAS5130CXX 15 -#define SENSOR_TAS5130C_VF0250 16 -#define SENSOR_MAX 17 +#define SENSOR_ADCM2700 0 +#define SENSOR_CS2102 1 +#define SENSOR_CS2102K 2 +#define SENSOR_GC0305 3 +#define SENSOR_HDCS2020b 4 +#define SENSOR_HV7131B 5 +#define SENSOR_HV7131C 6 +#define SENSOR_ICM105A 7 +#define SENSOR_MC501CB 8 +#define SENSOR_OV7620 9 +/*#define SENSOR_OV7648 9 - same values */ +#define SENSOR_OV7630C 10 +#define SENSOR_PAS106 11 +#define SENSOR_PAS202B 12 +#define SENSOR_PB0330 13 +#define SENSOR_PO2030 14 +#define SENSOR_TAS5130CK 15 +#define SENSOR_TAS5130CXX 16 +#define SENSOR_TAS5130C_VF0250 17 +#define SENSOR_MAX 18 unsigned short chip_revision; + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -206,6 +213,213 @@ struct usb_action { __u16 idx; }; +static const struct usb_action adcm2700_Initial[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */ + {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */ + {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */ + {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */ + {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ + {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */ + {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */ + {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ + {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */ + {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */ + {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */ + {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */ + {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */ + {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */ + {0xbb, 0x86, 0x0002}, /* 00,86,02,bb */ + {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */ + {0xbb, 0x86, 0x0802}, /* 08,86,02,bb */ + {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */ +/*mswin+*/ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, + {0xaa, 0xfe, 0x0002}, + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, + {0xaa, 0xb4, 0xcd37}, + {0xaa, 0xa4, 0x0004}, + {0xaa, 0xa8, 0x0007}, + {0xaa, 0xac, 0x0004}, +/*mswin-*/ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */ + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */ + {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ + {} +}; +static const struct usb_action adcm2700_InitialScale[] = { + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ + {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ + {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ + {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ + {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ + {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ + {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ + {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ + {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ + {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */ + {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */ + {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */ + {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */ + {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */ + {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */ + {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ + {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */ + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */ + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */ + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */ + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */ + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ + {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */ + {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */ + {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ + {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */ + {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */ + {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */ + {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */ + {0xdd, 0x00, 0x0050}, /* 00,00,50,dd */ + {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */ + {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */ + {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */ + {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */ + {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */ + {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */ + {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */ + {0xbb, 0x86, 0x0002}, /* 00,88,02,bb */ + {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */ + {0xbb, 0x86, 0x0802}, /* 08,88,02,bb */ + {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */ + /*******/ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */ + {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */ + {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ + {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */ + {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ + {} +}; +static const struct usb_action adcm2700_50HZ[] = { + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xbb, 0x05, 0x8400}, /* 84,05,00,bb */ + {0xbb, 0xd0, 0xb007}, /* b0,d0,07,bb */ + {0xbb, 0xa0, 0xb80f}, /* b8,a0,0f,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ + {0xaa, 0x26, 0x00d0}, /* 00,26,d0,aa */ + {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */ + {} +}; +static const struct usb_action adcm2700_60HZ[] = { + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */ + {0xbb, 0x82, 0xb006}, /* b0,82,06,bb */ + {0xbb, 0x04, 0xb80d}, /* b8,04,0d,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ + {0xaa, 0x26, 0x0057}, /* 00,26,57,aa */ + {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */ + {} +}; +static const struct usb_action adcm2700_NoFliker[] = { + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */ + {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */ + {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */ + {0xbb, 0x05, 0xb000}, /* b0,05,00,bb */ + {0xbb, 0xa0, 0xb801}, /* b8,a0,01,bb */ + {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ + {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ + {} +}; static const struct usb_action cs2102_Initial[] = { {0xa1, 0x01, 0x0008}, {0xa1, 0x01, 0x0008}, @@ -877,7 +1091,7 @@ static const struct usb_action cs2102K_Initial[] = { }; static const struct usb_action cs2102K_InitialScale[] = { - {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, @@ -894,6 +1108,7 @@ static const struct usb_action cs2102K_InitialScale[] = { {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, +/*fixme: next sequence = i2c exchanges*/ {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR}, {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, @@ -1077,207 +1292,6 @@ static const struct usb_action cs2102K_InitialScale[] = { {0xa0, 0x60, ZC3XX_R116_RGAIN}, {0xa0, 0x40, ZC3XX_R117_GGAIN}, {0xa0, 0x4c, ZC3XX_R118_BGAIN}, - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, - {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, - {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, - {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, - {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, - {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, - {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, - {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, - {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, - {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0A, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0B, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0C, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x7b, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0D, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0xA3, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0E, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x0C, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, - {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x01, 0x01b1}, - {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x60, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x4c, ZC3XX_R118_BGAIN}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ - {0xa0, 0x38, ZC3XX_R121_GAMMA01}, - {0xa0, 0x59, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x92, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, - {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf4, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf4, ZC3XX_R10D_RGB10}, - {0xa0, 0x58, ZC3XX_R10E_RGB11}, - {0xa0, 0xf4, ZC3XX_R10F_RGB12}, - {0xa0, 0xf4, ZC3XX_R110_RGB20}, - {0xa0, 0xf4, ZC3XX_R111_RGB21}, - {0xa0, 0x58, ZC3XX_R112_RGB22}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE}, - {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK}, - {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND}, - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, - {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x04, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0x19, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0x1f, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x60, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x4c, ZC3XX_R118_BGAIN}, {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT}, {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE}, @@ -1334,6 +1348,7 @@ static const struct usb_action cs2102K_InitialScale[] = { {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, +/*fixme:what does the next sequence?*/ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN}, {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN}, @@ -6237,7 +6252,7 @@ static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = { {} }; -static int reg_r_i(struct gspca_dev *gspca_dev, +static u8 reg_r_i(struct gspca_dev *gspca_dev, __u16 index) { usb_control_msg(gspca_dev->dev, @@ -6250,10 +6265,10 @@ static int reg_r_i(struct gspca_dev *gspca_dev, return gspca_dev->usb_buf[0]; } -static int reg_r(struct gspca_dev *gspca_dev, +static u8 reg_r(struct gspca_dev *gspca_dev, __u16 index) { - int ret; + u8 ret; ret = reg_r_i(gspca_dev, index); PDEBUG(D_USBI, "reg r [%04x] -> %02x", index, ret); @@ -6286,8 +6301,8 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev, __u8 retbyte; __u16 retval; - reg_w_i(gspca_dev->dev, reg, 0x92); - reg_w_i(gspca_dev->dev, 0x02, 0x90); /* <- read command */ + reg_w_i(gspca_dev->dev, reg, 0x0092); + reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */ msleep(25); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */ @@ -6332,6 +6347,12 @@ static void usb_exchange(struct gspca_dev *gspca_dev, action->idx & 0xff, /* valL */ action->idx >> 8); /* valH */ break; + case 0xbb: + i2c_write(gspca_dev, + action->idx >> 8, /* reg */ + action->idx & 0xff, /* valL */ + action->val); /* valH */ + break; default: /* case 0xdd: * delay */ msleep(action->val / 64 + 10); @@ -6347,6 +6368,10 @@ static void setmatrix(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int i; const __u8 *matrix; + static const u8 adcm2700_matrix[9] = +/* {0x66, 0xed, 0xed, 0xed, 0x66, 0xed, 0xed, 0xed, 0x66}; */ +/*ms-win*/ + {0x74, 0xed, 0xed, 0xed, 0x74, 0xed, 0xed, 0xed, 0x74}; static const __u8 gc0305_matrix[9] = {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50}; static const __u8 ov7620_matrix[9] = @@ -6358,23 +6383,24 @@ static void setmatrix(struct gspca_dev *gspca_dev) static const __u8 vf0250_matrix[9] = {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b}; static const __u8 *matrix_tb[SENSOR_MAX] = { - NULL, /* SENSOR_CS2102 0 */ - NULL, /* SENSOR_CS2102K 1 */ - gc0305_matrix, /* SENSOR_GC0305 2 */ - NULL, /* SENSOR_HDCS2020b 3 */ - NULL, /* SENSOR_HV7131B 4 */ - NULL, /* SENSOR_HV7131C 5 */ - NULL, /* SENSOR_ICM105A 6 */ - NULL, /* SENSOR_MC501CB 7 */ - ov7620_matrix, /* SENSOR_OV7620 8 */ - NULL, /* SENSOR_OV7630C 9 */ - NULL, /* SENSOR_PAS106 10 */ - pas202b_matrix, /* SENSOR_PAS202B 11 */ - NULL, /* SENSOR_PB0330 12 */ - po2030_matrix, /* SENSOR_PO2030 13 */ - NULL, /* SENSOR_TAS5130CK 14 */ - NULL, /* SENSOR_TAS5130CXX 15 */ - vf0250_matrix, /* SENSOR_TAS5130C_VF0250 16 */ + adcm2700_matrix, /* SENSOR_ADCM2700 0 */ + NULL, /* SENSOR_CS2102 1 */ + NULL, /* SENSOR_CS2102K 2 */ + gc0305_matrix, /* SENSOR_GC0305 3 */ + NULL, /* SENSOR_HDCS2020b 4 */ + NULL, /* SENSOR_HV7131B 5 */ + NULL, /* SENSOR_HV7131C 6 */ + NULL, /* SENSOR_ICM105A 7 */ + NULL, /* SENSOR_MC501CB 8 */ + ov7620_matrix, /* SENSOR_OV7620 9 */ + NULL, /* SENSOR_OV7630C 10 */ + NULL, /* SENSOR_PAS106 11 */ + pas202b_matrix, /* SENSOR_PAS202B 12 */ + NULL, /* SENSOR_PB0330 13 */ + po2030_matrix, /* SENSOR_PO2030 14 */ + NULL, /* SENSOR_TAS5130CK 15 */ + NULL, /* SENSOR_TAS5130CXX 16 */ + vf0250_matrix, /* SENSOR_TAS5130C_VF0250 17 */ }; matrix = matrix_tb[sd->sensor]; @@ -6398,8 +6424,11 @@ static void setbrightness(struct gspca_dev *gspca_dev) /*fixme: is it really write to 011d and 018d for all other sensors? */ brightness = sd->brightness; reg_w(gspca_dev->dev, brightness, 0x011d); - if (sd->sensor == SENSOR_HV7131B) + switch (sd->sensor) { + case SENSOR_ADCM2700: + case SENSOR_HV7131B: return; + } if (brightness < 0x70) brightness += 0x10; else @@ -6536,10 +6565,10 @@ static void setquality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - __u8 quality; __u8 frxt; switch (sd->sensor) { + case SENSOR_ADCM2700: case SENSOR_GC0305: case SENSOR_HV7131B: case SENSOR_OV7620: @@ -6547,26 +6576,18 @@ static void setquality(struct gspca_dev *gspca_dev) return; } /*fixme: is it really 0008 0007 0018 for all other sensors? */ - quality = sd->qindex; - reg_w(dev, quality, 0x0008); + reg_w(dev, QUANT_VAL, 0x0008); frxt = 0x30; reg_w(dev, frxt, 0x0007); - switch (quality) { - case 0: - case 1: - case 2: - frxt = 0xff; - break; - case 3: - frxt = 0xf0; - break; - case 4: - frxt = 0xe0; - break; - case 5: - frxt = 0x20; - break; - } +#if QUANT_VAL == 0 || QUANT_VAL == 1 || QUANT_VAL == 2 + frxt = 0xff; +#elif QUANT_VAL == 3 + frxt = 0xf0; +#elif QUANT_VAL == 4 + frxt = 0xe0; +#else + frxt = 0x20; +#endif reg_w(dev, frxt, 0x0018); } @@ -6583,71 +6604,75 @@ static int setlightfreq(struct gspca_dev *gspca_dev) int i, mode; const struct usb_action *zc3_freq; static const struct usb_action *freq_tb[SENSOR_MAX][6] = { -/* SENSOR_CS2102 0 */ +/* SENSOR_ADCM2700 0 */ + {adcm2700_NoFliker, adcm2700_NoFliker, + adcm2700_50HZ, adcm2700_50HZ, + adcm2700_60HZ, adcm2700_60HZ}, +/* SENSOR_CS2102 1 */ {cs2102_NoFliker, cs2102_NoFlikerScale, cs2102_50HZ, cs2102_50HZScale, cs2102_60HZ, cs2102_60HZScale}, -/* SENSOR_CS2102K 1 */ +/* SENSOR_CS2102K 2 */ {cs2102_NoFliker, cs2102_NoFlikerScale, NULL, NULL, /* currently disabled */ NULL, NULL}, -/* SENSOR_GC0305 2 */ +/* SENSOR_GC0305 3 */ {gc0305_NoFliker, gc0305_NoFliker, gc0305_50HZ, gc0305_50HZ, gc0305_60HZ, gc0305_60HZ}, -/* SENSOR_HDCS2020b 3 */ +/* SENSOR_HDCS2020b 4 */ {hdcs2020b_NoFliker, hdcs2020b_NoFliker, hdcs2020b_50HZ, hdcs2020b_50HZ, hdcs2020b_60HZ, hdcs2020b_60HZ}, -/* SENSOR_HV7131B 4 */ +/* SENSOR_HV7131B 5 */ {hv7131b_NoFlikerScale, hv7131b_NoFliker, hv7131b_50HZScale, hv7131b_50HZ, hv7131b_60HZScale, hv7131b_60HZ}, -/* SENSOR_HV7131C 5 */ +/* SENSOR_HV7131C 6 */ {NULL, NULL, NULL, NULL, NULL, NULL}, -/* SENSOR_ICM105A 6 */ +/* SENSOR_ICM105A 7 */ {icm105a_NoFliker, icm105a_NoFlikerScale, icm105a_50HZ, icm105a_50HZScale, icm105a_60HZ, icm105a_60HZScale}, -/* SENSOR_MC501CB 7 */ +/* SENSOR_MC501CB 8 */ {MC501CB_NoFliker, MC501CB_NoFlikerScale, MC501CB_50HZ, MC501CB_50HZScale, MC501CB_60HZ, MC501CB_60HZScale}, -/* SENSOR_OV7620 8 */ +/* SENSOR_OV7620 9 */ {OV7620_NoFliker, OV7620_NoFliker, OV7620_50HZ, OV7620_50HZ, OV7620_60HZ, OV7620_60HZ}, -/* SENSOR_OV7630C 9 */ +/* SENSOR_OV7630C 10 */ {NULL, NULL, NULL, NULL, NULL, NULL}, -/* SENSOR_PAS106 10 */ +/* SENSOR_PAS106 11 */ {pas106b_NoFliker, pas106b_NoFliker, pas106b_50HZ, pas106b_50HZ, pas106b_60HZ, pas106b_60HZ}, -/* SENSOR_PAS202B 11 */ +/* SENSOR_PAS202B 12 */ {pas202b_NoFlikerScale, pas202b_NoFliker, pas202b_50HZScale, pas202b_50HZ, pas202b_60HZScale, pas202b_60HZ}, -/* SENSOR_PB0330 12 */ +/* SENSOR_PB0330 13 */ {pb0330_NoFliker, pb0330_NoFlikerScale, pb0330_50HZ, pb0330_50HZScale, pb0330_60HZ, pb0330_60HZScale}, -/* SENSOR_PO2030 13 */ +/* SENSOR_PO2030 14 */ {PO2030_NoFliker, PO2030_NoFliker, PO2030_50HZ, PO2030_50HZ, PO2030_60HZ, PO2030_60HZ}, -/* SENSOR_TAS5130CK 14 */ +/* SENSOR_TAS5130CK 15 */ {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, tas5130cxx_50HZ, tas5130cxx_50HZScale, tas5130cxx_60HZ, tas5130cxx_60HZScale}, -/* SENSOR_TAS5130CXX 15 */ +/* SENSOR_TAS5130CXX 16 */ {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, tas5130cxx_50HZ, tas5130cxx_50HZScale, tas5130cxx_60HZ, tas5130cxx_60HZScale}, -/* SENSOR_TAS5130C_VF0250 16 */ +/* SENSOR_TAS5130C_VF0250 17 */ {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale, tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale, tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale}, @@ -6701,6 +6726,7 @@ static void send_unknown(struct usb_device *dev, int sensor) reg_w(dev, 0x0c, 0x003b); reg_w(dev, 0x08, 0x0038); break; + case SENSOR_ADCM2700: case SENSOR_GC0305: case SENSOR_OV7620: case SENSOR_PB0330: @@ -6743,26 +6769,25 @@ static int sif_probe(struct gspca_dev *gspca_dev) static int vga_2wr_probe(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; - __u8 retbyte; - __u16 checkword; + u16 retword; start_2wr_probe(dev, 0x00); /* HV7131B */ i2c_write(gspca_dev, 0x01, 0xaa, 0x00); - retbyte = i2c_read(gspca_dev, 0x01); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x01); + if (retword != 0) return 0x00; /* HV7131B */ start_2wr_probe(dev, 0x04); /* CS2102 */ i2c_write(gspca_dev, 0x01, 0xaa, 0x00); - retbyte = i2c_read(gspca_dev, 0x01); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x01); + if (retword != 0) return 0x04; /* CS2102 */ start_2wr_probe(dev, 0x06); /* OmniVision */ reg_w(dev, 0x08, 0x008d); i2c_write(gspca_dev, 0x11, 0xaa, 0x00); - retbyte = i2c_read(gspca_dev, 0x11); - if (retbyte != 0) { + retword = i2c_read(gspca_dev, 0x11); + if (retword != 0) { /* (should have returned 0xaa) --> Omnivision? */ /* reg_r 0x10 -> 0x06 --> */ goto ov_check; @@ -6770,40 +6795,40 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev) start_2wr_probe(dev, 0x08); /* HDCS2020 */ i2c_write(gspca_dev, 0x15, 0xaa, 0x00); - retbyte = i2c_read(gspca_dev, 0x15); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x15); + if (retword != 0) return 0x08; /* HDCS2020 */ start_2wr_probe(dev, 0x0a); /* PB0330 */ i2c_write(gspca_dev, 0x07, 0xaa, 0xaa); - retbyte = i2c_read(gspca_dev, 0x07); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x07); + if (retword != 0) return 0x0a; /* PB0330 */ - retbyte = i2c_read(gspca_dev, 0x03); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x03); + if (retword != 0) return 0x0a; /* PB0330 ?? */ - retbyte = i2c_read(gspca_dev, 0x04); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x04); + if (retword != 0) return 0x0a; /* PB0330 ?? */ start_2wr_probe(dev, 0x0c); /* ICM105A */ i2c_write(gspca_dev, 0x01, 0x11, 0x00); - retbyte = i2c_read(gspca_dev, 0x01); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x01); + if (retword != 0) return 0x0c; /* ICM105A */ start_2wr_probe(dev, 0x0e); /* PAS202BCB */ reg_w(dev, 0x08, 0x008d); i2c_write(gspca_dev, 0x03, 0xaa, 0x00); msleep(500); - retbyte = i2c_read(gspca_dev, 0x03); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x03); + if (retword != 0) return 0x0e; /* PAS202BCB */ start_2wr_probe(dev, 0x02); /* ?? */ i2c_write(gspca_dev, 0x01, 0xaa, 0x00); - retbyte = i2c_read(gspca_dev, 0x01); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x01); + if (retword != 0) return 0x02; /* ?? */ ov_check: reg_r(gspca_dev, 0x0010); /* ?? */ @@ -6817,12 +6842,10 @@ ov_check: msleep(500); reg_w(dev, 0x01, 0x0012); i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */ - retbyte = i2c_read(gspca_dev, 0x0a); - checkword = retbyte << 8; - retbyte = i2c_read(gspca_dev, 0x0b); - checkword |= retbyte; - PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", checkword); - switch (checkword) { + retword = i2c_read(gspca_dev, 0x0a) << 8; + retword |= i2c_read(gspca_dev, 0x0b); + PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword); + switch (retword) { case 0x7631: /* OV7630C */ reg_w(dev, 0x06, 0x0010); break; @@ -6832,7 +6855,7 @@ ov_check: default: return -1; /* not OmniVision */ } - return checkword; + return retword; } struct sensor_by_chipset_revision { @@ -6845,6 +6868,7 @@ static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { {0x8001, 0x13}, {0x8000, 0x14}, /* CS2102K */ {0x8400, 0x15}, /* TAS5130K */ + {0x4001, 0x16}, /* ADCM2700 */ }; static int vga_3wr_probe(struct gspca_dev *gspca_dev) @@ -6853,7 +6877,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) struct usb_device *dev = gspca_dev->dev; int i; __u8 retbyte; - __u16 checkword; + u16 retword; /*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/ reg_w(dev, 0x02, 0x0010); @@ -6865,27 +6889,25 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) reg_w(dev, 0x03, 0x0012); reg_w(dev, 0x01, 0x0012); reg_w(dev, 0x05, 0x0012); - retbyte = i2c_read(gspca_dev, 0x14); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x14); + if (retword != 0) return 0x11; /* HV7131R */ - retbyte = i2c_read(gspca_dev, 0x15); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x15); + if (retword != 0) return 0x11; /* HV7131R */ - retbyte = i2c_read(gspca_dev, 0x16); - if (retbyte != 0) + retword = i2c_read(gspca_dev, 0x16); + if (retword != 0) return 0x11; /* HV7131R */ reg_w(dev, 0x02, 0x0010); - retbyte = reg_r(gspca_dev, 0x000b); - checkword = retbyte << 8; - retbyte = reg_r(gspca_dev, 0x000a); - checkword |= retbyte; - PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", checkword); + retword = reg_r(gspca_dev, 0x000b) << 8; + retword |= reg_r(gspca_dev, 0x000a); + PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword); reg_r(gspca_dev, 0x0010); /* this is tested only once anyway */ for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) { - if (chipset_revision_sensor[i].revision == checkword) { - sd->chip_revision = checkword; + if (chipset_revision_sensor[i].revision == retword) { + sd->chip_revision = retword; send_unknown(dev, SENSOR_PB0330); return chipset_revision_sensor[i].internal_sensor_id; } @@ -6897,8 +6919,8 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) reg_w(dev, 0x0a, 0x0010); reg_w(dev, 0x03, 0x0012); reg_w(dev, 0x01, 0x0012); - retbyte = i2c_read(gspca_dev, 0x00); - if (retbyte != 0) { + retword = i2c_read(gspca_dev, 0x00); + if (retword != 0) { PDEBUG(D_PROBE, "probe 3wr vga type 0a ?"); return 0x0a; /* ?? */ } @@ -6910,14 +6932,14 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) reg_w(dev, 0x03, 0x0012); msleep(2); reg_w(dev, 0x01, 0x0012); - retbyte = i2c_read(gspca_dev, 0x00); - if (retbyte != 0) { - PDEBUG(D_PROBE, "probe 3wr vga type %02x", retbyte); - if (retbyte == 0x11) /* VF0250 */ + retword = i2c_read(gspca_dev, 0x00); + if (retword != 0) { + PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword); + if (retword == 0x0011) /* VF0250 */ return 0x0250; - if (retbyte == 0x29) /* gc0305 */ + if (retword == 0x0029) /* gc0305 */ send_unknown(dev, SENSOR_GC0305); - return retbyte; + return retword; } reg_w(dev, 0x01, 0x0000); /* check OmniVision */ @@ -6927,8 +6949,8 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) reg_w(dev, 0x06, 0x0010); reg_w(dev, 0x01, 0x0012); reg_w(dev, 0x05, 0x0012); - if (i2c_read(gspca_dev, 0x1c) == 0x7f /* OV7610 - manufacturer ID */ - && i2c_read(gspca_dev, 0x1d) == 0xa2) { + if (i2c_read(gspca_dev, 0x1c) == 0x007f /* OV7610 - manufacturer ID */ + && i2c_read(gspca_dev, 0x1d) == 0x00a2) { send_unknown(dev, SENSOR_OV7620); return 0x06; /* OmniVision confirm ? */ } @@ -6942,16 +6964,14 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) /* msleep(150); */ reg_w(dev, 0x01, 0x0012); reg_w(dev, 0x05, 0x0012); - retbyte = i2c_read(gspca_dev, 0x0000); /* ID 0 */ - checkword = retbyte << 8; - retbyte = i2c_read(gspca_dev, 0x0001); /* ID 1 */ - checkword |= retbyte; - PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", checkword); - if (checkword == 0x2030) { + retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */ + retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */ + PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword); + if (retword == 0x2030) { retbyte = i2c_read(gspca_dev, 0x02); /* revision number */ PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte); send_unknown(dev, SENSOR_PO2030); - return checkword; + return retword; } reg_w(dev, 0x01, 0x0000); @@ -6962,10 +6982,10 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) reg_w(dev, 0x01, 0x0012); reg_w(dev, 0x05, 0x0001); reg_w(dev, 0xd3, 0x008b); - retbyte = i2c_read(gspca_dev, 0x01); - if (retbyte != 0) { - PDEBUG(D_PROBE, "probe 3wr vga type 0a ?"); - return 0x0a; /* ?? */ + retword = i2c_read(gspca_dev, 0x01); + if (retword != 0) { + PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword); + return retword; } return -1; } @@ -6973,7 +6993,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) static int zcxx_probeSensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int sensor, sensor2; + int sensor; switch (sd->sensor) { case SENSOR_MC501CB: @@ -6988,16 +7008,9 @@ static int zcxx_probeSensor(struct gspca_dev *gspca_dev) break; } sensor = vga_2wr_probe(gspca_dev); - if (sensor >= 0) { - if (sensor < 0x7600) - return sensor; - /* next probe is needed for OmniVision ? */ - } - sensor2 = vga_3wr_probe(gspca_dev); - if (sensor2 >= 0 - && sensor >= 0) + if (sensor >= 0) return sensor; - return sensor2; + return vga_3wr_probe(gspca_dev); } /* this function is called at probe time */ @@ -7009,23 +7022,24 @@ static int sd_config(struct gspca_dev *gspca_dev, int sensor; int vga = 1; /* 1: vga, 0: sif */ static const __u8 gamma[SENSOR_MAX] = { - 5, /* SENSOR_CS2102 0 */ - 5, /* SENSOR_CS2102K 1 */ - 4, /* SENSOR_GC0305 2 */ - 4, /* SENSOR_HDCS2020b 3 */ - 4, /* SENSOR_HV7131B 4 */ - 4, /* SENSOR_HV7131C 5 */ - 4, /* SENSOR_ICM105A 6 */ - 4, /* SENSOR_MC501CB 7 */ - 3, /* SENSOR_OV7620 8 */ - 4, /* SENSOR_OV7630C 9 */ - 4, /* SENSOR_PAS106 10 */ - 4, /* SENSOR_PAS202B 11 */ - 4, /* SENSOR_PB0330 12 */ - 4, /* SENSOR_PO2030 13 */ - 4, /* SENSOR_TAS5130CK 14 */ - 4, /* SENSOR_TAS5130CXX 15 */ - 3, /* SENSOR_TAS5130C_VF0250 16 */ + 4, /* SENSOR_ADCM2700 0 */ + 5, /* SENSOR_CS2102 1 */ + 5, /* SENSOR_CS2102K 2 */ + 4, /* SENSOR_GC0305 3 */ + 4, /* SENSOR_HDCS2020b 4 */ + 4, /* SENSOR_HV7131B 5 */ + 4, /* SENSOR_HV7131C 6 */ + 4, /* SENSOR_ICM105A 7 */ + 4, /* SENSOR_MC501CB 8 */ + 3, /* SENSOR_OV7620 9 */ + 4, /* SENSOR_OV7630C 10 */ + 4, /* SENSOR_PAS106 11 */ + 4, /* SENSOR_PAS202B 12 */ + 4, /* SENSOR_PB0330 13 */ + 4, /* SENSOR_PO2030 14 */ + 4, /* SENSOR_TAS5130CK 15 */ + 4, /* SENSOR_TAS5130CXX 16 */ + 3, /* SENSOR_TAS5130C_VF0250 17 */ }; /* define some sensors from the vendor/product */ @@ -7033,7 +7047,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = id->driver_info; sensor = zcxx_probeSensor(gspca_dev); if (sensor >= 0) - PDEBUG(D_PROBE, "probe sensor -> %02x", sensor); + PDEBUG(D_PROBE, "probe sensor -> %04x", sensor); if ((unsigned) force_sensor < SENSOR_MAX) { sd->sensor = force_sensor; PDEBUG(D_PROBE, "sensor forced to %d", force_sensor); @@ -7112,6 +7126,10 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->chip_revision); sd->sensor = SENSOR_TAS5130CK; break; + case 0x16: + PDEBUG(D_PROBE, "Find Sensor ADCM2700"); + sd->sensor = SENSOR_ADCM2700; + break; case 0x29: PDEBUG(D_PROBE, "Find Sensor GC0305"); sd->sensor = SENSOR_GC0305; @@ -7129,12 +7147,16 @@ static int sd_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "Find Sensor OV7620"); sd->sensor = SENSOR_OV7620; break; + case 0x7631: + PDEBUG(D_PROBE, "Find Sensor OV7630C"); + sd->sensor = SENSOR_OV7630C; + break; case 0x7648: PDEBUG(D_PROBE, "Find Sensor OV7648"); sd->sensor = SENSOR_OV7620; /* same sensor (?) */ break; default: - PDEBUG(D_ERR|D_PROBE, "Unknown sensor %02x", sensor); + PDEBUG(D_ERR|D_PROBE, "Unknown sensor %04x", sensor); return -EINVAL; } } @@ -7147,7 +7169,6 @@ static int sd_config(struct gspca_dev *gspca_dev, } cam = &gspca_dev->cam; - cam->epaddr = 0x01; /*fixme:test*/ gspca_dev->nbalt--; if (vga) { @@ -7157,12 +7178,12 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); } - sd->qindex = 1; sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; sd->gamma = gamma[(int) sd->sensor]; sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; + sd->quality = QUALITY_DEF; switch (sd->sensor) { case SENSOR_GC0305: @@ -7196,27 +7217,34 @@ static int sd_start(struct gspca_dev *gspca_dev) const struct usb_action *zc3_init; int mode; static const struct usb_action *init_tb[SENSOR_MAX][2] = { - {cs2102_InitialScale, cs2102_Initial}, /* 0 */ - {cs2102K_InitialScale, cs2102K_Initial}, /* 1 */ - {gc0305_Initial, gc0305_InitialScale}, /* 2 */ - {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 3 */ - {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 4 */ - {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 5 */ - {icm105axx_InitialScale, icm105axx_Initial}, /* 6 */ - {MC501CB_InitialScale, MC501CB_Initial}, /* 7 */ - {OV7620_mode0, OV7620_mode1}, /* 8 */ - {ov7630c_InitialScale, ov7630c_Initial}, /* 9 */ - {pas106b_InitialScale, pas106b_Initial}, /* 10 */ - {pas202b_Initial, pas202b_InitialScale}, /* 11 */ - {pb0330xx_InitialScale, pb0330xx_Initial}, /* 12 */ + {adcm2700_Initial, adcm2700_InitialScale}, /* 0 */ + {cs2102_InitialScale, cs2102_Initial}, /* 1 */ + {cs2102K_InitialScale, cs2102K_Initial}, /* 2 */ + {gc0305_Initial, gc0305_InitialScale}, /* 3 */ + {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 4 */ + {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 5 */ + {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */ + {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */ + {MC501CB_InitialScale, MC501CB_Initial}, /* 8 */ + {OV7620_mode0, OV7620_mode1}, /* 9 */ + {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */ + {pas106b_InitialScale, pas106b_Initial}, /* 11 */ + {pas202b_Initial, pas202b_InitialScale}, /* 12 */ + {pb0330xx_InitialScale, pb0330xx_Initial}, /* 13 */ /* or {pb03303x_InitialScale, pb03303x_Initial}, */ - {PO2030_mode0, PO2030_mode1}, /* 13 */ - {tas5130CK_InitialScale, tas5130CK_Initial}, /* 14 */ - {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 15 */ + {PO2030_mode0, PO2030_mode1}, /* 14 */ + {tas5130CK_InitialScale, tas5130CK_Initial}, /* 15 */ + {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 16 */ {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial}, - /* 16 */ + /* 17 */ }; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; zc3_init = init_tb[(int) sd->sensor][mode]; switch (sd->sensor) { @@ -7243,11 +7271,12 @@ static int sd_start(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, zc3_init); switch (sd->sensor) { + case SENSOR_ADCM2700: case SENSOR_GC0305: case SENSOR_OV7620: case SENSOR_PO2030: case SENSOR_TAS5130C_VF0250: - msleep(100); /* ?? */ +/* msleep(100); * ?? */ reg_r(gspca_dev, 0x0002); /* --> 0x40 */ reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ reg_w(dev, 0x15, 0x01ae); @@ -7260,6 +7289,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setmatrix(gspca_dev); setbrightness(gspca_dev); switch (sd->sensor) { + case SENSOR_ADCM2700: case SENSOR_OV7620: reg_r(gspca_dev, 0x0008); reg_w(dev, 0x00, 0x0008); @@ -7301,6 +7331,13 @@ static int sd_start(struct gspca_dev *gspca_dev) setlightfreq(gspca_dev); switch (sd->sensor) { + case SENSOR_ADCM2700: + reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ + reg_w(dev, 0x15, 0x01ae); + reg_w(dev, 0x02, 0x0180); + /* ms-win + */ + reg_w(dev, 0x40, 0x0117); + break; case SENSOR_GC0305: reg_w(dev, 0x09, 0x01ad); /* (from win traces) */ reg_w(dev, 0x15, 0x01ae); @@ -7323,19 +7360,16 @@ static int sd_start(struct gspca_dev *gspca_dev) setautogain(gspca_dev); switch (sd->sensor) { - case SENSOR_PAS202B: - reg_w(dev, 0x00, 0x0007); /* (from win traces) */ - break; case SENSOR_PO2030: msleep(500); reg_r(gspca_dev, 0x0008); reg_r(gspca_dev, 0x0007); + /*fall thru*/ + case SENSOR_PAS202B: reg_w(dev, 0x00, 0x0007); /* (from win traces) */ - reg_w(dev, 0x02, 0x0008); + reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING); break; } - if (sd->sensor == SENSOR_PAS202B) - reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING); return 0; } @@ -7344,6 +7378,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + kfree(sd->jpeg_hdr); if (!gspca_dev->present) return; send_unknown(gspca_dev->dev, sd->sensor); @@ -7354,14 +7389,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, __u8 *data, int len) { + struct sd *sd = (struct sd *) gspca_dev; if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, - ((struct sd *) gspca_dev)->qindex, - 0x21); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); + /* remove the webcam's header: * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp * - 'ss ss' is the frame sequence number (BE) @@ -7503,6 +7539,34 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + if (gspca_dev->streaming) + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT + | V4L2_JPEG_MARKER_DQT; + return 0; +} + static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, @@ -7513,6 +7577,8 @@ static const struct sd_desc sd_desc = { .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; static const __devinitdata struct usb_device_id device_table[] = { @@ -7563,11 +7629,9 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x055f, 0xd004)}, {USB_DEVICE(0x0698, 0x2003)}, {USB_DEVICE(0x0ac8, 0x0301), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x0ac8, 0x0302)}, + {USB_DEVICE(0x0ac8, 0x0302), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x0ac8, 0x301b)}, -#if !defined CONFIG_USB_ZC0301 && !defined CONFIG_USB_ZC0301_MODULE {USB_DEVICE(0x0ac8, 0x303b)}, -#endif {USB_DEVICE(0x0ac8, 0x305b), .driver_info = SENSOR_TAS5130C_VF0250}, {USB_DEVICE(0x0ac8, 0x307b)}, {USB_DEVICE(0x10fd, 0x0128)}, @@ -7600,8 +7664,10 @@ static struct usb_driver sd_driver = { static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/drivers/media/video/hdpvr/Kconfig b/drivers/media/video/hdpvr/Kconfig new file mode 100644 index 0000000..de247f3 --- /dev/null +++ b/drivers/media/video/hdpvr/Kconfig @@ -0,0 +1,10 @@ + +config VIDEO_HDPVR + tristate "Hauppauge HD PVR support" + depends on VIDEO_DEV + ---help--- + This is a video4linux driver for Hauppauge's HD PVR USB device. + + To compile this driver as a module, choose M here: the + module will be called hdpvr + diff --git a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile new file mode 100644 index 0000000..e0230fc --- /dev/null +++ b/drivers/media/video/hdpvr/Makefile @@ -0,0 +1,9 @@ +hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o + +hdpvr-$(CONFIG_I2C) += hdpvr-i2c.o + +obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o + +EXTRA_CFLAGS += -Idrivers/media/video + +EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c new file mode 100644 index 0000000..0679174 --- /dev/null +++ b/drivers/media/video/hdpvr/hdpvr-control.c @@ -0,0 +1,201 @@ +/* + * Hauppauge HD PVR USB driver - video 4 linux 2 interface + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/mutex.h> + +#include <linux/videodev2.h> + +#include <media/v4l2-common.h> + +#include "hdpvr.h" + + +int hdpvr_config_call(struct hdpvr_device *dev, uint value, u8 valbuf) +{ + int ret; + char request_type = 0x38, snd_request = 0x01; + + msleep(10); + + mutex_lock(&dev->usbc_mutex); + dev->usbc_buf[0] = valbuf; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + snd_request, 0x00 | request_type, + value, CTRL_DEFAULT_INDEX, + dev->usbc_buf, 1, 10000); + + mutex_unlock(&dev->usbc_mutex); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "config call request for value 0x%x returned %d\n", value, + ret); + + return ret < 0 ? ret : 0; +} + +struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev) +{ + struct hdpvr_video_info *vidinf = NULL; +#ifdef HDPVR_DEBUG + char print_buf[15]; +#endif + int ret; + + vidinf = kzalloc(sizeof(struct hdpvr_video_info), GFP_KERNEL); + if (!vidinf) { + v4l2_err(&dev->v4l2_dev, "out of memory\n"); + goto err; + } + + mutex_lock(&dev->usbc_mutex); + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + 0x81, 0x80 | 0x38, + 0x1400, 0x0003, + dev->usbc_buf, 5, + 1000); + if (ret == 5) { + vidinf->width = dev->usbc_buf[1] << 8 | dev->usbc_buf[0]; + vidinf->height = dev->usbc_buf[3] << 8 | dev->usbc_buf[2]; + vidinf->fps = dev->usbc_buf[4]; + } + +#ifdef HDPVR_DEBUG + if (hdpvr_debug & MSG_INFO) { + hex_dump_to_buffer(dev->usbc_buf, 5, 16, 1, print_buf, + sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "get video info returned: %d, %s\n", ret, print_buf); + } +#endif + mutex_unlock(&dev->usbc_mutex); + + if (!vidinf->width || !vidinf->height || !vidinf->fps) { + kfree(vidinf); + vidinf = NULL; + } +err: + return vidinf; +} + +int get_input_lines_info(struct hdpvr_device *dev) +{ +#ifdef HDPVR_DEBUG + char print_buf[9]; +#endif + int ret, lines; + + mutex_lock(&dev->usbc_mutex); + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + 0x81, 0x80 | 0x38, + 0x1800, 0x0003, + dev->usbc_buf, 3, + 1000); + +#ifdef HDPVR_DEBUG + if (hdpvr_debug & MSG_INFO) { + hex_dump_to_buffer(dev->usbc_buf, 3, 16, 1, print_buf, + sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "get input lines info returned: %d, %s\n", ret, + print_buf); + } +#endif + lines = dev->usbc_buf[1] << 8 | dev->usbc_buf[0]; + mutex_unlock(&dev->usbc_mutex); + return lines; +} + + +int hdpvr_set_bitrate(struct hdpvr_device *dev) +{ + int ret; + + mutex_lock(&dev->usbc_mutex); + memset(dev->usbc_buf, 0, 4); + dev->usbc_buf[0] = dev->options.bitrate; + dev->usbc_buf[2] = dev->options.peak_bitrate; + + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0x01, 0x38, CTRL_BITRATE_VALUE, + CTRL_DEFAULT_INDEX, dev->usbc_buf, 4, 1000); + mutex_unlock(&dev->usbc_mutex); + + return ret; +} + +int hdpvr_set_audio(struct hdpvr_device *dev, u8 input, + enum v4l2_mpeg_audio_encoding codec) +{ + int ret = 0; + + if (dev->flags & HDPVR_FLAG_AC3_CAP) { + mutex_lock(&dev->usbc_mutex); + memset(dev->usbc_buf, 0, 2); + dev->usbc_buf[0] = input; + if (codec == V4L2_MPEG_AUDIO_ENCODING_AAC) + dev->usbc_buf[1] = 0; + else if (codec == V4L2_MPEG_AUDIO_ENCODING_AC3) + dev->usbc_buf[1] = 1; + else { + mutex_unlock(&dev->usbc_mutex); + v4l2_err(&dev->v4l2_dev, "invalid audio codec %d\n", + codec); + ret = -EINVAL; + goto error; + } + + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0x01, 0x38, CTRL_AUDIO_INPUT_VALUE, + CTRL_DEFAULT_INDEX, dev->usbc_buf, 2, + 1000); + mutex_unlock(&dev->usbc_mutex); + if (ret == 2) + ret = 0; + } else + ret = hdpvr_config_call(dev, CTRL_AUDIO_INPUT_VALUE, + dev->options.audio_input+1); +error: + return ret; +} + +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_INPUT_VALUE, + dev->options.video_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, + dev->options.bitrate_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); + + return 0; +} diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c new file mode 100644 index 0000000..188bd5a --- /dev/null +++ b/drivers/media/video/hdpvr/hdpvr-core.c @@ -0,0 +1,466 @@ +/* + * Hauppauge HD PVR USB driver + * + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * Copyright (C) 2008 John Poet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <asm/atomic.h> +#include <linux/usb.h> +#include <linux/mutex.h> +#include <linux/i2c.h> + +#include <linux/videodev2.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-common.h> + +#include "hdpvr.h" + +static int video_nr[HDPVR_MAX] = {[0 ... (HDPVR_MAX - 1)] = UNSET}; +module_param_array(video_nr, int, NULL, 0); +MODULE_PARM_DESC(video_nr, "video device number (-1=Auto)"); + +/* holds the number of currently registered devices */ +static atomic_t dev_nr = ATOMIC_INIT(-1); + +int hdpvr_debug; +module_param(hdpvr_debug, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(hdpvr_debug, "enable debugging output"); + +uint default_video_input = HDPVR_VIDEO_INPUTS; +module_param(default_video_input, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(default_video_input, "default video input: 0=Component / " + "1=S-Video / 2=Composite"); + +uint default_audio_input = HDPVR_AUDIO_INPUTS; +module_param(default_audio_input, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(default_audio_input, "default audio input: 0=RCA back / " + "1=RCA front / 2=S/PDIF"); + +static int boost_audio; +module_param(boost_audio, bool, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(boost_audio, "boost the audio signal"); + + +/* table of devices that work with this driver */ +static struct usb_device_id hdpvr_table[] = { + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID) }, + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID1) }, + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID2) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, hdpvr_table); + + +void hdpvr_delete(struct hdpvr_device *dev) +{ + hdpvr_free_buffers(dev); + + if (dev->video_dev) + video_device_release(dev->video_dev); + + usb_put_dev(dev->udev); +} + +static void challenge(u8 *bytes) +{ + u64 *i64P, tmp64; + uint i, idx; + + for (idx = 0; idx < 32; ++idx) { + + if (idx & 0x3) + bytes[(idx >> 3) + 3] = bytes[(idx >> 2) & 0x3]; + + switch (idx & 0x3) { + case 0x3: + bytes[2] += bytes[3] * 4 + bytes[4] + bytes[5]; + bytes[4] += bytes[(idx & 0x1) * 2] * 9 + 9; + break; + case 0x1: + bytes[0] *= 8; + bytes[0] += 7*idx + 4; + bytes[6] += bytes[3] * 3; + break; + case 0x0: + bytes[3 - (idx >> 3)] = bytes[idx >> 2]; + bytes[5] += bytes[6] * 3; + for (i = 0; i < 3; i++) + bytes[3] *= bytes[3] + 1; + break; + case 0x2: + for (i = 0; i < 3; i++) + bytes[1] *= bytes[6] + 1; + for (i = 0; i < 3; i++) { + i64P = (u64 *)bytes; + tmp64 = le64_to_cpup(i64P); + tmp64 <<= bytes[7] & 0x0f; + *i64P += cpu_to_le64(tmp64); + } + break; + } + } +} + +/* try to init the device like the windows driver */ +static int device_authorization(struct hdpvr_device *dev) +{ + + int ret, retval = -ENOMEM; + char request_type = 0x38, rcv_request = 0x81; + char *response; +#ifdef HDPVR_DEBUG + size_t buf_size = 46; + char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL); + if (!print_buf) { + v4l2_err(&dev->v4l2_dev, "Out of memory\n"); + goto error; + } +#endif + + mutex_lock(&dev->usbc_mutex); + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + rcv_request, 0x80 | request_type, + 0x0400, 0x0003, + dev->usbc_buf, 46, + 10000); + if (ret != 46) { + v4l2_err(&dev->v4l2_dev, + "unexpected answer of status request, len %d\n", ret); + goto error; + } +#ifdef HDPVR_DEBUG + else { + hex_dump_to_buffer(dev->usbc_buf, 46, 16, 1, print_buf, + sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "Status request returned, len %d: %s\n", + ret, print_buf); + } +#endif + if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION) { + dev->flags &= ~HDPVR_FLAG_AC3_CAP; + } else if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION_AC3) { + dev->flags |= HDPVR_FLAG_AC3_CAP; + } else if (dev->usbc_buf[1] > HDPVR_FIRMWARE_VERSION_AC3) { + v4l2_info(&dev->v4l2_dev, "untested firmware version 0x%x, " + "the driver might not work\n", dev->usbc_buf[1]); + dev->flags |= HDPVR_FLAG_AC3_CAP; + } else { + v4l2_err(&dev->v4l2_dev, "unknown firmware version 0x%x\n", + dev->usbc_buf[1]); + ret = -EINVAL; + goto error; + } + + response = dev->usbc_buf+38; +#ifdef HDPVR_DEBUG + hex_dump_to_buffer(response, 8, 16, 1, print_buf, sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %s\n", + print_buf); +#endif + challenge(response); +#ifdef HDPVR_DEBUG + hex_dump_to_buffer(response, 8, 16, 1, print_buf, sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %s\n", + print_buf); +#endif + + msleep(100); + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xd1, 0x00 | request_type, + 0x0000, 0x0000, + response, 8, + 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: + return retval; +} + +static int hdpvr_device_init(struct hdpvr_device *dev) +{ + int ret; + u8 *buf; + struct hdpvr_video_info *vidinf; + + if (device_authorization(dev)) + return -EACCES; + + /* default options for init */ + hdpvr_set_options(dev); + + /* set filter options */ + mutex_lock(&dev->usbc_mutex); + buf = dev->usbc_buf; + buf[0] = 0x03; buf[1] = 0x03; buf[2] = 0x00; buf[3] = 0x00; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0x01, 0x38, + CTRL_LOW_PASS_FILTER_VALUE, CTRL_DEFAULT_INDEX, + buf, 4, + 1000); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "control request returned %d\n", ret); + mutex_unlock(&dev->usbc_mutex); + + vidinf = get_video_info(dev); + if (!vidinf) + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "no valid video signal or device init failed\n"); + else + kfree(vidinf); + + /* enable fan and bling leds */ + mutex_lock(&dev->usbc_mutex); + buf[0] = 0x1; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xd4, 0x38, 0, 0, buf, 1, + 1000); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "control request returned %d\n", ret); + + /* boost analog audio */ + buf[0] = boost_audio; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xd5, 0x38, 0, 0, buf, 1, + 1000); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "control request returned %d\n", ret); + mutex_unlock(&dev->usbc_mutex); + + dev->status = STATUS_IDLE; + return 0; +} + +static const struct hdpvr_options hdpvr_default_options = { + .video_std = HDPVR_60HZ, + .video_input = HDPVR_COMPONENT, + .audio_input = HDPVR_RCA_BACK, + .bitrate = 65, /* 6 mbps */ + .peak_bitrate = 90, /* 9 mbps */ + .bitrate_mode = HDPVR_CONSTANT, + .gop_mode = HDPVR_SIMPLE_IDR_GOP, + .audio_codec = V4L2_MPEG_AUDIO_ENCODING_AAC, + .brightness = 0x86, + .contrast = 0x80, + .hue = 0x80, + .saturation = 0x80, + .sharpness = 0x80, +}; + +static int hdpvr_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct hdpvr_device *dev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + size_t buffer_size; + int i; + int retval = -ENOMEM; + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + err("Out of memory"); + goto error; + } + + /* register v4l2_device early so it can be used for printks */ + if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) { + err("v4l2_device_register failed"); + goto error; + } + + mutex_init(&dev->io_mutex); + mutex_init(&dev->i2c_mutex); + mutex_init(&dev->usbc_mutex); + dev->usbc_buf = kmalloc(64, GFP_KERNEL); + if (!dev->usbc_buf) { + v4l2_err(&dev->v4l2_dev, "Out of memory\n"); + goto error; + } + + init_waitqueue_head(&dev->wait_buffer); + init_waitqueue_head(&dev->wait_data); + + dev->workqueue = create_singlethread_workqueue("hdpvr_buffer"); + if (!dev->workqueue) + goto error; + + /* init video transfer queues */ + INIT_LIST_HEAD(&dev->free_buff_list); + INIT_LIST_HEAD(&dev->rec_buff_list); + + dev->options = hdpvr_default_options; + + if (default_video_input < HDPVR_VIDEO_INPUTS) + dev->options.video_input = default_video_input; + + if (default_audio_input < HDPVR_AUDIO_INPUTS) + dev->options.audio_input = default_audio_input; + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + + /* set up the endpoint information */ + /* use only the first bulk-in and bulk-out endpoints */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in_endpointAddr && + usb_endpoint_is_bulk_in(endpoint)) { + /* USB interface description is buggy, reported max + * packet size is 512 bytes, windows driver uses 8192 */ + buffer_size = 8192; + dev->bulk_in_size = buffer_size; + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; + } + + } + if (!dev->bulk_in_endpointAddr) { + v4l2_err(&dev->v4l2_dev, "Could not find bulk-in endpoint\n"); + goto error; + } + + /* init the device */ + if (hdpvr_device_init(dev)) { + v4l2_err(&dev->v4l2_dev, "device init failed\n"); + goto error; + } + + mutex_lock(&dev->io_mutex); + if (hdpvr_alloc_buffers(dev, NUM_BUFFERS)) { + v4l2_err(&dev->v4l2_dev, + "allocating transfer buffers failed\n"); + goto error; + } + mutex_unlock(&dev->io_mutex); + + if (hdpvr_register_videodev(dev, &interface->dev, + video_nr[atomic_inc_return(&dev_nr)])) { + v4l2_err(&dev->v4l2_dev, "registering videodev failed\n"); + goto error; + } + +#ifdef CONFIG_I2C + /* until i2c is working properly */ + retval = 0; /* hdpvr_register_i2c_adapter(dev); */ + if (retval < 0) { + v4l2_err(&dev->v4l2_dev, "registering i2c adapter failed\n"); + goto error; + } +#endif /* CONFIG_I2C */ + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* let the user know what node this device is now attached to */ + v4l2_info(&dev->v4l2_dev, "device now attached to /dev/video%d\n", + dev->video_dev->minor); + return 0; + +error: + if (dev) { + mutex_unlock(&dev->io_mutex); + /* this frees allocated memory */ + hdpvr_delete(dev); + } + return retval; +} + +static void hdpvr_disconnect(struct usb_interface *interface) +{ + struct hdpvr_device *dev; + int minor; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + minor = dev->video_dev->minor; + + /* prevent more I/O from starting and stop any ongoing */ + mutex_lock(&dev->io_mutex); + dev->status = STATUS_DISCONNECTED; + v4l2_device_disconnect(&dev->v4l2_dev); + video_unregister_device(dev->video_dev); + wake_up_interruptible(&dev->wait_data); + wake_up_interruptible(&dev->wait_buffer); + mutex_unlock(&dev->io_mutex); + msleep(100); + flush_workqueue(dev->workqueue); + mutex_lock(&dev->io_mutex); + hdpvr_cancel_queue(dev); + destroy_workqueue(dev->workqueue); + mutex_unlock(&dev->io_mutex); + + /* deregister I2C adapter */ +#ifdef CONFIG_I2C + mutex_lock(&dev->i2c_mutex); + if (dev->i2c_adapter) + i2c_del_adapter(dev->i2c_adapter); + kfree(dev->i2c_adapter); + dev->i2c_adapter = NULL; + mutex_unlock(&dev->i2c_mutex); +#endif /* CONFIG_I2C */ + + atomic_dec(&dev_nr); + + v4l2_info(&dev->v4l2_dev, "device /dev/video%d disconnected\n", minor); + + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev->usbc_buf); + kfree(dev); +} + + +static struct usb_driver hdpvr_usb_driver = { + .name = "hdpvr", + .probe = hdpvr_probe, + .disconnect = hdpvr_disconnect, + .id_table = hdpvr_table, +}; + +static int __init hdpvr_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&hdpvr_usb_driver); + if (result) + err("usb_register failed. Error number %d", result); + + return result; +} + +static void __exit hdpvr_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&hdpvr_usb_driver); +} + +module_init(hdpvr_init); +module_exit(hdpvr_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Janne Grunau"); +MODULE_DESCRIPTION("Hauppauge HD PVR driver"); diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c new file mode 100644 index 0000000..c4b5d15 --- /dev/null +++ b/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -0,0 +1,145 @@ + +/* + * Hauppauge HD PVR USB driver + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include <linux/i2c.h> + +#include "hdpvr.h" + +#define CTRL_READ_REQUEST 0xb8 +#define CTRL_WRITE_REQUEST 0x38 + +#define REQTYPE_I2C_READ 0xb1 +#define REQTYPE_I2C_WRITE 0xb0 +#define REQTYPE_I2C_WRITE_STATT 0xd0 + +static int hdpvr_i2c_read(struct hdpvr_device *dev, unsigned char addr, + char *data, int len) +{ + int ret; + char *buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + REQTYPE_I2C_READ, CTRL_READ_REQUEST, + 0x100|addr, 0, buf, len, 1000); + + if (ret == len) { + memcpy(data, buf, len); + ret = 0; + } else if (ret >= 0) + ret = -EIO; + + kfree(buf); + + return ret; +} + +static int hdpvr_i2c_write(struct hdpvr_device *dev, unsigned char addr, + char *data, int len) +{ + int ret; + char *buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, data, len); + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, + 0x100|addr, 0, buf, len, 1000); + + if (ret < 0) + goto error; + + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST, + 0, 0, buf, 2, 1000); + + if (ret == 2) + ret = 0; + else if (ret >= 0) + ret = -EIO; + +error: + kfree(buf); + return ret; +} + +static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs, + int num) +{ + struct hdpvr_device *dev = i2c_get_adapdata(i2c_adapter); + int retval = 0, i, addr; + + if (num <= 0) + return 0; + + mutex_lock(&dev->i2c_mutex); + + for (i = 0; i < num && !retval; i++) { + addr = msgs[i].addr << 1; + + if (msgs[i].flags & I2C_M_RD) + retval = hdpvr_i2c_read(dev, addr, msgs[i].buf, + msgs[i].len); + else + retval = hdpvr_i2c_write(dev, addr, msgs[i].buf, + msgs[i].len); + } + + mutex_unlock(&dev->i2c_mutex); + + return retval ? retval : num; +} + +static u32 hdpvr_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm hdpvr_algo = { + .master_xfer = hdpvr_transfer, + .functionality = hdpvr_functionality, +}; + +int hdpvr_register_i2c_adapter(struct hdpvr_device *dev) +{ + struct i2c_adapter *i2c_adap; + int retval = -ENOMEM; + + i2c_adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (i2c_adap == NULL) + goto error; + + strlcpy(i2c_adap->name, "Hauppauge HD PVR I2C", + 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; + + i2c_set_adapdata(i2c_adap, dev); + + retval = i2c_add_adapter(i2c_adap); + + if (!retval) + dev->i2c_adapter = i2c_adap; + else + kfree(i2c_adap); + +error: + return retval; +} diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c new file mode 100644 index 0000000..3e6ffee --- /dev/null +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -0,0 +1,1248 @@ +/* + * Hauppauge HD PVR USB driver - video 4 linux 2 interface + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/usb.h> +#include <linux/mutex.h> +#include <linux/version.h> +#include <linux/workqueue.h> + +#include <linux/videodev2.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include "hdpvr.h" + +#define BULK_URB_TIMEOUT 1250 /* 1.25 seconds */ + +#define print_buffer_status() { \ + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, \ + "%s:%d buffer stat: %d free, %d proc\n", \ + __func__, __LINE__, \ + list_size(&dev->free_buff_list), \ + list_size(&dev->rec_buff_list)); } + +struct hdpvr_fh { + struct hdpvr_device *dev; +}; + +static uint list_size(struct list_head *list) +{ + struct list_head *tmp; + uint count = 0; + + list_for_each(tmp, list) { + count++; + } + + return count; +} + +/*=========================================================================*/ +/* urb callback */ +static void hdpvr_read_bulk_callback(struct urb *urb) +{ + struct hdpvr_buffer *buf = (struct hdpvr_buffer *)urb->context; + struct hdpvr_device *dev = buf->dev; + + /* marking buffer as received and wake waiting */ + buf->status = BUFSTAT_READY; + wake_up_interruptible(&dev->wait_data); +} + +/*=========================================================================*/ +/* bufffer bits */ + +/* function expects dev->io_mutex to be hold by caller */ +int hdpvr_cancel_queue(struct hdpvr_device *dev) +{ + struct hdpvr_buffer *buf; + + list_for_each_entry(buf, &dev->rec_buff_list, buff_list) { + usb_kill_urb(buf->urb); + buf->status = BUFSTAT_AVAILABLE; + } + + list_splice_init(&dev->rec_buff_list, dev->free_buff_list.prev); + + return 0; +} + +static int hdpvr_free_queue(struct list_head *q) +{ + struct list_head *tmp; + struct list_head *p; + struct hdpvr_buffer *buf; + struct urb *urb; + + for (p = q->next; p != q;) { + buf = list_entry(p, struct hdpvr_buffer, buff_list); + + urb = buf->urb; + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + tmp = p->next; + list_del(p); + kfree(buf); + p = tmp; + } + + return 0; +} + +/* function expects dev->io_mutex to be hold by caller */ +int hdpvr_free_buffers(struct hdpvr_device *dev) +{ + hdpvr_cancel_queue(dev); + + hdpvr_free_queue(&dev->free_buff_list); + hdpvr_free_queue(&dev->rec_buff_list); + + return 0; +} + +/* function expects dev->io_mutex to be hold by caller */ +int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) +{ + uint i; + int retval = -ENOMEM; + u8 *mem; + struct hdpvr_buffer *buf; + struct urb *urb; + + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "allocating %u buffers\n", count); + + for (i = 0; i < count; i++) { + + buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL); + if (!buf) { + v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n"); + goto exit; + } + buf->dev = dev; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n"); + goto exit; + } + buf->urb = urb; + + mem = usb_buffer_alloc(dev->udev, dev->bulk_in_size, GFP_KERNEL, + &urb->transfer_dma); + if (!mem) { + v4l2_err(&dev->v4l2_dev, + "cannot allocate usb transfer buffer\n"); + goto exit; + } + + usb_fill_bulk_urb(buf->urb, dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpointAddr), + mem, dev->bulk_in_size, + hdpvr_read_bulk_callback, buf); + + buf->status = BUFSTAT_AVAILABLE; + list_add_tail(&buf->buff_list, &dev->free_buff_list); + } + return 0; +exit: + hdpvr_free_buffers(dev); + return retval; +} + +static int hdpvr_submit_buffers(struct hdpvr_device *dev) +{ + struct hdpvr_buffer *buf; + struct urb *urb; + int ret = 0, err_count = 0; + + mutex_lock(&dev->io_mutex); + + while (dev->status == STATUS_STREAMING && + !list_empty(&dev->free_buff_list)) { + + buf = list_entry(dev->free_buff_list.next, struct hdpvr_buffer, + buff_list); + if (buf->status != BUFSTAT_AVAILABLE) { + v4l2_err(&dev->v4l2_dev, + "buffer not marked as availbale\n"); + ret = -EFAULT; + goto err; + } + + urb = buf->urb; + urb->status = 0; + urb->actual_length = 0; + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "usb_submit_urb in %s returned %d\n", + __func__, ret); + if (++err_count > 2) + break; + continue; + } + buf->status = BUFSTAT_INPROGRESS; + list_move_tail(&buf->buff_list, &dev->rec_buff_list); + } +err: + print_buffer_status(); + mutex_unlock(&dev->io_mutex); + return ret; +} + +static struct hdpvr_buffer *hdpvr_get_next_buffer(struct hdpvr_device *dev) +{ + struct hdpvr_buffer *buf; + + mutex_lock(&dev->io_mutex); + + if (list_empty(&dev->rec_buff_list)) { + mutex_unlock(&dev->io_mutex); + return NULL; + } + + buf = list_entry(dev->rec_buff_list.next, struct hdpvr_buffer, + buff_list); + mutex_unlock(&dev->io_mutex); + + return buf; +} + +static void hdpvr_transmit_buffers(struct work_struct *work) +{ + struct hdpvr_device *dev = container_of(work, struct hdpvr_device, + worker); + + while (dev->status == STATUS_STREAMING) { + + if (hdpvr_submit_buffers(dev)) { + v4l2_err(&dev->v4l2_dev, "couldn't submit buffers\n"); + goto error; + } + if (wait_event_interruptible(dev->wait_buffer, + !list_empty(&dev->free_buff_list) || + dev->status != STATUS_STREAMING)) + goto error; + } + + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "transmit worker exited\n"); + return; +error: + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "transmit buffers errored\n"); + dev->status = STATUS_ERROR; +} + +/* function expects dev->io_mutex to be hold by caller */ +static int hdpvr_start_streaming(struct hdpvr_device *dev) +{ + int ret; + struct hdpvr_video_info *vidinf; + + if (dev->status == STATUS_STREAMING) + return 0; + else if (dev->status != STATUS_IDLE) + return -EAGAIN; + + vidinf = get_video_info(dev); + + if (vidinf) { + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "video signal: %dx%d@%dhz\n", vidinf->width, + vidinf->height, vidinf->fps); + kfree(vidinf); + + /* start streaming 2 request */ + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xb8, 0x38, 0x1, 0, NULL, 0, 8000); + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "encoder start control request returned %d\n", ret); + + hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00); + + INIT_WORK(&dev->worker, hdpvr_transmit_buffers); + queue_work(dev->workqueue, &dev->worker); + + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "streaming started\n"); + dev->status = STATUS_STREAMING; + + return 0; + } + msleep(250); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "no video signal at input %d\n", dev->options.video_input); + return -EAGAIN; +} + + +/* function expects dev->io_mutex to be hold by caller */ +static int hdpvr_stop_streaming(struct hdpvr_device *dev) +{ + uint actual_length, c = 0; + u8 *buf; + + if (dev->status == STATUS_IDLE) + return 0; + else if (dev->status != STATUS_STREAMING) + return -EAGAIN; + + buf = kmalloc(dev->bulk_in_size, GFP_KERNEL); + if (!buf) + v4l2_err(&dev->v4l2_dev, "failed to allocate temporary buffer " + "for emptying the internal device buffer. " + "Next capture start will be slow\n"); + + dev->status = STATUS_SHUTTING_DOWN; + hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00); + mutex_unlock(&dev->io_mutex); + + wake_up_interruptible(&dev->wait_buffer); + msleep(50); + + flush_workqueue(dev->workqueue); + + mutex_lock(&dev->io_mutex); + /* kill the still outstanding urbs */ + hdpvr_cancel_queue(dev); + + /* emptying the device buffer beforeshutting it down */ + while (buf && ++c < 500 && + !usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpointAddr), + buf, dev->bulk_in_size, &actual_length, + BULK_URB_TIMEOUT)) { + /* wait */ + msleep(5); + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "%2d: got %d bytes\n", c, actual_length); + } + kfree(buf); + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "used %d urbs to empty device buffers\n", c-1); + msleep(10); + + dev->status = STATUS_IDLE; + + return 0; +} + + +/*=======================================================================*/ +/* + * video 4 linux 2 file operations + */ + +static int hdpvr_open(struct file *file) +{ + struct hdpvr_device *dev; + struct hdpvr_fh *fh; + int retval = -ENOMEM; + + dev = (struct hdpvr_device *)video_get_drvdata(video_devdata(file)); + if (!dev) { + v4l2_err(&dev->v4l2_dev, "open failing with with ENODEV\n"); + retval = -ENODEV; + goto err; + } + + fh = kzalloc(sizeof(struct hdpvr_fh), GFP_KERNEL); + if (!fh) { + v4l2_err(&dev->v4l2_dev, "Out of memory\n"); + goto err; + } + /* lock the device to allow correctly handling errors + * in resumption */ + mutex_lock(&dev->io_mutex); + dev->open_count++; + + fh->dev = dev; + + /* save our object in the file's private structure */ + file->private_data = fh; + + retval = 0; +err: + mutex_unlock(&dev->io_mutex); + return retval; +} + +static int hdpvr_release(struct file *file) +{ + struct hdpvr_fh *fh = (struct hdpvr_fh *)file->private_data; + struct hdpvr_device *dev = fh->dev; + + if (!dev) + return -ENODEV; + + mutex_lock(&dev->io_mutex); + if (!(--dev->open_count) && dev->status == STATUS_STREAMING) + hdpvr_stop_streaming(dev); + + mutex_unlock(&dev->io_mutex); + + return 0; +} + +/* + * hdpvr_v4l2_read() + * will allocate buffers when called for the first time + */ +static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count, + loff_t *pos) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + struct hdpvr_buffer *buf = NULL; + struct urb *urb; + unsigned int ret = 0; + int rem, cnt; + + if (*pos) + return -ESPIPE; + + if (!dev) + return -ENODEV; + + mutex_lock(&dev->io_mutex); + if (dev->status == STATUS_IDLE) { + if (hdpvr_start_streaming(dev)) { + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "start_streaming failed\n"); + ret = -EIO; + msleep(200); + dev->status = STATUS_IDLE; + mutex_unlock(&dev->io_mutex); + goto err; + } + print_buffer_status(); + } + mutex_unlock(&dev->io_mutex); + + /* wait for the first buffer */ + if (!(file->f_flags & O_NONBLOCK)) { + if (wait_event_interruptible(dev->wait_data, + hdpvr_get_next_buffer(dev))) + return -ERESTARTSYS; + } + + buf = hdpvr_get_next_buffer(dev); + + while (count > 0 && buf) { + + if (buf->status != BUFSTAT_READY && + dev->status != STATUS_DISCONNECTED) { + /* return nonblocking */ + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto err; + } + + if (wait_event_interruptible(dev->wait_data, + buf->status == BUFSTAT_READY)) { + ret = -ERESTARTSYS; + goto err; + } + } + + if (buf->status != BUFSTAT_READY) + break; + + /* set remaining bytes to copy */ + urb = buf->urb; + rem = urb->actual_length - buf->pos; + cnt = rem > count ? count : rem; + + if (copy_to_user(buffer, urb->transfer_buffer + buf->pos, + cnt)) { + v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n"); + if (!ret) + ret = -EFAULT; + goto err; + } + + buf->pos += cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + + /* finished, take next buffer */ + if (buf->pos == urb->actual_length) { + mutex_lock(&dev->io_mutex); + buf->pos = 0; + buf->status = BUFSTAT_AVAILABLE; + + list_move_tail(&buf->buff_list, &dev->free_buff_list); + + print_buffer_status(); + + mutex_unlock(&dev->io_mutex); + + wake_up_interruptible(&dev->wait_buffer); + + buf = hdpvr_get_next_buffer(dev); + } + } +err: + if (!ret && !buf) + ret = -EAGAIN; + return ret; +} + +static unsigned int hdpvr_poll(struct file *filp, poll_table *wait) +{ + struct hdpvr_buffer *buf = NULL; + struct hdpvr_fh *fh = (struct hdpvr_fh *)filp->private_data; + struct hdpvr_device *dev = fh->dev; + unsigned int mask = 0; + + mutex_lock(&dev->io_mutex); + + if (video_is_unregistered(dev->video_dev)) + return -EIO; + + if (dev->status == STATUS_IDLE) { + if (hdpvr_start_streaming(dev)) { + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "start_streaming failed\n"); + dev->status = STATUS_IDLE; + } + + print_buffer_status(); + } + mutex_unlock(&dev->io_mutex); + + buf = hdpvr_get_next_buffer(dev); + /* only wait if no data is available */ + if (!buf || buf->status != BUFSTAT_READY) { + poll_wait(filp, &dev->wait_data, wait); + buf = hdpvr_get_next_buffer(dev); + } + if (buf && buf->status == BUFSTAT_READY) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + + +static const struct v4l2_file_operations hdpvr_fops = { + .owner = THIS_MODULE, + .open = hdpvr_open, + .release = hdpvr_release, + .read = hdpvr_read, + .poll = hdpvr_poll, + .unlocked_ioctl = video_ioctl2, +}; + +/*=======================================================================*/ +/* + * V4L2 ioctl handling + */ + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct hdpvr_device *dev = video_drvdata(file); + + strcpy(cap->driver, "hdpvr"); + strcpy(cap->card, "Haupauge HD PVR"); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->version = HDPVR_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE; + return 0; +} + +static int vidioc_s_std(struct file *file, void *private_data, + v4l2_std_id *std) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + u8 std_type = 1; + + if (*std & (V4L2_STD_NTSC | V4L2_STD_PAL_60)) + std_type = 0; + + return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type); +} + +static const char *iname[] = { + [HDPVR_COMPONENT] = "Component", + [HDPVR_SVIDEO] = "S-Video", + [HDPVR_COMPOSITE] = "Composite", +}; + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + unsigned int n; + + n = i->index; + if (n >= HDPVR_VIDEO_INPUTS) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_CAMERA; + + strncpy(i->name, iname[n], sizeof(i->name) - 1); + i->name[sizeof(i->name) - 1] = '\0'; + + i->audioset = 1<<HDPVR_RCA_FRONT | 1<<HDPVR_RCA_BACK | 1<<HDPVR_SPDIF; + + i->std = dev->video_dev->tvnorms; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *private_data, + unsigned int index) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int retval; + + if (index >= HDPVR_VIDEO_INPUTS) + return -EINVAL; + + if (dev->status != STATUS_IDLE) + return -EAGAIN; + + retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1); + if (!retval) + dev->options.video_input = index; + + return retval; +} + +static int vidioc_g_input(struct file *file, void *private_data, + unsigned int *index) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + + *index = dev->options.video_input; + return 0; +} + + +static const char *audio_iname[] = { + [HDPVR_RCA_FRONT] = "RCA front", + [HDPVR_RCA_BACK] = "RCA back", + [HDPVR_SPDIF] = "SPDIF", +}; + +static int vidioc_enumaudio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + unsigned int n; + + n = audio->index; + if (n >= HDPVR_AUDIO_INPUTS) + return -EINVAL; + + audio->capability = V4L2_AUDCAP_STEREO; + + strncpy(audio->name, audio_iname[n], sizeof(audio->name) - 1); + audio->name[sizeof(audio->name) - 1] = '\0'; + + return 0; +} + +static int vidioc_s_audio(struct file *file, void *private_data, + struct v4l2_audio *audio) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int retval; + + if (audio->index >= HDPVR_AUDIO_INPUTS) + return -EINVAL; + + if (dev->status != STATUS_IDLE) + return -EAGAIN; + + retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec); + if (!retval) + dev->options.audio_input = audio->index; + + return retval; +} + +static int vidioc_g_audio(struct file *file, void *private_data, + struct v4l2_audio *audio) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + + audio->index = dev->options.audio_input; + audio->capability = V4L2_AUDCAP_STEREO; + strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name)); + audio->name[sizeof(audio->name) - 1] = '\0'; + return 0; +} + +static const s32 supported_v4l2_ctrls[] = { + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_SHARPNESS, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_CID_MPEG_VIDEO_BITRATE, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, +}; + +static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc, + int ac3) +{ + int err; + + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_SHARPNESS: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_MPEG_AUDIO_ENCODING: + return v4l2_ctrl_query_fill( + qc, V4L2_MPEG_AUDIO_ENCODING_AAC, + ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 + : V4L2_MPEG_AUDIO_ENCODING_AAC, + 1, V4L2_MPEG_AUDIO_ENCODING_AAC); + case V4L2_CID_MPEG_VIDEO_ENCODING: + return v4l2_ctrl_query_fill( + qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC); + +/* case V4L2_CID_MPEG_VIDEO_? maybe keyframe interval: */ +/* return v4l2_ctrl_query_fill(qc, 0, 128, 128, 0); */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + return v4l2_ctrl_query_fill( + qc, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(qc, 1000000, 13500000, 100000, + 6500000); + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + err = v4l2_ctrl_query_fill(qc, 1100000, 20200000, 100000, + 9000000); + if (!err && opt->bitrate_mode == HDPVR_CONSTANT) + qc->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + default: + return -EINVAL; + } +} + +static int vidioc_queryctrl(struct file *file, void *private_data, + struct v4l2_queryctrl *qc) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, next; + u32 id = qc->id; + + memset(qc, 0, sizeof(*qc)); + + next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL); + qc->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL; + + for (i = 0; i < ARRAY_SIZE(supported_v4l2_ctrls); i++) { + if (next) { + if (qc->id < supported_v4l2_ctrls[i]) + qc->id = supported_v4l2_ctrls[i]; + else + continue; + } + + if (qc->id == supported_v4l2_ctrls[i]) + return fill_queryctrl(&dev->options, qc, + dev->flags & HDPVR_FLAG_AC3_CAP); + + if (qc->id < supported_v4l2_ctrls[i]) + break; + } + + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *private_data, + struct v4l2_control *ctrl) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dev->options.brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dev->options.contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = dev->options.saturation; + break; + case V4L2_CID_HUE: + ctrl->value = dev->options.hue; + break; + case V4L2_CID_SHARPNESS: + ctrl->value = dev->options.sharpness; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *private_data, + struct v4l2_control *ctrl) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int retval; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + retval = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->value); + if (!retval) + dev->options.brightness = ctrl->value; + break; + case V4L2_CID_CONTRAST: + retval = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->value); + if (!retval) + dev->options.contrast = ctrl->value; + break; + case V4L2_CID_SATURATION: + retval = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->value); + if (!retval) + dev->options.saturation = ctrl->value; + break; + case V4L2_CID_HUE: + retval = hdpvr_config_call(dev, CTRL_HUE, ctrl->value); + if (!retval) + dev->options.hue = ctrl->value; + break; + case V4L2_CID_SHARPNESS: + retval = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->value); + if (!retval) + dev->options.sharpness = ctrl->value; + break; + default: + return -EINVAL; + } + + return retval; +} + + +static int hdpvr_get_ctrl(struct hdpvr_options *opt, + struct v4l2_ext_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + ctrl->value = opt->audio_codec; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC; + break; +/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ +/* ctrl->value = (opt->gop_mode & 0x2) ? 0 : 128; */ +/* break; */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctrl->value = opt->bitrate_mode == HDPVR_CONSTANT + ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR + : V4L2_MPEG_VIDEO_BITRATE_MODE_VBR; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrl->value = opt->bitrate * 100000; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + ctrl->value = opt->peak_bitrate * 100000; + break; + case V4L2_CID_MPEG_STREAM_TYPE: + ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, err = 0; + + if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = hdpvr_get_ctrl(&dev->options, ctrl); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + + } + + return -EINVAL; +} + + +static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) +{ + int ret = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AAC || + (ac3 && ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AC3)) + ret = 0; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + if (ctrl->value == V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC) + ret = 0; + break; +/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ +/* if (ctrl->value == 0 || ctrl->value == 128) */ +/* ret = 0; */ +/* break; */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR || + ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) + ret = 0; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + { + uint bitrate = ctrl->value / 100000; + if (bitrate >= 10 && bitrate <= 135) + ret = 0; + break; + } + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + { + uint peak_bitrate = ctrl->value / 100000; + if (peak_bitrate >= 10 && peak_bitrate <= 202) + ret = 0; + break; + } + case V4L2_CID_MPEG_STREAM_TYPE: + if (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) + ret = 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_try_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, err = 0; + + if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = hdpvr_try_ctrl(ctrl, + dev->flags & HDPVR_FLAG_AC3_CAP); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + } + + return -EINVAL; +} + + +static int hdpvr_set_ctrl(struct hdpvr_device *dev, + struct v4l2_ext_control *ctrl) +{ + struct hdpvr_options *opt = &dev->options; + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (dev->flags & HDPVR_FLAG_AC3_CAP) { + opt->audio_codec = ctrl->value; + ret = hdpvr_set_audio(dev, opt->audio_input, + opt->audio_codec); + } + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + break; +/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ +/* if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */ +/* opt->gop_mode |= 0x2; */ +/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */ +/* opt->gop_mode); */ +/* } */ +/* if (ctrl->value == 128 && opt->gop_mode & 0x2) { */ +/* opt->gop_mode &= ~0x2; */ +/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */ +/* opt->gop_mode); */ +/* } */ +/* break; */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR && + opt->bitrate_mode != HDPVR_CONSTANT) { + opt->bitrate_mode = HDPVR_CONSTANT; + hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, + opt->bitrate_mode); + } + if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + opt->bitrate_mode == HDPVR_CONSTANT) { + opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE; + hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, + opt->bitrate_mode); + } + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: { + uint bitrate = ctrl->value / 100000; + + opt->bitrate = bitrate; + if (bitrate >= opt->peak_bitrate) + opt->peak_bitrate = bitrate+1; + + hdpvr_set_bitrate(dev); + break; + } + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: { + uint peak_bitrate = ctrl->value / 100000; + + if (opt->bitrate_mode == HDPVR_CONSTANT) + break; + + if (opt->bitrate < peak_bitrate) { + opt->peak_bitrate = peak_bitrate; + hdpvr_set_bitrate(dev); + } else + ret = -EINVAL; + break; + } + case V4L2_CID_MPEG_STREAM_TYPE: + break; + default: + return -EINVAL; + } + return ret; +} + +static int vidioc_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, err = 0; + + if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = hdpvr_try_ctrl(ctrl, + dev->flags & HDPVR_FLAG_AC3_CAP); + if (err) { + ctrls->error_idx = i; + break; + } + err = hdpvr_set_ctrl(dev, ctrl); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + + } + + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data, + struct v4l2_fmtdesc *f) +{ + + if (f->index != 0 || f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strncpy(f->description, "MPEG2-TS with AVC/AAC streams", 32); + f->pixelformat = V4L2_PIX_FMT_MPEG; + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data, + struct v4l2_format *f) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + struct hdpvr_video_info *vid_info; + + if (!dev) + return -ENODEV; + + vid_info = get_video_info(dev); + if (!vid_info) + return -EFAULT; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.width = vid_info->width; + f->fmt.pix.height = vid_info->height; + f->fmt.pix.sizeimage = dev->bulk_in_size; + f->fmt.pix.colorspace = 0; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.field = V4L2_FIELD_ANY; + + kfree(vid_info); + return 0; +} + +static int vidioc_encoder_cmd(struct file *filp, void *priv, + struct v4l2_encoder_cmd *a) +{ + struct hdpvr_fh *fh = filp->private_data; + struct hdpvr_device *dev = fh->dev; + int res; + + mutex_lock(&dev->io_mutex); + + memset(&a->raw, 0, sizeof(a->raw)); + switch (a->cmd) { + case V4L2_ENC_CMD_START: + a->flags = 0; + res = hdpvr_start_streaming(dev); + break; + case V4L2_ENC_CMD_STOP: + res = hdpvr_stop_streaming(dev); + break; + default: + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "Unsupported encoder cmd %d\n", a->cmd); + res = -EINVAL; + } + mutex_unlock(&dev->io_mutex); + return res; +} + +static int vidioc_try_encoder_cmd(struct file *filp, void *priv, + struct v4l2_encoder_cmd *a) +{ + switch (a->cmd) { + case V4L2_ENC_CMD_START: + case V4L2_ENC_CMD_STOP: + return 0; + default: + return -EINVAL; + } +} + +static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_enumaudio = vidioc_enumaudio, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, + .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, + .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_encoder_cmd = vidioc_encoder_cmd, + .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd, +}; + +static void hdpvr_device_release(struct video_device *vdev) +{ + struct hdpvr_device *dev = video_get_drvdata(vdev); + + hdpvr_delete(dev); +} + +static const struct video_device hdpvr_video_template = { +/* .type = VFL_TYPE_GRABBER, */ +/* .type2 = VID_TYPE_CAPTURE | VID_TYPE_MPEG_ENCODER, */ + .fops = &hdpvr_fops, + .release = hdpvr_device_release, + .ioctl_ops = &hdpvr_ioctl_ops, + .tvnorms = + V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_B | + V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I | + V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N | + V4L2_STD_PAL_60, +}; + +int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, + int devnum) +{ + /* setup and register video device */ + dev->video_dev = video_device_alloc(); + if (!dev->video_dev) { + v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n"); + goto error; + } + + *(dev->video_dev) = hdpvr_video_template; + strcpy(dev->video_dev->name, "Hauppauge HD PVR"); + dev->video_dev->parent = parent; + video_set_drvdata(dev->video_dev, dev); + + if (video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum)) { + v4l2_err(&dev->v4l2_dev, "video_device registration failed\n"); + goto error; + } + + return 0; +error: + return -ENOMEM; +} diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h new file mode 100644 index 0000000..1edd875 --- /dev/null +++ b/drivers/media/video/hdpvr/hdpvr.h @@ -0,0 +1,303 @@ +/* + * Hauppauge HD PVR USB driver + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/videodev2.h> + +#include <media/v4l2-device.h> + +#define HDPVR_MAJOR_VERSION 0 +#define HDPVR_MINOR_VERSION 2 +#define HDPVR_RELEASE 0 +#define HDPVR_VERSION \ + KERNEL_VERSION(HDPVR_MAJOR_VERSION, HDPVR_MINOR_VERSION, HDPVR_RELEASE) + +#define HDPVR_MAX 8 + +/* Define these values to match your devices */ +#define HD_PVR_VENDOR_ID 0x2040 +#define HD_PVR_PRODUCT_ID 0x4900 +#define HD_PVR_PRODUCT_ID1 0x4901 +#define HD_PVR_PRODUCT_ID2 0x4902 + +#define UNSET (-1U) + +#define NUM_BUFFERS 64 + +#define HDPVR_FIRMWARE_VERSION 0x8 +#define HDPVR_FIRMWARE_VERSION_AC3 0xd + +/* #define HDPVR_DEBUG */ + +extern int hdpvr_debug; + +#define MSG_INFO 1 +#define MSG_BUFFER 2 + +struct hdpvr_options { + u8 video_std; + u8 video_input; + u8 audio_input; + u8 bitrate; /* in 100kbps */ + u8 peak_bitrate; /* in 100kbps */ + u8 bitrate_mode; + u8 gop_mode; + enum v4l2_mpeg_audio_encoding audio_codec; + u8 brightness; + u8 contrast; + u8 hue; + u8 saturation; + u8 sharpness; +}; + +/* Structure to hold all of our device specific stuff */ +struct hdpvr_device { + /* the v4l device for this device */ + struct video_device *video_dev; + /* the usb device for this device */ + struct usb_device *udev; + /* v4l2-device unused */ + struct v4l2_device v4l2_dev; + + /* the max packet size of the bulk endpoint */ + size_t bulk_in_size; + /* the address of the bulk in endpoint */ + __u8 bulk_in_endpointAddr; + + /* holds the current device status */ + __u8 status; + /* count the number of openers */ + uint open_count; + + /* holds the cureent set options */ + struct hdpvr_options options; + + uint flags; + + /* synchronize I/O */ + struct mutex io_mutex; + /* available buffers */ + struct list_head free_buff_list; + /* in progress buffers */ + struct list_head rec_buff_list; + /* waitqueue for buffers */ + wait_queue_head_t wait_buffer; + /* waitqueue for data */ + wait_queue_head_t wait_data; + /**/ + struct workqueue_struct *workqueue; + /**/ + struct work_struct worker; + + /* I2C adapter */ + struct i2c_adapter *i2c_adapter; + /* I2C lock */ + struct mutex i2c_mutex; + + /* usb control transfer buffer and lock */ + struct mutex usbc_mutex; + u8 *usbc_buf; +}; + + +/* buffer one bulk urb of data */ +struct hdpvr_buffer { + struct list_head buff_list; + + struct urb *urb; + + struct hdpvr_device *dev; + + uint pos; + + __u8 status; +}; + +/* */ + +struct hdpvr_video_info { + u16 width; + u16 height; + u8 fps; +}; + +enum { + STATUS_UNINITIALIZED = 0, + STATUS_IDLE, + STATUS_STARTING, + STATUS_SHUTTING_DOWN, + STATUS_STREAMING, + STATUS_ERROR, + STATUS_DISCONNECTED, +}; + +enum { + HDPVR_FLAG_AC3_CAP = 1, +}; + +enum { + BUFSTAT_UNINITIALIZED = 0, + BUFSTAT_AVAILABLE, + BUFSTAT_INPROGRESS, + BUFSTAT_READY, +}; + +#define CTRL_START_STREAMING_VALUE 0x0700 +#define CTRL_STOP_STREAMING_VALUE 0x0800 +#define CTRL_BITRATE_VALUE 0x1000 +#define CTRL_BITRATE_MODE_VALUE 0x1200 +#define CTRL_GOP_MODE_VALUE 0x1300 +#define CTRL_VIDEO_INPUT_VALUE 0x1500 +#define CTRL_VIDEO_STD_TYPE 0x1700 +#define CTRL_AUDIO_INPUT_VALUE 0x2500 +#define CTRL_BRIGHTNESS 0x2900 +#define CTRL_CONTRAST 0x2a00 +#define CTRL_HUE 0x2b00 +#define CTRL_SATURATION 0x2c00 +#define CTRL_SHARPNESS 0x2d00 +#define CTRL_LOW_PASS_FILTER_VALUE 0x3100 + +#define CTRL_DEFAULT_INDEX 0x0003 + + + /* :0 s 38 01 1000 0003 0004 4 = 0a00ca00 + * BITRATE SETTING + * 1st and 2nd byte (little endian): average bitrate in 100 000 bit/s + * min: 1 mbit/s, max: 13.5 mbit/s + * 3rd and 4th byte (little endian): peak bitrate in 100 000 bit/s + * min: average + 100kbit/s, + * max: 20.2 mbit/s + */ + + /* :0 s 38 01 1200 0003 0001 1 = 02 + * BIT RATE MODE + * constant = 1, variable (peak) = 2, variable (average) = 3 + */ + + /* :0 s 38 01 1300 0003 0001 1 = 03 + * GOP MODE (2 bit) + * low bit 0/1: advanced/simple GOP + * high bit 0/1: IDR(4/32/128) / no IDR (4/32/0) + */ + + /* :0 s 38 01 1700 0003 0001 1 = 00 + * VIDEO STANDARD or FREQUNCY 0 = 60hz, 1 = 50hz + */ + + /* :0 s 38 01 3100 0003 0004 4 = 03030000 + * FILTER CONTROL + * 1st byte luma low pass filter strength, + * 2nd byte chroma low pass filter strength, + * 3rd byte MF enable chroma, min=0, max=1 + * 4th byte n + */ + + + /* :0 s 38 b9 0001 0000 0000 0 */ + + + +/* :0 s 38 d3 0000 0000 0001 1 = 00 */ +/* ret = usb_control_msg(dev->udev, */ +/* usb_sndctrlpipe(dev->udev, 0), */ +/* 0xd3, 0x38, */ +/* 0, 0, */ +/* "\0", 1, */ +/* 1000); */ + +/* info("control request returned %d", ret); */ +/* msleep(5000); */ + + + /* :0 s b8 81 1400 0003 0005 5 < + * :0 0 5 = d0024002 19 + * QUERY FRAME SIZE AND RATE + * 1st and 2nd byte (little endian): horizontal resolution + * 3rd and 4th byte (little endian): vertical resolution + * 5th byte: frame rate + */ + + /* :0 s b8 81 1800 0003 0003 3 < + * :0 0 3 = 030104 + * QUERY SIGNAL AND DETECTED LINES, maybe INPUT + */ + +enum hdpvr_video_std { + HDPVR_60HZ = 0, + HDPVR_50HZ, +}; + +enum hdpvr_video_input { + HDPVR_COMPONENT = 0, + HDPVR_SVIDEO, + HDPVR_COMPOSITE, + HDPVR_VIDEO_INPUTS +}; + +enum hdpvr_audio_inputs { + HDPVR_RCA_BACK = 0, + HDPVR_RCA_FRONT, + HDPVR_SPDIF, + HDPVR_AUDIO_INPUTS +}; + +enum hdpvr_bitrate_mode { + HDPVR_CONSTANT = 1, + HDPVR_VARIABLE_PEAK, + HDPVR_VARIABLE_AVERAGE, +}; + +enum hdpvr_gop_mode { + HDPVR_ADVANCED_IDR_GOP = 0, + HDPVR_SIMPLE_IDR_GOP, + HDPVR_ADVANCED_NOIDR_GOP, + HDPVR_SIMPLE_NOIDR_GOP, +}; + +void hdpvr_delete(struct hdpvr_device *dev); + +/*========================================================================*/ +/* hardware control functions */ +int hdpvr_set_options(struct hdpvr_device *dev); + +int hdpvr_set_bitrate(struct hdpvr_device *dev); + +int hdpvr_set_audio(struct hdpvr_device *dev, u8 input, + enum v4l2_mpeg_audio_encoding codec); + +int hdpvr_config_call(struct hdpvr_device *dev, uint value, + unsigned char valbuf); + +struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev); + +/* :0 s b8 81 1800 0003 0003 3 < */ +/* :0 0 3 = 0301ff */ +int get_input_lines_info(struct hdpvr_device *dev); + + +/*========================================================================*/ +/* v4l2 registration */ +int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, + int devnumber); + +int hdpvr_cancel_queue(struct hdpvr_device *dev); + +/*========================================================================*/ +/* i2c adapter registration */ +int hdpvr_register_i2c_adapter(struct hdpvr_device *dev); + +/*========================================================================*/ +/* buffer management */ +int hdpvr_free_buffers(struct hdpvr_device *dev); +int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count); diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index 79393d1..8e1463e 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -56,17 +56,6 @@ struct hexium_data u8 byte; }; -static struct saa7146_extension_ioctls ioctls[] = { - { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_QUERYCTRL, SAA7146_BEFORE }, - { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_S_STD, SAA7146_AFTER }, - { VIDIOC_G_CTRL, SAA7146_BEFORE }, - { VIDIOC_S_CTRL, SAA7146_BEFORE }, - { 0, 0 } -}; - #define HEXIUM_CONTROLS 1 static struct v4l2_queryctrl hexium_controls[] = { { V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 }, @@ -231,6 +220,132 @@ static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec) return 0; } +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index)); + + if (i->index < 0 || i->index >= HEXIUM_INPUTS) + return -EINVAL; + + memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); + + DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index)); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + *input = hexium->cur_input; + + DEB_D(("VIDIOC_G_INPUT: %d\n", *input)); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); + + if (input < 0 || input >= HEXIUM_INPUTS) + return -EINVAL; + + hexium->cur_input = input; + hexium_set_input(hexium, input); + return 0; +} + +/* the saa7146 provides some controls (brightness, contrast, saturation) + which gets registered *after* this function. because of this we have + to return with a value != 0 even if the function succeded.. */ +static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + int i; + + for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { + if (hexium_controls[i].id == qc->id) { + *qc = hexium_controls[i]; + DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id)); + return 0; + } + } + return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc); +} + +static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + int i; + + for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { + if (hexium_controls[i].id == vc->id) + break; + } + + if (i < 0) + return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc); + + if (vc->id == V4L2_CID_PRIVATE_BASE) { + vc->value = hexium->cur_bw; + DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value)); + return 0; + } + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + int i = 0; + + for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { + if (hexium_controls[i].id == vc->id) + break; + } + + if (i < 0) + return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc); + + if (vc->id == V4L2_CID_PRIVATE_BASE) + hexium->cur_bw = vc->value; + + DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw)); + + if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) { + hexium_set_standard(hexium, hexium_pal); + return 0; + } + if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) { + hexium_set_standard(hexium, hexium_ntsc); + return 0; + } + if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) { + hexium_set_standard(hexium, hexium_secam); + return 0; + } + if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) { + hexium_set_standard(hexium, hexium_pal_bw); + return 0; + } + if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) { + hexium_set_standard(hexium, hexium_ntsc_bw); + return 0; + } + if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) + /* fixme: is there no bw secam mode? */ + return -EINVAL; + + return -EINVAL; +} + + static struct saa7146_ext_vv vv_data; /* this function only gets called when the probing was successful */ @@ -279,6 +394,12 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d hexium->cur_input = 0; saa7146_vv_init(dev, &vv_data); + vv_data.ops.vidioc_queryctrl = vidioc_queryctrl; + vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl; + vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl; + vv_data.ops.vidioc_enum_input = vidioc_enum_input; + vv_data.ops.vidioc_g_input = vidioc_g_input; + vv_data.ops.vidioc_s_input = vidioc_s_input; if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER)) { printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n"); return -1; @@ -306,153 +427,6 @@ static int hexium_detach(struct saa7146_dev *dev) return 0; } -static long hexium_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) -{ - struct saa7146_dev *dev = fh->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; -/* - struct saa7146_vv *vv = dev->vv_data; -*/ - switch (cmd) { - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; - DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index)); - - if (i->index < 0 || i->index >= HEXIUM_INPUTS) { - return -EINVAL; - } - - memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); - - DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index)); - return 0; - } - case VIDIOC_G_INPUT: - { - int *input = (int *) arg; - *input = hexium->cur_input; - - DEB_D(("VIDIOC_G_INPUT: %d\n", *input)); - return 0; - } - case VIDIOC_S_INPUT: - { - int input = *(int *) arg; - - DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); - - if (input < 0 || input >= HEXIUM_INPUTS) { - return -EINVAL; - } - - hexium->cur_input = input; - hexium_set_input(hexium, input); - - return 0; - } - /* the saa7146 provides some controls (brightness, contrast, saturation) - which gets registered *after* this function. because of this we have - to return with a value != 0 even if the function succeded.. */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == qc->id) { - *qc = hexium_controls[i]; - DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id)); - return 0; - } - } - return -EAGAIN; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *vc = arg; - int i; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == vc->id) { - break; - } - } - - if (i < 0) { - return -EAGAIN; - } - - switch (vc->id) { - case V4L2_CID_PRIVATE_BASE:{ - vc->value = hexium->cur_bw; - DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value)); - return 0; - } - } - return -EINVAL; - } - - case VIDIOC_S_CTRL: - { - struct v4l2_control *vc = arg; - int i = 0; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == vc->id) { - break; - } - } - - if (i < 0) { - return -EAGAIN; - } - - switch (vc->id) { - case V4L2_CID_PRIVATE_BASE:{ - hexium->cur_bw = vc->value; - break; - } - } - - DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw)); - - if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) { - hexium_set_standard(hexium, hexium_pal); - return 0; - } - if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) { - hexium_set_standard(hexium, hexium_ntsc); - return 0; - } - if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) { - hexium_set_standard(hexium, hexium_secam); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) { - hexium_set_standard(hexium, hexium_pal_bw); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) { - hexium_set_standard(hexium, hexium_ntsc_bw); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) { - /* fixme: is there no bw secam mode? */ - return -EINVAL; - } - - return -EINVAL; - } - default: -/* - DEB_D(("hexium_ioctl() does not handle this ioctl.\n")); -*/ - return -ENOIOCTLCMD; - } - return 0; -} - static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) { struct hexium *hexium = (struct hexium *) dev->ext_priv; @@ -514,8 +488,6 @@ static struct saa7146_ext_vv vv_data = { .stds = &hexium_standards[0], .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard), .std_callback = &std_callback, - .ioctls = &ioctls[0], - .ioctl = hexium_ioctl, }; static struct saa7146_extension hexium_extension = { diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 074bec7..2bc39f6 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -57,14 +57,6 @@ struct hexium_data u8 byte; }; -static struct saa7146_extension_ioctls ioctls[] = { - { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_S_STD, SAA7146_AFTER }, - { 0, 0 } -}; - struct hexium { int type; @@ -329,6 +321,44 @@ static int hexium_set_input(struct hexium *hexium, int input) return 0; } +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index)); + + if (i->index < 0 || i->index >= HEXIUM_INPUTS) + return -EINVAL; + + memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); + + DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index)); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + *input = hexium->cur_input; + + DEB_D(("VIDIOC_G_INPUT: %d\n", *input)); + return 0; +} + +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct hexium *hexium = (struct hexium *) dev->ext_priv; + + if (input < 0 || input >= HEXIUM_INPUTS) + return -EINVAL; + + hexium->cur_input = input; + hexium_set_input(hexium, input); + + return 0; +} + static struct saa7146_ext_vv vv_data; /* this function only gets called when the probing was successful */ @@ -339,6 +369,9 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d DEB_EE((".\n")); saa7146_vv_init(dev, &vv_data); + vv_data.ops.vidioc_enum_input = vidioc_enum_input; + vv_data.ops.vidioc_g_input = vidioc_g_input; + vv_data.ops.vidioc_s_input = vidioc_s_input; if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) { printk("hexium_orion: cannot register capture v4l2 device. skipping.\n"); return -1; @@ -370,58 +403,6 @@ static int hexium_detach(struct saa7146_dev *dev) return 0; } -static long hexium_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) -{ - struct saa7146_dev *dev = fh->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; -/* - struct saa7146_vv *vv = dev->vv_data; -*/ - switch (cmd) { - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; - DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index)); - - if (i->index < 0 || i->index >= HEXIUM_INPUTS) { - return -EINVAL; - } - - memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); - - DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index)); - return 0; - } - case VIDIOC_G_INPUT: - { - int *input = (int *) arg; - *input = hexium->cur_input; - - DEB_D(("VIDIOC_G_INPUT: %d\n", *input)); - return 0; - } - case VIDIOC_S_INPUT: - { - int input = *(int *) arg; - - if (input < 0 || input >= HEXIUM_INPUTS) { - return -EINVAL; - } - - hexium->cur_input = input; - hexium_set_input(hexium, input); - - return 0; - } - default: -/* - DEB_D(("hexium_ioctl() does not handle this ioctl.\n")); -*/ - return -ENOIOCTLCMD; - } - return 0; -} - static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std) { return 0; @@ -479,8 +460,6 @@ static struct saa7146_ext_vv vv_data = { .stds = &hexium_standards[0], .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard), .std_callback = &std_callback, - .ioctls = &ioctls[0], - .ioctl = hexium_ioctl, }; static struct saa7146_extension extension = { diff --git a/drivers/media/video/indycam.c b/drivers/media/video/indycam.c index 84b9e4f..3d69401 100644 --- a/drivers/media/video/indycam.c +++ b/drivers/media/video/indycam.c @@ -19,10 +19,12 @@ #include <linux/mm.h> #include <linux/slab.h> -#include <linux/videodev.h> /* IndyCam decodes stream of photons into digital image representation ;-) */ -#include <linux/video_decoder.h> +#include <linux/videodev2.h> #include <linux/i2c.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> #include "indycam.h" @@ -33,6 +35,7 @@ MODULE_VERSION(INDYCAM_MODULE_VERSION); MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); MODULE_LICENSE("GPL"); + // #define INDYCAM_DEBUG #ifdef INDYCAM_DEBUG @@ -44,11 +47,14 @@ MODULE_LICENSE("GPL"); #endif struct indycam { - struct i2c_client *client; + struct v4l2_subdev sd; u8 version; }; -static struct i2c_driver i2c_driver_indycam; +static inline struct indycam *to_indycam(struct v4l2_subdev *sd) +{ + return container_of(sd, struct indycam, sd); +} static const u8 initseq[] = { INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */ @@ -63,8 +69,9 @@ static const u8 initseq[] = { /* IndyCam register handling */ -static int indycam_read_reg(struct i2c_client *client, u8 reg, u8 *value) +static int indycam_read_reg(struct v4l2_subdev *sd, u8 reg, u8 *value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; if (reg == INDYCAM_REG_RESET) { @@ -87,12 +94,12 @@ static int indycam_read_reg(struct i2c_client *client, u8 reg, u8 *value) return 0; } -static int indycam_write_reg(struct i2c_client *client, u8 reg, u8 value) +static int indycam_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int err; - if ((reg == INDYCAM_REG_BRIGHTNESS) - || (reg == INDYCAM_REG_VERSION)) { + if (reg == INDYCAM_REG_BRIGHTNESS || reg == INDYCAM_REG_VERSION) { dprintk("indycam_write_reg(): " "skipping read-only register %d\n", reg); return 0; @@ -108,13 +115,13 @@ static int indycam_write_reg(struct i2c_client *client, u8 reg, u8 value) return err; } -static int indycam_write_block(struct i2c_client *client, u8 reg, +static int indycam_write_block(struct v4l2_subdev *sd, u8 reg, u8 length, u8 *data) { int i, err; for (i = 0; i < length; i++) { - err = indycam_write_reg(client, reg + i, data[i]); + err = indycam_write_reg(sd, reg + i, data[i]); if (err) return err; } @@ -125,79 +132,78 @@ static int indycam_write_block(struct i2c_client *client, u8 reg, /* Helper functions */ #ifdef INDYCAM_DEBUG -static void indycam_regdump_debug(struct i2c_client *client) +static void indycam_regdump_debug(struct v4l2_subdev *sd) { int i; u8 val; for (i = 0; i < 9; i++) { - indycam_read_reg(client, i, &val); + indycam_read_reg(sd, i, &val); dprintk("Reg %d = 0x%02x\n", i, val); } } #endif -static int indycam_get_control(struct i2c_client *client, - struct indycam_control *ctrl) +static int indycam_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct indycam *camera = i2c_get_clientdata(client); + struct indycam *camera = to_indycam(sd); u8 reg; int ret = 0; - switch (ctrl->type) { - case INDYCAM_CONTROL_AGC: - case INDYCAM_CONTROL_AWB: - ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, ®); + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); if (ret) return -EIO; - if (ctrl->type == INDYCAM_CONTROL_AGC) + if (ctrl->id == V4L2_CID_AUTOGAIN) ctrl->value = (reg & INDYCAM_CONTROL_AGCENA) ? 1 : 0; else ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL) ? 1 : 0; break; - case INDYCAM_CONTROL_SHUTTER: - ret = indycam_read_reg(client, INDYCAM_REG_SHUTTER, ®); + case V4L2_CID_EXPOSURE: + ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, ®); if (ret) return -EIO; ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1); break; - case INDYCAM_CONTROL_GAIN: - ret = indycam_read_reg(client, INDYCAM_REG_GAIN, ®); + case V4L2_CID_GAIN: + ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, ®); if (ret) return -EIO; ctrl->value = (s32)reg; break; - case INDYCAM_CONTROL_RED_BALANCE: - ret = indycam_read_reg(client, INDYCAM_REG_RED_BALANCE, ®); + case V4L2_CID_RED_BALANCE: + ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, ®); if (ret) return -EIO; ctrl->value = (s32)reg; break; - case INDYCAM_CONTROL_BLUE_BALANCE: - ret = indycam_read_reg(client, INDYCAM_REG_BLUE_BALANCE, ®); + case V4L2_CID_BLUE_BALANCE: + ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, ®); if (ret) return -EIO; ctrl->value = (s32)reg; break; case INDYCAM_CONTROL_RED_SATURATION: - ret = indycam_read_reg(client, + ret = indycam_read_reg(sd, INDYCAM_REG_RED_SATURATION, ®); if (ret) return -EIO; ctrl->value = (s32)reg; break; case INDYCAM_CONTROL_BLUE_SATURATION: - ret = indycam_read_reg(client, + ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_SATURATION, ®); if (ret) return -EIO; ctrl->value = (s32)reg; break; - case INDYCAM_CONTROL_GAMMA: + case V4L2_CID_GAMMA: if (camera->version == CAMERA_VERSION_MOOSE) { - ret = indycam_read_reg(client, + ret = indycam_read_reg(sd, INDYCAM_REG_GAMMA, ®); if (ret) return -EIO; @@ -213,21 +219,20 @@ static int indycam_get_control(struct i2c_client *client, return ret; } -static int indycam_set_control(struct i2c_client *client, - struct indycam_control *ctrl) +static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct indycam *camera = i2c_get_clientdata(client); + struct indycam *camera = to_indycam(sd); u8 reg; int ret = 0; - switch (ctrl->type) { - case INDYCAM_CONTROL_AGC: - case INDYCAM_CONTROL_AWB: - ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, ®); + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + case V4L2_CID_AUTO_WHITE_BALANCE: + ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); if (ret) break; - if (ctrl->type == INDYCAM_CONTROL_AGC) { + if (ctrl->id == V4L2_CID_AUTOGAIN) { if (ctrl->value) reg |= INDYCAM_CONTROL_AGCENA; else @@ -239,34 +244,34 @@ static int indycam_set_control(struct i2c_client *client, reg &= ~INDYCAM_CONTROL_AWBCTL; } - ret = indycam_write_reg(client, INDYCAM_REG_CONTROL, reg); + ret = indycam_write_reg(sd, INDYCAM_REG_CONTROL, reg); break; - case INDYCAM_CONTROL_SHUTTER: + case V4L2_CID_EXPOSURE: reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1); - ret = indycam_write_reg(client, INDYCAM_REG_SHUTTER, reg); + ret = indycam_write_reg(sd, INDYCAM_REG_SHUTTER, reg); break; - case INDYCAM_CONTROL_GAIN: - ret = indycam_write_reg(client, INDYCAM_REG_GAIN, ctrl->value); + case V4L2_CID_GAIN: + ret = indycam_write_reg(sd, INDYCAM_REG_GAIN, ctrl->value); break; - case INDYCAM_CONTROL_RED_BALANCE: - ret = indycam_write_reg(client, INDYCAM_REG_RED_BALANCE, + case V4L2_CID_RED_BALANCE: + ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE, ctrl->value); break; - case INDYCAM_CONTROL_BLUE_BALANCE: - ret = indycam_write_reg(client, INDYCAM_REG_BLUE_BALANCE, + case V4L2_CID_BLUE_BALANCE: + ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE, ctrl->value); break; case INDYCAM_CONTROL_RED_SATURATION: - ret = indycam_write_reg(client, INDYCAM_REG_RED_SATURATION, + ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION, ctrl->value); break; case INDYCAM_CONTROL_BLUE_SATURATION: - ret = indycam_write_reg(client, INDYCAM_REG_BLUE_SATURATION, + ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_SATURATION, ctrl->value); break; - case INDYCAM_CONTROL_GAMMA: + case V4L2_CID_GAMMA: if (camera->version == CAMERA_VERSION_MOOSE) { - ret = indycam_write_reg(client, INDYCAM_REG_GAMMA, + ret = indycam_write_reg(sd, INDYCAM_REG_GAMMA, ctrl->value); } break; @@ -279,192 +284,103 @@ static int indycam_set_control(struct i2c_client *client, /* I2C-interface */ -static int indycam_attach(struct i2c_adapter *adap, int addr, int kind) +static int indycam_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct indycam *camera = to_indycam(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_INDYCAM, + camera->version); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops indycam_core_ops = { + .g_chip_ident = indycam_g_chip_ident, + .g_ctrl = indycam_g_ctrl, + .s_ctrl = indycam_s_ctrl, +}; + +static const struct v4l2_subdev_ops indycam_ops = { + .core = &indycam_core_ops, +}; + +static int indycam_probe(struct i2c_client *client, + const struct i2c_device_id *id) { int err = 0; struct indycam *camera; - struct i2c_client *client; + struct v4l2_subdev *sd; - printk(KERN_INFO "SGI IndyCam driver version %s\n", - INDYCAM_MODULE_VERSION); + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); - client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!client) - return -ENOMEM; camera = kzalloc(sizeof(struct indycam), GFP_KERNEL); - if (!camera) { - err = -ENOMEM; - goto out_free_client; - } - - client->addr = addr; - client->adapter = adap; - client->driver = &i2c_driver_indycam; - client->flags = 0; - strcpy(client->name, "IndyCam client"); - i2c_set_clientdata(client, camera); - - camera->client = client; + if (!camera) + return -ENOMEM; - err = i2c_attach_client(client); - if (err) - goto out_free_camera; + sd = &camera->sd; + v4l2_i2c_subdev_init(sd, client, &indycam_ops); camera->version = i2c_smbus_read_byte_data(client, INDYCAM_REG_VERSION); if (camera->version != CAMERA_VERSION_INDY && camera->version != CAMERA_VERSION_MOOSE) { - err = -ENODEV; - goto out_detach_client; + kfree(camera); + return -ENODEV; } + printk(KERN_INFO "IndyCam v%d.%d detected\n", INDYCAM_VERSION_MAJOR(camera->version), INDYCAM_VERSION_MINOR(camera->version)); - indycam_regdump(client); + indycam_regdump(sd); // initialize - err = indycam_write_block(client, 0, sizeof(initseq), (u8 *)&initseq); + err = indycam_write_block(sd, 0, sizeof(initseq), (u8 *)&initseq); if (err) { printk(KERN_ERR "IndyCam initialization failed\n"); - err = -EIO; - goto out_detach_client; + kfree(camera); + return -EIO; } - indycam_regdump(client); + indycam_regdump(sd); // white balance - err = indycam_write_reg(client, INDYCAM_REG_CONTROL, + err = indycam_write_reg(sd, INDYCAM_REG_CONTROL, INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL); if (err) { printk(KERN_ERR "IndyCam: White balancing camera failed\n"); - err = -EIO; - goto out_detach_client; + kfree(camera); + return -EIO; } - indycam_regdump(client); + indycam_regdump(sd); printk(KERN_INFO "IndyCam initialized\n"); return 0; - -out_detach_client: - i2c_detach_client(client); -out_free_camera: - kfree(camera); -out_free_client: - kfree(client); - return err; } -static int indycam_probe(struct i2c_adapter *adap) +static int indycam_remove(struct i2c_client *client) { - /* Indy specific crap */ - if (adap->id == I2C_HW_SGI_VINO) - return indycam_attach(adap, INDYCAM_ADDR, 0); - /* Feel free to add probe here :-) */ - return -ENODEV; -} - -static int indycam_detach(struct i2c_client *client) -{ - struct indycam *camera = i2c_get_clientdata(client); + struct v4l2_subdev *sd = i2c_get_clientdata(client); - i2c_detach_client(client); - kfree(camera); - kfree(client); + v4l2_device_unregister_subdev(sd); + kfree(to_indycam(sd)); return 0; } -static int indycam_command(struct i2c_client *client, unsigned int cmd, - void *arg) -{ - // struct indycam *camera = i2c_get_clientdata(client); - - /* The old video_decoder interface just isn't enough, - * so we'll use some custom commands. */ - switch (cmd) { - case DECODER_GET_CAPABILITIES: { - struct video_decoder_capability *cap = arg; - - cap->flags = VIDEO_DECODER_NTSC; - cap->inputs = 1; - cap->outputs = 1; - break; - } - case DECODER_GET_STATUS: { - int *iarg = arg; - - *iarg = DECODER_STATUS_GOOD | DECODER_STATUS_NTSC | - DECODER_STATUS_COLOR; - break; - } - case DECODER_SET_NORM: { - int *iarg = arg; - - switch (*iarg) { - case VIDEO_MODE_NTSC: - break; - default: - return -EINVAL; - } - break; - } - case DECODER_SET_INPUT: { - int *iarg = arg; - - if (*iarg != 0) - return -EINVAL; - break; - } - case DECODER_SET_OUTPUT: { - int *iarg = arg; - - if (*iarg != 0) - return -EINVAL; - break; - } - case DECODER_ENABLE_OUTPUT: { - /* Always enabled */ - break; - } - case DECODER_SET_PICTURE: { - // struct video_picture *pic = arg; - /* TODO: convert values for indycam_set_controls() */ - break; - } - case DECODER_INDYCAM_GET_CONTROL: { - return indycam_get_control(client, arg); - } - case DECODER_INDYCAM_SET_CONTROL: { - return indycam_set_control(client, arg); - } - default: - return -EINVAL; - } - - return 0; -} - -static struct i2c_driver i2c_driver_indycam = { - .driver = { - .name = "indycam", - }, - .id = I2C_DRIVERID_INDYCAM, - .attach_adapter = indycam_probe, - .detach_client = indycam_detach, - .command = indycam_command, +static const struct i2c_device_id indycam_id[] = { + { "indycam", 0 }, + { } }; +MODULE_DEVICE_TABLE(i2c, indycam_id); -static int __init indycam_init(void) -{ - return i2c_add_driver(&i2c_driver_indycam); -} - -static void __exit indycam_exit(void) -{ - i2c_del_driver(&i2c_driver_indycam); -} - -module_init(indycam_init); -module_exit(indycam_exit); +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "indycam", + .probe = indycam_probe, + .remove = indycam_remove, + .id_table = indycam_id, +}; diff --git a/drivers/media/video/indycam.h b/drivers/media/video/indycam.h index e6ee820..881f21c 100644 --- a/drivers/media/video/indycam.h +++ b/drivers/media/video/indycam.h @@ -87,22 +87,7 @@ /* Driver interface definitions */ -#define INDYCAM_CONTROL_AGC 0 /* boolean */ -#define INDYCAM_CONTROL_AWB 1 /* boolean */ -#define INDYCAM_CONTROL_SHUTTER 2 -#define INDYCAM_CONTROL_GAIN 3 -#define INDYCAM_CONTROL_RED_BALANCE 4 -#define INDYCAM_CONTROL_BLUE_BALANCE 5 -#define INDYCAM_CONTROL_RED_SATURATION 6 -#define INDYCAM_CONTROL_BLUE_SATURATION 7 -#define INDYCAM_CONTROL_GAMMA 8 - -struct indycam_control { - u8 type; - s32 value; -}; - -#define DECODER_INDYCAM_GET_CONTROL _IOR('d', 193, struct indycam_control) -#define DECODER_INDYCAM_SET_CONTROL _IOW('d', 194, struct indycam_control) +#define INDYCAM_CONTROL_RED_SATURATION (V4L2_CID_PRIVATE_BASE + 0) +#define INDYCAM_CONTROL_BLUE_SATURATION (V4L2_CID_PRIVATE_BASE + 1) #endif diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index d4658c5..092c7da 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -16,6 +16,8 @@ * Henry Wong <henry@stuffedcow.net> * Mark Schultz <n9xmj@yahoo.com> * Brian Rogers <brian_rogers@comcast.net> + * modified for AVerMedia Cardbus by + * Oldrich Jedlicka <oldium.pro@seznam.cz> * * 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 @@ -216,6 +218,46 @@ static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } +static int get_key_avermedia_cardbus(struct IR_i2c *ir, + u32 *ir_key, u32 *ir_raw) +{ + unsigned char subaddr, key, keygroup; + struct i2c_msg msg[] = { { .addr = ir->c.addr, .flags = 0, + .buf = &subaddr, .len = 1}, + { .addr = ir->c.addr, .flags = I2C_M_RD, + .buf = &key, .len = 1} }; + subaddr = 0x0d; + if (2 != i2c_transfer(ir->c.adapter, msg, 2)) { + dprintk(1, "read error\n"); + return -EIO; + } + + if (key == 0xff) + return 0; + + subaddr = 0x0b; + msg[1].buf = &keygroup; + if (2 != i2c_transfer(ir->c.adapter, msg, 2)) { + dprintk(1, "read error\n"); + return -EIO; + } + + if (keygroup == 0xff) + return 0; + + dprintk(1, "read key 0x%02x/0x%02x\n", key, keygroup); + if (keygroup < 2 || keygroup > 3) { + /* Only a warning */ + dprintk(1, "warning: invalid key group 0x%02x for key 0x%02x\n", + keygroup, key); + } + key |= (keygroup & 1) << 6; + + *ir_key = key; + *ir_raw = key; + return 1; +} + /* ----------------------------------------------------------------------- */ static void ir_key_poll(struct IR_i2c *ir) @@ -237,15 +279,9 @@ static void ir_key_poll(struct IR_i2c *ir) } } -static void ir_timer(unsigned long data) -{ - struct IR_i2c *ir = (struct IR_i2c*)data; - schedule_work(&ir->work); -} - static void ir_work(struct work_struct *work) { - struct IR_i2c *ir = container_of(work, struct IR_i2c, work); + struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work); int polling_interval = 100; /* MSI TV@nywhere Plus requires more frequent polling @@ -254,7 +290,7 @@ static void ir_work(struct work_struct *work) polling_interval = 50; ir_key_poll(ir); - mod_timer(&ir->timer, jiffies + msecs_to_jiffies(polling_interval)); + schedule_delayed_work(&ir->work, msecs_to_jiffies(polling_interval)); } /* ----------------------------------------------------------------------- */ @@ -360,6 +396,12 @@ static int ir_attach(struct i2c_adapter *adap, int addr, ir_type = IR_TYPE_OTHER; } break; + case 0x40: + name = "AVerMedia Cardbus remote"; + ir->get_key = get_key_avermedia_cardbus; + ir_type = IR_TYPE_OTHER; + ir_codes = ir_codes_avermedia_cardbus; + break; default: /* shouldn't happen */ printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n", addr); @@ -404,11 +446,8 @@ static int ir_attach(struct i2c_adapter *adap, int addr, ir->input->name, ir->input->phys, adap->name); /* start polling via eventd */ - INIT_WORK(&ir->work, ir_work); - init_timer(&ir->timer); - ir->timer.function = ir_timer; - ir->timer.data = (unsigned long)ir; - schedule_work(&ir->work); + INIT_DELAYED_WORK(&ir->work, ir_work); + schedule_delayed_work(&ir->work, 0); return 0; @@ -425,8 +464,7 @@ static int ir_detach(struct i2c_client *client) struct IR_i2c *ir = i2c_get_clientdata(client); /* kill outstanding polls */ - del_timer_sync(&ir->timer); - flush_scheduled_work(); + cancel_delayed_work_sync(&ir->work); /* unregister devices */ input_unregister_device(ir->input); @@ -524,6 +562,22 @@ static int ir_probe(struct i2c_adapter *adap) ir_attach(adap, msg.addr, 0, 0); } + /* Special case for AVerMedia Cardbus remote */ + if (adap->id == I2C_HW_SAA7134) { + unsigned char subaddr, data; + struct i2c_msg msg[] = { { .addr = 0x40, .flags = 0, + .buf = &subaddr, .len = 1}, + { .addr = 0x40, .flags = I2C_M_RD, + .buf = &data, .len = 1} }; + subaddr = 0x0d; + rc = i2c_transfer(adap, msg, 2); + dprintk(1, "probe 0x%02x/0x%02x @ %s: %s\n", + msg[0].addr, subaddr, adap->name, + (2 == rc) ? "yes" : "no"); + if (2 == rc) + ir_attach(adap, msg[0].addr, 0, 0); + } + return 0; } diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c index 62aa06f..84995bc 100644 --- a/drivers/media/video/ivtv/ivtv-controls.c +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -26,6 +26,7 @@ #include "ivtv-mailbox.h" #include "ivtv-controls.h" +/* Must be sorted from low to high control ID! */ static const u32 user_ctrls[] = { V4L2_CID_USER_CLASS, V4L2_CID_BRIGHTNESS, diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index c46c990..eca8bf9 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -357,7 +357,7 @@ void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv) static void ivtv_process_eeprom(struct ivtv *itv) { struct tveeprom tv; - int pci_slot = PCI_SLOT(itv->dev->devfn); + int pci_slot = PCI_SLOT(itv->pdev->devfn); ivtv_read_eeprom(itv, &tv); @@ -604,7 +604,7 @@ static void ivtv_process_options(struct ivtv *itv) itv->std = ivtv_parse_std(itv); if (itv->std == 0 && tunertype >= 0) itv->std = tunertype ? V4L2_STD_MN : (V4L2_STD_ALL & ~V4L2_STD_MN); - itv->has_cx23415 = (itv->dev->device == PCI_DEVICE_ID_IVTV15); + itv->has_cx23415 = (itv->pdev->device == PCI_DEVICE_ID_IVTV15); chipname = itv->has_cx23415 ? "cx23415" : "cx23416"; if (itv->options.cardtype == -1) { IVTV_INFO("Ignore card (detected %s based chip)\n", chipname); @@ -617,9 +617,9 @@ static void ivtv_process_options(struct ivtv *itv) IVTV_ERR("Unknown user specified type, trying to autodetect card\n"); } if (itv->card == NULL) { - if (itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE || - itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 || - itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) { + if (itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE || + itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 || + itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) { itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150); IVTV_INFO("Autodetected Hauppauge card (%s based)\n", chipname); @@ -630,13 +630,13 @@ static void ivtv_process_options(struct ivtv *itv) if (itv->card->pci_list == NULL) continue; for (j = 0; itv->card->pci_list[j].device; j++) { - if (itv->dev->device != + if (itv->pdev->device != itv->card->pci_list[j].device) continue; - if (itv->dev->subsystem_vendor != + if (itv->pdev->subsystem_vendor != itv->card->pci_list[j].subsystem_vendor) continue; - if (itv->dev->subsystem_device != + if (itv->pdev->subsystem_device != itv->card->pci_list[j].subsystem_device) continue; IVTV_INFO("Autodetected %s card (%s based)\n", @@ -650,9 +650,9 @@ done: if (itv->card == NULL) { itv->card = ivtv_get_card(IVTV_CARD_PVR_150); IVTV_ERR("Unknown card: vendor/device: [%04x:%04x]\n", - itv->dev->vendor, itv->dev->device); + itv->pdev->vendor, itv->pdev->device); IVTV_ERR(" subsystem vendor/device: [%04x:%04x]\n", - itv->dev->subsystem_vendor, itv->dev->subsystem_device); + itv->pdev->subsystem_vendor, itv->pdev->subsystem_device); IVTV_ERR(" %s based\n", chipname); IVTV_ERR("Defaulting to %s card\n", itv->card->name); IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); @@ -671,7 +671,7 @@ done: */ static int __devinit ivtv_init_struct1(struct ivtv *itv) { - itv->base_addr = pci_resource_start(itv->dev, 0); + itv->base_addr = pci_resource_start(itv->pdev, 0); itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */ itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */ @@ -682,7 +682,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) spin_lock_init(&itv->lock); spin_lock_init(&itv->dma_reg_lock); - itv->irq_work_queues = create_singlethread_workqueue(itv->device.name); + itv->irq_work_queues = create_singlethread_workqueue(itv->v4l2_dev.name); if (itv->irq_work_queues == NULL) { IVTV_ERR("Could not create ivtv workqueue\n"); return -1; @@ -766,7 +766,7 @@ static void __devinit ivtv_init_struct2(struct ivtv *itv) itv->audio_input = itv->card->video_inputs[i].audio_index; } -static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, +static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev, const struct pci_device_id *pci_id) { u16 cmd; @@ -775,11 +775,11 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, IVTV_DEBUG_INFO("Enabling pci device\n"); - if (pci_enable_device(dev)) { + if (pci_enable_device(pdev)) { IVTV_ERR("Can't enable device!\n"); return -EIO; } - if (pci_set_dma_mask(dev, 0xffffffff)) { + if (pci_set_dma_mask(pdev, 0xffffffff)) { IVTV_ERR("No suitable DMA available.\n"); return -EIO; } @@ -805,11 +805,11 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, } /* Check for bus mastering */ - pci_read_config_word(dev, PCI_COMMAND, &cmd); + pci_read_config_word(pdev, PCI_COMMAND, &cmd); if (!(cmd & PCI_COMMAND_MASTER)) { IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n"); - pci_set_master(dev); - pci_read_config_word(dev, PCI_COMMAND, &cmd); + pci_set_master(pdev); + pci_read_config_word(pdev, PCI_COMMAND, &cmd); if (!(cmd & PCI_COMMAND_MASTER)) { IVTV_ERR("Bus Mastering is not enabled\n"); return -ENXIO; @@ -817,26 +817,26 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, } IVTV_DEBUG_INFO("Bus Mastering Enabled.\n"); - pci_read_config_byte(dev, PCI_CLASS_REVISION, &card_rev); - pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &card_rev); + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency); if (pci_latency < 64 && ivtv_pci_latency) { IVTV_INFO("Unreasonably low latency timer, " "setting to 64 (was %d)\n", pci_latency); - pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); - pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64); + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency); } /* This config space value relates to DMA latencies. The default value 0x8080 is too low however and will lead to DMA errors. 0xffff is the max value which solves these problems. */ - pci_write_config_dword(dev, 0x40, 0xffff); + pci_write_config_dword(pdev, 0x40, 0xffff); IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, " "irq: %d, latency: %d, memory: 0x%lx\n", - itv->dev->device, card_rev, dev->bus->number, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), - itv->dev->irq, pci_latency, (unsigned long)itv->base_addr); + pdev->device, card_rev, pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + pdev->irq, pci_latency, (unsigned long)itv->base_addr); return 0; } @@ -935,7 +935,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) } } -static int __devinit ivtv_probe(struct pci_dev *dev, +static int __devinit ivtv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) { int retval = 0; @@ -945,17 +945,17 @@ static int __devinit ivtv_probe(struct pci_dev *dev, itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC); if (itv == NULL) return -ENOMEM; - itv->dev = dev; + itv->pdev = pdev; itv->instance = atomic_inc_return(&ivtv_instance) - 1; - retval = v4l2_device_register(&dev->dev, &itv->device); + retval = v4l2_device_register(&pdev->dev, &itv->v4l2_dev); if (retval) { kfree(itv); return retval; } /* "ivtv + PCI ID" is a bit of a mouthful, so use "ivtv + instance" instead. */ - snprintf(itv->device.name, sizeof(itv->device.name), + snprintf(itv->v4l2_dev.name, sizeof(itv->v4l2_dev.name), "ivtv%d", itv->instance); IVTV_INFO("Initializing card %d\n", itv->instance); @@ -972,12 +972,11 @@ static int __devinit ivtv_probe(struct pci_dev *dev, IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr); /* PCI Device Setup */ - if ((retval = ivtv_setup_pci(itv, dev, pci_id)) != 0) { - if (retval == -EIO) - goto free_workqueue; - else if (retval == -ENXIO) - goto free_mem; - } + retval = ivtv_setup_pci(itv, pdev, pci_id); + if (retval == -EIO) + goto free_workqueue; + if (retval == -ENXIO) + goto free_mem; /* map io memory */ IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", @@ -1154,8 +1153,8 @@ static int __devinit ivtv_probe(struct pci_dev *dev, ivtv_set_irq_mask(itv, 0xffffffff); /* Register IRQ */ - retval = request_irq(itv->dev->irq, ivtv_irq_handler, - IRQF_SHARED | IRQF_DISABLED, itv->device.name, (void *)itv); + retval = request_irq(itv->pdev->irq, ivtv_irq_handler, + IRQF_SHARED | IRQF_DISABLED, itv->v4l2_dev.name, (void *)itv); if (retval) { IVTV_ERR("Failed to register irq %d\n", retval); goto free_i2c; @@ -1177,7 +1176,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, free_streams: ivtv_streams_cleanup(itv, 1); free_irq: - free_irq(itv->dev->irq, (void *)itv); + free_irq(itv->pdev->irq, (void *)itv); free_i2c: exit_ivtv_i2c(itv); free_io: @@ -1194,7 +1193,7 @@ err: retval = -ENODEV; IVTV_ERR("Error %d on initialization\n", retval); - v4l2_device_unregister(&itv->device); + v4l2_device_unregister(&itv->v4l2_dev); kfree(itv); return retval; } @@ -1292,10 +1291,10 @@ int ivtv_init_on_first_open(struct ivtv *itv) return 0; } -static void ivtv_remove(struct pci_dev *pci_dev) +static void ivtv_remove(struct pci_dev *pdev) { - struct v4l2_device *dev = dev_get_drvdata(&pci_dev->dev); - struct ivtv *itv = to_ivtv(dev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct ivtv *itv = to_ivtv(v4l2_dev); int i; IVTV_DEBUG_INFO("Removing card\n"); @@ -1336,11 +1335,9 @@ static void ivtv_remove(struct pci_dev *pci_dev) ivtv_streams_cleanup(itv, 1); ivtv_udma_free(itv); - v4l2_device_unregister(&itv->device); - exit_ivtv_i2c(itv); - free_irq(itv->dev->irq, (void *)itv); + free_irq(itv->pdev->irq, (void *)itv); ivtv_iounmap(itv); release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); @@ -1348,11 +1345,13 @@ static void ivtv_remove(struct pci_dev *pci_dev) if (itv->has_cx23415) release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); - pci_disable_device(itv->dev); + pci_disable_device(itv->pdev); for (i = 0; i < IVTV_VBI_FRAMES; i++) kfree(itv->vbi.sliced_mpeg_data[i]); printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name); + + v4l2_device_unregister(&itv->v4l2_dev); kfree(itv); } diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index ce8d9b7..440f732 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -133,7 +133,7 @@ extern int ivtv_debug; #define IVTV_DEBUG(x, type, fmt, args...) \ do { \ if ((x) & ivtv_debug) \ - v4l2_info(&itv->device, " " type ": " fmt , ##args); \ + v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \ } while (0) #define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warn", fmt , ## args) #define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args) @@ -149,7 +149,7 @@ extern int ivtv_debug; #define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \ do { \ if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \ - v4l2_info(&itv->device, " " type ": " fmt , ##args); \ + v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \ } while (0) #define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warn", fmt , ## args) #define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info", fmt , ## args) @@ -163,9 +163,9 @@ extern int ivtv_debug; #define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) /* Standard kernel messages */ -#define IVTV_ERR(fmt, args...) v4l2_err(&itv->device, fmt , ## args) -#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->device, fmt , ## args) -#define IVTV_INFO(fmt, args...) v4l2_info(&itv->device, fmt , ## args) +#define IVTV_ERR(fmt, args...) v4l2_err(&itv->v4l2_dev, fmt , ## args) +#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->v4l2_dev, fmt , ## args) +#define IVTV_INFO(fmt, args...) v4l2_info(&itv->v4l2_dev, fmt , ## args) /* output modes (cx23415 only) */ #define OUT_NONE 0 @@ -315,7 +315,7 @@ struct ivtv; /* forward reference */ struct ivtv_stream { /* These first four fields are always set, even if the stream is not actually created. */ - struct video_device *v4l2dev; /* NULL when stream not created */ + struct video_device *vdev; /* NULL when stream not created */ struct ivtv *itv; /* for ease of use */ const char *name; /* name of the stream */ int type; /* stream type */ @@ -592,7 +592,7 @@ struct ivtv_card; /* Struct to hold info about ivtv cards */ struct ivtv { /* General fixed card data */ - struct pci_dev *dev; /* PCI device */ + struct pci_dev *pdev; /* PCI device */ const struct ivtv_card *card; /* card information */ const char *card_name; /* full name of the card */ const struct ivtv_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */ @@ -612,7 +612,7 @@ struct ivtv { volatile void __iomem *reg_mem; /* pointer to mapped registers */ struct ivtv_options options; /* user options */ - struct v4l2_device device; + struct v4l2_device v4l2_dev; struct v4l2_subdev sd_gpio; /* GPIO sub-device */ u16 instance; @@ -696,7 +696,7 @@ struct ivtv { u64 vbi_data_inserted; /* number of VBI bytes inserted into the MPEG stream */ u32 last_dec_timing[3]; /* cache last retrieved pts/scr/frame values */ unsigned long dualwatch_jiffies;/* jiffies value of the previous dualwatch check */ - u16 dualwatch_stereo_mode; /* current detected dualwatch stereo mode */ + u32 dualwatch_stereo_mode; /* current detected dualwatch stereo mode */ /* VBI state info */ @@ -719,9 +719,9 @@ struct ivtv { struct osd_info *osd_info; /* ivtvfb private OSD info */ }; -static inline struct ivtv *to_ivtv(struct v4l2_device *dev) +static inline struct ivtv *to_ivtv(struct v4l2_device *v4l2_dev) { - return container_of(dev, struct ivtv, device); + return container_of(v4l2_dev, struct ivtv, v4l2_dev); } /* Globals */ @@ -788,7 +788,7 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv) /* Call the specified callback for all subdevs matching hw (if 0, then match them all). Ignore any errors. */ #define ivtv_call_hw(itv, hw, o, f, args...) \ - __v4l2_device_call_subdevs(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args) + __v4l2_device_call_subdevs(&(itv)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args) #define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args) @@ -796,7 +796,7 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv) match them all). If the callback returns an error other than 0 or -ENOIOCTLCMD, then return with that error code. */ #define ivtv_call_hw_err(itv, hw, o, f, args...) \ - __v4l2_device_call_subdevs_until_err(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args) + __v4l2_device_call_subdevs_until_err(&(itv)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args) #define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args) diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index d594bc2..cfaacf6 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -148,10 +148,10 @@ void ivtv_release_stream(struct ivtv_stream *s) static void ivtv_dualwatch(struct ivtv *itv) { struct v4l2_tuner vt; - u16 new_bitmap; - u16 new_stereo_mode; - const u16 stereo_mask = 0x0300; - const u16 dual = 0x0200; + u32 new_bitmap; + u32 new_stereo_mode; + const u32 stereo_mask = 0x0300; + const u32 dual = 0x0200; new_stereo_mode = itv->params.audio_properties & stereo_mask; memset(&vt, 0, sizeof(vt)); @@ -991,7 +991,7 @@ int ivtv_v4l2_open(struct file *filp) mutex_lock(&itv->serialize_lock); if (ivtv_init_on_first_open(itv)) { IVTV_ERR("Failed to initialize on minor %d\n", - s->v4l2dev->minor); + vdev->minor); mutex_unlock(&itv->serialize_lock); return -ENXIO; } diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c index 6dba55b..c1b7ec4 100644 --- a/drivers/media/video/ivtv/ivtv-firmware.c +++ b/drivers/media/video/ivtv/ivtv-firmware.c @@ -52,7 +52,7 @@ static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv int retries = 3; retry: - if (retries && request_firmware(&fw, fn, &itv->dev->dev) == 0) { + if (retries && request_firmware(&fw, fn, &itv->pdev->dev) == 0) { int i; volatile u32 __iomem *dst = (volatile u32 __iomem *)mem; const u32 *src = (const u32 *)fw->data; diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c index dc2850e..3321983 100644 --- a/drivers/media/video/ivtv/ivtv-gpio.c +++ b/drivers/media/video/ivtv/ivtv-gpio.c @@ -384,7 +384,7 @@ int ivtv_gpio_init(struct ivtv *itv) write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT); write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR); v4l2_subdev_init(&itv->sd_gpio, &subdev_ops); - snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->device.name); + snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name); itv->sd_gpio.grp_id = IVTV_HW_GPIO; - return v4l2_device_register_subdev(&itv->device, &itv->sd_gpio); + return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio); } diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index ca1d955..e73a196 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -194,14 +194,14 @@ struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw) struct v4l2_subdev *result = NULL; struct v4l2_subdev *sd; - spin_lock(&itv->device.lock); - v4l2_device_for_each_subdev(sd, &itv->device) { + spin_lock(&itv->v4l2_dev.lock); + v4l2_device_for_each_subdev(sd, &itv->v4l2_dev) { if (sd->grp_id == hw) { result = sd; break; } } - spin_unlock(&itv->device.lock); + spin_unlock(&itv->v4l2_dev.lock); return result; } @@ -472,8 +472,8 @@ static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, intervening stop condition */ static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { - struct v4l2_device *drv = i2c_get_adapdata(i2c_adap); - struct ivtv *itv = to_ivtv(drv); + struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap); + struct ivtv *itv = to_ivtv(v4l2_dev); int retval; int i; @@ -604,12 +604,12 @@ int init_ivtv_i2c(struct ivtv *itv) sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d", itv->instance); - i2c_set_adapdata(&itv->i2c_adap, &itv->device); + i2c_set_adapdata(&itv->i2c_adap, &itv->v4l2_dev); memcpy(&itv->i2c_client, &ivtv_i2c_client_template, sizeof(struct i2c_client)); itv->i2c_client.adapter = &itv->i2c_adap; - itv->i2c_adap.dev.parent = &itv->dev->dev; + itv->i2c_adap.dev.parent = &itv->pdev->dev; IVTV_DEBUG_I2C("setting scl and sda to 1\n"); ivtv_setscl(itv, 1); diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index c13bd2a..9a04242 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -345,10 +345,8 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f pixfmt->priv = 0; if (id->type == IVTV_ENC_STREAM_TYPE_YUV) { pixfmt->pixelformat = V4L2_PIX_FMT_HM12; - /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ - pixfmt->sizeimage = - pixfmt->height * pixfmt->width + - pixfmt->height * (pixfmt->width / 2); + /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */ + pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2; pixfmt->bytesperline = 720; } else { pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; @@ -469,11 +467,17 @@ static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format struct ivtv *itv = id->itv; int w = fmt->fmt.pix.width; int h = fmt->fmt.pix.height; + int min_h = 2; w = min(w, 720); w = max(w, 2); + if (id->type == IVTV_ENC_STREAM_TYPE_YUV) { + /* YUV height must be a multiple of 32 */ + h &= ~0x1f; + min_h = 32; + } h = min(h, itv->is_50hz ? 576 : 480); - h = max(h, 2); + h = max(h, min_h); ivtv_g_fmt_vid_cap(file, fh, fmt); fmt->fmt.pix.width = w; fmt->fmt.pix.height = h; @@ -766,7 +770,7 @@ static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vc strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver)); strlcpy(vcap->card, itv->card_name, sizeof(vcap->card)); - snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->dev)); + snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev)); vcap->version = IVTV_DRIVER_VERSION; /* version */ vcap->capabilities = itv->v4l2_cap; /* capabilities */ return 0; @@ -1513,12 +1517,12 @@ static int ivtv_log_status(struct file *file, void *fh) } IVTV_INFO("Tuner: %s\n", test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV"); - cx2341x_log_status(&itv->params, itv->device.name); + cx2341x_log_status(&itv->params, itv->v4l2_dev.name); IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags); for (i = 0; i < IVTV_MAX_STREAMS; i++) { struct ivtv_stream *s = &itv->streams[i]; - if (s->v4l2dev == NULL || s->buffers == 0) + if (s->vdev == NULL || s->buffers == 0) continue; IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, (s->buffers - s->q_free.buffers) * 100 / s->buffers, diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c index f5d00ec..01c14d2 100644 --- a/drivers/media/video/ivtv/ivtv-irq.c +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -46,7 +46,7 @@ static void ivtv_pio_work_handler(struct ivtv *itv) IVTV_DEBUG_HI_DMA("ivtv_pio_work_handler\n"); if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS || - s->v4l2dev == NULL || !ivtv_use_pio(s)) { + s->vdev == NULL || !ivtv_use_pio(s)) { itv->cur_pio_stream = -1; /* trigger PIO complete user interrupt */ write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44); @@ -109,7 +109,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA int rc; /* sanity checks */ - if (s->v4l2dev == NULL) { + if (s->vdev == NULL) { IVTV_DEBUG_WARN("Stream %s not started\n", s->name); return -1; } diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c index 71bd13e..ff7b7de 100644 --- a/drivers/media/video/ivtv/ivtv-queue.c +++ b/drivers/media/video/ivtv/ivtv-queue.c @@ -230,7 +230,7 @@ int ivtv_stream_alloc(struct ivtv_stream *s) return -ENOMEM; } if (ivtv_might_use_dma(s)) { - s->sg_handle = pci_map_single(itv->dev, s->sg_dma, sizeof(struct ivtv_sg_element), s->dma); + s->sg_handle = pci_map_single(itv->pdev, s->sg_dma, sizeof(struct ivtv_sg_element), s->dma); ivtv_stream_sync_for_cpu(s); } @@ -248,7 +248,7 @@ int ivtv_stream_alloc(struct ivtv_stream *s) } INIT_LIST_HEAD(&buf->list); if (ivtv_might_use_dma(s)) { - buf->dma_handle = pci_map_single(s->itv->dev, + buf->dma_handle = pci_map_single(s->itv->pdev, buf->buf, s->buf_size + 256, s->dma); ivtv_buf_sync_for_cpu(s, buf); } @@ -271,7 +271,7 @@ void ivtv_stream_free(struct ivtv_stream *s) /* empty q_free */ while ((buf = ivtv_dequeue(s, &s->q_free))) { if (ivtv_might_use_dma(s)) - pci_unmap_single(s->itv->dev, buf->dma_handle, + pci_unmap_single(s->itv->pdev, buf->dma_handle, s->buf_size + 256, s->dma); kfree(buf->buf); kfree(buf); @@ -280,7 +280,7 @@ void ivtv_stream_free(struct ivtv_stream *s) /* Free SG Array/Lists */ if (s->sg_dma != NULL) { if (s->sg_handle != IVTV_DMA_UNMAPPED) { - pci_unmap_single(s->itv->dev, s->sg_handle, + pci_unmap_single(s->itv->pdev, s->sg_handle, sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE); s->sg_handle = IVTV_DMA_UNMAPPED; } diff --git a/drivers/media/video/ivtv/ivtv-queue.h b/drivers/media/video/ivtv/ivtv-queue.h index 476556a..9123383 100644 --- a/drivers/media/video/ivtv/ivtv-queue.h +++ b/drivers/media/video/ivtv/ivtv-queue.h @@ -53,14 +53,14 @@ static inline int ivtv_use_dma(struct ivtv_stream *s) static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf) { if (ivtv_use_dma(s)) - pci_dma_sync_single_for_cpu(s->itv->dev, buf->dma_handle, + pci_dma_sync_single_for_cpu(s->itv->pdev, buf->dma_handle, s->buf_size + 256, s->dma); } static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf) { if (ivtv_use_dma(s)) - pci_dma_sync_single_for_device(s->itv->dev, buf->dma_handle, + pci_dma_sync_single_for_device(s->itv->pdev, buf->dma_handle, s->buf_size + 256, s->dma); } @@ -82,14 +82,14 @@ void ivtv_stream_free(struct ivtv_stream *s); static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s) { if (ivtv_use_dma(s)) - pci_dma_sync_single_for_cpu(s->itv->dev, s->sg_handle, + pci_dma_sync_single_for_cpu(s->itv->pdev, s->sg_handle, sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE); } static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s) { if (ivtv_use_dma(s)) - pci_dma_sync_single_for_device(s->itv->dev, s->sg_handle, + pci_dma_sync_single_for_device(s->itv->pdev, s->sg_handle, sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE); } diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 854a950..15da017 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -137,11 +137,11 @@ static struct { static void ivtv_stream_init(struct ivtv *itv, int type) { struct ivtv_stream *s = &itv->streams[type]; - struct video_device *dev = s->v4l2dev; + struct video_device *vdev = s->vdev; - /* we need to keep v4l2dev, so restore it afterwards */ + /* we need to keep vdev, so restore it afterwards */ memset(s, 0, sizeof(*s)); - s->v4l2dev = dev; + s->vdev = vdev; /* initialize ivtv_stream fields */ s->itv = itv; @@ -172,10 +172,10 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) int num_offset = ivtv_stream_info[type].num_offset; int num = itv->instance + ivtv_first_minor + num_offset; - /* These four fields are always initialized. If v4l2dev == NULL, then + /* These four fields are always initialized. If vdev == NULL, then this stream is not in use. In that case no other fields but these four can be used. */ - s->v4l2dev = NULL; + s->vdev = NULL; s->itv = itv; s->type = type; s->name = ivtv_stream_info[type].name; @@ -197,21 +197,21 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) ivtv_stream_init(itv, type); /* allocate and initialize the v4l2 video device structure */ - s->v4l2dev = video_device_alloc(); - if (s->v4l2dev == NULL) { + s->vdev = video_device_alloc(); + if (s->vdev == NULL) { IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name); return -ENOMEM; } - snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "%s %s", - itv->device.name, s->name); + snprintf(s->vdev->name, sizeof(s->vdev->name), "%s %s", + itv->v4l2_dev.name, s->name); - s->v4l2dev->num = num; - s->v4l2dev->v4l2_dev = &itv->device; - s->v4l2dev->fops = ivtv_stream_info[type].fops; - s->v4l2dev->release = video_device_release; - s->v4l2dev->tvnorms = V4L2_STD_ALL; - ivtv_set_funcs(s->v4l2dev); + s->vdev->num = num; + s->vdev->v4l2_dev = &itv->v4l2_dev; + s->vdev->fops = ivtv_stream_info[type].fops; + s->vdev->release = video_device_release; + s->vdev->tvnorms = V4L2_STD_ALL; + ivtv_set_funcs(s->vdev); return 0; } @@ -226,7 +226,7 @@ int ivtv_streams_setup(struct ivtv *itv) if (ivtv_prep_dev(itv, type)) break; - if (itv->streams[type].v4l2dev == NULL) + if (itv->streams[type].vdev == NULL) continue; /* Allocate Stream */ @@ -247,28 +247,28 @@ static int ivtv_reg_dev(struct ivtv *itv, int type) int vfl_type = ivtv_stream_info[type].vfl_type; int num; - if (s->v4l2dev == NULL) + if (s->vdev == NULL) return 0; - num = s->v4l2dev->num; + num = s->vdev->num; /* card number + user defined offset + device offset */ if (type != IVTV_ENC_STREAM_TYPE_MPG) { struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG]; - if (s_mpg->v4l2dev) - num = s_mpg->v4l2dev->num + ivtv_stream_info[type].num_offset; + if (s_mpg->vdev) + num = s_mpg->vdev->num + ivtv_stream_info[type].num_offset; } - video_set_drvdata(s->v4l2dev, s); + video_set_drvdata(s->vdev, s); /* Register device. First try the desired minor, then any free one. */ - if (video_register_device(s->v4l2dev, vfl_type, num)) { + if (video_register_device(s->vdev, vfl_type, num)) { IVTV_ERR("Couldn't register v4l2 device for %s kernel number %d\n", s->name, num); - video_device_release(s->v4l2dev); - s->v4l2dev = NULL; + video_device_release(s->vdev); + s->vdev = NULL; return -ENOMEM; } - num = s->v4l2dev->num; + num = s->vdev->num; switch (vfl_type) { case VFL_TYPE_GRABBER: @@ -316,9 +316,9 @@ void ivtv_streams_cleanup(struct ivtv *itv, int unregister) /* Teardown all streams */ for (type = 0; type < IVTV_MAX_STREAMS; type++) { - struct video_device *vdev = itv->streams[type].v4l2dev; + struct video_device *vdev = itv->streams[type].vdev; - itv->streams[type].v4l2dev = NULL; + itv->streams[type].vdev = NULL; if (vdev == NULL) continue; @@ -449,7 +449,7 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) int captype = 0, subtype = 0; int enable_passthrough = 0; - if (s->v4l2dev == NULL) + if (s->vdev == NULL) return -EINVAL; IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name); @@ -611,7 +611,7 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s) struct cx2341x_mpeg_params *p = &itv->params; int datatype; - if (s->v4l2dev == NULL) + if (s->vdev == NULL) return -EINVAL; IVTV_DEBUG_INFO("Setting some initial decoder settings\n"); @@ -657,7 +657,7 @@ int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) { struct ivtv *itv = s->itv; - if (s->v4l2dev == NULL) + if (s->vdev == NULL) return -EINVAL; if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) @@ -705,7 +705,7 @@ void ivtv_stop_all_captures(struct ivtv *itv) for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) { struct ivtv_stream *s = &itv->streams[i]; - if (s->v4l2dev == NULL) + if (s->vdev == NULL) continue; if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { ivtv_stop_v4l2_encode_stream(s, 0); @@ -720,7 +720,7 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) int cap_type; int stopmode; - if (s->v4l2dev == NULL) + if (s->vdev == NULL) return -EINVAL; /* This function assumes that you are allowed to stop the capture @@ -834,7 +834,7 @@ int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) { struct ivtv *itv = s->itv; - if (s->v4l2dev == NULL) + if (s->vdev == NULL) return -EINVAL; if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG) @@ -895,7 +895,7 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable) struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV]; struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; - if (yuv_stream->v4l2dev == NULL || dec_stream->v4l2dev == NULL) + if (yuv_stream->vdev == NULL || dec_stream->vdev == NULL) return -EINVAL; IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n"); diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c index 460db03..d07ad6c 100644 --- a/drivers/media/video/ivtv/ivtv-udma.c +++ b/drivers/media/video/ivtv/ivtv-udma.c @@ -93,7 +93,7 @@ void ivtv_udma_alloc(struct ivtv *itv) { if (itv->udma.SG_handle == 0) { /* Map DMA Page Array Buffer */ - itv->udma.SG_handle = pci_map_single(itv->dev, itv->udma.SGarray, + itv->udma.SG_handle = pci_map_single(itv->pdev, itv->udma.SGarray, sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); ivtv_udma_sync_for_cpu(itv); } @@ -147,7 +147,7 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, } /* Map SG List */ - dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + dma->SG_length = pci_map_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); /* Fill SG Array with new values */ ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1); @@ -172,7 +172,7 @@ void ivtv_udma_unmap(struct ivtv *itv) /* Unmap Scatterlist */ if (dma->SG_length) { - pci_unmap_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + pci_unmap_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); dma->SG_length = 0; } /* sync DMA */ @@ -191,13 +191,13 @@ void ivtv_udma_free(struct ivtv *itv) /* Unmap SG Array */ if (itv->udma.SG_handle) { - pci_unmap_single(itv->dev, itv->udma.SG_handle, + pci_unmap_single(itv->pdev, itv->udma.SG_handle, sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); } /* Unmap Scatterlist */ if (itv->udma.SG_length) { - pci_unmap_sg(itv->dev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE); + pci_unmap_sg(itv->pdev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE); } for (i = 0; i < IVTV_DMA_SG_OSD_ENT; i++) { diff --git a/drivers/media/video/ivtv/ivtv-udma.h b/drivers/media/video/ivtv/ivtv-udma.h index df727e2..ee3c9ef 100644 --- a/drivers/media/video/ivtv/ivtv-udma.h +++ b/drivers/media/video/ivtv/ivtv-udma.h @@ -35,13 +35,13 @@ void ivtv_udma_start(struct ivtv *itv); static inline void ivtv_udma_sync_for_device(struct ivtv *itv) { - pci_dma_sync_single_for_device((struct pci_dev *)itv->dev, itv->udma.SG_handle, + pci_dma_sync_single_for_device(itv->pdev, itv->udma.SG_handle, sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); } static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv) { - pci_dma_sync_single_for_cpu((struct pci_dev *)itv->dev, itv->udma.SG_handle, + pci_dma_sync_single_for_cpu(itv->pdev, itv->udma.SG_handle, sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); } diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c index 5c5d1c4..f420d31 100644 --- a/drivers/media/video/ivtv/ivtv-vbi.c +++ b/drivers/media/video/ivtv/ivtv-vbi.c @@ -185,6 +185,8 @@ static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp) size = 4 + ((43 * line + 3) & ~3); } else { memcpy(dst + sd, "itv0", 4); + cpu_to_le32s(&linemask[0]); + cpu_to_le32s(&linemask[1]); memcpy(dst + sd + 4, &linemask[0], 8); size = 12 + ((43 * line + 3) & ~3); } diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h index 8cd753d..b530dec 100644 --- a/drivers/media/video/ivtv/ivtv-version.h +++ b/drivers/media/video/ivtv/ivtv-version.h @@ -23,7 +23,7 @@ #define IVTV_DRIVER_NAME "ivtv" #define IVTV_DRIVER_VERSION_MAJOR 1 #define IVTV_DRIVER_VERSION_MINOR 4 -#define IVTV_DRIVER_VERSION_PATCHLEVEL 0 +#define IVTV_DRIVER_VERSION_PATCHLEVEL 1 #define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) #define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c index ee91107..7912ed6 100644 --- a/drivers/media/video/ivtv/ivtv-yuv.c +++ b/drivers/media/video/ivtv/ivtv-yuv.c @@ -103,7 +103,7 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, dma->page_count = 0; return -ENOMEM; } - dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + dma->SG_length = pci_map_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); /* Fill SG Array with new values */ ivtv_udma_fill_sg_array(dma, y_buffer_offset, uv_buffer_offset, y_size); @@ -910,7 +910,7 @@ static void ivtv_yuv_init(struct ivtv *itv) /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */ yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL|__GFP_NOWARN); if (yi->blanking_ptr) { - yi->blanking_dmaptr = pci_map_single(itv->dev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE); + yi->blanking_dmaptr = pci_map_single(itv->pdev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE); } else { yi->blanking_dmaptr = 0; IVTV_DEBUG_WARN("Failed to allocate yuv blanking buffer\n"); @@ -1237,7 +1237,7 @@ void ivtv_yuv_close(struct ivtv *itv) if (yi->blanking_ptr) { kfree(yi->blanking_ptr); yi->blanking_ptr = NULL; - pci_unmap_single(itv->dev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE); + pci_unmap_single(itv->pdev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE); } /* Invalidate the old dimension information */ diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index 36abd2a..66e6eb5 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -1192,12 +1192,12 @@ static int ivtvfb_init_card(struct ivtv *itv) static int __init ivtvfb_callback_init(struct device *dev, void *p) { struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); - struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device); + struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { if (ivtvfb_init_card(itv) == 0) { IVTVFB_INFO("Framebuffer registered on %s\n", - itv->device.name); + itv->v4l2_dev.name); (*(int *)p)++; } } @@ -1207,7 +1207,7 @@ static int __init ivtvfb_callback_init(struct device *dev, void *p) static int ivtvfb_callback_cleanup(struct device *dev, void *p) { struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); - struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device); + struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev); if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) { diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c index bae2d2b..841024b 100644 --- a/drivers/media/video/ks0127.c +++ b/drivers/media/video/ks0127.c @@ -39,19 +39,20 @@ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/i2c.h> -#include <linux/video_decoder.h> -#include <media/v4l2-common.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> #include "ks0127.h" MODULE_DESCRIPTION("KS0127 video decoder driver"); MODULE_AUTHOR("Ryan Drake"); MODULE_LICENSE("GPL"); -#define KS_TYPE_UNKNOWN 0 -#define KS_TYPE_0122S 1 -#define KS_TYPE_0127 2 -#define KS_TYPE_0127B 3 +/* Addresses */ +#define I2C_KS0127_ADDON 0xD8 +#define I2C_KS0127_ONBOARD 0xDA + /* ks0127 control registers */ #define KS_STAT 0x00 @@ -197,15 +198,17 @@ struct adjust { }; struct ks0127 { - int format_width; - int format_height; - int cap_width; - int cap_height; - int norm; - int ks_type; + struct v4l2_subdev sd; + v4l2_std_id norm; + int ident; u8 regs[256]; }; +static inline struct ks0127 *to_ks0127(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ks0127, sd); +} + static int debug; /* insmod parameter */ @@ -311,43 +314,45 @@ static void init_reg_defaults(void) */ -static u8 ks0127_read(struct i2c_client *c, u8 reg) +static u8 ks0127_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); char val = 0; struct i2c_msg msgs[] = { - { c->addr, 0, sizeof(reg), ® }, - { c->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val } + { client->addr, 0, sizeof(reg), ® }, + { client->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val } }; int ret; - ret = i2c_transfer(c->adapter, msgs, ARRAY_SIZE(msgs)); + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret != ARRAY_SIZE(msgs)) - v4l_dbg(1, debug, c, "read error\n"); + v4l2_dbg(1, debug, sd, "read error\n"); return val; } -static void ks0127_write(struct i2c_client *c, u8 reg, u8 val) +static void ks0127_write(struct v4l2_subdev *sd, u8 reg, u8 val) { - struct ks0127 *ks = i2c_get_clientdata(c); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ks0127 *ks = to_ks0127(sd); char msg[] = { reg, val }; - if (i2c_master_send(c, msg, sizeof(msg)) != sizeof(msg)) - v4l_dbg(1, debug, c, "write error\n"); + if (i2c_master_send(client, msg, sizeof(msg)) != sizeof(msg)) + v4l2_dbg(1, debug, sd, "write error\n"); ks->regs[reg] = val; } /* generic bit-twiddling */ -static void ks0127_and_or(struct i2c_client *client, u8 reg, u8 and_v, u8 or_v) +static void ks0127_and_or(struct v4l2_subdev *sd, u8 reg, u8 and_v, u8 or_v) { - struct ks0127 *ks = i2c_get_clientdata(client); + struct ks0127 *ks = to_ks0127(sd); u8 val = ks->regs[reg]; val = (val & and_v) | or_v; - ks0127_write(client, reg, val); + ks0127_write(sd, reg, val); } @@ -355,439 +360,363 @@ static void ks0127_and_or(struct i2c_client *client, u8 reg, u8 and_v, u8 or_v) /**************************************************************************** * ks0127 private api ****************************************************************************/ -static void ks0127_reset(struct i2c_client *c) +static void ks0127_init(struct v4l2_subdev *sd) { - struct ks0127 *ks = i2c_get_clientdata(c); + struct ks0127 *ks = to_ks0127(sd); u8 *table = reg_defaults; int i; - ks->ks_type = KS_TYPE_UNKNOWN; + ks->ident = V4L2_IDENT_KS0127; - v4l_dbg(1, debug, c, "reset\n"); + v4l2_dbg(1, debug, sd, "reset\n"); msleep(1); /* initialize all registers to known values */ /* (except STAT, 0x21, 0x22, TEST and 0x38,0x39) */ for (i = 1; i < 33; i++) - ks0127_write(c, i, table[i]); + ks0127_write(sd, i, table[i]); for (i = 35; i < 40; i++) - ks0127_write(c, i, table[i]); + ks0127_write(sd, i, table[i]); for (i = 41; i < 56; i++) - ks0127_write(c, i, table[i]); + ks0127_write(sd, i, table[i]); for (i = 58; i < 64; i++) - ks0127_write(c, i, table[i]); + ks0127_write(sd, i, table[i]); - if ((ks0127_read(c, KS_STAT) & 0x80) == 0) { - ks->ks_type = KS_TYPE_0122S; - v4l_dbg(1, debug, c, "ks0122s found\n"); + if ((ks0127_read(sd, KS_STAT) & 0x80) == 0) { + ks->ident = V4L2_IDENT_KS0122S; + v4l2_dbg(1, debug, sd, "ks0122s found\n"); return; } - switch (ks0127_read(c, KS_CMDE) & 0x0f) { + switch (ks0127_read(sd, KS_CMDE) & 0x0f) { case 0: - ks->ks_type = KS_TYPE_0127; - v4l_dbg(1, debug, c, "ks0127 found\n"); + v4l2_dbg(1, debug, sd, "ks0127 found\n"); break; case 9: - ks->ks_type = KS_TYPE_0127B; - v4l_dbg(1, debug, c, "ks0127B Revision A found\n"); + ks->ident = V4L2_IDENT_KS0127B; + v4l2_dbg(1, debug, sd, "ks0127B Revision A found\n"); break; default: - v4l_dbg(1, debug, c, "unknown revision\n"); + v4l2_dbg(1, debug, sd, "unknown revision\n"); break; } } -static int ks0127_command(struct i2c_client *c, unsigned cmd, void *arg) +static int ks0127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct ks0127 *ks = i2c_get_clientdata(c); - int *iarg = (int *)arg; - int status; - - if (!ks) - return -ENODEV; + struct ks0127 *ks = to_ks0127(sd); + + switch (route->input) { + case KS_INPUT_COMPOSITE_1: + case KS_INPUT_COMPOSITE_2: + case KS_INPUT_COMPOSITE_3: + case KS_INPUT_COMPOSITE_4: + case KS_INPUT_COMPOSITE_5: + case KS_INPUT_COMPOSITE_6: + v4l2_dbg(1, debug, sd, + "s_routing %d: Composite\n", route->input); + /* autodetect 50/60 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); + /* VSE=0 */ + ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); + /* set input line */ + ks0127_and_or(sd, KS_CMDB, 0xb0, route->input); + /* non-freerunning mode */ + ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); + /* analog input */ + ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); + /* enable chroma demodulation */ + ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); + /* chroma trap, HYBWR=1 */ + ks0127_and_or(sd, KS_LUMA, 0x00, + (reg_defaults[KS_LUMA])|0x0c); + /* scaler fullbw, luma comb off */ + ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); + /* manual chroma comb .25 .5 .25 */ + ks0127_and_or(sd, KS_VERTIC, 0x0f, 0x90); + + /* chroma path delay */ + ks0127_and_or(sd, KS_CHROMB, 0x0f, 0x90); + + ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); + ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); + ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); + ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); + break; - switch (cmd) { - case DECODER_INIT: - v4l_dbg(1, debug, c, "DECODER_INIT\n"); - ks0127_reset(c); + case KS_INPUT_SVIDEO_1: + case KS_INPUT_SVIDEO_2: + case KS_INPUT_SVIDEO_3: + v4l2_dbg(1, debug, sd, + "s_routing %d: S-Video\n", route->input); + /* autodetect 50/60 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); + /* VSE=0 */ + ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); + /* set input line */ + ks0127_and_or(sd, KS_CMDB, 0xb0, route->input); + /* non-freerunning mode */ + ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); + /* analog input */ + ks0127_and_or(sd, KS_CMDD, 0x03, 0x00); + /* enable chroma demodulation */ + ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00); + ks0127_and_or(sd, KS_LUMA, 0x00, + reg_defaults[KS_LUMA]); + /* disable luma comb */ + ks0127_and_or(sd, KS_VERTIA, 0x08, + (reg_defaults[KS_VERTIA]&0xf0)|0x01); + ks0127_and_or(sd, KS_VERTIC, 0x0f, + reg_defaults[KS_VERTIC]&0xf0); + + ks0127_and_or(sd, KS_CHROMB, 0x0f, + reg_defaults[KS_CHROMB]&0xf0); + + ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]); + ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]); + ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]); + ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]); break; - case DECODER_SET_INPUT: - switch(*iarg) { - case KS_INPUT_COMPOSITE_1: - case KS_INPUT_COMPOSITE_2: - case KS_INPUT_COMPOSITE_3: - case KS_INPUT_COMPOSITE_4: - case KS_INPUT_COMPOSITE_5: - case KS_INPUT_COMPOSITE_6: - v4l_dbg(1, debug, c, - "DECODER_SET_INPUT %d: Composite\n", *iarg); - /* autodetect 50/60 Hz */ - ks0127_and_or(c, KS_CMDA, 0xfc, 0x00); - /* VSE=0 */ - ks0127_and_or(c, KS_CMDA, ~0x40, 0x00); - /* set input line */ - ks0127_and_or(c, KS_CMDB, 0xb0, *iarg); - /* non-freerunning mode */ - ks0127_and_or(c, KS_CMDC, 0x70, 0x0a); - /* analog input */ - ks0127_and_or(c, KS_CMDD, 0x03, 0x00); - /* enable chroma demodulation */ - ks0127_and_or(c, KS_CTRACK, 0xcf, 0x00); - /* chroma trap, HYBWR=1 */ - ks0127_and_or(c, KS_LUMA, 0x00, - (reg_defaults[KS_LUMA])|0x0c); - /* scaler fullbw, luma comb off */ - ks0127_and_or(c, KS_VERTIA, 0x08, 0x81); - /* manual chroma comb .25 .5 .25 */ - ks0127_and_or(c, KS_VERTIC, 0x0f, 0x90); - - /* chroma path delay */ - ks0127_and_or(c, KS_CHROMB, 0x0f, 0x90); - - ks0127_write(c, KS_UGAIN, reg_defaults[KS_UGAIN]); - ks0127_write(c, KS_VGAIN, reg_defaults[KS_VGAIN]); - ks0127_write(c, KS_UVOFFH, reg_defaults[KS_UVOFFH]); - ks0127_write(c, KS_UVOFFL, reg_defaults[KS_UVOFFL]); - break; - - case KS_INPUT_SVIDEO_1: - case KS_INPUT_SVIDEO_2: - case KS_INPUT_SVIDEO_3: - v4l_dbg(1, debug, c, - "DECODER_SET_INPUT %d: S-Video\n", *iarg); - /* autodetect 50/60 Hz */ - ks0127_and_or(c, KS_CMDA, 0xfc, 0x00); - /* VSE=0 */ - ks0127_and_or(c, KS_CMDA, ~0x40, 0x00); - /* set input line */ - ks0127_and_or(c, KS_CMDB, 0xb0, *iarg); - /* non-freerunning mode */ - ks0127_and_or(c, KS_CMDC, 0x70, 0x0a); - /* analog input */ - ks0127_and_or(c, KS_CMDD, 0x03, 0x00); - /* enable chroma demodulation */ - ks0127_and_or(c, KS_CTRACK, 0xcf, 0x00); - ks0127_and_or(c, KS_LUMA, 0x00, - reg_defaults[KS_LUMA]); - /* disable luma comb */ - ks0127_and_or(c, KS_VERTIA, 0x08, - (reg_defaults[KS_VERTIA]&0xf0)|0x01); - ks0127_and_or(c, KS_VERTIC, 0x0f, - reg_defaults[KS_VERTIC]&0xf0); - - ks0127_and_or(c, KS_CHROMB, 0x0f, - reg_defaults[KS_CHROMB]&0xf0); - - ks0127_write(c, KS_UGAIN, reg_defaults[KS_UGAIN]); - ks0127_write(c, KS_VGAIN, reg_defaults[KS_VGAIN]); - ks0127_write(c, KS_UVOFFH, reg_defaults[KS_UVOFFH]); - ks0127_write(c, KS_UVOFFL, reg_defaults[KS_UVOFFL]); - break; - - case KS_INPUT_YUV656: - v4l_dbg(1, debug, c, - "DECODER_SET_INPUT 15: YUV656\n"); - if (ks->norm == VIDEO_MODE_NTSC || - ks->norm == KS_STD_PAL_M) - /* force 60 Hz */ - ks0127_and_or(c, KS_CMDA, 0xfc, 0x03); - else - /* force 50 Hz */ - ks0127_and_or(c, KS_CMDA, 0xfc, 0x02); - - ks0127_and_or(c, KS_CMDA, 0xff, 0x40); /* VSE=1 */ - /* set input line and VALIGN */ - ks0127_and_or(c, KS_CMDB, 0xb0, (*iarg | 0x40)); - /* freerunning mode, */ - /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/ - ks0127_and_or(c, KS_CMDC, 0x70, 0x87); - /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */ - ks0127_and_or(c, KS_CMDD, 0x03, 0x08); - /* disable chroma demodulation */ - ks0127_and_or(c, KS_CTRACK, 0xcf, 0x30); - /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */ - ks0127_and_or(c, KS_LUMA, 0x00, 0x71); - ks0127_and_or(c, KS_VERTIC, 0x0f, - reg_defaults[KS_VERTIC]&0xf0); - - /* scaler fullbw, luma comb off */ - ks0127_and_or(c, KS_VERTIA, 0x08, 0x81); - - ks0127_and_or(c, KS_CHROMB, 0x0f, - reg_defaults[KS_CHROMB]&0xf0); - - ks0127_and_or(c, KS_CON, 0x00, 0x00); - ks0127_and_or(c, KS_BRT, 0x00, 32); /* spec: 34 */ - /* spec: 229 (e5) */ - ks0127_and_or(c, KS_SAT, 0x00, 0xe8); - ks0127_and_or(c, KS_HUE, 0x00, 0); - - ks0127_and_or(c, KS_UGAIN, 0x00, 238); - ks0127_and_or(c, KS_VGAIN, 0x00, 0x00); - - /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */ - ks0127_and_or(c, KS_UVOFFH, 0x00, 0x4f); - ks0127_and_or(c, KS_UVOFFL, 0x00, 0x00); - break; - - default: - v4l_dbg(1, debug, c, - "DECODER_SET_INPUT: Unknown input %d\n", *iarg); - break; - } - - /* hack: CDMLPF sometimes spontaneously switches on; */ - /* force back off */ - ks0127_write(c, KS_DEMOD, reg_defaults[KS_DEMOD]); + case KS_INPUT_YUV656: + v4l2_dbg(1, debug, sd, "s_routing 15: YUV656\n"); + if (ks->norm & V4L2_STD_525_60) + /* force 60 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x03); + else + /* force 50 Hz */ + ks0127_and_or(sd, KS_CMDA, 0xfc, 0x02); + + ks0127_and_or(sd, KS_CMDA, 0xff, 0x40); /* VSE=1 */ + /* set input line and VALIGN */ + ks0127_and_or(sd, KS_CMDB, 0xb0, (route->input | 0x40)); + /* freerunning mode, */ + /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/ + ks0127_and_or(sd, KS_CMDC, 0x70, 0x87); + /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */ + ks0127_and_or(sd, KS_CMDD, 0x03, 0x08); + /* disable chroma demodulation */ + ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x30); + /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */ + ks0127_and_or(sd, KS_LUMA, 0x00, 0x71); + ks0127_and_or(sd, KS_VERTIC, 0x0f, + reg_defaults[KS_VERTIC]&0xf0); + + /* scaler fullbw, luma comb off */ + ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81); + + ks0127_and_or(sd, KS_CHROMB, 0x0f, + reg_defaults[KS_CHROMB]&0xf0); + + ks0127_and_or(sd, KS_CON, 0x00, 0x00); + ks0127_and_or(sd, KS_BRT, 0x00, 32); /* spec: 34 */ + /* spec: 229 (e5) */ + ks0127_and_or(sd, KS_SAT, 0x00, 0xe8); + ks0127_and_or(sd, KS_HUE, 0x00, 0); + + ks0127_and_or(sd, KS_UGAIN, 0x00, 238); + ks0127_and_or(sd, KS_VGAIN, 0x00, 0x00); + + /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */ + ks0127_and_or(sd, KS_UVOFFH, 0x00, 0x4f); + ks0127_and_or(sd, KS_UVOFFL, 0x00, 0x00); break; - case DECODER_SET_OUTPUT: - switch(*iarg) { - case KS_OUTPUT_YUV656E: - v4l_dbg(1, debug, c, - "DECODER_SET_OUTPUT: OUTPUT_YUV656E (Missing)\n"); - return -EINVAL; - - case KS_OUTPUT_EXV: - v4l_dbg(1, debug, c, - "DECODER_SET_OUTPUT: OUTPUT_EXV\n"); - ks0127_and_or(c, KS_OFMTA, 0xf0, 0x09); - break; - } + default: + v4l2_dbg(1, debug, sd, + "s_routing: Unknown input %d\n", route->input); break; + } - case DECODER_SET_NORM: /* sam This block mixes old and new norm names... */ - /* Set to automatic SECAM/Fsc mode */ - ks0127_and_or(c, KS_DEMOD, 0xf0, 0x00); - - ks->norm = *iarg; - switch (*iarg) { - /* this is untested !! */ - /* It just detects PAL_N/NTSC_M (no special frequencies) */ - /* And you have to set the standard a second time afterwards */ - case VIDEO_MODE_AUTO: - v4l_dbg(1, debug, c, - "DECODER_SET_NORM: AUTO\n"); - - /* The chip determines the format */ - /* based on the current field rate */ - ks0127_and_or(c, KS_CMDA, 0xfc, 0x00); - ks0127_and_or(c, KS_CHROMA, 0x9f, 0x20); - /* This is wrong for PAL ! As I said, */ - /* you need to set the standard once again !! */ - ks->format_height = 240; - ks->format_width = 704; - break; - - case VIDEO_MODE_NTSC: - v4l_dbg(1, debug, c, - "DECODER_SET_NORM: NTSC_M\n"); - ks0127_and_or(c, KS_CHROMA, 0x9f, 0x20); - ks->format_height = 240; - ks->format_width = 704; - break; - - case KS_STD_NTSC_N: - v4l_dbg(1, debug, c, - "KS0127_SET_NORM: NTSC_N (fixme)\n"); - ks0127_and_or(c, KS_CHROMA, 0x9f, 0x40); - ks->format_height = 240; - ks->format_width = 704; - break; - - case VIDEO_MODE_PAL: - v4l_dbg(1, debug, c, - "DECODER_SET_NORM: PAL_N\n"); - ks0127_and_or(c, KS_CHROMA, 0x9f, 0x20); - ks->format_height = 290; - ks->format_width = 704; - break; - - case KS_STD_PAL_M: - v4l_dbg(1, debug, c, - "KS0127_SET_NORM: PAL_M (fixme)\n"); - ks0127_and_or(c, KS_CHROMA, 0x9f, 0x40); - ks->format_height = 290; - ks->format_width = 704; - break; - - case VIDEO_MODE_SECAM: - v4l_dbg(1, debug, c, - "KS0127_SET_NORM: SECAM\n"); - ks->format_height = 290; - ks->format_width = 704; - - /* set to secam autodetection */ - ks0127_and_or(c, KS_CHROMA, 0xdf, 0x20); - ks0127_and_or(c, KS_DEMOD, 0xf0, 0x00); - schedule_timeout_interruptible(HZ/10+1); - - /* did it autodetect? */ - if (ks0127_read(c, KS_DEMOD) & 0x40) - break; + /* hack: CDMLPF sometimes spontaneously switches on; */ + /* force back off */ + ks0127_write(sd, KS_DEMOD, reg_defaults[KS_DEMOD]); + return 0; +} +static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct ks0127 *ks = to_ks0127(sd); + + /* Set to automatic SECAM/Fsc mode */ + ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); + + ks->norm = std; + if (std & V4L2_STD_NTSC) { + v4l2_dbg(1, debug, sd, + "s_std: NTSC_M\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); + } else if (std & V4L2_STD_PAL_N) { + v4l2_dbg(1, debug, sd, + "s_std: NTSC_N (fixme)\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); + } else if (std & V4L2_STD_PAL) { + v4l2_dbg(1, debug, sd, + "s_std: PAL_N\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); + } else if (std & V4L2_STD_PAL_M) { + v4l2_dbg(1, debug, sd, + "s_std: PAL_M (fixme)\n"); + ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); + } else if (std & V4L2_STD_SECAM) { + v4l2_dbg(1, debug, sd, + "s_std: SECAM\n"); + + /* set to secam autodetection */ + ks0127_and_or(sd, KS_CHROMA, 0xdf, 0x20); + ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00); + schedule_timeout_interruptible(HZ/10+1); + + /* did it autodetect? */ + if (!(ks0127_read(sd, KS_DEMOD) & 0x40)) /* force to secam mode */ - ks0127_and_or(c, KS_DEMOD, 0xf0, 0x0f); - break; - - default: - v4l_dbg(1, debug, c, - "DECODER_SET_NORM: Unknown norm %d\n", *iarg); - break; - } - break; - - case DECODER_SET_PICTURE: - v4l_dbg(1, debug, c, - "DECODER_SET_PICTURE: not yet supported\n"); - return -EINVAL; - - /* sam todo: KS0127_SET_BRIGHTNESS: Merge into DECODER_SET_PICTURE */ - /* sam todo: KS0127_SET_CONTRAST: Merge into DECODER_SET_PICTURE */ - /* sam todo: KS0127_SET_HUE: Merge into DECODER_SET_PICTURE? */ - /* sam todo: KS0127_SET_SATURATION: Merge into DECODER_SET_PICTURE */ - /* sam todo: KS0127_SET_AGC_MODE: */ - /* sam todo: KS0127_SET_AGC: */ - /* sam todo: KS0127_SET_CHROMA_MODE: */ - /* sam todo: KS0127_SET_PIXCLK_MODE: */ - /* sam todo: KS0127_SET_GAMMA_MODE: */ - /* sam todo: KS0127_SET_UGAIN: */ - /* sam todo: KS0127_SET_VGAIN: */ - /* sam todo: KS0127_SET_INVALY: */ - /* sam todo: KS0127_SET_INVALU: */ - /* sam todo: KS0127_SET_INVALV: */ - /* sam todo: KS0127_SET_UNUSEY: */ - /* sam todo: KS0127_SET_UNUSEU: */ - /* sam todo: KS0127_SET_UNUSEV: */ - /* sam todo: KS0127_SET_VSALIGN_MODE: */ - - case DECODER_ENABLE_OUTPUT: - { - int enable; - - iarg = arg; - enable = (*iarg != 0); - if (enable) { - v4l_dbg(1, debug, c, - "DECODER_ENABLE_OUTPUT on\n"); - /* All output pins on */ - ks0127_and_or(c, KS_OFMTA, 0xcf, 0x30); - /* Obey the OEN pin */ - ks0127_and_or(c, KS_CDEM, 0x7f, 0x00); - } else { - v4l_dbg(1, debug, c, - "DECODER_ENABLE_OUTPUT off\n"); - /* Video output pins off */ - ks0127_and_or(c, KS_OFMTA, 0xcf, 0x00); - /* Ignore the OEN pin */ - ks0127_and_or(c, KS_CDEM, 0x7f, 0x80); - } - break; + ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f); + } else { + v4l2_dbg(1, debug, sd, "s_std: Unknown norm %llx\n", + (unsigned long long)std); } + return 0; +} - /* sam todo: KS0127_SET_OUTPUT_MODE: */ - /* sam todo: KS0127_SET_WIDTH: */ - /* sam todo: KS0127_SET_HEIGHT: */ - /* sam todo: KS0127_SET_HSCALE: */ - - case DECODER_GET_STATUS: - v4l_dbg(1, debug, c, "DECODER_GET_STATUS\n"); - *iarg = 0; - status = ks0127_read(c, KS_STAT); - if (!(status & 0x20)) /* NOVID not set */ - *iarg = (*iarg | DECODER_STATUS_GOOD); - if ((status & 0x01)) /* CLOCK set */ - *iarg = (*iarg | DECODER_STATUS_COLOR); - if ((status & 0x08)) /* PALDET set */ - *iarg = (*iarg | DECODER_STATUS_PAL); - else - *iarg = (*iarg | DECODER_STATUS_NTSC); - break; - - /* Catch any unknown command */ - default: - v4l_dbg(1, debug, c, "unknown: 0x%08x\n", cmd); - return -EINVAL; +static int ks0127_s_stream(struct v4l2_subdev *sd, int enable) +{ + v4l2_dbg(1, debug, sd, "s_stream(%d)\n", enable); + if (enable) { + /* All output pins on */ + ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x30); + /* Obey the OEN pin */ + ks0127_and_or(sd, KS_CDEM, 0x7f, 0x00); + } else { + /* Video output pins off */ + ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x00); + /* Ignore the OEN pin */ + ks0127_and_or(sd, KS_CDEM, 0x7f, 0x80); } return 0; } +static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) +{ + int stat = V4L2_IN_ST_NO_SIGNAL; + u8 status; + v4l2_std_id std = V4L2_STD_ALL; + + status = ks0127_read(sd, KS_STAT); + if (!(status & 0x20)) /* NOVID not set */ + stat = 0; + if (!(status & 0x01)) /* CLOCK set */ + stat |= V4L2_IN_ST_NO_COLOR; + if ((status & 0x08)) /* PALDET set */ + std = V4L2_STD_PAL; + else + std = V4L2_STD_NTSC; + if (pstd) + *pstd = std; + if (pstatus) + *pstatus = stat; + return 0; +} + +static int ks0127_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + v4l2_dbg(1, debug, sd, "querystd\n"); + return ks0127_status(sd, NULL, std); +} -/* Addresses to scan */ -#define I2C_KS0127_ADDON 0xD8 -#define I2C_KS0127_ONBOARD 0xDA +static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + v4l2_dbg(1, debug, sd, "g_input_status\n"); + return ks0127_status(sd, status, NULL); +} + +static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ks0127 *ks = to_ks0127(sd); + + return v4l2_chip_ident_i2c_client(client, chip, ks->ident, 0); +} + +/* ----------------------------------------------------------------------- */ -static unsigned short normal_i2c[] = { - I2C_KS0127_ADDON >> 1, - I2C_KS0127_ONBOARD >> 1, - I2C_CLIENT_END +static const struct v4l2_subdev_core_ops ks0127_core_ops = { + .g_chip_ident = ks0127_g_chip_ident, }; -I2C_CLIENT_INSMOD; +static const struct v4l2_subdev_tuner_ops ks0127_tuner_ops = { + .s_std = ks0127_s_std, +}; + +static const struct v4l2_subdev_video_ops ks0127_video_ops = { + .s_routing = ks0127_s_routing, + .s_stream = ks0127_s_stream, + .querystd = ks0127_querystd, + .g_input_status = ks0127_g_input_status, +}; + +static const struct v4l2_subdev_ops ks0127_ops = { + .core = &ks0127_core_ops, + .tuner = &ks0127_tuner_ops, + .video = &ks0127_video_ops, +}; + +/* ----------------------------------------------------------------------- */ + -static int ks0127_probe(struct i2c_client *c, const struct i2c_device_id *id) +static int ks0127_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ks0127 *ks; + struct v4l2_subdev *sd; - v4l_info(c, "%s chip found @ 0x%x (%s)\n", - c->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board", - c->addr << 1, c->adapter->name); + v4l_info(client, "%s chip found @ 0x%x (%s)\n", + client->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board", + client->addr << 1, client->adapter->name); ks = kzalloc(sizeof(*ks), GFP_KERNEL); if (ks == NULL) return -ENOMEM; - - i2c_set_clientdata(c, ks); - - ks->ks_type = KS_TYPE_UNKNOWN; + sd = &ks->sd; + v4l2_i2c_subdev_init(sd, client, &ks0127_ops); /* power up */ init_reg_defaults(); - ks0127_write(c, KS_CMDA, 0x2c); + ks0127_write(sd, KS_CMDA, 0x2c); mdelay(10); /* reset the device */ - ks0127_reset(c); + ks0127_init(sd); return 0; } -static int ks0127_remove(struct i2c_client *c) +static int ks0127_remove(struct i2c_client *client) { - struct ks0127 *ks = i2c_get_clientdata(c); - - ks0127_write(c, KS_OFMTA, 0x20); /* tristate */ - ks0127_write(c, KS_CMDA, 0x2c | 0x80); /* power down */ + struct v4l2_subdev *sd = i2c_get_clientdata(client); - kfree(ks); + v4l2_device_unregister_subdev(sd); + ks0127_write(sd, KS_OFMTA, 0x20); /* tristate */ + ks0127_write(sd, KS_CMDA, 0x2c | 0x80); /* power down */ + kfree(to_ks0127(sd)); return 0; } -static int ks0127_legacy_probe(struct i2c_adapter *adapter) -{ - return adapter->id == I2C_HW_B_ZR36067; -} - static const struct i2c_device_id ks0127_id[] = { { "ks0127", 0 }, + { "ks0127b", 0 }, + { "ks0122s", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ks0127_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "ks0127", - .driverid = I2C_DRIVERID_KS0127, - .command = ks0127_command, .probe = ks0127_probe, .remove = ks0127_remove, - .legacy_probe = ks0127_legacy_probe, .id_table = ks0127_id, }; diff --git a/drivers/media/video/ks0127.h b/drivers/media/video/ks0127.h index 1ec5788..cb8abd5 100644 --- a/drivers/media/video/ks0127.h +++ b/drivers/media/video/ks0127.h @@ -24,8 +24,6 @@ #ifndef KS0127_H #define KS0127_H -#include <linux/videodev.h> - /* input channels */ #define KS_INPUT_COMPOSITE_1 0 #define KS_INPUT_COMPOSITE_2 1 diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c index de397ef..1f340fe 100644 --- a/drivers/media/video/m52790.c +++ b/drivers/media/video/m52790.c @@ -132,11 +132,6 @@ static int m52790_log_status(struct v4l2_subdev *sd) return 0; } -static int m52790_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops m52790_core_ops = { @@ -210,8 +205,6 @@ MODULE_DEVICE_TABLE(i2c, m52790_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "m52790", - .driverid = I2C_DRIVERID_M52790, - .command = m52790_command, .probe = m52790_probe, .remove = m52790_remove, .id_table = m52790_id, diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index b76e33d..2ad11f0 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -1017,7 +1017,6 @@ static int meyeioc_stilljcapt(int *len) static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { - memset(cap, 0, sizeof(*cap)); strcpy(cap->driver, "meye"); strcpy(cap->card, "meye"); sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev)); @@ -1036,8 +1035,6 @@ static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) if (i->index != 0) return -EINVAL; - memset(i, 0, sizeof(*i)); - i->index = 0; strcpy(i->name, "Camera"); i->type = V4L2_INPUT_TYPE_CAMERA; @@ -1259,22 +1256,13 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, if (f->index > 1) return -EINVAL; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->index == 0) { /* standard YUV 422 capture */ - memset(f, 0, sizeof(*f)); - f->index = 0; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->flags = 0; strcpy(f->description, "YUV422"); f->pixelformat = V4L2_PIX_FMT_YUYV; } else { /* compressed MJPEG capture */ - memset(f, 0, sizeof(*f)); - f->index = 1; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->flags = V4L2_FMT_FLAG_COMPRESSED; strcpy(f->description, "MJPEG"); f->pixelformat = V4L2_PIX_FMT_MJPEG; @@ -1286,9 +1274,6 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) return -EINVAL; @@ -1319,12 +1304,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - switch (meye.mchip_mode) { case MCHIP_HIC_MODE_CONT_OUT: default: @@ -1341,8 +1320,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, f->fmt.pix.bytesperline = f->fmt.pix.width * 2; f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; return 0; } @@ -1350,9 +1327,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) return -EINVAL; @@ -1398,9 +1372,6 @@ static int vidioc_reqbufs(struct file *file, void *fh, { int i; - if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (req->memory != V4L2_MEMORY_MMAP) return -EINVAL; @@ -1441,15 +1412,11 @@ static int vidioc_reqbufs(struct file *file, void *fh, static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { - int index = buf->index; + unsigned int index = buf->index; - if (index < 0 || index >= gbuffers) + if (index >= gbuffers) return -EINVAL; - memset(buf, 0, sizeof(*buf)); - - buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->index = index; buf->bytesused = meye.grab_buffer[index].size; buf->flags = V4L2_BUF_FLAG_MAPPED; @@ -1471,13 +1438,10 @@ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (buf->memory != V4L2_MEMORY_MMAP) return -EINVAL; - if (buf->index < 0 || buf->index >= gbuffers) + if (buf->index >= gbuffers) return -EINVAL; if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED) @@ -1497,9 +1461,6 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { int reqnr; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (buf->memory != V4L2_MEMORY_MMAP) return -EINVAL; diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 4d7a918..9e8e06c 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -366,29 +366,6 @@ int msp_sleep(struct msp_state *state, int timeout) } /* ------------------------------------------------------------------------ */ -#ifdef CONFIG_VIDEO_ALLOW_V4L1 -static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode) -{ - if (rxsubchans == V4L2_TUNER_SUB_MONO) - return VIDEO_SOUND_MONO; - if (rxsubchans == V4L2_TUNER_SUB_STEREO) - return VIDEO_SOUND_STEREO; - if (audmode == V4L2_TUNER_MODE_LANG2) - return VIDEO_SOUND_LANG2; - return VIDEO_SOUND_LANG1; -} - -static int msp_mode_v4l1_to_v4l2(int mode) -{ - if (mode & VIDEO_SOUND_STEREO) - return V4L2_TUNER_MODE_STEREO; - if (mode & VIDEO_SOUND_LANG2) - return V4L2_TUNER_MODE_LANG2; - if (mode & VIDEO_SOUND_LANG1) - return V4L2_TUNER_MODE_LANG1; - return V4L2_TUNER_MODE_MONO; -} -#endif static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { @@ -482,96 +459,6 @@ static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) return 0; } -#ifdef CONFIG_VIDEO_ALLOW_V4L1 -static long msp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct msp_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - switch (cmd) { - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOCGAUDIO: - { - struct video_audio *va = arg; - - va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_MUTABLE; - if (state->has_sound_processing) - va->flags |= VIDEO_AUDIO_BALANCE | - VIDEO_AUDIO_BASS | - VIDEO_AUDIO_TREBLE; - if (state->muted) - va->flags |= VIDEO_AUDIO_MUTE; - va->volume = state->volume; - va->balance = state->volume ? state->balance : 32768; - va->bass = state->bass; - va->treble = state->treble; - - if (state->radio) - break; - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans, state->audmode); - break; - } - - case VIDIOCSAUDIO: - { - struct video_audio *va = arg; - - state->muted = (va->flags & VIDEO_AUDIO_MUTE); - state->volume = va->volume; - state->balance = va->balance; - state->bass = va->bass; - state->treble = va->treble; - msp_set_audio(client); - - if (va->mode != 0 && state->radio == 0 && - state->audmode != msp_mode_v4l1_to_v4l2(va->mode)) { - state->audmode = msp_mode_v4l1_to_v4l2(va->mode); - msp_set_audmode(client); - } - break; - } - - case VIDIOCSCHAN: - { - struct video_channel *vc = arg; - int update = 0; - v4l2_std_id std; - - if (state->radio) - update = 1; - state->radio = 0; - if (vc->norm == VIDEO_MODE_PAL) - std = V4L2_STD_PAL; - else if (vc->norm == VIDEO_MODE_SECAM) - std = V4L2_STD_SECAM; - else - std = V4L2_STD_NTSC; - if (std != state->v4l2_std) { - state->v4l2_std = std; - update = 1; - } - if (update) - msp_wake_thread(client); - break; - } - - case VIDIOCSFREQ: - { - /* new channel -- kick audio carrier scan */ - msp_wake_thread(client); - break; - } - default: - return -ENOIOCTLCMD; - } - return 0; -} -#endif - /* --- v4l2 ioctls --- */ static int msp_s_radio(struct v4l2_subdev *sd) { @@ -713,22 +600,24 @@ static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) struct msp_state *state = to_state(sd); switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill_std(qc); - default: - break; + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + default: + break; } if (!state->has_sound_processing) return -EINVAL; switch (qc->id) { - case V4L2_CID_AUDIO_LOUDNESS: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); - default: - return -EINVAL; + case V4L2_CID_AUDIO_LOUDNESS: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); + default: + return -EINVAL; } return 0; } @@ -820,9 +709,6 @@ static const struct v4l2_subdev_core_ops msp_core_ops = { .g_ctrl = msp_g_ctrl, .s_ctrl = msp_s_ctrl, .queryctrl = msp_queryctrl, -#ifdef CONFIG_VIDEO_ALLOW_V4L1 - .ioctl = msp_ioctl, -#endif }; static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index c1bf75e..fa7e509 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -12,7 +12,6 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/log2.h> -#include <linux/gpio.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> @@ -73,9 +72,7 @@ struct mt9m001 { struct i2c_client *client; struct soc_camera_device icd; int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ - int switch_gpio; unsigned char autoexposure; - unsigned char datawidth; }; static int reg_read(struct soc_camera_device *icd, const u8 reg) @@ -181,92 +178,28 @@ static int mt9m001_stop_capture(struct soc_camera_device *icd) return 0; } -static int bus_switch_request(struct mt9m001 *mt9m001, - struct soc_camera_link *icl) -{ -#ifdef CONFIG_MT9M001_PCA9536_SWITCH - int ret; - unsigned int gpio = icl->gpio; - - if (gpio_is_valid(gpio)) { - /* We have a data bus switch. */ - ret = gpio_request(gpio, "mt9m001"); - if (ret < 0) { - dev_err(&mt9m001->client->dev, "Cannot get GPIO %u\n", - gpio); - return ret; - } - - ret = gpio_direction_output(gpio, 0); - if (ret < 0) { - dev_err(&mt9m001->client->dev, - "Cannot set GPIO %u to output\n", gpio); - gpio_free(gpio); - return ret; - } - } - - mt9m001->switch_gpio = gpio; -#else - mt9m001->switch_gpio = -EINVAL; -#endif - return 0; -} - -static void bus_switch_release(struct mt9m001 *mt9m001) -{ -#ifdef CONFIG_MT9M001_PCA9536_SWITCH - if (gpio_is_valid(mt9m001->switch_gpio)) - gpio_free(mt9m001->switch_gpio); -#endif -} - -static int bus_switch_act(struct mt9m001 *mt9m001, int go8bit) -{ -#ifdef CONFIG_MT9M001_PCA9536_SWITCH - if (!gpio_is_valid(mt9m001->switch_gpio)) - return -ENODEV; - - gpio_set_value_cansleep(mt9m001->switch_gpio, go8bit); - return 0; -#else - return -ENODEV; -#endif -} - -static int bus_switch_possible(struct mt9m001 *mt9m001) -{ -#ifdef CONFIG_MT9M001_PCA9536_SWITCH - return gpio_is_valid(mt9m001->switch_gpio); -#else - return 0; -#endif -} - static int mt9m001_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; - int ret; + struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK; - /* Flags validity verified in test_bus_param */ + /* Only one width bit may be set */ + if (!is_power_of_2(width_flag)) + return -EINVAL; - if ((mt9m001->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) || - (mt9m001->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) || - (mt9m001->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) { - /* Well, we actually only can do 10 or 8 bits... */ - if (width_flag == SOCAM_DATAWIDTH_9) - return -EINVAL; - ret = bus_switch_act(mt9m001, - width_flag == SOCAM_DATAWIDTH_8); - if (ret < 0) - return ret; + if (icl->set_bus_param) + return icl->set_bus_param(icl, width_flag); - mt9m001->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; - } + /* + * Without board specific bus width settings we only support the + * sensors native bus width + */ + if (width_flag == SOCAM_DATAWIDTH_10) + return 0; - return 0; + return -EINVAL; } static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) @@ -274,18 +207,20 @@ 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; /* MT9M001 has all capture_format parameters fixed */ - unsigned long flags = SOCAM_DATAWIDTH_10 | SOCAM_PCLK_SAMPLE_RISING | + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_MASTER; + SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER; - if (bus_switch_possible(mt9m001)) - flags |= SOCAM_DATAWIDTH_8; + if (icl->query_bus_param) + flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK; + else + flags |= SOCAM_DATAWIDTH_10; return soc_camera_apply_sensor_flags(icl, flags); } -static int mt9m001_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9m001_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); int ret; @@ -324,6 +259,20 @@ static int mt9m001_set_fmt(struct soc_camera_device *icd, return ret; } +static int mt9m001_set_fmt(struct soc_camera_device *icd, + 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, + }; + + /* No support for scaling so far, just crop. TODO: use skipping */ + return mt9m001_set_crop(icd, &rect); +} + static int mt9m001_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { @@ -449,6 +398,7 @@ static struct soc_camera_ops mt9m001_ops = { .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, @@ -583,6 +533,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) struct soc_camera_link *icl = mt9m001->client->dev.platform_data; s32 data; int ret; + unsigned long flags; /* We must have a parent by now. And it cannot be a wrong one. * So this entire test is completely redundant. */ @@ -603,18 +554,10 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) case 0x8421: mt9m001->model = V4L2_IDENT_MT9M001C12ST; icd->formats = mt9m001_colour_formats; - if (gpio_is_valid(icl->gpio)) - icd->num_formats = ARRAY_SIZE(mt9m001_colour_formats); - else - icd->num_formats = 1; break; case 0x8431: mt9m001->model = V4L2_IDENT_MT9M001C12STM; icd->formats = mt9m001_monochrome_formats; - if (gpio_is_valid(icl->gpio)) - icd->num_formats = ARRAY_SIZE(mt9m001_monochrome_formats); - else - icd->num_formats = 1; break; default: ret = -ENODEV; @@ -623,6 +566,26 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) goto ei2c; } + icd->num_formats = 0; + + /* + * This is a 10bit sensor, so by default we only allow 10bit. + * The platform may support different bus widths due to + * different routing of the data lines. + */ + if (icl->query_bus_param) + flags = icl->query_bus_param(icl); + else + flags = SOCAM_DATAWIDTH_10; + + if (flags & SOCAM_DATAWIDTH_10) + icd->num_formats++; + else + icd->formats++; + + if (flags & SOCAM_DATAWIDTH_8) + icd->num_formats++; + dev_info(&icd->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, data == 0x8431 ? "C12STM" : "C12ST"); @@ -688,18 +651,10 @@ static int mt9m001_probe(struct i2c_client *client, icd->height_max = 1024; icd->y_skip_top = 1; icd->iface = icl->bus_id; - /* Default datawidth - this is the only width this camera (normally) - * supports. It is only with extra logic that it can support - * other widths. Therefore it seems to be a sensible default. */ - mt9m001->datawidth = 10; /* Simulated autoexposure. If enabled, we calculate shutter width * ourselves in the driver based on vertical blanking and frame width */ mt9m001->autoexposure = 1; - ret = bus_switch_request(mt9m001, icl); - if (ret) - goto eswinit; - ret = soc_camera_device_register(icd); if (ret) goto eisdr; @@ -707,8 +662,6 @@ static int mt9m001_probe(struct i2c_client *client, return 0; eisdr: - bus_switch_release(mt9m001); -eswinit: kfree(mt9m001); return ret; } @@ -718,7 +671,6 @@ static int mt9m001_remove(struct i2c_client *client) struct mt9m001 *mt9m001 = i2c_get_clientdata(client); soc_camera_device_unregister(&mt9m001->icd); - bus_switch_release(mt9m001); kfree(mt9m001); return 0; diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index 5b8e209..cdd1ddb 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -152,7 +152,7 @@ struct mt9m111 { struct soc_camera_device icd; int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */ enum mt9m111_context context; - unsigned int left, top, width, height; + struct v4l2_rect rect; u32 pixfmt; unsigned char autoexposure; unsigned char datawidth; @@ -249,12 +249,13 @@ 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 soc_camera_device *icd, + struct v4l2_rect *rect) { struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); int ret, is_raw_format; - int width = mt9m111->width; - int height = mt9m111->height; + int width = rect->width; + int height = rect->height; if ((mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8) || (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16)) @@ -262,9 +263,9 @@ static int mt9m111_setup_rect(struct soc_camera_device *icd) else is_raw_format = 0; - ret = reg_write(COLUMN_START, mt9m111->left); + ret = reg_write(COLUMN_START, rect->left); if (!ret) - ret = reg_write(ROW_START, mt9m111->top); + ret = reg_write(ROW_START, rect->top); if (is_raw_format) { if (!ret) @@ -393,6 +394,8 @@ static int mt9m111_disable(struct soc_camera_device *icd) static int mt9m111_reset(struct soc_camera_device *icd) { + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct soc_camera_link *icl = mt9m111->client->dev.platform_data; int ret; ret = reg_set(RESET, MT9M111_RESET_RESET_MODE); @@ -401,6 +404,10 @@ static int mt9m111_reset(struct soc_camera_device *icd) if (!ret) ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE | MT9M111_RESET_RESET_SOC); + + if (icl->reset) + icl->reset(&mt9m111->client->dev); + return ret; } @@ -420,7 +427,7 @@ static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd) struct soc_camera_link *icl = mt9m111->client->dev.platform_data; unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | - SOCAM_DATAWIDTH_8; + SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; return soc_camera_apply_sensor_flags(icl, flags); } @@ -430,6 +437,22 @@ 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) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + 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); + + ret = mt9m111_setup_rect(icd, rect); + if (!ret) + mt9m111->rect = *rect; + return ret; +} + static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) { struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); @@ -480,23 +503,27 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) } static int mt9m111_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) + struct v4l2_format *f) { struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_rect rect = { + .left = mt9m111->rect.left, + .top = mt9m111->rect.top, + .width = pix->width, + .height = pix->height, + }; int ret; - mt9m111->left = rect->left; - mt9m111->top = rect->top; - mt9m111->width = rect->width; - mt9m111->height = rect->height; - dev_dbg(&icd->dev, "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n", - __func__, pixfmt, mt9m111->left, mt9m111->top, mt9m111->width, - mt9m111->height); + __func__, pix->pixelformat, rect.left, rect.top, rect.width, + rect.height); - ret = mt9m111_setup_rect(icd); + ret = mt9m111_setup_rect(icd, &rect); + if (!ret) + ret = mt9m111_set_pixfmt(icd, pix->pixelformat); if (!ret) - ret = mt9m111_set_pixfmt(icd, pixfmt); + mt9m111->rect = rect; return ret; } @@ -627,6 +654,7 @@ static struct soc_camera_ops mt9m111_ops = { .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, @@ -811,7 +839,7 @@ static int mt9m111_restore_state(struct soc_camera_device *icd) mt9m111_set_context(icd, mt9m111->context); mt9m111_set_pixfmt(icd, mt9m111->pixfmt); - mt9m111_setup_rect(icd); + 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); diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 349d8e3..23f9ce9 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -144,13 +144,11 @@ static int mt9t031_init(struct soc_camera_device *icd) int ret; /* Disable chip output, synchronous option update */ - dev_dbg(icd->vdev->parent, "%s\n", __func__); - ret = reg_write(icd, MT9T031_RESET, 1); if (ret >= 0) ret = reg_write(icd, MT9T031_RESET, 0); if (ret >= 0) - ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3); + ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2); return ret >= 0 ? 0 : -EIO; } @@ -158,14 +156,14 @@ static int mt9t031_init(struct soc_camera_device *icd) static int mt9t031_release(struct soc_camera_device *icd) { /* Disable the chip */ - reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3); + reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2); return 0; } static int mt9t031_start_capture(struct soc_camera_device *icd) { /* Switch to master "normal" mode */ - if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 3) < 0) + if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 2) < 0) return -EIO; return 0; } @@ -173,7 +171,7 @@ static int mt9t031_start_capture(struct soc_camera_device *icd) static int mt9t031_stop_capture(struct soc_camera_device *icd) { /* Stop sensor readout */ - if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3) < 0) + if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2) < 0) return -EIO; return 0; } @@ -186,9 +184,9 @@ static int mt9t031_set_bus_param(struct soc_camera_device *icd, return -EINVAL; if (flags & SOCAM_PCLK_SAMPLE_FALLING) - reg_set(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); - else reg_clear(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); + else + reg_set(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); return 0; } @@ -201,67 +199,73 @@ static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd) return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM); } -static int mt9t031_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +/* Round up minima and round down maxima */ +static void recalculate_limits(struct soc_camera_device *icd, + u16 xskip, u16 yskip) +{ + 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; +} + +static int mt9t031_set_params(struct soc_camera_device *icd, + struct v4l2_rect *rect, u16 xskip, u16 yskip) { struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); int ret; + u16 xbin, ybin, width, height, left, top; const u16 hblank = MT9T031_HORIZONTAL_BLANK, vblank = MT9T031_VERTICAL_BLANK; - u16 xbin, xskip = mt9t031->xskip, ybin, yskip = mt9t031->yskip, - width = rect->width * xskip, height = rect->height * yskip; - if (pixfmt) { - /* S_FMT - use binning and skipping for scaling, recalculate */ - /* Is this more optimal than just a division? */ - for (xskip = 8; xskip > 1; xskip--) - if (rect->width * xskip <= icd->width_max) - break; + /* 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; - for (yskip = 8; yskip > 1; yskip--) - if (rect->height * yskip <= icd->height_max) - break; + 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; - - dev_dbg(&icd->dev, "xskip %u, width %u, yskip %u, height %u\n", - xskip, width, yskip, height); - } + 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); - /* Make sure we don't exceed frame limits */ - if (rect->left + width > icd->width_max) - rect->left = (icd->width_max - width) / 2; - - if (rect->top + height > icd->height_max) - rect->top = (icd->height_max - height) / 2; + 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); but this is cheaper */ + /* Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper */ switch (xbin) { case 2: - rect->left = (rect->left + 1) & ~1; + left = (left + 3) & ~3; break; case 3: - rect->left = roundup(rect->left, 3); + left = roundup(left, 6); } switch (ybin) { case 2: - rect->top = (rect->top + 1) & ~1; + top = (top + 3) & ~3; break; case 3: - rect->top = roundup(rect->top, 3); + top = roundup(top, 6); } + /* Disable register update, reconfigure atomically */ + ret = reg_set(icd, MT9T031_OUTPUT_CONTROL, 1); + if (ret < 0) + return ret; + /* Blanking and start values - default... */ ret = reg_write(icd, MT9T031_HORIZONTAL_BLANKING, hblank); if (ret >= 0) ret = reg_write(icd, MT9T031_VERTICAL_BLANKING, vblank); - if (pixfmt) { + if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) { /* Binning, skipping */ if (ret >= 0) ret = reg_write(icd, MT9T031_COLUMN_ADDRESS_MODE, @@ -270,14 +274,14 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd, ret = reg_write(icd, MT9T031_ROW_ADDRESS_MODE, ((ybin - 1) << 4) | (yskip - 1)); } - dev_dbg(&icd->dev, "new left %u, top %u\n", rect->left, rect->top); + dev_dbg(&icd->dev, "new physical left %u, top %u\n", left, 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(icd, MT9T031_COLUMN_START, rect->left); + ret = reg_write(icd, MT9T031_COLUMN_START, left); if (ret >= 0) - ret = reg_write(icd, MT9T031_ROW_START, rect->top); + ret = reg_write(icd, MT9T031_ROW_START, top); if (ret >= 0) ret = reg_write(icd, MT9T031_WINDOW_WIDTH, width - 1); if (ret >= 0) @@ -297,12 +301,58 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd, } } - if (!ret && pixfmt) { + /* Re-enable register update, commit all changes */ + if (ret >= 0) + ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 1); + + return ret < 0 ? ret : 0; +} + +static int mt9t031_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + + /* CROP - no change in scaling, or in limits */ + return mt9t031_set_params(icd, rect, mt9t031->xskip, mt9t031->yskip); +} + +static int mt9t031_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + 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, + }; + + /* + * 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; + + for (yskip = 8; yskip > 1; yskip--) + if (rect.height * yskip <= MT9T031_MAX_HEIGHT) + break; + + recalculate_limits(icd, xskip, yskip); + + ret = mt9t031_set_params(icd, &rect, xskip, yskip); + if (!ret) { mt9t031->xskip = xskip; mt9t031->yskip = yskip; } - return ret < 0 ? ret : 0; + return ret; } static int mt9t031_try_fmt(struct soc_camera_device *icd, @@ -310,14 +360,14 @@ static int mt9t031_try_fmt(struct soc_camera_device *icd, { struct v4l2_pix_format *pix = &f->fmt.pix; - if (pix->height < icd->height_min) - pix->height = icd->height_min; - if (pix->height > icd->height_max) - pix->height = icd->height_max; - if (pix->width < icd->width_min) - pix->width = icd->width_min; - if (pix->width > icd->width_max) - pix->width = icd->width_max; + if (pix->height < MT9T031_MIN_HEIGHT) + pix->height = MT9T031_MIN_HEIGHT; + if (pix->height > MT9T031_MAX_HEIGHT) + pix->height = MT9T031_MAX_HEIGHT; + if (pix->width < MT9T031_MIN_WIDTH) + pix->width = MT9T031_MIN_WIDTH; + if (pix->width > MT9T031_MAX_WIDTH) + pix->width = MT9T031_MAX_WIDTH; pix->width &= ~0x01; /* has to be even */ pix->height &= ~0x01; /* has to be even */ @@ -390,6 +440,14 @@ static const struct v4l2_queryctrl mt9t031_controls[] = { .step = 1, .default_value = 0, }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", @@ -431,6 +489,7 @@ static struct soc_camera_ops mt9t031_ops = { .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, @@ -513,21 +572,23 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro if (data < 0) return -EIO; } else { - /* Pack it into 1.125..15 variable step, register values 9..67 */ + /* Pack it into 1.125..128 variable step, register values 9..0x7860 */ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */ unsigned long range = qctrl->maximum - qctrl->default_value - 1; + /* calculated gain: map 65..127 to 9..1024 step 0.125 */ unsigned long gain = ((ctrl->value - qctrl->default_value - 1) * - 111 + range / 2) / range + 9; + 1015 + range / 2) / range + 9; - if (gain <= 32) + if (gain <= 32) /* calculated gain 9..32 -> 9..32 */ data = gain; - else if (gain <= 64) + else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */ data = ((gain - 32) * 16 + 16) / 32 + 80; else - data = ((gain - 64) * 7 + 28) / 56 + 96; + /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */ + data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; - dev_dbg(&icd->dev, "Setting gain from %d to %d\n", - reg_read(icd, MT9T031_GLOBAL_GAIN), data); + dev_dbg(&icd->dev, "Setting gain from 0x%x to 0x%x\n", + reg_read(icd, MT9T031_GLOBAL_GAIN), data); data = reg_write(icd, MT9T031_GLOBAL_GAIN, data); if (data < 0) return -EIO; diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index b04c8cb..4d3b481 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -13,7 +13,6 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/log2.h> -#include <linux/gpio.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> @@ -89,9 +88,7 @@ struct mt9v022 { struct i2c_client *client; struct soc_camera_device icd; int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */ - int switch_gpio; u16 chip_control; - unsigned char datawidth; }; static int reg_read(struct soc_camera_device *icd, const u8 reg) @@ -209,66 +206,6 @@ static int mt9v022_stop_capture(struct soc_camera_device *icd) return 0; } -static int bus_switch_request(struct mt9v022 *mt9v022, struct soc_camera_link *icl) -{ -#ifdef CONFIG_MT9V022_PCA9536_SWITCH - int ret; - unsigned int gpio = icl->gpio; - - if (gpio_is_valid(gpio)) { - /* We have a data bus switch. */ - ret = gpio_request(gpio, "mt9v022"); - if (ret < 0) { - dev_err(&mt9v022->client->dev, "Cannot get GPIO %u\n", gpio); - return ret; - } - - ret = gpio_direction_output(gpio, 0); - if (ret < 0) { - dev_err(&mt9v022->client->dev, - "Cannot set GPIO %u to output\n", gpio); - gpio_free(gpio); - return ret; - } - } - - mt9v022->switch_gpio = gpio; -#else - mt9v022->switch_gpio = -EINVAL; -#endif - return 0; -} - -static void bus_switch_release(struct mt9v022 *mt9v022) -{ -#ifdef CONFIG_MT9V022_PCA9536_SWITCH - if (gpio_is_valid(mt9v022->switch_gpio)) - gpio_free(mt9v022->switch_gpio); -#endif -} - -static int bus_switch_act(struct mt9v022 *mt9v022, int go8bit) -{ -#ifdef CONFIG_MT9V022_PCA9536_SWITCH - if (!gpio_is_valid(mt9v022->switch_gpio)) - return -ENODEV; - - gpio_set_value_cansleep(mt9v022->switch_gpio, go8bit); - return 0; -#else - return -ENODEV; -#endif -} - -static int bus_switch_possible(struct mt9v022 *mt9v022) -{ -#ifdef CONFIG_MT9V022_PCA9536_SWITCH - return gpio_is_valid(mt9v022->switch_gpio); -#else - return 0; -#endif -} - static int mt9v022_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { @@ -282,19 +219,17 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, if (!is_power_of_2(width_flag)) return -EINVAL; - if ((mt9v022->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) || - (mt9v022->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) || - (mt9v022->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) { - /* Well, we actually only can do 10 or 8 bits... */ - if (width_flag == SOCAM_DATAWIDTH_9) - return -EINVAL; - - ret = bus_switch_act(mt9v022, - width_flag == SOCAM_DATAWIDTH_8); - if (ret < 0) + if (icl->set_bus_param) { + ret = icl->set_bus_param(icl, width_flag); + if (ret) return ret; - - mt9v022->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; + } else { + /* + * Without board specific bus width settings we only support the + * sensors native bus width + */ + if (width_flag != SOCAM_DATAWIDTH_10) + return -EINVAL; } flags = soc_camera_apply_sensor_flags(icl, flags); @@ -328,44 +263,27 @@ 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); - unsigned int width_flag = SOCAM_DATAWIDTH_10; + struct soc_camera_link *icl = mt9v022->client->dev.platform_data; + unsigned int width_flag; - if (bus_switch_possible(mt9v022)) - width_flag |= SOCAM_DATAWIDTH_8; + if (icl->query_bus_param) + width_flag = icl->query_bus_param(icl) & + SOCAM_DATAWIDTH_MASK; + else + width_flag = SOCAM_DATAWIDTH_10; return SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | - SOCAM_MASTER | SOCAM_SLAVE | + SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER | SOCAM_SLAVE | width_flag; } -static int mt9v022_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9v022_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); int ret; - /* The caller provides a supported format, as verified per call to - * icd->try_fmt(), datawidth is from our supported format list */ - switch (pixfmt) { - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_Y16: - if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM) - return -EINVAL; - break; - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SBGGR16: - if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC) - return -EINVAL; - break; - case 0: - /* No format change, only geometry */ - break; - default: - return -EINVAL; - } - /* Like in example app. Contradicts the datasheet though */ ret = reg_read(icd, MT9V022_AEC_AGC_ENABLE); if (ret >= 0) { @@ -403,6 +321,42 @@ static int mt9v022_set_fmt(struct soc_camera_device *icd, return 0; } +static int mt9v022_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + 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, + }; + + /* The caller provides a supported format, as verified per call to + * icd->try_fmt(), datawidth is from our supported format list */ + switch (pix->pixelformat) { + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y16: + if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM) + return -EINVAL; + break; + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SBGGR16: + if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC) + return -EINVAL; + break; + case 0: + /* No format change, only geometry */ + break; + default: + return -EINVAL; + } + + /* No support for scaling on this camera, just crop. */ + return mt9v022_set_crop(icd, &rect); +} + static int mt9v022_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { @@ -544,6 +498,7 @@ static struct soc_camera_ops mt9v022_ops = { .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, @@ -699,6 +654,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) struct soc_camera_link *icl = mt9v022->client->dev.platform_data; s32 data; int ret; + unsigned long flags; if (!icd->dev.parent || to_soc_camera_host(icd->dev.parent)->nr != icd->iface) @@ -732,22 +688,36 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); mt9v022->model = V4L2_IDENT_MT9V022IX7ATC; icd->formats = mt9v022_colour_formats; - if (gpio_is_valid(icl->gpio)) - icd->num_formats = ARRAY_SIZE(mt9v022_colour_formats); - else - icd->num_formats = 1; } else { ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 0x11); mt9v022->model = V4L2_IDENT_MT9V022IX7ATM; icd->formats = mt9v022_monochrome_formats; - if (gpio_is_valid(icl->gpio)) - icd->num_formats = ARRAY_SIZE(mt9v022_monochrome_formats); - else - icd->num_formats = 1; } - if (!ret) - ret = soc_camera_video_start(icd); + if (ret < 0) + goto eisis; + + icd->num_formats = 0; + + /* + * This is a 10bit sensor, so by default we only allow 10bit. + * The platform may support different bus widths due to + * different routing of the data lines. + */ + if (icl->query_bus_param) + flags = icl->query_bus_param(icl); + else + flags = SOCAM_DATAWIDTH_10; + + if (flags & SOCAM_DATAWIDTH_10) + icd->num_formats++; + else + icd->formats++; + + if (flags & SOCAM_DATAWIDTH_8) + icd->num_formats++; + + ret = soc_camera_video_start(icd); if (ret < 0) goto eisis; @@ -812,14 +782,6 @@ static int mt9v022_probe(struct i2c_client *client, icd->height_max = 480; icd->y_skip_top = 1; icd->iface = icl->bus_id; - /* Default datawidth - this is the only width this camera (normally) - * supports. It is only with extra logic that it can support - * other widths. Therefore it seems to be a sensible default. */ - mt9v022->datawidth = 10; - - ret = bus_switch_request(mt9v022, icl); - if (ret) - goto eswinit; ret = soc_camera_device_register(icd); if (ret) @@ -828,8 +790,6 @@ static int mt9v022_probe(struct i2c_client *client, return 0; eisdr: - bus_switch_release(mt9v022); -eswinit: kfree(mt9v022); return ret; } @@ -839,7 +799,6 @@ static int mt9v022_remove(struct i2c_client *client) struct mt9v022 *mt9v022 = i2c_get_clientdata(client); soc_camera_device_unregister(&mt9v022->icd); - bus_switch_release(mt9v022); kfree(mt9v022); return 0; diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c new file mode 100644 index 0000000..70629e1 --- /dev/null +++ b/drivers/media/video/mx3_camera.c @@ -0,0 +1,1220 @@ +/* + * V4L2 Driver for i.MX3x camera host + * + * Copyright (C) 2008 + * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/videodev2.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/vmalloc.h> +#include <linux/interrupt.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/videobuf-dma-contig.h> +#include <media/soc_camera.h> + +#include <mach/ipu.h> +#include <mach/mx3_camera.h> + +#define MX3_CAM_DRV_NAME "mx3-camera" + +/* CMOS Sensor Interface Registers */ +#define CSI_REG_START 0x60 + +#define CSI_SENS_CONF (0x60 - CSI_REG_START) +#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START) +#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START) +#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START) +#define CSI_TST_CTRL (0x70 - CSI_REG_START) +#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START) +#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START) +#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START) +#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START) +#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START) + +#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0 +#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1 +#define CSI_SENS_CONF_DATA_POL_SHIFT 2 +#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3 +#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4 +#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7 +#define CSI_SENS_CONF_DATA_FMT_SHIFT 8 +#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10 +#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15 +#define CSI_SENS_CONF_DIVRATIO_SHIFT 16 + +#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT) +#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT) +#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT) + +#define MAX_VIDEO_MEM 16 + +struct mx3_camera_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct soc_camera_data_format *fmt; + + /* One descriptot per scatterlist (per frame) */ + struct dma_async_tx_descriptor *txd; + + /* We have to "build" a scatterlist ourselves - one element per frame */ + struct scatterlist sg; +}; + +/** + * struct mx3_camera_dev - i.MX3x camera (CSI) object + * @dev: camera device, to which the coherent buffer is attached + * @icd: currently attached camera sensor + * @clk: pointer to clock + * @base: remapped register base address + * @pdata: platform data + * @platform_flags: platform flags + * @mclk: master clock frequency in Hz + * @capture: list of capture videobuffers + * @lock: protects video buffer lists + * @active: active video buffer + * @idmac_channel: array of pointers to IPU DMAC DMA channels + * @soc_host: embedded soc_host object + */ +struct mx3_camera_dev { + struct device *dev; + /* + * i.MX3x is only supposed to handle one camera on its Camera Sensor + * Interface. If anyone ever builds hardware to enable more than one + * camera _simultaneously_, they will have to modify this driver too + */ + struct soc_camera_device *icd; + struct clk *clk; + + void __iomem *base; + + struct mx3_camera_pdata *pdata; + + unsigned long platform_flags; + unsigned long mclk; + + struct list_head capture; + spinlock_t lock; /* Protects video buffer lists */ + struct mx3_camera_buffer *active; + + /* IDMAC / dmaengine interface */ + struct idmac_channel *idmac_channel[1]; /* We need one channel */ + + struct soc_camera_host soc_host; +}; + +struct dma_chan_request { + struct mx3_camera_dev *mx3_cam; + enum ipu_channel id; +}; + +static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt); + +static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg) +{ + return __raw_readl(mx3->base + reg); +} + +static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg) +{ + __raw_writel(value, mx3->base + reg); +} + +/* Called from the IPU IDMAC ISR */ +static void mx3_cam_dma_done(void *arg) +{ + struct idmac_tx_desc *desc = to_tx_desc(arg); + struct dma_chan *chan = desc->txd.chan; + struct idmac_channel *ichannel = to_idmac_chan(chan); + struct mx3_camera_dev *mx3_cam = ichannel->client; + struct videobuf_buffer *vb; + + dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n", + desc->txd.cookie, mx3_cam->active ? sg_dma_address(&mx3_cam->active->sg) : 0); + + spin_lock(&mx3_cam->lock); + if (mx3_cam->active) { + vb = &mx3_cam->active->vb; + + list_del_init(&vb->queue); + vb->state = VIDEOBUF_DONE; + do_gettimeofday(&vb->ts); + vb->field_count++; + wake_up(&vb->done); + } + + if (list_empty(&mx3_cam->capture)) { + mx3_cam->active = NULL; + spin_unlock(&mx3_cam->lock); + + /* + * stop capture - without further buffers IPU_CHA_BUF0_RDY will + * not get updated + */ + return; + } + + mx3_cam->active = list_entry(mx3_cam->capture.next, + struct mx3_camera_buffer, vb.queue); + mx3_cam->active->vb.state = VIDEOBUF_ACTIVE; + spin_unlock(&mx3_cam->lock); +} + +static void free_buffer(struct videobuf_queue *vq, struct mx3_camera_buffer *buf) +{ + struct soc_camera_device *icd = vq->priv_data; + struct videobuf_buffer *vb = &buf->vb; + struct dma_async_tx_descriptor *txd = buf->txd; + struct idmac_channel *ichan; + + BUG_ON(in_interrupt()); + + dev_dbg(&icd->dev, "%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 + * longer in STATE_QUEUED or STATE_ACTIVE + */ + videobuf_waiton(vb, 0, 0); + if (txd) { + ichan = to_idmac_chan(txd->chan); + async_tx_ack(txd); + } + videobuf_dma_contig_free(vq, vb); + buf->txd = NULL; + + vb->state = VIDEOBUF_NEEDS_INIT; +} + +/* + * Videobuf operations + */ + +/* + * Calculate the __buffer__ (not data) size and number of buffers. + * Called with .vb_lock held + */ +static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + /* + * bits-per-pixel (depth) as specified in camera's pixel format does + * not necessarily match what the camera interface writes to RAM, but + * it should be good enough for now. + */ + unsigned int bpp = DIV_ROUND_UP(icd->current_fmt->depth, 8); + + if (!mx3_cam->idmac_channel[0]) + return -EINVAL; + + *size = icd->width * icd->height * bpp; + + if (!*count) + *count = 32; + + if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) + *count = MAX_VIDEO_MEM * 1024 * 1024 / *size; + + return 0; +} + +/* Called with .vb_lock held */ +static int mx3_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + 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 * + ((icd->current_fmt->depth + 7) >> 3); + int ret; + + /* + * I think, in buf_prepare you only have to protect global data, + * the actual buffer is yours + */ + + if (buf->fmt != icd->current_fmt || + vb->width != icd->width || + vb->height != icd->height || + vb->field != field) { + buf->fmt = icd->current_fmt; + vb->width = icd->width; + vb->height = icd->height; + vb->field = field; + if (vb->state != VIDEOBUF_NEEDS_INIT) + free_buffer(vq, buf); + } + + if (vb->baddr && vb->bsize < new_size) { + /* User provided buffer, but it is too small */ + ret = -ENOMEM; + goto out; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; + struct scatterlist *sg = &buf->sg; + + /* + * The total size of video-buffers that will be allocated / mapped. + * *size that we calculated in videobuf_setup gets assigned to + * vb->bsize, and now we use the same calculation to get vb->size. + */ + vb->size = new_size; + + /* This actually (allocates and) maps buffers */ + ret = videobuf_iolock(vq, vb, NULL); + if (ret) + goto fail; + + /* + * We will have to configure the IDMAC channel. It has two slots + * for DMA buffers, we shall enter the first two buffers there, + * and then submit new buffers in DMA-ready interrupts + */ + sg_init_table(sg, 1); + sg_dma_address(sg) = videobuf_to_dma_contig(vb); + sg_dma_len(sg) = vb->size; + + buf->txd = ichan->dma_chan.device->device_prep_slave_sg( + &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT); + if (!buf->txd) { + ret = -EIO; + goto fail; + } + + buf->txd->callback_param = buf->txd; + buf->txd->callback = mx3_cam_dma_done; + + vb->state = VIDEOBUF_PREPARED; + } + + return 0; + +fail: + free_buffer(vq, buf); +out: + return ret; +} + +static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) +{ + /* Add more formats as need arises and test possibilities appear... */ + switch (fourcc) { + case V4L2_PIX_FMT_RGB565: + return IPU_PIX_FMT_RGB565; + case V4L2_PIX_FMT_RGB24: + return IPU_PIX_FMT_RGB24; + case V4L2_PIX_FMT_RGB332: + return IPU_PIX_FMT_RGB332; + case V4L2_PIX_FMT_YUV422P: + return IPU_PIX_FMT_YVU422P; + default: + return IPU_PIX_FMT_GENERIC; + } +} + +/* Called with .vb_lock held */ +static void mx3_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + struct mx3_camera_buffer *buf = + container_of(vb, struct mx3_camera_buffer, vb); + struct dma_async_tx_descriptor *txd = buf->txd; + struct idmac_channel *ichan = to_idmac_chan(txd->chan); + struct idmac_video_param *video = &ichan->params.video; + const struct soc_camera_data_format *data_fmt = icd->current_fmt; + dma_cookie_t cookie; + unsigned long flags; + + /* 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; + +#ifdef DEBUG + /* helps to see what DMA actually has written */ + memset((void *)vb->baddr, 0xaa, vb->bsize); +#endif + + spin_lock_irqsave(&mx3_cam->lock, flags); + + list_add_tail(&vb->queue, &mx3_cam->capture); + + if (!mx3_cam->active) { + mx3_cam->active = buf; + vb->state = VIDEOBUF_ACTIVE; + } else { + vb->state = VIDEOBUF_QUEUED; + } + + spin_unlock_irqrestore(&mx3_cam->lock, flags); + + cookie = txd->tx_submit(txd); + dev_dbg(&icd->dev, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg)); + if (cookie >= 0) + return; + + /* Submit error */ + vb->state = VIDEOBUF_PREPARED; + + spin_lock_irqsave(&mx3_cam->lock, flags); + + list_del_init(&vb->queue); + + if (mx3_cam->active == buf) + mx3_cam->active = NULL; + + spin_unlock_irqrestore(&mx3_cam->lock, flags); +} + +/* Called with .vb_lock held */ +static void mx3_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 mx3_camera_dev *mx3_cam = ici->priv; + struct mx3_camera_buffer *buf = + 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", + mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg), + 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)) { + vb->state = VIDEOBUF_ERROR; + + list_del_init(&vb->queue); + if (mx3_cam->active == buf) + mx3_cam->active = NULL; + } + spin_unlock_irqrestore(&mx3_cam->lock, flags); + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops mx3_videobuf_ops = { + .buf_setup = mx3_videobuf_setup, + .buf_prepare = mx3_videobuf_prepare, + .buf_queue = mx3_videobuf_queue, + .buf_release = mx3_videobuf_release, +}; + +static void mx3_camera_init_videobuf(struct videobuf_queue *q, + 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; + + videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, mx3_cam->dev, + &mx3_cam->lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct mx3_camera_buffer), icd); +} + +/* First part of ipu_csi_init_interface() */ +static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, + struct soc_camera_device *icd) +{ + u32 conf; + long rate; + + /* Set default size: ipu_csi_set_window_size() */ + csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE); + /* ...and position to 0:0: ipu_csi_set_window_pos() */ + conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000; + csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL); + + /* We use only gated clock synchronisation mode so far */ + conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT; + + /* Set generic data, platform-biggest bus-width */ + conf |= CSI_SENS_CONF_DATA_FMT_BAYER; + + if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15) + conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10) + conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8) + conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/ + conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + + if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC) + conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT; + if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC) + conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT; + if (mx3_cam->platform_flags & MX3_CAMERA_DP) + conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; + if (mx3_cam->platform_flags & MX3_CAMERA_PCP) + conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; + if (mx3_cam->platform_flags & MX3_CAMERA_HSP) + conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; + if (mx3_cam->platform_flags & MX3_CAMERA_VSP) + conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; + + /* ipu_csi_init_interface() */ + csi_reg_write(mx3_cam, conf, CSI_SENS_CONF); + + 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); + if (rate) + clk_set_rate(mx3_cam->clk, rate); +} + +/* Called with .video_lock held */ +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; + } + + 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); + + return ret; +} + +/* Called with .video_lock held */ +static void mx3_camera_remove_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; + struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; + + BUG_ON(icd != mx3_cam->icd); + + if (*ichan) { + dma_release_channel(&(*ichan)->dma_chan); + *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", + icd->devnum); +} + +static bool channel_change_requested(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + 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; +} + +static int test_platform_param(struct mx3_camera_dev *mx3_cam, + unsigned char buswidth, unsigned long *flags) +{ + /* + * Platform specified synchronization and pixel clock polarities are + * only a recommendation and are only used during probing. MX3x + * camera interface only works in master mode, i.e., uses HSYNC and + * VSYNC signals from the sensor + */ + *flags = SOCAM_MASTER | + SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_HSYNC_ACTIVE_LOW | + SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_VSYNC_ACTIVE_LOW | + SOCAM_PCLK_SAMPLE_RISING | + SOCAM_PCLK_SAMPLE_FALLING | + SOCAM_DATA_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_LOW; + + /* If requested data width is supported by the platform, use it or any + * possible lower value - i.MX31 is smart enough to schift bits */ + switch (buswidth) { + case 15: + if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_10 | + SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4; + break; + case 10: + if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8 | + SOCAM_DATAWIDTH_4; + break; + case 8: + if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4; + break; + case 4: + if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_4; + break; + default: + dev_info(mx3_cam->dev, "Unsupported bus width %d\n", buswidth); + return -EINVAL; + } + + return 0; +} + +static int mx3_camera_try_bus_param(struct soc_camera_device *icd, + const unsigned int depth) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + 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); + + if (ret < 0) + return ret; + + camera_flags = icd->ops->query_bus_param(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", + camera_flags, bus_flags); + + return ret; +} + +static bool chan_filter(struct dma_chan *chan, void *arg) +{ + struct dma_chan_request *rq = arg; + struct mx3_camera_pdata *pdata; + + if (!rq) + return false; + + pdata = rq->mx3_cam->dev->platform_data; + + return rq->id == chan->chan_id && + pdata->dma_dev == chan->device->dev; +} + +static const struct soc_camera_data_format mx3_camera_formats[] = { + { + .name = "Bayer (sRGB) 8 bit", + .depth = 8, + .fourcc = V4L2_PIX_FMT_SBGGR8, + .colorspace = V4L2_COLORSPACE_SRGB, + }, { + .name = "Monochrome 8 bit", + .depth = 8, + .fourcc = V4L2_PIX_FMT_GREY, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; + +static bool buswidth_supported(struct soc_camera_host *ici, int depth) +{ + struct mx3_camera_dev *mx3_cam = ici->priv; + + switch (depth) { + case 4: + return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4); + case 8: + return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8); + case 10: + return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10); + case 15: + return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15); + } + return false; +} + +static int mx3_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); + int formats = 0, buswidth, ret; + + buswidth = icd->formats[idx].depth; + + if (!buswidth_supported(ici, buswidth)) + return 0; + + ret = mx3_camera_try_bus_param(icd, buswidth); + if (ret < 0) + return 0; + + switch (icd->formats[idx].fourcc) { + case V4L2_PIX_FMT_SGRBG10: + formats++; + if (xlate) { + xlate->host_fmt = &mx3_camera_formats[0]; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = buswidth; + xlate++; + dev_dbg(&ici->dev, "Providing format %s using %s\n", + mx3_camera_formats[0].name, + icd->formats[idx].name); + } + goto passthrough; + case V4L2_PIX_FMT_Y16: + formats++; + if (xlate) { + xlate->host_fmt = &mx3_camera_formats[1]; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = buswidth; + xlate++; + dev_dbg(&ici->dev, "Providing format %s using %s\n", + mx3_camera_formats[0].name, + icd->formats[idx].name); + } + default: +passthrough: + /* Generic pass-through */ + formats++; + if (xlate) { + xlate->host_fmt = icd->formats + idx; + xlate->cam_fmt = icd->formats + idx; + xlate->buswidth = buswidth; + xlate++; + dev_dbg(&ici->dev, + "Providing format %s in pass-through mode\n", + icd->formats[idx].name); + } + } + + return formats; +} + +static void configure_geometry(struct mx3_camera_dev *mx3_cam, + struct v4l2_rect *rect) +{ + 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; + 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); + csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2); + + csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE); + + /* ...and position */ + 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) +{ + dma_cap_mask_t mask; + struct dma_chan *chan; + struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; + /* We have to use IDMAC_IC_7 for Bayer / generic data */ + struct dma_chan_request rq = {.mx3_cam = mx3_cam, + .id = IDMAC_IC_7}; + + if (*ichan) { + struct videobuf_buffer *vb, *_vb; + dma_release_channel(&(*ichan)->dma_chan); + *ichan = NULL; + mx3_cam->active = NULL; + list_for_each_entry_safe(vb, _vb, &mx3_cam->capture, queue) { + list_del_init(&vb->queue); + vb->state = VIDEOBUF_ERROR; + wake_up(&vb->done); + } + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_PRIVATE, mask); + chan = dma_request_channel(mask, chan_filter, &rq); + if (!chan) + return -EBUSY; + + *ichan = to_idmac_chan(chan); + (*ichan)->client = mx3_cam; + + return 0; +} + +static int mx3_camera_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + + /* + * 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, rect); + + return icd->ops->set_crop(icd, rect); +} + +static int mx3_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 mx3_camera_dev *mx3_cam = ici->priv; + 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); + return -EINVAL; + } + + ret = acquire_dma_channel(mx3_cam); + if (ret < 0) + return ret; + + /* + * Might have to perform a complete interface initialisation like in + * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider + * mxc_v4l2_s_fmt() + */ + + configure_geometry(mx3_cam, &rect); + + ret = icd->ops->set_fmt(icd, f); + if (!ret) { + icd->buswidth = xlate->buswidth; + icd->current_fmt = xlate->host_fmt; + } + + 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); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + __u32 pixfmt = pix->pixelformat; + enum v4l2_field field; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (pixfmt && !xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + /* limit to MX3 hardware capabilities */ + if (pix->height > 4096) + pix->height = 4096; + if (pix->width > 4096) + pix->width = 4096; + + pix->bytesperline = pix->width * + DIV_ROUND_UP(xlate->host_fmt->depth, 8); + pix->sizeimage = pix->height * pix->bytesperline; + + /* 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; + + 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); + return -EINVAL; + } + + return ret; +} + +static int mx3_camera_reqbufs(struct soc_camera_file *icf, + struct v4l2_requestbuffers *p) +{ + return 0; +} + +static unsigned int mx3_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_file *icf = file->private_data; + + return videobuf_poll_stream(file, &icf->vb_vidq, pt); +} + +static int mx3_camera_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + /* cap->name is set by the firendly caller:-> */ + strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card)); + cap->version = KERNEL_VERSION(0, 2, 2); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + unsigned long bus_flags, camera_flags, common_flags; + u32 dw, sens_conf; + int ret = test_platform_param(mx3_cam, icd->buswidth, &bus_flags); + const struct soc_camera_format_xlate *xlate; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + dev_dbg(&ici->dev, "requested bus width %d bit: %d\n", + icd->buswidth, ret); + + 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) { + dev_dbg(&ici->dev, "no common flags: camera %lx, host %lx\n", + camera_flags, bus_flags); + return -EINVAL; + } + + /* Make choices, based on platform preferences */ + if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if (mx3_cam->platform_flags & MX3_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 (mx3_cam->platform_flags & MX3_CAMERA_VSP) + common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; + else + common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; + } + + if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) && + (common_flags & SOCAM_DATA_ACTIVE_LOW)) { + if (mx3_cam->platform_flags & MX3_CAMERA_DP) + common_flags &= ~SOCAM_DATA_ACTIVE_HIGH; + else + common_flags &= ~SOCAM_DATA_ACTIVE_LOW; + } + + if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && + (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if (mx3_cam->platform_flags & MX3_CAMERA_PCP) + common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + else + common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + } + + /* Make the camera work in widest common mode, we'll take care of + * the rest */ + if (common_flags & SOCAM_DATAWIDTH_15) + common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | + SOCAM_DATAWIDTH_15; + else if (common_flags & SOCAM_DATAWIDTH_10) + common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | + SOCAM_DATAWIDTH_10; + else if (common_flags & SOCAM_DATAWIDTH_8) + common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | + SOCAM_DATAWIDTH_8; + else + common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) | + SOCAM_DATAWIDTH_4; + + ret = icd->ops->set_bus_param(icd, common_flags); + if (ret < 0) + return ret; + + /* + * So far only gated clock mode is supported. Add a line + * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) | + * below and select the required mode when supporting other + * synchronisation protocols. + */ + sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) & + ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) | + (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) | + (1 << CSI_SENS_CONF_DATA_POL_SHIFT) | + (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) | + (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) | + (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT)); + + /* TODO: Support RGB and YUV formats */ + + /* This has been set in mx3_camera_activate(), but we clear it above */ + sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER; + + if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) + sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT; + if (common_flags & SOCAM_HSYNC_ACTIVE_LOW) + sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT; + if (common_flags & SOCAM_VSYNC_ACTIVE_LOW) + sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT; + if (common_flags & SOCAM_DATA_ACTIVE_LOW) + sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT; + + /* Just do what we're asked to do */ + switch (xlate->host_fmt->depth) { + case 4: + dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + break; + case 8: + dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + break; + case 10: + dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + break; + default: + /* + * Actually it can only be 15 now, default is just to silence + * compiler warnings + */ + case 15: + dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT; + } + + csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF); + + dev_dbg(&ici->dev, "Set SENS_CONF to %x\n", sens_conf | dw); + + return 0; +} + +static struct soc_camera_host_ops mx3_soc_camera_host_ops = { + .owner = THIS_MODULE, + .add = mx3_camera_add_device, + .remove = mx3_camera_remove_device, +#ifdef CONFIG_PM + .suspend = mx3_camera_suspend, + .resume = mx3_camera_resume, +#endif + .set_crop = mx3_camera_set_crop, + .set_fmt = mx3_camera_set_fmt, + .try_fmt = mx3_camera_try_fmt, + .get_formats = mx3_camera_get_formats, + .init_videobuf = mx3_camera_init_videobuf, + .reqbufs = mx3_camera_reqbufs, + .poll = mx3_camera_poll, + .querycap = mx3_camera_querycap, + .set_bus_param = mx3_camera_set_bus_param, +}; + +static int mx3_camera_probe(struct platform_device *pdev) +{ + struct mx3_camera_dev *mx3_cam; + struct resource *res; + void __iomem *base; + int err = 0; + struct soc_camera_host *soc_host; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENODEV; + goto egetres; + } + + mx3_cam = vmalloc(sizeof(*mx3_cam)); + if (!mx3_cam) { + dev_err(&pdev->dev, "Could not allocate mx3 camera object\n"); + err = -ENOMEM; + goto ealloc; + } + memset(mx3_cam, 0, sizeof(*mx3_cam)); + + mx3_cam->clk = clk_get(&pdev->dev, "csi_clk"); + if (IS_ERR(mx3_cam->clk)) { + err = PTR_ERR(mx3_cam->clk); + goto eclkget; + } + + dev_set_drvdata(&pdev->dev, mx3_cam); + + mx3_cam->pdata = pdev->dev.platform_data; + mx3_cam->platform_flags = mx3_cam->pdata->flags; + if (!(mx3_cam->platform_flags & (MX3_CAMERA_DATAWIDTH_4 | + MX3_CAMERA_DATAWIDTH_8 | MX3_CAMERA_DATAWIDTH_10 | + MX3_CAMERA_DATAWIDTH_15))) { + /* Platform hasn't set available data widths. This is bad. + * Warn and use a default. */ + dev_warn(&pdev->dev, "WARNING! Platform hasn't set available " + "data widths, using default 8 bit\n"); + mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8; + } + + mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000; + if (!mx3_cam->mclk) { + dev_warn(&pdev->dev, + "mclk_10khz == 0! Please, fix your platform data. " + "Using default 20MHz\n"); + mx3_cam->mclk = 20000000; + } + + /* list of video-buffers */ + INIT_LIST_HEAD(&mx3_cam->capture); + spin_lock_init(&mx3_cam->lock); + + base = ioremap(res->start, res->end - res->start + 1); + if (!base) { + err = -ENOMEM; + goto eioremap; + } + + mx3_cam->base = base; + mx3_cam->dev = &pdev->dev; + + soc_host = &mx3_cam->soc_host; + soc_host->drv_name = MX3_CAM_DRV_NAME; + soc_host->ops = &mx3_soc_camera_host_ops; + soc_host->priv = mx3_cam; + soc_host->dev.parent = &pdev->dev; + soc_host->nr = pdev->id; + err = soc_camera_host_register(soc_host); + if (err) + goto ecamhostreg; + + /* IDMAC interface */ + dmaengine_get(); + + return 0; + +ecamhostreg: + iounmap(base); +eioremap: + clk_put(mx3_cam->clk); +eclkget: + vfree(mx3_cam); +ealloc: +egetres: + return err; +} + +static int __devexit mx3_camera_remove(struct platform_device *pdev) +{ + struct mx3_camera_dev *mx3_cam = platform_get_drvdata(pdev); + + clk_put(mx3_cam->clk); + + soc_camera_host_unregister(&mx3_cam->soc_host); + + iounmap(mx3_cam->base); + + /* + * The channel has either not been allocated, + * or should have been released + */ + if (WARN_ON(mx3_cam->idmac_channel[0])) + dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan); + + vfree(mx3_cam); + + dmaengine_put(); + + dev_info(&pdev->dev, "i.MX3x Camera driver unloaded\n"); + + return 0; +} + +static struct platform_driver mx3_camera_driver = { + .driver = { + .name = MX3_CAM_DRV_NAME, + }, + .probe = mx3_camera_probe, + .remove = __exit_p(mx3_camera_remove), +}; + + +static int __devinit mx3_camera_init(void) +{ + return platform_driver_register(&mx3_camera_driver); +} + +static void __exit mx3_camera_exit(void) +{ + platform_driver_unregister(&mx3_camera_driver); +} + +module_init(mx3_camera_init); +module_exit(mx3_camera_exit); + +MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index e3cbe14..84aec62 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -25,16 +25,20 @@ #include <media/saa7146_vv.h> #include <media/tuner.h> -#include <linux/video_decoder.h> #include <media/v4l2-common.h> #include <media/saa7115.h> #include "mxb.h" #include "tea6415c.h" #include "tea6420.h" -#include "tda9840.h" -#define I2C_SAA7111 0x24 +#define I2C_SAA5246A 0x11 +#define I2C_SAA7111A 0x24 +#define I2C_TDA9840 0x42 +#define I2C_TEA6415C 0x43 +#define I2C_TEA6420_1 0x4c +#define I2C_TEA6420_2 0x4d +#define I2C_TUNER 0x60 #define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0) @@ -79,57 +83,35 @@ static struct { static int video_audio_connect[MXB_INPUTS] = { 0, 1, 3, 3 }; -/* these are the necessary input-output-pins for bringing one audio source -(see above) to the CD-output */ -static struct tea6420_multiplex TEA6420_cd[MXB_AUDIOS+1][2] = - { - {{1,1,0},{1,1,0}}, /* Tuner */ - {{5,1,0},{6,1,0}}, /* AUX 1 */ - {{4,1,0},{6,1,0}}, /* AUX 2 */ - {{3,1,0},{6,1,0}}, /* AUX 3 */ - {{1,1,0},{3,1,0}}, /* Radio */ - {{1,1,0},{2,1,0}}, /* CD-Rom */ - {{6,1,0},{6,1,0}} /* Mute */ - }; - -/* these are the necessary input-output-pins for bringing one audio source -(see above) to the line-output */ -static struct tea6420_multiplex TEA6420_line[MXB_AUDIOS+1][2] = - { - {{2,3,0},{1,2,0}}, - {{5,3,0},{6,2,0}}, - {{4,3,0},{6,2,0}}, - {{3,3,0},{6,2,0}}, - {{2,3,0},{3,2,0}}, - {{2,3,0},{2,2,0}}, - {{6,3,0},{6,2,0}} /* Mute */ - }; +/* These are the necessary input-output-pins for bringing one audio source + (see above) to the CD-output. Note that gain is set to 0 in this table. */ +static struct v4l2_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { + { { 1, 1 }, { 1, 1 } }, /* Tuner */ + { { 5, 1 }, { 6, 1 } }, /* AUX 1 */ + { { 4, 1 }, { 6, 1 } }, /* AUX 2 */ + { { 3, 1 }, { 6, 1 } }, /* AUX 3 */ + { { 1, 1 }, { 3, 1 } }, /* Radio */ + { { 1, 1 }, { 2, 1 } }, /* CD-Rom */ + { { 6, 1 }, { 6, 1 } } /* Mute */ +}; + +/* These are the necessary input-output-pins for bringing one audio source + (see above) to the line-output. Note that gain is set to 0 in this table. */ +static struct v4l2_routing TEA6420_line[MXB_AUDIOS + 1][2] = { + { { 2, 3 }, { 1, 2 } }, + { { 5, 3 }, { 6, 2 } }, + { { 4, 3 }, { 6, 2 } }, + { { 3, 3 }, { 6, 2 } }, + { { 2, 3 }, { 3, 2 } }, + { { 2, 3 }, { 2, 2 } }, + { { 6, 3 }, { 6, 2 } } /* Mute */ +}; #define MAXCONTROLS 1 static struct v4l2_queryctrl mxb_controls[] = { { V4L2_CID_AUDIO_MUTE, V4L2_CTRL_TYPE_BOOLEAN, "Mute", 0, 1, 1, 0, 0 }, }; -static struct saa7146_extension_ioctls ioctls[] = { - { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE }, - { VIDIOC_QUERYCTRL, SAA7146_BEFORE }, - { VIDIOC_G_CTRL, SAA7146_BEFORE }, - { VIDIOC_S_CTRL, SAA7146_BEFORE }, - { VIDIOC_G_TUNER, SAA7146_EXCLUSIVE }, - { VIDIOC_S_TUNER, SAA7146_EXCLUSIVE }, - { VIDIOC_G_FREQUENCY, SAA7146_EXCLUSIVE }, - { VIDIOC_S_FREQUENCY, SAA7146_EXCLUSIVE }, - { VIDIOC_G_AUDIO, SAA7146_EXCLUSIVE }, - { VIDIOC_S_AUDIO, SAA7146_EXCLUSIVE }, - { VIDIOC_DBG_G_REGISTER, SAA7146_EXCLUSIVE }, - { VIDIOC_DBG_S_REGISTER, SAA7146_EXCLUSIVE }, - { MXB_S_AUDIO_CD, SAA7146_EXCLUSIVE }, /* custom control */ - { MXB_S_AUDIO_LINE, SAA7146_EXCLUSIVE }, /* custom control */ - { 0, 0 } -}; - struct mxb { struct video_device *video_dev; @@ -137,12 +119,12 @@ struct mxb struct i2c_adapter i2c_adapter; - struct i2c_client *saa7111a; - struct i2c_client *tda9840; - struct i2c_client *tea6415c; - struct i2c_client *tuner; - struct i2c_client *tea6420_1; - struct i2c_client *tea6420_2; + struct v4l2_subdev *saa7111a; + struct v4l2_subdev *tda9840; + struct v4l2_subdev *tea6415c; + struct v4l2_subdev *tuner; + struct v4l2_subdev *tea6420_1; + struct v4l2_subdev *tea6420_2; int cur_mode; /* current audio mode (mono, stereo, ...) */ int cur_input; /* current input */ @@ -150,84 +132,51 @@ struct mxb struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */ }; -static struct saa7146_extension extension; - -static int mxb_check_clients(struct device *dev, void *data) -{ - struct mxb *mxb = data; - struct i2c_client *client = i2c_verify_client(dev); - - if (!client) - return 0; - - if (I2C_ADDR_TEA6420_1 == client->addr) - mxb->tea6420_1 = client; - if (I2C_ADDR_TEA6420_2 == client->addr) - mxb->tea6420_2 = client; - if (I2C_TEA6415C_2 == client->addr) - mxb->tea6415c = client; - if (I2C_ADDR_TDA9840 == client->addr) - mxb->tda9840 = client; - if (I2C_SAA7111 == client->addr) - mxb->saa7111a = client; - if (0x60 == client->addr) - mxb->tuner = client; +#define saa7111a_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->saa7111a, o, f, ##args) +#define tea6420_1_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->tea6420_1, o, f, ##args) +#define tea6420_2_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->tea6420_2, o, f, ##args) +#define tda9840_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->tda9840, o, f, ##args) +#define tea6415c_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->tea6415c, o, f, ##args) +#define tuner_call(mxb, o, f, args...) \ + v4l2_subdev_call(mxb->tuner, o, f, ##args) +#define call_all(dev, o, f, args...) \ + v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) - return 0; -} +static struct saa7146_extension extension; -static int mxb_probe(struct saa7146_dev* dev) +static int mxb_probe(struct saa7146_dev *dev) { - struct mxb* mxb = NULL; - int result; - - result = request_module("saa7115"); - if (result < 0) { - printk("mxb: saa7111 i2c module not available.\n"); - return -ENODEV; - } - result = request_module("tea6420"); - if (result < 0) { - printk("mxb: tea6420 i2c module not available.\n"); - return -ENODEV; - } - result = request_module("tea6415c"); - if (result < 0) { - printk("mxb: tea6415c i2c module not available.\n"); - return -ENODEV; - } - result = request_module("tda9840"); - if (result < 0) { - printk("mxb: tda9840 i2c module not available.\n"); - return -ENODEV; - } - result = request_module("tuner"); - if (result < 0) { - printk("mxb: tuner i2c module not available.\n"); - return -ENODEV; - } + struct mxb *mxb = NULL; mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); - if( NULL == mxb ) { + if (mxb == NULL) { DEB_D(("not enough kernel memory.\n")); return -ENOMEM; } - mxb->i2c_adapter = (struct i2c_adapter) { - .class = I2C_CLASS_TV_ANALOG, - }; - snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); - if(i2c_add_adapter(&mxb->i2c_adapter) < 0) { + if (i2c_add_adapter(&mxb->i2c_adapter) < 0) { DEB_S(("cannot register i2c-device. skipping.\n")); kfree(mxb); return -EFAULT; } - /* loop through all i2c-devices on the bus and look who is there */ - device_for_each_child(&mxb->i2c_adapter.dev, mxb, mxb_check_clients); + mxb->saa7111a = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "saa7115", "saa7111", I2C_SAA7111A); + mxb->tea6420_1 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6420", "tea6420", I2C_TEA6420_1); + mxb->tea6420_2 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6420", "tea6420", I2C_TEA6420_2); + mxb->tea6415c = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6415c", "tea6415c", I2C_TEA6415C); + mxb->tda9840 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tda9840", "tda9840", I2C_TDA9840); + mxb->tuner = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tuner", "tuner", I2C_TUNER); + if (v4l2_i2c_new_subdev(&mxb->i2c_adapter, "saa5246a", "saa5246a", I2C_SAA5246A)) { + printk(KERN_INFO "mxb: found teletext decoder\n"); + } /* check if all devices are present */ if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c || @@ -315,47 +264,45 @@ static int mxb_init_done(struct saa7146_dev* dev) struct v4l2_routing route; int i = 0, err = 0; - struct tea6415c_multiplex vm; /* select video mode in saa7111a */ - mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_S_STD, &std); + saa7111a_call(mxb, tuner, s_std, std); /* select tuner-output on saa7111a */ i = 0; route.input = SAA7115_COMPOSITE0; route.output = SAA7111_FMT_CCIR | SAA7111_VBI_BYPASS; - mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route); + saa7111a_call(mxb, video, s_routing, &route); /* select a tuner type */ tun_setup.mode_mask = T_ANALOG_TV; tun_setup.addr = ADDR_UNSET; tun_setup.type = TUNER_PHILIPS_PAL; - mxb->tuner->driver->command(mxb->tuner, TUNER_SET_TYPE_ADDR, &tun_setup); + tuner_call(mxb, tuner, s_type_addr, &tun_setup); /* tune in some frequency on tuner */ mxb->cur_freq.tuner = 0; mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV; mxb->cur_freq.frequency = freq; - mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_FREQUENCY, - &mxb->cur_freq); + tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); /* set a default video standard */ - mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); + tuner_call(mxb, tuner, s_std, std); /* mute audio on tea6420s */ - mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_line[6][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_line[6][1]); - mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_cd[6][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_cd[6][1]); + tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[6][0]); + tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[6][1]); + tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[6][0]); + tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[6][1]); - /* switch to tuner-channel on tea6415c*/ - vm.out = 17; - vm.in = 3; - mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm); + /* switch to tuner-channel on tea6415c */ + route.input = 3; + route.output = 17; + tea6415c_call(mxb, video, s_routing, &route); - /* select tuner-output on multicable on tea6415c*/ - vm.in = 3; - vm.out = 13; - mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm); + /* select tuner-output on multicable on tea6415c */ + route.input = 3; + route.output = 13; + tea6415c_call(mxb, video, s_routing, &route); /* the rest for mxb */ mxb->cur_input = 0; @@ -424,395 +371,414 @@ void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) } */ -static struct saa7146_ext_vv vv_data; - -/* this function only gets called when the probing was successful */ -static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc) { - struct mxb *mxb = (struct mxb *)dev->ext_priv; - - DEB_EE(("dev:%p\n", dev)); - - /* checking for i2c-devices can be omitted here, because we - already did this in "mxb_vl42_probe" */ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + int i; - saa7146_vv_init(dev, &vv_data); - if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { - ERR(("cannot register capture v4l2 device. skipping.\n")); - return -1; + for (i = MAXCONTROLS - 1; i >= 0; i--) { + if (mxb_controls[i].id == qc->id) { + *qc = mxb_controls[i]; + DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id)); + return 0; + } } + return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc); +} - /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ - if (MXB_BOARD_CAN_DO_VBI(dev)) { - if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { - ERR(("cannot register vbi v4l2 device. skipping.\n")); - } +static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + int i; + + for (i = MAXCONTROLS - 1; i >= 0; i--) { + if (mxb_controls[i].id == vc->id) + break; } - i2c_use_client(mxb->tea6420_1); - i2c_use_client(mxb->tea6420_2); - i2c_use_client(mxb->tea6415c); - i2c_use_client(mxb->tda9840); - i2c_use_client(mxb->saa7111a); - i2c_use_client(mxb->tuner); + if (i < 0) + return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc); - printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num); + if (vc->id == V4L2_CID_AUDIO_MUTE) { + vc->value = mxb->cur_mute; + DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); + return 0; + } - mxb_num++; - mxb_init_done(dev); + DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); return 0; } -static int mxb_detach(struct saa7146_dev *dev) +static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) { + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; + int i = 0; - DEB_EE(("dev:%p\n", dev)); - - i2c_release_client(mxb->tea6420_1); - i2c_release_client(mxb->tea6420_2); - i2c_release_client(mxb->tea6415c); - i2c_release_client(mxb->tda9840); - i2c_release_client(mxb->saa7111a); - i2c_release_client(mxb->tuner); - - saa7146_unregister_device(&mxb->video_dev,dev); - if (MXB_BOARD_CAN_DO_VBI(dev)) - saa7146_unregister_device(&mxb->vbi_dev, dev); - saa7146_vv_release(dev); - - mxb_num--; + for (i = MAXCONTROLS - 1; i >= 0; i--) { + if (mxb_controls[i].id == vc->id) + break; + } - i2c_del_adapter(&mxb->i2c_adapter); - kfree(mxb); + if (i < 0) + return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc); + + if (vc->id == V4L2_CID_AUDIO_MUTE) { + mxb->cur_mute = vc->value; + if (!vc->value) { + /* switch the audio-source */ + tea6420_1_call(mxb, audio, s_routing, + &TEA6420_line[video_audio_connect[mxb->cur_input]][0]); + tea6420_2_call(mxb, audio, s_routing, + &TEA6420_line[video_audio_connect[mxb->cur_input]][1]); + } else { + tea6420_1_call(mxb, audio, s_routing, + &TEA6420_line[6][0]); + tea6420_2_call(mxb, audio, s_routing, + &TEA6420_line[6][1]); + } + DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value)); + } + return 0; +} +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) +{ + DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index)); + if (i->index < 0 || i->index >= MXB_INPUTS) + return -EINVAL; + memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); return 0; } -static long mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) { - struct saa7146_dev *dev = fh->dev; + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; - struct saa7146_vv *vv = dev->vv_data; + *i = mxb->cur_input; - switch(cmd) { - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; + DEB_EE(("VIDIOC_G_INPUT %d.\n", *i)); + return 0; +} - DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index)); - if (i->index < 0 || i->index >= MXB_INPUTS) - return -EINVAL; - memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); - return 0; - } - /* the saa7146 provides some controls (brightness, contrast, saturation) - which gets registered *after* this function. because of this we have - to return with a value != 0 even if the function succeded.. */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == qc->id) { - *qc = mxb_controls[i]; - DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id)); - return 0; - } - } - return -EAGAIN; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *vc = arg; - int i; +static int vidioc_s_input(struct file *file, void *fh, unsigned int input) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + struct v4l2_routing route; + int i = 0; - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) - break; - } + DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); - if (i < 0) - return -EAGAIN; + if (input < 0 || input >= MXB_INPUTS) + return -EINVAL; - if (vc->id == V4L2_CID_AUDIO_MUTE) { - vc->value = mxb->cur_mute; - DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); - return 0; - } + mxb->cur_input = input; - DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value)); - return 0; - } + saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, + input_port_selection[input].hps_sync); - case VIDIOC_S_CTRL: - { - struct v4l2_control *vc = arg; - int i = 0; + /* prepare switching of tea6415c and saa7111a; + have a look at the 'background'-file for further informations */ + switch (input) { + case TUNER: + i = SAA7115_COMPOSITE0; + route.input = 3; + route.output = 17; - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) - break; + if (tea6415c_call(mxb, video, s_routing, &route)) { + printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n"); + return -EFAULT; } + /* connect tuner-output always to multicable */ + route.input = 3; + route.output = 13; + break; + case AUX3_YC: + /* nothing to be done here. aux3_yc is + directly connected to the saa711a */ + i = SAA7115_SVIDEO1; + break; + case AUX3: + /* nothing to be done here. aux3 is + directly connected to the saa711a */ + i = SAA7115_COMPOSITE1; + break; + case AUX1: + i = SAA7115_COMPOSITE0; + route.input = 1; + route.output = 17; + break; + } - if (i < 0) - return -EAGAIN; - - if (vc->id == V4L2_CID_AUDIO_MUTE) { - mxb->cur_mute = vc->value; - if (!vc->value) { - /* switch the audio-source */ - mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, - &TEA6420_line[video_audio_connect[mxb->cur_input]][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, - &TEA6420_line[video_audio_connect[mxb->cur_input]][1]); - } else { - mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, - &TEA6420_line[6][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, - &TEA6420_line[6][1]); - } - DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value)); + /* switch video in tea6415c only if necessary */ + switch (input) { + case TUNER: + case AUX1: + if (tea6415c_call(mxb, video, s_routing, &route)) { + printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n"); + return -EFAULT; } - return 0; + break; + default: + break; } - case VIDIOC_G_INPUT: - { - int *input = (int *)arg; - *input = mxb->cur_input; - DEB_EE(("VIDIOC_G_INPUT %d.\n", *input)); - return 0; + /* switch video in saa7111a */ + route.input = i; + route.output = 0; + if (saa7111a_call(mxb, video, s_routing, &route)) + printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a #1.\n"); + + /* switch the audio-source only if necessary */ + if (0 == mxb->cur_mute) { + tea6420_1_call(mxb, audio, s_routing, + &TEA6420_line[video_audio_connect[input]][0]); + tea6420_2_call(mxb, audio, s_routing, + &TEA6420_line[video_audio_connect[input]][1]); } - case VIDIOC_S_INPUT: - { - int input = *(int *)arg; - struct tea6415c_multiplex vm; - struct v4l2_routing route; - int i = 0; - DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); + return 0; +} - if (input < 0 || input >= MXB_INPUTS) - return -EINVAL; +static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; - mxb->cur_input = input; + if (t->index) { + DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index)); + return -EINVAL; + } - saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source, - input_port_selection[input].hps_sync); + DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index)); - /* prepare switching of tea6415c and saa7111a; - have a look at the 'background'-file for further informations */ - switch (input) { - case TUNER: - i = SAA7115_COMPOSITE0; - vm.in = 3; - vm.out = 17; + memset(t, 0, sizeof(*t)); + strlcpy(t->name, "TV Tuner", sizeof(t->name)); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + t->audmode = mxb->cur_mode; + return call_all(dev, tuner, g_tuner, t); +} - if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) { - printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n"); - return -EFAULT; - } - /* connect tuner-output always to multicable */ - vm.in = 3; - vm.out = 13; - break; - case AUX3_YC: - /* nothing to be done here. aux3_yc is - directly connected to the saa711a */ - i = SAA7115_SVIDEO1; - break; - case AUX3: - /* nothing to be done here. aux3 is - directly connected to the saa711a */ - i = SAA7115_COMPOSITE1; - break; - case AUX1: - i = SAA7115_COMPOSITE0; - vm.in = 1; - vm.out = 17; - break; - } +static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; - /* switch video in tea6415c only if necessary */ - switch (input) { - case TUNER: - case AUX1: - if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) { - printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n"); - return -EFAULT; - } - break; - default: - break; - } + if (t->index) { + DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n", t->index)); + return -EINVAL; + } - /* switch video in saa7111a */ - route.input = i; - route.output = 0; - if (mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route)) - printk("VIDIOC_S_INPUT: could not address saa7111a #1.\n"); - - /* switch the audio-source only if necessary */ - if( 0 == mxb->cur_mute ) { - mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, - &TEA6420_line[video_audio_connect[input]][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, - &TEA6420_line[video_audio_connect[input]][1]); - } + mxb->cur_mode = t->audmode; + return call_all(dev, tuner, s_tuner, t); +} - return 0; +static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + if (mxb->cur_input) { + DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n", + mxb->cur_input)); + return -EINVAL; } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; - if (t->index) { - DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index)); - return -EINVAL; - } + *f = mxb->cur_freq; - DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index)); + DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq.frequency)); + return 0; +} - memset(t, 0, sizeof(*t)); - i2c_clients_command(&mxb->i2c_adapter, cmd, arg); +static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + struct saa7146_vv *vv = dev->vv_data; - strlcpy(t->name, "TV Tuner", sizeof(t->name)); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | \ - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - t->audmode = mxb->cur_mode; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *t = arg; + if (f->tuner) + return -EINVAL; - if (t->index) { - DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n",t->index)); - return -EINVAL; - } + if (V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; - mxb->cur_mode = t->audmode; - i2c_clients_command(&mxb->i2c_adapter, cmd, arg); - return 0; + if (mxb->cur_input) { + DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input)); + return -EINVAL; } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; - if (mxb->cur_input) { - DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n", - mxb->cur_input)); - return -EINVAL; - } + mxb->cur_freq = *f; + DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency)); - *f = mxb->cur_freq; + /* tune in desired frequency */ + tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); - DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq.frequency)); - return 0; + /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ + spin_lock(&dev->slock); + vv->vbi_fieldcount = 0; + spin_unlock(&dev->slock); + + return 0; +} + +static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + if (a->index < 0 || a->index > MXB_INPUTS) { + DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index)); + return -EINVAL; } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - if (f->tuner) - return -EINVAL; + DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index)); + memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio)); + return 0; +} - if (V4L2_TUNER_ANALOG_TV != f->type) - return -EINVAL; +static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index)); + return 0; +} - if (mxb->cur_input) { - DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input)); - return -EINVAL; - } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - mxb->cur_freq = *f; - DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency)); + return call_all(dev, core, g_register, reg); +} - /* tune in desired frequency */ - mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_FREQUENCY, &mxb->cur_freq); +static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ - spin_lock(&dev->slock); - vv->vbi_fieldcount = 0; - spin_unlock(&dev->slock); + return call_all(dev, core, s_register, reg); +} +#endif - return 0; - } +static long vidioc_default(struct file *file, void *fh, int cmd, void *arg) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + switch (cmd) { case MXB_S_AUDIO_CD: { - int i = *(int*)arg; + int i = *(int *)arg; if (i < 0 || i >= MXB_AUDIOS) { - DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n",i)); + DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n", i)); return -EINVAL; } - DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n",i)); + DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n", i)); - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[i][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[i][1]); + tea6420_1_call(mxb, audio, s_routing, &TEA6420_cd[i][0]); + tea6420_2_call(mxb, audio, s_routing, &TEA6420_cd[i][1]); return 0; } case MXB_S_AUDIO_LINE: { - int i = *(int*)arg; + int i = *(int *)arg; if (i < 0 || i >= MXB_AUDIOS) { - DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n",i)); + DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n", i)); return -EINVAL; } - DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n",i)); - mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[i][0]); - mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[i][1]); + DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n", i)); + tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[i][0]); + tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[i][1]); return 0; } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *a = arg; + default: +/* + DEB2(printk("does not handle this ioctl.\n")); +*/ + return -ENOIOCTLCMD; + } + return 0; +} - if (a->index < 0 || a->index > MXB_INPUTS) { - DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index)); - return -EINVAL; - } +static struct saa7146_ext_vv vv_data; - DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index)); - memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio)); +/* this function only gets called when the probing was successful */ +static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) +{ + struct mxb *mxb = (struct mxb *)dev->ext_priv; - return 0; - } - case VIDIOC_S_AUDIO: - { - struct v4l2_audio *a = arg; + DEB_EE(("dev:%p\n", dev)); - DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index)); - return 0; - } + /* checking for i2c-devices can be omitted here, because we + already did this in "mxb_vl42_probe" */ + + saa7146_vv_init(dev, &vv_data); + vv_data.ops.vidioc_queryctrl = vidioc_queryctrl; + vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl; + vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl; + vv_data.ops.vidioc_enum_input = vidioc_enum_input; + vv_data.ops.vidioc_g_input = vidioc_g_input; + vv_data.ops.vidioc_s_input = vidioc_s_input; + vv_data.ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data.ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data.ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data.ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data.ops.vidioc_g_audio = vidioc_g_audio; + vv_data.ops.vidioc_s_audio = vidioc_s_audio; #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_S_REGISTER: - case VIDIOC_DBG_G_REGISTER: - i2c_clients_command(&mxb->i2c_adapter, cmd, arg); - return 0; + vv_data.ops.vidioc_g_register = vidioc_g_register; + vv_data.ops.vidioc_s_register = vidioc_s_register; #endif - default: -/* - DEB2(printk("does not handle this ioctl.\n")); -*/ - return -ENOIOCTLCMD; + vv_data.ops.vidioc_default = vidioc_default; + if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { + ERR(("cannot register capture v4l2 device. skipping.\n")); + return -1; } + + /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/ + if (MXB_BOARD_CAN_DO_VBI(dev)) { + if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) { + ERR(("cannot register vbi v4l2 device. skipping.\n")); + } + } + + printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num); + + mxb_num++; + mxb_init_done(dev); + return 0; +} + +static int mxb_detach(struct saa7146_dev *dev) +{ + struct mxb *mxb = (struct mxb *)dev->ext_priv; + + DEB_EE(("dev:%p\n", dev)); + + saa7146_unregister_device(&mxb->video_dev,dev); + if (MXB_BOARD_CAN_DO_VBI(dev)) + saa7146_unregister_device(&mxb->vbi_dev, dev); + saa7146_vv_release(dev); + + mxb_num--; + + i2c_del_adapter(&mxb->i2c_adapter); + kfree(mxb); + return 0; } static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard) { struct mxb *mxb = (struct mxb *)dev->ext_priv; - int zero = 0; - int one = 1; if (V4L2_STD_PAL_I == standard->id) { v4l2_std_id std = V4L2_STD_PAL_I; @@ -821,8 +787,8 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa /* set the 7146 gpio register -- I don't know what this does exactly */ saa7146_write(dev, GPIO_CTRL, 0x00404050); /* unset the 7111 gpio register -- I don't know what this does exactly */ - mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &zero); - mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); + saa7111a_call(mxb, core, s_gpio, 0); + tuner_call(mxb, tuner, s_std, std); } else { v4l2_std_id std = V4L2_STD_PAL_BG; @@ -830,8 +796,8 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa /* set the 7146 gpio register -- I don't know what this does exactly */ saa7146_write(dev, GPIO_CTRL, 0x00404050); /* set the 7111 gpio register -- I don't know what this does exactly */ - mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &one); - mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std); + saa7111a_call(mxb, core, s_gpio, 1); + tuner_call(mxb, tuner, s_std, std); } return 0; } @@ -885,8 +851,6 @@ static struct saa7146_ext_vv vv_data = { .stds = &standard[0], .num_stds = sizeof(standard)/sizeof(struct saa7146_standard), .std_callback = &std_callback, - .ioctls = &ioctls[0], - .ioctl = mxb_ioctl, }; static struct saa7146_extension extension = { diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index 73eb656..5fc4ac0 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -80,17 +80,17 @@ static int omap24xxcam_clock_get(struct omap24xxcam_device *cam) { int rval = 0; - cam->fck = clk_get(cam->dev, "cam_fck"); + cam->fck = clk_get(cam->dev, "fck"); if (IS_ERR(cam->fck)) { - dev_err(cam->dev, "can't get cam_fck"); + dev_err(cam->dev, "can't get camera fck"); rval = PTR_ERR(cam->fck); omap24xxcam_clock_put(cam); return rval; } - cam->ick = clk_get(cam->dev, "cam_ick"); + cam->ick = clk_get(cam->dev, "ick"); if (IS_ERR(cam->ick)) { - dev_err(cam->dev, "can't get cam_ick"); + dev_err(cam->dev, "can't get camera ick"); rval = PTR_ERR(cam->ick); omap24xxcam_clock_put(cam); } @@ -1285,9 +1285,6 @@ static int vidioc_g_parm(struct file *file, void *fh, struct omap24xxcam_device *cam = ofh->cam; int rval; - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - mutex_lock(&cam->mutex); rval = vidioc_int_g_parm(cam->sdev, a); mutex_unlock(&cam->mutex); @@ -1303,9 +1300,6 @@ static int vidioc_s_parm(struct file *file, void *fh, struct v4l2_streamparm old_streamparm; int rval; - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - mutex_lock(&cam->mutex); if (cam->streaming) { rval = -EBUSY; @@ -1665,7 +1659,6 @@ static int omap24xxcam_device_register(struct v4l2_int_device *s) vfd->parent = cam->dev; strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name)); - vfd->vfl_type = VID_TYPE_CAPTURE | VID_TYPE_CHROMAKEY; vfd->fops = &omap24xxcam_fops; vfd->minor = -1; vfd->ioctl_ops = &omap24xxcam_ioctl_fops; diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 05c14a2..0e2184e 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -12,18 +12,22 @@ */ #include <linux/init.h> #include <linux/module.h> -#include <linux/slab.h> +#include <linux/i2c.h> #include <linux/delay.h> -#include <linux/videodev.h> -#include <media/v4l2-common.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> -#include <linux/i2c.h> +#include <media/v4l2-i2c-drv.h> MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); MODULE_LICENSE("GPL"); +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + /* * Basic window sizes. These probably belong somewhere more globally * useful. @@ -189,11 +193,16 @@ MODULE_LICENSE("GPL"); */ struct ov7670_format_struct; /* coming later */ struct ov7670_info { + struct v4l2_subdev sd; struct ov7670_format_struct *fmt; /* Current format */ unsigned char sat; /* Saturation value */ int hue; /* Hue value */ }; +static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov7670_info, sd); +} @@ -400,24 +409,27 @@ static struct regval_list ov7670_fmt_raw[] = { * Low-level register I/O. */ -static int ov7670_read(struct i2c_client *c, unsigned char reg, +static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, unsigned char *value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; - ret = i2c_smbus_read_byte_data(c, reg); + ret = i2c_smbus_read_byte_data(client, reg); if (ret >= 0) { - *value = (unsigned char) ret; + *value = (unsigned char)ret; ret = 0; } return ret; } -static int ov7670_write(struct i2c_client *c, unsigned char reg, +static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, unsigned char value) { - int ret = i2c_smbus_write_byte_data(c, reg, value); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = i2c_smbus_write_byte_data(client, reg, value); + if (reg == REG_COM7 && (value & COM7_RESET)) msleep(2); /* Wait for reset to run */ return ret; @@ -427,10 +439,10 @@ static int ov7670_write(struct i2c_client *c, unsigned char reg, /* * Write a list of register settings; ff/ff stops the process. */ -static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals) +static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals) { while (vals->reg_num != 0xff || vals->value != 0xff) { - int ret = ov7670_write(c, vals->reg_num, vals->value); + int ret = ov7670_write(sd, vals->reg_num, vals->value); if (ret < 0) return ret; vals++; @@ -442,34 +454,35 @@ static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals) /* * Stuff that knows about the sensor. */ -static void ov7670_reset(struct i2c_client *client) +static int ov7670_reset(struct v4l2_subdev *sd, u32 val) { - ov7670_write(client, REG_COM7, COM7_RESET); + ov7670_write(sd, REG_COM7, COM7_RESET); msleep(1); + return 0; } -static int ov7670_init(struct i2c_client *client) +static int ov7670_init(struct v4l2_subdev *sd, u32 val) { - return ov7670_write_array(client, ov7670_default_regs); + return ov7670_write_array(sd, ov7670_default_regs); } -static int ov7670_detect(struct i2c_client *client) +static int ov7670_detect(struct v4l2_subdev *sd) { unsigned char v; int ret; - ret = ov7670_init(client); + ret = ov7670_init(sd, 0); if (ret < 0) return ret; - ret = ov7670_read(client, REG_MIDH, &v); + ret = ov7670_read(sd, REG_MIDH, &v); if (ret < 0) return ret; if (v != 0x7f) /* OV manuf. id. */ return -ENODEV; - ret = ov7670_read(client, REG_MIDL, &v); + ret = ov7670_read(sd, REG_MIDL, &v); if (ret < 0) return ret; if (v != 0xa2) @@ -477,12 +490,12 @@ static int ov7670_detect(struct i2c_client *client) /* * OK, we know we have an OmniVision chip...but which one? */ - ret = ov7670_read(client, REG_PID, &v); + ret = ov7670_read(sd, REG_PID, &v); if (ret < 0) return ret; if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ return -ENODEV; - ret = ov7670_read(client, REG_VER, &v); + ret = ov7670_read(sd, REG_VER, &v); if (ret < 0) return ret; if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ @@ -627,7 +640,7 @@ static struct ov7670_win_size { /* * Store a set of start/stop values into the camera. */ -static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop, +static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, int vstart, int vstop) { int ret; @@ -637,26 +650,26 @@ static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop, * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is * a mystery "edge offset" value in the top two bits of href. */ - ret = ov7670_write(client, REG_HSTART, (hstart >> 3) & 0xff); - ret += ov7670_write(client, REG_HSTOP, (hstop >> 3) & 0xff); - ret += ov7670_read(client, REG_HREF, &v); + ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); + ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); + ret += ov7670_read(sd, REG_HREF, &v); v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); msleep(10); - ret += ov7670_write(client, REG_HREF, v); + ret += ov7670_write(sd, REG_HREF, v); /* * Vertical: similar arrangement, but only 10 bits. */ - ret += ov7670_write(client, REG_VSTART, (vstart >> 2) & 0xff); - ret += ov7670_write(client, REG_VSTOP, (vstop >> 2) & 0xff); - ret += ov7670_read(client, REG_VREF, &v); + ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); + ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); + ret += ov7670_read(sd, REG_VREF, &v); v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); msleep(10); - ret += ov7670_write(client, REG_VREF, v); + ret += ov7670_write(sd, REG_VREF, v); return ret; } -static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt) +static int ov7670_enum_fmt(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt) { struct ov7670_format_struct *ofmt; @@ -671,7 +684,8 @@ static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt) } -static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, +static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, + struct v4l2_format *fmt, struct ov7670_format_struct **ret_fmt, struct ov7670_win_size **ret_wsize) { @@ -715,18 +729,23 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, return 0; } +static int ov7670_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); +} + /* * Set a format. */ -static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) +static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { int ret; struct ov7670_format_struct *ovfmt; struct ov7670_win_size *wsize; - struct ov7670_info *info = i2c_get_clientdata(c); - unsigned char com7, clkrc; + struct ov7670_info *info = to_state(sd); + unsigned char com7, clkrc = 0; - ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize); + ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); if (ret) return ret; /* @@ -735,7 +754,7 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) * the colors. */ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) { - ret = ov7670_read(c, REG_CLKRC, &clkrc); + ret = ov7670_read(sd, REG_CLKRC, &clkrc); if (ret) return ret; } @@ -747,20 +766,20 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) */ com7 = ovfmt->regs[0].value; com7 |= wsize->com7_bit; - ov7670_write(c, REG_COM7, com7); + ov7670_write(sd, REG_COM7, com7); /* * Now write the rest of the array. Also store start/stops */ - ov7670_write_array(c, ovfmt->regs + 1); - ov7670_set_hw(c, wsize->hstart, wsize->hstop, wsize->vstart, + ov7670_write_array(sd, ovfmt->regs + 1); + ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, wsize->vstop); ret = 0; if (wsize->regs) - ret = ov7670_write_array(c, wsize->regs); + ret = ov7670_write_array(sd, wsize->regs); info->fmt = ovfmt; if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565 && ret == 0) - ret = ov7670_write(c, REG_CLKRC, clkrc); + ret = ov7670_write(sd, REG_CLKRC, clkrc); return ret; } @@ -768,7 +787,7 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) * Implement G/S_PARM. There is a "high quality" mode we could try * to do someday; for now, we just do the frame rate tweak. */ -static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) { struct v4l2_captureparm *cp = &parms->parm.capture; unsigned char clkrc; @@ -776,7 +795,7 @@ static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - ret = ov7670_read(c, REG_CLKRC, &clkrc); + ret = ov7670_read(sd, REG_CLKRC, &clkrc); if (ret < 0) return ret; memset(cp, 0, sizeof(struct v4l2_captureparm)); @@ -788,7 +807,7 @@ static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) return 0; } -static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) { struct v4l2_captureparm *cp = &parms->parm.capture; struct v4l2_fract *tpf = &cp->timeperframe; @@ -802,7 +821,7 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) /* * CLKRC has a reserved bit, so let's preserve it. */ - ret = ov7670_read(c, REG_CLKRC, &clkrc); + ret = ov7670_read(sd, REG_CLKRC, &clkrc); if (ret < 0) return ret; if (tpf->numerator == 0 || tpf->denominator == 0) @@ -816,7 +835,7 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) clkrc = (clkrc & 0x80) | div; tpf->numerator = 1; tpf->denominator = OV7670_FRAME_RATE/div; - return ov7670_write(c, REG_CLKRC, clkrc); + return ov7670_write(sd, REG_CLKRC, clkrc); } @@ -829,7 +848,7 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) -static int ov7670_store_cmatrix(struct i2c_client *client, +static int ov7670_store_cmatrix(struct v4l2_subdev *sd, int matrix[CMATRIX_LEN]) { int i, ret; @@ -839,7 +858,7 @@ static int ov7670_store_cmatrix(struct i2c_client *client, * Weird crap seems to exist in the upper part of * the sign bits register, so let's preserve it. */ - ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits); + ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits); signbits &= 0xc0; for (i = 0; i < CMATRIX_LEN; i++) { @@ -858,9 +877,9 @@ static int ov7670_store_cmatrix(struct i2c_client *client, else raw = matrix[i] & 0xff; } - ret += ov7670_write(client, REG_CMATRIX_BASE + i, raw); + ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw); } - ret += ov7670_write(client, REG_CMATRIX_SIGN, signbits); + ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits); return ret; } @@ -943,29 +962,29 @@ static void ov7670_calc_cmatrix(struct ov7670_info *info, -static int ov7670_t_sat(struct i2c_client *client, int value) +static int ov7670_s_sat(struct v4l2_subdev *sd, int value) { - struct ov7670_info *info = i2c_get_clientdata(client); + struct ov7670_info *info = to_state(sd); int matrix[CMATRIX_LEN]; int ret; info->sat = value; ov7670_calc_cmatrix(info, matrix); - ret = ov7670_store_cmatrix(client, matrix); + ret = ov7670_store_cmatrix(sd, matrix); return ret; } -static int ov7670_q_sat(struct i2c_client *client, __s32 *value) +static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value) { - struct ov7670_info *info = i2c_get_clientdata(client); + struct ov7670_info *info = to_state(sd); *value = info->sat; return 0; } -static int ov7670_t_hue(struct i2c_client *client, int value) +static int ov7670_s_hue(struct v4l2_subdev *sd, int value) { - struct ov7670_info *info = i2c_get_clientdata(client); + struct ov7670_info *info = to_state(sd); int matrix[CMATRIX_LEN]; int ret; @@ -973,14 +992,14 @@ static int ov7670_t_hue(struct i2c_client *client, int value) return -EINVAL; info->hue = value; ov7670_calc_cmatrix(info, matrix); - ret = ov7670_store_cmatrix(client, matrix); + ret = ov7670_store_cmatrix(sd, matrix); return ret; } -static int ov7670_q_hue(struct i2c_client *client, __s32 *value) +static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value) { - struct ov7670_info *info = i2c_get_clientdata(client); + struct ov7670_info *info = to_state(sd); *value = info->hue; return 0; @@ -994,8 +1013,7 @@ static unsigned char ov7670_sm_to_abs(unsigned char v) { if ((v & 0x80) == 0) return v + 128; - else - return 128 - (v & 0x7f); + return 128 - (v & 0x7f); } @@ -1003,369 +1021,275 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) { if (v > 127) return v & 0x7f; - else - return (128 - v) | 0x80; + return (128 - v) | 0x80; } -static int ov7670_t_brightness(struct i2c_client *client, int value) +static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) { unsigned char com8 = 0, v; int ret; - ov7670_read(client, REG_COM8, &com8); + ov7670_read(sd, REG_COM8, &com8); com8 &= ~COM8_AEC; - ov7670_write(client, REG_COM8, com8); + ov7670_write(sd, REG_COM8, com8); v = ov7670_abs_to_sm(value); - ret = ov7670_write(client, REG_BRIGHT, v); + ret = ov7670_write(sd, REG_BRIGHT, v); return ret; } -static int ov7670_q_brightness(struct i2c_client *client, __s32 *value) +static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value) { unsigned char v = 0; - int ret = ov7670_read(client, REG_BRIGHT, &v); + int ret = ov7670_read(sd, REG_BRIGHT, &v); *value = ov7670_sm_to_abs(v); return ret; } -static int ov7670_t_contrast(struct i2c_client *client, int value) +static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) { - return ov7670_write(client, REG_CONTRAS, (unsigned char) value); + return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); } -static int ov7670_q_contrast(struct i2c_client *client, __s32 *value) +static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value) { unsigned char v = 0; - int ret = ov7670_read(client, REG_CONTRAS, &v); + int ret = ov7670_read(sd, REG_CONTRAS, &v); *value = v; return ret; } -static int ov7670_q_hflip(struct i2c_client *client, __s32 *value) +static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value) { int ret; unsigned char v = 0; - ret = ov7670_read(client, REG_MVFP, &v); + ret = ov7670_read(sd, REG_MVFP, &v); *value = (v & MVFP_MIRROR) == MVFP_MIRROR; return ret; } -static int ov7670_t_hflip(struct i2c_client *client, int value) +static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) { unsigned char v = 0; int ret; - ret = ov7670_read(client, REG_MVFP, &v); + ret = ov7670_read(sd, REG_MVFP, &v); if (value) v |= MVFP_MIRROR; else v &= ~MVFP_MIRROR; msleep(10); /* FIXME */ - ret += ov7670_write(client, REG_MVFP, v); + ret += ov7670_write(sd, REG_MVFP, v); return ret; } -static int ov7670_q_vflip(struct i2c_client *client, __s32 *value) +static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value) { int ret; unsigned char v = 0; - ret = ov7670_read(client, REG_MVFP, &v); + ret = ov7670_read(sd, REG_MVFP, &v); *value = (v & MVFP_FLIP) == MVFP_FLIP; return ret; } -static int ov7670_t_vflip(struct i2c_client *client, int value) +static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) { unsigned char v = 0; int ret; - ret = ov7670_read(client, REG_MVFP, &v); + ret = ov7670_read(sd, REG_MVFP, &v); if (value) v |= MVFP_FLIP; else v &= ~MVFP_FLIP; msleep(10); /* FIXME */ - ret += ov7670_write(client, REG_MVFP, v); + ret += ov7670_write(sd, REG_MVFP, v); return ret; } - -static struct ov7670_control { - struct v4l2_queryctrl qc; - int (*query)(struct i2c_client *c, __s32 *value); - int (*tweak)(struct i2c_client *c, int value); -} ov7670_controls[] = +static int ov7670_queryctrl(struct v4l2_subdev *sd, + struct v4l2_queryctrl *qc) { - { - .qc = { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 0x80, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .tweak = ov7670_t_brightness, - .query = ov7670_q_brightness, - }, - { - .qc = { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 0x40, /* XXX ov7670 spec */ - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .tweak = ov7670_t_contrast, - .query = ov7670_q_contrast, - }, - { - .qc = { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 256, - .step = 1, - .default_value = 0x80, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .tweak = ov7670_t_sat, - .query = ov7670_q_sat, - }, - { - .qc = { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "HUE", - .minimum = -180, - .maximum = 180, - .step = 5, - .default_value = 0, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .tweak = ov7670_t_hue, - .query = ov7670_q_hue, - }, - { - .qc = { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .tweak = ov7670_t_vflip, - .query = ov7670_q_vflip, - }, - { - .qc = { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Horizontal mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .tweak = ov7670_t_hflip, - .query = ov7670_q_hflip, - }, -}; -#define N_CONTROLS (ARRAY_SIZE(ov7670_controls)) + /* Fill in min, max, step and default value for these controls. */ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); + } + return -EINVAL; +} -static struct ov7670_control *ov7670_find_control(__u32 id) +static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - int i; - - for (i = 0; i < N_CONTROLS; i++) - if (ov7670_controls[i].qc.id == id) - return ov7670_controls + i; - return NULL; + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov7670_g_brightness(sd, &ctrl->value); + case V4L2_CID_CONTRAST: + return ov7670_g_contrast(sd, &ctrl->value); + case V4L2_CID_SATURATION: + return ov7670_g_sat(sd, &ctrl->value); + case V4L2_CID_HUE: + return ov7670_g_hue(sd, &ctrl->value); + case V4L2_CID_VFLIP: + return ov7670_g_vflip(sd, &ctrl->value); + case V4L2_CID_HFLIP: + return ov7670_g_hflip(sd, &ctrl->value); + } + return -EINVAL; } +static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov7670_s_brightness(sd, ctrl->value); + case V4L2_CID_CONTRAST: + return ov7670_s_contrast(sd, ctrl->value); + case V4L2_CID_SATURATION: + return ov7670_s_sat(sd, ctrl->value); + case V4L2_CID_HUE: + return ov7670_s_hue(sd, ctrl->value); + case V4L2_CID_VFLIP: + return ov7670_s_vflip(sd, ctrl->value); + case V4L2_CID_HFLIP: + return ov7670_s_hflip(sd, ctrl->value); + } + return -EINVAL; +} -static int ov7670_queryctrl(struct i2c_client *client, - struct v4l2_queryctrl *qc) +static int ov7670_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) { - struct ov7670_control *ctrl = ov7670_find_control(qc->id); + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (ctrl == NULL) - return -EINVAL; - *qc = ctrl->qc; - return 0; + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); } -static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { - struct ov7670_control *octrl = ov7670_find_control(ctrl->id); + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned char val = 0; int ret; - if (octrl == NULL) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; - ret = octrl->query(client, &ctrl->value); - if (ret >= 0) - return 0; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ret = ov7670_read(sd, reg->reg & 0xff, &val); + reg->val = val; + reg->size = 1; return ret; } -static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { - struct ov7670_control *octrl = ov7670_find_control(ctrl->id); - int ret; + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (octrl == NULL) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; - ret = octrl->tweak(client, ctrl->value); - if (ret >= 0) - return 0; - return ret; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; } +#endif + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops ov7670_core_ops = { + .g_chip_ident = ov7670_g_chip_ident, + .g_ctrl = ov7670_g_ctrl, + .s_ctrl = ov7670_s_ctrl, + .queryctrl = ov7670_queryctrl, + .reset = ov7670_reset, + .init = ov7670_init, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov7670_g_register, + .s_register = ov7670_s_register, +#endif +}; +static const struct v4l2_subdev_video_ops ov7670_video_ops = { + .enum_fmt = ov7670_enum_fmt, + .try_fmt = ov7670_try_fmt, + .s_fmt = ov7670_s_fmt, + .s_parm = ov7670_s_parm, + .g_parm = ov7670_g_parm, +}; +static const struct v4l2_subdev_ops ov7670_ops = { + .core = &ov7670_core_ops, + .video = &ov7670_video_ops, +}; +/* ----------------------------------------------------------------------- */ - - -/* - * Basic i2c stuff. - */ -static struct i2c_driver ov7670_driver; - -static int ov7670_attach(struct i2c_adapter *adapter) +static int ov7670_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - int ret; - struct i2c_client *client; + struct v4l2_subdev *sd; struct ov7670_info *info; + int ret; - /* - * For now: only deal with adapters we recognize. - */ - if (adapter->id != I2C_HW_SMBUS_CAFE) - return -ENODEV; - - client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL); - if (! client) + info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); + if (info == NULL) return -ENOMEM; - client->adapter = adapter; - client->addr = OV7670_I2C_ADDR; - client->driver = &ov7670_driver, - strcpy(client->name, "OV7670"); - /* - * Set up our info structure. - */ - info = kzalloc(sizeof (struct ov7670_info), GFP_KERNEL); - if (! info) { - ret = -ENOMEM; - goto out_free; + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &ov7670_ops); + + /* Make sure it's an ov7670 */ + ret = ov7670_detect(sd); + if (ret) { + v4l_dbg(1, debug, client, + "chip found @ 0x%x (%s) is not an ov7670 chip.\n", + client->addr << 1, client->adapter->name); + kfree(info); + return ret; } + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + info->fmt = &ov7670_formats[0]; info->sat = 128; /* Review this */ - i2c_set_clientdata(client, info); - /* - * Make sure it's an ov7670 - */ - ret = ov7670_detect(client); - if (ret) - goto out_free_info; - ret = i2c_attach_client(client); - if (ret) - goto out_free_info; return 0; - - out_free_info: - kfree(info); - out_free: - kfree(client); - return ret; } -static int ov7670_detach(struct i2c_client *client) +static int ov7670_remove(struct i2c_client *client) { - i2c_detach_client(client); - kfree(i2c_get_clientdata(client)); - kfree(client); - return 0; -} - + struct v4l2_subdev *sd = i2c_get_clientdata(client); -static int ov7670_command(struct i2c_client *client, unsigned int cmd, - void *arg) -{ - switch (cmd) { - case VIDIOC_DBG_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7670, 0); - - case VIDIOC_INT_RESET: - ov7670_reset(client); - return 0; - - case VIDIOC_INT_INIT: - return ov7670_init(client); - - case VIDIOC_ENUM_FMT: - return ov7670_enum_fmt(client, (struct v4l2_fmtdesc *) arg); - case VIDIOC_TRY_FMT: - return ov7670_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL); - case VIDIOC_S_FMT: - return ov7670_s_fmt(client, (struct v4l2_format *) arg); - case VIDIOC_QUERYCTRL: - return ov7670_queryctrl(client, (struct v4l2_queryctrl *) arg); - case VIDIOC_S_CTRL: - return ov7670_s_ctrl(client, (struct v4l2_control *) arg); - case VIDIOC_G_CTRL: - return ov7670_g_ctrl(client, (struct v4l2_control *) arg); - case VIDIOC_S_PARM: - return ov7670_s_parm(client, (struct v4l2_streamparm *) arg); - case VIDIOC_G_PARM: - return ov7670_g_parm(client, (struct v4l2_streamparm *) arg); - } - return -EINVAL; + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; } - - -static struct i2c_driver ov7670_driver = { - .driver = { - .name = "ov7670", - }, - .id = I2C_DRIVERID_OV7670, - .attach_adapter = ov7670_attach, - .detach_client = ov7670_detach, - .command = ov7670_command, +static const struct i2c_device_id ov7670_id[] = { + { "ov7670", 0 }, + { } }; +MODULE_DEVICE_TABLE(i2c, ov7670_id); - -/* - * Module initialization - */ -static int __init ov7670_mod_init(void) -{ - printk(KERN_NOTICE "OmniVision ov7670 sensor driver, at your service\n"); - return i2c_add_driver(&ov7670_driver); -} - -static void __exit ov7670_mod_exit(void) -{ - i2c_del_driver(&ov7670_driver); -} - -module_init(ov7670_mod_init); -module_exit(ov7670_mod_exit); +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "ov7670", + .probe = ov7670_probe, + .remove = ov7670_remove, + .id_table = ov7670_id, +}; diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 3c9e0ba..84b0fc1 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -217,10 +217,11 @@ #define OCAP_4x 0x03 /* 4x */ /* COM3 */ -#define SWAP_MASK 0x38 +#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML) +#define IMG_MASK (VFLIP_IMG | HFLIP_IMG) -#define VFIMG_ON_OFF 0x80 /* Vertical flip image ON/OFF selection */ -#define HMIMG_ON_OFF 0x40 /* Horizontal mirror image ON/OFF selection */ +#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */ +#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */ #define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */ #define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */ #define SWAP_ML 0x08 /* Swap output MSB/LSB */ @@ -271,11 +272,13 @@ #define SLCT_QVGA 0x40 /* 1 : QVGA */ #define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */ /* RGB output format control */ +#define FMT_MASK 0x0c /* Mask of color format */ #define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */ #define FMT_RGB565 0x04 /* 01 : RGB 565 */ #define FMT_RGB555 0x08 /* 10 : RGB 555 */ #define FMT_RGB444 0x0c /* 11 : RGB 444 */ /* Output format control */ +#define OFMT_MASK 0x03 /* Mask of output format */ #define OFMT_YUV 0x00 /* 00 : YUV */ #define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */ #define OFMT_RGB 0x02 /* 10 : RGB */ @@ -299,7 +302,7 @@ #define GAIN_2x 0x00 /* 000 : 2x */ #define GAIN_4x 0x10 /* 001 : 4x */ #define GAIN_8x 0x20 /* 010 : 8x */ -#define GAIN_16x 0x30 /* 011 : 16x */ +#define GAIN_16x 0x30 /* 011 : 16x */ #define GAIN_32x 0x40 /* 100 : 32x */ #define GAIN_64x 0x50 /* 101 : 64x */ #define GAIN_128x 0x60 /* 110 : 128x */ @@ -356,13 +359,6 @@ #define VOSZ_QVGA 0x78 /* - * bit configure (32 bit) - * this is used in struct ov772x_color_format :: option - */ -#define OP_UV 0x00000001 -#define OP_SWAP_RGB 0x00000002 - -/* * ID */ #define OV7720 0x7720 @@ -380,8 +376,9 @@ struct regval_list { struct ov772x_color_format { char *name; __u32 fourcc; - const struct regval_list *regs; - unsigned int option; + u8 dsp3; + u8 com3; + u8 com7; }; struct ov772x_win_size { @@ -399,39 +396,13 @@ struct ov772x_priv { const struct ov772x_color_format *fmt; const struct ov772x_win_size *win; int model; + unsigned int flag_vflip:1; + unsigned int flag_hflip:1; }; #define ENDMARKER { 0xff, 0xff } /* - * register setting for color format - */ -static const struct regval_list ov772x_RGB555_regs[] = { - { COM3, 0x00 }, - { COM7, FMT_RGB555 | OFMT_RGB }, - ENDMARKER, -}; - -static const struct regval_list ov772x_RGB565_regs[] = { - { COM3, 0x00 }, - { COM7, FMT_RGB565 | OFMT_RGB }, - ENDMARKER, -}; - -static const struct regval_list ov772x_YYUV_regs[] = { - { COM3, SWAP_YUV }, - { COM7, OFMT_YUV }, - ENDMARKER, -}; - -static const struct regval_list ov772x_UVYY_regs[] = { - { COM3, 0x00 }, - { COM7, OFMT_YUV }, - ENDMARKER, -}; - - -/* * register setting for window size */ static const struct regval_list ov772x_qvga_regs[] = { @@ -500,38 +471,48 @@ static const struct soc_camera_data_format ov772x_fmt_lists[] = { /* * color format list */ -#define T_YUYV 0 static const struct ov772x_color_format ov772x_cfmts[] = { - [T_YUYV] = { + { SETFOURCC(YUYV), - .regs = ov772x_YYUV_regs, + .dsp3 = 0x0, + .com3 = SWAP_YUV, + .com7 = OFMT_YUV, }, { SETFOURCC(YVYU), - .regs = ov772x_YYUV_regs, - .option = OP_UV, + .dsp3 = UV_ON, + .com3 = SWAP_YUV, + .com7 = OFMT_YUV, }, { SETFOURCC(UYVY), - .regs = ov772x_UVYY_regs, + .dsp3 = 0x0, + .com3 = 0x0, + .com7 = OFMT_YUV, }, { SETFOURCC(RGB555), - .regs = ov772x_RGB555_regs, - .option = OP_SWAP_RGB, + .dsp3 = 0x0, + .com3 = SWAP_RGB, + .com7 = FMT_RGB555 | OFMT_RGB, }, { SETFOURCC(RGB555X), - .regs = ov772x_RGB555_regs, + .dsp3 = 0x0, + .com3 = 0x0, + .com7 = FMT_RGB555 | OFMT_RGB, }, { SETFOURCC(RGB565), - .regs = ov772x_RGB565_regs, - .option = OP_SWAP_RGB, + .dsp3 = 0x0, + .com3 = SWAP_RGB, + .com7 = FMT_RGB565 | OFMT_RGB, }, { SETFOURCC(RGB565X), - .regs = ov772x_RGB565_regs, + .dsp3 = 0x0, + .com3 = 0x0, + .com7 = FMT_RGB565 | OFMT_RGB, }, }; @@ -562,6 +543,27 @@ static const struct ov772x_win_size ov772x_win_qvga = { .regs = ov772x_qvga_regs, }; +static const struct v4l2_queryctrl ov772x_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, +}; + /* * general function @@ -587,8 +589,11 @@ static int ov772x_mask_set(struct i2c_client *client, u8 set) { s32 val = i2c_smbus_read_byte_data(client, command); + if (val < 0) + return val; + val &= ~mask; - val |= set; + val |= set & mask; return i2c_smbus_write_byte_data(client, command, val); } @@ -635,74 +640,24 @@ static int ov772x_release(struct soc_camera_device *icd) static int ov772x_start_capture(struct soc_camera_device *icd) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - int ret; - - if (!priv->win) - priv->win = &ov772x_win_vga; - if (!priv->fmt) - priv->fmt = &ov772x_cfmts[T_YUYV]; - - /* - * reset hardware - */ - ov772x_reset(priv->client); - /* - * set color format - */ - ret = ov772x_write_array(priv->client, priv->fmt->regs); - if (ret < 0) - goto start_end; - - /* - * set size format - */ - ret = ov772x_write_array(priv->client, priv->win->regs); - if (ret < 0) - goto start_end; - - /* - * set COM7 bit ( QVGA or VGA ) - */ - ret = ov772x_mask_set(priv->client, - COM7, SLCT_MASK, priv->win->com7_bit); - if (ret < 0) - goto start_end; - - /* - * set UV setting - */ - if (priv->fmt->option & OP_UV) { - ret = ov772x_mask_set(priv->client, - DSP_CTRL3, UV_MASK, UV_ON); - if (ret < 0) - goto start_end; + if (!priv->win || !priv->fmt) { + dev_err(&icd->dev, "norm or win select error\n"); + return -EPERM; } - /* - * set SWAP setting - */ - if (priv->fmt->option & OP_SWAP_RGB) { - ret = ov772x_mask_set(priv->client, - COM3, SWAP_MASK, SWAP_RGB); - if (ret < 0) - goto start_end; - } + ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, 0); dev_dbg(&icd->dev, "format %s, win %s\n", priv->fmt->name, priv->win->name); -start_end: - priv->fmt = NULL; - priv->win = NULL; - - return ret; + return 0; } static int ov772x_stop_capture(struct soc_camera_device *icd) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - ov772x_reset(priv->client); + ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); return 0; } @@ -718,11 +673,54 @@ static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) struct soc_camera_link *icl = priv->client->dev.platform_data; unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | - priv->info->buswidth; + SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth; return soc_camera_apply_sensor_flags(icl, flags); } +static int ov772x_get_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + ctrl->value = priv->flag_vflip; + break; + case V4L2_CID_HFLIP: + ctrl->value = priv->flag_hflip; + break; + } + return 0; +} + +static int ov772x_set_control(struct soc_camera_device *icd, + struct v4l2_control *ctrl) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + int ret = 0; + u8 val; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + val = ctrl->value ? VFLIP_IMG : 0x00; + 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); + 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); + break; + } + + return ret; +} + static int ov772x_get_chip_id(struct soc_camera_device *icd, struct v4l2_dbg_chip_ident *id) { @@ -787,13 +785,11 @@ ov772x_select_win(u32 width, u32 height) return win; } - -static int ov772x_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, - struct v4l2_rect *rect) +static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, + u32 pixfmt) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); int ret = -EINVAL; + u8 val; int i; /* @@ -803,19 +799,101 @@ static int ov772x_set_fmt(struct soc_camera_device *icd, for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) { if (pixfmt == ov772x_cfmts[i].fourcc) { priv->fmt = ov772x_cfmts + i; - ret = 0; break; } } + if (!priv->fmt) + goto ov772x_set_fmt_error; /* * select win */ - priv->win = ov772x_select_win(rect->width, rect->height); + priv->win = ov772x_select_win(width, height); + + /* + * reset hardware + */ + ov772x_reset(priv->client); + + /* + * set size format + */ + ret = ov772x_write_array(priv->client, priv->win->regs); + if (ret < 0) + goto ov772x_set_fmt_error; + + /* + * set DSP_CTRL3 + */ + val = priv->fmt->dsp3; + if (val) { + ret = ov772x_mask_set(priv->client, + DSP_CTRL3, UV_MASK, val); + if (ret < 0) + goto ov772x_set_fmt_error; + } + + /* + * set COM3 + */ + val = priv->fmt->com3; + if (priv->info->flags & OV772X_FLAG_VFLIP) + val |= VFLIP_IMG; + if (priv->info->flags & OV772X_FLAG_HFLIP) + val |= HFLIP_IMG; + if (priv->flag_vflip) + val ^= VFLIP_IMG; + if (priv->flag_hflip) + val ^= HFLIP_IMG; + + ret = ov772x_mask_set(priv->client, + COM3, SWAP_MASK | IMG_MASK, val); + if (ret < 0) + goto ov772x_set_fmt_error; + + /* + * set COM7 + */ + val = priv->win->com7_bit | priv->fmt->com7; + ret = ov772x_mask_set(priv->client, + COM7, (SLCT_MASK | FMT_MASK | OFMT_MASK), + val); + if (ret < 0) + goto ov772x_set_fmt_error; + + return ret; + +ov772x_set_fmt_error: + + ov772x_reset(priv->client); + priv->win = NULL; + priv->fmt = NULL; return ret; } +static int ov772x_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + + if (!priv->fmt) + return -EINVAL; + + return ov772x_set_params(priv, rect->width, rect->height, + priv->fmt->fourcc); +} + +static int ov772x_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct v4l2_pix_format *pix = &f->fmt.pix; + + return ov772x_set_params(priv, pix->width, pix->height, + pix->pixelformat); +} + static int ov772x_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { @@ -889,7 +967,6 @@ static int ov772x_video_probe(struct soc_camera_device *icd) i2c_smbus_read_byte_data(priv->client, MIDH), i2c_smbus_read_byte_data(priv->client, MIDL)); - return soc_camera_video_start(icd); } @@ -906,10 +983,15 @@ static struct soc_camera_ops ov772x_ops = { .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, #ifdef CONFIG_VIDEO_ADV_DEBUG .get_register = ov772x_get_register, diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c index c841f4e..d573d84 100644 --- a/drivers/media/video/ovcamchip/ovcamchip_core.c +++ b/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -15,6 +15,9 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/i2c.h> +#include <media/v4l2-device.h> +#include <media/v4l2-i2c-drv.h> #include "ovcamchip_priv.h" #define DRIVER_VERSION "v2.27 for Linux 2.6" @@ -44,6 +47,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); + /* Registers common to all chips, that are needed for detection */ #define GENERIC_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ #define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */ @@ -61,10 +65,6 @@ static char *chip_names[NUM_CC_TYPES] = { [CC_OV6630AF] = "OV6630AF", }; -/* Forward declarations */ -static struct i2c_driver driver; -static struct i2c_client client_template; - /* ----------------------------------------------------------------------- */ int ov_write_regvals(struct i2c_client *c, struct ovcamchip_regvals *rvals) @@ -253,112 +253,36 @@ static int ovcamchip_detect(struct i2c_client *c) /* Test for 7xx0 */ PDEBUG(3, "Testing for 0V7xx0"); - c->addr = OV7xx0_SID; - if (init_camchip(c) < 0) { - /* Test for 6xx0 */ - PDEBUG(3, "Testing for 0V6xx0"); - c->addr = OV6xx0_SID; - if (init_camchip(c) < 0) { - return -ENODEV; - } else { - if (ov6xx0_detect(c) < 0) { - PERROR("Failed to init OV6xx0"); - return -EIO; - } - } - } else { + if (init_camchip(c) < 0) + return -ENODEV; + /* 7-bit addresses with bit 0 set are for the OV7xx0 */ + if (c->addr & 1) { if (ov7xx0_detect(c) < 0) { PERROR("Failed to init OV7xx0"); return -EIO; } + return 0; + } + /* Test for 6xx0 */ + PDEBUG(3, "Testing for 0V6xx0"); + if (ov6xx0_detect(c) < 0) { + PERROR("Failed to init OV6xx0"); + return -EIO; } - return 0; } /* ----------------------------------------------------------------------- */ -static int ovcamchip_attach(struct i2c_adapter *adap) +static long ovcamchip_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { - int rc = 0; - struct ovcamchip *ov; - struct i2c_client *c; - - /* I2C is not a PnP bus, so we can never be certain that we're talking - * to the right chip. To prevent damage to EEPROMS and such, only - * attach to adapters that are known to contain OV camera chips. */ - - switch (adap->id) { - case I2C_HW_SMBUS_OV511: - case I2C_HW_SMBUS_OV518: - case I2C_HW_SMBUS_W9968CF: - PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id); - break; - default: - PDEBUG(1, "Adapter ID 0x%06x rejected", adap->id); - return -ENODEV; - } - - c = kmalloc(sizeof *c, GFP_KERNEL); - if (!c) { - rc = -ENOMEM; - goto no_client; - } - memcpy(c, &client_template, sizeof *c); - c->adapter = adap; - strcpy(c->name, "OV????"); - - ov = kzalloc(sizeof *ov, GFP_KERNEL); - if (!ov) { - rc = -ENOMEM; - goto no_ov; - } - i2c_set_clientdata(c, ov); - - rc = ovcamchip_detect(c); - if (rc < 0) - goto error; - - strcpy(c->name, chip_names[ov->subtype]); - - PDEBUG(1, "Camera chip detection complete"); - - i2c_attach_client(c); - - return rc; -error: - kfree(ov); -no_ov: - kfree(c); -no_client: - PDEBUG(1, "returning %d", rc); - return rc; -} - -static int ovcamchip_detach(struct i2c_client *c) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); - int rc; - - rc = ov->sops->free(c); - if (rc < 0) - return rc; - - i2c_detach_client(c); - - kfree(ov); - kfree(c); - return 0; -} - -static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg) -{ - struct ovcamchip *ov = i2c_get_clientdata(c); + struct ovcamchip *ov = to_ovcamchip(sd); + struct i2c_client *c = v4l2_get_subdevdata(sd); if (!ov->initialized && cmd != OVCAMCHIP_CMD_Q_SUBTYPE && cmd != OVCAMCHIP_CMD_INITIALIZE) { - dev_err(&c->dev, "ERROR: Camera chip not initialized yet!\n"); + v4l2_err(sd, "Camera chip not initialized yet!\n"); return -EPERM; } @@ -379,10 +303,10 @@ static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg) if (ov->mono) { if (ov->subtype != CC_OV7620) - dev_warn(&c->dev, "Warning: Monochrome not " + v4l2_warn(sd, "Monochrome not " "implemented for this chip\n"); else - dev_info(&c->dev, "Initializing chip as " + v4l2_info(sd, "Initializing chip as " "monochrome\n"); } @@ -400,35 +324,72 @@ static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg) /* ----------------------------------------------------------------------- */ -static struct i2c_driver driver = { - .driver = { - .name = "ovcamchip", - }, - .id = I2C_DRIVERID_OVCAMCHIP, - .attach_adapter = ovcamchip_attach, - .detach_client = ovcamchip_detach, - .command = ovcamchip_command, +static const struct v4l2_subdev_core_ops ovcamchip_core_ops = { + .ioctl = ovcamchip_ioctl, }; -static struct i2c_client client_template = { - .name = "(unset)", - .driver = &driver, +static const struct v4l2_subdev_ops ovcamchip_ops = { + .core = &ovcamchip_core_ops, }; -static int __init ovcamchip_init(void) +static int ovcamchip_probe(struct i2c_client *client, + const struct i2c_device_id *id) { -#ifdef DEBUG - ovcamchip_debug = debug; -#endif + struct ovcamchip *ov; + struct v4l2_subdev *sd; + int rc = 0; + + ov = kzalloc(sizeof *ov, GFP_KERNEL); + if (!ov) { + rc = -ENOMEM; + goto no_ov; + } + sd = &ov->sd; + v4l2_i2c_subdev_init(sd, client, &ovcamchip_ops); + + rc = ovcamchip_detect(client); + if (rc < 0) + goto error; + + v4l_info(client, "%s found @ 0x%02x (%s)\n", + chip_names[ov->subtype], client->addr << 1, client->adapter->name); + + PDEBUG(1, "Camera chip detection complete"); - PINFO(DRIVER_VERSION " : " DRIVER_DESC); - return i2c_add_driver(&driver); + return rc; +error: + kfree(ov); +no_ov: + PDEBUG(1, "returning %d", rc); + return rc; } -static void __exit ovcamchip_exit(void) +static int ovcamchip_remove(struct i2c_client *client) { - i2c_del_driver(&driver); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ovcamchip *ov = to_ovcamchip(sd); + int rc; + + v4l2_device_unregister_subdev(sd); + rc = ov->sops->free(client); + if (rc < 0) + return rc; + + kfree(ov); + return 0; } -module_init(ovcamchip_init); -module_exit(ovcamchip_exit); +/* ----------------------------------------------------------------------- */ + +static const struct i2c_device_id ovcamchip_id[] = { + { "ovcamchip", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ovcamchip_id); + +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "ovcamchip", + .probe = ovcamchip_probe, + .remove = ovcamchip_remove, + .id_table = ovcamchip_id, +}; diff --git a/drivers/media/video/ovcamchip/ovcamchip_priv.h b/drivers/media/video/ovcamchip/ovcamchip_priv.h index a05650f..4f07b78 100644 --- a/drivers/media/video/ovcamchip/ovcamchip_priv.h +++ b/drivers/media/video/ovcamchip/ovcamchip_priv.h @@ -16,6 +16,7 @@ #define __LINUX_OVCAMCHIP_PRIV_H #include <linux/i2c.h> +#include <media/v4l2-subdev.h> #include <media/ovcamchip.h> #ifdef DEBUG @@ -46,6 +47,7 @@ struct ovcamchip_ops { }; struct ovcamchip { + struct v4l2_subdev sd; struct ovcamchip_ops *sops; void *spriv; /* Private data for OV7x10.c etc... */ int subtype; /* = SEN_OV7610 etc... */ @@ -53,6 +55,11 @@ struct ovcamchip { int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */ }; +static inline struct ovcamchip *to_ovcamchip(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ovcamchip, sd); +} + extern struct ovcamchip_ops ov6x20_ops; extern struct ovcamchip_ops ov6x30_ops; extern struct ovcamchip_ops ov7x10_ops; diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig index 854c2a8..f9b6001 100644 --- a/drivers/media/video/pvrusb2/Kconfig +++ b/drivers/media/video/pvrusb2/Kconfig @@ -40,10 +40,10 @@ config VIDEO_PVRUSB2_DVB select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_S5H1409 if !DVB_FE_CUSTOMISE select DVB_S5H1411 if !DVB_FE_CUSTOMISE - select DVB_TDA10048 if !DVB_FE_CUSTOMIZE - select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE + select DVB_TDA10048 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE ---help--- This option enables a DVB interface for the pvrusb2 driver. diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile index 4fda2de..de2fc14 100644 --- a/drivers/media/video/pvrusb2/Makefile +++ b/drivers/media/video/pvrusb2/Makefile @@ -2,14 +2,15 @@ obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o -pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ - pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \ +pvrusb2-objs := pvrusb2-i2c-core.o \ + pvrusb2-audio.o \ pvrusb2-encoder.o pvrusb2-video-v4l.o \ - pvrusb2-eeprom.o pvrusb2-tuner.o \ + pvrusb2-eeprom.o \ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ + pvrusb2-cs53l32a.o \ $(obj-pvrusb2-dvb-y) \ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c index cdedaa5..ccf2a3c 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -26,14 +26,6 @@ #include <media/msp3400.h> #include <media/v4l2-common.h> -struct pvr2_msp3400_handler { - struct pvr2_hdw *hdw; - struct pvr2_i2c_client *client; - struct pvr2_i2c_handler i2c_handler; - unsigned long stale_mask; -}; - - struct routing_scheme { const int *def; @@ -63,123 +55,33 @@ static const struct routing_scheme routing_schemes[] = { }, }; -/* This function selects the correct audio input source */ -static void set_stereo(struct pvr2_msp3400_handler *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - struct v4l2_routing route; - const struct routing_scheme *sp; - unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo"); - - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - route.input = sp->def[hdw->input_val]; - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "*** WARNING *** i2c msp3400 v4l2 set_stereo:" - " Invalid routing scheme (%u) and/or input (%d)", - sid,hdw->input_val); - return; - } - route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); -} - - -static int check_stereo(struct pvr2_msp3400_handler *ctxt) +void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) { - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->input_dirty; -} - - -struct pvr2_msp3400_ops { - void (*update)(struct pvr2_msp3400_handler *); - int (*check)(struct pvr2_msp3400_handler *); -}; - - -static const struct pvr2_msp3400_ops msp3400_ops[] = { - { .update = set_stereo, .check = check_stereo}, -}; - - -static int msp3400_check(struct pvr2_msp3400_handler *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(msp3400_ops); idx++) { - msk = 1 << idx; - if (ctxt->stale_mask & msk) continue; - if (msp3400_ops[idx].check(ctxt)) { - ctxt->stale_mask |= msk; + if (hdw->input_dirty || hdw->force_dirty) { + struct v4l2_routing route; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + + pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo"); + + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != NULL) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + route.input = sp->def[hdw->input_val]; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev msp3400 set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; } + route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); + sd->ops->audio->s_routing(sd, &route); } - return ctxt->stale_mask != 0; } - -static void msp3400_update(struct pvr2_msp3400_handler *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(msp3400_ops); idx++) { - msk = 1 << idx; - if (!(ctxt->stale_mask & msk)) continue; - ctxt->stale_mask &= ~msk; - msp3400_ops[idx].update(ctxt); - } -} - - -static void pvr2_msp3400_detach(struct pvr2_msp3400_handler *ctxt) -{ - ctxt->client->handler = NULL; - kfree(ctxt); -} - - -static unsigned int pvr2_msp3400_describe(struct pvr2_msp3400_handler *ctxt, - char *buf,unsigned int cnt) -{ - return scnprintf(buf,cnt,"handler: pvrusb2-audio v4l2"); -} - - -static const struct pvr2_i2c_handler_functions msp3400_funcs = { - .detach = (void (*)(void *))pvr2_msp3400_detach, - .check = (int (*)(void *))msp3400_check, - .update = (void (*)(void *))msp3400_update, - .describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_msp3400_describe, -}; - - -int pvr2_i2c_msp3400_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) -{ - struct pvr2_msp3400_handler *ctxt; - if (cp->handler) return 0; - - ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); - if (!ctxt) return 0; - - ctxt->i2c_handler.func_data = ctxt; - ctxt->i2c_handler.func_table = &msp3400_funcs; - ctxt->client = cp; - ctxt->hdw = hdw; - ctxt->stale_mask = (1 << ARRAY_SIZE(msp3400_ops)) - 1; - cp->handler = &ctxt->i2c_handler; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x msp3400 V4L2 handler set up", - cp->client->addr); - return !0; -} - - /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.h b/drivers/media/video/pvrusb2/pvrusb2-audio.h index ac54eed..e3e63d7 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.h +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.h @@ -22,10 +22,8 @@ #ifndef __PVRUSB2_AUDIO_H #define __PVRUSB2_AUDIO_H -#include "pvrusb2-i2c-core.h" - -int pvr2_i2c_msp3400_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); - +#include "pvrusb2-hdw-internal.h" +void pvr2_msp3400_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); #endif /* __PVRUSB2_AUDIO_H */ /* diff --git a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c new file mode 100644 index 0000000..b5c3428 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c @@ -0,0 +1,95 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + + This source file is specifically designed to interface with the + v4l-dvb cs53l32a module. + +*/ + +#include "pvrusb2-cs53l32a.h" + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <linux/errno.h> +#include <linux/slab.h> + +struct routing_scheme { + const int *def; + unsigned int cnt; +}; + + +static const int routing_scheme1[] = { + [PVR2_CVAL_INPUT_TV] = 2, /* 1 or 2 seems to work here */ + [PVR2_CVAL_INPUT_RADIO] = 2, + [PVR2_CVAL_INPUT_COMPOSITE] = 0, + [PVR2_CVAL_INPUT_SVIDEO] = 0, +}; + +static const struct routing_scheme routing_schemes[] = { + [PVR2_ROUTING_SCHEME_ONAIR] = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), + }, +}; + + +void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) +{ + if (hdw->input_dirty || hdw->force_dirty) { + struct v4l2_routing route; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", + hdw->input_val); + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != NULL) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + route.input = sp->def[hdw->input_val]; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev v4l2 set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; + } + route.output = 0; + sd->ops->audio->s_routing(sd, &route); + } +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.h b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h index ef4afaf..53ba548 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-tuner.h +++ b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h @@ -2,6 +2,7 @@ * * * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,14 +18,24 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#ifndef __PVRUSB2_TUNER_H -#define __PVRUSB2_TUNER_H -#include "pvrusb2-i2c-core.h" +#ifndef __PVRUSB2_CS53L32A_H +#define __PVRUSB2_CS53L32A_H -int pvr2_i2c_tuner_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which handles device video processing. This interface is + used internally by the driver; higher level code should only + interact through the interface provided by pvrusb2-hdw.h. + +*/ + + +#include "pvrusb2-hdw-internal.h" +void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); -#endif /* __PVRUSB2_TUNER_H */ +#endif /* __PVRUSB2_AUDIO_CS53L32A_H */ /* Stuff for Emacs to see, in order to encourage consistent editing style: diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index 895859e..4e017ff 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -28,7 +28,6 @@ #include "pvrusb2-cx2584x-v4l.h" #include "pvrusb2-video-v4l.h" -#include "pvrusb2-i2c-cmd-v4l2.h" #include "pvrusb2-hdw-internal.h" @@ -39,14 +38,6 @@ #include <linux/errno.h> #include <linux/slab.h> -struct pvr2_v4l_cx2584x { - struct pvr2_i2c_handler handler; - struct pvr2_decoder_ctrl ctrl; - struct pvr2_i2c_client *client; - struct pvr2_hdw *hdw; - unsigned long stale_mask; -}; - struct routing_scheme_item { int vid; @@ -110,218 +101,44 @@ static const struct routing_scheme routing_schemes[] = { }, }; -static void set_input(struct pvr2_v4l_cx2584x *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - struct v4l2_routing route; - enum cx25840_video_input vid_input; - enum cx25840_audio_input aud_input; - const struct routing_scheme *sp; - unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - - memset(&route,0,sizeof(route)); - - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - vid_input = sp->def[hdw->input_val].vid; - aud_input = sp->def[hdw->input_val].aud; - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "*** WARNING *** i2c cx2584x set_input:" - " Invalid routing scheme (%u) and/or input (%d)", - sid,hdw->input_val); - return; - } - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x", - vid_input,aud_input); - route.input = (u32)vid_input; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route); - route.input = (u32)aud_input; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); -} - - -static int check_input(struct pvr2_v4l_cx2584x *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->input_dirty != 0; -} - - -static void set_audio(struct pvr2_v4l_cx2584x *ctxt) -{ - u32 val; - struct pvr2_hdw *hdw = ctxt->hdw; - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_audio %d", - hdw->srate_val); - switch (hdw->srate_val) { - default: - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000: - val = 48000; - break; - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100: - val = 44100; - break; - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000: - val = 32000; - break; - } - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val); -} - - -static int check_audio(struct pvr2_v4l_cx2584x *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->srate_dirty != 0; -} - - -struct pvr2_v4l_cx2584x_ops { - void (*update)(struct pvr2_v4l_cx2584x *); - int (*check)(struct pvr2_v4l_cx2584x *); -}; - - -static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = { - { .update = set_input, .check = check_input}, - { .update = set_audio, .check = check_audio}, -}; - - -static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt) +void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) { - ctxt->client->handler = NULL; - pvr2_hdw_set_decoder(ctxt->hdw,NULL); - kfree(ctxt); -} - - -static int decoder_check(struct pvr2_v4l_cx2584x *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { - msk = 1 << idx; - if (ctxt->stale_mask & msk) continue; - if (decoder_ops[idx].check(ctxt)) { - ctxt->stale_mask |= msk; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev cx2584x update..."); + if (hdw->input_dirty || hdw->force_dirty) { + struct v4l2_routing route; + enum cx25840_video_input vid_input; + enum cx25840_audio_input aud_input; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + + memset(&route, 0, sizeof(route)); + + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != NULL) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + vid_input = sp->def[hdw->input_val].vid; + aud_input = sp->def[hdw->input_val].aud; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev cx2584x set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; } - } - return ctxt->stale_mask != 0; -} - -static void decoder_update(struct pvr2_v4l_cx2584x *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { - msk = 1 << idx; - if (!(ctxt->stale_mask & msk)) continue; - ctxt->stale_mask &= ~msk; - decoder_ops[idx].update(ctxt); + pvr2_trace(PVR2_TRACE_CHIPS, + "subdev cx2584x set_input vid=0x%x aud=0x%x", + vid_input, aud_input); + route.input = (u32)vid_input; + sd->ops->video->s_routing(sd, &route); + route.input = (u32)aud_input; + sd->ops->audio->s_routing(sd, &route); } } -static void decoder_enable(struct pvr2_v4l_cx2584x *ctxt,int fl) -{ - pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_enable(%d)",fl); - pvr2_v4l2_cmd_stream(ctxt->client,fl); -} - - -static int decoder_detect(struct pvr2_i2c_client *cp) -{ - int ret; - /* Attempt to query the decoder - let's see if it will answer */ - struct v4l2_queryctrl qc; - - memset(&qc,0,sizeof(qc)); - - qc.id = V4L2_CID_BRIGHTNESS; - - ret = pvr2_i2c_client_cmd(cp,VIDIOC_QUERYCTRL,&qc); - return ret == 0; /* Return true if it answered */ -} - - -static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt, - char *buf,unsigned int cnt) -{ - return scnprintf(buf,cnt,"handler: pvrusb2-cx2584x-v4l"); -} - - -static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt) -{ - int ret; - ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,NULL); - pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_reset (ret=%d)",ret); -} - - -static const struct pvr2_i2c_handler_functions hfuncs = { - .detach = (void (*)(void *))decoder_detach, - .check = (int (*)(void *))decoder_check, - .update = (void (*)(void *))decoder_update, - .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe, -}; - - -int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw, - struct pvr2_i2c_client *cp) -{ - struct pvr2_v4l_cx2584x *ctxt; - - if (hdw->decoder_ctrl) return 0; - if (cp->handler) return 0; - if (!decoder_detect(cp)) return 0; - - ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); - if (!ctxt) return 0; - - ctxt->handler.func_data = ctxt; - ctxt->handler.func_table = &hfuncs; - ctxt->ctrl.ctxt = ctxt; - ctxt->ctrl.detach = (void (*)(void *))decoder_detach; - ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; - ctxt->ctrl.force_reset = (void (*)(void*))decoder_reset; - ctxt->client = cp; - ctxt->hdw = hdw; - ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; - pvr2_hdw_set_decoder(hdw,&ctxt->ctrl); - cp->handler = &ctxt->handler; - { - /* - Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit - of nuttiness for cx25840 causes that module to - correctly set up its video scaling. This is really - a problem in the cx25840 module itself, but we work - around it here. The problem has not been seen in - ivtv because there VBI is supported and set up. We - don't do VBI here (at least not yet) and thus we - never attempted to even set it up. - */ - struct v4l2_format fmt; - memset(&fmt,0,sizeof(fmt)); - fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_FMT,&fmt); - } - pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up", - cp->client->addr); - return !0; -} - - - /* Stuff for Emacs to see, in order to encourage consistent editing style: diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h index 66abf77..e35c232 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h @@ -34,9 +34,9 @@ -#include "pvrusb2-i2c-core.h" +#include "pvrusb2-hdw-internal.h" -int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); +void pvr2_cx25840_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); #endif /* __PVRUSB2_CX2584X_V4L_H */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index ca892fb..fbe3856 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -23,7 +23,6 @@ #include "pvrusb2-debugifc.h" #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" -#include "pvrusb2-i2c-core.h" struct debugifc_mask_item { const char *name; @@ -147,10 +146,6 @@ int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) bcnt += ccnt; acnt -= ccnt; buf += ccnt; ccnt = pvr2_hdw_state_report(hdw,buf,acnt); bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = pvr2_i2c_report(hdw,buf,acnt); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; return bcnt; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h index e24ff59..2f8d467 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h @@ -22,16 +22,16 @@ struct pvr2_hdw; -/* Non-intrusively print some useful debugging info from inside the - driver. This should work even if the driver appears to be - wedged. */ -int pvr2_debugifc_print_info(struct pvr2_hdw *, - char *buf_ptr,unsigned int buf_size); - /* Print general status of driver. This will also trigger a probe of the USB link. Unlike print_info(), this one synchronizes with the driver so the information should be self-consistent (but it will hang if the driver is wedged). */ +int pvr2_debugifc_print_info(struct pvr2_hdw *, + char *buf_ptr, unsigned int buf_size); + +/* Non-intrusively print some useful debugging info from inside the + driver. This should work even if the driver appears to be + wedged. */ int pvr2_debugifc_print_status(struct pvr2_hdw *, char *buf_ptr,unsigned int buf_size); diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index cbe2a34..1cb6a26 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -46,10 +46,11 @@ pvr2_device_desc structures. /*------------------------------------------------------------------------*/ /* Hauppauge PVR-USB2 Model 29xxx */ -static const char *pvr2_client_29xxx[] = { - "msp3400", - "saa7115", - "tuner", +static const struct pvr2_device_client_desc pvr2_cli_29xxx[] = { + { .module_id = PVR2_CLIENT_ID_SAA7115 }, + { .module_id = PVR2_CLIENT_ID_MSP3400 }, + { .module_id = PVR2_CLIENT_ID_TUNER }, + { .module_id = PVR2_CLIENT_ID_DEMOD }, }; static const char *pvr2_fw1_names_29xxx[] = { @@ -59,8 +60,8 @@ static const char *pvr2_fw1_names_29xxx[] = { static const struct pvr2_device_desc pvr2_device_29xxx = { .description = "WinTV PVR USB2 Model Category 29xxx", .shortname = "29xxx", - .client_modules.lst = pvr2_client_29xxx, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx), + .client_table.lst = pvr2_cli_29xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx), .fx2_firmware.lst = pvr2_fw1_names_29xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx), .flag_has_hauppauge_rom = !0, @@ -77,10 +78,11 @@ static const struct pvr2_device_desc pvr2_device_29xxx = { /*------------------------------------------------------------------------*/ /* Hauppauge PVR-USB2 Model 24xxx */ -static const char *pvr2_client_24xxx[] = { - "cx25840", - "tuner", - "wm8775", +static const struct pvr2_device_client_desc pvr2_cli_24xxx[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, + { .module_id = PVR2_CLIENT_ID_TUNER }, + { .module_id = PVR2_CLIENT_ID_WM8775 }, + { .module_id = PVR2_CLIENT_ID_DEMOD }, }; static const char *pvr2_fw1_names_24xxx[] = { @@ -90,8 +92,8 @@ static const char *pvr2_fw1_names_24xxx[] = { static const struct pvr2_device_desc pvr2_device_24xxx = { .description = "WinTV PVR USB2 Model Category 24xxx", .shortname = "24xxx", - .client_modules.lst = pvr2_client_24xxx, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx), + .client_table.lst = pvr2_cli_24xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx), .fx2_firmware.lst = pvr2_fw1_names_24xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx), .flag_has_cx25840 = !0, @@ -111,16 +113,16 @@ static const struct pvr2_device_desc pvr2_device_24xxx = { /*------------------------------------------------------------------------*/ /* GOTVIEW USB2.0 DVD2 */ -static const char *pvr2_client_gotview_2[] = { - "cx25840", - "tuner", +static const struct pvr2_device_client_desc pvr2_cli_gotview_2[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, + { .module_id = PVR2_CLIENT_ID_TUNER }, }; static const struct pvr2_device_desc pvr2_device_gotview_2 = { .description = "Gotview USB 2.0 DVD 2", .shortname = "gv2", - .client_modules.lst = pvr2_client_gotview_2, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2), + .client_table.lst = pvr2_cli_gotview_2, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2), .flag_has_cx25840 = !0, .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .flag_has_analogtuner = !0, @@ -140,8 +142,8 @@ static const struct pvr2_device_desc pvr2_device_gotview_2 = { static const struct pvr2_device_desc pvr2_device_gotview_2d = { .description = "Gotview USB 2.0 DVD Deluxe", .shortname = "gv2d", - .client_modules.lst = pvr2_client_gotview_2, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2), + .client_table.lst = pvr2_cli_gotview_2, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2), .flag_has_cx25840 = !0, .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .flag_has_analogtuner = !0, @@ -181,29 +183,29 @@ static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap) return 0; } -static struct pvr2_dvb_props pvr2_onair_creator_fe_props = { +static const struct pvr2_dvb_props pvr2_onair_creator_fe_props = { .frontend_attach = pvr2_lgdt3303_attach, .tuner_attach = pvr2_lgh06xf_attach, }; #endif -static const char *pvr2_client_onair_creator[] = { - "saa7115", - "tuner", - "cs53l32a", +static const struct pvr2_device_client_desc pvr2_cli_onair_creator[] = { + { .module_id = PVR2_CLIENT_ID_SAA7115 }, + { .module_id = PVR2_CLIENT_ID_CS53L32A }, + { .module_id = PVR2_CLIENT_ID_TUNER }, }; static const struct pvr2_device_desc pvr2_device_onair_creator = { .description = "OnAir Creator Hybrid USB tuner", .shortname = "oac", - .client_modules.lst = pvr2_client_onair_creator, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_creator), + .client_table.lst = pvr2_cli_onair_creator, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_creator), .default_tuner_type = TUNER_LG_TDVS_H06XF, .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, .flag_digital_requires_cx23416 = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR, .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, .default_std_mask = V4L2_STD_NTSC_M, #ifdef CONFIG_VIDEO_PVRUSB2_DVB @@ -241,29 +243,29 @@ static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap) return 0; } -static struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { +static const struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { .frontend_attach = pvr2_lgdt3302_attach, .tuner_attach = pvr2_fcv1236d_attach, }; #endif -static const char *pvr2_client_onair_usb2[] = { - "saa7115", - "tuner", - "cs53l32a", +static const struct pvr2_device_client_desc pvr2_cli_onair_usb2[] = { + { .module_id = PVR2_CLIENT_ID_SAA7115 }, + { .module_id = PVR2_CLIENT_ID_CS53L32A }, + { .module_id = PVR2_CLIENT_ID_TUNER }, }; static const struct pvr2_device_desc pvr2_device_onair_usb2 = { .description = "OnAir USB2 Hybrid USB tuner", .shortname = "oa2", - .client_modules.lst = pvr2_client_onair_usb2, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_usb2), + .client_table.lst = pvr2_cli_onair_usb2, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_usb2), .default_tuner_type = TUNER_PHILIPS_FCV1236D, .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, .flag_digital_requires_cx23416 = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR, .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, .default_std_mask = V4L2_STD_NTSC_M, #ifdef CONFIG_VIDEO_PVRUSB2_DVB @@ -314,15 +316,16 @@ static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) return 0; } -static struct pvr2_dvb_props pvr2_73xxx_dvb_props = { +static const struct pvr2_dvb_props pvr2_73xxx_dvb_props = { .frontend_attach = pvr2_tda10048_attach, .tuner_attach = pvr2_73xxx_tda18271_8295_attach, }; #endif -static const char *pvr2_client_73xxx[] = { - "cx25840", - "tuner", +static const struct pvr2_device_client_desc pvr2_cli_73xxx[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, + { .module_id = PVR2_CLIENT_ID_TUNER, + .i2c_address_list = "\x42"}, }; static const char *pvr2_fw1_names_73xxx[] = { @@ -332,8 +335,8 @@ static const char *pvr2_fw1_names_73xxx[] = { static const struct pvr2_device_desc pvr2_device_73xxx = { .description = "WinTV HVR-1900 Model Category 73xxx", .shortname = "73xxx", - .client_modules.lst = pvr2_client_73xxx, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_73xxx), + .client_table.lst = pvr2_cli_73xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), .fx2_firmware.lst = pvr2_fw1_names_73xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx), .flag_has_cx25840 = !0, @@ -418,22 +421,17 @@ static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) return 0; } -static struct pvr2_dvb_props pvr2_750xx_dvb_props = { +static const struct pvr2_dvb_props pvr2_750xx_dvb_props = { .frontend_attach = pvr2_s5h1409_attach, .tuner_attach = pvr2_tda18271_8295_attach, }; -static struct pvr2_dvb_props pvr2_751xx_dvb_props = { +static const struct pvr2_dvb_props pvr2_751xx_dvb_props = { .frontend_attach = pvr2_s5h1411_attach, .tuner_attach = pvr2_tda18271_8295_attach, }; #endif -static const char *pvr2_client_75xxx[] = { - "cx25840", - "tuner", -}; - static const char *pvr2_fw1_names_75xxx[] = { "v4l-pvrusb2-73xxx-01.fw", }; @@ -441,8 +439,8 @@ static const char *pvr2_fw1_names_75xxx[] = { static const struct pvr2_device_desc pvr2_device_750xx = { .description = "WinTV HVR-1950 Model Category 750xx", .shortname = "750xx", - .client_modules.lst = pvr2_client_75xxx, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), + .client_table.lst = pvr2_cli_73xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), .fx2_firmware.lst = pvr2_fw1_names_75xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), .flag_has_cx25840 = !0, @@ -463,8 +461,8 @@ static const struct pvr2_device_desc pvr2_device_750xx = { static const struct pvr2_device_desc pvr2_device_751xx = { .description = "WinTV HVR-1950 Model Category 751xx", .shortname = "751xx", - .client_modules.lst = pvr2_client_75xxx, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), + .client_table.lst = pvr2_cli_73xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), .fx2_firmware.lst = pvr2_fw1_names_75xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), .flag_has_cx25840 = !0, diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h index cb3a33e..3e55338 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -33,6 +33,34 @@ */ +#define PVR2_CLIENT_ID_NULL 0 +#define PVR2_CLIENT_ID_MSP3400 1 +#define PVR2_CLIENT_ID_CX25840 2 +#define PVR2_CLIENT_ID_SAA7115 3 +#define PVR2_CLIENT_ID_TUNER 4 +#define PVR2_CLIENT_ID_CS53L32A 5 +#define PVR2_CLIENT_ID_WM8775 6 +#define PVR2_CLIENT_ID_DEMOD 7 + +struct pvr2_device_client_desc { + /* One ovr PVR2_CLIENT_ID_xxxx */ + unsigned char module_id; + + /* Null-terminated array of I2C addresses to try in order + initialize the module. It's safe to make this null terminated + since we're never going to encounter an i2c device with an + address of zero. If this is a null pointer or zero-length, + then no I2C addresses have been specified, in which case we'll + try some compiled in defaults for now. */ + unsigned char *i2c_address_list; +}; + +struct pvr2_device_client_table { + const struct pvr2_device_client_desc *lst; + unsigned char cnt; +}; + + struct pvr2_string_table { const char **lst; unsigned int cnt; @@ -40,6 +68,7 @@ struct pvr2_string_table { #define PVR2_ROUTING_SCHEME_HAUPPAUGE 0 #define PVR2_ROUTING_SCHEME_GOTVIEW 1 +#define PVR2_ROUTING_SCHEME_ONAIR 2 #define PVR2_DIGITAL_SCHEME_NONE 0 #define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1 @@ -66,6 +95,9 @@ struct pvr2_device_desc { /* List of additional client modules we need to load */ struct pvr2_string_table client_modules; + /* List of defined client modules we need to load */ + struct pvr2_device_client_table client_table; + /* List of FX2 firmware file names we should search; if empty then FX2 firmware check / load is skipped and we assume the device was initialized from internal ROM. */ @@ -73,7 +105,7 @@ struct pvr2_device_desc { #ifdef CONFIG_VIDEO_PVRUSB2_DVB /* callback functions to handle attachment of digital tuner & demod */ - struct pvr2_dvb_props *dvb_props; + const struct pvr2_dvb_props *dvb_props; #endif /* Initial standard bits to use for this device, if not zero. diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c index 77b3c33..b7f5c49 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-dvb.c +++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c @@ -321,7 +321,7 @@ static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap) static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap) { struct pvr2_hdw *hdw = adap->channel.hdw; - struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props; + const struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props; int ret = 0; if (dvb_props == NULL) { diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 273d2a1..54ac534 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -347,7 +347,7 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) int encMisc3Arg = 0; #if 0 - /* This inexplicable bit happens in the Hauppage windows + /* This inexplicable bit happens in the Hauppauge windows driver (for both 24xxx and 29xxx devices). However I currently see no difference in behavior with or without this stuff. Leave this here as a note of its existence, diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index de7ee72..5d75eb5 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -38,6 +38,7 @@ #include <linux/mutex.h> #include "pvrusb2-hdw.h" #include "pvrusb2-io.h" +#include <media/v4l2-device.h> #include <media/cx2341x.h> #include "pvrusb2-devattr.h" @@ -57,8 +58,6 @@ #define LOCK_TAKE(x) do { mutex_lock(&x##_mutex); x##_held = !0; } while (0) #define LOCK_GIVE(x) do { x##_held = 0; mutex_unlock(&x##_mutex); } while (0) -struct pvr2_decoder; - typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *); typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *); typedef int (*pvr2_ctlf_check_value)(struct pvr2_ctrl *,int); @@ -139,22 +138,6 @@ struct pvr2_ctrl { }; -struct pvr2_decoder_ctrl { - void *ctxt; - void (*detach)(void *); - void (*enable)(void *,int); - void (*force_reset)(void *); -}; - -#define PVR2_I2C_PEND_DETECT 0x01 /* Need to detect a client type */ -#define PVR2_I2C_PEND_CLIENT 0x02 /* Client needs a specific update */ -#define PVR2_I2C_PEND_REFRESH 0x04 /* Client has specific pending bits */ -#define PVR2_I2C_PEND_STALE 0x08 /* Broadcast pending bits */ - -#define PVR2_I2C_PEND_ALL (PVR2_I2C_PEND_DETECT |\ - PVR2_I2C_PEND_CLIENT |\ - PVR2_I2C_PEND_REFRESH |\ - PVR2_I2C_PEND_STALE) /* Disposition of firmware1 loading situation */ #define FW1_STATE_UNKNOWN 0 @@ -179,6 +162,8 @@ struct pvr2_hdw { struct usb_device *usb_dev; struct usb_interface *usb_intf; + /* Our handle into the v4l2 sub-device architecture */ + struct v4l2_device v4l2_dev; /* Device description, anything that must adjust behavior based on device specific info will use information held here. */ const struct pvr2_device_desc *hdw_desc; @@ -186,7 +171,6 @@ struct pvr2_hdw { /* Kernel worker thread handling */ struct workqueue_struct *workqueue; struct work_struct workpoll; /* Update driver state */ - struct work_struct worki2csync; /* Update i2c clients */ /* Video spigot */ struct pvr2_stream *vid_stream; @@ -195,20 +179,26 @@ struct pvr2_hdw { struct mutex big_lock_mutex; int big_lock_held; /* For debugging */ + /* This is a simple string which identifies the instance of this + driver. It is unique within the set of existing devices, but + there is no attempt to keep the name consistent with the same + physical device each time. */ char name[32]; + /* This is a simple string which identifies the physical device + instance itself - if possible. (If not possible, then it is + based on the specific driver instance, similar to name above.) + The idea here is that userspace might hopefully be able to use + this recognize specific tuners. It will encode a serial number, + if available. */ + char identifier[32]; + /* I2C stuff */ struct i2c_adapter i2c_adap; struct i2c_algorithm i2c_algo; pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT]; int i2c_cx25840_hack_state; int i2c_linked; - unsigned int i2c_pend_types; /* Which types of update are needed */ - unsigned long i2c_pend_mask; /* Change bits we need to scan */ - unsigned long i2c_stale_mask; /* Pending broadcast change bits */ - unsigned long i2c_active_mask; /* All change bits currently in use */ - struct list_head i2c_clients; - struct mutex i2c_list_lock; /* Frequency table */ unsigned int freqTable[FREQTABLE_SIZE]; @@ -275,6 +265,7 @@ struct pvr2_hdw { wait_queue_head_t state_wait_data; + int force_dirty; /* consider all controls dirty if true */ int flag_ok; /* device in known good state */ int flag_disconnected; /* flag_ok == 0 due to disconnect */ int flag_init_ok; /* true if structure is fully initialized */ @@ -283,17 +274,13 @@ struct pvr2_hdw { int flag_decoder_missed;/* We've noticed missing decoder */ int flag_tripped; /* Indicates overall failure to start */ - struct pvr2_decoder_ctrl *decoder_ctrl; + unsigned int decoder_client_id; // CPU firmware info (used to help find / save firmware data) char *fw_buffer; unsigned int fw_size; int fw_cpu_flag; /* True if we are dealing with the CPU */ - // True if there is a request to trigger logging of state in each - // module. - int log_requested; - /* Tuner / frequency control stuff */ unsigned int tuner_type; int tuner_updated; @@ -391,7 +378,8 @@ struct pvr2_hdw { /* This function gets the current frequency */ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *); -void pvr2_hdw_set_decoder(struct pvr2_hdw *,struct pvr2_decoder_ctrl *); + +void pvr2_hdw_status_poll(struct pvr2_hdw *); #endif /* __PVRUSB2_HDW_INTERNAL_H */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index fa304e5..7a65b42 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -24,17 +24,22 @@ #include <linux/firmware.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/tuner.h> #include "pvrusb2.h" #include "pvrusb2-std.h" #include "pvrusb2-util.h" #include "pvrusb2-hdw.h" #include "pvrusb2-i2c-core.h" -#include "pvrusb2-tuner.h" #include "pvrusb2-eeprom.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-encoder.h" #include "pvrusb2-debug.h" #include "pvrusb2-fx2-cmd.h" +#include "pvrusb2-wm8775.h" +#include "pvrusb2-video-v4l.h" +#include "pvrusb2-cx2584x-v4l.h" +#include "pvrusb2-cs53l32a.h" +#include "pvrusb2-audio.h" #define TV_MIN_FREQ 55250000L #define TV_MAX_FREQ 850000000L @@ -104,6 +109,39 @@ MODULE_PARM_DESC(radio_freq, "specify initial radio frequency"); /* size of a firmware chunk */ #define FIRMWARE_CHUNK_SIZE 0x2000 +typedef void (*pvr2_subdev_update_func)(struct pvr2_hdw *, + struct v4l2_subdev *); + +static const pvr2_subdev_update_func pvr2_module_update_functions[] = { + [PVR2_CLIENT_ID_WM8775] = pvr2_wm8775_subdev_update, + [PVR2_CLIENT_ID_SAA7115] = pvr2_saa7115_subdev_update, + [PVR2_CLIENT_ID_MSP3400] = pvr2_msp3400_subdev_update, + [PVR2_CLIENT_ID_CX25840] = pvr2_cx25840_subdev_update, + [PVR2_CLIENT_ID_CS53L32A] = pvr2_cs53l32a_subdev_update, +}; + +static const char *module_names[] = { + [PVR2_CLIENT_ID_MSP3400] = "msp3400", + [PVR2_CLIENT_ID_CX25840] = "cx25840", + [PVR2_CLIENT_ID_SAA7115] = "saa7115", + [PVR2_CLIENT_ID_TUNER] = "tuner", + [PVR2_CLIENT_ID_DEMOD] = "tuner", + [PVR2_CLIENT_ID_CS53L32A] = "cs53l32a", + [PVR2_CLIENT_ID_WM8775] = "wm8775", +}; + + +static const unsigned char *module_i2c_addresses[] = { + [PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63", + [PVR2_CLIENT_ID_DEMOD] = "\x43", + [PVR2_CLIENT_ID_MSP3400] = "\x40", + [PVR2_CLIENT_ID_SAA7115] = "\x21", + [PVR2_CLIENT_ID_WM8775] = "\x1b", + [PVR2_CLIENT_ID_CX25840] = "\x44", + [PVR2_CLIENT_ID_CS53L32A] = "\x11", +}; + + /* Define the list of additional controls we'll dynamically construct based on query of the cx2341x module. */ struct pvr2_mpeg_ids { @@ -277,7 +315,6 @@ static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v); static void pvr2_hdw_state_sched(struct pvr2_hdw *); static int pvr2_hdw_state_eval(struct pvr2_hdw *); static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); -static void pvr2_hdw_worker_i2c(struct work_struct *work); static void pvr2_hdw_worker_poll(struct work_struct *work); static int pvr2_hdw_wait(struct pvr2_hdw *,int state); static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *); @@ -642,7 +679,7 @@ static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp) unsigned long fv; struct pvr2_hdw *hdw = cptr->hdw; if (hdw->tuner_signal_stale) { - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); } fv = hdw->tuner_signal_info.rangehigh; if (!fv) { @@ -664,7 +701,7 @@ static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp) unsigned long fv; struct pvr2_hdw *hdw = cptr->hdw; if (hdw->tuner_signal_stale) { - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); } fv = hdw->tuner_signal_info.rangelow; if (!fv) { @@ -858,7 +895,7 @@ static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr) static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp) { struct pvr2_hdw *hdw = cptr->hdw; - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); *vp = hdw->tuner_signal_info.signal; return 0; } @@ -868,7 +905,7 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp) int val = 0; unsigned int subchan; struct pvr2_hdw *hdw = cptr->hdw; - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); subchan = hdw->tuner_signal_info.rxsubchans; if (subchan & V4L2_TUNER_SUB_MONO) { val |= (1 << V4L2_TUNER_MODE_MONO); @@ -1283,6 +1320,12 @@ const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw) } +const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *hdw) +{ + return hdw->identifier; +} + + unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) { return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio; @@ -1634,33 +1677,27 @@ static const char *pvr2_get_state_name(unsigned int st) static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl) { - if (!hdw->decoder_ctrl) { - if (!hdw->flag_decoder_missed) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: No decoder present"); - hdw->flag_decoder_missed = !0; - trace_stbit("flag_decoder_missed", - hdw->flag_decoder_missed); - } - return -EIO; + /* Even though we really only care about the video decoder chip at + this point, we'll broadcast stream on/off to all sub-devices + anyway, just in case somebody else wants to hear the + command... */ + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s", + (enablefl ? "on" : "off")); + v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl); + if (hdw->decoder_client_id) { + /* We get here if the encoder has been noticed. Otherwise + we'll issue a warning to the user (which should + normally never happen). */ + return 0; } - hdw->decoder_ctrl->enable(hdw->decoder_ctrl->ctxt,enablefl); - return 0; -} - - -void pvr2_hdw_set_decoder(struct pvr2_hdw *hdw,struct pvr2_decoder_ctrl *ptr) -{ - if (hdw->decoder_ctrl == ptr) return; - hdw->decoder_ctrl = ptr; - if (hdw->decoder_ctrl && hdw->flag_decoder_missed) { - hdw->flag_decoder_missed = 0; + if (!hdw->flag_decoder_missed) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: No decoder present"); + hdw->flag_decoder_missed = !0; trace_stbit("flag_decoder_missed", hdw->flag_decoder_missed); - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Decoder has appeared"); - pvr2_hdw_state_sched(hdw); } + return -EIO; } @@ -1927,6 +1964,166 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) } +static unsigned int pvr2_copy_i2c_addr_list( + unsigned short *dst, const unsigned char *src, + unsigned int dst_max) +{ + unsigned int cnt = 0; + if (!src) return 0; + while (src[cnt] && (cnt + 1) < dst_max) { + dst[cnt] = src[cnt]; + cnt++; + } + dst[cnt] = I2C_CLIENT_END; + return cnt; +} + + +static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, + const struct pvr2_device_client_desc *cd) +{ + const char *fname; + unsigned char mid; + struct v4l2_subdev *sd; + unsigned int i2ccnt; + const unsigned char *p; + /* Arbitrary count - max # i2c addresses we will probe */ + unsigned short i2caddr[25]; + + mid = cd->module_id; + fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL; + if (!fname) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Module ID %u for device %s has no name", + mid, + hdw->hdw_desc->description); + return -EINVAL; + } + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u (%s) for device %s being loaded...", + mid, fname, + hdw->hdw_desc->description); + + i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list, + ARRAY_SIZE(i2caddr)); + if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ? + module_i2c_addresses[mid] : NULL) != NULL)) { + /* Second chance: Try default i2c address list */ + i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p, + ARRAY_SIZE(i2caddr)); + if (i2ccnt) { + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Using default i2c address list", + mid); + } + } + + if (!i2ccnt) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Module ID %u (%s) for device %s:" + " No i2c addresses", + mid, fname, hdw->hdw_desc->description); + return -EINVAL; + } + + /* Note how the 2nd and 3rd arguments are the same for both + * v4l2_i2c_new_subdev() and v4l2_i2c_new_probed_subdev(). Why? + * Well the 2nd argument is the module name to load, while the 3rd + * argument is documented in the framework as being the "chipid" - + * and every other place where I can find examples of this, the + * "chipid" appears to just be the module name again. So here we + * just do the same thing. */ + if (i2ccnt == 1) { + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Setting up with specified i2c address 0x%x", + mid, i2caddr[0]); + sd = v4l2_i2c_new_subdev(&hdw->i2c_adap, + fname, fname, + i2caddr[0]); + } else { + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Setting up with address probe list", + mid); + sd = v4l2_i2c_new_probed_subdev(&hdw->i2c_adap, + fname, fname, + i2caddr); + } + + if (!sd) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Module ID %u (%s) for device %s failed to load", + mid, fname, hdw->hdw_desc->description); + return -EIO; + } + + /* Tag this sub-device instance with the module ID we know about. + In other places we'll use that tag to determine if the instance + requires special handling. */ + sd->grp_id = mid; + + pvr2_trace(PVR2_TRACE_INFO, "Attached sub-driver %s", fname); + + + /* client-specific setup... */ + switch (mid) { + case PVR2_CLIENT_ID_CX25840: + hdw->decoder_client_id = mid; + { + /* + Mike Isely <isely@pobox.com> 19-Nov-2006 - This + bit of nuttiness for cx25840 causes that module + to correctly set up its video scaling. This is + really a problem in the cx25840 module itself, + but we work around it here. The problem has not + been seen in ivtv because there VBI is supported + and set up. We don't do VBI here (at least not + yet) and thus we never attempted to even set it + up. + */ + struct v4l2_format fmt; + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Executing cx25840 VBI hack", + mid); + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + v4l2_device_call_all(&hdw->v4l2_dev, mid, + video, s_fmt, &fmt); + } + break; + case PVR2_CLIENT_ID_SAA7115: + hdw->decoder_client_id = mid; + break; + default: break; + } + + return 0; +} + + +static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw) +{ + unsigned int idx; + const struct pvr2_string_table *cm; + const struct pvr2_device_client_table *ct; + int okFl = !0; + + cm = &hdw->hdw_desc->client_modules; + for (idx = 0; idx < cm->cnt; idx++) { + request_module(cm->lst[idx]); + } + + ct = &hdw->hdw_desc->client_table; + for (idx = 0; idx < ct->cnt; idx++) { + if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0; + } + if (!okFl) pvr2_hdw_render_useless(hdw); +} + + static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) { int ret; @@ -1966,9 +2163,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; - for (idx = 0; idx < hdw->hdw_desc->client_modules.cnt; idx++) { - request_module(hdw->hdw_desc->client_modules.lst[idx]); - } + hdw->force_dirty = !0; if (!hdw->hdw_desc->flag_no_powerup) { pvr2_hdw_cmd_powerup(hdw); @@ -1987,6 +2182,11 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) pvr2_i2c_core_init(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; + pvr2_hdw_load_modules(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, init, 0); + for (idx = 0; idx < CTRLDEF_COUNT; idx++) { cptr = hdw->controls + idx; if (cptr->info->skip_init) continue; @@ -2024,6 +2224,19 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) hdw->std_mask_eeprom = V4L2_STD_ALL; } + if (hdw->serial_number) { + idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1, + "sn-%lu", hdw->serial_number); + } else if (hdw->unit_number >= 0) { + idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1, + "unit-%c", + hdw->unit_number + 'a'); + } else { + idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1, + "unit-??"); + } + hdw->identifier[idx] = 0; + pvr2_hdw_setup_std(hdw); if (!get_default_tuner_type(hdw)) { @@ -2032,8 +2245,6 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) hdw->tuner_type); } - pvr2_i2c_core_check_stale(hdw); - hdw->tuner_updated = 0; if (!pvr2_hdw_dev_ok(hdw)) return; @@ -2171,11 +2382,14 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, struct pvr2_hdw *hdw = NULL; int valid_std_mask; struct pvr2_ctrl *cptr; + struct usb_device *usb_dev; const struct pvr2_device_desc *hdw_desc; __u8 ifnum; struct v4l2_queryctrl qctrl; struct pvr2_ctl_info *ciptr; + usb_dev = interface_to_usbdev(intf); + hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info); if (hdw_desc == NULL) { @@ -2360,6 +2574,11 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL); if (!hdw->ctl_read_urb) goto fail; + if (v4l2_device_register(&usb_dev->dev, &hdw->v4l2_dev) != 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Error registering with v4l core, giving up"); + goto fail; + } mutex_lock(&pvr2_unit_mtx); do { for (idx = 0; idx < PVR_NUM; idx++) { if (unit_pointers[idx]) continue; @@ -2382,7 +2601,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->workqueue = create_singlethread_workqueue(hdw->name); INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll); - INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c); pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s", hdw->unit_number,hdw->name); @@ -2391,12 +2609,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->flag_ok = !0; hdw->usb_intf = intf; - hdw->usb_dev = interface_to_usbdev(intf); + hdw->usb_dev = usb_dev; - scnprintf(hdw->bus_info,sizeof(hdw->bus_info), - "usb %s address %d", - dev_name(&hdw->usb_dev->dev), - hdw->usb_dev->devnum); + usb_make_path(hdw->usb_dev, hdw->bus_info, sizeof(hdw->bus_info)); ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber; usb_set_interface(hdw->usb_dev,ifnum,0); @@ -2454,6 +2669,10 @@ static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw) hdw->ctl_write_buffer = NULL; } hdw->flag_disconnected = !0; + /* If we don't do this, then there will be a dangling struct device + reference to our disappearing device persisting inside the V4L + core... */ + v4l2_device_disconnect(&hdw->v4l2_dev); hdw->usb_dev = NULL; hdw->usb_intf = NULL; pvr2_hdw_render_useless(hdw); @@ -2481,10 +2700,8 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) pvr2_stream_destroy(hdw->vid_stream); hdw->vid_stream = NULL; } - if (hdw->decoder_ctrl) { - hdw->decoder_ctrl->detach(hdw->decoder_ctrl->ctxt); - } pvr2_i2c_core_done(hdw); + v4l2_device_unregister(&hdw->v4l2_dev); pvr2_hdw_remove_usb_stuff(hdw); mutex_lock(&pvr2_unit_mtx); do { if ((hdw->unit_number >= 0) && @@ -2678,6 +2895,150 @@ static const char *get_ctrl_typename(enum pvr2_ctl_type tp) } +static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id, + const char *name, int val) +{ + struct v4l2_control ctrl; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 %s=%d", name, val); + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = id; + ctrl.value = val; + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, s_ctrl, &ctrl); +} + +#define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \ + if ((hdw)->lab##_dirty || (hdw)->force_dirty) { \ + pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \ + } + +/* Execute whatever commands are required to update the state of all the + sub-devices so that they match our current control values. */ +static void pvr2_subdev_update(struct pvr2_hdw *hdw) +{ + struct v4l2_subdev *sd; + unsigned int id; + pvr2_subdev_update_func fp; + + pvr2_trace(PVR2_TRACE_CHIPS, "subdev update..."); + + if (hdw->tuner_updated || hdw->force_dirty) { + struct tuner_setup setup; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev tuner set_type(%d)", + hdw->tuner_type); + if (((int)(hdw->tuner_type)) >= 0) { + setup.addr = ADDR_UNSET; + setup.type = hdw->tuner_type; + setup.mode_mask = T_RADIO | T_ANALOG_TV; + v4l2_device_call_all(&hdw->v4l2_dev, 0, + tuner, s_type_addr, &setup); + } + } + + if (hdw->input_dirty || hdw->std_dirty || hdw->force_dirty) { + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_standard"); + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + v4l2_device_call_all(&hdw->v4l2_dev, 0, + tuner, s_radio); + } else { + v4l2_std_id vs; + vs = hdw->std_mask_cur; + v4l2_device_call_all(&hdw->v4l2_dev, 0, + tuner, s_std, vs); + } + hdw->tuner_signal_stale = !0; + hdw->cropcap_stale = !0; + } + + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_BRIGHTNESS, brightness); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_CONTRAST, contrast); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_SATURATION, saturation); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_HUE, hue); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_MUTE, mute); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_VOLUME, volume); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BALANCE, balance); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BASS, bass); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_TREBLE, treble); + + if (hdw->input_dirty || hdw->audiomode_dirty || hdw->force_dirty) { + struct v4l2_tuner vt; + memset(&vt, 0, sizeof(vt)); + vt.audmode = hdw->audiomode_val; + v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, s_tuner, &vt); + } + + if (hdw->freqDirty || hdw->force_dirty) { + unsigned long fv; + struct v4l2_frequency freq; + fv = pvr2_hdw_get_cur_freq(hdw); + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_freq(%lu)", fv); + if (hdw->tuner_signal_stale) pvr2_hdw_status_poll(hdw); + memset(&freq, 0, sizeof(freq)); + if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { + /* ((fv * 1000) / 62500) */ + freq.frequency = (fv * 2) / 125; + } else { + freq.frequency = fv / 62500; + } + /* tuner-core currently doesn't seem to care about this, but + let's set it anyway for completeness. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + freq.type = V4L2_TUNER_RADIO; + } else { + freq.type = V4L2_TUNER_ANALOG_TV; + } + freq.tuner = 0; + v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, + s_frequency, &freq); + } + + if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) { + struct v4l2_format fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = hdw->res_hor_val; + fmt.fmt.pix.height = hdw->res_ver_val; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)", + fmt.fmt.pix.width, fmt.fmt.pix.height); + v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_fmt, &fmt); + } + + if (hdw->srate_dirty || hdw->force_dirty) { + u32 val; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_audio %d", + hdw->srate_val); + switch (hdw->srate_val) { + default: + case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000: + val = 48000; + break; + case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100: + val = 44100; + break; + case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000: + val = 32000; + break; + } + v4l2_device_call_all(&hdw->v4l2_dev, 0, + audio, s_clock_freq, val); + } + + /* Unable to set crop parameters; there is apparently no equivalent + for VIDIOC_S_CROP */ + + v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) { + id = sd->grp_id; + if (id >= ARRAY_SIZE(pvr2_module_update_functions)) continue; + fp = pvr2_module_update_functions[id]; + if (!fp) continue; + (*fp)(hdw, sd); + } + + if (hdw->tuner_signal_stale || hdw->cropcap_stale) { + pvr2_hdw_status_poll(hdw); + } +} + + /* Figure out if we need to commit control changes. If so, mark internal state flags to indicate this fact and return true. Otherwise do nothing else and return false. */ @@ -2686,7 +3047,7 @@ static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw) unsigned int idx; struct pvr2_ctrl *cptr; int value; - int commit_flag = 0; + int commit_flag = hdw->force_dirty; char buf[100]; unsigned int bcnt,ccnt; @@ -2842,18 +3203,6 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,VIDIOC_S_EXT_CTRLS); } - /* Scan i2c core at this point - before we clear all the dirty - bits. Various parts of the i2c core will notice dirty bits as - appropriate and arrange to broadcast or directly send updates to - the client drivers in order to keep everything in sync */ - pvr2_i2c_core_check_stale(hdw); - - for (idx = 0; idx < hdw->control_cnt; idx++) { - cptr = hdw->controls + idx; - if (!cptr->info->clear_dirty) continue; - cptr->info->clear_dirty(cptr); - } - if (hdw->active_stream_type != hdw->desired_stream_type) { /* Handle any side effects of stream config here */ hdw->active_stream_type = hdw->desired_stream_type; @@ -2873,8 +3222,16 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) } } - /* Now execute i2c core update */ - pvr2_i2c_core_sync(hdw); + /* Check and update state for all sub-devices. */ + pvr2_subdev_update(hdw); + + hdw->tuner_updated = 0; + hdw->force_dirty = 0; + for (idx = 0; idx < hdw->control_cnt; idx++) { + cptr = hdw->controls + idx; + if (!cptr->info->clear_dirty) continue; + cptr->info->clear_dirty(cptr); + } if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) && hdw->state_encoder_run) { @@ -2904,15 +3261,6 @@ int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw) } -static void pvr2_hdw_worker_i2c(struct work_struct *work) -{ - struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,worki2csync); - LOCK_TAKE(hdw->big_lock); do { - pvr2_i2c_core_sync(hdw); - } while (0); LOCK_GIVE(hdw->big_lock); -} - - static void pvr2_hdw_worker_poll(struct work_struct *work) { int fl = 0; @@ -2973,7 +3321,7 @@ int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw) { LOCK_TAKE(hdw->big_lock); do { - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); } while (0); LOCK_GIVE(hdw->big_lock); } @@ -2983,7 +3331,7 @@ static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw) if (!hdw->cropcap_stale) { return 0; } - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); if (hdw->cropcap_stale) { return -EIO; } @@ -3010,7 +3358,7 @@ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp) { LOCK_TAKE(hdw->big_lock); do { if (hdw->tuner_signal_stale) { - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); } memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner)); } while (0); LOCK_GIVE(hdw->big_lock); @@ -3029,11 +3377,8 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw) { int nr = pvr2_hdw_get_unit_number(hdw); LOCK_TAKE(hdw->big_lock); do { - hdw->log_requested = !0; printk(KERN_INFO "pvrusb2: ================= START STATUS CARD #%d =================\n", nr); - pvr2_i2c_core_check_stale(hdw); - hdw->log_requested = 0; - pvr2_i2c_core_sync(hdw); + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, log_status); pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:"); cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2"); pvr2_hdw_state_log_state(hdw); @@ -3716,22 +4061,16 @@ int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw) int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) { - if (!hdw->decoder_ctrl) { - pvr2_trace(PVR2_TRACE_INIT, - "Unable to reset decoder: nothing attached"); - return -ENOTTY; - } - - if (!hdw->decoder_ctrl->force_reset) { - pvr2_trace(PVR2_TRACE_INIT, - "Unable to reset decoder: not implemented"); - return -ENOTTY; - } - pvr2_trace(PVR2_TRACE_INIT, "Requesting decoder reset"); - hdw->decoder_ctrl->force_reset(hdw->decoder_ctrl->ctxt); - return 0; + if (hdw->decoder_client_id) { + v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, + core, reset, 0); + return 0; + } + pvr2_trace(PVR2_TRACE_INIT, + "Unable to reset decoder: nothing attached"); + return -ENOTTY; } @@ -4476,6 +4815,79 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, } +/* Generate report containing info about attached sub-devices and attached + i2c clients, including an indication of which attached i2c clients are + actually sub-devices. */ +static unsigned int pvr2_hdw_report_clients(struct pvr2_hdw *hdw, + char *buf, unsigned int acnt) +{ + struct v4l2_subdev *sd; + unsigned int tcnt = 0; + unsigned int ccnt; + struct i2c_client *client; + struct list_head *item; + void *cd; + const char *p; + unsigned int id; + + ccnt = scnprintf(buf, acnt, "Associated v4l2-subdev drivers:"); + tcnt += ccnt; + v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) { + id = sd->grp_id; + p = NULL; + if (id < ARRAY_SIZE(module_names)) p = module_names[id]; + if (p) { + ccnt = scnprintf(buf + tcnt, acnt - tcnt, " %s", p); + tcnt += ccnt; + } else { + ccnt = scnprintf(buf + tcnt, acnt - tcnt, + " (unknown id=%u)", id); + tcnt += ccnt; + } + } + ccnt = scnprintf(buf + tcnt, acnt - tcnt, "\n"); + tcnt += ccnt; + + ccnt = scnprintf(buf + tcnt, acnt - tcnt, "I2C clients:\n"); + tcnt += ccnt; + + mutex_lock(&hdw->i2c_adap.clist_lock); + list_for_each(item, &hdw->i2c_adap.clients) { + client = list_entry(item, struct i2c_client, list); + ccnt = scnprintf(buf + tcnt, acnt - tcnt, + " %s: i2c=%02x", client->name, client->addr); + tcnt += ccnt; + cd = i2c_get_clientdata(client); + v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) { + if (cd == sd) { + id = sd->grp_id; + p = NULL; + if (id < ARRAY_SIZE(module_names)) { + p = module_names[id]; + } + if (p) { + ccnt = scnprintf(buf + tcnt, + acnt - tcnt, + " subdev=%s", p); + tcnt += ccnt; + } else { + ccnt = scnprintf(buf + tcnt, + acnt - tcnt, + " subdev= id %u)", + id); + tcnt += ccnt; + } + break; + } + } + ccnt = scnprintf(buf + tcnt, acnt - tcnt, "\n"); + tcnt += ccnt; + } + mutex_unlock(&hdw->i2c_adap.clist_lock); + return tcnt; +} + + unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, char *buf,unsigned int acnt) { @@ -4490,6 +4902,8 @@ unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, buf[0] = '\n'; ccnt = 1; bcnt += ccnt; acnt -= ccnt; buf += ccnt; } + ccnt = pvr2_hdw_report_clients(hdw, buf, acnt); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; LOCK_GIVE(hdw->big_lock); return bcnt; } @@ -4497,14 +4911,25 @@ unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw) { - char buf[128]; - unsigned int idx,ccnt; + char buf[256]; + unsigned int idx, ccnt; + unsigned int lcnt, ucnt; for (idx = 0; ; idx++) { ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf)); if (!ccnt) break; printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf); } + ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf)); + ucnt = 0; + while (ucnt < ccnt) { + lcnt = 0; + while ((lcnt + ucnt < ccnt) && (buf[lcnt + ucnt] != '\n')) { + lcnt++; + } + printk(KERN_INFO "%s %.*s\n", hdw->name, lcnt, buf + ucnt); + ucnt += lcnt + 1; + } } @@ -4641,6 +5066,30 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val) } +void pvr2_hdw_status_poll(struct pvr2_hdw *hdw) +{ + struct v4l2_tuner *vtp = &hdw->tuner_signal_info; + memset(vtp, 0, sizeof(*vtp)); + hdw->tuner_signal_stale = 0; + /* Note: There apparently is no replacement for VIDIOC_CROPCAP + using v4l2-subdev - therefore we can't support that AT ALL right + now. (Of course, no sub-drivers seem to implement it either. + But now it's a a chicken and egg problem...) */ + v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, + &hdw->tuner_signal_info); + pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll" + " type=%u strength=%u audio=0x%x cap=0x%x" + " low=%u hi=%u", + vtp->type, + vtp->signal, vtp->rxsubchans, vtp->capability, + vtp->rangelow, vtp->rangehigh); + + /* We have to do this to avoid getting into constant polling if + there's nobody to answer a poll of cropcap info. */ + hdw->cropcap_stale = 0; +} + + unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw) { return hdw->input_avail_mask; @@ -4736,7 +5185,6 @@ int pvr2_hdw_register_access(struct pvr2_hdw *hdw, int setFl, u64 *val_ptr) { #ifdef CONFIG_VIDEO_ADV_DEBUG - struct pvr2_i2c_client *cp; struct v4l2_dbg_register req; int stat = 0; int okFl = 0; @@ -4746,21 +5194,9 @@ int pvr2_hdw_register_access(struct pvr2_hdw *hdw, req.match = *match; req.reg = reg_id; if (setFl) req.val = *val_ptr; - mutex_lock(&hdw->i2c_list_lock); do { - list_for_each_entry(cp, &hdw->i2c_clients, list) { - if (!v4l2_chip_match_i2c_client( - cp->client, - &req.match)) { - continue; - } - stat = pvr2_i2c_client_cmd( - cp,(setFl ? VIDIOC_DBG_S_REGISTER : - VIDIOC_DBG_G_REGISTER),&req); - if (!setFl) *val_ptr = req.val; - okFl = !0; - break; - } - } while (0); mutex_unlock(&hdw->i2c_list_lock); + /* It would be nice to know if a sub-device answered the request */ + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, g_register, &req); + if (!setFl) *val_ptr = req.val; if (okFl) { return stat; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 1b4fec3..7b69405 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -132,6 +132,9 @@ unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *); /* Retrieve bus location info of device */ const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *); +/* Retrieve per-instance string identifier for this specific device */ +const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *); + /* Called when hardware has been unplugged */ void pvr2_hdw_disconnect(struct pvr2_hdw *); @@ -236,8 +239,7 @@ void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *, enum pvr2_v4l_type index,int); /* Direct read/write access to chip's registers: - match_type - how to interpret match_chip (e.g. driver ID, i2c address) - match_chip - chip match value (e.g. I2C_DRIVERD_xxxx) + match - specify criteria to identify target chip (this is a v4l dbg struct) reg_id - register number to access setFl - true to set the register, false to read it val_ptr - storage location for source / result. */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c deleted file mode 100644 index 94a4771..0000000 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely <isely@pobox.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include <linux/kernel.h> -#include "pvrusb2-i2c-core.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include "pvrusb2-i2c-cmd-v4l2.h" -#include "pvrusb2-audio.h" -#include "pvrusb2-tuner.h" -#include "pvrusb2-video-v4l.h" -#include "pvrusb2-cx2584x-v4l.h" -#include "pvrusb2-wm8775.h" - -#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) - -#define OP_STANDARD 0 -#define OP_AUDIOMODE 1 -#define OP_BCSH 2 -#define OP_VOLUME 3 -#define OP_FREQ 4 -#define OP_AUDIORATE 5 -#define OP_CROP 6 -#define OP_SIZE 7 -#define OP_LOG 8 - -static const struct pvr2_i2c_op * const ops[] = { - [OP_STANDARD] = &pvr2_i2c_op_v4l2_standard, - [OP_AUDIOMODE] = &pvr2_i2c_op_v4l2_audiomode, - [OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh, - [OP_VOLUME] = &pvr2_i2c_op_v4l2_volume, - [OP_FREQ] = &pvr2_i2c_op_v4l2_frequency, - [OP_CROP] = &pvr2_i2c_op_v4l2_crop, - [OP_SIZE] = &pvr2_i2c_op_v4l2_size, - [OP_LOG] = &pvr2_i2c_op_v4l2_log, -}; - -void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) -{ - int id; - id = cp->client->driver->id; - cp->ctl_mask = ((1 << OP_STANDARD) | - (1 << OP_AUDIOMODE) | - (1 << OP_BCSH) | - (1 << OP_VOLUME) | - (1 << OP_FREQ) | - (1 << OP_CROP) | - (1 << OP_SIZE) | - (1 << OP_LOG)); - cp->status_poll = pvr2_v4l2_cmd_status_poll; - - if (id == I2C_DRIVERID_MSP3400) { - if (pvr2_i2c_msp3400_setup(hdw,cp)) { - return; - } - } - if (id == I2C_DRIVERID_TUNER) { - if (pvr2_i2c_tuner_setup(hdw,cp)) { - return; - } - } - if (id == I2C_DRIVERID_CX25840) { - if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) { - return; - } - } - if (id == I2C_DRIVERID_WM8775) { - if (pvr2_i2c_wm8775_setup(hdw,cp)) { - return; - } - } - if (id == I2C_DRIVERID_SAA711X) { - if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) { - return; - } - } -} - - -const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx) -{ - if (idx >= ARRAY_SIZE(ops)) - return NULL; - return ops[idx]; -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c deleted file mode 100644 index 16bb119..0000000 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ /dev/null @@ -1,322 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely <isely@pobox.com> - * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pvrusb2-i2c-cmd-v4l2.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include <linux/videodev2.h> -#include <media/v4l2-common.h> - -static void set_standard(struct pvr2_hdw *hdw) -{ - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_standard"); - - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - pvr2_i2c_core_cmd(hdw,AUDC_SET_RADIO,NULL); - } else { - v4l2_std_id vs; - vs = hdw->std_mask_cur; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs); - } - hdw->tuner_signal_stale = !0; - hdw->cropcap_stale = !0; -} - - -static int check_standard(struct pvr2_hdw *hdw) -{ - return (hdw->input_dirty != 0) || (hdw->std_dirty != 0); -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard = { - .check = check_standard, - .update = set_standard, - .name = "v4l2_standard", -}; - - -static void set_bcsh(struct pvr2_hdw *hdw) -{ - struct v4l2_control ctrl; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_bcsh" - " b=%d c=%d s=%d h=%d", - hdw->brightness_val,hdw->contrast_val, - hdw->saturation_val,hdw->hue_val); - memset(&ctrl,0,sizeof(ctrl)); - ctrl.id = V4L2_CID_BRIGHTNESS; - ctrl.value = hdw->brightness_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_CONTRAST; - ctrl.value = hdw->contrast_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_SATURATION; - ctrl.value = hdw->saturation_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_HUE; - ctrl.value = hdw->hue_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); -} - - -static int check_bcsh(struct pvr2_hdw *hdw) -{ - return (hdw->brightness_dirty || - hdw->contrast_dirty || - hdw->saturation_dirty || - hdw->hue_dirty); -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh = { - .check = check_bcsh, - .update = set_bcsh, - .name = "v4l2_bcsh", -}; - - -static void set_volume(struct pvr2_hdw *hdw) -{ - struct v4l2_control ctrl; - pvr2_trace(PVR2_TRACE_CHIPS, - "i2c v4l2 set_volume" - "(vol=%d bal=%d bas=%d treb=%d mute=%d)", - hdw->volume_val, - hdw->balance_val, - hdw->bass_val, - hdw->treble_val, - hdw->mute_val); - memset(&ctrl,0,sizeof(ctrl)); - ctrl.id = V4L2_CID_AUDIO_MUTE; - ctrl.value = hdw->mute_val ? 1 : 0; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_AUDIO_VOLUME; - ctrl.value = hdw->volume_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_AUDIO_BALANCE; - ctrl.value = hdw->balance_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_AUDIO_BASS; - ctrl.value = hdw->bass_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_AUDIO_TREBLE; - ctrl.value = hdw->treble_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); -} - - -static int check_volume(struct pvr2_hdw *hdw) -{ - return (hdw->volume_dirty || - hdw->balance_dirty || - hdw->bass_dirty || - hdw->treble_dirty || - hdw->mute_dirty); -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume = { - .check = check_volume, - .update = set_volume, - .name = "v4l2_volume", -}; - - -static void set_audiomode(struct pvr2_hdw *hdw) -{ - struct v4l2_tuner vt; - memset(&vt,0,sizeof(vt)); - vt.audmode = hdw->audiomode_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_TUNER,&vt); -} - - -static int check_audiomode(struct pvr2_hdw *hdw) -{ - return (hdw->input_dirty || - hdw->audiomode_dirty); -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode = { - .check = check_audiomode, - .update = set_audiomode, - .name = "v4l2_audiomode", -}; - - -static void set_frequency(struct pvr2_hdw *hdw) -{ - unsigned long fv; - struct v4l2_frequency freq; - fv = pvr2_hdw_get_cur_freq(hdw); - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv); - if (hdw->tuner_signal_stale) { - pvr2_i2c_core_status_poll(hdw); - } - memset(&freq,0,sizeof(freq)); - if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { - // ((fv * 1000) / 62500) - freq.frequency = (fv * 2) / 125; - } else { - freq.frequency = fv / 62500; - } - /* tuner-core currently doesn't seem to care about this, but - let's set it anyway for completeness. */ - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - freq.type = V4L2_TUNER_RADIO; - } else { - freq.type = V4L2_TUNER_ANALOG_TV; - } - freq.tuner = 0; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_FREQUENCY,&freq); -} - - -static int check_frequency(struct pvr2_hdw *hdw) -{ - return hdw->freqDirty != 0; -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency = { - .check = check_frequency, - .update = set_frequency, - .name = "v4l2_freq", -}; - - -static void set_size(struct pvr2_hdw *hdw) -{ - struct v4l2_format fmt; - - memset(&fmt,0,sizeof(fmt)); - - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = hdw->res_hor_val; - fmt.fmt.pix.height = hdw->res_ver_val; - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_size(%dx%d)", - fmt.fmt.pix.width,fmt.fmt.pix.height); - - pvr2_i2c_core_cmd(hdw,VIDIOC_S_FMT,&fmt); -} - - -static int check_size(struct pvr2_hdw *hdw) -{ - return (hdw->res_hor_dirty || hdw->res_ver_dirty); -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size = { - .check = check_size, - .update = set_size, - .name = "v4l2_size", -}; - - -static void set_crop(struct pvr2_hdw *hdw) -{ - struct v4l2_crop crop; - - memset(&crop, 0, sizeof crop); - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - crop.c.left = hdw->cropl_val; - crop.c.top = hdw->cropt_val; - crop.c.height = hdw->croph_val; - crop.c.width = hdw->cropw_val; - - pvr2_trace(PVR2_TRACE_CHIPS, - "i2c v4l2 set_crop crop=%d:%d:%d:%d", - crop.c.width, crop.c.height, crop.c.left, crop.c.top); - - pvr2_i2c_core_cmd(hdw, VIDIOC_S_CROP, &crop); -} - -static int check_crop(struct pvr2_hdw *hdw) -{ - return (hdw->cropl_dirty || hdw->cropt_dirty || - hdw->cropw_dirty || hdw->croph_dirty); -} - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop = { - .check = check_crop, - .update = set_crop, - .name = "v4l2_crop", -}; - - -static void do_log(struct pvr2_hdw *hdw) -{ - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 do_log()"); - pvr2_i2c_core_cmd(hdw,VIDIOC_LOG_STATUS,NULL); - -} - - -static int check_log(struct pvr2_hdw *hdw) -{ - return hdw->log_requested != 0; -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log = { - .check = check_log, - .update = do_log, - .name = "v4l2_log", -}; - - -void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl) -{ - pvr2_i2c_client_cmd(cp, - (fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),NULL); -} - - -void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *cp) -{ - int stat; - struct pvr2_hdw *hdw = cp->hdw; - if (hdw->cropcap_stale) { - hdw->cropcap_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - stat = pvr2_i2c_client_cmd(cp, VIDIOC_CROPCAP, - &hdw->cropcap_info); - if (stat == 0) { - /* Check was successful, so the data is no - longer considered stale. */ - hdw->cropcap_stale = 0; - } - } - pvr2_i2c_client_cmd(cp, VIDIOC_G_TUNER, &hdw->tuner_signal_info); -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h deleted file mode 100644 index eb744a2..0000000 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely <isely@pobox.com> - * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __PVRUSB2_CMD_V4L2_H -#define __PVRUSB2_CMD_V4L2_H - -#include "pvrusb2-i2c-core.h" - -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_radio; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log; - -void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *,int); -void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *); - -#endif /* __PVRUSB2_CMD_V4L2_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index d6a3540..9464862 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -18,6 +18,7 @@ * */ +#include <linux/i2c.h> #include "pvrusb2-i2c-core.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" @@ -29,8 +30,7 @@ /* This module attempts to implement a compliant I2C adapter for the pvrusb2 - device. By doing this we can then make use of existing functionality in - V4L (e.g. tuner.c) rather than rolling our own. + device. */ @@ -42,10 +42,6 @@ static int ir_mode[PVR_NUM] = { [0 ... PVR_NUM-1] = 1 }; module_param_array(ir_mode, int, NULL, 0444); MODULE_PARM_DESC(ir_mode,"specify: 0=disable IR reception, 1=normal IR"); -static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp, - unsigned int detail, - char *buf,unsigned int maxlen); - static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ u8 i2c_addr, /* I2C address we're talking to */ u8 *data, /* Data to write */ @@ -524,414 +520,13 @@ static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; } -static int pvr2_i2c_core_singleton(struct i2c_client *cp, - unsigned int cmd,void *arg) -{ - int stat; - if (!cp) return -EINVAL; - if (!(cp->driver)) return -EINVAL; - if (!(cp->driver->command)) return -EINVAL; - if (!try_module_get(cp->driver->driver.owner)) return -EAGAIN; - stat = cp->driver->command(cp,cmd,arg); - module_put(cp->driver->driver.owner); - return stat; -} - -int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg) -{ - int stat; - if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { - char buf[100]; - unsigned int cnt; - cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, - buf,sizeof(buf)); - pvr2_trace(PVR2_TRACE_I2C_CMD, - "i2c COMMAND (code=%u 0x%x) to %.*s", - cmd,cmd,cnt,buf); - } - stat = pvr2_i2c_core_singleton(cp->client,cmd,arg); - if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { - char buf[100]; - unsigned int cnt; - cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, - buf,sizeof(buf)); - pvr2_trace(PVR2_TRACE_I2C_CMD, - "i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat); - } - return stat; -} - -int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) -{ - struct pvr2_i2c_client *cp, *ncp; - int stat = -EINVAL; - - if (!hdw) return stat; - - mutex_lock(&hdw->i2c_list_lock); - list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) { - if (!cp->recv_enable) continue; - mutex_unlock(&hdw->i2c_list_lock); - stat = pvr2_i2c_client_cmd(cp,cmd,arg); - mutex_lock(&hdw->i2c_list_lock); - } - mutex_unlock(&hdw->i2c_list_lock); - return stat; -} - - -static int handler_check(struct pvr2_i2c_client *cp) -{ - struct pvr2_i2c_handler *hp = cp->handler; - if (!hp) return 0; - if (!hp->func_table->check) return 0; - return hp->func_table->check(hp->func_data) != 0; -} - -#define BUFSIZE 500 - - -void pvr2_i2c_core_status_poll(struct pvr2_hdw *hdw) -{ - struct pvr2_i2c_client *cp; - mutex_lock(&hdw->i2c_list_lock); do { - struct v4l2_tuner *vtp = &hdw->tuner_signal_info; - memset(vtp,0,sizeof(*vtp)); - list_for_each_entry(cp, &hdw->i2c_clients, list) { - if (!cp->detected_flag) continue; - if (!cp->status_poll) continue; - cp->status_poll(cp); - } - hdw->tuner_signal_stale = 0; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c status poll" - " type=%u strength=%u audio=0x%x cap=0x%x" - " low=%u hi=%u", - vtp->type, - vtp->signal,vtp->rxsubchans,vtp->capability, - vtp->rangelow,vtp->rangehigh); - } while (0); mutex_unlock(&hdw->i2c_list_lock); -} - - -/* Issue various I2C operations to bring chip-level drivers into sync with - state stored in this driver. */ -void pvr2_i2c_core_sync(struct pvr2_hdw *hdw) -{ - unsigned long msk; - unsigned int idx; - struct pvr2_i2c_client *cp, *ncp; - - if (!hdw->i2c_linked) return; - if (!(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL)) { - return; - } - mutex_lock(&hdw->i2c_list_lock); do { - pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync BEGIN"); - if (hdw->i2c_pend_types & PVR2_I2C_PEND_DETECT) { - /* One or more I2C clients have attached since we - last synced. So scan the list and identify the - new clients. */ - char *buf; - unsigned int cnt; - unsigned long amask = 0; - buf = kmalloc(BUFSIZE,GFP_KERNEL); - pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_DETECT"); - hdw->i2c_pend_types &= ~PVR2_I2C_PEND_DETECT; - list_for_each_entry(cp, &hdw->i2c_clients, list) { - if (!cp->detected_flag) { - cp->ctl_mask = 0; - pvr2_i2c_probe(hdw,cp); - cp->detected_flag = !0; - msk = cp->ctl_mask; - cnt = 0; - if (buf) { - cnt = pvr2_i2c_client_describe( - cp, - PVR2_I2C_DETAIL_ALL, - buf,BUFSIZE); - } - trace_i2c("Probed: %.*s",cnt,buf); - if (handler_check(cp)) { - hdw->i2c_pend_types |= - PVR2_I2C_PEND_CLIENT; - } - cp->pend_mask = msk; - hdw->i2c_pend_mask |= msk; - hdw->i2c_pend_types |= - PVR2_I2C_PEND_REFRESH; - } - amask |= cp->ctl_mask; - } - hdw->i2c_active_mask = amask; - if (buf) kfree(buf); - } - if (hdw->i2c_pend_types & PVR2_I2C_PEND_STALE) { - /* Need to do one or more global updates. Arrange - for this to happen. */ - unsigned long m2; - pvr2_trace(PVR2_TRACE_I2C_CORE, - "i2c: PEND_STALE (0x%lx)", - hdw->i2c_stale_mask); - hdw->i2c_pend_types &= ~PVR2_I2C_PEND_STALE; - list_for_each_entry(cp, &hdw->i2c_clients, list) { - m2 = hdw->i2c_stale_mask; - m2 &= cp->ctl_mask; - m2 &= ~cp->pend_mask; - if (m2) { - pvr2_trace(PVR2_TRACE_I2C_CORE, - "i2c: cp=%p setting 0x%lx", - cp,m2); - cp->pend_mask |= m2; - } - } - hdw->i2c_pend_mask |= hdw->i2c_stale_mask; - hdw->i2c_stale_mask = 0; - hdw->i2c_pend_types |= PVR2_I2C_PEND_REFRESH; - } - if (hdw->i2c_pend_types & PVR2_I2C_PEND_CLIENT) { - /* One or more client handlers are asking for an - update. Run through the list of known clients - and update each one. */ - pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_CLIENT"); - hdw->i2c_pend_types &= ~PVR2_I2C_PEND_CLIENT; - list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, - list) { - if (!cp->handler) continue; - if (!cp->handler->func_table->update) continue; - pvr2_trace(PVR2_TRACE_I2C_CORE, - "i2c: cp=%p update",cp); - mutex_unlock(&hdw->i2c_list_lock); - cp->handler->func_table->update( - cp->handler->func_data); - mutex_lock(&hdw->i2c_list_lock); - /* If client's update function set some - additional pending bits, account for that - here. */ - if (cp->pend_mask & ~hdw->i2c_pend_mask) { - hdw->i2c_pend_mask |= cp->pend_mask; - hdw->i2c_pend_types |= - PVR2_I2C_PEND_REFRESH; - } - } - } - if (hdw->i2c_pend_types & PVR2_I2C_PEND_REFRESH) { - const struct pvr2_i2c_op *opf; - unsigned long pm; - /* Some actual updates are pending. Walk through - each update type and perform it. */ - pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_REFRESH" - " (0x%lx)",hdw->i2c_pend_mask); - hdw->i2c_pend_types &= ~PVR2_I2C_PEND_REFRESH; - pm = hdw->i2c_pend_mask; - hdw->i2c_pend_mask = 0; - for (idx = 0, msk = 1; pm; idx++, msk <<= 1) { - if (!(pm & msk)) continue; - pm &= ~msk; - list_for_each_entry(cp, &hdw->i2c_clients, - list) { - if (cp->pend_mask & msk) { - cp->pend_mask &= ~msk; - cp->recv_enable = !0; - } else { - cp->recv_enable = 0; - } - } - opf = pvr2_i2c_get_op(idx); - if (!opf) continue; - mutex_unlock(&hdw->i2c_list_lock); - opf->update(hdw); - mutex_lock(&hdw->i2c_list_lock); - } - } - pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync END"); - } while (0); mutex_unlock(&hdw->i2c_list_lock); -} - -int pvr2_i2c_core_check_stale(struct pvr2_hdw *hdw) -{ - unsigned long msk,sm,pm; - unsigned int idx; - const struct pvr2_i2c_op *opf; - struct pvr2_i2c_client *cp; - unsigned int pt = 0; - - pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale BEGIN"); - - pm = hdw->i2c_active_mask; - sm = 0; - for (idx = 0, msk = 1; pm; idx++, msk <<= 1) { - if (!(msk & pm)) continue; - pm &= ~msk; - opf = pvr2_i2c_get_op(idx); - if (!opf) continue; - if (opf->check(hdw)) { - sm |= msk; - } - } - if (sm) pt |= PVR2_I2C_PEND_STALE; - - list_for_each_entry(cp, &hdw->i2c_clients, list) - if (handler_check(cp)) - pt |= PVR2_I2C_PEND_CLIENT; - - if (pt) { - mutex_lock(&hdw->i2c_list_lock); do { - hdw->i2c_pend_types |= pt; - hdw->i2c_stale_mask |= sm; - hdw->i2c_pend_mask |= hdw->i2c_stale_mask; - } while (0); mutex_unlock(&hdw->i2c_list_lock); - } - - pvr2_trace(PVR2_TRACE_I2C_CORE, - "i2c: types=0x%x stale=0x%lx pend=0x%lx", - hdw->i2c_pend_types, - hdw->i2c_stale_mask, - hdw->i2c_pend_mask); - pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale END"); - - return (hdw->i2c_pend_types & PVR2_I2C_PEND_ALL) != 0; -} - -static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp, - unsigned int detail, - char *buf,unsigned int maxlen) -{ - unsigned int ccnt,bcnt; - int spcfl = 0; - const struct pvr2_i2c_op *opf; - - ccnt = 0; - if (detail & PVR2_I2C_DETAIL_DEBUG) { - bcnt = scnprintf(buf,maxlen, - "ctxt=%p ctl_mask=0x%lx", - cp,cp->ctl_mask); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - spcfl = !0; - } - bcnt = scnprintf(buf,maxlen, - "%s%s @ 0x%x", - (spcfl ? " " : ""), - cp->client->name, - cp->client->addr); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - if ((detail & PVR2_I2C_DETAIL_HANDLER) && - cp->handler && cp->handler->func_table->describe) { - bcnt = scnprintf(buf,maxlen," ("); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - bcnt = cp->handler->func_table->describe( - cp->handler->func_data,buf,maxlen); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - bcnt = scnprintf(buf,maxlen,")"); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - } - if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) { - unsigned int idx; - unsigned long msk,sm; - - bcnt = scnprintf(buf,maxlen," ["); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - sm = 0; - spcfl = 0; - for (idx = 0, msk = 1; msk; idx++, msk <<= 1) { - if (!(cp->ctl_mask & msk)) continue; - opf = pvr2_i2c_get_op(idx); - if (opf) { - bcnt = scnprintf(buf,maxlen,"%s%s", - spcfl ? " " : "", - opf->name); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - spcfl = !0; - } else { - sm |= msk; - } - } - if (sm) { - bcnt = scnprintf(buf,maxlen,"%s%lx", - idx != 0 ? " " : "",sm); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - } - bcnt = scnprintf(buf,maxlen,"]"); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - } - return ccnt; -} - -unsigned int pvr2_i2c_report(struct pvr2_hdw *hdw, - char *buf,unsigned int maxlen) -{ - unsigned int ccnt,bcnt; - struct pvr2_i2c_client *cp; - ccnt = 0; - mutex_lock(&hdw->i2c_list_lock); do { - list_for_each_entry(cp, &hdw->i2c_clients, list) { - bcnt = pvr2_i2c_client_describe( - cp, - (PVR2_I2C_DETAIL_HANDLER| - PVR2_I2C_DETAIL_CTLMASK), - buf,maxlen); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - bcnt = scnprintf(buf,maxlen,"\n"); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - } - } while (0); mutex_unlock(&hdw->i2c_list_lock); - return ccnt; -} - static int pvr2_i2c_attach_inform(struct i2c_client *client) { - struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); - struct pvr2_i2c_client *cp; - int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL); - cp = kzalloc(sizeof(*cp),GFP_KERNEL); - trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]", - client->name, - client->addr,cp); - if (!cp) return -ENOMEM; - cp->hdw = hdw; - INIT_LIST_HEAD(&cp->list); - cp->client = client; - mutex_lock(&hdw->i2c_list_lock); do { - hdw->cropcap_stale = !0; - list_add_tail(&cp->list,&hdw->i2c_clients); - hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT; - } while (0); mutex_unlock(&hdw->i2c_list_lock); - if (fl) queue_work(hdw->workqueue,&hdw->worki2csync); return 0; } static int pvr2_i2c_detach_inform(struct i2c_client *client) { - struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); - struct pvr2_i2c_client *cp, *ncp; - unsigned long amask = 0; - int foundfl = 0; - mutex_lock(&hdw->i2c_list_lock); do { - hdw->cropcap_stale = !0; - list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) { - if (cp->client == client) { - trace_i2c("pvr2_i2c_detach" - " [client=%s @ 0x%x ctxt=%p]", - client->name, - client->addr,cp); - if (cp->handler && - cp->handler->func_table->detach) { - cp->handler->func_table->detach( - cp->handler->func_data); - } - list_del(&cp->list); - kfree(cp); - foundfl = !0; - continue; - } - amask |= cp->ctl_mask; - } - hdw->i2c_active_mask = amask; - } while (0); mutex_unlock(&hdw->i2c_list_lock); - if (!foundfl) { - trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x ctxt=<unknown>]", - client->name, - client->addr); - } return 0; } @@ -942,7 +537,7 @@ static struct i2c_algorithm pvr2_i2c_algo_template = { static struct i2c_adapter pvr2_i2c_adap_template = { .owner = THIS_MODULE, - .class = I2C_CLASS_TV_ANALOG, + .class = 0, .id = I2C_HW_B_BT848, .client_register = pvr2_i2c_attach_inform, .client_unregister = pvr2_i2c_detach_inform, @@ -1009,12 +604,8 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev; hdw->i2c_adap.algo = &hdw->i2c_algo; hdw->i2c_adap.algo_data = hdw; - hdw->i2c_pend_mask = 0; - hdw->i2c_stale_mask = 0; - hdw->i2c_active_mask = 0; - INIT_LIST_HEAD(&hdw->i2c_clients); - mutex_init(&hdw->i2c_list_lock); hdw->i2c_linked = !0; + i2c_set_adapdata(&hdw->i2c_adap, &hdw->v4l2_dev); i2c_add_adapter(&hdw->i2c_adap); if (hdw->i2c_func[0x18] == i2c_24xxx_ir) { /* Probe for a different type of IR receiver on this diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h index 6ef7a1c..6a757692 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h @@ -20,68 +20,13 @@ #ifndef __PVRUSB2_I2C_CORE_H #define __PVRUSB2_I2C_CORE_H -#include <linux/list.h> -#include <linux/i2c.h> - struct pvr2_hdw; -struct pvr2_i2c_client; -struct pvr2_i2c_handler; -struct pvr2_i2c_handler_functions; -struct pvr2_i2c_op; -struct pvr2_i2c_op_functions; - -struct pvr2_i2c_client { - struct i2c_client *client; - struct pvr2_i2c_handler *handler; - struct list_head list; - struct pvr2_hdw *hdw; - int detected_flag; - int recv_enable; - unsigned long pend_mask; - unsigned long ctl_mask; - void (*status_poll)(struct pvr2_i2c_client *); -}; - -struct pvr2_i2c_handler { - void *func_data; - const struct pvr2_i2c_handler_functions *func_table; -}; - -struct pvr2_i2c_handler_functions { - void (*detach)(void *); - int (*check)(void *); - void (*update)(void *); - unsigned int (*describe)(void *,char *,unsigned int); -}; - -struct pvr2_i2c_op { - int (*check)(struct pvr2_hdw *); - void (*update)(struct pvr2_hdw *); - const char *name; -}; void pvr2_i2c_core_init(struct pvr2_hdw *); void pvr2_i2c_core_done(struct pvr2_hdw *); -int pvr2_i2c_client_cmd(struct pvr2_i2c_client *,unsigned int cmd,void *arg); -int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg); - -int pvr2_i2c_core_check_stale(struct pvr2_hdw *); -void pvr2_i2c_core_sync(struct pvr2_hdw *); -void pvr2_i2c_core_status_poll(struct pvr2_hdw *); -unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen); -#define PVR2_I2C_DETAIL_DEBUG 0x0001 -#define PVR2_I2C_DETAIL_HANDLER 0x0002 -#define PVR2_I2C_DETAIL_CTLMASK 0x0004 -#define PVR2_I2C_DETAIL_ALL (\ - PVR2_I2C_DETAIL_DEBUG |\ - PVR2_I2C_DETAIL_HANDLER |\ - PVR2_I2C_DETAIL_CTLMASK) - -void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *); -const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx); -#endif /* __PVRUSB2_I2C_CORE_H */ +#endif /* __PVRUSB2_I2C_ADAPTER_H */ /* diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c index 9b3c874..8689ddb 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -137,10 +137,10 @@ static int __init pvr_init(void) ret = usb_register(&pvr_driver); if (ret == 0) - printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":" + printk(KERN_INFO "pvrusb2: " DRIVER_VERSION ":" DRIVER_DESC "\n"); if (pvrusb2_debug) - printk(KERN_INFO KBUILD_MODNAME ": Debug mask is %d (0x%x)\n", + printk(KERN_INFO "pvrusb2: Debug mask is %d (0x%x)\n", pvrusb2_debug,pvrusb2_debug); pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete"); diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index e641cd9..e20ba1e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -627,16 +627,8 @@ static void class_dev_create(struct pvr2_sysfs *sfp, pvr2_sysfs_trace("Creating class_dev id=%p",class_dev); class_dev->class = &class_ptr->class; - if (pvr2_hdw_get_sn(sfp->channel.hdw)) { - dev_set_name(class_dev, "sn-%lu", - pvr2_hdw_get_sn(sfp->channel.hdw)); - } else if (pvr2_hdw_get_unit_number(sfp->channel.hdw) >= 0) { - dev_set_name(class_dev, "unit-%c", - pvr2_hdw_get_unit_number(sfp->channel.hdw) + 'a'); - } else { - kfree(class_dev); - return; - } + dev_set_name(class_dev, "%s", + pvr2_hdw_get_device_identifier(sfp->channel.hdw)); class_dev->parent = &usb_dev->dev; diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/drivers/media/video/pvrusb2/pvrusb2-tuner.c deleted file mode 100644 index 07775d1..0000000 --- a/drivers/media/video/pvrusb2/pvrusb2-tuner.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely <isely@pobox.com> - * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pvrusb2.h" -#include "pvrusb2-util.h" -#include "pvrusb2-tuner.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include <linux/videodev2.h> -#include <media/tuner.h> -#include <media/v4l2-common.h> - -struct pvr2_tuner_handler { - struct pvr2_hdw *hdw; - struct pvr2_i2c_client *client; - struct pvr2_i2c_handler i2c_handler; - int type_update_fl; -}; - - -static void set_type(struct pvr2_tuner_handler *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - struct tuner_setup setup; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c tuner set_type(%d)",hdw->tuner_type); - if (((int)(hdw->tuner_type)) < 0) return; - - setup.addr = ADDR_UNSET; - setup.type = hdw->tuner_type; - setup.mode_mask = T_RADIO | T_ANALOG_TV; - /* We may really want mode_mask to be T_ANALOG_TV for now */ - pvr2_i2c_client_cmd(ctxt->client,TUNER_SET_TYPE_ADDR,&setup); - ctxt->type_update_fl = 0; -} - - -static int tuner_check(struct pvr2_tuner_handler *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - if (hdw->tuner_updated) ctxt->type_update_fl = !0; - return ctxt->type_update_fl != 0; -} - - -static void tuner_update(struct pvr2_tuner_handler *ctxt) -{ - if (ctxt->type_update_fl) set_type(ctxt); -} - - -static void pvr2_tuner_detach(struct pvr2_tuner_handler *ctxt) -{ - ctxt->client->handler = NULL; - kfree(ctxt); -} - - -static unsigned int pvr2_tuner_describe(struct pvr2_tuner_handler *ctxt,char *buf,unsigned int cnt) -{ - return scnprintf(buf,cnt,"handler: pvrusb2-tuner"); -} - - -static const struct pvr2_i2c_handler_functions tuner_funcs = { - .detach = (void (*)(void *))pvr2_tuner_detach, - .check = (int (*)(void *))tuner_check, - .update = (void (*)(void *))tuner_update, - .describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_tuner_describe, -}; - - -int pvr2_i2c_tuner_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) -{ - struct pvr2_tuner_handler *ctxt; - if (cp->handler) return 0; - - ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); - if (!ctxt) return 0; - - ctxt->i2c_handler.func_data = ctxt; - ctxt->i2c_handler.func_table = &tuner_funcs; - ctxt->type_update_fl = !0; - ctxt->client = cp; - ctxt->hdw = hdw; - cp->handler = &ctxt->i2c_handler; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x tuner handler set up", - cp->client->addr); - return !0; -} - - - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 878fd52..9e0f2b0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -91,7 +91,7 @@ static struct v4l2_capability pvr_capability ={ .card = "Hauppauge WinTV pvr-usb2", .bus_info = "usb", .version = KERNEL_VERSION(0,8,0), - .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | + .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_READWRITE), .reserved = {0,0,0,0} @@ -952,10 +952,6 @@ static long pvr2_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { -/* Temporary hack : use ivtv api until a v4l2 one is available. */ -#define IVTV_IOC_G_CODEC 0xFFEE7703 -#define IVTV_IOC_S_CODEC 0xFFEE7704 - if (cmd == IVTV_IOC_G_CODEC || cmd == IVTV_IOC_S_CODEC) return 0; return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl); } @@ -1268,8 +1264,9 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, dip->minor_type = pvr2_v4l_type_video; nr_ptr = video_nr; if (!dip->stream) { - err("Failed to set up pvrusb2 v4l video dev" - " due to missing stream instance"); + pr_err(KBUILD_MODNAME + ": Failed to set up pvrusb2 v4l video dev" + " due to missing stream instance\n"); return; } break; @@ -1286,8 +1283,8 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, break; default: /* Bail out (this should be impossible) */ - err("Failed to set up pvrusb2 v4l dev" - " due to unrecognized config"); + pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev" + " due to unrecognized config\n"); return; } @@ -1303,7 +1300,8 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, dip->v4l_type, mindevnum) < 0) && (video_register_device(&dip->devbase, dip->v4l_type, -1) < 0)) { - err("Failed to register pvrusb2 v4l device"); + pr_err(KBUILD_MODNAME + ": Failed to register pvrusb2 v4l device\n"); } printk(KERN_INFO "pvrusb2: registered device %s%u [%s]\n", diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index 4059648..b3862f5 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -28,7 +28,7 @@ */ #include "pvrusb2-video-v4l.h" -#include "pvrusb2-i2c-cmd-v4l2.h" + #include "pvrusb2-hdw-internal.h" @@ -39,15 +39,6 @@ #include <linux/errno.h> #include <linux/slab.h> -struct pvr2_v4l_decoder { - struct pvr2_i2c_handler handler; - struct pvr2_decoder_ctrl ctrl; - struct pvr2_i2c_client *client; - struct pvr2_hdw *hdw; - unsigned long stale_mask; -}; - - struct routing_scheme { const int *def; unsigned int cnt; @@ -63,190 +54,51 @@ static const int routing_scheme0[] = { [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, }; +static const int routing_scheme1[] = { + [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, + [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, + [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE3, + [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, /* or SVIDEO0, it seems */ +}; + static const struct routing_scheme routing_schemes[] = { [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { .def = routing_scheme0, .cnt = ARRAY_SIZE(routing_scheme0), }, + [PVR2_ROUTING_SCHEME_ONAIR] = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), + }, }; -static void set_input(struct pvr2_v4l_decoder *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - struct v4l2_routing route; - const struct routing_scheme *sp; - unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val); - - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - route.input = sp->def[hdw->input_val]; - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "*** WARNING *** i2c v4l2 set_input:" - " Invalid routing scheme (%u) and/or input (%d)", - sid,hdw->input_val); - return; - } - - route.output = 0; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route); -} - - -static int check_input(struct pvr2_v4l_decoder *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->input_dirty != 0; -} - - -static void set_audio(struct pvr2_v4l_decoder *ctxt) -{ - u32 val; - struct pvr2_hdw *hdw = ctxt->hdw; - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_audio %d", - hdw->srate_val); - switch (hdw->srate_val) { - default: - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000: - val = 48000; - break; - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100: - val = 44100; - break; - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000: - val = 32000; - break; - } - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val); -} - - -static int check_audio(struct pvr2_v4l_decoder *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->srate_dirty != 0; -} - - -struct pvr2_v4l_decoder_ops { - void (*update)(struct pvr2_v4l_decoder *); - int (*check)(struct pvr2_v4l_decoder *); -}; - - -static const struct pvr2_v4l_decoder_ops decoder_ops[] = { - { .update = set_input, .check = check_input}, - { .update = set_audio, .check = check_audio}, -}; - - -static void decoder_detach(struct pvr2_v4l_decoder *ctxt) +void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) { - ctxt->client->handler = NULL; - pvr2_hdw_set_decoder(ctxt->hdw,NULL); - kfree(ctxt); -} - - -static int decoder_check(struct pvr2_v4l_decoder *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { - msk = 1 << idx; - if (ctxt->stale_mask & msk) continue; - if (decoder_ops[idx].check(ctxt)) { - ctxt->stale_mask |= msk; + if (hdw->input_dirty || hdw->force_dirty) { + struct v4l2_routing route; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", + hdw->input_val); + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != NULL) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + route.input = sp->def[hdw->input_val]; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev v4l2 set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; } + route.output = 0; + sd->ops->video->s_routing(sd, &route); } - return ctxt->stale_mask != 0; -} - - -static void decoder_update(struct pvr2_v4l_decoder *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { - msk = 1 << idx; - if (!(ctxt->stale_mask & msk)) continue; - ctxt->stale_mask &= ~msk; - decoder_ops[idx].update(ctxt); - } -} - - -static int decoder_detect(struct pvr2_i2c_client *cp) -{ - /* Attempt to query the decoder - let's see if it will answer */ - struct v4l2_tuner vt; - int ret; - - memset(&vt,0,sizeof(vt)); - ret = pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&vt); - return ret == 0; /* Return true if it answered */ -} - - -static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl) -{ - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 decoder_enable(%d)",fl); - pvr2_v4l2_cmd_stream(ctxt->client,fl); -} - - -static unsigned int decoder_describe(struct pvr2_v4l_decoder *ctxt,char *buf,unsigned int cnt) -{ - return scnprintf(buf,cnt,"handler: pvrusb2-video-v4l"); -} - - -static const struct pvr2_i2c_handler_functions hfuncs = { - .detach = (void (*)(void *))decoder_detach, - .check = (int (*)(void *))decoder_check, - .update = (void (*)(void *))decoder_update, - .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe, -}; - - -int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw, - struct pvr2_i2c_client *cp) -{ - struct pvr2_v4l_decoder *ctxt; - - if (hdw->decoder_ctrl) return 0; - if (cp->handler) return 0; - if (!decoder_detect(cp)) return 0; - - ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); - if (!ctxt) return 0; - - ctxt->handler.func_data = ctxt; - ctxt->handler.func_table = &hfuncs; - ctxt->ctrl.ctxt = ctxt; - ctxt->ctrl.detach = (void (*)(void *))decoder_detach; - ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; - ctxt->client = cp; - ctxt->hdw = hdw; - ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; - pvr2_hdw_set_decoder(hdw,&ctxt->ctrl); - cp->handler = &ctxt->handler; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up", - cp->client->addr); - return !0; } - - /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h index 4ff5b89..3b0bd5d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h +++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h @@ -32,11 +32,8 @@ */ - -#include "pvrusb2-i2c-core.h" - -int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); - +#include "pvrusb2-hdw-internal.h" +void pvr2_saa7115_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); #endif /* __PVRUSB2_VIDEO_V4L_H */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c index f6fcf0a..1670aa4 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c +++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -27,7 +27,6 @@ */ #include "pvrusb2-wm8775.h" -#include "pvrusb2-i2c-cmd-v4l2.h" #include "pvrusb2-hdw-internal.h" @@ -37,128 +36,31 @@ #include <linux/errno.h> #include <linux/slab.h> -struct pvr2_v4l_wm8775 { - struct pvr2_i2c_handler handler; - struct pvr2_i2c_client *client; - struct pvr2_hdw *hdw; - unsigned long stale_mask; -}; - - -static void set_input(struct pvr2_v4l_wm8775 *ctxt) -{ - struct v4l2_routing route; - struct pvr2_hdw *hdw = ctxt->hdw; - - memset(&route,0,sizeof(route)); - - switch(hdw->input_val) { - case PVR2_CVAL_INPUT_RADIO: - route.input = 1; - break; - default: - /* All other cases just use the second input */ - route.input = 2; - break; - } - pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d route=0x%x)", - hdw->input_val,route.input); - - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); -} - -static int check_input(struct pvr2_v4l_wm8775 *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->input_dirty != 0; -} - - -struct pvr2_v4l_wm8775_ops { - void (*update)(struct pvr2_v4l_wm8775 *); - int (*check)(struct pvr2_v4l_wm8775 *); -}; - - -static const struct pvr2_v4l_wm8775_ops wm8775_ops[] = { - { .update = set_input, .check = check_input}, -}; - - -static unsigned int wm8775_describe(struct pvr2_v4l_wm8775 *ctxt, - char *buf,unsigned int cnt) -{ - return scnprintf(buf,cnt,"handler: pvrusb2-wm8775"); -} - - -static void wm8775_detach(struct pvr2_v4l_wm8775 *ctxt) +void pvr2_wm8775_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) { - ctxt->client->handler = NULL; - kfree(ctxt); -} - - -static int wm8775_check(struct pvr2_v4l_wm8775 *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(wm8775_ops); idx++) { - msk = 1 << idx; - if (ctxt->stale_mask & msk) continue; - if (wm8775_ops[idx].check(ctxt)) { - ctxt->stale_mask |= msk; + if (hdw->input_dirty || hdw->force_dirty) { + struct v4l2_routing route; + + memset(&route, 0, sizeof(route)); + + switch (hdw->input_val) { + case PVR2_CVAL_INPUT_RADIO: + route.input = 1; + break; + default: + /* All other cases just use the second input */ + route.input = 2; + break; } - } - return ctxt->stale_mask != 0; -} - - -static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt) -{ - unsigned long msk; - unsigned int idx; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev wm8775" + " set_input(val=%d route=0x%x)", + hdw->input_val, route.input); - for (idx = 0; idx < ARRAY_SIZE(wm8775_ops); idx++) { - msk = 1 << idx; - if (!(ctxt->stale_mask & msk)) continue; - ctxt->stale_mask &= ~msk; - wm8775_ops[idx].update(ctxt); + sd->ops->audio->s_routing(sd, &route); } } -static const struct pvr2_i2c_handler_functions hfuncs = { - .detach = (void (*)(void *))wm8775_detach, - .check = (int (*)(void *))wm8775_check, - .update = (void (*)(void *))wm8775_update, - .describe = (unsigned int (*)(void *,char *,unsigned int))wm8775_describe, -}; - - -int pvr2_i2c_wm8775_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) -{ - struct pvr2_v4l_wm8775 *ctxt; - - if (cp->handler) return 0; - - ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); - if (!ctxt) return 0; - - ctxt->handler.func_data = ctxt; - ctxt->handler.func_table = &hfuncs; - ctxt->client = cp; - ctxt->hdw = hdw; - ctxt->stale_mask = (1 << ARRAY_SIZE(wm8775_ops)) - 1; - cp->handler = &ctxt->handler; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x wm8775 V4L2 handler set up", - cp->client->addr); - return !0; -} - - - /* Stuff for Emacs to see, in order to encourage consistent editing style: diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h index 8070909..0577bc7 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h +++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h @@ -34,9 +34,9 @@ -#include "pvrusb2-i2c-core.h" +#include "pvrusb2-hdw-internal.h" -int pvr2_i2c_wm8775_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); +void pvr2_wm8775_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); #endif /* __PVRUSB2_WM8775_H */ diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig index 7298cf2..8b9f0aa 100644 --- a/drivers/media/video/pwc/Kconfig +++ b/drivers/media/video/pwc/Kconfig @@ -35,3 +35,13 @@ config USB_PWC_DEBUG Say Y here in order to have the pwc driver generate verbose debugging messages. A special module options 'trace' is used to control the verbosity. + +config USB_PWC_INPUT_EVDEV + bool "USB Philips Cameras input events device support" + default y + depends on USB_PWC && INPUT + ---help--- + This option makes USB Philips cameras register the snapshot button as + an input device to report button events. + + If you are in doubt, say Y. diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 0d81018..7c542ca 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -53,6 +53,7 @@ - Xavier Roche: QuickCam Pro 4000 ID - Jens Knudsen: QuickCam Zoom ID - J. Debert: QuickCam for Notebooks ID + - Pham Thanh Nam: webcam snapshot button as an event input device */ #include <linux/errno.h> @@ -61,6 +62,9 @@ #include <linux/module.h> #include <linux/poll.h> #include <linux/slab.h> +#ifdef CONFIG_USB_PWC_INPUT_EVDEV +#include <linux/usb/input.h> +#endif #include <linux/vmalloc.h> #include <asm/io.h> @@ -586,6 +590,23 @@ static void pwc_frame_dumped(struct pwc_device *pdev) pdev->vframe_count); } +static void pwc_snapshot_button(struct pwc_device *pdev, int down) +{ + if (down) { + PWC_TRACE("Snapshot button pressed.\n"); + pdev->snapshot_button_status = 1; + } else { + PWC_TRACE("Snapshot button released.\n"); + } + +#ifdef CONFIG_USB_PWC_INPUT_EVDEV + if (pdev->button_dev) { + input_report_key(pdev->button_dev, BTN_0, down); + input_sync(pdev->button_dev); + } +#endif +} + static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_buf *fbuf) { int awake = 0; @@ -603,13 +624,7 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_ pdev->vframes_error++; } if ((ptr[0] ^ pdev->vmirror) & 0x01) { - if (ptr[0] & 0x01) { - pdev->snapshot_button_status = 1; - PWC_TRACE("Snapshot button pressed.\n"); - } - else { - PWC_TRACE("Snapshot button released.\n"); - } + pwc_snapshot_button(pdev, ptr[0] & 0x01); } if ((ptr[0] ^ pdev->vmirror) & 0x02) { if (ptr[0] & 0x02) @@ -633,12 +648,7 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_ else if (pdev->type == 740 || pdev->type == 720) { unsigned char *ptr = (unsigned char *)fbuf->data; if ((ptr[0] ^ pdev->vmirror) & 0x01) { - if (ptr[0] & 0x01) { - pdev->snapshot_button_status = 1; - PWC_TRACE("Snapshot button pressed.\n"); - } - else - PWC_TRACE("Snapshot button released.\n"); + pwc_snapshot_button(pdev, ptr[0] & 0x01); } pdev->vmirror = ptr[0] & 0x03; } @@ -1115,6 +1125,7 @@ static int pwc_video_open(struct file *file) } mutex_lock(&pdev->modlock); + pwc_construct(pdev); /* set min/max sizes correct */ if (!pdev->usb_init) { PWC_DEBUG_OPEN("Doing first time initialization.\n"); pdev->usb_init = 1; @@ -1139,7 +1150,6 @@ static int pwc_video_open(struct file *file) if (pwc_set_leds(pdev, led_on, led_off) < 0) PWC_DEBUG_OPEN("Failed to set LED on/off time.\n"); - pwc_construct(pdev); /* set min/max sizes correct */ /* So far, so good. Allocate memory. */ i = pwc_allocate_buffers(pdev); @@ -1216,6 +1226,15 @@ static void pwc_cleanup(struct pwc_device *pdev) { pwc_remove_sysfs_files(pdev->vdev); video_unregister_device(pdev->vdev); + +#ifdef CONFIG_USB_PWC_INPUT_EVDEV + 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 } /* Note that all cleanup is done in the reverse order as in _open */ @@ -1483,6 +1502,9 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id 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); @@ -1807,6 +1829,35 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pwc_set_leds(pdev, 0, 0); pwc_camera_power(pdev, 0); +#ifdef CONFIG_USB_PWC_INPUT_EVDEV + /* register webcam snapshot button input device */ + pdev->button_dev = input_allocate_device(); + if (!pdev->button_dev) { + PWC_ERROR("Err, insufficient memory for webcam snapshot button device."); + return -ENOMEM; + } + + 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; + 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); + pdev->button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); + + 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; + } +#endif + return 0; err_unreg: diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 01411fb..0be6f81 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -37,6 +37,9 @@ #include <linux/videodev.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#ifdef CONFIG_USB_PWC_INPUT_EVDEV +#include <linux/input.h> +#endif #include "pwc-uncompress.h" #include <media/pwc-ioctl.h> @@ -255,6 +258,9 @@ struct pwc_device int pan_angle; /* in degrees * 100 */ int tilt_angle; /* absolute angle; 0,0 is home position */ 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 */ +#endif /*** Misc. data ***/ wait_queue_head_t frameq; /* When waiting for a frame to finish... */ diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 07c334f..c522616 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -35,7 +35,6 @@ #include <linux/videodev2.h> #include <mach/dma.h> -#include <mach/pxa-regs.h> #include <mach/camera.h> #define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) @@ -879,6 +878,7 @@ static int test_platform_param(struct pxa_camera_dev *pcdev, SOCAM_HSYNC_ACTIVE_LOW | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | + SOCAM_DATA_ACTIVE_HIGH | SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING; @@ -1150,8 +1150,43 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, return formats; } +static int pxa_camera_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + struct soc_camera_sense sense = { + .master_clock = pcdev->mclk, + .pixel_clock_max = pcdev->ciclk / 4, + }; + 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); + + icd->sense = NULL; + + if (ret < 0) { + dev_warn(&ici->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) { + if (sense.pixel_clock > sense.pixel_clock_max) { + dev_err(&ici->dev, + "pixel clock %lu set by the camera too high!", + sense.pixel_clock); + return -EIO; + } + recalculate_fifo_timeout(pcdev, sense.pixel_clock); + } + + return ret; +} + static int pxa_camera_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) + struct v4l2_format *f) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; @@ -1161,35 +1196,30 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, .master_clock = pcdev->mclk, .pixel_clock_max = pcdev->ciclk / 4, }; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_format cam_f = *f; int ret; - if (pixfmt) { - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - cam_fmt = xlate->cam_fmt; + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) { + dev_warn(&ici->dev, "Format %x not found\n", pix->pixelformat); + return -EINVAL; } + cam_fmt = xlate->cam_fmt; + /* If PCLK is used to latch data from the sensor, check sense */ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) icd->sense = &sense; - switch (pixfmt) { - case 0: /* Only geometry change */ - ret = icd->ops->set_fmt(icd, pixfmt, rect); - break; - default: - ret = icd->ops->set_fmt(icd, cam_fmt->fourcc, rect); - } + cam_f.fmt.pix.pixelformat = cam_fmt->fourcc; + ret = icd->ops->set_fmt(icd, &cam_f); icd->sense = NULL; if (ret < 0) { dev_warn(&ici->dev, "Failed to configure for format %x\n", - pixfmt); + pix->pixelformat); } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { if (sense.pixel_clock > sense.pixel_clock_max) { dev_err(&ici->dev, @@ -1200,7 +1230,7 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, recalculate_fifo_timeout(pcdev, sense.pixel_clock); } - if (pixfmt && !ret) { + if (!ret) { icd->buswidth = xlate->buswidth; icd->current_fmt = xlate->host_fmt; } @@ -1364,6 +1394,7 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .remove = pxa_camera_remove_device, .suspend = pxa_camera_suspend, .resume = pxa_camera_resume, + .set_crop = pxa_camera_set_crop, .get_formats = pxa_camera_get_formats, .set_fmt = pxa_camera_set_fmt, .try_fmt = pxa_camera_try_fmt, diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 13f85ad..b5be633 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -336,14 +336,19 @@ static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, u16 index, u16 value, void *buf, s32 buf_len, int bOut); +/* dev_err macro with driver name */ +#define S2255_DRIVER_NAME "s2255" +#define s2255_dev_err(dev, fmt, arg...) \ + dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg) + #define dprintk(level, fmt, arg...) \ do { \ if (*s2255_debug >= (level)) { \ - printk(KERN_DEBUG "s2255: " fmt, ##arg); \ + printk(KERN_DEBUG S2255_DRIVER_NAME \ + ": " fmt, ##arg); \ } \ } while (0) - static struct usb_driver s2255_driver; @@ -528,14 +533,14 @@ static void s2255_fwchunk_complete(struct urb *urb) int len; dprintk(100, "udev %p urb %p", udev, urb); if (urb->status) { - dev_err(&udev->dev, "URB failed with status %d", urb->status); + dev_err(&udev->dev, "URB failed with status %d\n", urb->status); atomic_set(&data->fw_state, S2255_FW_FAILED); /* wake up anything waiting for the firmware */ wake_up(&data->wait_fw); return; } if (data->fw_urb == NULL) { - dev_err(&udev->dev, "s2255 disconnected\n"); + s2255_dev_err(&udev->dev, "disconnected\n"); atomic_set(&data->fw_state, S2255_FW_FAILED); /* wake up anything waiting for the firmware */ wake_up(&data->wait_fw); @@ -841,8 +846,7 @@ static int vidioc_querycap(struct file *file, void *priv, struct s2255_dev *dev = fh->dev; strlcpy(cap->driver, "s2255", sizeof(cap->driver)); strlcpy(cap->card, "s2255", sizeof(cap->card)); - strlcpy(cap->bus_info, dev_name(&dev->udev->dev), - sizeof(cap->bus_info)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); cap->version = S2255_VERSION; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; return 0; @@ -1278,7 +1282,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) } if (!res_get(dev, fh)) { - dev_err(&dev->udev->dev, "s2255: stream busy\n"); + s2255_dev_err(&dev->udev->dev, "stream busy\n"); return -EBUSY; } @@ -1545,7 +1549,8 @@ static int s2255_open(struct file *file) switch (atomic_read(&dev->fw_data->fw_state)) { case S2255_FW_FAILED: - err("2255 firmware load failed. retrying.\n"); + s2255_dev_err(&dev->udev->dev, + "firmware load failed. retrying.\n"); s2255_fwload_start(dev, 1); wait_event_timeout(dev->fw_data->wait_fw, ((atomic_read(&dev->fw_data->fw_state) @@ -2173,7 +2178,8 @@ static int s2255_board_init(struct s2255_dev *dev) printk(KERN_INFO "2255 usb firmware version %d \n", fw_ver); if (fw_ver < CUR_USB_FWVER) - err("usb firmware not up to date %d\n", fw_ver); + dev_err(&dev->udev->dev, + "usb firmware not up to date %d\n", fw_ver); for (j = 0; j < MAX_CHANNELS; j++) { dev->b_acquire[j] = 0; @@ -2228,13 +2234,13 @@ static void read_pipe_completion(struct urb *purb) dprintk(100, "read pipe completion %p, status %d\n", purb, purb->status); if (pipe_info == NULL) { - err("no context !"); + dev_err(&purb->dev->dev, "no context!\n"); return; } dev = pipe_info->dev; if (dev == NULL) { - err("no context !"); + dev_err(&purb->dev->dev, "no context!\n"); return; } status = purb->status; @@ -2286,7 +2292,7 @@ static int s2255_start_readpipe(struct s2255_dev *dev) pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pipe_info->stream_urb) { dev_err(&dev->udev->dev, - "ReadStream: Unable to alloc URB"); + "ReadStream: Unable to alloc URB\n"); return -ENOMEM; } /* transfer buffer allocated in board_init */ @@ -2391,7 +2397,7 @@ static void s2255_stop_readpipe(struct s2255_dev *dev) int j; if (dev == NULL) { - err("s2255: invalid device"); + s2255_dev_err(&dev->udev->dev, "invalid device\n"); return; } dprintk(4, "stop read pipe\n"); @@ -2453,7 +2459,7 @@ static int s2255_probe(struct usb_interface *interface, /* allocate memory for our device state and initialize it to zero */ dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL); if (dev == NULL) { - err("s2255: out of memory"); + s2255_dev_err(&interface->dev, "out of memory\n"); goto error; } @@ -2487,7 +2493,7 @@ static int s2255_probe(struct usb_interface *interface, } if (!dev->read_endpoint) { - dev_err(&interface->dev, "Could not find bulk-in endpoint"); + dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); goto error; } @@ -2583,7 +2589,7 @@ static void s2255_disconnect(struct usb_interface *interface) } static struct usb_driver s2255_driver = { - .name = "s2255", + .name = S2255_DRIVER_NAME, .probe = s2255_probe, .disconnect = s2255_disconnect, .id_table = s2255_table, @@ -2597,7 +2603,8 @@ static int __init usb_s2255_init(void) result = usb_register(&s2255_driver); if (result) - err("usb_register failed. Error number %d", result); + pr_err(KBUILD_MODNAME + ": usb_register failed. Error number %d\n", result); dprintk(2, "s2255_init: done\n"); return result; diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c index e637e44..da47b2f 100644 --- a/drivers/media/video/saa5246a.c +++ b/drivers/media/video/saa5246a.c @@ -46,10 +46,11 @@ #include <linux/smp_lock.h> #include <linux/mutex.h> #include <linux/videotext.h> -#include <linux/videodev.h> -#include <media/v4l2-common.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> #include <media/v4l2-ioctl.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver"); @@ -388,13 +389,19 @@ MODULE_LICENSE("GPL"); struct saa5246a_device { + struct v4l2_subdev sd; + struct video_device *vdev; u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE]; int is_searching[NUM_DAUS]; - struct i2c_client *client; unsigned long in_use; struct mutex lock; }; +static inline struct saa5246a_device *to_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa5246a_device, sd); +} + static struct video_device saa_template; /* Declared near bottom */ /* @@ -403,12 +410,13 @@ static struct video_device saa_template; /* Declared near bottom */ static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data) { + struct i2c_client *client = v4l2_get_subdevdata(&t->sd); char buf[64]; buf[0] = reg; memcpy(buf+1, data, count); - if(i2c_master_send(t->client, buf, count+1)==count+1) + if (i2c_master_send(client, buf, count + 1) == count + 1) return 0; return -1; } @@ -436,7 +444,9 @@ static int i2c_senddata(struct saa5246a_device *t, ...) */ static int i2c_getdata(struct saa5246a_device *t, int count, u8 *buf) { - if(i2c_master_recv(t->client, buf, count)!=count) + struct i2c_client *client = v4l2_get_subdevdata(&t->sd); + + if (i2c_master_recv(client, buf, count) != count) return -1; return 0; } @@ -961,9 +971,6 @@ static int saa5246a_open(struct file *file) { struct saa5246a_device *t = video_drvdata(file); - if (t->client == NULL) - return -ENODEV; - if (test_and_set_bit(0, &t->in_use)) return -EBUSY; @@ -1033,18 +1040,29 @@ static struct video_device saa_template = .minor = -1, }; -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; +static int saa5246a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA5246A, 0); +} + +static const struct v4l2_subdev_core_ops saa5246a_core_ops = { + .g_chip_ident = saa5246a_g_chip_ident, +}; + +static const struct v4l2_subdev_ops saa5246a_ops = { + .core = &saa5246a_core_ops, +}; -I2C_CLIENT_INSMOD; static int saa5246a_probe(struct i2c_client *client, const struct i2c_device_id *id) { int pgbuf; int err; - struct video_device *vd; struct saa5246a_device *t; + struct v4l2_subdev *sd; v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); @@ -1053,40 +1071,43 @@ static int saa5246a_probe(struct i2c_client *client, t = kzalloc(sizeof(*t), GFP_KERNEL); if (t == NULL) return -ENOMEM; + sd = &t->sd; + v4l2_i2c_subdev_init(sd, client, &saa5246a_ops); mutex_init(&t->lock); /* Now create a video4linux device */ - vd = video_device_alloc(); - if (vd == NULL) { + t->vdev = video_device_alloc(); + if (t->vdev == NULL) { kfree(t); return -ENOMEM; } - i2c_set_clientdata(client, vd); - memcpy(vd, &saa_template, sizeof(*vd)); + memcpy(t->vdev, &saa_template, sizeof(*t->vdev)); for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); t->is_searching[pgbuf] = false; } - video_set_drvdata(vd, t); + video_set_drvdata(t->vdev, t); /* Register it */ - err = video_register_device(vd, VFL_TYPE_VTX, -1); + err = video_register_device(t->vdev, VFL_TYPE_VTX, -1); if (err < 0) { kfree(t); - video_device_release(vd); + video_device_release(t->vdev); + t->vdev = NULL; return err; } - t->client = client; return 0; } static int saa5246a_remove(struct i2c_client *client) { - struct video_device *vd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct saa5246a_device *t = to_dev(sd); - video_unregister_device(vd); - kfree(video_get_drvdata(vd)); + video_unregister_device(t->vdev); + v4l2_device_unregister_subdev(sd); + kfree(t); return 0; } @@ -1098,7 +1119,6 @@ MODULE_DEVICE_TABLE(i2c, saa5246a_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa5246a", - .driverid = I2C_DRIVERID_SAA5249, .probe = saa5246a_probe, .remove = saa5246a_remove, .id_table = saa5246a_id, diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index e297651..48b27fe 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -50,15 +50,17 @@ #include <linux/mutex.h> #include <linux/delay.h> #include <linux/videotext.h> -#include <linux/videodev.h> -#include <media/v4l2-common.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> #include <media/v4l2-ioctl.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); MODULE_DESCRIPTION("Philips SAA5249 Teletext decoder driver"); MODULE_LICENSE("GPL"); + #define VTX_VER_MAJ 1 #define VTX_VER_MIN 8 @@ -95,17 +97,23 @@ typedef struct { struct saa5249_device { + struct v4l2_subdev sd; + struct video_device *vdev; vdau_t vdau[NUM_DAUS]; /* Data for virtual DAUs (the 5249 only has one */ /* real DAU, so we have to simulate some more) */ int vtx_use_count; int is_searching[NUM_DAUS]; int disp_mode; int virtual_mode; - struct i2c_client *client; unsigned long in_use; struct mutex lock; }; +static inline struct saa5249_device *to_dev(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa5249_device, sd); +} + #define CCTWR 34 /* I²C write/read-address of vtx-chip */ #define CCTRD 35 @@ -147,12 +155,13 @@ static void jdelay(unsigned long delay) static int i2c_sendbuf(struct saa5249_device *t, int reg, int count, u8 *data) { + struct i2c_client *client = v4l2_get_subdevdata(&t->sd); char buf[64]; buf[0] = reg; memcpy(buf+1, data, count); - if (i2c_master_send(t->client, buf, count + 1) == count + 1) + if (i2c_master_send(client, buf, count + 1) == count + 1) return 0; return -1; } @@ -180,7 +189,9 @@ static int i2c_senddata(struct saa5249_device *t, ...) static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf) { - if(i2c_master_recv(t->client, buf, count)!=count) + struct i2c_client *client = v4l2_get_subdevdata(&t->sd); + + if (i2c_master_recv(client, buf, count) != count) return -1; return 0; } @@ -497,9 +508,6 @@ static int saa5249_open(struct file *file) struct saa5249_device *t = video_drvdata(file); int pgbuf; - if (t->client == NULL) - return -ENODEV; - if (test_and_set_bit(0, &t->in_use)) return -EBUSY; @@ -553,18 +561,28 @@ static struct video_device saa_template = .release = video_device_release, }; -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; +static int saa5249_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA5249, 0); +} -I2C_CLIENT_INSMOD; +static const struct v4l2_subdev_core_ops saa5249_core_ops = { + .g_chip_ident = saa5249_g_chip_ident, +}; + +static const struct v4l2_subdev_ops saa5249_ops = { + .core = &saa5249_core_ops, +}; static int saa5249_probe(struct i2c_client *client, const struct i2c_device_id *id) { int pgbuf; int err; - struct video_device *vd; struct saa5249_device *t; + struct v4l2_subdev *sd; v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); @@ -573,16 +591,17 @@ static int saa5249_probe(struct i2c_client *client, t = kzalloc(sizeof(*t), GFP_KERNEL); if (t == NULL) return -ENOMEM; + sd = &t->sd; + v4l2_i2c_subdev_init(sd, client, &saa5249_ops); mutex_init(&t->lock); /* Now create a video4linux device */ - vd = kmalloc(sizeof(struct video_device), GFP_KERNEL); - if (vd == NULL) { + t->vdev = video_device_alloc(); + if (t->vdev == NULL) { kfree(client); return -ENOMEM; } - i2c_set_clientdata(client, vd); - memcpy(vd, &saa_template, sizeof(*vd)); + memcpy(t->vdev, &saa_template, sizeof(*t->vdev)); for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); @@ -593,26 +612,27 @@ static int saa5249_probe(struct i2c_client *client, t->vdau[pgbuf].stopped = true; t->is_searching[pgbuf] = false; } - video_set_drvdata(vd, t); + video_set_drvdata(t->vdev, t); /* Register it */ - err = video_register_device(vd, VFL_TYPE_VTX, -1); + err = video_register_device(t->vdev, VFL_TYPE_VTX, -1); if (err < 0) { kfree(t); - kfree(vd); + video_device_release(t->vdev); + t->vdev = NULL; return err; } - t->client = client; return 0; } static int saa5249_remove(struct i2c_client *client) { - struct video_device *vd = i2c_get_clientdata(client); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct saa5249_device *t = to_dev(sd); - video_unregister_device(vd); - kfree(video_get_drvdata(vd)); - kfree(vd); + video_unregister_device(t->vdev); + v4l2_device_unregister_subdev(sd); + kfree(t); return 0; } @@ -624,7 +644,6 @@ MODULE_DEVICE_TABLE(i2c, saa5249_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa5249", - .driverid = I2C_DRIVERID_SAA5249, .probe = saa5249_probe, .remove = saa5249_remove, .id_table = saa5249_id, diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c index f050242..c25e81a 100644 --- a/drivers/media/video/saa6588.c +++ b/drivers/media/video/saa6588.c @@ -23,7 +23,7 @@ #include <linux/kernel.h> #include <linux/i2c.h> #include <linux/types.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/slab.h> @@ -32,15 +32,10 @@ #include <asm/uaccess.h> #include <media/rds.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> -/* Addresses to scan */ -static unsigned short normal_i2c[] = { - 0x20 >> 1, - 0x22 >> 1, - I2C_CLIENT_END, -}; - -I2C_CLIENT_INSMOD; /* insmod options */ static unsigned int debug; @@ -72,9 +67,8 @@ MODULE_LICENSE("GPL"); #define dprintk if (debug) printk struct saa6588 { - struct i2c_client client; - struct work_struct work; - struct timer_list timer; + struct v4l2_subdev sd; + struct delayed_work work; spinlock_t lock; unsigned char *buffer; unsigned int buf_size; @@ -86,8 +80,10 @@ struct saa6588 { int data_available_for_read; }; -static struct i2c_driver driver; -static struct i2c_client client_template; +static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa6588, sd); +} /* ---------------------------------------------------------------------- */ @@ -258,6 +254,7 @@ static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf) static void saa6588_i2c_poll(struct saa6588 *s) { + struct i2c_client *client = v4l2_get_subdevdata(&s->sd); unsigned long flags; unsigned char tmpbuf[6]; unsigned char blocknum; @@ -265,7 +262,7 @@ static void saa6588_i2c_poll(struct saa6588 *s) /* Although we only need 3 bytes, we have to read at least 6. SAA6588 returns garbage otherwise */ - if (6 != i2c_master_recv(&s->client, &tmpbuf[0], 6)) { + if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) { if (debug > 1) dprintk(PREFIX "read error!\n"); return; @@ -316,23 +313,17 @@ static void saa6588_i2c_poll(struct saa6588 *s) wake_up_interruptible(&s->read_queue); } -static void saa6588_timer(unsigned long data) -{ - struct saa6588 *s = (struct saa6588 *)data; - - schedule_work(&s->work); -} - static void saa6588_work(struct work_struct *work) { - struct saa6588 *s = container_of(work, struct saa6588, work); + struct saa6588 *s = container_of(work, struct saa6588, work.work); saa6588_i2c_poll(s); - mod_timer(&s->timer, jiffies + msecs_to_jiffies(20)); + schedule_delayed_work(&s->work, msecs_to_jiffies(20)); } static int saa6588_configure(struct saa6588 *s) { + struct i2c_client *client = v4l2_get_subdevdata(&s->sd); unsigned char buf[3]; int rc; @@ -380,7 +371,8 @@ static int saa6588_configure(struct saa6588 *s) dprintk(PREFIX "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n", buf[0], buf[1], buf[2]); - if (3 != (rc = i2c_master_send(&s->client, buf, 3))) + 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; @@ -388,70 +380,10 @@ static int saa6588_configure(struct saa6588 *s) /* ---------------------------------------------------------------------- */ -static int saa6588_attach(struct i2c_adapter *adap, int addr, int kind) -{ - struct saa6588 *s; - client_template.adapter = adap; - client_template.addr = addr; - - printk(PREFIX "chip found @ 0x%x\n", addr << 1); - - if (NULL == (s = kmalloc(sizeof(*s), GFP_KERNEL))) - return -ENOMEM; - - s->buf_size = bufblocks * 3; - - if (NULL == (s->buffer = kmalloc(s->buf_size, GFP_KERNEL))) { - kfree(s); - return -ENOMEM; - } - spin_lock_init(&s->lock); - s->client = client_template; - s->block_count = 0; - s->wr_index = 0; - s->rd_index = 0; - s->last_blocknum = 0xff; - init_waitqueue_head(&s->read_queue); - s->data_available_for_read = 0; - i2c_set_clientdata(&s->client, s); - i2c_attach_client(&s->client); - - saa6588_configure(s); - - /* start polling via eventd */ - INIT_WORK(&s->work, saa6588_work); - init_timer(&s->timer); - s->timer.function = saa6588_timer; - s->timer.data = (unsigned long)s; - schedule_work(&s->work); - return 0; -} - -static int saa6588_probe(struct i2c_adapter *adap) -{ - if (adap->class & I2C_CLASS_TV_ANALOG) - return i2c_probe(adap, &addr_data, saa6588_attach); - return 0; -} - -static int saa6588_detach(struct i2c_client *client) -{ - struct saa6588 *s = i2c_get_clientdata(client); - - del_timer_sync(&s->timer); - flush_scheduled_work(); - - i2c_detach_client(client); - kfree(s->buffer); - kfree(s); - return 0; -} - -static int saa6588_command(struct i2c_client *client, unsigned int cmd, - void *arg) +static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { - struct saa6588 *s = i2c_get_clientdata(client); - struct rds_command *a = (struct rds_command *)arg; + struct saa6588 *s = to_saa6588(sd); + struct rds_command *a = arg; switch (cmd) { /* --- open() for /dev/radio --- */ @@ -479,45 +411,94 @@ static int saa6588_command(struct i2c_client *client, unsigned int cmd, default: /* nothing */ - break; + return -ENOIOCTLCMD; } 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); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA6588, 0); +} + /* ----------------------------------------------------------------------- */ -static struct i2c_driver driver = { - .driver = { - .name = "saa6588", - }, - .id = -1, /* FIXME */ - .attach_adapter = saa6588_probe, - .detach_client = saa6588_detach, - .command = saa6588_command, +static const struct v4l2_subdev_core_ops saa6588_core_ops = { + .g_chip_ident = saa6588_g_chip_ident, + .ioctl = saa6588_ioctl, }; -static struct i2c_client client_template = { - .name = "saa6588", - .driver = &driver, +static const struct v4l2_subdev_ops saa6588_ops = { + .core = &saa6588_core_ops, }; -static int __init saa6588_init_module(void) +/* ---------------------------------------------------------------------- */ + +static int saa6588_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - return i2c_add_driver(&driver); + struct saa6588 *s; + struct v4l2_subdev *sd; + + v4l_info(client, "saa6588 found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (s == NULL) + return -ENOMEM; + + s->buf_size = bufblocks * 3; + + s->buffer = kmalloc(s->buf_size, GFP_KERNEL); + if (s->buffer == NULL) { + kfree(s); + return -ENOMEM; + } + sd = &s->sd; + v4l2_i2c_subdev_init(sd, client, &saa6588_ops); + spin_lock_init(&s->lock); + s->block_count = 0; + s->wr_index = 0; + s->rd_index = 0; + s->last_blocknum = 0xff; + init_waitqueue_head(&s->read_queue); + s->data_available_for_read = 0; + + saa6588_configure(s); + + /* start polling via eventd */ + INIT_DELAYED_WORK(&s->work, saa6588_work); + schedule_delayed_work(&s->work, 0); + return 0; } -static void __exit saa6588_cleanup_module(void) +static int saa6588_remove(struct i2c_client *client) { - i2c_del_driver(&driver); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct saa6588 *s = to_saa6588(sd); + + v4l2_device_unregister_subdev(sd); + + cancel_delayed_work_sync(&s->work); + + kfree(s->buffer); + kfree(s); + return 0; } -module_init(saa6588_init_module); -module_exit(saa6588_cleanup_module); +/* ----------------------------------------------------------------------- */ -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ +static const struct i2c_device_id saa6588_id[] = { + { "saa6588", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa6588_id); + +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "saa6588", + .probe = saa6588_probe, + .remove = saa6588_remove, + .id_table = saa6588_id, +}; diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c index 3786069..df4e08d 100644 --- a/drivers/media/video/saa7110.c +++ b/drivers/media/video/saa7110.c @@ -33,15 +33,16 @@ #include <linux/wait.h> #include <asm/uaccess.h> #include <linux/i2c.h> -#include <linux/videodev.h> -#include <linux/video_decoder.h> -#include <media/v4l2-common.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("Philips SAA7110 video decoder driver"); MODULE_AUTHOR("Pauline Middelink"); MODULE_LICENSE("GPL"); + static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); @@ -52,9 +53,10 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define SAA7110_NR_REG 0x35 struct saa7110 { + struct v4l2_subdev sd; u8 reg[SAA7110_NR_REG]; - int norm; + v4l2_std_id norm; int input; int enable; int bright; @@ -65,20 +67,28 @@ struct saa7110 { wait_queue_head_t wq; }; +static inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7110, sd); +} + /* ----------------------------------------------------------------------- */ /* I2C support functions */ /* ----------------------------------------------------------------------- */ -static int saa7110_write(struct i2c_client *client, u8 reg, u8 value) +static int saa7110_write(struct v4l2_subdev *sd, u8 reg, u8 value) { - struct saa7110 *decoder = i2c_get_clientdata(client); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7110 *decoder = to_saa7110(sd); decoder->reg[reg] = value; return i2c_smbus_write_byte_data(client, reg, value); } -static int saa7110_write_block(struct i2c_client *client, const u8 *data, unsigned int len) +static int saa7110_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7110 *decoder = to_saa7110(sd); int ret = -1; u8 reg = *data; /* first register to write to */ @@ -89,15 +99,13 @@ static int saa7110_write_block(struct i2c_client *client, const u8 *data, unsign /* the saa7110 has an autoincrement function, use it if * the adapter understands raw I2C */ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - struct saa7110 *decoder = i2c_get_clientdata(client); - ret = i2c_master_send(client, data, len); /* Cache the written data */ memcpy(decoder->reg + reg, data + 1, len - 1); } else { for (++data, --len; len; len--) { - ret = saa7110_write(client, reg++, *data++); + ret = saa7110_write(sd, reg++, *data++); if (ret < 0) break; } @@ -106,8 +114,10 @@ static int saa7110_write_block(struct i2c_client *client, const u8 *data, unsign return ret; } -static inline int saa7110_read(struct i2c_client *client) +static inline int saa7110_read(struct v4l2_subdev *sd) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte(client); } @@ -115,11 +125,11 @@ static inline int saa7110_read(struct i2c_client *client) /* SAA7110 functions */ /* ----------------------------------------------------------------------- */ -#define FRESP_06H_COMPST 0x03 //0x13 -#define FRESP_06H_SVIDEO 0x83 //0xC0 +#define FRESP_06H_COMPST 0x03 /*0x13*/ +#define FRESP_06H_SVIDEO 0x83 /*0xC0*/ -static int saa7110_selmux(struct i2c_client *client, int chan) +static int saa7110_selmux(struct v4l2_subdev *sd, int chan) { static const unsigned char modes[9][8] = { /* mode 0 */ @@ -150,17 +160,17 @@ static int saa7110_selmux(struct i2c_client *client, int chan) {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23, 0x44, 0x75, 0x21} }; - struct saa7110 *decoder = i2c_get_clientdata(client); + struct saa7110 *decoder = to_saa7110(sd); const unsigned char *ptr = modes[chan]; - saa7110_write(client, 0x06, ptr[0]); /* Luminance control */ - saa7110_write(client, 0x20, ptr[1]); /* Analog Control #1 */ - saa7110_write(client, 0x21, ptr[2]); /* Analog Control #2 */ - saa7110_write(client, 0x22, ptr[3]); /* Mixer Control #1 */ - saa7110_write(client, 0x2C, ptr[4]); /* Mixer Control #2 */ - saa7110_write(client, 0x30, ptr[5]); /* ADCs gain control */ - saa7110_write(client, 0x31, ptr[6]); /* Mixer Control #3 */ - saa7110_write(client, 0x21, ptr[7]); /* Analog Control #2 */ + saa7110_write(sd, 0x06, ptr[0]); /* Luminance control */ + saa7110_write(sd, 0x20, ptr[1]); /* Analog Control #1 */ + saa7110_write(sd, 0x21, ptr[2]); /* Analog Control #2 */ + saa7110_write(sd, 0x22, ptr[3]); /* Mixer Control #1 */ + saa7110_write(sd, 0x2C, ptr[4]); /* Mixer Control #2 */ + saa7110_write(sd, 0x30, ptr[5]); /* ADCs gain control */ + saa7110_write(sd, 0x31, ptr[6]); /* Mixer Control #3 */ + saa7110_write(sd, 0x21, ptr[7]); /* Analog Control #2 */ decoder->input = chan; return 0; @@ -176,246 +186,260 @@ static const unsigned char initseq[1 + SAA7110_NR_REG] = { /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02 }; -static int determine_norm(struct i2c_client *client) +static v4l2_std_id determine_norm(struct v4l2_subdev *sd) { DEFINE_WAIT(wait); - struct saa7110 *decoder = i2c_get_clientdata(client); + struct saa7110 *decoder = to_saa7110(sd); int status; /* mode changed, start automatic detection */ - saa7110_write_block(client, initseq, sizeof(initseq)); - saa7110_selmux(client, decoder->input); + saa7110_write_block(sd, initseq, sizeof(initseq)); + saa7110_selmux(sd, decoder->input); prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); schedule_timeout(msecs_to_jiffies(250)); finish_wait(&decoder->wq, &wait); - status = saa7110_read(client); + status = saa7110_read(sd); if (status & 0x40) { - v4l_dbg(1, debug, client, "status=0x%02x (no signal)\n", status); - return decoder->norm; // no change + v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status); + return decoder->norm; /* no change*/ } if ((status & 3) == 0) { - saa7110_write(client, 0x06, 0x83); + saa7110_write(sd, 0x06, 0x83); if (status & 0x20) { - v4l_dbg(1, debug, client, "status=0x%02x (NTSC/no color)\n", status); - //saa7110_write(client,0x2E,0x81); - return VIDEO_MODE_NTSC; + v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC/no color)\n", status); + /*saa7110_write(sd,0x2E,0x81);*/ + return V4L2_STD_NTSC; } - v4l_dbg(1, debug, client, "status=0x%02x (PAL/no color)\n", status); - //saa7110_write(client,0x2E,0x9A); - return VIDEO_MODE_PAL; + v4l2_dbg(1, debug, sd, "status=0x%02x (PAL/no color)\n", status); + /*saa7110_write(sd,0x2E,0x9A);*/ + return V4L2_STD_PAL; } - //saa7110_write(client,0x06,0x03); + /*saa7110_write(sd,0x06,0x03);*/ if (status & 0x20) { /* 60Hz */ - v4l_dbg(1, debug, client, "status=0x%02x (NTSC)\n", status); - saa7110_write(client, 0x0D, 0x86); - saa7110_write(client, 0x0F, 0x50); - saa7110_write(client, 0x11, 0x2C); - //saa7110_write(client,0x2E,0x81); - return VIDEO_MODE_NTSC; + v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC)\n", status); + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x50); + saa7110_write(sd, 0x11, 0x2C); + /*saa7110_write(sd,0x2E,0x81);*/ + return V4L2_STD_NTSC; } /* 50Hz -> PAL/SECAM */ - saa7110_write(client, 0x0D, 0x86); - saa7110_write(client, 0x0F, 0x10); - saa7110_write(client, 0x11, 0x59); - //saa7110_write(client,0x2E,0x9A); + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd,0x2E,0x9A);*/ prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE); schedule_timeout(msecs_to_jiffies(250)); finish_wait(&decoder->wq, &wait); - status = saa7110_read(client); + status = saa7110_read(sd); if ((status & 0x03) == 0x01) { - v4l_dbg(1, debug, client, "status=0x%02x (SECAM)\n", status); - saa7110_write(client, 0x0D, 0x87); - return VIDEO_MODE_SECAM; + v4l2_dbg(1, debug, sd, "status=0x%02x (SECAM)\n", status); + saa7110_write(sd, 0x0D, 0x87); + return V4L2_STD_SECAM; } - v4l_dbg(1, debug, client, "status=0x%02x (PAL)\n", status); - return VIDEO_MODE_PAL; + v4l2_dbg(1, debug, sd, "status=0x%02x (PAL)\n", status); + return V4L2_STD_PAL; } -static int -saa7110_command (struct i2c_client *client, - unsigned int cmd, - void *arg) +static int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus) { - struct saa7110 *decoder = i2c_get_clientdata(client); - int v; + struct saa7110 *decoder = to_saa7110(sd); + int res = V4L2_IN_ST_NO_SIGNAL; + int status = saa7110_read(sd); + + v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n", + status, (unsigned long long)decoder->norm); + if (!(status & 0x40)) + res = 0; + if (!(status & 0x03)) + res |= V4L2_IN_ST_NO_COLOR; + + *pstatus = res; + return 0; +} - switch (cmd) { - case 0: - //saa7110_write_block(client, initseq, sizeof(initseq)); - break; +static int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + *(v4l2_std_id *)std = determine_norm(sd); + return 0; +} - case DECODER_GET_CAPABILITIES: - { - struct video_decoder_capability *dc = arg; +static int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa7110 *decoder = to_saa7110(sd); + + if (decoder->norm != std) { + decoder->norm = std; + /*saa7110_write(sd, 0x06, 0x03);*/ + if (std & V4L2_STD_NTSC) { + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x50); + saa7110_write(sd, 0x11, 0x2C); + /*saa7110_write(sd, 0x2E, 0x81);*/ + v4l2_dbg(1, debug, sd, "switched to NTSC\n"); + } else if (std & V4L2_STD_PAL) { + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd, 0x2E, 0x9A);*/ + v4l2_dbg(1, debug, sd, "switched to PAL\n"); + } else if (std & V4L2_STD_SECAM) { + saa7110_write(sd, 0x0D, 0x87); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd, 0x2E, 0x9A);*/ + v4l2_dbg(1, debug, sd, "switched to SECAM\n"); + } else { + return -EINVAL; + } + } + return 0; +} - dc->flags = - VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC | - VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO; - dc->inputs = SAA7110_MAX_INPUT; - dc->outputs = SAA7110_MAX_OUTPUT; - break; +static int saa7110_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa7110 *decoder = to_saa7110(sd); + + if (route->input < 0 || route->input >= SAA7110_MAX_INPUT) { + v4l2_dbg(1, debug, sd, "input=%d not available\n", route->input); + return -EINVAL; + } + if (decoder->input != route->input) { + saa7110_selmux(sd, route->input); + v4l2_dbg(1, debug, sd, "switched to input=%d\n", route->input); } + return 0; +} - case DECODER_GET_STATUS: - { - int status; - int res = 0; - - status = saa7110_read(client); - v4l_dbg(1, debug, client, "status=0x%02x norm=%d\n", - status, decoder->norm); - if (!(status & 0x40)) - res |= DECODER_STATUS_GOOD; - if (status & 0x03) - res |= DECODER_STATUS_COLOR; - - switch (decoder->norm) { - case VIDEO_MODE_NTSC: - res |= DECODER_STATUS_NTSC; - break; - case VIDEO_MODE_PAL: - res |= DECODER_STATUS_PAL; - break; - case VIDEO_MODE_SECAM: - res |= DECODER_STATUS_SECAM; - break; - } - *(int *) arg = res; - break; +static int saa7110_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa7110 *decoder = to_saa7110(sd); + + if (decoder->enable != enable) { + decoder->enable = enable; + saa7110_write(sd, 0x0E, enable ? 0x18 : 0x80); + v4l2_dbg(1, debug, sd, "YUV %s\n", enable ? "on" : "off"); } + return 0; +} - case DECODER_SET_NORM: - v = *(int *) arg; - if (decoder->norm != v) { - decoder->norm = v; - //saa7110_write(client, 0x06, 0x03); - switch (v) { - case VIDEO_MODE_NTSC: - saa7110_write(client, 0x0D, 0x86); - saa7110_write(client, 0x0F, 0x50); - saa7110_write(client, 0x11, 0x2C); - //saa7110_write(client, 0x2E, 0x81); - v4l_dbg(1, debug, client, "switched to NTSC\n"); - break; - case VIDEO_MODE_PAL: - saa7110_write(client, 0x0D, 0x86); - saa7110_write(client, 0x0F, 0x10); - saa7110_write(client, 0x11, 0x59); - //saa7110_write(client, 0x2E, 0x9A); - v4l_dbg(1, debug, client, "switched to PAL\n"); - break; - case VIDEO_MODE_SECAM: - saa7110_write(client, 0x0D, 0x87); - saa7110_write(client, 0x0F, 0x10); - saa7110_write(client, 0x11, 0x59); - //saa7110_write(client, 0x2E, 0x9A); - v4l_dbg(1, debug, client, "switched to SECAM\n"); - break; - case VIDEO_MODE_AUTO: - v4l_dbg(1, debug, client, "switched to AUTO\n"); - decoder->norm = determine_norm(client); - *(int *) arg = decoder->norm; - break; - default: - return -EPERM; - } - } - break; +static int saa7110_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); + default: + return -EINVAL; + } + return 0; +} - case DECODER_SET_INPUT: - v = *(int *) arg; - if (v < 0 || v >= SAA7110_MAX_INPUT) { - v4l_dbg(1, debug, client, "input=%d not available\n", v); - return -EINVAL; - } - if (decoder->input != v) { - saa7110_selmux(client, v); - v4l_dbg(1, debug, client, "switched to input=%d\n", v); - } - break; +static int saa7110_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct saa7110 *decoder = to_saa7110(sd); - case DECODER_SET_OUTPUT: - v = *(int *) arg; - /* not much choice of outputs */ - if (v != 0) - return -EINVAL; + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = decoder->bright; break; - - case DECODER_ENABLE_OUTPUT: - v = *(int *) arg; - if (decoder->enable != v) { - decoder->enable = v; - saa7110_write(client, 0x0E, v ? 0x18 : 0x80); - v4l_dbg(1, debug, client, "YUV %s\n", v ? "on" : "off"); - } + case V4L2_CID_CONTRAST: + ctrl->value = decoder->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = decoder->sat; + break; + case V4L2_CID_HUE: + ctrl->value = decoder->hue; break; + default: + return -EINVAL; + } + return 0; +} - case DECODER_SET_PICTURE: - { - struct video_picture *pic = arg; +static int saa7110_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct saa7110 *decoder = to_saa7110(sd); - if (decoder->bright != pic->brightness) { - /* We want 0 to 255 we get 0-65535 */ - decoder->bright = pic->brightness; - saa7110_write(client, 0x19, decoder->bright >> 8); - } - if (decoder->contrast != pic->contrast) { - /* We want 0 to 127 we get 0-65535 */ - decoder->contrast = pic->contrast; - saa7110_write(client, 0x13, - decoder->contrast >> 9); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (decoder->bright != ctrl->value) { + decoder->bright = ctrl->value; + saa7110_write(sd, 0x19, decoder->bright); } - if (decoder->sat != pic->colour) { - /* We want 0 to 127 we get 0-65535 */ - decoder->sat = pic->colour; - saa7110_write(client, 0x12, decoder->sat >> 9); + break; + case V4L2_CID_CONTRAST: + if (decoder->contrast != ctrl->value) { + decoder->contrast = ctrl->value; + saa7110_write(sd, 0x13, decoder->contrast); } - if (decoder->hue != pic->hue) { - /* We want -128 to 127 we get 0-65535 */ - decoder->hue = pic->hue; - saa7110_write(client, 0x07, - (decoder->hue >> 8) - 128); + break; + case V4L2_CID_SATURATION: + if (decoder->sat != ctrl->value) { + decoder->sat = ctrl->value; + saa7110_write(sd, 0x12, decoder->sat); } break; - } - - case DECODER_DUMP: - if (!debug) - break; - for (v = 0; v < SAA7110_NR_REG; v += 16) { - int j; - v4l_dbg(1, debug, client, "%02x:", v); - for (j = 0; j < 16 && v + j < SAA7110_NR_REG; j++) - printk(KERN_CONT " %02x", decoder->reg[v + j]); - printk(KERN_CONT "\n"); + case V4L2_CID_HUE: + if (decoder->hue != ctrl->value) { + decoder->hue = ctrl->value; + saa7110_write(sd, 0x07, decoder->hue); } break; - default: - v4l_dbg(1, debug, client, "unknown command %08x\n", cmd); return -EINVAL; } return 0; } +static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7110, 0); +} + /* ----------------------------------------------------------------------- */ -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ +static const struct v4l2_subdev_core_ops saa7110_core_ops = { + .g_chip_ident = saa7110_g_chip_ident, + .g_ctrl = saa7110_g_ctrl, + .s_ctrl = saa7110_s_ctrl, + .queryctrl = saa7110_queryctrl, +}; -static unsigned short normal_i2c[] = { 0x9c >> 1, 0x9e >> 1, I2C_CLIENT_END }; +static const struct v4l2_subdev_tuner_ops saa7110_tuner_ops = { + .s_std = saa7110_s_std, +}; -I2C_CLIENT_INSMOD; +static const struct v4l2_subdev_video_ops saa7110_video_ops = { + .s_routing = saa7110_s_routing, + .s_stream = saa7110_s_stream, + .querystd = saa7110_querystd, + .g_input_status = saa7110_g_input_status, +}; + +static const struct v4l2_subdev_ops saa7110_ops = { + .core = &saa7110_core_ops, + .tuner = &saa7110_tuner_ops, + .video = &saa7110_video_ops, +}; + +/* ----------------------------------------------------------------------- */ static int saa7110_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct saa7110 *decoder; + struct v4l2_subdev *sd; int rv; /* Check if the adapter supports the needed features */ @@ -429,7 +453,9 @@ static int saa7110_probe(struct i2c_client *client, decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL); if (!decoder) return -ENOMEM; - decoder->norm = VIDEO_MODE_PAL; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa7110_ops); + decoder->norm = V4L2_STD_PAL; decoder->input = 0; decoder->enable = 1; decoder->bright = 32768; @@ -437,30 +463,29 @@ static int saa7110_probe(struct i2c_client *client, decoder->hue = 32768; decoder->sat = 32768; init_waitqueue_head(&decoder->wq); - i2c_set_clientdata(client, decoder); - rv = saa7110_write_block(client, initseq, sizeof(initseq)); + rv = saa7110_write_block(sd, initseq, sizeof(initseq)); if (rv < 0) { - v4l_dbg(1, debug, client, "init status %d\n", rv); + v4l2_dbg(1, debug, sd, "init status %d\n", rv); } else { int ver, status; - saa7110_write(client, 0x21, 0x10); - saa7110_write(client, 0x0e, 0x18); - saa7110_write(client, 0x0D, 0x04); - ver = saa7110_read(client); - saa7110_write(client, 0x0D, 0x06); - //mdelay(150); - status = saa7110_read(client); - v4l_dbg(1, debug, client, "version %x, status=0x%02x\n", + saa7110_write(sd, 0x21, 0x10); + saa7110_write(sd, 0x0e, 0x18); + saa7110_write(sd, 0x0D, 0x04); + ver = saa7110_read(sd); + saa7110_write(sd, 0x0D, 0x06); + /*mdelay(150);*/ + status = saa7110_read(sd); + v4l2_dbg(1, debug, sd, "version %x, status=0x%02x\n", ver, status); - saa7110_write(client, 0x0D, 0x86); - saa7110_write(client, 0x0F, 0x10); - saa7110_write(client, 0x11, 0x59); - //saa7110_write(client, 0x2E, 0x9A); + saa7110_write(sd, 0x0D, 0x86); + saa7110_write(sd, 0x0F, 0x10); + saa7110_write(sd, 0x11, 0x59); + /*saa7110_write(sd, 0x2E, 0x9A);*/ } - //saa7110_selmux(client,0); - //determine_norm(client); + /*saa7110_selmux(sd,0);*/ + /*determine_norm(sd);*/ /* setup and implicit mode 0 select has been performed */ return 0; @@ -468,7 +493,10 @@ static int saa7110_probe(struct i2c_client *client, static int saa7110_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_saa7110(sd)); return 0; } @@ -482,8 +510,6 @@ MODULE_DEVICE_TABLE(i2c, saa7110_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7110", - .driverid = I2C_DRIVERID_SAA7110, - .command = saa7110_command, .probe = saa7110_probe, .remove = saa7110_remove, .id_table = saa7110_id, diff --git a/drivers/media/video/saa7111.c b/drivers/media/video/saa7111.c deleted file mode 100644 index a4738a2..0000000 --- a/drivers/media/video/saa7111.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * saa7111 - Philips SAA7111A video decoder driver version 0.0.3 - * - * Copyright (C) 1998 Dave Perks <dperks@ibm.net> - * - * Slight changes for video timing and attachment output by - * Wolfgang Scherr <scherr@net4you.net> - * - * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net> - * - moved over to linux>=2.4.x i2c protocol (1/1/2003) - * - * Changes by Michael Hunold <michael@mihu.de> - * - implemented DECODER_SET_GPIO, DECODER_INIT, DECODER_SET_VBI_BYPASS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/ioctl.h> -#include <asm/uaccess.h> -#include <linux/i2c.h> -#include <linux/i2c-id.h> -#include <linux/videodev.h> -#include <linux/video_decoder.h> -#include <media/v4l2-common.h> -#include <media/v4l2-i2c-drv-legacy.h> - -MODULE_DESCRIPTION("Philips SAA7111 video decoder driver"); -MODULE_AUTHOR("Dave Perks"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/* ----------------------------------------------------------------------- */ - -#define SAA7111_NR_REG 0x18 - -struct saa7111 { - unsigned char reg[SAA7111_NR_REG]; - - int norm; - int input; - int enable; -}; - -/* ----------------------------------------------------------------------- */ - -static inline int saa7111_write(struct i2c_client *client, u8 reg, u8 value) -{ - struct saa7111 *decoder = i2c_get_clientdata(client); - - decoder->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -static inline void saa7111_write_if_changed(struct i2c_client *client, u8 reg, u8 value) -{ - struct saa7111 *decoder = i2c_get_clientdata(client); - - if (decoder->reg[reg] != value) { - decoder->reg[reg] = value; - i2c_smbus_write_byte_data(client, reg, value); - } -} - -static int saa7111_write_block(struct i2c_client *client, const u8 *data, unsigned int len) -{ - int ret = -1; - u8 reg; - - /* the saa7111 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - /* do raw I2C, not smbus compatible */ - struct saa7111 *decoder = i2c_get_clientdata(client); - u8 block_data[32]; - int block_len; - - while (len >= 2) { - block_len = 0; - block_data[block_len++] = reg = data[0]; - do { - block_data[block_len++] = - decoder->reg[reg++] = data[1]; - len -= 2; - data += 2; - } while (len >= 2 && data[0] == reg && block_len < 32); - ret = i2c_master_send(client, block_data, block_len); - if (ret < 0) - break; - } - } else { - /* do some slow I2C emulation kind of thing */ - while (len >= 2) { - reg = *data++; - ret = saa7111_write(client, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - } - - return ret; -} - -static int saa7111_init_decoder(struct i2c_client *client, - struct video_decoder_init *init) -{ - return saa7111_write_block(client, init->data, init->len); -} - -static inline int saa7111_read(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -/* ----------------------------------------------------------------------- */ - -static const unsigned char saa7111_i2c_init[] = { - 0x00, 0x00, /* 00 - ID byte */ - 0x01, 0x00, /* 01 - reserved */ - - /*front end */ - 0x02, 0xd0, /* 02 - FUSE=3, GUDL=2, MODE=0 */ - 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, - * HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */ - 0x04, 0x00, /* 04 - GAI1=256 */ - 0x05, 0x00, /* 05 - GAI2=256 */ - - /* decoder */ - 0x06, 0xf3, /* 06 - HSB at 13(50Hz) / 17(60Hz) - * pixels after end of last line */ - /*0x07, 0x13, * 07 - HSS at 113(50Hz) / 117(60Hz) pixels - * after end of last line */ - 0x07, 0xe8, /* 07 - HSS seems to be needed to - * work with NTSC, too */ - 0x08, 0xc8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, - * VTRC=1, HPLL=0, VNOI=0 */ - 0x09, 0x01, /* 09 - BYPS=0, PREF=0, BPSS=0, - * VBLB=0, UPTCV=0, APER=1 */ - 0x0a, 0x80, /* 0a - BRIG=128 */ - 0x0b, 0x47, /* 0b - CONT=1.109 */ - 0x0c, 0x40, /* 0c - SATN=1.0 */ - 0x0d, 0x00, /* 0d - HUE=0 */ - 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, - * FCTC=0, CHBW=1 */ - 0x0f, 0x00, /* 0f - reserved */ - 0x10, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ - 0x11, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, - * OEYC=1, OEHV=1, VIPB=0, COLO=0 */ - 0x12, 0x00, /* 12 - output control 2 */ - 0x13, 0x00, /* 13 - output control 3 */ - 0x14, 0x00, /* 14 - reserved */ - 0x15, 0x00, /* 15 - VBI */ - 0x16, 0x00, /* 16 - VBI */ - 0x17, 0x00, /* 17 - VBI */ -}; - -static int saa7111_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - struct saa7111 *decoder = i2c_get_clientdata(client); - - switch (cmd) { - case 0: - break; - case DECODER_INIT: - { - struct video_decoder_init *init = arg; - struct video_decoder_init vdi; - - if (NULL != init) - return saa7111_init_decoder(client, init); - vdi.data = saa7111_i2c_init; - vdi.len = sizeof(saa7111_i2c_init); - return saa7111_init_decoder(client, &vdi); - } - - case DECODER_DUMP: - { - int i; - - for (i = 0; i < SAA7111_NR_REG; i += 16) { - int j; - - v4l_info(client, "%03x", i); - for (j = 0; j < 16 && i + j < SAA7111_NR_REG; ++j) { - printk(KERN_CONT " %02x", - saa7111_read(client, i + j)); - } - printk(KERN_CONT "\n"); - } - break; - } - - case DECODER_GET_CAPABILITIES: - { - struct video_decoder_capability *cap = arg; - - cap->flags = VIDEO_DECODER_PAL | - VIDEO_DECODER_NTSC | - VIDEO_DECODER_SECAM | - VIDEO_DECODER_AUTO | - VIDEO_DECODER_CCIR; - cap->inputs = 8; - cap->outputs = 1; - break; - } - - case DECODER_GET_STATUS: - { - int *iarg = arg; - int status; - int res; - - status = saa7111_read(client, 0x1f); - v4l_dbg(1, debug, client, "status: 0x%02x\n", status); - res = 0; - if ((status & (1 << 6)) == 0) { - res |= DECODER_STATUS_GOOD; - } - switch (decoder->norm) { - case VIDEO_MODE_NTSC: - res |= DECODER_STATUS_NTSC; - break; - case VIDEO_MODE_PAL: - res |= DECODER_STATUS_PAL; - break; - case VIDEO_MODE_SECAM: - res |= DECODER_STATUS_SECAM; - break; - default: - case VIDEO_MODE_AUTO: - if ((status & (1 << 5)) != 0) { - res |= DECODER_STATUS_NTSC; - } else { - res |= DECODER_STATUS_PAL; - } - break; - } - if ((status & (1 << 0)) != 0) { - res |= DECODER_STATUS_COLOR; - } - *iarg = res; - break; - } - - case DECODER_SET_GPIO: - { - int *iarg = arg; - if (0 != *iarg) { - saa7111_write(client, 0x11, - (decoder->reg[0x11] | 0x80)); - } else { - saa7111_write(client, 0x11, - (decoder->reg[0x11] & 0x7f)); - } - break; - } - - case DECODER_SET_VBI_BYPASS: - { - int *iarg = arg; - if (0 != *iarg) { - saa7111_write(client, 0x13, - (decoder->reg[0x13] & 0xf0) | 0x0a); - } else { - saa7111_write(client, 0x13, - (decoder->reg[0x13] & 0xf0)); - } - break; - } - - case DECODER_SET_NORM: - { - int *iarg = arg; - - switch (*iarg) { - - case VIDEO_MODE_NTSC: - saa7111_write(client, 0x08, - (decoder->reg[0x08] & 0x3f) | 0x40); - saa7111_write(client, 0x0e, - (decoder->reg[0x0e] & 0x8f)); - break; - - case VIDEO_MODE_PAL: - saa7111_write(client, 0x08, - (decoder->reg[0x08] & 0x3f) | 0x00); - saa7111_write(client, 0x0e, - (decoder->reg[0x0e] & 0x8f)); - break; - - case VIDEO_MODE_SECAM: - saa7111_write(client, 0x08, - (decoder->reg[0x08] & 0x3f) | 0x00); - saa7111_write(client, 0x0e, - (decoder->reg[0x0e] & 0x8f) | 0x50); - break; - - case VIDEO_MODE_AUTO: - saa7111_write(client, 0x08, - (decoder->reg[0x08] & 0x3f) | 0x80); - saa7111_write(client, 0x0e, - (decoder->reg[0x0e] & 0x8f)); - break; - - default: - return -EINVAL; - - } - decoder->norm = *iarg; - break; - } - - case DECODER_SET_INPUT: - { - int *iarg = arg; - - if (*iarg < 0 || *iarg > 7) { - return -EINVAL; - } - - if (decoder->input != *iarg) { - decoder->input = *iarg; - /* select mode */ - saa7111_write(client, 0x02, - (decoder-> - reg[0x02] & 0xf8) | decoder->input); - /* bypass chrominance trap for modes 4..7 */ - saa7111_write(client, 0x09, - (decoder-> - reg[0x09] & 0x7f) | ((decoder-> - input > - 3) ? 0x80 : - 0)); - } - break; - } - - case DECODER_SET_OUTPUT: - { - int *iarg = arg; - - /* not much choice of outputs */ - if (*iarg != 0) { - return -EINVAL; - } - break; - } - - case DECODER_ENABLE_OUTPUT: - { - int *iarg = arg; - int enable = (*iarg != 0); - - if (decoder->enable != enable) { - decoder->enable = enable; - - /* RJ: If output should be disabled (for - * playing videos), we also need a open PLL. - * The input is set to 0 (where no input - * source is connected), although this - * is not necessary. - * - * If output should be enabled, we have to - * reverse the above. - */ - - if (decoder->enable) { - saa7111_write(client, 0x02, - (decoder-> - reg[0x02] & 0xf8) | - decoder->input); - saa7111_write(client, 0x08, - (decoder->reg[0x08] & 0xfb)); - saa7111_write(client, 0x11, - (decoder-> - reg[0x11] & 0xf3) | 0x0c); - } else { - saa7111_write(client, 0x02, - (decoder->reg[0x02] & 0xf8)); - saa7111_write(client, 0x08, - (decoder-> - reg[0x08] & 0xfb) | 0x04); - saa7111_write(client, 0x11, - (decoder->reg[0x11] & 0xf3)); - } - } - break; - } - - case DECODER_SET_PICTURE: - { - struct video_picture *pic = arg; - - /* We want 0 to 255 we get 0-65535 */ - saa7111_write_if_changed(client, 0x0a, pic->brightness >> 8); - /* We want 0 to 127 we get 0-65535 */ - saa7111_write(client, 0x0b, pic->contrast >> 9); - /* We want 0 to 127 we get 0-65535 */ - saa7111_write(client, 0x0c, pic->colour >> 9); - /* We want -128 to 127 we get 0-65535 */ - saa7111_write(client, 0x0d, (pic->hue - 32768) >> 8); - break; - } - - default: - return -EINVAL; - } - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static unsigned short normal_i2c[] = { 0x48 >> 1, I2C_CLIENT_END }; - -I2C_CLIENT_INSMOD; - -static int saa7111_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int i; - struct saa7111 *decoder; - struct video_decoder_init vdi; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - decoder = kzalloc(sizeof(struct saa7111), GFP_KERNEL); - if (decoder == NULL) { - kfree(client); - return -ENOMEM; - } - decoder->norm = VIDEO_MODE_NTSC; - decoder->input = 0; - decoder->enable = 1; - i2c_set_clientdata(client, decoder); - - vdi.data = saa7111_i2c_init; - vdi.len = sizeof(saa7111_i2c_init); - i = saa7111_init_decoder(client, &vdi); - if (i < 0) { - v4l_dbg(1, debug, client, "init status %d\n", i); - } else { - v4l_dbg(1, debug, client, "revision %x\n", - saa7111_read(client, 0x00) >> 4); - } - return 0; -} - -static int saa7111_remove(struct i2c_client *client) -{ - kfree(i2c_get_clientdata(client)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id saa7111_id[] = { - { "saa7111_old", 0 }, /* "saa7111" maps to the saa7115 driver */ - { } -}; -MODULE_DEVICE_TABLE(i2c, saa7111_id); - -static struct v4l2_i2c_driver_data v4l2_i2c_data = { - .name = "saa7111", - .driverid = I2C_DRIVERID_SAA7111A, - .command = saa7111_command, - .probe = saa7111_probe, - .remove = saa7111_remove, - .id_table = saa7111_id, -}; diff --git a/drivers/media/video/saa7114.c b/drivers/media/video/saa7114.c deleted file mode 100644 index 7ca709f..0000000 --- a/drivers/media/video/saa7114.c +++ /dev/null @@ -1,1068 +0,0 @@ -/* - * saa7114 - Philips SAA7114H video decoder driver version 0.0.1 - * - * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com> - * - * Based on saa7111 driver by Dave Perks - * - * Copyright (C) 1998 Dave Perks <dperks@ibm.net> - * - * Slight changes for video timing and attachment output by - * Wolfgang Scherr <scherr@net4you.net> - * - * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net> - * - moved over to linux>=2.4.x i2c protocol (1/1/2003) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/ioctl.h> -#include <asm/uaccess.h> -#include <linux/i2c.h> -#include <linux/i2c-id.h> -#include <linux/videodev.h> -#include <linux/video_decoder.h> -#include <media/v4l2-common.h> -#include <media/v4l2-i2c-drv-legacy.h> - -MODULE_DESCRIPTION("Philips SAA7114H video decoder driver"); -MODULE_AUTHOR("Maxim Yevtyushkin"); -MODULE_LICENSE("GPL"); - -static int debug; -module_param(debug, int, 0); -MODULE_PARM_DESC(debug, "Debug level (0-1)"); - -/* ----------------------------------------------------------------------- */ - -struct saa7114 { - unsigned char reg[0xf0 * 2]; - - int norm; - int input; - int enable; - int bright; - int contrast; - int hue; - int sat; - int playback; -}; - -#define I2C_DELAY 10 - - -//#define SAA_7114_NTSC_HSYNC_START (-3) -//#define SAA_7114_NTSC_HSYNC_STOP (-18) - -#define SAA_7114_NTSC_HSYNC_START (-17) -#define SAA_7114_NTSC_HSYNC_STOP (-32) - -//#define SAA_7114_NTSC_HOFFSET (5) -#define SAA_7114_NTSC_HOFFSET (6) -#define SAA_7114_NTSC_VOFFSET (10) -#define SAA_7114_NTSC_WIDTH (720) -#define SAA_7114_NTSC_HEIGHT (250) - -#define SAA_7114_SECAM_HSYNC_START (-17) -#define SAA_7114_SECAM_HSYNC_STOP (-32) - -#define SAA_7114_SECAM_HOFFSET (2) -#define SAA_7114_SECAM_VOFFSET (10) -#define SAA_7114_SECAM_WIDTH (720) -#define SAA_7114_SECAM_HEIGHT (300) - -#define SAA_7114_PAL_HSYNC_START (-17) -#define SAA_7114_PAL_HSYNC_STOP (-32) - -#define SAA_7114_PAL_HOFFSET (2) -#define SAA_7114_PAL_VOFFSET (10) -#define SAA_7114_PAL_WIDTH (720) -#define SAA_7114_PAL_HEIGHT (300) - - - -#define SAA_7114_VERTICAL_CHROMA_OFFSET 0 //0x50504040 -#define SAA_7114_VERTICAL_LUMA_OFFSET 0 - -#define REG_ADDR(x) (((x) << 1) + 1) -#define LOBYTE(x) ((unsigned char)((x) & 0xff)) -#define HIBYTE(x) ((unsigned char)(((x) >> 8) & 0xff)) -#define LOWORD(x) ((unsigned short int)((x) & 0xffff)) -#define HIWORD(x) ((unsigned short int)(((x) >> 16) & 0xffff)) - - -/* ----------------------------------------------------------------------- */ - -static inline int saa7114_write(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int saa7114_write_block(struct i2c_client *client, const u8 *data, unsigned int len) -{ - int ret = -1; - u8 reg; - - /* the saa7114 has an autoincrement function, use it if - * the adapter understands raw I2C */ - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - /* do raw I2C, not smbus compatible */ - u8 block_data[32]; - int block_len; - - while (len >= 2) { - block_len = 0; - block_data[block_len++] = reg = data[0]; - do { - block_data[block_len++] = data[1]; - reg++; - len -= 2; - data += 2; - } while (len >= 2 && data[0] == reg && block_len < 32); - ret = i2c_master_send(client, block_data, block_len); - if (ret < 0) - break; - } - } else { - /* do some slow I2C emulation kind of thing */ - while (len >= 2) { - reg = *data++; - ret = saa7114_write(client, reg, *data++); - if (ret < 0) - break; - len -= 2; - } - } - - return ret; -} - -static inline int saa7114_read(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -/* ----------------------------------------------------------------------- */ - -// initially set NTSC, composite - - -static const unsigned char init[] = { - 0x00, 0x00, /* 00 - ID byte , chip version, - * read only */ - 0x01, 0x08, /* 01 - X,X,X,X, IDEL3 to IDEL0 - - * horizontal increment delay, - * recommended position */ - 0x02, 0x00, /* 02 - FUSE=3, GUDL=2, MODE=0 ; - * input control */ - 0x03, 0x10, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, - * HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */ - 0x04, 0x90, /* 04 - GAI1=256 */ - 0x05, 0x90, /* 05 - GAI2=256 */ - 0x06, SAA_7114_NTSC_HSYNC_START, /* 06 - HSB: hsync start, - * depends on the video standard */ - 0x07, SAA_7114_NTSC_HSYNC_STOP, /* 07 - HSS: hsync stop, depends - *on the video standard */ - 0x08, 0xb8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1, - * HPLL: free running in playback, locked - * in capture, VNOI=0 */ - 0x09, 0x80, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0, - * UPTCV=0, APER=1; depends from input */ - 0x0a, 0x80, /* 0a - BRIG=128 */ - 0x0b, 0x44, /* 0b - CONT=1.109 */ - 0x0c, 0x40, /* 0c - SATN=1.0 */ - 0x0d, 0x00, /* 0d - HUE=0 */ - 0x0e, 0x84, /* 0e - CDTO, CSTD2 to 0, DCVF, FCTC, - * CCOMB; depends from video standard */ - 0x0f, 0x24, /* 0f - ACGC,CGAIN6 to CGAIN0; depends - * from video standard */ - 0x10, 0x03, /* 10 - OFFU1 to 0, OFFV1 to 0, CHBW, - * LCBW2 to 0 */ - 0x11, 0x59, /* 11 - COLO, RTP1, HEDL1 to 0, RTP0, - * YDEL2 to 0 */ - 0x12, 0xc9, /* 12 - RT signal control RTSE13 to 10 - * and 03 to 00 */ - 0x13, 0x80, /* 13 - RT/X port output control */ - 0x14, 0x00, /* 14 - analog, ADC, compatibility control */ - 0x15, 0x00, /* 15 - VGATE start FID change */ - 0x16, 0xfe, /* 16 - VGATE stop */ - 0x17, 0x00, /* 17 - Misc., VGATE MSBs */ - 0x18, 0x40, /* RAWG */ - 0x19, 0x80, /* RAWO */ - 0x1a, 0x00, - 0x1b, 0x00, - 0x1c, 0x00, - 0x1d, 0x00, - 0x1e, 0x00, - 0x1f, 0x00, /* status byte, read only */ - 0x20, 0x00, /* video decoder reserved part */ - 0x21, 0x00, - 0x22, 0x00, - 0x23, 0x00, - 0x24, 0x00, - 0x25, 0x00, - 0x26, 0x00, - 0x27, 0x00, - 0x28, 0x00, - 0x29, 0x00, - 0x2a, 0x00, - 0x2b, 0x00, - 0x2c, 0x00, - 0x2d, 0x00, - 0x2e, 0x00, - 0x2f, 0x00, - 0x30, 0xbc, /* audio clock generator */ - 0x31, 0xdf, - 0x32, 0x02, - 0x33, 0x00, - 0x34, 0xcd, - 0x35, 0xcc, - 0x36, 0x3a, - 0x37, 0x00, - 0x38, 0x03, - 0x39, 0x10, - 0x3a, 0x00, - 0x3b, 0x00, - 0x3c, 0x00, - 0x3d, 0x00, - 0x3e, 0x00, - 0x3f, 0x00, - 0x40, 0x00, /* VBI data slicer */ - 0x41, 0xff, - 0x42, 0xff, - 0x43, 0xff, - 0x44, 0xff, - 0x45, 0xff, - 0x46, 0xff, - 0x47, 0xff, - 0x48, 0xff, - 0x49, 0xff, - 0x4a, 0xff, - 0x4b, 0xff, - 0x4c, 0xff, - 0x4d, 0xff, - 0x4e, 0xff, - 0x4f, 0xff, - 0x50, 0xff, - 0x51, 0xff, - 0x52, 0xff, - 0x53, 0xff, - 0x54, 0xff, - 0x55, 0xff, - 0x56, 0xff, - 0x57, 0xff, - 0x58, 0x40, // framing code - 0x59, 0x47, // horizontal offset - 0x5a, 0x06, // vertical offset - 0x5b, 0x83, // field offset - 0x5c, 0x00, // reserved - 0x5d, 0x3e, // header and data - 0x5e, 0x00, // sliced data - 0x5f, 0x00, // reserved - 0x60, 0x00, /* video decoder reserved part */ - 0x61, 0x00, - 0x62, 0x00, - 0x63, 0x00, - 0x64, 0x00, - 0x65, 0x00, - 0x66, 0x00, - 0x67, 0x00, - 0x68, 0x00, - 0x69, 0x00, - 0x6a, 0x00, - 0x6b, 0x00, - 0x6c, 0x00, - 0x6d, 0x00, - 0x6e, 0x00, - 0x6f, 0x00, - 0x70, 0x00, /* video decoder reserved part */ - 0x71, 0x00, - 0x72, 0x00, - 0x73, 0x00, - 0x74, 0x00, - 0x75, 0x00, - 0x76, 0x00, - 0x77, 0x00, - 0x78, 0x00, - 0x79, 0x00, - 0x7a, 0x00, - 0x7b, 0x00, - 0x7c, 0x00, - 0x7d, 0x00, - 0x7e, 0x00, - 0x7f, 0x00, - 0x80, 0x00, /* X-port, I-port and scaler */ - 0x81, 0x00, - 0x82, 0x00, - 0x83, 0x00, - 0x84, 0xc5, - 0x85, 0x0d, // hsync and vsync ? - 0x86, 0x40, - 0x87, 0x01, - 0x88, 0x00, - 0x89, 0x00, - 0x8a, 0x00, - 0x8b, 0x00, - 0x8c, 0x00, - 0x8d, 0x00, - 0x8e, 0x00, - 0x8f, 0x00, - 0x90, 0x03, /* Task A definition */ - 0x91, 0x08, - 0x92, 0x00, - 0x93, 0x40, - 0x94, 0x00, // window settings - 0x95, 0x00, - 0x96, 0x00, - 0x97, 0x00, - 0x98, 0x00, - 0x99, 0x00, - 0x9a, 0x00, - 0x9b, 0x00, - 0x9c, 0x00, - 0x9d, 0x00, - 0x9e, 0x00, - 0x9f, 0x00, - 0xa0, 0x01, /* horizontal integer prescaling ratio */ - 0xa1, 0x00, /* horizontal prescaler accumulation - * sequence length */ - 0xa2, 0x00, /* UV FIR filter, Y FIR filter, prescaler - * DC gain */ - 0xa3, 0x00, - 0xa4, 0x80, // luminance brightness - 0xa5, 0x40, // luminance gain - 0xa6, 0x40, // chrominance saturation - 0xa7, 0x00, - 0xa8, 0x00, // horizontal luminance scaling increment - 0xa9, 0x04, - 0xaa, 0x00, // horizontal luminance phase offset - 0xab, 0x00, - 0xac, 0x00, // horizontal chrominance scaling increment - 0xad, 0x02, - 0xae, 0x00, // horizontal chrominance phase offset - 0xaf, 0x00, - 0xb0, 0x00, // vertical luminance scaling increment - 0xb1, 0x04, - 0xb2, 0x00, // vertical chrominance scaling increment - 0xb3, 0x04, - 0xb4, 0x00, - 0xb5, 0x00, - 0xb6, 0x00, - 0xb7, 0x00, - 0xb8, 0x00, - 0xb9, 0x00, - 0xba, 0x00, - 0xbb, 0x00, - 0xbc, 0x00, - 0xbd, 0x00, - 0xbe, 0x00, - 0xbf, 0x00, - 0xc0, 0x02, // Task B definition - 0xc1, 0x08, - 0xc2, 0x00, - 0xc3, 0x40, - 0xc4, 0x00, // window settings - 0xc5, 0x00, - 0xc6, 0x00, - 0xc7, 0x00, - 0xc8, 0x00, - 0xc9, 0x00, - 0xca, 0x00, - 0xcb, 0x00, - 0xcc, 0x00, - 0xcd, 0x00, - 0xce, 0x00, - 0xcf, 0x00, - 0xd0, 0x01, // horizontal integer prescaling ratio - 0xd1, 0x00, // horizontal prescaler accumulation sequence length - 0xd2, 0x00, // UV FIR filter, Y FIR filter, prescaler DC gain - 0xd3, 0x00, - 0xd4, 0x80, // luminance brightness - 0xd5, 0x40, // luminance gain - 0xd6, 0x40, // chrominance saturation - 0xd7, 0x00, - 0xd8, 0x00, // horizontal luminance scaling increment - 0xd9, 0x04, - 0xda, 0x00, // horizontal luminance phase offset - 0xdb, 0x00, - 0xdc, 0x00, // horizontal chrominance scaling increment - 0xdd, 0x02, - 0xde, 0x00, // horizontal chrominance phase offset - 0xdf, 0x00, - 0xe0, 0x00, // vertical luminance scaling increment - 0xe1, 0x04, - 0xe2, 0x00, // vertical chrominance scaling increment - 0xe3, 0x04, - 0xe4, 0x00, - 0xe5, 0x00, - 0xe6, 0x00, - 0xe7, 0x00, - 0xe8, 0x00, - 0xe9, 0x00, - 0xea, 0x00, - 0xeb, 0x00, - 0xec, 0x00, - 0xed, 0x00, - 0xee, 0x00, - 0xef, 0x00 -}; - -static int saa7114_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - struct saa7114 *decoder = i2c_get_clientdata(client); - - switch (cmd) { - case 0: - //dprintk(1, KERN_INFO "%s: writing init\n", I2C_NAME(client)); - //saa7114_write_block(client, init, sizeof(init)); - break; - - case DECODER_DUMP: - { - int i; - - if (!debug) - break; - v4l_info(client, "decoder dump\n"); - - for (i = 0; i < 32; i += 16) { - int j; - - v4l_info(client, "%03x", i); - for (j = 0; j < 16; ++j) { - printk(KERN_CONT " %02x", - saa7114_read(client, i + j)); - } - printk(KERN_CONT "\n"); - } - break; - } - - case DECODER_GET_CAPABILITIES: - { - struct video_decoder_capability *cap = arg; - - v4l_dbg(1, debug, client, "get capabilities\n"); - - cap->flags = VIDEO_DECODER_PAL | - VIDEO_DECODER_NTSC | - VIDEO_DECODER_AUTO | - VIDEO_DECODER_CCIR; - cap->inputs = 8; - cap->outputs = 1; - break; - } - - case DECODER_GET_STATUS: - { - int *iarg = arg; - int status; - int res; - - status = saa7114_read(client, 0x1f); - - v4l_dbg(1, debug, client, "status: 0x%02x\n", status); - res = 0; - if ((status & (1 << 6)) == 0) { - res |= DECODER_STATUS_GOOD; - } - switch (decoder->norm) { - case VIDEO_MODE_NTSC: - res |= DECODER_STATUS_NTSC; - break; - case VIDEO_MODE_PAL: - res |= DECODER_STATUS_PAL; - break; - case VIDEO_MODE_SECAM: - res |= DECODER_STATUS_SECAM; - break; - default: - case VIDEO_MODE_AUTO: - if ((status & (1 << 5)) != 0) { - res |= DECODER_STATUS_NTSC; - } else { - res |= DECODER_STATUS_PAL; - } - break; - } - if ((status & (1 << 0)) != 0) { - res |= DECODER_STATUS_COLOR; - } - *iarg = res; - break; - } - - case DECODER_SET_NORM: - { - int *iarg = arg; - - short int hoff = 0, voff = 0, w = 0, h = 0; - - v4l_dbg(1, debug, client, "set norm\n"); - - switch (*iarg) { - case VIDEO_MODE_NTSC: - v4l_dbg(1, debug, client, "NTSC\n"); - decoder->reg[REG_ADDR(0x06)] = - SAA_7114_NTSC_HSYNC_START; - decoder->reg[REG_ADDR(0x07)] = - SAA_7114_NTSC_HSYNC_STOP; - - decoder->reg[REG_ADDR(0x08)] = decoder->playback ? 0x7c : 0xb8; // PLL free when playback, PLL close when capture - - decoder->reg[REG_ADDR(0x0e)] = 0x85; - decoder->reg[REG_ADDR(0x0f)] = 0x24; - - hoff = SAA_7114_NTSC_HOFFSET; - voff = SAA_7114_NTSC_VOFFSET; - w = SAA_7114_NTSC_WIDTH; - h = SAA_7114_NTSC_HEIGHT; - - break; - - case VIDEO_MODE_PAL: - v4l_dbg(1, debug, client, "PAL\n"); - decoder->reg[REG_ADDR(0x06)] = - SAA_7114_PAL_HSYNC_START; - decoder->reg[REG_ADDR(0x07)] = - SAA_7114_PAL_HSYNC_STOP; - - decoder->reg[REG_ADDR(0x08)] = decoder->playback ? 0x7c : 0xb8; // PLL free when playback, PLL close when capture - - decoder->reg[REG_ADDR(0x0e)] = 0x81; - decoder->reg[REG_ADDR(0x0f)] = 0x24; - - hoff = SAA_7114_PAL_HOFFSET; - voff = SAA_7114_PAL_VOFFSET; - w = SAA_7114_PAL_WIDTH; - h = SAA_7114_PAL_HEIGHT; - - break; - - default: - v4l_dbg(1, debug, client, "Unknown video mode\n"); - return -EINVAL; - } - - - decoder->reg[REG_ADDR(0x94)] = LOBYTE(hoff); // hoffset low - decoder->reg[REG_ADDR(0x95)] = HIBYTE(hoff) & 0x0f; // hoffset high - decoder->reg[REG_ADDR(0x96)] = LOBYTE(w); // width low - decoder->reg[REG_ADDR(0x97)] = HIBYTE(w) & 0x0f; // width high - decoder->reg[REG_ADDR(0x98)] = LOBYTE(voff); // voffset low - decoder->reg[REG_ADDR(0x99)] = HIBYTE(voff) & 0x0f; // voffset high - decoder->reg[REG_ADDR(0x9a)] = LOBYTE(h + 2); // height low - decoder->reg[REG_ADDR(0x9b)] = HIBYTE(h + 2) & 0x0f; // height high - decoder->reg[REG_ADDR(0x9c)] = LOBYTE(w); // out width low - decoder->reg[REG_ADDR(0x9d)] = HIBYTE(w) & 0x0f; // out width high - decoder->reg[REG_ADDR(0x9e)] = LOBYTE(h); // out height low - decoder->reg[REG_ADDR(0x9f)] = HIBYTE(h) & 0x0f; // out height high - - decoder->reg[REG_ADDR(0xc4)] = LOBYTE(hoff); // hoffset low - decoder->reg[REG_ADDR(0xc5)] = HIBYTE(hoff) & 0x0f; // hoffset high - decoder->reg[REG_ADDR(0xc6)] = LOBYTE(w); // width low - decoder->reg[REG_ADDR(0xc7)] = HIBYTE(w) & 0x0f; // width high - decoder->reg[REG_ADDR(0xc8)] = LOBYTE(voff); // voffset low - decoder->reg[REG_ADDR(0xc9)] = HIBYTE(voff) & 0x0f; // voffset high - decoder->reg[REG_ADDR(0xca)] = LOBYTE(h + 2); // height low - decoder->reg[REG_ADDR(0xcb)] = HIBYTE(h + 2) & 0x0f; // height high - decoder->reg[REG_ADDR(0xcc)] = LOBYTE(w); // out width low - decoder->reg[REG_ADDR(0xcd)] = HIBYTE(w) & 0x0f; // out width high - decoder->reg[REG_ADDR(0xce)] = LOBYTE(h); // out height low - decoder->reg[REG_ADDR(0xcf)] = HIBYTE(h) & 0x0f; // out height high - - - saa7114_write(client, 0x80, 0x06); // i-port and scaler back end clock selection, task A&B off - saa7114_write(client, 0x88, 0xd8); // sw reset scaler - saa7114_write(client, 0x88, 0xf8); // sw reset scaler release - - saa7114_write_block(client, decoder->reg + (0x06 << 1), - 3 << 1); - saa7114_write_block(client, decoder->reg + (0x0e << 1), - 2 << 1); - saa7114_write_block(client, decoder->reg + (0x5a << 1), - 2 << 1); - - saa7114_write_block(client, decoder->reg + (0x94 << 1), - (0x9f + 1 - 0x94) << 1); - saa7114_write_block(client, decoder->reg + (0xc4 << 1), - (0xcf + 1 - 0xc4) << 1); - - saa7114_write(client, 0x88, 0xd8); // sw reset scaler - saa7114_write(client, 0x88, 0xf8); // sw reset scaler release - saa7114_write(client, 0x80, 0x36); // i-port and scaler back end clock selection - - decoder->norm = *iarg; - break; - } - - case DECODER_SET_INPUT: - { - int *iarg = arg; - - v4l_dbg(1, debug, client, "set input (%d)\n", *iarg); - if (*iarg < 0 || *iarg > 7) { - return -EINVAL; - } - - if (decoder->input != *iarg) { - v4l_dbg(1, debug, client, "now setting %s input\n", - *iarg >= 6 ? "S-Video" : "Composite"); - decoder->input = *iarg; - - /* select mode */ - decoder->reg[REG_ADDR(0x02)] = - (decoder-> - reg[REG_ADDR(0x02)] & 0xf0) | (decoder-> - input < - 6 ? 0x0 : 0x9); - saa7114_write(client, 0x02, - decoder->reg[REG_ADDR(0x02)]); - - /* bypass chrominance trap for modes 6..9 */ - decoder->reg[REG_ADDR(0x09)] = - (decoder-> - reg[REG_ADDR(0x09)] & 0x7f) | (decoder-> - input < - 6 ? 0x0 : - 0x80); - saa7114_write(client, 0x09, - decoder->reg[REG_ADDR(0x09)]); - - decoder->reg[REG_ADDR(0x0e)] = - decoder->input < - 6 ? decoder-> - reg[REG_ADDR(0x0e)] | 1 : decoder-> - reg[REG_ADDR(0x0e)] & ~1; - saa7114_write(client, 0x0e, - decoder->reg[REG_ADDR(0x0e)]); - } - break; - } - - case DECODER_SET_OUTPUT: - { - int *iarg = arg; - - v4l_dbg(1, debug, client, "set output\n"); - - /* not much choice of outputs */ - if (*iarg != 0) { - return -EINVAL; - } - break; - } - - case DECODER_ENABLE_OUTPUT: - { - int *iarg = arg; - int enable = (*iarg != 0); - - v4l_dbg(1, debug, client, "%s output\n", - enable ? "enable" : "disable"); - - decoder->playback = !enable; - - if (decoder->enable != enable) { - decoder->enable = enable; - - /* RJ: If output should be disabled (for - * playing videos), we also need a open PLL. - * The input is set to 0 (where no input - * source is connected), although this - * is not necessary. - * - * If output should be enabled, we have to - * reverse the above. - */ - - if (decoder->enable) { - decoder->reg[REG_ADDR(0x08)] = 0xb8; - decoder->reg[REG_ADDR(0x12)] = 0xc9; - decoder->reg[REG_ADDR(0x13)] = 0x80; - decoder->reg[REG_ADDR(0x87)] = 0x01; - } else { - decoder->reg[REG_ADDR(0x08)] = 0x7c; - decoder->reg[REG_ADDR(0x12)] = 0x00; - decoder->reg[REG_ADDR(0x13)] = 0x00; - decoder->reg[REG_ADDR(0x87)] = 0x00; - } - - saa7114_write_block(client, - decoder->reg + (0x12 << 1), - 2 << 1); - saa7114_write(client, 0x08, - decoder->reg[REG_ADDR(0x08)]); - saa7114_write(client, 0x87, - decoder->reg[REG_ADDR(0x87)]); - saa7114_write(client, 0x88, 0xd8); // sw reset scaler - saa7114_write(client, 0x88, 0xf8); // sw reset scaler release - saa7114_write(client, 0x80, 0x36); - - } - break; - } - - case DECODER_SET_PICTURE: - { - struct video_picture *pic = arg; - - v4l_dbg(1, debug, client, - "decoder set picture bright=%d contrast=%d saturation=%d hue=%d\n", - pic->brightness, pic->contrast, pic->colour, pic->hue); - - if (decoder->bright != pic->brightness) { - /* We want 0 to 255 we get 0-65535 */ - decoder->bright = pic->brightness; - saa7114_write(client, 0x0a, decoder->bright >> 8); - } - if (decoder->contrast != pic->contrast) { - /* We want 0 to 127 we get 0-65535 */ - decoder->contrast = pic->contrast; - saa7114_write(client, 0x0b, - decoder->contrast >> 9); - } - if (decoder->sat != pic->colour) { - /* We want 0 to 127 we get 0-65535 */ - decoder->sat = pic->colour; - saa7114_write(client, 0x0c, decoder->sat >> 9); - } - if (decoder->hue != pic->hue) { - /* We want -128 to 127 we get 0-65535 */ - decoder->hue = pic->hue; - saa7114_write(client, 0x0d, - (decoder->hue - 32768) >> 8); - } - break; - } - - default: - return -EINVAL; - } - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static unsigned short normal_i2c[] = { 0x42 >> 1, 0x40 >> 1, I2C_CLIENT_END }; - -I2C_CLIENT_INSMOD; - -static int saa7114_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int i, err[30]; - short int hoff = SAA_7114_NTSC_HOFFSET; - short int voff = SAA_7114_NTSC_VOFFSET; - short int w = SAA_7114_NTSC_WIDTH; - short int h = SAA_7114_NTSC_HEIGHT; - struct saa7114 *decoder; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - decoder = kzalloc(sizeof(struct saa7114), GFP_KERNEL); - if (decoder == NULL) - return -ENOMEM; - decoder->norm = VIDEO_MODE_NTSC; - decoder->input = -1; - decoder->enable = 1; - decoder->bright = 32768; - decoder->contrast = 32768; - decoder->hue = 32768; - decoder->sat = 32768; - decoder->playback = 0; // initially capture mode useda - i2c_set_clientdata(client, decoder); - - memcpy(decoder->reg, init, sizeof(init)); - - decoder->reg[REG_ADDR(0x94)] = LOBYTE(hoff); // hoffset low - decoder->reg[REG_ADDR(0x95)] = HIBYTE(hoff) & 0x0f; // hoffset high - decoder->reg[REG_ADDR(0x96)] = LOBYTE(w); // width low - decoder->reg[REG_ADDR(0x97)] = HIBYTE(w) & 0x0f; // width high - decoder->reg[REG_ADDR(0x98)] = LOBYTE(voff); // voffset low - decoder->reg[REG_ADDR(0x99)] = HIBYTE(voff) & 0x0f; // voffset high - decoder->reg[REG_ADDR(0x9a)] = LOBYTE(h + 2); // height low - decoder->reg[REG_ADDR(0x9b)] = HIBYTE(h + 2) & 0x0f; // height high - decoder->reg[REG_ADDR(0x9c)] = LOBYTE(w); // out width low - decoder->reg[REG_ADDR(0x9d)] = HIBYTE(w) & 0x0f; // out width high - decoder->reg[REG_ADDR(0x9e)] = LOBYTE(h); // out height low - decoder->reg[REG_ADDR(0x9f)] = HIBYTE(h) & 0x0f; // out height high - - decoder->reg[REG_ADDR(0xc4)] = LOBYTE(hoff); // hoffset low - decoder->reg[REG_ADDR(0xc5)] = HIBYTE(hoff) & 0x0f; // hoffset high - decoder->reg[REG_ADDR(0xc6)] = LOBYTE(w); // width low - decoder->reg[REG_ADDR(0xc7)] = HIBYTE(w) & 0x0f; // width high - decoder->reg[REG_ADDR(0xc8)] = LOBYTE(voff); // voffset low - decoder->reg[REG_ADDR(0xc9)] = HIBYTE(voff) & 0x0f; // voffset high - decoder->reg[REG_ADDR(0xca)] = LOBYTE(h + 2); // height low - decoder->reg[REG_ADDR(0xcb)] = HIBYTE(h + 2) & 0x0f; // height high - decoder->reg[REG_ADDR(0xcc)] = LOBYTE(w); // out width low - decoder->reg[REG_ADDR(0xcd)] = HIBYTE(w) & 0x0f; // out width high - decoder->reg[REG_ADDR(0xce)] = LOBYTE(h); // out height low - decoder->reg[REG_ADDR(0xcf)] = HIBYTE(h) & 0x0f; // out height high - - decoder->reg[REG_ADDR(0xb8)] = - LOBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); - decoder->reg[REG_ADDR(0xb9)] = - HIBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); - decoder->reg[REG_ADDR(0xba)] = - LOBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); - decoder->reg[REG_ADDR(0xbb)] = - HIBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); - - decoder->reg[REG_ADDR(0xbc)] = - LOBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); - decoder->reg[REG_ADDR(0xbd)] = - HIBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); - decoder->reg[REG_ADDR(0xbe)] = - LOBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); - decoder->reg[REG_ADDR(0xbf)] = - HIBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); - - decoder->reg[REG_ADDR(0xe8)] = - LOBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); - decoder->reg[REG_ADDR(0xe9)] = - HIBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); - decoder->reg[REG_ADDR(0xea)] = - LOBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); - decoder->reg[REG_ADDR(0xeb)] = - HIBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET)); - - decoder->reg[REG_ADDR(0xec)] = - LOBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); - decoder->reg[REG_ADDR(0xed)] = - HIBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); - decoder->reg[REG_ADDR(0xee)] = - LOBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); - decoder->reg[REG_ADDR(0xef)] = - HIBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET)); - - - decoder->reg[REG_ADDR(0x13)] = 0x80; // RTC0 on - decoder->reg[REG_ADDR(0x87)] = 0x01; // I-Port - decoder->reg[REG_ADDR(0x12)] = 0xc9; // RTS0 - - decoder->reg[REG_ADDR(0x02)] = 0xc0; // set composite1 input, aveasy - decoder->reg[REG_ADDR(0x09)] = 0x00; // chrominance trap - decoder->reg[REG_ADDR(0x0e)] |= 1; // combfilter on - - - v4l_dbg(1, debug, client, "starting init\n"); - - err[0] = - saa7114_write_block(client, decoder->reg + (0x20 << 1), - 0x10 << 1); - err[1] = - saa7114_write_block(client, decoder->reg + (0x30 << 1), - 0x10 << 1); - err[2] = - saa7114_write_block(client, decoder->reg + (0x63 << 1), - (0x7f + 1 - 0x63) << 1); - err[3] = - saa7114_write_block(client, decoder->reg + (0x89 << 1), - 6 << 1); - err[4] = - saa7114_write_block(client, decoder->reg + (0xb8 << 1), - 8 << 1); - err[5] = - saa7114_write_block(client, decoder->reg + (0xe8 << 1), - 8 << 1); - - - for (i = 0; i <= 5; i++) { - if (err[i] < 0) { - v4l_dbg(1, debug, client, - "init error %d at stage %d, leaving attach.\n", - i, err[i]); - kfree(decoder); - return -EIO; - } - } - - for (i = 6; i < 8; i++) { - v4l_dbg(1, debug, client, - "reg[0x%02x] = 0x%02x (0x%02x)\n", - i, saa7114_read(client, i), - decoder->reg[REG_ADDR(i)]); - } - - v4l_dbg(1, debug, client, - "performing decoder reset sequence\n"); - - err[6] = saa7114_write(client, 0x80, 0x06); // i-port and scaler backend clock selection, task A&B off - err[7] = saa7114_write(client, 0x88, 0xd8); // sw reset scaler - err[8] = saa7114_write(client, 0x88, 0xf8); // sw reset scaler release - - for (i = 6; i <= 8; i++) { - if (err[i] < 0) { - v4l_dbg(1, debug, client, - "init error %d at stage %d, leaving attach.\n", - i, err[i]); - kfree(decoder); - return -EIO; - } - } - - v4l_dbg(1, debug, client, "performing the rest of init\n"); - - err[9] = saa7114_write(client, 0x01, decoder->reg[REG_ADDR(0x01)]); - err[10] = saa7114_write_block(client, decoder->reg + (0x03 << 1), (0x1e + 1 - 0x03) << 1); // big seq - err[11] = saa7114_write_block(client, decoder->reg + (0x40 << 1), (0x5f + 1 - 0x40) << 1); // slicer - err[12] = saa7114_write_block(client, decoder->reg + (0x81 << 1), 2 << 1); // ? - err[13] = saa7114_write_block(client, decoder->reg + (0x83 << 1), 5 << 1); // ? - err[14] = saa7114_write_block(client, decoder->reg + (0x90 << 1), 4 << 1); // Task A - err[15] = - saa7114_write_block(client, decoder->reg + (0x94 << 1), - 12 << 1); - err[16] = - saa7114_write_block(client, decoder->reg + (0xa0 << 1), - 8 << 1); - err[17] = - saa7114_write_block(client, decoder->reg + (0xa8 << 1), - 8 << 1); - err[18] = - saa7114_write_block(client, decoder->reg + (0xb0 << 1), - 8 << 1); - err[19] = saa7114_write_block(client, decoder->reg + (0xc0 << 1), 4 << 1); // Task B - err[15] = - saa7114_write_block(client, decoder->reg + (0xc4 << 1), - 12 << 1); - err[16] = - saa7114_write_block(client, decoder->reg + (0xd0 << 1), - 8 << 1); - err[17] = - saa7114_write_block(client, decoder->reg + (0xd8 << 1), - 8 << 1); - err[18] = - saa7114_write_block(client, decoder->reg + (0xe0 << 1), - 8 << 1); - - for (i = 9; i <= 18; i++) { - if (err[i] < 0) { - v4l_dbg(1, debug, client, - "init error %d at stage %d, leaving attach.\n", - i, err[i]); - kfree(decoder); - return -EIO; - } - } - - - for (i = 6; i < 8; i++) { - v4l_dbg(1, debug, client, - "reg[0x%02x] = 0x%02x (0x%02x)\n", - i, saa7114_read(client, i), - decoder->reg[REG_ADDR(i)]); - } - - - for (i = 0x11; i <= 0x13; i++) { - v4l_dbg(1, debug, client, - "reg[0x%02x] = 0x%02x (0x%02x)\n", - i, saa7114_read(client, i), - decoder->reg[REG_ADDR(i)]); - } - - - v4l_dbg(1, debug, client, "setting video input\n"); - - err[19] = - saa7114_write(client, 0x02, decoder->reg[REG_ADDR(0x02)]); - err[20] = - saa7114_write(client, 0x09, decoder->reg[REG_ADDR(0x09)]); - err[21] = - saa7114_write(client, 0x0e, decoder->reg[REG_ADDR(0x0e)]); - - for (i = 19; i <= 21; i++) { - if (err[i] < 0) { - v4l_dbg(1, debug, client, - "init error %d at stage %d, leaving attach.\n", - i, err[i]); - kfree(decoder); - return -EIO; - } - } - - v4l_dbg(1, debug, client, "performing decoder reset sequence\n"); - - err[22] = saa7114_write(client, 0x88, 0xd8); // sw reset scaler - err[23] = saa7114_write(client, 0x88, 0xf8); // sw reset scaler release - err[24] = saa7114_write(client, 0x80, 0x36); // i-port and scaler backend clock selection, task A&B off - - - for (i = 22; i <= 24; i++) { - if (err[i] < 0) { - v4l_dbg(1, debug, client, - "init error %d at stage %d, leaving attach.\n", - i, err[i]); - kfree(decoder); - return -EIO; - } - } - - err[25] = saa7114_write(client, 0x06, init[REG_ADDR(0x06)]); - err[26] = saa7114_write(client, 0x07, init[REG_ADDR(0x07)]); - err[27] = saa7114_write(client, 0x10, init[REG_ADDR(0x10)]); - - v4l_dbg(1, debug, client, "chip version %x, decoder status 0x%02x\n", - saa7114_read(client, 0x00) >> 4, - saa7114_read(client, 0x1f)); - v4l_dbg(1, debug, client, - "power save control: 0x%02x, scaler status: 0x%02x\n", - saa7114_read(client, 0x88), - saa7114_read(client, 0x8f)); - - - for (i = 0x94; i < 0x96; i++) { - v4l_dbg(1, debug, client, - "reg[0x%02x] = 0x%02x (0x%02x)\n", - i, saa7114_read(client, i), - decoder->reg[REG_ADDR(i)]); - } - - //i = saa7114_write_block(client, init, sizeof(init)); - return 0; -} - -static int saa7114_remove(struct i2c_client *client) -{ - kfree(i2c_get_clientdata(client)); - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id saa7114_id[] = { - { "saa7114_old", 0 }, /* "saa7114" maps to the saa7115 driver */ - { } -}; -MODULE_DEVICE_TABLE(i2c, saa7114_id); - -static struct v4l2_i2c_driver_data v4l2_i2c_data = { - .name = "saa7114", - .driverid = I2C_DRIVERID_SAA7114, - .command = saa7114_command, - .probe = saa7114_probe, - .remove = saa7114_remove, - .id_table = saa7114_id, -}; diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 46c796c..cebf159 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -778,7 +778,7 @@ static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) break; case V4L2_CID_HUE: - if (ctrl->value < -127 || ctrl->value > 127) { + if (ctrl->value < -128 || ctrl->value > 127) { v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } @@ -931,8 +931,8 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) /* Prevent unnecessary standard changes. During a standard change the I-Port is temporarily disabled. Any devices reading from that port can get confused. - Note that VIDIOC_S_STD is also used to switch from - radio to TV mode, so if a VIDIOC_S_STD is broadcast to + Note that s_std is also used to switch from + radio to TV mode, so if a s_std is broadcast to all I2C devices then you do not want to have an unwanted side-effect here. */ if (std == state->std) @@ -1206,10 +1206,12 @@ static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { switch (qc->id) { case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); default: return -EINVAL; } @@ -1308,11 +1310,12 @@ static int saa711x_s_stream(struct v4l2_subdev *sd, int enable) v4l2_dbg(1, debug, sd, "%s output\n", enable ? "enable" : "disable"); - if (state->enable != enable) { - state->enable = enable; - saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, - state->enable); - } + if (state->enable == enable) + return 0; + state->enable = enable; + if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED)) + return 0; + saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable); return 0; } @@ -1370,6 +1373,47 @@ static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_dat } } +static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct saa711x_state *state = to_state(sd); + int reg1e; + + *std = V4L2_STD_ALL; + if (state->ident != V4L2_IDENT_SAA7115) + return 0; + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + + switch (reg1e & 0x03) { + case 1: + *std = V4L2_STD_NTSC; + break; + case 2: + *std = V4L2_STD_PAL; + break; + case 3: + *std = V4L2_STD_SECAM; + break; + default: + break; + } + return 0; +} + +static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct saa711x_state *state = to_state(sd); + int reg1e = 0x80; + int reg1f; + + *status = V4L2_IN_ST_NO_SIGNAL; + if (state->ident == V4L2_IDENT_SAA7115) + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80) + *status = 0; + return 0; +} + #ifdef CONFIG_VIDEO_ADV_DEBUG static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { @@ -1493,6 +1537,8 @@ static const struct v4l2_subdev_video_ops saa711x_video_ops = { .g_vbi_data = saa711x_g_vbi_data, .decode_vbi_line = saa711x_decode_vbi_line, .s_stream = saa711x_s_stream, + .querystd = saa711x_querystd, + .g_input_status = saa711x_g_input_status, }; static const struct v4l2_subdev_ops saa711x_ops = { diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index 05221d4..128bb8b 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -810,7 +810,6 @@ MODULE_DEVICE_TABLE(i2c, saa7127_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7127", - .driverid = I2C_DRIVERID_SAA7127, .probe = saa7127_probe, .remove = saa7127_remove, .id_table = saa7127_id, diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index fc2164e..0ba6898 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -6,6 +6,7 @@ config VIDEO_SAA7134 select VIDEO_TUNER select VIDEO_TVEEPROM select CRC32 + select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO ---help--- This is a video4linux driver for Philips SAA713x based TV cards. @@ -35,8 +36,16 @@ config VIDEO_SAA7134_DVB select DVB_TDA10086 if !DVB_FE_CUSTOMISE select DVB_TDA826X if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE + select DVB_ISL6405 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select DVB_ZL10036 if !DVB_FE_CUSTOMISE + select DVB_MT312 if !DVB_FE_CUSTOMISE + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_LGDT3305 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_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 1fee6e8..dc2213e 100644 --- a/drivers/media/video/saa7134/saa6752hs.c +++ b/drivers/media/video/saa7134/saa6752hs.c @@ -33,9 +33,10 @@ #include <linux/i2c.h> #include <linux/types.h> #include <linux/videodev2.h> +#include <media/v4l2-device.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include <linux/init.h> #include <linux/crc32.h> @@ -44,10 +45,6 @@ #define MPEG_TOTAL_TARGET_BITRATE_MAX 27000 #define MPEG_PID_MAX ((1 << 14) - 1) -/* Addresses to scan */ -static unsigned short normal_i2c[] = {0x20, I2C_CLIENT_END}; - -I2C_CLIENT_INSMOD; MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder"); MODULE_AUTHOR("Andrew de Quincey"); @@ -95,6 +92,7 @@ static const struct v4l2_format v4l2_format_table[] = }; struct saa6752hs_state { + struct v4l2_subdev sd; int chip; u32 revision; int has_ac3; @@ -115,6 +113,11 @@ enum saa6752hs_command { SAA6752HS_COMMAND_MAX }; +static inline struct saa6752hs_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa6752hs_state, sd); +} + /* ---------------------------------------------------------------------- */ static u8 PAT[] = { @@ -360,185 +363,191 @@ static int saa6752hs_set_bitrate(struct i2c_client *client, return 0; } -static void saa6752hs_set_subsampling(struct i2c_client *client, - struct v4l2_format *f) -{ - struct saa6752hs_state *h = i2c_get_clientdata(client); - int dist_352, dist_480, dist_720; - - /* - FIXME: translate and round width/height into EMPRESS - subsample type: - type | PAL | NTSC - --------------------------- - SIF | 352x288 | 352x240 - 1/2 D1 | 352x576 | 352x480 - 2/3 D1 | 480x576 | 480x480 - D1 | 720x576 | 720x480 - */ - - dist_352 = abs(f->fmt.pix.width - 352); - dist_480 = abs(f->fmt.pix.width - 480); - dist_720 = abs(f->fmt.pix.width - 720); - if (dist_720 < dist_480) { - f->fmt.pix.width = 720; - f->fmt.pix.height = 576; - h->video_format = SAA6752HS_VF_D1; - } - else if (dist_480 < dist_352) { - f->fmt.pix.width = 480; - f->fmt.pix.height = 576; - h->video_format = SAA6752HS_VF_2_3_D1; - } - else { - f->fmt.pix.width = 352; - if (abs(f->fmt.pix.height - 576) < - abs(f->fmt.pix.height - 288)) { - f->fmt.pix.height = 576; - h->video_format = SAA6752HS_VF_1_2_D1; - } - else { - f->fmt.pix.height = 288; - h->video_format = SAA6752HS_VF_SIF; - } +static int get_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params, + struct v4l2_ext_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_TYPE: + ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; + break; + case V4L2_CID_MPEG_STREAM_PID_PMT: + ctrl->value = params->ts_pid_pmt; + break; + case V4L2_CID_MPEG_STREAM_PID_AUDIO: + ctrl->value = params->ts_pid_audio; + break; + case V4L2_CID_MPEG_STREAM_PID_VIDEO: + ctrl->value = params->ts_pid_video; + break; + case V4L2_CID_MPEG_STREAM_PID_PCR: + ctrl->value = params->ts_pid_pcr; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + ctrl->value = params->au_encoding; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + ctrl->value = params->au_l2_bitrate; + break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!has_ac3) + return -EINVAL; + ctrl->value = params->au_ac3_bitrate; + break; + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + ctrl->value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + ctrl->value = params->vi_aspect; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrl->value = params->vi_bitrate * 1000; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + ctrl->value = params->vi_bitrate_peak * 1000; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctrl->value = params->vi_bitrate_mode; + break; + default: + return -EINVAL; } + return 0; } - static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params, - struct v4l2_ext_control *ctrl, unsigned int cmd) + struct v4l2_ext_control *ctrl, int set) { int old = 0, new; - int set = (cmd == VIDIOC_S_EXT_CTRLS); new = ctrl->value; switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_TYPE: - old = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; - if (set && new != old) - return -ERANGE; - new = old; - break; - case V4L2_CID_MPEG_STREAM_PID_PMT: - old = params->ts_pid_pmt; - if (set && new > MPEG_PID_MAX) - return -ERANGE; - if (new > MPEG_PID_MAX) - new = MPEG_PID_MAX; - params->ts_pid_pmt = new; - break; - case V4L2_CID_MPEG_STREAM_PID_AUDIO: - old = params->ts_pid_audio; - if (set && new > MPEG_PID_MAX) - return -ERANGE; - if (new > MPEG_PID_MAX) - new = MPEG_PID_MAX; - params->ts_pid_audio = new; - break; - case V4L2_CID_MPEG_STREAM_PID_VIDEO: - old = params->ts_pid_video; - if (set && new > MPEG_PID_MAX) - return -ERANGE; - if (new > MPEG_PID_MAX) - new = MPEG_PID_MAX; - params->ts_pid_video = new; - break; - case V4L2_CID_MPEG_STREAM_PID_PCR: - old = params->ts_pid_pcr; - if (set && new > MPEG_PID_MAX) - return -ERANGE; - if (new > MPEG_PID_MAX) - new = MPEG_PID_MAX; - params->ts_pid_pcr = new; - break; - case V4L2_CID_MPEG_AUDIO_ENCODING: - old = params->au_encoding; - if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && - (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3)) - return -ERANGE; - new = old; - break; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - old = params->au_l2_bitrate; - if (set && new != V4L2_MPEG_AUDIO_L2_BITRATE_256K && - new != V4L2_MPEG_AUDIO_L2_BITRATE_384K) - return -ERANGE; - if (new <= V4L2_MPEG_AUDIO_L2_BITRATE_256K) - new = V4L2_MPEG_AUDIO_L2_BITRATE_256K; - else - new = V4L2_MPEG_AUDIO_L2_BITRATE_384K; - params->au_l2_bitrate = new; - break; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - if (!has_ac3) - return -EINVAL; - old = params->au_ac3_bitrate; - if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K && - new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K) - return -ERANGE; - if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K) - new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K; - else - new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K; - params->au_ac3_bitrate = new; - break; - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000; - if (set && new != old) - return -ERANGE; - new = old; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - old = V4L2_MPEG_VIDEO_ENCODING_MPEG_2; - if (set && new != old) - return -ERANGE; - new = old; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - old = params->vi_aspect; - if (set && new != V4L2_MPEG_VIDEO_ASPECT_16x9 && - new != V4L2_MPEG_VIDEO_ASPECT_4x3) - return -ERANGE; - if (new != V4L2_MPEG_VIDEO_ASPECT_16x9) - new = V4L2_MPEG_VIDEO_ASPECT_4x3; - params->vi_aspect = new; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - old = params->vi_bitrate * 1000; - new = 1000 * (new / 1000); - if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) - return -ERANGE; - if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) - new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000; - params->vi_bitrate = new / 1000; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - old = params->vi_bitrate_peak * 1000; - new = 1000 * (new / 1000); - if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) - return -ERANGE; - if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) - new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000; - params->vi_bitrate_peak = new / 1000; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - old = params->vi_bitrate_mode; - params->vi_bitrate_mode = new; - break; - default: + case V4L2_CID_MPEG_STREAM_TYPE: + old = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; + if (set && new != old) + return -ERANGE; + new = old; + break; + case V4L2_CID_MPEG_STREAM_PID_PMT: + old = params->ts_pid_pmt; + if (set && new > MPEG_PID_MAX) + return -ERANGE; + if (new > MPEG_PID_MAX) + new = MPEG_PID_MAX; + params->ts_pid_pmt = new; + break; + case V4L2_CID_MPEG_STREAM_PID_AUDIO: + old = params->ts_pid_audio; + if (set && new > MPEG_PID_MAX) + return -ERANGE; + if (new > MPEG_PID_MAX) + new = MPEG_PID_MAX; + params->ts_pid_audio = new; + break; + case V4L2_CID_MPEG_STREAM_PID_VIDEO: + old = params->ts_pid_video; + if (set && new > MPEG_PID_MAX) + return -ERANGE; + if (new > MPEG_PID_MAX) + new = MPEG_PID_MAX; + params->ts_pid_video = new; + break; + case V4L2_CID_MPEG_STREAM_PID_PCR: + old = params->ts_pid_pcr; + if (set && new > MPEG_PID_MAX) + return -ERANGE; + if (new > MPEG_PID_MAX) + new = MPEG_PID_MAX; + params->ts_pid_pcr = new; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + old = params->au_encoding; + if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && + (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3)) + return -ERANGE; + new = old; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + old = params->au_l2_bitrate; + if (set && new != V4L2_MPEG_AUDIO_L2_BITRATE_256K && + new != V4L2_MPEG_AUDIO_L2_BITRATE_384K) + return -ERANGE; + if (new <= V4L2_MPEG_AUDIO_L2_BITRATE_256K) + new = V4L2_MPEG_AUDIO_L2_BITRATE_256K; + else + new = V4L2_MPEG_AUDIO_L2_BITRATE_384K; + params->au_l2_bitrate = new; + break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!has_ac3) return -EINVAL; + old = params->au_ac3_bitrate; + if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K && + new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K) + return -ERANGE; + if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K) + new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K; + else + new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K; + params->au_ac3_bitrate = new; + break; + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000; + if (set && new != old) + return -ERANGE; + new = old; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + old = V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + if (set && new != old) + return -ERANGE; + new = old; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + old = params->vi_aspect; + if (set && new != V4L2_MPEG_VIDEO_ASPECT_16x9 && + new != V4L2_MPEG_VIDEO_ASPECT_4x3) + return -ERANGE; + if (new != V4L2_MPEG_VIDEO_ASPECT_16x9) + new = V4L2_MPEG_VIDEO_ASPECT_4x3; + params->vi_aspect = new; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + old = params->vi_bitrate * 1000; + new = 1000 * (new / 1000); + if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) + return -ERANGE; + if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) + new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000; + params->vi_bitrate = new / 1000; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + old = params->vi_bitrate_peak * 1000; + new = 1000 * (new / 1000); + if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) + return -ERANGE; + if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) + new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000; + params->vi_bitrate_peak = new / 1000; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + old = params->vi_bitrate_mode; + params->vi_bitrate_mode = new; + break; + default: + return -EINVAL; } - if (cmd == VIDIOC_G_EXT_CTRLS) - ctrl->value = old; - else - ctrl->value = new; + ctrl->value = new; return 0; } -static int saa6752hs_qctrl(struct saa6752hs_state *h, - struct v4l2_queryctrl *qctrl) + +static int saa6752hs_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl) { + struct saa6752hs_state *h = to_state(sd); struct saa6752hs_mpeg_params *params = &h->params; int err; @@ -583,7 +592,7 @@ static int saa6752hs_qctrl(struct saa6752hs_state *h, V4L2_MPEG_VIDEO_ASPECT_4x3); case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); if (err == 0 && params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) @@ -597,12 +606,20 @@ static int saa6752hs_qctrl(struct saa6752hs_state *h, V4L2_MPEG_STREAM_TYPE_MPEG2_TS); case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); case V4L2_CID_MPEG_STREAM_PID_PMT: + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 16); case V4L2_CID_MPEG_STREAM_PID_AUDIO: + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 260); case V4L2_CID_MPEG_STREAM_PID_VIDEO: + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 256); case V4L2_CID_MPEG_STREAM_PID_PCR: - return v4l2_ctrl_query_fill_std(qctrl); + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 259); default: break; @@ -610,8 +627,7 @@ static int saa6752hs_qctrl(struct saa6752hs_state *h, return -EINVAL; } -static int saa6752hs_qmenu(struct saa6752hs_state *h, - struct v4l2_querymenu *qmenu) +static int saa6752hs_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qmenu) { static const u32 mpeg_audio_encoding[] = { V4L2_MPEG_AUDIO_ENCODING_LAYER_2, @@ -632,11 +648,12 @@ static int saa6752hs_qmenu(struct saa6752hs_state *h, V4L2_MPEG_AUDIO_AC3_BITRATE_384K, V4L2_CTRL_MENU_IDS_END }; + struct saa6752hs_state *h = to_state(sd); struct v4l2_queryctrl qctrl; int err; qctrl.id = qmenu->id; - err = saa6752hs_qctrl(h, &qctrl); + err = saa6752hs_queryctrl(sd, &qctrl); if (err) return err; switch (qmenu->id) { @@ -656,17 +673,16 @@ static int saa6752hs_qmenu(struct saa6752hs_state *h, return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL); } -static int saa6752hs_init(struct i2c_client *client, u32 leading_null_bytes) +static int saa6752hs_init(struct v4l2_subdev *sd, u32 leading_null_bytes) { unsigned char buf[9], buf2[4]; - struct saa6752hs_state *h; + struct saa6752hs_state *h = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); unsigned size; u32 crc; unsigned char localPAT[256]; unsigned char localPMT[256]; - h = i2c_get_clientdata(client); - /* Set video format - must be done first as it resets other settings */ set_reg8(client, 0x41, h->video_format); @@ -762,7 +778,7 @@ static int saa6752hs_init(struct i2c_client *client, u32 leading_null_bytes) buf[3] = 0x82; buf[4] = 0xB0; buf[5] = buf2[0]; - switch(h->params.vi_aspect) { + switch (h->params.vi_aspect) { case V4L2_MPEG_VIDEO_ASPECT_16x9: buf[6] = buf2[1] | 0x40; break; @@ -770,7 +786,6 @@ static int saa6752hs_init(struct i2c_client *client, u32 leading_null_bytes) default: buf[6] = buf2[1] & 0xBF; break; - break; } buf[7] = buf2[2]; buf[8] = buf2[3]; @@ -779,81 +794,162 @@ static int saa6752hs_init(struct i2c_client *client, u32 leading_null_bytes) return 0; } -static int -saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg) +static int saa6752hs_do_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls, int set) { - struct saa6752hs_state *h = i2c_get_clientdata(client); - struct v4l2_ext_controls *ctrls = arg; + struct saa6752hs_state *h = to_state(sd); struct saa6752hs_mpeg_params params; - int err = 0; int i; - switch (cmd) { - case VIDIOC_INT_INIT: - /* apply settings and start encoder */ - saa6752hs_init(client, *(u32 *)arg); - break; - case VIDIOC_S_EXT_CTRLS: - if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - /* fall through */ - case VIDIOC_TRY_EXT_CTRLS: - case VIDIOC_G_EXT_CTRLS: - if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - params = h->params; - for (i = 0; i < ctrls->count; i++) { - err = handle_ctrl(h->has_ac3, ¶ms, ctrls->controls + i, cmd); - if (err) { - ctrls->error_idx = i; - return err; - } + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + + params = h->params; + for (i = 0; i < ctrls->count; i++) { + int err = handle_ctrl(h->has_ac3, ¶ms, ctrls->controls + i, set); + + if (err) { + ctrls->error_idx = i; + return err; } - h->params = params; - break; - case VIDIOC_QUERYCTRL: - return saa6752hs_qctrl(h, arg); - case VIDIOC_QUERYMENU: - return saa6752hs_qmenu(h, arg); - case VIDIOC_G_FMT: - { - struct v4l2_format *f = arg; - - if (h->video_format == SAA6752HS_VF_UNKNOWN) - h->video_format = SAA6752HS_VF_D1; - f->fmt.pix.width = - v4l2_format_table[h->video_format].fmt.pix.width; - f->fmt.pix.height = - v4l2_format_table[h->video_format].fmt.pix.height; - break ; } - case VIDIOC_S_FMT: - { - struct v4l2_format *f = arg; + if (set) + h->params = params; + return 0; +} - saa6752hs_set_subsampling(client, f); - break; +static int saa6752hs_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) +{ + return saa6752hs_do_ext_ctrls(sd, ctrls, 1); +} + +static int saa6752hs_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) +{ + return saa6752hs_do_ext_ctrls(sd, ctrls, 0); +} + +static int saa6752hs_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) +{ + struct saa6752hs_state *h = to_state(sd); + int i; + + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + int err = get_ctrl(h->has_ac3, &h->params, ctrls->controls + i); + + if (err) { + ctrls->error_idx = i; + return err; + } } - case VIDIOC_S_STD: - h->standard = *((v4l2_std_id *) arg); - break; + return 0; +} - case VIDIOC_DBG_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, h->chip, h->revision); +static int saa6752hs_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct saa6752hs_state *h = to_state(sd); - default: - /* nothing */ - break; + if (h->video_format == SAA6752HS_VF_UNKNOWN) + h->video_format = SAA6752HS_VF_D1; + f->fmt.pix.width = + v4l2_format_table[h->video_format].fmt.pix.width; + f->fmt.pix.height = + v4l2_format_table[h->video_format].fmt.pix.height; + return 0; +} + +static int saa6752hs_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct saa6752hs_state *h = to_state(sd); + int dist_352, dist_480, dist_720; + + /* + FIXME: translate and round width/height into EMPRESS + subsample type: + + type | PAL | NTSC + --------------------------- + SIF | 352x288 | 352x240 + 1/2 D1 | 352x576 | 352x480 + 2/3 D1 | 480x576 | 480x480 + D1 | 720x576 | 720x480 + */ + + dist_352 = abs(f->fmt.pix.width - 352); + dist_480 = abs(f->fmt.pix.width - 480); + dist_720 = abs(f->fmt.pix.width - 720); + if (dist_720 < dist_480) { + f->fmt.pix.width = 720; + f->fmt.pix.height = 576; + h->video_format = SAA6752HS_VF_D1; + } else if (dist_480 < dist_352) { + f->fmt.pix.width = 480; + f->fmt.pix.height = 576; + h->video_format = SAA6752HS_VF_2_3_D1; + } else { + f->fmt.pix.width = 352; + if (abs(f->fmt.pix.height - 576) < + abs(f->fmt.pix.height - 288)) { + f->fmt.pix.height = 576; + h->video_format = SAA6752HS_VF_1_2_D1; + } else { + f->fmt.pix.height = 288; + h->video_format = SAA6752HS_VF_SIF; + } } + return 0; +} + +static int saa6752hs_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa6752hs_state *h = to_state(sd); + + h->standard = std; + return 0; +} - return err; +static int saa6752hs_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa6752hs_state *h = to_state(sd); + + return v4l2_chip_ident_i2c_client(client, + chip, h->chip, h->revision); } +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa6752hs_core_ops = { + .g_chip_ident = saa6752hs_g_chip_ident, + .init = saa6752hs_init, + .queryctrl = saa6752hs_queryctrl, + .querymenu = saa6752hs_querymenu, + .g_ext_ctrls = saa6752hs_g_ext_ctrls, + .s_ext_ctrls = saa6752hs_s_ext_ctrls, + .try_ext_ctrls = saa6752hs_try_ext_ctrls, +}; + +static const struct v4l2_subdev_tuner_ops saa6752hs_tuner_ops = { + .s_std = saa6752hs_s_std, +}; + +static const struct v4l2_subdev_video_ops saa6752hs_video_ops = { + .s_fmt = saa6752hs_s_fmt, + .g_fmt = saa6752hs_g_fmt, +}; + +static const struct v4l2_subdev_ops saa6752hs_ops = { + .core = &saa6752hs_core_ops, + .tuner = &saa6752hs_tuner_ops, + .video = &saa6752hs_video_ops, +}; + static int saa6752hs_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL); + struct v4l2_subdev *sd; u8 addr = 0x13; u8 data[12]; @@ -861,6 +957,8 @@ static int saa6752hs_probe(struct i2c_client *client, client->addr << 1, client->adapter->name); if (h == NULL) return -ENOMEM; + sd = &h->sd; + v4l2_i2c_subdev_init(sd, client, &saa6752hs_ops); i2c_master_send(client, &addr, 1); i2c_master_recv(client, data, sizeof(data)); @@ -874,14 +972,15 @@ static int saa6752hs_probe(struct i2c_client *client, } h->params = param_defaults; h->standard = 0; /* Assume 625 input lines */ - - i2c_set_clientdata(client, h); return 0; } static int saa6752hs_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } @@ -893,8 +992,6 @@ MODULE_DEVICE_TABLE(i2c, saa6752hs_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa6752hs", - .driverid = I2C_DRIVERID_SAA6752HS, - .command = saa6752hs_command, .probe = saa6752hs_probe, .remove = saa6752hs_remove, .id_table = saa6752hs_id, diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index c750d3d..8b0b64a 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -990,10 +990,10 @@ static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) if (!enable[devnum]) return -ENODEV; - card = snd_card_new(index[devnum], id[devnum], THIS_MODULE, sizeof(snd_card_saa7134_t)); - - if (card == NULL) - return -ENOMEM; + err = snd_card_create(index[devnum], id[devnum], THIS_MODULE, + sizeof(snd_card_saa7134_t), &card); + if (err < 0) + return err; strcpy(card->driver, "SAA7134"); diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index e2febcd..a790a72 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -31,6 +31,7 @@ #include <media/v4l2-common.h> #include <media/tveeprom.h> #include "tea5767.h" +#include "tda18271.h" /* commly used strings */ static char name_mute[] = "mute"; @@ -272,6 +273,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .inputs = {{ .name = name_comp1, @@ -408,6 +410,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .tda9887_conf = TDA9887_PRESENT, .gpiomask = 0x820000, .inputs = {{ @@ -818,6 +821,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .inputs = {{ .name = name_comp1, .vmux = 4, @@ -977,6 +981,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .inputs = {{ .name = name_comp1, .vmux = 1, @@ -1699,6 +1704,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, @@ -2364,6 +2370,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x21, .inputs = {{ .name = "Composite 0", .vmux = 0, @@ -3291,6 +3298,68 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200100, }, }, + [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { + .name = "Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 3, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_SERIAL, + .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0800100, /* GPIO 23 HI for FM */ + }, + }, + [SAA7134_BOARD_HAUPPAUGE_HVR1110R3] = { + .name = "Hauppauge WinTV-HVR1110r3", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 3, + .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0800100, /* GPIO 23 HI for FM */ + }, + }, [SAA7134_BOARD_CINERGY_HT_PCMCIA] = { .name = "Terratec Cinergy HT PCMCIA", .audio_clock = 0x00187de7, @@ -4070,6 +4139,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .tda9887_conf = TDA9887_PRESENT, .inputs = { { .name = name_tv, @@ -4106,6 +4176,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .tda9887_conf = TDA9887_PRESENT, .inputs = { { .name = name_tv, @@ -4143,6 +4214,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .tda9887_conf = TDA9887_PRESENT, .inputs = { { .name = name_tv, @@ -4323,13 +4395,13 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, .tv = 1, }, { - .name = name_comp, - .vmux = 0, + .name = name_comp1, + .vmux = 3, .amux = LINE1, }, { .name = name_svideo, .vmux = 8, - .amux = LINE1, + .amux = LINE2, } }, .radio = { .name = name_radio, @@ -4421,8 +4493,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - /* no DVB support for now */ - /* .mpeg = SAA7134_MPEG_DVB, */ + .mpeg = SAA7134_MPEG_DVB, .inputs = { { .name = name_comp, .vmux = 1, @@ -4441,8 +4512,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - /* no DVB support for now */ - /* .mpeg = SAA7134_MPEG_DVB, */ + .mpeg = SAA7134_MPEG_DVB, .inputs = { { .name = name_comp, .vmux = 1, @@ -4611,7 +4681,7 @@ struct saa7134_board saa7134_boards[] = { .tuner_type = TUNER_YMEC_TVF_5533MF, .radio_type = TUNER_TEA5767, .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, + .radio_addr = 0x60, .gpiomask = 0x80000700, .inputs = { { .name = name_tv, @@ -5405,6 +5475,36 @@ struct pci_device_id saa7134_pci_tbl[] = { },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6706, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6707, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6708, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6709, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x670a, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x153b, .subdevice = 0x1172, .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA, @@ -5821,8 +5921,8 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev, } -static int saa7134_tda8290_callback(struct saa7134_dev *dev, - int command, int arg) +static int saa7134_tda8290_827x_callback(struct saa7134_dev *dev, + int command, int arg) { u8 sync_control; @@ -5848,6 +5948,65 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, return 0; } +static inline int saa7134_tda18271_hvr11x0_toggle_agc(struct saa7134_dev *dev, + enum tda18271_mode mode) +{ + /* toggle AGC switch through GPIO 26 */ + switch (mode) { + case TDA18271_ANALOG: + saa7134_set_gpio(dev, 26, 0); + break; + case TDA18271_DIGITAL: + saa7134_set_gpio(dev, 26, 1); + break; + default: + return -EINVAL; + } + return 0; +} + +static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, + int command, int arg) +{ + int ret = 0; + + switch (command) { + case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */ + switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg); + break; + default: + break; + } + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int saa7134_tda8290_callback(struct saa7134_dev *dev, + int command, int arg) +{ + int ret; + + switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + /* tda8290 + tda18271 */ + ret = saa7134_tda8290_18271_callback(dev, command, arg); + break; + default: + /* tda8290 + tda827x */ + ret = saa7134_tda8290_827x_callback(dev, command, arg); + break; + } + return ret; +} + int saa7134_tuner_callback(void *priv, int component, int command, int arg) { struct saa7134_dev *dev = priv; @@ -5878,11 +6037,16 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) switch (tv.model) { case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ + case 67201: /* WinTV-HVR1120 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ + case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ + case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ case 67569: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM) */ case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */ case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ + case 67651: /* WinTV-HVR1120 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ + case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ break; default: printk(KERN_WARNING "%s: warning: " @@ -6019,6 +6183,11 @@ int saa7134_board_init1(struct saa7134_dev *dev) msleep(10); break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + saa7134_set_gpio(dev, 23, 0); + msleep(10); + saa7134_set_gpio(dev, 23, 1); + dev->has_remote = SAA7134_REMOTE_I2C; + break; case SAA7134_BOARD_AVERMEDIA_M103: saa7134_set_gpio(dev, 23, 0); msleep(10); @@ -6054,6 +6223,16 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00); break; + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + /* GPIO 26 high for digital, low for analog */ + saa7134_set_gpio(dev, 26, 0); + msleep(1); + + saa7134_set_gpio(dev, 22, 0); + msleep(10); + saa7134_set_gpio(dev, 22, 1); + break; /* i2c remotes */ case SAA7134_BOARD_PINNACLE_PCTV_110i: case SAA7134_BOARD_PINNACLE_PCTV_310i: @@ -6079,15 +6258,15 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x8c040007, 0x8c040007); saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd); break; - case SAA7134_BOARD_AVERMEDIA_A700_PRO: case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: - /* write windows gpio values */ - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100); printk("%s: %s: hybrid analog/dvb card\n" - "%s: Sorry, only analog s-video and composite input " + "%s: Sorry, of the analog inputs, only analog s-video and composite " "are supported for now.\n", dev->name, card(dev).name, dev->name); + case SAA7134_BOARD_AVERMEDIA_A700_PRO: + /* write windows gpio values */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100); break; } return 0; @@ -6109,7 +6288,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) tun_setup.mode_mask = T_RADIO; - saa7134_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); + saa_call_all(dev, tuner, s_type_addr, &tun_setup); mode_mask &= ~T_RADIO; } @@ -6121,7 +6300,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) tun_setup.mode_mask = mode_mask; - saa7134_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); + saa_call_all(dev, tuner, s_type_addr, &tun_setup); } if (dev->tda9887_conf) { @@ -6130,8 +6309,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) tda9887_cfg.tuner = TUNER_TDA9887; tda9887_cfg.priv = &dev->tda9887_conf; - saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, - &tda9887_cfg); + saa_call_all(dev, tuner, s_config, &tda9887_cfg); } if (dev->tuner_type == TUNER_XC2028) { @@ -6158,7 +6336,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) xc2028_cfg.tuner = TUNER_XC2028; xc2028_cfg.priv = &ctl; - saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg); + saa_call_all(dev, tuner, s_config, &xc2028_cfg); } } @@ -6168,9 +6346,20 @@ int saa7134_board_init2(struct saa7134_dev *dev) unsigned char buf; int board; + /* Put here the code that enables the chips that are needed + for analog mode and doesn't depend on the tuner attachment. + It is also a good idea to get tuner type from eeprom, etc before + initializing tuner, since we can avoid loading tuner driver + on devices that has TUNER_ABSENT + */ switch (dev->board) { case SAA7134_BOARD_BMK_MPEX_NOTUNER: case SAA7134_BOARD_BMK_MPEX_TUNER: + /* Checks if the device has a tuner at 0x60 addr + If the device doesn't have a tuner, TUNER_ABSENT + will be used at tuner_type, avoiding loading tuner + without needing it + */ dev->i2c_client.addr = 0x60; board = (i2c_master_recv(&dev->i2c_client, &buf, 0) < 0) ? SAA7134_BOARD_BMK_MPEX_NOTUNER @@ -6188,11 +6377,15 @@ int saa7134_board_init2(struct saa7134_dev *dev) u8 subaddr; u8 data[3]; int ret, tuner_t; - struct i2c_msg msg[] = {{.addr=0x50, .flags=0, .buf=&subaddr, .len = 1}, {.addr=0x50, .flags=I2C_M_RD, .buf=data, .len = 3}}; + subaddr= 0x14; tuner_t = 0; + + /* Retrieve device data from eeprom, checking for the + proper tuner_type. + */ ret = i2c_transfer(&dev->i2c_adap, msg, 2); if (ret != 2) { printk(KERN_ERR "EEPROM read failure\n"); @@ -6248,12 +6441,14 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->name, saa7134_boards[dev->board].name); break; } + /* break intentionally omitted */ case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: { - /* The Philips EUROPA based hybrid boards have the tuner connected through - * the channel decoder. We have to make it transparent to find it + /* The Philips EUROPA based hybrid boards have the tuner + connected through the channel decoder. We have to make it + transparent to find it */ u8 data[] = { 0x07, 0x02}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; @@ -6274,21 +6469,15 @@ int saa7134_board_init2(struct saa7134_dev *dev) if (dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) { dev->tuner_type = TUNER_PHILIPS_TDA8290; - saa7134_tuner_setup(dev); - data[2] = 0x68; i2c_transfer(&dev->i2c_adap, &msg, 1); - - /* Tuner setup is handled before I2C transfer. - Due to that, there's no need to do it later - */ - return 0; + break; } i2c_transfer(&dev->i2c_adap, &msg, 1); break; } - case SAA7134_BOARD_ASUSTeK_TVFM7135: - /* The card below is detected as card=53, but is different */ + case SAA7134_BOARD_ASUSTeK_TVFM7135: + /* The card below is detected as card=53, but is different */ if (dev->autodetected && (dev->eedata[0x27] == 0x03)) { dev->board = SAA7134_BOARD_ASUSTeK_P7131_ANALOG; printk(KERN_INFO "%s: P7131 analog only, using " @@ -6296,6 +6485,10 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->name, saa7134_boards[dev->board].name); } break; + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + hauppauge_eeprom(dev, dev->eedata+0x80); + break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: hauppauge_eeprom(dev, dev->eedata+0x80); /* break intentionally omitted */ @@ -6351,22 +6544,6 @@ int saa7134_board_init2(struct saa7134_dev *dev) i2c_transfer(&dev->i2c_adap, &msg, 1); break; } - case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI: - case SAA7134_BOARD_KWORLD_ATSC110: - { - /* enable tuner */ - int i; - static const u8 buffer [] = { 0x10, 0x12, 0x13, 0x04, 0x16, - 0x00, 0x14, 0x04, 0x17, 0x00 }; - dev->i2c_client.addr = 0x0a; - for (i = 0; i < 5; i++) - if (2 != i2c_master_send(&dev->i2c_client, - &buffer[i*2], 2)) - printk(KERN_WARNING - "%s: Unable to enable tuner(%i).\n", - dev->name, i); - break; - } case SAA7134_BOARD_VIDEOMATE_DVBT_200: case SAA7134_BOARD_VIDEOMATE_DVBT_200A: /* The T200 and the T200A share the same pci id. Consequently, @@ -6375,9 +6552,9 @@ int saa7134_board_init2(struct saa7134_dev *dev) /* Don't do this if the board was specifically selected with an * insmod option or if we have the default configuration T200*/ - if(!dev->autodetected || (dev->eedata[0x41] == 0xd0)) + if (!dev->autodetected || (dev->eedata[0x41] == 0xd0)) break; - if(dev->eedata[0x41] == 0x02) { + if (dev->eedata[0x41] == 0x02) { /* Reconfigure board as T200A */ dev->board = SAA7134_BOARD_VIDEOMATE_DVBT_200A; dev->tuner_type = saa7134_boards[dev->board].tuner_type; @@ -6390,6 +6567,58 @@ int saa7134_board_init2(struct saa7134_dev *dev) break; } break; + case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI: + case SAA7134_BOARD_KWORLD_ATSC110: + { + struct i2c_msg msg = { .addr = 0x0a, .flags = 0 }; + int i; + static u8 buffer[][2] = { + { 0x10, 0x12 }, + { 0x13, 0x04 }, + { 0x16, 0x00 }, + { 0x14, 0x04 }, + { 0x17, 0x00 }, + }; + + for (i = 0; i < ARRAY_SIZE(buffer); i++) { + msg.buf = &buffer[i][0]; + msg.len = ARRAY_SIZE(buffer[0]); + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) + printk(KERN_WARNING + "%s: Unable to enable tuner(%i).\n", + dev->name, i); + } + break; + } + } /* switch() */ + + /* initialize tuner */ + if (TUNER_ABSENT != dev->tuner_type) { + int has_demod = (dev->tda9887_conf & TDA9887_PRESENT); + + /* Note: radio tuner address is always filled in, + so we do not need to probe for a radio tuner device. */ + if (dev->radio_type != UNSET) + v4l2_i2c_new_subdev(&dev->i2c_adap, + "tuner", "tuner", dev->radio_addr); + if (has_demod) + v4l2_i2c_new_probed_subdev(&dev->i2c_adap, "tuner", + "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + if (dev->tuner_addr == ADDR_UNSET) { + enum v4l2_i2c_tuner_type type = + has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; + + v4l2_i2c_new_probed_subdev(&dev->i2c_adap, "tuner", + "tuner", v4l2_i2c_tuner_addrs(type)); + } else { + v4l2_i2c_new_subdev(&dev->i2c_adap, + "tuner", "tuner", dev->tuner_addr); + } + } + + saa7134_tuner_setup(dev); + + switch (dev->board) { case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: { struct v4l2_priv_tun_config tea5767_cfg; @@ -6401,12 +6630,10 @@ int saa7134_board_init2(struct saa7134_dev *dev) ctl.xtal_freq = TEA5767_HIGH_LO_13MHz; tea5767_cfg.tuner = TUNER_TEA5767; tea5767_cfg.priv = &ctl; - saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tea5767_cfg); + saa_call_all(dev, tuner, s_config, &tea5767_cfg); break; } } /* switch() */ - saa7134_tuner_setup(dev); - return 0; } diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 99221d7..dafa0d8 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -54,13 +54,9 @@ static unsigned int gpio_tracking; module_param(gpio_tracking, int, 0644); MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]"); -static unsigned int alsa; +static unsigned int alsa = 1; module_param(alsa, int, 0644); -MODULE_PARM_DESC(alsa,"enable ALSA DMA sound [dmasound]"); - -static unsigned int oss; -module_param(oss, int, 0644); -MODULE_PARM_DESC(oss,"enable OSS DMA sound [dmasound]"); +MODULE_PARM_DESC(alsa,"enable/disable ALSA DMA sound [dmasound]"); static unsigned int latency = UNSET; module_param(latency, int, 0444); @@ -90,8 +86,10 @@ MODULE_PARM_DESC(radio_nr, "radio device number"); MODULE_PARM_DESC(tuner, "tuner type"); MODULE_PARM_DESC(card, "card type"); -static DEFINE_MUTEX(devlist_lock); +DEFINE_MUTEX(saa7134_devlist_lock); +EXPORT_SYMBOL(saa7134_devlist_lock); LIST_HEAD(saa7134_devlist); +EXPORT_SYMBOL(saa7134_devlist); static LIST_HEAD(mops_list); static unsigned int saa7134_devcount; @@ -156,10 +154,10 @@ static void request_module_async(struct work_struct *work){ request_module("saa7134-empress"); if (card_is_dvb(dev)) request_module("saa7134-dvb"); - if (alsa) - request_module("saa7134-alsa"); - if (oss) - request_module("saa7134-oss"); + if (alsa) { + if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130) + request_module("saa7134-alsa"); + } } static void request_submodules(struct saa7134_dev *dev) @@ -778,7 +776,7 @@ static struct video_device *vdev_init(struct saa7134_dev *dev, return NULL; *vfd = *template; vfd->minor = -1; - vfd->parent = &dev->pci->dev; + vfd->v4l2_dev = &dev->v4l2_dev; vfd->release = video_device_release; vfd->debug = video_debug; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", @@ -851,6 +849,10 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, if (NULL == dev) return -ENOMEM; + err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); + if (err) + goto fail0; + /* pci init */ dev->pci = pci_dev; if (pci_enable_device(pci_dev)) { @@ -927,6 +929,8 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, dev->autodetected = card[dev->nr] != dev->board; dev->tuner_type = saa7134_boards[dev->board].tuner_type; dev->tuner_addr = saa7134_boards[dev->board].tuner_addr; + dev->radio_type = saa7134_boards[dev->board].radio_type; + dev->radio_addr = saa7134_boards[dev->board].radio_addr; dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; if (UNSET != tuner[dev->nr]) dev->tuner_type = tuner[dev->nr]; @@ -971,23 +975,48 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, /* wait a bit, register i2c bus */ msleep(100); saa7134_i2c_register(dev); - - /* initialize hardware #2 */ - if (TUNER_ABSENT != dev->tuner_type) - request_module("tuner"); saa7134_board_init2(dev); saa7134_hwinit2(dev); /* load i2c helpers */ if (card_is_empress(dev)) { - request_module("saa6752hs"); + struct v4l2_subdev *sd = + v4l2_i2c_new_subdev(&dev->i2c_adap, + "saa6752hs", "saa6752hs", + saa7134_boards[dev->board].empress_addr); + + if (sd) + sd->grp_id = GRP_EMPRESS; + } + + if (saa7134_boards[dev->board].rds_addr) { + unsigned short addrs[2] = { 0, I2C_CLIENT_END }; + struct v4l2_subdev *sd; + + addrs[0] = saa7134_boards[dev->board].rds_addr; + sd = v4l2_i2c_new_probed_subdev(&dev->i2c_adap, "saa6588", + "saa6588", addrs); + if (sd) + printk(KERN_INFO "%s: found RDS decoder\n", dev->name); } request_submodules(dev); v4l2_prio_init(&dev->prio); + mutex_lock(&saa7134_devlist_lock); + list_for_each_entry(mops, &mops_list, next) + mpeg_ops_attach(mops, dev); + list_add_tail(&dev->devlist, &saa7134_devlist); + mutex_unlock(&saa7134_devlist_lock); + + /* check for signal */ + saa7134_irq_video_signalchange(dev); + + if (TUNER_ABSENT != dev->tuner_type) + saa_call_all(dev, core, s_standby, 0); + /* register v4l devices */ if (saa7134_no_overlay > 0) printk(KERN_INFO "%s: Overlay support disabled.\n", dev->name); @@ -1023,24 +1052,10 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, } /* everything worked */ - pci_set_drvdata(pci_dev,dev); saa7134_devcount++; - mutex_lock(&devlist_lock); - list_for_each_entry(mops, &mops_list, next) - mpeg_ops_attach(mops, dev); - list_add_tail(&dev->devlist,&saa7134_devlist); - mutex_unlock(&devlist_lock); - - /* check for signal */ - saa7134_irq_video_signalchange(dev); - - if (saa7134_dmasound_init && !dev->dmasound.priv_data) { + if (saa7134_dmasound_init && !dev->dmasound.priv_data) saa7134_dmasound_init(dev); - } - - if (TUNER_ABSENT != dev->tuner_type) - saa7134_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); return 0; @@ -1055,13 +1070,16 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, release_mem_region(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0)); fail1: + v4l2_device_unregister(&dev->v4l2_dev); + fail0: kfree(dev); return err; } static void __devexit saa7134_finidev(struct pci_dev *pci_dev) { - struct saa7134_dev *dev = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); struct saa7134_mpeg_ops *mops; /* Release DMA sound modules if present */ @@ -1088,11 +1106,11 @@ static void __devexit saa7134_finidev(struct pci_dev *pci_dev) saa7134_hwfini(dev); /* unregister */ - mutex_lock(&devlist_lock); + mutex_lock(&saa7134_devlist_lock); list_del(&dev->devlist); list_for_each_entry(mops, &mops_list, next) mpeg_ops_detach(mops, dev); - mutex_unlock(&devlist_lock); + mutex_unlock(&saa7134_devlist_lock); saa7134_devcount--; saa7134_i2c_unregister(dev); @@ -1113,7 +1131,8 @@ static void __devexit saa7134_finidev(struct pci_dev *pci_dev) release_mem_region(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0)); - pci_set_drvdata(pci_dev, NULL); + + v4l2_device_unregister(&dev->v4l2_dev); /* free memory */ kfree(dev); @@ -1148,8 +1167,8 @@ static int saa7134_buffer_requeue(struct saa7134_dev *dev, static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state) { - - struct saa7134_dev *dev = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); /* disable overlay - apps should enable it explicitly on resume*/ dev->ovenable = 0; @@ -1185,7 +1204,8 @@ static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state) static int saa7134_resume(struct pci_dev *pci_dev) { - struct saa7134_dev *dev = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); unsigned long flags; pci_set_power_state(pci_dev, PCI_D0); @@ -1247,11 +1267,11 @@ int saa7134_ts_register(struct saa7134_mpeg_ops *ops) { struct saa7134_dev *dev; - mutex_lock(&devlist_lock); + mutex_lock(&saa7134_devlist_lock); list_for_each_entry(dev, &saa7134_devlist, devlist) mpeg_ops_attach(ops, dev); list_add_tail(&ops->next,&mops_list); - mutex_unlock(&devlist_lock); + mutex_unlock(&saa7134_devlist_lock); return 0; } @@ -1259,11 +1279,11 @@ void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops) { struct saa7134_dev *dev; - mutex_lock(&devlist_lock); + mutex_lock(&saa7134_devlist_lock); list_del(&ops->next); list_for_each_entry(dev, &saa7134_devlist, devlist) mpeg_ops_detach(ops, dev); - mutex_unlock(&devlist_lock); + mutex_unlock(&saa7134_devlist_lock); } EXPORT_SYMBOL(saa7134_ts_register); @@ -1307,8 +1327,6 @@ module_exit(saa7134_fini); /* ----------------------------------------------------------- */ EXPORT_SYMBOL(saa7134_set_gpio); -EXPORT_SYMBOL(saa7134_i2c_call_clients); -EXPORT_SYMBOL(saa7134_devlist); EXPORT_SYMBOL(saa7134_boards); /* ----------------- for the DMA sound modules --------------- */ diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 0776ecf..4eff1ca 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -48,9 +48,15 @@ #include "isl6405.h" #include "lnbp21.h" #include "tuner-simple.h" +#include "tda18271.h" +#include "lgdt3305.h" +#include "tda8290.h" #include "zl10353.h" +#include "zl10036.h" +#include "mt312.h" + MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); @@ -189,7 +195,7 @@ static int mt352_pinnacle_tuner_set_params(struct dvb_frontend* fe, if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &msg, 1); - saa7134_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,&f); + saa_call_all(dev, tuner, s_frequency, &f); msg.buf = on; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -860,6 +866,7 @@ static struct zl10353_config behold_h6_config = { .demod_address = 0x1e>>1, .no_tuner = 1, .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, }; /* ================================================================== @@ -949,6 +956,45 @@ static struct nxt200x_config kworldatsc110 = { .demod_address = 0x0a, }; +/* ------------------------------------------------------------------ */ + +static struct mt312_config avertv_a700_mt312 = { + .demod_address = 0x0e, + .voltage_inverted = 1, +}; + +static struct zl10036_config avertv_a700_tuner = { + .tuner_address = 0x60, +}; + +static struct lgdt3305_config hcw_lgdt3305_config = { + .i2c_addr = 0x0e, + .mpeg_mode = LGDT3305_MPEG_SERIAL, + .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, + .deny_i2c_rptr = 1, + .spectral_inversion = 1, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, +}; + +static struct tda18271_std_map hauppauge_tda18271_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x58, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x58, }, +}; + +static struct tda18271_config hcw_tda18271_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, + .config = 3, +}; + +static struct tda829x_config tda829x_no_probe = { + .probe_tuner = TDA829X_DONT_PROBE, +}; + /* ================================================================== * Core code */ @@ -1075,6 +1121,19 @@ static int dvb_init(struct saa7134_dev *dev) &tda827x_cfg_1) < 0) goto dettach_frontend; break; + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + fe0->dvb.frontend = dvb_attach(lgdt3305_attach, + &hcw_lgdt3305_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &hcw_tda18271_config); + } + break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: if (configure_tda827x_fe(dev, &asus_p7131_dual_config, &tda827x_cfg_0) < 0) @@ -1375,6 +1434,19 @@ static int dvb_init(struct saa7134_dev *dev) TUNER_PHILIPS_FMD1216ME_MK3); } break; + case SAA7134_BOARD_AVERMEDIA_A700_PRO: + case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: + /* Zarlink ZL10313 */ + fe0->dvb.frontend = dvb_attach(mt312_attach, + &avertv_a700_mt312, &dev->i2c_adap); + if (fe0->dvb.frontend) { + if (dvb_attach(zl10036_attach, fe0->dvb.frontend, + &avertv_a700_tuner, &dev->i2c_adap) == NULL) { + wprintk("%s: No zl10036 found!\n", + __func__); + } + } + break; default: wprintk("Huh? unknown DVB card?\n"); break; @@ -1448,7 +1520,7 @@ static int dvb_fini(struct saa7134_dev *dev) tda9887_cfg.priv = &on; /* otherwise we don't detect the tuner on next insmod */ - saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tda9887_cfg); + saa_call_all(dev, tuner, s_config, &tda9887_cfg); } else if (dev->board == SAA7134_BOARD_MEDION_MD8800_QUADRO) { if ((dev->eedata[2] == 0x07) && use_frontend) { /* turn off the 2nd lnb supply */ diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index c9d8beb..9db3472 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -76,7 +76,7 @@ static int ts_init_encoder(struct saa7134_dev* dev) break; } ts_reset_encoder(dev); - saa7134_i2c_call_clients(dev, VIDIOC_INT_INIT, &leading_null_bytes); + saa_call_all(dev, core, init, leading_null_bytes); dev->empress_started = 1; return 0; } @@ -234,7 +234,7 @@ static int empress_g_fmt_vid_cap(struct file *file, void *priv, { struct saa7134_dev *dev = file->private_data; - saa7134_i2c_call_clients(dev, VIDIOC_G_FMT, f); + saa_call_all(dev, video, g_fmt, f); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; @@ -247,7 +247,7 @@ static int empress_s_fmt_vid_cap(struct file *file, void *priv, { struct saa7134_dev *dev = file->private_data; - saa7134_i2c_call_clients(dev, VIDIOC_S_FMT, f); + saa_call_all(dev, video, s_fmt, f); f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; @@ -317,7 +317,7 @@ static int empress_s_ext_ctrls(struct file *file, void *priv, if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) return -EINVAL; - err = saa7134_i2c_call_saa6752(dev, VIDIOC_S_EXT_CTRLS, ctrls); + err = saa_call_empress(dev, core, s_ext_ctrls, ctrls); ts_init_encoder(dev); return err; @@ -330,7 +330,7 @@ static int empress_g_ext_ctrls(struct file *file, void *priv, if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) return -EINVAL; - return saa7134_i2c_call_saa6752(dev, VIDIOC_G_EXT_CTRLS, ctrls); + return saa_call_empress(dev, core, g_ext_ctrls, ctrls); } static int empress_g_ctrl(struct file *file, void *priv, @@ -352,6 +352,7 @@ static int empress_s_ctrl(struct file *file, void *priv, static int empress_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c) { + /* Must be sorted from low to high control ID! */ static const u32 user_ctrls[] = { V4L2_CID_USER_CLASS, V4L2_CID_BRIGHTNESS, @@ -364,6 +365,7 @@ static int empress_queryctrl(struct file *file, void *priv, 0 }; + /* Must be sorted from low to high control ID! */ static const u32 mpeg_ctrls[] = { V4L2_CID_MPEG_CLASS, V4L2_CID_MPEG_STREAM_TYPE, @@ -388,10 +390,10 @@ static int empress_queryctrl(struct file *file, void *priv, if (c->id == 0) return -EINVAL; if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS) - return v4l2_ctrl_query_fill_std(c); + return v4l2_ctrl_query_fill(c, 0, 0, 0, 0); if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG) return saa7134_queryctrl(file, priv, c); - return saa7134_i2c_call_saa6752(dev, VIDIOC_QUERYCTRL, c); + return saa_call_empress(dev, core, queryctrl, c); } static int empress_querymenu(struct file *file, void *priv, @@ -401,7 +403,7 @@ static int empress_querymenu(struct file *file, void *priv, if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG) return -EINVAL; - return saa7134_i2c_call_saa6752(dev, VIDIOC_QUERYMENU, c); + return saa_call_empress(dev, core, querymenu, c); } static int empress_g_chip_ident(struct file *file, void *fh, @@ -411,14 +413,11 @@ static int empress_g_chip_ident(struct file *file, void *fh, chip->ident = V4L2_IDENT_NONE; chip->revision = 0; - if (dev->mpeg_i2c_client == NULL) - return -EINVAL; if (chip->match.type == V4L2_CHIP_MATCH_I2C_DRIVER && !strcmp(chip->match.name, "saa6752hs")) - return saa7134_i2c_call_saa6752(dev, VIDIOC_DBG_G_CHIP_IDENT, chip); - if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR && - chip->match.addr == dev->mpeg_i2c_client->addr) - return saa7134_i2c_call_saa6752(dev, VIDIOC_DBG_G_CHIP_IDENT, chip); + return saa_call_empress(dev, core, g_chip_ident, chip); + if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR) + return saa_call_empress(dev, core, g_chip_ident, chip); return -EINVAL; } diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 20c1b33..f3e285a 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -255,7 +255,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, addr = msgs[i].addr << 1; if (msgs[i].flags & I2C_M_RD) addr |= 1; - if (i > 0 && msgs[i].flags & I2C_M_RD) { + if (i > 0 && msgs[i].flags & I2C_M_RD && msgs[i].addr != 0x40) { /* workaround for a saa7134 i2c bug * needed to talk to the mt352 demux * thanks to pinnacle for the hint */ @@ -327,8 +327,6 @@ static int attach_inform(struct i2c_client *client) d1printk( "%s i2c attach [addr=0x%x,client=%s]\n", client->driver->driver.name, client->addr, client->name); - if (client->addr == 0x20 && client->driver && client->driver->command) - dev->mpeg_i2c_client = client; /* Am I an i2c remote control? */ @@ -357,7 +355,6 @@ static struct i2c_algorithm saa7134_algo = { static struct i2c_adapter saa7134_adap_template = { .owner = THIS_MODULE, - .class = I2C_CLASS_TV_ANALOG, .name = "saa7134", .id = I2C_HW_SAA7134, .algo = &saa7134_algo, @@ -421,29 +418,13 @@ static void do_i2c_scan(char *name, struct i2c_client *c) } } -void saa7134_i2c_call_clients(struct saa7134_dev *dev, - unsigned int cmd, void *arg) -{ - BUG_ON(NULL == dev->i2c_adap.algo_data); - i2c_clients_command(&dev->i2c_adap, cmd, arg); -} - -int saa7134_i2c_call_saa6752(struct saa7134_dev *dev, - unsigned int cmd, void *arg) -{ - if (dev->mpeg_i2c_client == NULL) - return -EINVAL; - return dev->mpeg_i2c_client->driver->command(dev->mpeg_i2c_client, - cmd, arg); -} -EXPORT_SYMBOL_GPL(saa7134_i2c_call_saa6752); - int saa7134_i2c_register(struct saa7134_dev *dev) { dev->i2c_adap = saa7134_adap_template; dev->i2c_adap.dev.parent = &dev->pci->dev; strcpy(dev->i2c_adap.name,dev->name); dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); i2c_add_adapter(&dev->i2c_adap); dev->i2c_client = saa7134_client_template; diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/video/saa7134/saa7134-ts.c index ef55a59..cc8b923 100644 --- a/drivers/media/video/saa7134/saa7134-ts.c +++ b/drivers/media/video/saa7134/saa7134-ts.c @@ -79,8 +79,19 @@ static int buffer_activate(struct saa7134_dev *dev, saa_writeb(SAA7134_TS_SERIAL1, 0x00); /* Start TS stream */ - saa_writeb(SAA7134_TS_SERIAL0, 0x40); - saa_writeb(SAA7134_TS_PARALLEL, 0xEC); + switch (saa7134_boards[dev->board].ts_type) { + case SAA7134_MPEG_TS_PARALLEL: + saa_writeb(SAA7134_TS_SERIAL0, 0x40); + saa_writeb(SAA7134_TS_PARALLEL, 0xec); + break; + case SAA7134_MPEG_TS_SERIAL: + saa_writeb(SAA7134_TS_SERIAL0, 0xd8); + saa_writeb(SAA7134_TS_PARALLEL, 0x6c); + saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 0xbc); + saa_writeb(SAA7134_TS_SERIAL1, 0x02); + break; + } + dev->ts_state = SAA7134_TS_STARTED; } diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index a1f7e35..404f70e 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -30,11 +30,7 @@ #include "saa7134-reg.h" #include "saa7134.h" #include <media/v4l2-common.h> - -#ifdef CONFIG_VIDEO_V4L1_COMPAT -/* Include V4L1 specific functions. Should be removed soon */ -#include <linux/videodev.h> -#endif +#include <media/rds.h> /* ------------------------------------------------------------------ */ @@ -452,6 +448,7 @@ static const struct v4l2_queryctrl video_ctrls[] = { .name = "y offset odd field", .minimum = 0, .maximum = 128, + .step = 1, .default_value = 0, .type = V4L2_CTRL_TYPE_INTEGER, },{ @@ -459,6 +456,7 @@ static const struct v4l2_queryctrl video_ctrls[] = { .name = "y offset even field", .minimum = 0, .maximum = 128, + .step = 1, .default_value = 0, .type = V4L2_CTRL_TYPE_INTEGER, },{ @@ -627,10 +625,10 @@ void saa7134_set_tvnorm_hw(struct saa7134_dev *dev) saa7134_set_decoder(dev); if (card_in(dev, dev->ctl_input).tv) - saa7134_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id); + saa_call_all(dev, tuner, s_std, dev->tvnorm->id); /* Set the correct norm for the saa6752hs. This function does nothing if there is no saa6752hs. */ - saa7134_i2c_call_saa6752(dev, VIDIOC_S_STD, &dev->tvnorm->id); + saa_call_empress(dev, tuner, s_std, dev->tvnorm->id); } static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale) @@ -1266,8 +1264,7 @@ int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, str else dev->tda9887_conf &= ~TDA9887_AUTOMUTE; - saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, - &tda9887_cfg); + saa_call_all(dev, tuner, s_config, &tda9887_cfg); } break; } @@ -1334,7 +1331,7 @@ static int video_open(struct file *file) enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; int radio = 0; - lock_kernel(); + mutex_lock(&saa7134_devlist_lock); list_for_each_entry(dev, &saa7134_devlist, devlist) { if (dev->video_dev && (dev->video_dev->minor == minor)) goto found; @@ -1347,19 +1344,20 @@ static int video_open(struct file *file) goto found; } } - unlock_kernel(); + mutex_unlock(&saa7134_devlist_lock); return -ENODEV; - found: + +found: + mutex_unlock(&saa7134_devlist_lock); dprintk("open minor=%d radio=%d type=%s\n",minor,radio, v4l2_type_names[type]); /* allocate + initialize per filehandle data */ fh = kzalloc(sizeof(*fh),GFP_KERNEL); - if (NULL == fh) { - unlock_kernel(); + if (NULL == fh) return -ENOMEM; - } + file->private_data = fh; fh->dev = dev; fh->radio = radio; @@ -1387,12 +1385,11 @@ static int video_open(struct file *file) if (fh->radio) { /* switch to radio mode */ saa7134_tvaudio_setinput(dev,&card(dev).radio); - saa7134_i2c_call_clients(dev,AUDC_SET_RADIO, NULL); + saa_call_all(dev, tuner, s_radio); } else { /* switch to video/vbi mode */ video_mux(dev,dev->ctl_input); } - unlock_kernel(); return 0; } @@ -1466,6 +1463,7 @@ static int video_release(struct file *file) { struct saa7134_fh *fh = file->private_data; struct saa7134_dev *dev = fh->dev; + struct rds_command cmd; unsigned long flags; /* turn off overlay */ @@ -1498,7 +1496,9 @@ static int video_release(struct file *file) saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0); saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0); - saa7134_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + saa_call_all(dev, core, s_standby, 0); + if (fh->radio) + saa_call_all(dev, core, ioctl, RDS_CMD_CLOSE, &cmd); /* free stuff */ videobuf_mmap_free(&fh->cap); @@ -1519,6 +1519,37 @@ static int video_mmap(struct file *file, struct vm_area_struct * vma) return videobuf_mmap_mapper(saa7134_queue(fh), vma); } +static ssize_t radio_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + struct rds_command cmd; + + cmd.block_count = count/3; + cmd.buffer = data; + cmd.instance = file; + cmd.result = -ENODEV; + + saa_call_all(dev, core, ioctl, RDS_CMD_READ, &cmd); + + return cmd.result; +} + +static unsigned int radio_poll(struct file *file, poll_table *wait) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + struct rds_command cmd; + + cmd.instance = file; + cmd.event_list = wait; + cmd.result = -ENODEV; + saa_call_all(dev, core, ioctl, RDS_CMD_POLL, &cmd); + + return cmd.result; +} + /* ------------------------------------------------------------------ */ static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv, @@ -2041,7 +2072,7 @@ static int saa7134_s_frequency(struct file *file, void *priv, mutex_lock(&dev->lock); dev->ctl_freq = f->frequency; - saa7134_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f); + saa_call_all(dev, tuner, s_frequency, f); saa7134_tvaudio_do_scan(dev); mutex_unlock(&dev->lock); @@ -2299,7 +2330,7 @@ static int radio_g_tuner(struct file *file, void *priv, strcpy(t->name, "Radio"); t->type = V4L2_TUNER_RADIO; - saa7134_i2c_call_clients(dev, VIDIOC_G_TUNER, t); + saa_call_all(dev, tuner, g_tuner, t); if (dev->input->amux == TV) { t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11); t->rxsubchans = (saa_readb(0x529) & 0x08) ? @@ -2316,7 +2347,7 @@ static int radio_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; - saa7134_i2c_call_clients(dev, VIDIOC_S_TUNER, t); + saa_call_all(dev, tuner, s_tuner, t); return 0; } @@ -2443,8 +2474,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { static const struct v4l2_file_operations radio_fops = { .owner = THIS_MODULE, .open = video_open, + .read = radio_read, .release = video_release, .ioctl = video_ioctl2, + .poll = radio_poll, }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 14ee265..a2dd326 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -35,6 +35,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> #include <media/tuner.h> #include <media/ir-common.h> #include <media/ir-kbd-i2c.h> @@ -277,6 +278,8 @@ struct saa7134_format { #define SAA7134_BOARD_ASUSTeK_TIGER 152 #define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 #define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154 +#define SAA7134_BOARD_HAUPPAUGE_HVR1120 155 +#define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -309,6 +312,11 @@ enum saa7134_mpeg_type { SAA7134_MPEG_DVB, }; +enum saa7134_mpeg_ts_type { + SAA7134_MPEG_TS_PARALLEL = 0, + SAA7134_MPEG_TS_SERIAL, +}; + struct saa7134_board { char *name; unsigned int audio_clock; @@ -324,6 +332,8 @@ struct saa7134_board { unsigned int radio_type; unsigned char tuner_addr; unsigned char radio_addr; + unsigned char empress_addr; + unsigned char rds_addr; unsigned int tda9887_conf; unsigned int tuner_config; @@ -331,6 +341,7 @@ struct saa7134_board { /* peripheral I/O */ enum saa7134_video_out video_out; enum saa7134_mpeg_type mpeg; + enum saa7134_mpeg_ts_type ts_type; unsigned int vid_port_opts; }; @@ -445,7 +456,6 @@ struct saa7134_dmasound { unsigned int bufsize; struct saa7134_pgtable pt; struct videobuf_dmabuf dma; - wait_queue_head_t wq; unsigned int dma_blk; unsigned int read_offset; unsigned int read_count; @@ -482,6 +492,7 @@ struct saa7134_dev { struct mutex lock; spinlock_t slock; struct v4l2_prio_state prio; + struct v4l2_device v4l2_dev; /* workstruct for loading modules */ struct work_struct request_module_wk; @@ -572,7 +583,6 @@ struct saa7134_dev { enum saa7134_ts_status ts_state; unsigned int buff_cnt; struct saa7134_mpeg_ops *mops; - struct i2c_client *mpeg_i2c_client; /* SAA7134_MPEG_EMPRESS only */ struct video_device *empress_dev; @@ -588,6 +598,7 @@ struct saa7134_dev { int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg); #endif + void (*gate_ctrl)(struct saa7134_dev *dev, int open); }; /* ----------------------------------------------------------- */ @@ -616,10 +627,31 @@ struct saa7134_dev { V4L2_STD_NTSC | V4L2_STD_PAL_M | \ V4L2_STD_PAL_60) +#define GRP_EMPRESS (1) +#define saa_call_all(dev, o, f, args...) do { \ + if (dev->gate_ctrl) \ + dev->gate_ctrl(dev, 1); \ + v4l2_device_call_all(&(dev)->v4l2_dev, 0, o, f , ##args); \ + if (dev->gate_ctrl) \ + dev->gate_ctrl(dev, 0); \ +} while (0) + +#define saa_call_empress(dev, o, f, args...) ({ \ + long _rc; \ + if (dev->gate_ctrl) \ + dev->gate_ctrl(dev, 1); \ + _rc = v4l2_device_call_until_err(&(dev)->v4l2_dev, \ + GRP_EMPRESS, o, f , ##args); \ + if (dev->gate_ctrl) \ + dev->gate_ctrl(dev, 0); \ + _rc; \ +}) + /* ----------------------------------------------------------- */ /* saa7134-core.c */ extern struct list_head saa7134_devlist; +extern struct mutex saa7134_devlist_lock; extern int saa7134_no_overlay; void saa7134_track_gpio(struct saa7134_dev *dev, char *msg); @@ -668,10 +700,6 @@ int saa7134_tuner_callback(void *priv, int component, int command, int arg); int saa7134_i2c_register(struct saa7134_dev *dev); int saa7134_i2c_unregister(struct saa7134_dev *dev); -void saa7134_i2c_call_clients(struct saa7134_dev *dev, - unsigned int cmd, void *arg); -int saa7134_i2c_call_saa6752(struct saa7134_dev *dev, - unsigned int cmd, void *arg); /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/saa7146.h b/drivers/media/video/saa7146.h index 2830b5e..9fadb33 100644 --- a/drivers/media/video/saa7146.h +++ b/drivers/media/video/saa7146.h @@ -25,8 +25,6 @@ #include <linux/types.h> #include <linux/wait.h> -#include <linux/videodev.h> - #ifndef O_NONCAP #define O_NONCAP O_TRUNC #endif diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c index 88c5e94..25bf230 100644 --- a/drivers/media/video/saa717x.c +++ b/drivers/media/video/saa717x.c @@ -931,7 +931,7 @@ static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) break; case V4L2_CID_HUE: - if (ctrl->value < -127 || ctrl->value > 127) { + if (ctrl->value < -128 || ctrl->value > 127) { v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } @@ -1380,11 +1380,6 @@ static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops saa717x_core_ops = { @@ -1528,10 +1523,7 @@ MODULE_DEVICE_TABLE(i2c, saa717x_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa717x", - .driverid = I2C_DRIVERID_SAA717X, - .command = saa717x_command, .probe = saa717x_probe, .remove = saa717x_remove, - .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, .id_table = saa717x_id, }; diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c index 6debb65..75747b1 100644 --- a/drivers/media/video/saa7185.c +++ b/drivers/media/video/saa7185.c @@ -30,52 +30,58 @@ #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> -#include <linux/video_encoder.h> -#include <media/v4l2-common.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("Philips SAA7185 video encoder driver"); MODULE_AUTHOR("Dave Perks"); MODULE_LICENSE("GPL"); - static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); + /* ----------------------------------------------------------------------- */ struct saa7185 { + struct v4l2_subdev sd; unsigned char reg[128]; - int norm; - int enable; - int bright; - int contrast; - int hue; - int sat; + v4l2_std_id norm; }; +static inline struct saa7185 *to_saa7185(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7185, sd); +} + /* ----------------------------------------------------------------------- */ -static inline int saa7185_read(struct i2c_client *client) +static inline int saa7185_read(struct v4l2_subdev *sd) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte(client); } -static int saa7185_write(struct i2c_client *client, u8 reg, u8 value) +static int saa7185_write(struct v4l2_subdev *sd, u8 reg, u8 value) { - struct saa7185 *encoder = i2c_get_clientdata(client); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7185 *encoder = to_saa7185(sd); - v4l_dbg(1, debug, client, "%02x set to %02x\n", reg, value); + v4l2_dbg(1, debug, sd, "%02x set to %02x\n", reg, value); encoder->reg[reg] = value; return i2c_smbus_write_byte_data(client, reg, value); } -static int saa7185_write_block(struct i2c_client *client, +static int saa7185_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7185 *encoder = to_saa7185(sd); int ret = -1; u8 reg; @@ -83,7 +89,6 @@ static int saa7185_write_block(struct i2c_client *client, * the adapter understands raw I2C */ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { /* do raw I2C, not smbus compatible */ - struct saa7185 *encoder = i2c_get_clientdata(client); u8 block_data[32]; int block_len; @@ -104,7 +109,7 @@ static int saa7185_write_block(struct i2c_client *client, /* do some slow I2C emulation kind of thing */ while (len >= 2) { reg = *data++; - ret = saa7185_write(client, reg, *data++); + ret = saa7185_write(sd, reg, *data++); if (ret < 0) break; len -= 2; @@ -213,133 +218,106 @@ static const unsigned char init_ntsc[] = { 0x66, 0x21, /* FSC3 */ }; -static int saa7185_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - struct saa7185 *encoder = i2c_get_clientdata(client); - - switch (cmd) { - case 0: - saa7185_write_block(client, init_common, - sizeof(init_common)); - switch (encoder->norm) { - - case VIDEO_MODE_NTSC: - saa7185_write_block(client, init_ntsc, - sizeof(init_ntsc)); - break; - - case VIDEO_MODE_PAL: - saa7185_write_block(client, init_pal, - sizeof(init_pal)); - break; - } - break; - case ENCODER_GET_CAPABILITIES: - { - struct video_encoder_capability *cap = arg; +static int saa7185_init(struct v4l2_subdev *sd, u32 val) +{ + struct saa7185 *encoder = to_saa7185(sd); - cap->flags = - VIDEO_ENCODER_PAL | VIDEO_ENCODER_NTSC | - VIDEO_ENCODER_SECAM | VIDEO_ENCODER_CCIR; - cap->inputs = 1; - cap->outputs = 1; - break; - } + saa7185_write_block(sd, init_common, sizeof(init_common)); + if (encoder->norm & V4L2_STD_NTSC) + saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); + else + saa7185_write_block(sd, init_pal, sizeof(init_pal)); + return 0; +} - case ENCODER_SET_NORM: - { - int *iarg = arg; +static int saa7185_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa7185 *encoder = to_saa7185(sd); - //saa7185_write_block(client, init_common, sizeof(init_common)); + if (std & V4L2_STD_NTSC) + saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); + else if (std & V4L2_STD_PAL) + saa7185_write_block(sd, init_pal, sizeof(init_pal)); + else + return -EINVAL; + encoder->norm = std; + return 0; +} - switch (*iarg) { - case VIDEO_MODE_NTSC: - saa7185_write_block(client, init_ntsc, - sizeof(init_ntsc)); - break; +static int saa7185_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa7185 *encoder = to_saa7185(sd); - case VIDEO_MODE_PAL: - saa7185_write_block(client, init_pal, - sizeof(init_pal)); - break; + /* RJ: route->input = 0: input is from SA7111 + route->input = 1: input is from ZR36060 */ - case VIDEO_MODE_SECAM: - default: - return -EINVAL; - } - encoder->norm = *iarg; - break; - } - - case ENCODER_SET_INPUT: - { - int *iarg = arg; - - /* RJ: *iarg = 0: input is from SA7111 - *iarg = 1: input is from ZR36060 */ - - switch (*iarg) { - case 0: - /* Switch RTCE to 1 */ - saa7185_write(client, 0x61, - (encoder->reg[0x61] & 0xf7) | 0x08); - saa7185_write(client, 0x6e, 0x01); - break; - - case 1: - /* Switch RTCE to 0 */ - saa7185_write(client, 0x61, - (encoder->reg[0x61] & 0xf7) | 0x00); - /* SW: a slight sync problem... */ - saa7185_write(client, 0x6e, 0x00); - break; - - default: - return -EINVAL; - } + switch (route->input) { + case 0: + /* turn off colorbar */ + saa7185_write(sd, 0x3a, 0x0f); + /* Switch RTCE to 1 */ + saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); + saa7185_write(sd, 0x6e, 0x01); break; - } - case ENCODER_SET_OUTPUT: - { - int *iarg = arg; - - /* not much choice of outputs */ - if (*iarg != 0) - return -EINVAL; + case 1: + /* turn off colorbar */ + saa7185_write(sd, 0x3a, 0x0f); + /* Switch RTCE to 0 */ + saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x00); + /* SW: a slight sync problem... */ + saa7185_write(sd, 0x6e, 0x00); break; - } - - case ENCODER_ENABLE_OUTPUT: - { - int *iarg = arg; - encoder->enable = !!*iarg; - saa7185_write(client, 0x61, - (encoder->reg[0x61] & 0xbf) | - (encoder->enable ? 0x00 : 0x40)); + case 2: + /* turn on colorbar */ + saa7185_write(sd, 0x3a, 0x8f); + /* Switch RTCE to 0 */ + saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08); + /* SW: a slight sync problem... */ + saa7185_write(sd, 0x6e, 0x01); break; - } default: return -EINVAL; } - return 0; } +static int saa7185_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7185, 0); +} + /* ----------------------------------------------------------------------- */ -static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; +static const struct v4l2_subdev_core_ops saa7185_core_ops = { + .g_chip_ident = saa7185_g_chip_ident, + .init = saa7185_init, +}; -I2C_CLIENT_INSMOD; +static const struct v4l2_subdev_video_ops saa7185_video_ops = { + .s_std_output = saa7185_s_std_output, + .s_routing = saa7185_s_routing, +}; + +static const struct v4l2_subdev_ops saa7185_ops = { + .core = &saa7185_core_ops, + .video = &saa7185_video_ops, +}; + + +/* ----------------------------------------------------------------------- */ static int saa7185_probe(struct i2c_client *client, const struct i2c_device_id *id) { int i; struct saa7185 *encoder; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -351,28 +329,29 @@ static int saa7185_probe(struct i2c_client *client, encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL); if (encoder == NULL) return -ENOMEM; - encoder->norm = VIDEO_MODE_NTSC; - encoder->enable = 1; - i2c_set_clientdata(client, encoder); + encoder->norm = V4L2_STD_NTSC; + sd = &encoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa7185_ops); - i = saa7185_write_block(client, init_common, sizeof(init_common)); + i = saa7185_write_block(sd, init_common, sizeof(init_common)); if (i >= 0) - i = saa7185_write_block(client, init_ntsc, sizeof(init_ntsc)); + i = saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc)); if (i < 0) - v4l_dbg(1, debug, client, "init error %d\n", i); + v4l2_dbg(1, debug, sd, "init error %d\n", i); else - v4l_dbg(1, debug, client, "revision 0x%x\n", - saa7185_read(client) >> 5); + v4l2_dbg(1, debug, sd, "revision 0x%x\n", + saa7185_read(sd) >> 5); return 0; } static int saa7185_remove(struct i2c_client *client) { - struct saa7185 *encoder = i2c_get_clientdata(client); - - saa7185_write(client, 0x61, (encoder->reg[0x61]) | 0x40); /* SW: output off is active */ - //saa7185_write(client, 0x3a, (encoder->reg[0x3a]) | 0x80); /* SW: color bar */ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct saa7185 *encoder = to_saa7185(sd); + v4l2_device_unregister_subdev(sd); + /* SW: output off is active */ + saa7185_write(sd, 0x61, (encoder->reg[0x61]) | 0x40); kfree(encoder); return 0; } @@ -387,8 +366,6 @@ MODULE_DEVICE_TABLE(i2c, saa7185_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7185", - .driverid = I2C_DRIVERID_SAA7185B, - .command = saa7185_command, .probe = saa7185_probe, .remove = saa7185_remove, .id_table = saa7185_id, diff --git a/drivers/media/video/saa7191.c b/drivers/media/video/saa7191.c index b4018cc..3f523ae 100644 --- a/drivers/media/video/saa7191.c +++ b/drivers/media/video/saa7191.c @@ -19,9 +19,11 @@ #include <linux/mm.h> #include <linux/slab.h> -#include <linux/videodev.h> -#include <linux/video_decoder.h> +#include <linux/videodev2.h> #include <linux/i2c.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> #include "saa7191.h" @@ -32,6 +34,7 @@ MODULE_VERSION(SAA7191_MODULE_VERSION); MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); MODULE_LICENSE("GPL"); + // #define SAA7191_DEBUG #ifdef SAA7191_DEBUG @@ -44,17 +47,20 @@ MODULE_LICENSE("GPL"); #define SAA7191_SYNC_DELAY 100 /* milliseconds */ struct saa7191 { - struct i2c_client *client; + struct v4l2_subdev sd; /* the register values are stored here as the actual * I2C-registers are write-only */ u8 reg[25]; int input; - int norm; + v4l2_std_id norm; }; -static struct i2c_driver i2c_driver_saa7191; +static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7191, sd); +} static const u8 initseq[] = { 0, /* Subaddress */ @@ -100,15 +106,14 @@ static const u8 initseq[] = { /* SAA7191 register handling */ -static u8 saa7191_read_reg(struct i2c_client *client, - u8 reg) +static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg) { - return ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg]; + return to_saa7191(sd)->reg[reg]; } -static int saa7191_read_status(struct i2c_client *client, - u8 *value) +static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; ret = i2c_master_recv(client, value, 1); @@ -121,21 +126,23 @@ static int saa7191_read_status(struct i2c_client *client, } -static int saa7191_write_reg(struct i2c_client *client, u8 reg, - u8 value) +static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) { - ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg] = value; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + to_saa7191(sd)->reg[reg] = value; return i2c_smbus_write_byte_data(client, reg, value); } /* the first byte of data must be the first subaddress number (register) */ -static int saa7191_write_block(struct i2c_client *client, +static int saa7191_write_block(struct v4l2_subdev *sd, u8 length, const u8 *data) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa7191 *decoder = to_saa7191(sd); int i; int ret; - struct saa7191 *decoder = (struct saa7191 *)i2c_get_clientdata(client); for (i = 0; i < (length - 1); i++) { decoder->reg[data[0] + i] = data[i + 1]; } @@ -152,14 +159,15 @@ static int saa7191_write_block(struct i2c_client *client, /* Helper functions */ -static int saa7191_set_input(struct i2c_client *client, int input) +static int saa7191_s_routing(struct v4l2_subdev *sd, + const struct v4l2_routing *route) { - struct saa7191 *decoder = i2c_get_clientdata(client); - u8 luma = saa7191_read_reg(client, SAA7191_REG_LUMA); - u8 iock = saa7191_read_reg(client, SAA7191_REG_IOCK); + struct saa7191 *decoder = to_saa7191(sd); + u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA); + u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK); int err; - switch (input) { + switch (route->input) { case SAA7191_INPUT_COMPOSITE: /* Set Composite input */ iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1 | SAA7191_IOCK_GPSW2); @@ -175,54 +183,50 @@ static int saa7191_set_input(struct i2c_client *client, int input) return -EINVAL; } - err = saa7191_write_reg(client, SAA7191_REG_LUMA, luma); + err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma); if (err) return -EIO; - err = saa7191_write_reg(client, SAA7191_REG_IOCK, iock); + err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock); if (err) return -EIO; - decoder->input = input; + decoder->input = route->input; return 0; } -static int saa7191_set_norm(struct i2c_client *client, int norm) +static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) { - struct saa7191 *decoder = i2c_get_clientdata(client); - u8 stdc = saa7191_read_reg(client, SAA7191_REG_STDC); - u8 ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3); - u8 chcv = saa7191_read_reg(client, SAA7191_REG_CHCV); + struct saa7191 *decoder = to_saa7191(sd); + u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); + u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); + u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV); int err; - switch(norm) { - case SAA7191_NORM_PAL: + if (norm & V4L2_STD_PAL) { stdc &= ~SAA7191_STDC_SECS; ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); chcv = SAA7191_CHCV_PAL; - break; - case SAA7191_NORM_NTSC: + } else if (norm & V4L2_STD_NTSC) { stdc &= ~SAA7191_STDC_SECS; ctl3 &= ~SAA7191_CTL3_AUFD; ctl3 |= SAA7191_CTL3_FSEL; chcv = SAA7191_CHCV_NTSC; - break; - case SAA7191_NORM_SECAM: + } else if (norm & V4L2_STD_SECAM) { stdc |= SAA7191_STDC_SECS; ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); chcv = SAA7191_CHCV_PAL; - break; - default: + } else { return -EINVAL; } - err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); if (err) return -EIO; - err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc); + err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); if (err) return -EIO; - err = saa7191_write_reg(client, SAA7191_REG_CHCV, chcv); + err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv); if (err) return -EIO; @@ -230,19 +234,19 @@ static int saa7191_set_norm(struct i2c_client *client, int norm) dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3, stdc, chcv); - dprintk("norm: %d\n", norm); + dprintk("norm: %llx\n", norm); return 0; } -static int saa7191_wait_for_signal(struct i2c_client *client, u8 *status) +static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status) { int i = 0; dprintk("Checking for signal...\n"); for (i = 0; i < SAA7191_SYNC_COUNT; i++) { - if (saa7191_read_status(client, status)) + if (saa7191_read_status(sd, status)) return -EIO; if (((*status) & SAA7191_STATUS_HLCK) == 0) { @@ -258,31 +262,34 @@ static int saa7191_wait_for_signal(struct i2c_client *client, u8 *status) return -EBUSY; } -static int saa7191_autodetect_norm_extended(struct i2c_client *client) +static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm) { - u8 stdc = saa7191_read_reg(client, SAA7191_REG_STDC); - u8 ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3); + struct saa7191 *decoder = to_saa7191(sd); + u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); + u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); u8 status; + v4l2_std_id old_norm = decoder->norm; int err = 0; dprintk("SAA7191 extended signal auto-detection...\n"); + *norm = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; stdc &= ~SAA7191_STDC_SECS; ctl3 &= ~(SAA7191_CTL3_FSEL); - err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc); + err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); if (err) { err = -EIO; goto out; } - err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); if (err) { err = -EIO; goto out; } ctl3 |= SAA7191_CTL3_AUFD; - err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); + err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); if (err) { err = -EIO; goto out; @@ -290,53 +297,54 @@ static int saa7191_autodetect_norm_extended(struct i2c_client *client) msleep(SAA7191_SYNC_DELAY); - err = saa7191_wait_for_signal(client, &status); + err = saa7191_wait_for_signal(sd, &status); if (err) goto out; if (status & SAA7191_STATUS_FIDT) { /* 60Hz signal -> NTSC */ dprintk("60Hz signal: NTSC\n"); - return saa7191_set_norm(client, SAA7191_NORM_NTSC); + *norm = V4L2_STD_NTSC; + return 0; } /* 50Hz signal */ dprintk("50Hz signal: Trying PAL...\n"); /* try PAL first */ - err = saa7191_set_norm(client, SAA7191_NORM_PAL); + err = saa7191_s_std(sd, V4L2_STD_PAL); if (err) goto out; msleep(SAA7191_SYNC_DELAY); - err = saa7191_wait_for_signal(client, &status); + err = saa7191_wait_for_signal(sd, &status); if (err) goto out; /* not 50Hz ? */ if (status & SAA7191_STATUS_FIDT) { dprintk("No 50Hz signal\n"); - err = -EAGAIN; - goto out; + saa7191_s_std(sd, old_norm); + return -EAGAIN; } if (status & SAA7191_STATUS_CODE) { dprintk("PAL\n"); - return 0; + *norm = V4L2_STD_PAL; + return saa7191_s_std(sd, old_norm); } dprintk("No color detected with PAL - Trying SECAM...\n"); /* no color detected ? -> try SECAM */ - err = saa7191_set_norm(client, - SAA7191_NORM_SECAM); + err = saa7191_s_std(sd, V4L2_STD_SECAM); if (err) goto out; msleep(SAA7191_SYNC_DELAY); - err = saa7191_wait_for_signal(client, &status); + err = saa7191_wait_for_signal(sd, &status); if (err) goto out; @@ -350,32 +358,17 @@ static int saa7191_autodetect_norm_extended(struct i2c_client *client) if (status & SAA7191_STATUS_CODE) { /* Color detected -> SECAM */ dprintk("SECAM\n"); - return 0; + *norm = V4L2_STD_SECAM; + return saa7191_s_std(sd, old_norm); } dprintk("No color detected with SECAM - Going back to PAL.\n"); - /* still no color detected ? - * -> set norm back to PAL */ - err = saa7191_set_norm(client, - SAA7191_NORM_PAL); - if (err) - goto out; - out: - ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3); - if (ctl3 & SAA7191_CTL3_AUFD) { - ctl3 &= ~(SAA7191_CTL3_AUFD); - err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3); - if (err) { - err = -EIO; - } - } - - return err; + return saa7191_s_std(sd, old_norm); } -static int saa7191_autodetect_norm(struct i2c_client *client) +static int saa7191_autodetect_norm(struct v4l2_subdev *sd) { u8 status; @@ -383,7 +376,7 @@ static int saa7191_autodetect_norm(struct i2c_client *client) dprintk("Reading status...\n"); - if (saa7191_read_status(client, &status)) + if (saa7191_read_status(sd, &status)) return -EIO; dprintk("Checking for signal...\n"); @@ -399,26 +392,25 @@ static int saa7191_autodetect_norm(struct i2c_client *client) if (status & SAA7191_STATUS_FIDT) { /* 60hz signal -> NTSC */ dprintk("NTSC\n"); - return saa7191_set_norm(client, SAA7191_NORM_NTSC); + return saa7191_s_std(sd, V4L2_STD_NTSC); } else { /* 50hz signal -> PAL */ dprintk("PAL\n"); - return saa7191_set_norm(client, SAA7191_NORM_PAL); + return saa7191_s_std(sd, V4L2_STD_PAL); } } -static int saa7191_get_control(struct i2c_client *client, - struct saa7191_control *ctrl) +static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { u8 reg; int ret = 0; - switch (ctrl->type) { + switch (ctrl->id) { case SAA7191_CONTROL_BANDPASS: case SAA7191_CONTROL_BANDPASS_WEIGHT: case SAA7191_CONTROL_CORING: - reg = saa7191_read_reg(client, SAA7191_REG_LUMA); - switch (ctrl->type) { + reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); + switch (ctrl->id) { case SAA7191_CONTROL_BANDPASS: ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK) >> SAA7191_LUMA_BPSS_SHIFT; @@ -435,15 +427,15 @@ static int saa7191_get_control(struct i2c_client *client, break; case SAA7191_CONTROL_FORCE_COLOUR: case SAA7191_CONTROL_CHROMA_GAIN: - reg = saa7191_read_reg(client, SAA7191_REG_GAIN); - if (ctrl->type == SAA7191_CONTROL_FORCE_COLOUR) + reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); + if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0; else ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK) >> SAA7191_GAIN_LFIS_SHIFT; break; - case SAA7191_CONTROL_HUE: - reg = saa7191_read_reg(client, SAA7191_REG_HUEC); + case V4L2_CID_HUE: + reg = saa7191_read_reg(sd, SAA7191_REG_HUEC); if (reg < 0x80) reg += 0x80; else @@ -451,18 +443,18 @@ static int saa7191_get_control(struct i2c_client *client, ctrl->value = (s32)reg; break; case SAA7191_CONTROL_VTRC: - reg = saa7191_read_reg(client, SAA7191_REG_STDC); + reg = saa7191_read_reg(sd, SAA7191_REG_STDC); ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0; break; case SAA7191_CONTROL_LUMA_DELAY: - reg = saa7191_read_reg(client, SAA7191_REG_CTL3); + reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK) >> SAA7191_CTL3_YDEL_SHIFT; if (ctrl->value >= 4) ctrl->value -= 8; break; case SAA7191_CONTROL_VNR: - reg = saa7191_read_reg(client, SAA7191_REG_CTL4); + reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK) >> SAA7191_CTL4_VNOI_SHIFT; break; @@ -473,18 +465,17 @@ static int saa7191_get_control(struct i2c_client *client, return ret; } -static int saa7191_set_control(struct i2c_client *client, - struct saa7191_control *ctrl) +static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { u8 reg; int ret = 0; - switch (ctrl->type) { + switch (ctrl->id) { case SAA7191_CONTROL_BANDPASS: case SAA7191_CONTROL_BANDPASS_WEIGHT: case SAA7191_CONTROL_CORING: - reg = saa7191_read_reg(client, SAA7191_REG_LUMA); - switch (ctrl->type) { + reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); + switch (ctrl->id) { case SAA7191_CONTROL_BANDPASS: reg &= ~SAA7191_LUMA_BPSS_MASK; reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT) @@ -501,12 +492,12 @@ static int saa7191_set_control(struct i2c_client *client, & SAA7191_LUMA_CORI_MASK; break; } - ret = saa7191_write_reg(client, SAA7191_REG_LUMA, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg); break; case SAA7191_CONTROL_FORCE_COLOUR: case SAA7191_CONTROL_CHROMA_GAIN: - reg = saa7191_read_reg(client, SAA7191_REG_GAIN); - if (ctrl->type == SAA7191_CONTROL_FORCE_COLOUR) { + reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); + if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) { if (ctrl->value) reg |= SAA7191_GAIN_COLO; else @@ -516,41 +507,41 @@ static int saa7191_set_control(struct i2c_client *client, reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT) & SAA7191_GAIN_LFIS_MASK; } - ret = saa7191_write_reg(client, SAA7191_REG_GAIN, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg); break; - case SAA7191_CONTROL_HUE: + case V4L2_CID_HUE: reg = ctrl->value & 0xff; if (reg < 0x80) reg += 0x80; else reg -= 0x80; - ret = saa7191_write_reg(client, SAA7191_REG_HUEC, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg); break; case SAA7191_CONTROL_VTRC: - reg = saa7191_read_reg(client, SAA7191_REG_STDC); + reg = saa7191_read_reg(sd, SAA7191_REG_STDC); if (ctrl->value) reg |= SAA7191_STDC_VTRC; else reg &= ~SAA7191_STDC_VTRC; - ret = saa7191_write_reg(client, SAA7191_REG_STDC, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg); break; case SAA7191_CONTROL_LUMA_DELAY: { s32 value = ctrl->value; if (value < 0) value += 8; - reg = saa7191_read_reg(client, SAA7191_REG_CTL3); + reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); reg &= ~SAA7191_CTL3_YDEL_MASK; reg |= (value << SAA7191_CTL3_YDEL_SHIFT) & SAA7191_CTL3_YDEL_MASK; - ret = saa7191_write_reg(client, SAA7191_REG_CTL3, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg); break; } case SAA7191_CONTROL_VNR: - reg = saa7191_read_reg(client, SAA7191_REG_CTL4); + reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); reg &= ~SAA7191_CTL4_VNOI_MASK; reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT) & SAA7191_CTL4_VNOI_MASK; - ret = saa7191_write_reg(client, SAA7191_REG_CTL4, reg); + ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg); break; default: ret = -EINVAL; @@ -561,247 +552,108 @@ static int saa7191_set_control(struct i2c_client *client, /* I2C-interface */ -static int saa7191_attach(struct i2c_adapter *adap, int addr, int kind) +static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status) { - int err = 0; - struct saa7191 *decoder; - struct i2c_client *client; - - printk(KERN_INFO "Philips SAA7191 driver version %s\n", - SAA7191_MODULE_VERSION); - - client = kzalloc(sizeof(*client), GFP_KERNEL); - if (!client) - return -ENOMEM; - decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); - if (!decoder) { - err = -ENOMEM; - goto out_free_client; - } - - client->addr = addr; - client->adapter = adap; - client->driver = &i2c_driver_saa7191; - client->flags = 0; - strcpy(client->name, "saa7191 client"); - i2c_set_clientdata(client, decoder); - - decoder->client = client; - - err = i2c_attach_client(client); - if (err) - goto out_free_decoder; - - err = saa7191_write_block(client, sizeof(initseq), initseq); - if (err) { - printk(KERN_ERR "SAA7191 initialization failed\n"); - goto out_detach_client; - } - - printk(KERN_INFO "SAA7191 initialized\n"); - - decoder->input = SAA7191_INPUT_COMPOSITE; - decoder->norm = SAA7191_NORM_PAL; - - err = saa7191_autodetect_norm(client); - if (err && (err != -EBUSY)) { - printk(KERN_ERR "SAA7191: Signal auto-detection failed\n"); - } + u8 status_reg; + int res = V4L2_IN_ST_NO_SIGNAL; + if (saa7191_read_status(sd, &status_reg)) + return -EIO; + if ((status_reg & SAA7191_STATUS_HLCK) == 0) + res = 0; + if (!(status_reg & SAA7191_STATUS_CODE)) + res |= V4L2_IN_ST_NO_COLOR; + *status = res; return 0; - -out_detach_client: - i2c_detach_client(client); -out_free_decoder: - kfree(decoder); -out_free_client: - kfree(client); - return err; } -static int saa7191_probe(struct i2c_adapter *adap) -{ - /* Always connected to VINO */ - if (adap->id == I2C_HW_SGI_VINO) - return saa7191_attach(adap, SAA7191_ADDR, 0); - /* Feel free to add probe here :-) */ - return -ENODEV; -} -static int saa7191_detach(struct i2c_client *client) +static int saa7191_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) { - struct saa7191 *decoder = i2c_get_clientdata(client); + struct i2c_client *client = v4l2_get_subdevdata(sd); - i2c_detach_client(client); - kfree(decoder); - kfree(client); - return 0; + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7191, 0); } -static int saa7191_command(struct i2c_client *client, unsigned int cmd, - void *arg) -{ - struct saa7191 *decoder = i2c_get_clientdata(client); +/* ----------------------------------------------------------------------- */ - switch (cmd) { - case DECODER_GET_CAPABILITIES: { - struct video_decoder_capability *cap = arg; +static const struct v4l2_subdev_core_ops saa7191_core_ops = { + .g_chip_ident = saa7191_g_chip_ident, + .g_ctrl = saa7191_g_ctrl, + .s_ctrl = saa7191_s_ctrl, +}; - cap->flags = VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC | - VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO; - cap->inputs = (client->adapter->id == I2C_HW_SGI_VINO) ? 2 : 1; - cap->outputs = 1; - break; - } - case DECODER_GET_STATUS: { - int *iarg = arg; - u8 status; - int res = 0; +static const struct v4l2_subdev_tuner_ops saa7191_tuner_ops = { + .s_std = saa7191_s_std, +}; - if (saa7191_read_status(client, &status)) { - return -EIO; - } - if ((status & SAA7191_STATUS_HLCK) == 0) - res |= DECODER_STATUS_GOOD; - if (status & SAA7191_STATUS_CODE) - res |= DECODER_STATUS_COLOR; - switch (decoder->norm) { - case SAA7191_NORM_NTSC: - res |= DECODER_STATUS_NTSC; - break; - case SAA7191_NORM_PAL: - res |= DECODER_STATUS_PAL; - break; - case SAA7191_NORM_SECAM: - res |= DECODER_STATUS_SECAM; - break; - case SAA7191_NORM_AUTO: - default: - if (status & SAA7191_STATUS_FIDT) - res |= DECODER_STATUS_NTSC; - else - res |= DECODER_STATUS_PAL; - break; - } - *iarg = res; - break; - } - case DECODER_SET_NORM: { - int *iarg = arg; - - switch (*iarg) { - case VIDEO_MODE_AUTO: - return saa7191_autodetect_norm(client); - case VIDEO_MODE_PAL: - return saa7191_set_norm(client, SAA7191_NORM_PAL); - case VIDEO_MODE_NTSC: - return saa7191_set_norm(client, SAA7191_NORM_NTSC); - case VIDEO_MODE_SECAM: - return saa7191_set_norm(client, SAA7191_NORM_SECAM); - default: - return -EINVAL; - } - break; - } - case DECODER_SET_INPUT: { - int *iarg = arg; - - switch (client->adapter->id) { - case I2C_HW_SGI_VINO: - return saa7191_set_input(client, *iarg); - default: - if (*iarg != 0) - return -EINVAL; - } - break; - } - case DECODER_SET_OUTPUT: { - int *iarg = arg; +static const struct v4l2_subdev_video_ops saa7191_video_ops = { + .s_routing = saa7191_s_routing, + .querystd = saa7191_querystd, + .g_input_status = saa7191_g_input_status, +}; - /* not much choice of outputs */ - if (*iarg != 0) - return -EINVAL; - break; - } - case DECODER_ENABLE_OUTPUT: { - /* Always enabled */ - break; - } - case DECODER_SET_PICTURE: { - struct video_picture *pic = arg; - unsigned val; - int err; +static const struct v4l2_subdev_ops saa7191_ops = { + .core = &saa7191_core_ops, + .video = &saa7191_video_ops, + .tuner = &saa7191_tuner_ops, +}; - val = (pic->hue >> 8) - 0x80; +static int saa7191_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err = 0; + struct saa7191 *decoder; + struct v4l2_subdev *sd; - err = saa7191_write_reg(client, SAA7191_REG_HUEC, val); - if (err) - return -EIO; + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); - break; - } - case DECODER_SAA7191_GET_STATUS: { - struct saa7191_status *status = arg; - u8 status_reg; + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; - if (saa7191_read_status(client, &status_reg)) - return -EIO; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa7191_ops); - status->signal = ((status_reg & SAA7191_STATUS_HLCK) == 0) - ? 1 : 0; - status->signal_60hz = (status_reg & SAA7191_STATUS_FIDT) - ? 1 : 0; - status->color = (status_reg & SAA7191_STATUS_CODE) ? 1 : 0; + err = saa7191_write_block(sd, sizeof(initseq), initseq); + if (err) { + printk(KERN_ERR "SAA7191 initialization failed\n"); + kfree(decoder); + return err; + } - status->input = decoder->input; - status->norm = decoder->norm; + printk(KERN_INFO "SAA7191 initialized\n"); - break; - } - case DECODER_SAA7191_SET_NORM: { - int *norm = arg; - - switch (*norm) { - case SAA7191_NORM_AUTO: - return saa7191_autodetect_norm(client); - case SAA7191_NORM_AUTO_EXT: - return saa7191_autodetect_norm_extended(client); - default: - return saa7191_set_norm(client, *norm); - } - } - case DECODER_SAA7191_GET_CONTROL: { - return saa7191_get_control(client, arg); - } - case DECODER_SAA7191_SET_CONTROL: { - return saa7191_set_control(client, arg); - } - default: - return -EINVAL; - } + decoder->input = SAA7191_INPUT_COMPOSITE; + decoder->norm = V4L2_STD_PAL; + + err = saa7191_autodetect_norm(sd); + if (err && (err != -EBUSY)) + printk(KERN_ERR "SAA7191: Signal auto-detection failed\n"); return 0; } -static struct i2c_driver i2c_driver_saa7191 = { - .driver = { - .name = "saa7191", - }, - .id = I2C_DRIVERID_SAA7191, - .attach_adapter = saa7191_probe, - .detach_client = saa7191_detach, - .command = saa7191_command -}; - -static int saa7191_init(void) +static int saa7191_remove(struct i2c_client *client) { - return i2c_add_driver(&i2c_driver_saa7191); -} + struct v4l2_subdev *sd = i2c_get_clientdata(client); -static void saa7191_exit(void) -{ - i2c_del_driver(&i2c_driver_saa7191); + v4l2_device_unregister_subdev(sd); + kfree(to_saa7191(sd)); + return 0; } -module_init(saa7191_init); -module_exit(saa7191_exit); +static const struct i2c_device_id saa7191_id[] = { + { "saa7191", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa7191_id); + +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "saa7191", + .probe = saa7191_probe, + .remove = saa7191_remove, + .id_table = saa7191_id, +}; diff --git a/drivers/media/video/saa7191.h b/drivers/media/video/saa7191.h index a2310da..803c74d 100644 --- a/drivers/media/video/saa7191.h +++ b/drivers/media/video/saa7191.h @@ -176,11 +176,9 @@ #define SAA7191_INPUT_COMPOSITE 0 #define SAA7191_INPUT_SVIDEO 1 -#define SAA7191_NORM_AUTO 0 #define SAA7191_NORM_PAL 1 #define SAA7191_NORM_NTSC 2 #define SAA7191_NORM_SECAM 3 -#define SAA7191_NORM_AUTO_EXT 4 /* extended auto-detection */ struct saa7191_status { /* 0=no signal, 1=signal detected */ @@ -232,24 +230,16 @@ struct saa7191_status { #define SAA7191_VNR_MAX 0x03 #define SAA7191_VNR_DEFAULT 0x00 -#define SAA7191_CONTROL_BANDPASS 0 -#define SAA7191_CONTROL_BANDPASS_WEIGHT 1 -#define SAA7191_CONTROL_CORING 2 -#define SAA7191_CONTROL_FORCE_COLOUR 3 /* boolean */ -#define SAA7191_CONTROL_CHROMA_GAIN 4 -#define SAA7191_CONTROL_HUE 5 -#define SAA7191_CONTROL_VTRC 6 /* boolean */ -#define SAA7191_CONTROL_LUMA_DELAY 7 -#define SAA7191_CONTROL_VNR 8 - -struct saa7191_control { - u8 type; - s32 value; -}; +#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0) +#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1) +#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2) +#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3) +#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4) +#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5) +#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6) +#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7) #define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status) #define DECODER_SAA7191_SET_NORM _IOW('d', 196, int) -#define DECODER_SAA7191_GET_CONTROL _IOR('d', 197, struct saa7191_control) -#define DECODER_SAA7191_SET_CONTROL _IOW('d', 198, struct saa7191_control) #endif diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index ddcb81d..b5e37a5 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -94,13 +94,37 @@ struct sh_mobile_ceu_dev { spinlock_t lock; struct list_head capture; struct videobuf_buffer *active; - int is_interlace; + int is_interlaced; struct sh_mobile_ceu_info *pdata; const struct soc_camera_data_format *camera_fmt; }; +static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev) +{ + unsigned long flags; + + flags = SOCAM_MASTER | + SOCAM_PCLK_SAMPLE_RISING | + SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_HSYNC_ACTIVE_LOW | + SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_VSYNC_ACTIVE_LOW | + SOCAM_DATA_ACTIVE_HIGH; + + if (pcdev->pdata->flags & SH_CEU_FLAG_USE_8BIT_BUS) + flags |= SOCAM_DATAWIDTH_8; + + if (pcdev->pdata->flags & SH_CEU_FLAG_USE_16BIT_BUS) + flags |= SOCAM_DATAWIDTH_16; + + if (flags & SOCAM_DATAWIDTH_MASK) + return flags; + + return 0; +} + static void ceu_write(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs, u32 data) { @@ -150,6 +174,7 @@ static void free_buffer(struct videobuf_queue *vq, if (in_interrupt()) BUG(); + videobuf_waiton(&buf->vb, 0, 0); videobuf_dma_contig_free(vq, &buf->vb); dev_dbg(&icd->dev, "%s freed\n", __func__); buf->vb.state = VIDEOBUF_NEEDS_INIT; @@ -181,7 +206,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_interlace) { + if (pcdev->is_interlaced) { phys_addr_bottom = phys_addr_top + icd->width; ceu_write(pcdev, CDBYR, phys_addr_bottom); } @@ -193,7 +218,7 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) case V4L2_PIX_FMT_NV61: phys_addr_top += icd->width * icd->height; ceu_write(pcdev, CDACR, phys_addr_top); - if (pcdev->is_interlace) { + if (pcdev->is_interlaced) { phys_addr_bottom = phys_addr_top + icd->width; ceu_write(pcdev, CDBCR, phys_addr_bottom); } @@ -396,7 +421,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, camera_flags = icd->ops->query_bus_param(icd); common_flags = soc_camera_bus_param_compatible(camera_flags, - pcdev->pdata->flags); + make_bus_param(pcdev)); if (!common_flags) return -EINVAL; @@ -457,7 +482,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, ceu_write(pcdev, CAMCR, value); ceu_write(pcdev, CAPCR, 0x00300000); - ceu_write(pcdev, CAIFR, (pcdev->is_interlace) ? 0x101 : 0); + ceu_write(pcdev, CAIFR, pcdev->is_interlaced ? 0x101 : 0); mdelay(1); @@ -473,7 +498,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, } height = icd->height; - if (pcdev->is_interlace) { + if (pcdev->is_interlaced) { height /= 2; cdwdr_width *= 2; } @@ -517,7 +542,7 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd) camera_flags = icd->ops->query_bus_param(icd); common_flags = soc_camera_bus_param_compatible(camera_flags, - pcdev->pdata->flags); + make_bus_param(pcdev)); if (!common_flags) return -EINVAL; @@ -562,11 +587,29 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, if (ret < 0) return 0; + /* Beginning of a pass */ + if (!idx) + icd->host_priv = 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) + goto add_single_format; + + /* + * Our case is simple so far: for any of the above four camera + * formats we add all our four synthesized NV* formats, so, + * just marking the device with a single flag suffices. If + * the format generation rules are more complex, you would have + * to actually hang your already added / counted formats onto + * 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; + n = ARRAY_SIZE(sh_mobile_ceu_formats); formats += n; for (k = 0; xlate && k < n; k++) { @@ -579,6 +622,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, icd->formats[idx].name); } default: +add_single_format: /* Generic pass-through */ formats++; if (xlate) { @@ -595,24 +639,30 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, return formats; } +static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + return icd->ops->set_crop(icd, rect); +} + static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) + 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 v4l2_format cam_f = *f; int ret; - if (!pixfmt) - return icd->ops->set_fmt(icd, pixfmt, rect); - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { dev_warn(&ici->dev, "Format %x not found\n", pixfmt); return -EINVAL; } - ret = icd->ops->set_fmt(icd, xlate->cam_fmt->fourcc, rect); + cam_f.fmt.pix.pixelformat = xlate->cam_fmt->fourcc; + ret = icd->ops->set_fmt(icd, &cam_f); if (!ret) { icd->buswidth = xlate->buswidth; @@ -662,13 +712,13 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, switch (f->fmt.pix.field) { case V4L2_FIELD_INTERLACED: - pcdev->is_interlace = 1; + pcdev->is_interlaced = 1; break; case V4L2_FIELD_ANY: f->fmt.pix.field = V4L2_FIELD_NONE; /* fall-through */ case V4L2_FIELD_NONE: - pcdev->is_interlace = 0; + pcdev->is_interlaced = 0; break; default: ret = -EINVAL; @@ -734,7 +784,8 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q, &sh_mobile_ceu_videobuf_ops, &ici->dev, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_ANY, + pcdev->is_interlaced ? + V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE, sizeof(struct sh_mobile_ceu_buffer), icd); } @@ -744,6 +795,7 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .add = sh_mobile_ceu_add_device, .remove = sh_mobile_ceu_remove_device, .get_formats = sh_mobile_ceu_get_formats, + .set_crop = sh_mobile_ceu_set_crop, .set_fmt = sh_mobile_ceu_set_fmt, .try_fmt = sh_mobile_ceu_try_fmt, .reqbufs = sh_mobile_ceu_reqbufs, diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index 8cb3457..38a7160 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -96,9 +96,7 @@ static const struct usb_device_id sn9c102_id_table[] = { #if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, -#endif { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, @@ -123,7 +121,9 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, { } }; diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index fcb05f0..6d8bfd4 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -30,6 +30,10 @@ #include <media/videobuf-core.h> #include <media/soc_camera.h> +/* Default to VGA resolution */ +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + static LIST_HEAD(hosts); static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); @@ -256,6 +260,46 @@ static void soc_camera_free_user_formats(struct soc_camera_device *icd) vfree(icd->user_formats); } +/* Called with .vb_lock held */ +static int soc_camera_set_fmt(struct soc_camera_file *icf, + struct v4l2_format *f) +{ + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + /* We always call try_fmt() before set_fmt() or set_crop() */ + ret = ici->ops->try_fmt(icd, f); + if (ret < 0) + return ret; + + ret = ici->ops->set_fmt(icd, f); + if (ret < 0) { + return ret; + } else if (!icd->current_fmt || + icd->current_fmt->fourcc != pix->pixelformat) { + dev_err(&ici->dev, + "Host driver hasn't set up current format correctly!\n"); + return -EINVAL; + } + + icd->width = pix->width; + icd->height = pix->height; + icf->vb_vidq.field = + icd->field = pix->field; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", + f->type); + + dev_dbg(&icd->dev, "set width: %d height: %d\n", + icd->width, icd->height); + + /* set physical bus parameters */ + return ici->ops->set_bus_param(icd, pix->pixelformat); +} + static int soc_camera_open(struct file *file) { struct video_device *vdev; @@ -297,14 +341,28 @@ static int soc_camera_open(struct file *file) /* Now we really have to activate the camera */ if (icd->use_count == 1) { - ret = soc_camera_init_user_formats(icd); - if (ret < 0) - goto eiufmt; + /* Restore parameters before the last close() per V4L2 API */ + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = icd->width, + .height = icd->height, + .field = icd->field, + .pixelformat = icd->current_fmt->fourcc, + .colorspace = icd->current_fmt->colorspace, + }, + }; + ret = ici->ops->add(icd); if (ret < 0) { dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); goto eiciadd; } + + /* Try to configure with default parameters */ + ret = soc_camera_set_fmt(icf, &f); + if (ret < 0) + goto esfmt; } mutex_unlock(&icd->video_lock); @@ -316,10 +374,13 @@ static int soc_camera_open(struct file *file) return 0; - /* First two errors are entered with the .video_lock held */ + /* + * First three errors are entered with the .video_lock held + * and use_count == 1 + */ +esfmt: + ici->ops->remove(icd); eiciadd: - soc_camera_free_user_formats(icd); -eiufmt: icd->use_count--; mutex_unlock(&icd->video_lock); module_put(ici->ops->owner); @@ -339,10 +400,9 @@ static int soc_camera_close(struct file *file) mutex_lock(&icd->video_lock); icd->use_count--; - if (!icd->use_count) { + if (!icd->use_count) ici->ops->remove(icd); - soc_camera_free_user_formats(icd); - } + mutex_unlock(&icd->video_lock); module_put(icd->ops->owner); @@ -415,18 +475,10 @@ static int soc_camera_s_fmt_vid_cap(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_pix_format *pix = &f->fmt.pix; - __u32 pixfmt = pix->pixelformat; int ret; - struct v4l2_rect rect; WARN_ON(priv != file->private_data); - ret = soc_camera_try_fmt_vid_cap(file, priv, f); - if (ret < 0) - return ret; - mutex_lock(&icf->vb_vidq.vb_lock); if (videobuf_queue_is_busy(&icf->vb_vidq)) { @@ -435,33 +487,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, goto unlock; } - rect.left = icd->x_current; - rect.top = icd->y_current; - rect.width = pix->width; - rect.height = pix->height; - ret = ici->ops->set_fmt(icd, pix->pixelformat, &rect); - if (ret < 0) { - goto unlock; - } else if (!icd->current_fmt || - icd->current_fmt->fourcc != pixfmt) { - dev_err(&ici->dev, - "Host driver hasn't set up current format correctly!\n"); - ret = -EINVAL; - goto unlock; - } - - icd->width = rect.width; - icd->height = rect.height; - icf->vb_vidq.field = pix->field; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", - f->type); - - dev_dbg(&icd->dev, "set width: %d height: %d\n", - icd->width, icd->height); - - /* set physical bus parameters */ - ret = ici->ops->set_bus_param(icd, pixfmt); + ret = soc_camera_set_fmt(icf, f); unlock: mutex_unlock(&icf->vb_vidq.vb_lock); @@ -648,8 +674,8 @@ static int soc_camera_cropcap(struct file *file, void *fh, a->bounds.height = icd->height_max; a->defrect.left = icd->x_min; a->defrect.top = icd->y_min; - a->defrect.width = 640; - a->defrect.height = 480; + a->defrect.width = DEFAULT_WIDTH; + a->defrect.height = DEFAULT_HEIGHT; a->pixelaspect.numerator = 1; a->pixelaspect.denominator = 1; @@ -685,7 +711,7 @@ static int soc_camera_s_crop(struct file *file, void *fh, /* Cropping is allowed during a running capture, guard consistency */ mutex_lock(&icf->vb_vidq.vb_lock); - ret = ici->ops->set_fmt(icd, 0, &a->c); + ret = ici->ops->set_crop(icd, &a->c); if (!ret) { icd->width = a->c.width; icd->height = a->c.height; @@ -844,9 +870,18 @@ static int soc_camera_probe(struct device *dev) qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); icd->exposure = qctrl ? qctrl->default_value : (unsigned short)~0; + + ret = soc_camera_init_user_formats(icd); + if (ret < 0) + goto eiufmt; + + icd->height = DEFAULT_HEIGHT; + icd->width = DEFAULT_WIDTH; + icd->field = V4L2_FIELD_ANY; } - ici->ops->remove(icd); +eiufmt: + ici->ops->remove(icd); eiadd: mutex_unlock(&icd->video_lock); module_put(ici->ops->owner); @@ -865,6 +900,8 @@ static int soc_camera_remove(struct device *dev) if (icd->ops->remove) icd->ops->remove(icd); + soc_camera_free_user_formats(icd); + return 0; } @@ -918,6 +955,7 @@ int soc_camera_host_register(struct soc_camera_host *ici) 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 || @@ -998,6 +1036,7 @@ int soc_camera_device_register(struct soc_camera_device *icd) !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 || diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index 013ab06..c486763 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -79,8 +79,14 @@ soc_camera_platform_query_bus_param(struct soc_camera_device *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, - __u32 pixfmt, struct v4l2_rect *rect) + struct v4l2_format *f) { return 0; } @@ -125,6 +131,7 @@ static struct soc_camera_ops soc_camera_platform_ops = { .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, diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index 26378cf..1a6d39c 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -933,8 +933,6 @@ static int stk_vidioc_s_ctrl(struct file *filp, static int stk_vidioc_enum_fmt_vid_cap(struct file *filp, void *priv, struct v4l2_fmtdesc *fmtd) { - fmtd->flags = 0; - switch (fmtd->index) { case 0: fmtd->pixelformat = V4L2_PIX_FMT_RGB565; @@ -992,7 +990,6 @@ static int stk_vidioc_g_fmt_vid_cap(struct file *filp, pix_format->height = stk_sizes[i].h; pix_format->field = V4L2_FIELD_NONE; pix_format->colorspace = V4L2_COLORSPACE_SRGB; - pix_format->priv = 0; pix_format->pixelformat = dev->vsettings.palette; if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) pix_format->bytesperline = pix_format->width; @@ -1115,8 +1112,6 @@ static int stk_vidioc_reqbufs(struct file *filp, if (dev == NULL) return -ENODEV; - if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (rb->memory != V4L2_MEMORY_MMAP) return -EINVAL; if (is_streaming(dev) @@ -1139,16 +1134,10 @@ static int stk_vidioc_reqbufs(struct file *filp, static int stk_vidioc_querybuf(struct file *filp, void *priv, struct v4l2_buffer *buf) { - int index; struct stk_camera *dev = priv; struct stk_sio_buffer *sbuf; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - index = buf->index; - - if (index < 0 || index >= dev->n_sbufs) + if (buf->index < 0 || buf->index >= dev->n_sbufs) return -EINVAL; sbuf = dev->sio_bufs + buf->index; *buf = sbuf->v4lbuf; @@ -1161,8 +1150,6 @@ static int stk_vidioc_qbuf(struct file *filp, struct stk_camera *dev = priv; struct stk_sio_buffer *sbuf; unsigned long flags; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (buf->memory != V4L2_MEMORY_MMAP) return -EINVAL; @@ -1189,8 +1176,7 @@ static int stk_vidioc_dqbuf(struct file *filp, unsigned long flags; int ret; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE - || !is_streaming(dev)) + if (!is_streaming(dev)) return -EINVAL; if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) @@ -1249,16 +1235,10 @@ static int stk_vidioc_streamoff(struct file *filp, static int stk_vidioc_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *sp) { - if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp->parm.capture.capability = 0; - sp->parm.capture.capturemode = 0; /*FIXME This is not correct */ sp->parm.capture.timeperframe.numerator = 1; sp->parm.capture.timeperframe.denominator = 30; sp->parm.capture.readbuffers = 2; - sp->parm.capture.extendedmode = 0; return 0; } diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c index 29991d1..b30c492 100644 --- a/drivers/media/video/tcm825x.c +++ b/drivers/media/video/tcm825x.c @@ -50,7 +50,7 @@ struct tcm825x_sensor { }; /* list of image formats supported by TCM825X sensor */ -const static struct v4l2_fmtdesc tcm825x_formats[] = { +static const struct v4l2_fmtdesc tcm825x_formats[] = { { .description = "YUYV (YUV 4:2:2), packed", .pixelformat = V4L2_PIX_FMT_UYVY, @@ -76,15 +76,15 @@ const static struct v4l2_fmtdesc tcm825x_formats[] = { * TCM825X register configuration for all combinations of pixel format and * image size */ -const static struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ }; -const static struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ }; -const static struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ }; -const static struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ }; -const static struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ }; -const static struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ }; +static const struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ }; +static const struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ }; +static const struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ }; +static const struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ }; +static const struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ }; +static const struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ }; -const static struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT }; -const static struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT }; +static const struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT }; +static const struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT }; /* Our own specific controls */ #define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE @@ -248,10 +248,10 @@ static struct vcontrol { }; -const static struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] = +static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] = { &subqcif, &qqvga, &qcif, &qvga, &cif, &vga }; -const static struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] = +static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] = { &yuv422, &rgb565 }; /* diff --git a/drivers/media/video/tcm825x.h b/drivers/media/video/tcm825x.h index 770ebac..5b7e696 100644 --- a/drivers/media/video/tcm825x.h +++ b/drivers/media/video/tcm825x.h @@ -188,7 +188,7 @@ struct tcm825x_platform_data { /* Array of image sizes supported by TCM825X. These must be ordered from * smallest image size to largest. */ -const static struct capture_size tcm825x_sizes[] = { +static const struct capture_size tcm825x_sizes[] = { { 128, 96 }, /* subQCIF */ { 160, 120 }, /* QQVGA */ { 176, 144 }, /* QCIF */ diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index 0c02058..005f8a4 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -50,7 +50,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/i2c-addr.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #ifndef VIDEO_AUDIO_BALANCE # define VIDEO_AUDIO_BALANCE 32 @@ -69,13 +69,6 @@ MODULE_PARM_DESC(maxvol,"Set maximium volume to +20db (0), default is 0db(1)"); module_param(maxvol, int, S_IRUGO | S_IWUSR); -/* Address to scan (I2C address of this chip) */ -static unsigned short normal_i2c[] = { - I2C_ADDR_TDA7432 >> 1, - I2C_CLIENT_END, -}; - -I2C_CLIENT_INSMOD; /* Structure of address and subaddresses for the tda7432 */ @@ -421,21 +414,18 @@ static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); } return -EINVAL; } -static int tda7432_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tda7432_core_ops = { @@ -498,8 +488,6 @@ MODULE_DEVICE_TABLE(i2c, tda7432_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tda7432", - .driverid = I2C_DRIVERID_TDA7432, - .command = tda7432_command, .probe = tda7432_probe, .remove = tda7432_remove, .id_table = tda7432_id, diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 6afb705..fe11580 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -30,8 +30,8 @@ #include <linux/ioctl.h> #include <linux/i2c.h> #include <media/v4l2-device.h> -#include <media/v4l2-i2c-drv-legacy.h> -#include "tda9840.h" +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); MODULE_DESCRIPTION("tda9840 driver"); @@ -56,11 +56,6 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define TDA9840_SET_BOTH_R 0x16 #define TDA9840_SET_EXTERNAL 0x7a -/* addresses to scan, found only at 0x42 (7-Bit) */ -static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END }; - -/* magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) { @@ -137,60 +132,17 @@ static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) return 0; } -static long tda9840_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) +static int tda9840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { - int byte; - - switch (cmd) { - case TDA9840_LEVEL_ADJUST: - byte = *(int *)arg; - v4l2_dbg(1, debug, sd, "TDA9840_LEVEL_ADJUST: %d\n", byte); - - /* check for correct range */ - if (byte > 25 || byte < -20) - return -EINVAL; - - /* calculate actual value to set, see specs, page 18 */ - byte /= 5; - if (0 < byte) - byte += 0x8; - else - byte = -byte; - tda9840_write(sd, LEVEL_ADJUST, byte); - break; - - case TDA9840_STEREO_ADJUST: - byte = *(int *)arg; - v4l2_dbg(1, debug, sd, "TDA9840_STEREO_ADJUST: %d\n", byte); - - /* check for correct range */ - if (byte > 25 || byte < -24) - return -EINVAL; - - /* calculate actual value to set */ - byte /= 5; - if (0 < byte) - byte += 0x20; - else - byte = -byte; - - tda9840_write(sd, STEREO_ADJUST, byte); - break; - default: - return -ENOIOCTLCMD; - } - return 0; -} + struct i2c_client *client = v4l2_get_subdevdata(sd); -static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TDA9840, 0); } /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tda9840_core_ops = { - .ioctl = tda9840_ioctl, + .g_chip_ident = tda9840_g_chip_ident, }; static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = { @@ -209,8 +161,6 @@ static int tda9840_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct v4l2_subdev *sd; - int result; - int byte; /* let's see whether this adapter can support what we need */ if (!i2c_check_functionality(client->adapter, @@ -227,15 +177,9 @@ static int tda9840_probe(struct i2c_client *client, v4l2_i2c_subdev_init(sd, client, &tda9840_ops); /* set initial values for level & stereo - adjustment, mode */ - byte = 0; - result = tda9840_ioctl(sd, TDA9840_LEVEL_ADJUST, &byte); - result |= tda9840_ioctl(sd, TDA9840_STEREO_ADJUST, &byte); + tda9840_write(sd, LEVEL_ADJUST, 0); + tda9840_write(sd, STEREO_ADJUST, 0); tda9840_write(sd, SWITCH, TDA9840_SET_STEREO); - if (result) { - v4l2_dbg(1, debug, sd, "could not initialize tda9840\n"); - kfree(sd); - return -ENODEV; - } return 0; } @@ -248,12 +192,7 @@ static int tda9840_remove(struct i2c_client *client) return 0; } -static int tda9840_legacy_probe(struct i2c_adapter *adapter) -{ - /* Let's see whether this is a known adapter we can attach to. - Prevents conflicts with tvaudio.c. */ - return adapter->id == I2C_HW_SAA7146; -} + static const struct i2c_device_id tda9840_id[] = { { "tda9840", 0 }, { } @@ -262,10 +201,7 @@ MODULE_DEVICE_TABLE(i2c, tda9840_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tda9840", - .driverid = I2C_DRIVERID_TDA9840, - .command = tda9840_command, .probe = tda9840_probe, .remove = tda9840_remove, - .legacy_probe = tda9840_legacy_probe, .id_table = tda9840_id, }; diff --git a/drivers/media/video/tda9840.h b/drivers/media/video/tda9840.h deleted file mode 100644 index dc12ae7..0000000 --- a/drivers/media/video/tda9840.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __INCLUDED_TDA9840__ -#define __INCLUDED_TDA9840__ - -#define I2C_ADDR_TDA9840 0x42 - -/* values may range between +2.5 and -2.0; - the value has to be multiplied with 10 */ -#define TDA9840_LEVEL_ADJUST _IOW('v',3,int) - -/* values may range between +2.5 and -2.4; - the value has to be multiplied with 10 */ -#define TDA9840_STEREO_ADJUST _IOW('v',4,int) - -#endif diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index 00c6cbe..24e2b7d 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -28,20 +28,13 @@ #include <linux/i2c.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include <media/i2c-addr.h> static int debug; /* insmod parameter */ module_param(debug, int, S_IRUGO | S_IWUSR); MODULE_LICENSE("GPL"); -/* Addresses to scan */ -static unsigned short normal_i2c[] = { - I2C_ADDR_TDA9875 >> 1, - I2C_CLIENT_END -}; - -I2C_CLIENT_INSMOD; /* This is a superset of the TDA9875 */ struct tda9875 { @@ -313,18 +306,14 @@ static int tda9875_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { switch (qc->id) { case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); } return -EINVAL; } -static int tda9875_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tda9875_core_ops = { @@ -401,8 +390,6 @@ MODULE_DEVICE_TABLE(i2c, tda9875_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tda9875", - .driverid = I2C_DRIVERID_TDA9875, - .command = tda9875_command, .probe = tda9875_probe, .remove = tda9875_remove, .id_table = tda9875_id, diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c index 7519fd1..d61c56f 100644 --- a/drivers/media/video/tea6415c.c +++ b/drivers/media/video/tea6415c.c @@ -32,7 +32,8 @@ #include <linux/ioctl.h> #include <linux/i2c.h> #include <media/v4l2-device.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> #include "tea6415c.h" MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); @@ -44,25 +45,22 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -/* addresses to scan, found only at 0x03 and/or 0x43 (7-bit) */ -static unsigned short normal_i2c[] = { I2C_TEA6415C_1, I2C_TEA6415C_2, I2C_CLIENT_END }; -/* magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; - -/* makes a connection between the input-pin 'i' and the output-pin 'o' - for the tea6415c-client 'client' */ -static int switch_matrix(struct i2c_client *client, int i, int o) +/* makes a connection between the input-pin 'i' and the output-pin 'o' */ +static int tea6415c_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 byte = 0; + u32 i = route->input; + u32 o = route->output; int ret; - v4l_dbg(1, debug, client, "i=%d, o=%d\n", i, o); + v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o); /* check if the pins are valid */ if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i) && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o))) - return -1; + return -EINVAL; /* to understand this, have a look at the tea6415c-specs (p.5) */ switch (o) { @@ -115,37 +113,33 @@ static int switch_matrix(struct i2c_client *client, int i, int o) ret = i2c_smbus_write_byte(client, byte); if (ret) { - v4l_dbg(1, debug, client, + v4l2_dbg(1, debug, sd, "i2c_smbus_write_byte() failed, ret:%d\n", ret); return -EIO; } return ret; } -static long tea6415c_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) +static int tea6415c_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { - if (cmd == TEA6415C_SWITCH) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg; + struct i2c_client *client = v4l2_get_subdevdata(sd); - return switch_matrix(client, v->in, v->out); - } - return -ENOIOCTLCMD; -} - -static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6415C, 0); } /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tea6415c_core_ops = { - .ioctl = tea6415c_ioctl, + .g_chip_ident = tea6415c_g_chip_ident, +}; + +static const struct v4l2_subdev_video_ops tea6415c_video_ops = { + .s_routing = tea6415c_s_routing, }; static const struct v4l2_subdev_ops tea6415c_ops = { .core = &tea6415c_core_ops, + .video = &tea6415c_video_ops, }; /* this function is called by i2c_probe */ @@ -176,12 +170,6 @@ static int tea6415c_remove(struct i2c_client *client) return 0; } -static int tea6415c_legacy_probe(struct i2c_adapter *adapter) -{ - /* Let's see whether this is a known adapter we can attach to. - Prevents conflicts with tvaudio.c. */ - return adapter->id == I2C_HW_SAA7146; -} static const struct i2c_device_id tea6415c_id[] = { { "tea6415c", 0 }, @@ -191,10 +179,7 @@ MODULE_DEVICE_TABLE(i2c, tea6415c_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tea6415c", - .driverid = I2C_DRIVERID_TEA6415C, - .command = tea6415c_command, .probe = tea6415c_probe, .remove = tea6415c_remove, - .legacy_probe = tea6415c_legacy_probe, .id_table = tea6415c_id, }; diff --git a/drivers/media/video/tea6415c.h b/drivers/media/video/tea6415c.h index f84ed80..3a47d697 100644 --- a/drivers/media/video/tea6415c.h +++ b/drivers/media/video/tea6415c.h @@ -1,10 +1,6 @@ #ifndef __INCLUDED_TEA6415C__ #define __INCLUDED_TEA6415C__ -/* possible i2c-addresses */ -#define I2C_TEA6415C_1 0x03 -#define I2C_TEA6415C_2 0x43 - /* the tea6415c's design is quite brain-dead. although there are 8 inputs and 6 outputs, these aren't enumerated in any way. because I don't want to say "connect input pin 20 to output pin 17", I define @@ -28,12 +24,4 @@ #define TEA6415C_INPUT7 1 #define TEA6415C_INPUT8 11 -struct tea6415c_multiplex -{ - int in; /* input-pin */ - int out; /* output-pin */ -}; - -#define TEA6415C_SWITCH _IOW('v',1,struct tea6415c_multiplex) - #endif diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c index 081e74f..3492223 100644 --- a/drivers/media/video/tea6420.c +++ b/drivers/media/video/tea6420.c @@ -32,7 +32,8 @@ #include <linux/ioctl.h> #include <linux/i2c.h> #include <media/v4l2-device.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> #include "tea6420.h" MODULE_AUTHOR("Michael Hunold <michael@mihu.de>"); @@ -44,24 +45,23 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -/* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */ -static unsigned short normal_i2c[] = { I2C_ADDR_TEA6420_1, I2C_ADDR_TEA6420_2, I2C_CLIENT_END }; - -/* magic definition of all other variables and things */ -I2C_CLIENT_INSMOD; /* make a connection between the input 'i' and the output 'o' - with gain 'g' for the tea6420-client 'client' (note: i = 6 means 'mute') */ -static int tea6420_switch(struct i2c_client *client, int i, int o, int g) + with gain 'g' (note: i = 6 means 'mute') */ +static int tea6420_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + int i = route->input; + int o = route->output & 0xf; + int g = (route->output >> 4) & 0xf; u8 byte; int ret; - v4l_dbg(1, debug, client, "i=%d, o=%d, g=%d\n", i, o, g); + v4l2_dbg(1, debug, sd, "i=%d, o=%d, g=%d\n", i, o, g); /* check if the parameters are valid */ if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0) - return -1; + return -EINVAL; byte = ((o - 1) << 5); byte |= (i - 1); @@ -83,37 +83,33 @@ static int tea6420_switch(struct i2c_client *client, int i, int o, int g) ret = i2c_smbus_write_byte(client, byte); if (ret) { - v4l_dbg(1, debug, client, + v4l2_dbg(1, debug, sd, "i2c_smbus_write_byte() failed, ret:%d\n", ret); return -EIO; } return 0; } -static long tea6420_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg) +static int tea6420_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { - if (cmd == TEA6420_SWITCH) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg; + struct i2c_client *client = v4l2_get_subdevdata(sd); - return tea6420_switch(client, a->in, a->out, a->gain); - } - return -ENOIOCTLCMD; -} - -static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6420, 0); } /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tea6420_core_ops = { - .ioctl = tea6420_ioctl, + .g_chip_ident = tea6420_g_chip_ident, +}; + +static const struct v4l2_subdev_audio_ops tea6420_audio_ops = { + .s_routing = tea6420_s_routing, }; static const struct v4l2_subdev_ops tea6420_ops = { .core = &tea6420_core_ops, + .audio = &tea6420_audio_ops, }; /* this function is called by i2c_probe */ @@ -130,20 +126,24 @@ static int tea6420_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); + sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &tea6420_ops); + /* set initial values: set "mute"-input to all outputs at gain 0 */ err = 0; for (i = 1; i < 5; i++) { - err += tea6420_switch(client, 6, i, 0); + struct v4l2_routing route; + + route.input = 6; + route.output = i; + err += tea6420_s_routing(sd, &route); } if (err) { v4l_dbg(1, debug, client, "could not initialize tea6420\n"); return -ENODEV; } - - sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); - if (sd == NULL) - return -ENOMEM; - v4l2_i2c_subdev_init(sd, client, &tea6420_ops); return 0; } @@ -156,12 +156,6 @@ static int tea6420_remove(struct i2c_client *client) return 0; } -static int tea6420_legacy_probe(struct i2c_adapter *adapter) -{ - /* Let's see whether this is a known adapter we can attach to. - Prevents conflicts with tvaudio.c. */ - return adapter->id == I2C_HW_SAA7146; -} static const struct i2c_device_id tea6420_id[] = { { "tea6420", 0 }, @@ -171,10 +165,7 @@ MODULE_DEVICE_TABLE(i2c, tea6420_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tea6420", - .driverid = I2C_DRIVERID_TEA6420, - .command = tea6420_command, .probe = tea6420_probe, .remove = tea6420_remove, - .legacy_probe = tea6420_legacy_probe, .id_table = tea6420_id, }; diff --git a/drivers/media/video/tea6420.h b/drivers/media/video/tea6420.h index 5ef7c18..4aa3edb 100644 --- a/drivers/media/video/tea6420.h +++ b/drivers/media/video/tea6420.h @@ -1,17 +1,24 @@ #ifndef __INCLUDED_TEA6420__ #define __INCLUDED_TEA6420__ -/* possible addresses */ -#define I2C_ADDR_TEA6420_1 0x4c -#define I2C_ADDR_TEA6420_2 0x4d +/* input pins */ +#define TEA6420_OUTPUT1 1 +#define TEA6420_OUTPUT2 2 +#define TEA6420_OUTPUT3 3 +#define TEA6420_OUTPUT4 4 -struct tea6420_multiplex -{ - int in; /* input of audio switch */ - int out; /* output of audio switch */ - int gain; /* gain of connection */ -}; +/* output pins */ +#define TEA6420_INPUT1 1 +#define TEA6420_INPUT2 2 +#define TEA6420_INPUT3 3 +#define TEA6420_INPUT4 4 +#define TEA6420_INPUT5 5 +#define TEA6420_INPUT6 6 -#define TEA6420_SWITCH _IOW('v',1,struct tea6420_multiplex) +/* gain on the output pins, ORed with the output pin */ +#define TEA6420_GAIN0 0x00 +#define TEA6420_GAIN2 0x20 +#define TEA6420_GAIN4 0x40 +#define TEA6420_GAIN6 0x60 #endif diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c index 5c95ecd..07789c6 100644 --- a/drivers/media/video/tlv320aic23b.c +++ b/drivers/media/video/tlv320aic23b.c @@ -31,15 +31,12 @@ #include <linux/i2c-id.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("tlv320aic23b driver"); MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil"); MODULE_LICENSE("GPL"); -static unsigned short normal_i2c[] = { 0x34 >> 1, I2C_CLIENT_END }; - -I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ @@ -121,11 +118,6 @@ static int tlv320aic23b_log_status(struct v4l2_subdev *sd) return 0; } -static int tlv320aic23b_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { @@ -208,8 +200,6 @@ MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tlv320aic23b", - .driverid = I2C_DRIVERID_TLV320AIC23B, - .command = tlv320aic23b_command, .probe = tlv320aic23b_probe, .remove = tlv320aic23b_remove, .id_table = tlv320aic23b_id, diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 30640fb..72d4103 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -364,7 +364,8 @@ static void set_type(struct i2c_client *c, unsigned int type, } t->type = type; - t->config = new_config; + /* prevent invalid config values */ + t->config = ((new_config >= 0) && (new_config < 256)) ? new_config : 0; if (tuner_callback != NULL) { tuner_dbg("defining GPIO callback\n"); t->fe.callback = tuner_callback; @@ -452,7 +453,8 @@ static void set_type(struct i2c_client *c, unsigned int type, struct dvb_tuner_ops *xc_tuner_ops; xc5000_cfg.i2c_address = t->i2c->addr; - xc5000_cfg.if_khz = 5380; + /* if_khz will be set when the digital dvb_attach() occurs */ + xc5000_cfg.if_khz = 0; if (!dvb_attach(xc5000_attach, &t->fe, t->i2c->adapter, &xc5000_cfg)) goto attach_failed; @@ -776,8 +778,7 @@ static int tuner_s_radio(struct v4l2_subdev *sd) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO") - == -EINVAL) + if (set_mode(client, t, V4L2_TUNER_RADIO, "s_radio") == -EINVAL) return 0; if (t->radio_freq) set_freq(client, t->radio_freq); @@ -791,7 +792,7 @@ static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby) tuner_dbg("Putting tuner to sleep\n"); - if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL) + if (check_mode(t, "s_standby") == -EINVAL) return 0; t->mode = T_STANDBY; if (analog_ops->standby) @@ -799,132 +800,6 @@ static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby) return 0; } -#ifdef CONFIG_VIDEO_ALLOW_V4L1 -static long tuner_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct tuner *t = to_tuner(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - - switch (cmd) { - case VIDIOCSAUDIO: - if (check_mode(t, "VIDIOCSAUDIO") == -EINVAL) - return 0; - if (check_v4l2(t) == -EINVAL) - return 0; - - /* Should be implemented, since bttv calls it */ - tuner_dbg("VIDIOCSAUDIO not implemented.\n"); - break; - case VIDIOCSCHAN: - { - static const v4l2_std_id map[] = { - [VIDEO_MODE_PAL] = V4L2_STD_PAL, - [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M, - [VIDEO_MODE_SECAM] = V4L2_STD_SECAM, - [4 /* bttv */ ] = V4L2_STD_PAL_M, - [5 /* bttv */ ] = V4L2_STD_PAL_N, - [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP, - }; - struct video_channel *vc = arg; - - if (check_v4l2(t) == -EINVAL) - return 0; - - if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==-EINVAL) - return 0; - - if (vc->norm < ARRAY_SIZE(map)) - t->std = map[vc->norm]; - tuner_fixup_std(t); - if (t->tv_freq) - set_tv_freq(client, t->tv_freq); - return 0; - } - case VIDIOCSFREQ: - { - unsigned long *v = arg; - - if (check_mode(t, "VIDIOCSFREQ") == -EINVAL) - return 0; - if (check_v4l2(t) == -EINVAL) - return 0; - - set_freq(client, *v); - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner *vt = arg; - - if (check_mode(t, "VIDIOCGTUNER") == -EINVAL) - return 0; - if (check_v4l2(t) == -EINVAL) - return 0; - - if (V4L2_TUNER_RADIO == t->mode) { - if (fe_tuner_ops->get_status) { - u32 tuner_status; - - fe_tuner_ops->get_status(&t->fe, &tuner_status); - if (tuner_status & TUNER_STATUS_STEREO) - vt->flags |= VIDEO_TUNER_STEREO_ON; - else - vt->flags &= ~VIDEO_TUNER_STEREO_ON; - } else { - if (analog_ops->is_stereo) { - if (analog_ops->is_stereo(&t->fe)) - vt->flags |= - VIDEO_TUNER_STEREO_ON; - else - vt->flags &= - ~VIDEO_TUNER_STEREO_ON; - } - } - if (analog_ops->has_signal) - vt->signal = - analog_ops->has_signal(&t->fe); - - vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */ - - vt->rangelow = radio_range[0] * 16000; - vt->rangehigh = radio_range[1] * 16000; - - } else { - vt->rangelow = tv_range[0] * 16; - vt->rangehigh = tv_range[1] * 16; - } - - return 0; - } - case VIDIOCGAUDIO: - { - struct video_audio *va = arg; - - if (check_mode(t, "VIDIOCGAUDIO") == -EINVAL) - return 0; - if (check_v4l2(t) == -EINVAL) - return 0; - - if (V4L2_TUNER_RADIO == t->mode) { - if (fe_tuner_ops->get_status) { - u32 tuner_status; - - fe_tuner_ops->get_status(&t->fe, &tuner_status); - va->mode = (tuner_status & TUNER_STATUS_STEREO) - ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; - } else if (analog_ops->is_stereo) - va->mode = analog_ops->is_stereo(&t->fe) - ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; - } - return 0; - } - } - return -ENOIOCTLCMD; -} -#endif - static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg) { struct tuner *t = to_tuner(sd); @@ -950,8 +825,7 @@ static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD") - == -EINVAL) + if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "s_std") == -EINVAL) return 0; switch_v4l2(); @@ -968,8 +842,7 @@ static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (set_mode(client, t, f->type, "VIDIOC_S_FREQUENCY") - == -EINVAL) + if (set_mode(client, t, f->type, "s_frequency") == -EINVAL) return 0; switch_v4l2(); set_freq(client, f->frequency); @@ -982,7 +855,7 @@ static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) struct tuner *t = to_tuner(sd); struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL) + if (check_mode(t, "g_frequency") == -EINVAL) return 0; switch_v4l2(); f->type = t->mode; @@ -1006,7 +879,7 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL) + if (check_mode(t, "g_tuner") == -EINVAL) return 0; switch_v4l2(); @@ -1055,7 +928,7 @@ static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL) + if (check_mode(t, "s_tuner") == -EINVAL) return 0; switch_v4l2(); @@ -1112,9 +985,6 @@ static int tuner_resume(struct i2c_client *c) static const struct v4l2_subdev_core_ops tuner_core_ops = { .log_status = tuner_log_status, .s_standby = tuner_s_standby, -#ifdef CONFIG_VIDEO_ALLOW_V4L1 - .ioctl = tuner_ioctl, -#endif }; static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 5aeccb3..226bf35 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -26,7 +26,7 @@ #include <linux/delay.h> #include <linux/errno.h> #include <linux/slab.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/kthread.h> @@ -54,7 +54,7 @@ MODULE_LICENSE("GPL"); /* ---------------------------------------------------------------------- */ /* our structs */ -#define MAXREGS 64 +#define MAXREGS 256 struct CHIPSTATE; typedef int (*getvalue)(int); @@ -1047,6 +1047,116 @@ static int tda9874a_initialize(struct CHIPSTATE *chip) return 0; } +/* ---------------------------------------------------------------------- */ +/* audio chip description - defines+functions for tda9875 */ +/* The TDA9875 is made by Philips Semiconductor + * http://www.semiconductors.philips.com + * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator + * + */ + +/* subaddresses for TDA9875 */ +#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/ +#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */ +#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/ +#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/ + +#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/ +#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/ +#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/ +#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/ + +#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/ +#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/ +#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/ +#define TDA9875_MVL 0x1a /* Main volume gauche */ +#define TDA9875_MVR 0x1b /* Main volume droite */ +#define TDA9875_MBA 0x1d /* Main Basse */ +#define TDA9875_MTR 0x1e /* Main treble */ +#define TDA9875_ACS 0x1f /* Auxilary channel select (FM) 0b0000000*/ +#define TDA9875_AVL 0x20 /* Auxilary volume gauche */ +#define TDA9875_AVR 0x21 /* Auxilary volume droite */ +#define TDA9875_ABA 0x22 /* Auxilary Basse */ +#define TDA9875_ATR 0x23 /* Auxilary treble */ + +#define TDA9875_MSR 0x02 /* Monitor select register */ +#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */ +#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */ +#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */ +#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */ +#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */ +#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */ +#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/ +#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/ +#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/ + +/* values */ +#define TDA9875_MUTE_ON 0xff /* general mute */ +#define TDA9875_MUTE_OFF 0xcc /* general no mute */ + +static int tda9875_initialize(struct CHIPSTATE *chip) +{ + chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/ + chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/ + chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/ + chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/ + chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/ + chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/ + chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/ + chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/ + chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/ + chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/ + chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/ + chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/ + chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/ + + chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/ + chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */ + chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/ + chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/ + chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/ + chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */ + chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */ + chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */ + chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/ + chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/ + chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/ + chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/ + chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/ + chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/ + chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/ + chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/ + + chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */ + return 0; +} + +static int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); } +static int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); } +static int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); } + +/* ----------------------------------------------------------------------- */ + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda9875_checkit(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int dic, rev; + + dic = chip_read2(chip, 254); + rev = chip_read2(chip, 255); + + if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ + v4l2_info(sd, "found tda9875%s rev. %d.\n", + dic == 0 ? "" : "A", rev); + return 1; + } + return 0; +} /* ---------------------------------------------------------------------- */ /* audio chip descriptions - defines+functions for tea6420 */ @@ -1280,6 +1390,7 @@ static int tda9850 = 1; static int tda9855 = 1; static int tda9873 = 1; static int tda9874a = 1; +static int tda9875 = 1; static int tea6300; /* default 0 - address clash with msp34xx */ static int tea6320; /* default 0 - address clash with msp34xx */ static int tea6420 = 1; @@ -1292,6 +1403,7 @@ module_param(tda9850, int, 0444); module_param(tda9855, int, 0444); module_param(tda9873, int, 0444); module_param(tda9874a, int, 0444); +module_param(tda9875, int, 0444); module_param(tea6300, int, 0444); module_param(tea6320, int, 0444); module_param(tea6420, int, 0444); @@ -1349,6 +1461,26 @@ static struct CHIPDESC chiplist[] = { .setmode = tda9874a_setmode, }, { + .name = "tda9875", + .insmodopt = &tda9875, + .addr_lo = I2C_ADDR_TDA9875 >> 1, + .addr_hi = I2C_ADDR_TDA9875 >> 1, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, + + /* callbacks */ + .initialize = tda9875_initialize, + .checkit = tda9875_checkit, + .volfunc = tda9875_volume, + .bassfunc = tda9875_bass, + .treblefunc = tda9875_treble, + .leftreg = TDA9875_MVL, + .rightreg = TDA9875_MVR, + .bassreg = TDA9875_MBA, + .treblereg = TDA9875_MTR, + .leftinit = 58880, + .rightinit = 58880, + }, + { .name = "tda9850", .insmodopt = &tda9850, .addr_lo = I2C_ADDR_TDA985x_L >> 1, @@ -1511,6 +1643,8 @@ static int tvaudio_g_ctrl(struct v4l2_subdev *sd, switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + break; ctrl->value=chip->muted; return 0; case V4L2_CID_AUDIO_VOLUME: @@ -1552,6 +1686,9 @@ static int tvaudio_s_ctrl(struct v4l2_subdev *sd, switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + break; + if (ctrl->value < 0 || ctrl->value >= 2) return -ERANGE; chip->muted = ctrl->value; @@ -1636,21 +1773,26 @@ static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) switch (qc->id) { case V4L2_CID_AUDIO_MUTE: + if (desc->flags & CHIP_HAS_INPUTSEL) + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); break; case V4L2_CID_AUDIO_VOLUME: + if (desc->flags & CHIP_HAS_VOLUME) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); + break; case V4L2_CID_AUDIO_BALANCE: - if (!(desc->flags & CHIP_HAS_VOLUME)) - return -EINVAL; + if (desc->flags & CHIP_HAS_VOLUME) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); break; case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - if (!(desc->flags & CHIP_HAS_BASSTREBLE)) - return -EINVAL; + if (desc->flags & CHIP_HAS_BASSTREBLE) + return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768); break; default: - return -EINVAL; + break; } - return v4l2_ctrl_query_fill_std(qc); + return -EINVAL; } static int tvaudio_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt) @@ -1658,7 +1800,9 @@ static int tvaudio_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing * struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; - if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4) + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + return 0; + if (rt->input >= 4) return -EINVAL; /* There are four inputs: tuner, radio, extern and intern. */ chip->input = rt->input; @@ -1675,8 +1819,11 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct CHIPDESC *desc = chip->desc; int mode = 0; + if (!desc->setmode) + return 0; if (chip->radio) return 0; + switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: case V4L2_TUNER_MODE_STEREO: @@ -1692,7 +1839,7 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) } chip->audmode = vt->audmode; - if (desc->setmode && mode) { + if (mode) { chip->watch_stereo = 0; /* del_timer(&chip->wt); */ chip->mode = mode; @@ -1707,15 +1854,17 @@ static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct CHIPDESC *desc = chip->desc; int mode = V4L2_TUNER_MODE_MONO; + if (!desc->getmode) + return 0; if (chip->radio) return 0; + vt->audmode = chip->audmode; vt->rxsubchans = 0; vt->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - if (desc->getmode) - mode = desc->getmode(chip); + mode = desc->getmode(chip); if (mode & V4L2_TUNER_MODE_MONO) vt->rxsubchans |= V4L2_TUNER_SUB_MONO; @@ -1901,6 +2050,7 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * } chip->thread = NULL; + init_timer(&chip->wt); if (desc->flags & CHIP_NEED_CHECKMODE) { if (!desc->getmode || !desc->setmode) { /* This shouldn't be happen. Warn user, but keep working @@ -1910,7 +2060,6 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * return 0; } /* start async thread */ - init_timer(&chip->wt); chip->wt.function = chip_thread_wake; chip->wt.data = (unsigned long)chip; chip->thread = kthread_run(chip_thread, chip, client->name); diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 78277ab..e24a38c 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -261,7 +261,12 @@ hauppauge_tuner[] = { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, /* 150-159 */ - { TUNER_ABSENT, "Xceive XC5000"}, + { TUNER_XC5000, "Xceive XC5000"}, + { TUNER_ABSENT, "Xceive XC3028L"}, + { TUNER_ABSENT, "NXP 18271C2_716x"}, + { TUNER_ABSENT, "Xceive XC4000"}, + { TUNER_ABSENT, "Dibcom 7070"}, + { TUNER_PHILIPS_TDA8290, "NXP 18271C2"}, }; /* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index 8e23aa5..4262e60 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -86,9 +86,12 @@ struct tvp514x_std_info { struct v4l2_standard standard; }; +static struct tvp514x_reg tvp514x_reg_list_default[0x40]; /** - * struct tvp514x_decoded - TVP5146/47 decoder object + * struct tvp514x_decoder - TVP5146/47 decoder object * @v4l2_int_device: Slave handle + * @tvp514x_slave: Slave pointer which is used by @v4l2_int_device + * @tvp514x_regs: copy of hw's regs with preset values. * @pdata: Board specific * @client: I2C client data * @id: Entry from I2C table @@ -103,7 +106,9 @@ struct tvp514x_std_info { * @route: input and output routing at chip level */ struct tvp514x_decoder { - struct v4l2_int_device *v4l2_int_device; + struct v4l2_int_device v4l2_int_device; + struct v4l2_int_slave tvp514x_slave; + struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)]; const struct tvp514x_platform_data *pdata; struct i2c_client *client; @@ -124,7 +129,7 @@ struct tvp514x_decoder { }; /* TVP514x default register values */ -static struct tvp514x_reg tvp514x_reg_list[] = { +static struct tvp514x_reg tvp514x_reg_list_default[] = { {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */ {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F}, {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */ @@ -422,7 +427,7 @@ static int tvp514x_configure(struct tvp514x_decoder *decoder) /* common register initialization */ err = - tvp514x_write_regs(decoder->client, tvp514x_reg_list); + tvp514x_write_regs(decoder->client, decoder->tvp514x_regs); if (err) return err; @@ -580,7 +585,8 @@ static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id) return err; decoder->current_std = i; - tvp514x_reg_list[REG_VIDEO_STD].val = decoder->std_list[i].video_std; + decoder->tvp514x_regs[REG_VIDEO_STD].val = + decoder->std_list[i].video_std; v4l_dbg(1, debug, decoder->client, "Standard set to: %s", decoder->std_list[i].standard.name); @@ -625,8 +631,8 @@ static int ioctl_s_routing(struct v4l2_int_device *s, if (err) return err; - tvp514x_reg_list[REG_INPUT_SEL].val = input_sel; - tvp514x_reg_list[REG_OUTPUT_FORMATTER1].val = output_sel; + decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel; + decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel; /* Clear status */ msleep(LOCK_RETRY_DELAY); @@ -686,7 +692,7 @@ static int ioctl_s_routing(struct v4l2_int_device *s, break; /* Input detected */ } - if ((current_std == STD_INVALID) || (try_count < 0)) + if ((current_std == STD_INVALID) || (try_count <= 0)) return -EINVAL; decoder->current_std = current_std; @@ -719,10 +725,9 @@ ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl) switch (qctrl->id) { case V4L2_CID_BRIGHTNESS: - /* Brightness supported is same as standard one (0-255), - * so make use of standard API provided. + /* Brightness supported is (0-255), */ - err = v4l2_ctrl_query_fill_std(qctrl); + err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); break; case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: @@ -779,16 +784,16 @@ ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ctrl->value = tvp514x_reg_list[REG_BRIGHTNESS].val; + ctrl->value = decoder->tvp514x_regs[REG_BRIGHTNESS].val; break; case V4L2_CID_CONTRAST: - ctrl->value = tvp514x_reg_list[REG_CONTRAST].val; + ctrl->value = decoder->tvp514x_regs[REG_CONTRAST].val; break; case V4L2_CID_SATURATION: - ctrl->value = tvp514x_reg_list[REG_SATURATION].val; + ctrl->value = decoder->tvp514x_regs[REG_SATURATION].val; break; case V4L2_CID_HUE: - ctrl->value = tvp514x_reg_list[REG_HUE].val; + ctrl->value = decoder->tvp514x_regs[REG_HUE].val; if (ctrl->value == 0x7F) ctrl->value = 180; else if (ctrl->value == 0x80) @@ -798,7 +803,7 @@ ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) break; case V4L2_CID_AUTOGAIN: - ctrl->value = tvp514x_reg_list[REG_AFE_GAIN_CTRL].val; + ctrl->value = decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val; if ((ctrl->value & 0x3) == 3) ctrl->value = 1; else @@ -848,7 +853,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) value); if (err) return err; - tvp514x_reg_list[REG_BRIGHTNESS].val = value; + decoder->tvp514x_regs[REG_BRIGHTNESS].val = value; break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 255) { @@ -861,7 +866,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) value); if (err) return err; - tvp514x_reg_list[REG_CONTRAST].val = value; + decoder->tvp514x_regs[REG_CONTRAST].val = value; break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 255) { @@ -874,7 +879,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) value); if (err) return err; - tvp514x_reg_list[REG_SATURATION].val = value; + decoder->tvp514x_regs[REG_SATURATION].val = value; break; case V4L2_CID_HUE: if (value == 180) @@ -893,7 +898,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) value); if (err) return err; - tvp514x_reg_list[REG_HUE].val = value; + decoder->tvp514x_regs[REG_HUE].val = value; break; case V4L2_CID_AUTOGAIN: if (value == 1) @@ -910,7 +915,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl) value); if (err) return err; - tvp514x_reg_list[REG_AFE_GAIN_CTRL].val = value; + decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value; break; default: v4l_err(decoder->client, @@ -1275,7 +1280,7 @@ static int ioctl_init(struct v4l2_int_device *s) struct tvp514x_decoder *decoder = s->priv; /* Set default standard to auto */ - tvp514x_reg_list[REG_VIDEO_STD].val = + decoder->tvp514x_regs[REG_VIDEO_STD].val = VIDEO_STD_AUTO_SWITCH_BIT; return tvp514x_configure(decoder); @@ -1344,11 +1349,6 @@ static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = { (v4l2_int_ioctl_func *) ioctl_s_routing}, }; -static struct v4l2_int_slave tvp514x_slave = { - .ioctls = tvp514x_ioctl_desc, - .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc), -}; - static struct tvp514x_decoder tvp514x_dev = { .state = STATE_NOT_DETECTED, @@ -1369,17 +1369,15 @@ static struct tvp514x_decoder tvp514x_dev = { .current_std = STD_NTSC_MJ, .std_list = tvp514x_std_list, .num_stds = ARRAY_SIZE(tvp514x_std_list), - -}; - -static struct v4l2_int_device tvp514x_int_device = { - .module = THIS_MODULE, - .name = TVP514X_MODULE_NAME, - .priv = &tvp514x_dev, - .type = v4l2_int_type_slave, - .u = { - .slave = &tvp514x_slave, - }, + .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), + }, }; /** @@ -1392,26 +1390,37 @@ static struct v4l2_int_device tvp514x_int_device = { static int tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct tvp514x_decoder *decoder = &tvp514x_dev; + struct tvp514x_decoder *decoder; int err; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - decoder->pdata = client->dev.platform_data; - if (!decoder->pdata) { + decoder = kzalloc(sizeof(*decoder), GFP_KERNEL); + if (!decoder) + return -ENOMEM; + + if (!client->dev.platform_data) { v4l_err(client, "No platform data!!\n"); - return -ENODEV; + err = -ENODEV; + goto out_free; } + + *decoder = tvp514x_dev; + decoder->v4l2_int_device.priv = decoder; + decoder->pdata = client->dev.platform_data; + decoder->v4l2_int_device.u.slave = &decoder->tvp514x_slave; + memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default, + sizeof(tvp514x_reg_list_default)); /* * Fetch platform specific data, and configure the * tvp514x_reg_list[] accordingly. Since this is one * time configuration, no need to preserve. */ - tvp514x_reg_list[REG_OUTPUT_FORMATTER2].val |= + decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |= (decoder->pdata->clk_polarity << 1); - tvp514x_reg_list[REG_SYNC_CONTROL].val |= + decoder->tvp514x_regs[REG_SYNC_CONTROL].val |= ((decoder->pdata->hs_polarity << 2) | (decoder->pdata->vs_polarity << 3)); /* @@ -1419,23 +1428,27 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id) */ decoder->id = (struct i2c_device_id *)id; /* Attach to Master */ - strcpy(tvp514x_int_device.u.slave->attach_to, decoder->pdata->master); - decoder->v4l2_int_device = &tvp514x_int_device; + strcpy(decoder->v4l2_int_device.u.slave->attach_to, + decoder->pdata->master); decoder->client = client; i2c_set_clientdata(client, decoder); /* Register with V4L2 layer as slave device */ - err = v4l2_int_device_register(decoder->v4l2_int_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); - return 0; + +out_free: + kfree(decoder); + return err; } /** @@ -1452,9 +1465,9 @@ static int __exit tvp514x_remove(struct i2c_client *client) if (!client->adapter) return -ENODEV; /* our client isn't attached */ - v4l2_int_device_unregister(decoder->v4l2_int_device); + v4l2_int_device_unregister(&decoder->v4l2_int_device); i2c_set_clientdata(client, NULL); - + kfree(decoder); return 0; } /* diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 2cd64ef..3a5a95f 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -8,7 +8,6 @@ #include <linux/i2c.h> #include <linux/videodev2.h> #include <linux/delay.h> -#include <linux/video_decoder.h> #include <media/v4l2-device.h> #include <media/tvp5150.h> #include <media/v4l2-i2c-drv-legacy.h> @@ -632,7 +631,7 @@ static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, const struct i2c_vbi_ram_value *regs = vbi_ram_default; int line; - v4l2_dbg(1, debug, sd, "VIDIOC_G_SLICED_VBI_CAP\n"); + v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n"); memset(cap, 0, sizeof *cap); while (regs->reg != (u16)-1 ) { @@ -831,7 +830,7 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) static int tvp5150_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - v4l2_dbg(1, debug, sd, "VIDIOC_G_CTRL called\n"); + v4l2_dbg(1, debug, sd, "g_ctrl called\n"); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -861,7 +860,7 @@ static int tvp5150_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) if (ctrl->value < tvp5150_qctrl[i].minimum || ctrl->value > tvp5150_qctrl[i].maximum) return -ERANGE; - v4l2_dbg(1, debug, sd, "VIDIOC_S_CTRL: id=%d, value=%d\n", + v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", ctrl->id, ctrl->value); break; } @@ -1015,7 +1014,7 @@ static int tvp5150_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { int i; - v4l2_dbg(1, debug, sd, "VIDIOC_QUERYCTRL called\n"); + v4l2_dbg(1, debug, sd, "queryctrl called\n"); for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++) if (qc->id && qc->id == tvp5150_qctrl[i].id) { @@ -1126,7 +1125,6 @@ MODULE_DEVICE_TABLE(i2c, tvp5150_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tvp5150", - .driverid = I2C_DRIVERID_TVP5150, .command = tvp5150_command, .probe = tvp5150_probe, .remove = tvp5150_remove, diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index 52c0357..a399476 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -460,9 +460,11 @@ static int tw9910_mask_set(struct i2c_client *client, u8 command, u8 mask, u8 set) { s32 val = i2c_smbus_read_byte_data(client, command); + if (val < 0) + return val; val &= ~mask; - val |= set; + val |= set & mask; return i2c_smbus_write_byte_data(client, command, val); } @@ -639,8 +641,8 @@ static int tw9910_set_register(struct soc_camera_device *icd, } #endif -static int tw9910_set_fmt(struct soc_camera_device *icd, __u32 pixfmt, - struct v4l2_rect *rect) +static int tw9910_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) { struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); int ret = -EINVAL; @@ -731,8 +733,33 @@ tw9910_set_fmt_error: return ret; } +static int tw9910_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + 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 i; + + /* + * check color format + */ + for (i = 0; i < ARRAY_SIZE(tw9910_color_fmt); i++) + if (pix->pixelformat == tw9910_color_fmt[i].fourcc) + break; + + if (i == ARRAY_SIZE(tw9910_color_fmt)) + return -EINVAL; + + return tw9910_set_crop(icd, &rect); +} + static int tw9910_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) + struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; const struct tw9910_scale_ctrl *scale; @@ -820,6 +847,7 @@ static struct soc_camera_ops tw9910_ops = { .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, diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index f4522bb..c0ac651 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -187,11 +187,6 @@ static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register } #endif -static int upd64031a_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops upd64031a_core_ops = { @@ -267,8 +262,6 @@ MODULE_DEVICE_TABLE(i2c, upd64031a_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "upd64031a", - .driverid = I2C_DRIVERID_UPD64031A, - .command = upd64031a_command, .probe = upd64031a_probe, .remove = upd64031a_remove, .id_table = upd64031a_id, diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index a5fb74b..410c915 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -164,11 +164,6 @@ static int upd64083_log_status(struct v4l2_subdev *sd) return 0; } -static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops upd64083_core_ops = { @@ -239,8 +234,6 @@ MODULE_DEVICE_TABLE(i2c, upd64083_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "upd64083", - .driverid = I2C_DRIVERID_UPD64083, - .command = upd64083_command, .probe = upd64083_probe, .remove = upd64083_remove, .id_table = upd64083_id, diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c index 2f11063..8d73979 100644 --- a/drivers/media/video/usbvideo/vicam.c +++ b/drivers/media/video/usbvideo/vicam.c @@ -191,7 +191,7 @@ initialize_camera(struct vicam_camera *cam) { int err; const struct ihex_binrec *rec; - const struct firmware *fw; + const struct firmware *uninitialized_var(fw); err = request_ihex_firmware(&fw, "vicam/firmware.fw", &cam->udev->dev); if (err) { diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 9e4f506..a0feb1c 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -36,7 +36,6 @@ #include <linux/spinlock.h> #include <asm/io.h> #include <linux/videodev2.h> -#include <linux/video_decoder.h> #include <linux/i2c.h> #include <media/saa7115.h> @@ -381,8 +380,9 @@ int usbvision_scratch_alloc(struct usb_usbvision *usbvision) usbvision->scratch = vmalloc_32(scratch_buf_size); scratch_reset(usbvision); if(usbvision->scratch == NULL) { - err("%s: unable to allocate %d bytes for scratch", - __func__, scratch_buf_size); + dev_err(&usbvision->dev->dev, + "%s: unable to allocate %d bytes for scratch\n", + __func__, scratch_buf_size); return -ENOMEM; } return 0; @@ -491,8 +491,9 @@ int usbvision_decompress_alloc(struct usb_usbvision *usbvision) int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2; usbvision->IntraFrameBuffer = vmalloc_32(IFB_size); if (usbvision->IntraFrameBuffer == NULL) { - err("%s: unable to allocate %d for compr. frame buffer", - __func__, IFB_size); + dev_err(&usbvision->dev->dev, + "%s: unable to allocate %d for compr. frame buffer\n", + __func__, IFB_size); return -ENOMEM; } return 0; @@ -1514,8 +1515,9 @@ static void usbvision_isocIrq(struct urb *urb) errCode = usb_submit_urb (urb, GFP_ATOMIC); if(errCode) { - err("%s: usb_submit_urb failed: error %d", - __func__, errCode); + dev_err(&usbvision->dev->dev, + "%s: usb_submit_urb failed: error %d\n", + __func__, errCode); } return; @@ -1546,7 +1548,8 @@ int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg) 0, (__u16) reg, buffer, 1, HZ); if (errCode < 0) { - err("%s: failed: error %d", __func__, errCode); + dev_err(&usbvision->dev->dev, + "%s: failed: error %d\n", __func__, errCode); return errCode; } return buffer[0]; @@ -1574,7 +1577,8 @@ int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ); if (errCode < 0) { - err("%s: failed: error %d", __func__, errCode); + dev_err(&usbvision->dev->dev, + "%s: failed: error %d\n", __func__, errCode); } return errCode; } @@ -1850,7 +1854,8 @@ int usbvision_set_output(struct usb_usbvision *usbvision, int width, 0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ); if (errCode < 0) { - err("%s failed: error %d", __func__, errCode); + dev_err(&usbvision->dev->dev, + "%s failed: error %d\n", __func__, errCode); return errCode; } usbvision->curwidth = usbvision->stretch_width * UsbWidth; @@ -2236,7 +2241,7 @@ static int usbvision_set_dram_settings(struct usb_usbvision *usbvision) (__u16) USBVISION_DRM_PRM1, value, 8, HZ); if (rc < 0) { - err("%sERROR=%d", __func__, rc); + dev_err(&usbvision->dev->dev, "%sERROR=%d\n", __func__, rc); return rc; } @@ -2432,8 +2437,9 @@ int usbvision_set_alternate(struct usb_usbvision *dev) PDEBUG(DBG_FUNC,"setting alternate %d with wMaxPacketSize=%u", dev->ifaceAlt,dev->isocPacketSize); errCode = usb_set_interface(dev->dev, dev->iface, dev->ifaceAlt); if (errCode < 0) { - err ("cannot change alternate number to %d (error=%i)", - dev->ifaceAlt, errCode); + dev_err(&dev->dev->dev, + "cannot change alternate number to %d (error=%i)\n", + dev->ifaceAlt, errCode); return errCode; } } @@ -2484,7 +2490,8 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); if (urb == NULL) { - err("%s: usb_alloc_urb() failed", __func__); + dev_err(&usbvision->dev->dev, + "%s: usb_alloc_urb() failed\n", __func__); return -ENOMEM; } usbvision->sbuf[bufIdx].urb = urb; @@ -2496,7 +2503,7 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) urb->dev = dev; urb->context = usbvision; urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp); - urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->interval = 1; urb->transfer_buffer = usbvision->sbuf[bufIdx].data; urb->complete = usbvision_isocIrq; @@ -2516,8 +2523,9 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb, GFP_KERNEL); if (errCode) { - err("%s: usb_submit_urb(%d) failed: error %d", - __func__, bufIdx, errCode); + dev_err(&usbvision->dev->dev, + "%s: usb_submit_urb(%d) failed: error %d\n", + __func__, bufIdx, errCode); } } @@ -2566,8 +2574,9 @@ void usbvision_stop_isoc(struct usb_usbvision *usbvision) errCode = usb_set_interface(usbvision->dev, usbvision->iface, usbvision->ifaceAlt); if (errCode < 0) { - err("%s: usb_set_interface() failed: error %d", - __func__, errCode); + dev_err(&usbvision->dev->dev, + "%s: usb_set_interface() failed: error %d\n", + __func__, errCode); usbvision->last_error = errCode; } regValue = (16-usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F; @@ -2623,7 +2632,7 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) } route.input = mode[channel]; route.output = 0; - call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route); + call_all(usbvision, video, s_routing, &route); usbvision_set_audio(usbvision, audio[channel]); return 0; } diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index 6b66ae4..dd2f8f2 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -119,7 +119,8 @@ static inline int usb_find_address(struct i2c_adapter *i2c_adap, /* try extended address code... */ ret = try_write_address(i2c_adap, addr, retries); if (ret != 1) { - err("died at extended address code, while writing"); + dev_err(&i2c_adap->dev, + "died at extended address code, while writing\n"); return -EREMOTEIO; } add[0] = addr; @@ -128,7 +129,8 @@ static inline int usb_find_address(struct i2c_adapter *i2c_adap, addr |= 0x01; ret = try_read_address(i2c_adap, addr, retries); if (ret != 1) { - err("died at extended address code, while reading"); + dev_err(&i2c_adap->dev, + "died at extended address code, while reading\n"); return -EREMOTEIO; } } @@ -200,72 +202,78 @@ static struct i2c_algorithm usbvision_algo = { }; -/* - * registering functions to load algorithms at runtime - */ -static int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap) -{ - PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]"); - PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]"); - - /* register new adapter to i2c module... */ - - adap->algo = &usbvision_algo; - - adap->timeout = 100; /* default values, should */ - adap->retries = 3; /* be replaced by defines */ - - i2c_add_adapter(adap); - - PDEBUG(DBG_I2C,"i2c bus for %s registered", adap->name); - - return 0; -} - /* ----------------------------------------------------------------------- */ /* usbvision specific I2C functions */ /* ----------------------------------------------------------------------- */ static struct i2c_adapter i2c_adap_template; -static struct i2c_client i2c_client_template; int usbvision_i2c_register(struct usb_usbvision *usbvision) { + static unsigned short saa711x_addrs[] = { + 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ + 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ + I2C_CLIENT_END }; + memcpy(&usbvision->i2c_adap, &i2c_adap_template, sizeof(struct i2c_adapter)); - memcpy(&usbvision->i2c_client, &i2c_client_template, - sizeof(struct i2c_client)); sprintf(usbvision->i2c_adap.name + strlen(usbvision->i2c_adap.name), " #%d", usbvision->vdev->num); PDEBUG(DBG_I2C,"Adaptername: %s", usbvision->i2c_adap.name); usbvision->i2c_adap.dev.parent = &usbvision->dev->dev; - i2c_set_adapdata(&usbvision->i2c_adap, usbvision); - i2c_set_clientdata(&usbvision->i2c_client, usbvision); - - usbvision->i2c_client.adapter = &usbvision->i2c_adap; + i2c_set_adapdata(&usbvision->i2c_adap, &usbvision->v4l2_dev); if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) { printk(KERN_ERR "usbvision_register: can't write reg\n"); return -EBUSY; } -#ifdef CONFIG_MODULES + PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]"); + PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]"); + + /* register new adapter to i2c module... */ + + usbvision->i2c_adap.algo = &usbvision_algo; + + usbvision->i2c_adap.timeout = 100; /* default values, should */ + usbvision->i2c_adap.retries = 3; /* be replaced by defines */ + + i2c_add_adapter(&usbvision->i2c_adap); + + PDEBUG(DBG_I2C, "i2c bus for %s registered", usbvision->i2c_adap.name); + /* Request the load of the i2c modules we need */ switch (usbvision_device_data[usbvision->DevModel].Codec) { case CODEC_SAA7113: - request_module("saa7115"); - break; case CODEC_SAA7111: - request_module("saa7115"); + v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "saa7115", + "saa7115_auto", saa711x_addrs); break; } if (usbvision_device_data[usbvision->DevModel].Tuner == 1) { - request_module("tuner"); + struct v4l2_subdev *sd; + enum v4l2_i2c_tuner_type type; + struct tuner_setup tun_setup; + + sd = v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "tuner", + "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + /* depending on whether we found a demod or not, select + the tuner type. */ + type = sd ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; + + sd = v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "tuner", + "tuner", v4l2_i2c_tuner_addrs(type)); + + if (usbvision->tuner_type != -1) { + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = usbvision->tuner_type; + tun_setup.addr = v4l2_i2c_subdev_addr(sd); + call_all(usbvision, tuner, s_type_addr, &tun_setup); + } } -#endif - return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap); + return 0; } int usbvision_i2c_unregister(struct usb_usbvision *usbvision) @@ -278,67 +286,6 @@ int usbvision_i2c_unregister(struct usb_usbvision *usbvision) return 0; } -void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, - void *arg) -{ - i2c_clients_command(&usbvision->i2c_adap, cmd, arg); -} - -static int attach_inform(struct i2c_client *client) -{ - struct usb_usbvision *usbvision; - - usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); - - switch (client->addr << 1) { - case 0x42 << 1: - case 0x43 << 1: - case 0x4a << 1: - case 0x4b << 1: - PDEBUG(DBG_I2C,"attach_inform: tda9887 detected."); - break; - case 0x42: - PDEBUG(DBG_I2C,"attach_inform: saa7114 detected."); - break; - case 0x4a: - PDEBUG(DBG_I2C,"attach_inform: saa7113 detected."); - break; - case 0x48: - PDEBUG(DBG_I2C,"attach_inform: saa7111 detected."); - break; - case 0xa0: - PDEBUG(DBG_I2C,"attach_inform: eeprom detected."); - break; - - default: - { - struct tuner_setup tun_setup; - - PDEBUG(DBG_I2C,"attach inform: detected I2C address %x", client->addr << 1); - usbvision->tuner_addr = client->addr; - - if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) { - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = usbvision->tuner_type; - tun_setup.addr = usbvision->tuner_addr; - call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); - } - } - break; - } - return 0; -} - -static int detach_inform(struct i2c_client *client) -{ - struct usb_usbvision *usbvision; - - usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); - - PDEBUG(DBG_I2C,"usbvision[%d] detaches %s", usbvision->nr, client->name); - return 0; -} - static int usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr, char *buf, short len) @@ -511,14 +458,6 @@ static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char add static struct i2c_adapter i2c_adap_template = { .owner = THIS_MODULE, .name = "usbvision", - .id = I2C_HW_B_BT848, /* FIXME */ - .client_register = attach_inform, - .client_unregister = detach_inform, - .class = I2C_CLASS_TV_ANALOG, -}; - -static struct i2c_client i2c_client_template = { - .name = "usbvision internal", }; /* diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 2622de0..fa62a2f 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -59,7 +59,6 @@ #include <linux/spinlock.h> #include <asm/io.h> #include <linux/videodev2.h> -#include <linux/video_decoder.h> #include <linux/i2c.h> #include <media/saa7115.h> @@ -212,7 +211,7 @@ static ssize_t show_hue(struct device *cd, ctrl.id = V4L2_CID_HUE; ctrl.value = 0; if(usbvision->user) - call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + call_all(usbvision, core, g_ctrl, &ctrl); return sprintf(buf, "%d\n", ctrl.value); } static DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); @@ -227,7 +226,7 @@ static ssize_t show_contrast(struct device *cd, ctrl.id = V4L2_CID_CONTRAST; ctrl.value = 0; if(usbvision->user) - call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + call_all(usbvision, core, g_ctrl, &ctrl); return sprintf(buf, "%d\n", ctrl.value); } static DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); @@ -242,7 +241,7 @@ static ssize_t show_brightness(struct device *cd, ctrl.id = V4L2_CID_BRIGHTNESS; ctrl.value = 0; if(usbvision->user) - call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + call_all(usbvision, core, g_ctrl, &ctrl); return sprintf(buf, "%d\n", ctrl.value); } static DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); @@ -257,7 +256,7 @@ static ssize_t show_saturation(struct device *cd, ctrl.id = V4L2_CID_SATURATION; ctrl.value = 0; if(usbvision->user) - call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); + call_all(usbvision, core, g_ctrl, &ctrl); return sprintf(buf, "%d\n", ctrl.value); } static DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); @@ -329,7 +328,7 @@ static void usbvision_create_sysfs(struct video_device *vdev) return; } while (0); - err("%s error: %d\n", __func__, res); + dev_err(&vdev->dev, "%s error: %d\n", __func__, res); } static void usbvision_remove_sysfs(struct video_device *vdev) @@ -487,8 +486,9 @@ static int vidioc_g_register (struct file *file, void *priv, /* NT100x has a 8-bit register space */ errCode = usbvision_read_reg(usbvision, reg->reg&0xff); if (errCode < 0) { - err("%s: VIDIOC_DBG_G_REGISTER failed: error %d", - __func__, errCode); + dev_err(&usbvision->vdev->dev, + "%s: VIDIOC_DBG_G_REGISTER failed: error %d\n", + __func__, errCode); return errCode; } reg->val = errCode; @@ -507,8 +507,9 @@ static int vidioc_s_register (struct file *file, void *priv, /* NT100x has a 8-bit register space */ errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val); if (errCode < 0) { - err("%s: VIDIOC_DBG_S_REGISTER failed: error %d", - __func__, errCode); + dev_err(&usbvision->vdev->dev, + "%s: VIDIOC_DBG_S_REGISTER failed: error %d\n", + __func__, errCode); return errCode; } return 0; @@ -524,8 +525,7 @@ static int vidioc_querycap (struct file *file, void *priv, strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString, sizeof(vc->card)); - strlcpy(vc->bus_info, dev_name(&usbvision->dev->dev), - sizeof(vc->bus_info)); + usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info)); vc->version = USBVISION_DRIVER_VERSION; vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO | @@ -621,8 +621,7 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) usbvision->tvnormId=*id; mutex_lock(&usbvision->lock); - call_i2c_clients(usbvision, VIDIOC_S_STD, - &usbvision->tvnormId); + call_all(usbvision, tuner, s_std, usbvision->tvnormId); mutex_unlock(&usbvision->lock); /* propagate the change to the decoder */ usbvision_muxsel(usbvision, usbvision->ctl_input); @@ -644,7 +643,7 @@ static int vidioc_g_tuner (struct file *file, void *priv, strcpy(vt->name, "Television"); } /* Let clients fill in the remainder of this struct */ - call_i2c_clients(usbvision,VIDIOC_G_TUNER,vt); + call_all(usbvision, tuner, g_tuner, vt); return 0; } @@ -658,7 +657,7 @@ static int vidioc_s_tuner (struct file *file, void *priv, if (!usbvision->have_tuner || vt->index) return -EINVAL; /* let clients handle this */ - call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt); + call_all(usbvision, tuner, s_tuner, vt); return 0; } @@ -689,7 +688,7 @@ static int vidioc_s_frequency (struct file *file, void *priv, return -EINVAL; usbvision->freq = freq->frequency; - call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, freq); + call_all(usbvision, tuner, s_frequency, freq); return 0; } @@ -698,7 +697,6 @@ static int vidioc_g_audio (struct file *file, void *priv, struct v4l2_audio *a) { struct usb_usbvision *usbvision = video_drvdata(file); - memset(a,0,sizeof(*a)); if(usbvision->radio) { strcpy(a->name,"Radio"); } else { @@ -722,12 +720,8 @@ static int vidioc_queryctrl (struct file *file, void *priv, struct v4l2_queryctrl *ctrl) { struct usb_usbvision *usbvision = video_drvdata(file); - int id=ctrl->id; - memset(ctrl,0,sizeof(*ctrl)); - ctrl->id=id; - - call_i2c_clients(usbvision, VIDIOC_QUERYCTRL, ctrl); + call_all(usbvision, core, queryctrl, ctrl); if (!ctrl->type) return -EINVAL; @@ -739,7 +733,7 @@ static int vidioc_g_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { struct usb_usbvision *usbvision = video_drvdata(file); - call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); + call_all(usbvision, core, g_ctrl, ctrl); return 0; } @@ -748,7 +742,7 @@ static int vidioc_s_ctrl (struct file *file, void *priv, struct v4l2_control *ctrl) { struct usb_usbvision *usbvision = video_drvdata(file); - call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); + call_all(usbvision, core, s_ctrl, ctrl); return 0; } @@ -763,8 +757,7 @@ static int vidioc_reqbufs (struct file *file, /* Check input validity: the user must do a VIDEO CAPTURE and MMAP method. */ - if((vr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || - (vr->memory != V4L2_MEMORY_MMAP)) + if (vr->memory != V4L2_MEMORY_MMAP) return -EINVAL; if(usbvision->streaming == Stream_On) { @@ -789,9 +782,6 @@ static int vidioc_querybuf (struct file *file, /* FIXME : must control that buffers are mapped (VIDIOC_REQBUFS has been called) */ - if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { - return -EINVAL; - } if(vb->index>=usbvision->num_frames) { return -EINVAL; } @@ -825,9 +815,6 @@ static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *vb) unsigned long lock_flags; /* FIXME : works only on VIDEO_CAPTURE MODE, MMAP. */ - if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { - return -EINVAL; - } if(vb->index>=usbvision->num_frames) { return -EINVAL; } @@ -862,9 +849,6 @@ static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb) struct usbvision_frame *f; unsigned long lock_flags; - if (vb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (list_empty(&(usbvision->outqueue))) { if (usbvision->streaming == Stream_Idle) return -EINVAL; @@ -899,10 +883,9 @@ static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb) static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct usb_usbvision *usbvision = video_drvdata(file); - int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; usbvision->streaming = Stream_On; - call_i2c_clients(usbvision,VIDIOC_STREAMON , &b); + call_all(usbvision, video, s_stream, 1); return 0; } @@ -911,7 +894,6 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { struct usb_usbvision *usbvision = video_drvdata(file); - int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; @@ -919,7 +901,7 @@ static int vidioc_streamoff(struct file *file, if(usbvision->streaming == Stream_On) { usbvision_stream_interrupt(usbvision); /* Stop all video streamings */ - call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b); + call_all(usbvision, video, s_stream, 0); } usbvision_empty_framequeues(usbvision); @@ -932,11 +914,8 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) { return -EINVAL; } - vfd->flags = 0; - vfd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc); vfd->pixelformat = usbvision_v4l2_format[vfd->index].format; - memset(vfd->reserved, 0, sizeof(vfd->reserved)); return 0; } @@ -1042,7 +1021,7 @@ static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, if(usbvision->streaming != Stream_On) { /* no stream is running, make it running ! */ usbvision->streaming = Stream_On; - call_i2c_clients(usbvision,VIDIOC_STREAMON , NULL); + call_all(usbvision, video, s_stream, 1); } /* Then, enqueue as many frames as possible @@ -1189,7 +1168,9 @@ static int usbvision_radio_open(struct file *file) mutex_lock(&usbvision->lock); if (usbvision->user) { - err("%s: Someone tried to open an already opened USBVision Radio!", __func__); + dev_err(&usbvision->rdev->dev, + "%s: Someone tried to open an already opened USBVision Radio!\n", + __func__); errCode = -EBUSY; } else { @@ -1211,7 +1192,7 @@ static int usbvision_radio_open(struct file *file) // If so far no errors then we shall start the radio usbvision->radio = 1; - call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type); + call_all(usbvision, tuner, s_radio); usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); usbvision->user++; } @@ -1413,7 +1394,8 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, struct video_device *vdev; if (usb_dev == NULL) { - err("%s: usbvision->dev is not set", __func__); + dev_err(&usbvision->dev->dev, + "%s: usbvision->dev is not set\n", __func__); return NULL; } @@ -1423,7 +1405,7 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, } *vdev = *vdev_template; // vdev->minor = -1; - vdev->parent = &usb_dev->dev; + vdev->v4l2_dev = &usbvision->v4l2_dev; snprintf(vdev->name, sizeof(vdev->name), "%s", name); video_set_drvdata(vdev, usbvision); return vdev; @@ -1524,7 +1506,9 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) return 0; err_exit: - err("USBVision[%d]: video_register_device() failed", usbvision->nr); + dev_err(&usbvision->dev->dev, + "USBVision[%d]: video_register_device() failed\n", + usbvision->nr); usbvision_unregister_video(usbvision); return -1; } @@ -1542,33 +1526,30 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev) { struct usb_usbvision *usbvision; - if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) == - NULL) { - goto err_exit; - } + usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL); + if (usbvision == NULL) + return NULL; usbvision->dev = dev; + if (v4l2_device_register(&dev->dev, &usbvision->v4l2_dev)) + goto err_free; mutex_init(&usbvision->lock); /* available */ // prepare control urb for control messages during interrupts usbvision->ctrlUrb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL); - if (usbvision->ctrlUrb == NULL) { - goto err_exit; - } + if (usbvision->ctrlUrb == NULL) + goto err_unreg; init_waitqueue_head(&usbvision->ctrlUrb_wq); usbvision_init_powerOffTimer(usbvision); return usbvision; -err_exit: - if (usbvision && usbvision->ctrlUrb) { - usb_free_urb(usbvision->ctrlUrb); - } - if (usbvision) { - kfree(usbvision); - } +err_unreg: + v4l2_device_unregister(&usbvision->v4l2_dev); +err_free: + kfree(usbvision); return NULL; } @@ -1598,6 +1579,7 @@ static void usbvision_release(struct usb_usbvision *usbvision) usb_free_urb(usbvision->ctrlUrb); } + v4l2_device_unregister(&usbvision->v4l2_dev); kfree(usbvision); PDEBUG(DBG_PROBE, "success"); @@ -1675,20 +1657,20 @@ static int __devinit usbvision_probe(struct usb_interface *intf, } endpoint = &interface->endpoint[1].desc; if (!usb_endpoint_xfer_isoc(endpoint)) { - err("%s: interface %d. has non-ISO endpoint!", + dev_err(&intf->dev, "%s: interface %d. has non-ISO endpoint!\n", __func__, ifnum); - err("%s: Endpoint attributes %d", + dev_err(&intf->dev, "%s: Endpoint attributes %d", __func__, endpoint->bmAttributes); return -ENODEV; } if (usb_endpoint_dir_out(endpoint)) { - err("%s: interface %d. has ISO OUT endpoint!", + dev_err(&intf->dev, "%s: interface %d. has ISO OUT endpoint!\n", __func__, ifnum); return -ENODEV; } if ((usbvision = usbvision_alloc(dev)) == NULL) { - err("%s: couldn't allocate USBVision struct", __func__); + dev_err(&intf->dev, "%s: couldn't allocate USBVision struct\n", __func__); return -ENOMEM; } @@ -1711,7 +1693,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, usbvision->alt_max_pkt_size = kmalloc(32* usbvision->num_alt,GFP_KERNEL); if (usbvision->alt_max_pkt_size == NULL) { - err("usbvision: out of memory!\n"); + dev_err(&intf->dev, "usbvision: out of memory!\n"); mutex_unlock(&usbvision->lock); return -ENOMEM; } @@ -1733,8 +1715,6 @@ static int __devinit usbvision_probe(struct usb_interface *intf, usbvision->tuner_type = usbvision_device_data[model].TunerType; } - usbvision->tuner_addr = ADDR_UNSET; - usbvision->DevModel = model; usbvision->remove_pending = 0; usbvision->iface = ifnum; @@ -1772,7 +1752,8 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) PDEBUG(DBG_PROBE, ""); if (usbvision == NULL) { - err("%s: usb_get_intfdata() failed", __func__); + dev_err(&usbvision->dev->dev, + "%s: usb_get_intfdata() failed\n", __func__); return; } usb_set_intfdata (intf, NULL); @@ -1782,6 +1763,8 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) // At this time we ask to cancel outstanding URBs usbvision_stop_isoc(usbvision); + v4l2_device_disconnect(&usbvision->v4l2_dev); + if (usbvision->power) { usbvision_i2c_unregister(usbvision); usbvision_power_off(usbvision); diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index 20d7ec6..f8d7458 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -6,7 +6,7 @@ * Dwaine Garden <dwainegarden@rogers.com> * * - * Report problems to v4l MailingList : http://www.redhat.com/mailman/listinfo/video4linux-list + * Report problems to v4l MailingList: linux-media@vger.kernel.org * * This module is part of usbvision driver project. * Updates to driver completed by Dwaine P. Garden @@ -35,7 +35,7 @@ #include <linux/usb.h> #include <linux/i2c.h> #include <linux/mutex.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/tuner.h> #include <linux/videodev2.h> @@ -357,13 +357,13 @@ extern struct usbvision_device_data_st usbvision_device_data[]; extern struct usb_device_id usbvision_table[]; struct usb_usbvision { + struct v4l2_device v4l2_dev; struct video_device *vdev; /* Video Device */ struct video_device *rdev; /* Radio Device */ struct video_device *vbi; /* VBI Device */ /* i2c Declaration Section*/ struct i2c_adapter i2c_adap; - struct i2c_client i2c_client; struct urb *ctrlUrb; unsigned char ctrlUrbBuffer[8]; @@ -374,7 +374,6 @@ struct usb_usbvision { /* configuration part */ int have_tuner; int tuner_type; - int tuner_addr; int bridgeType; // NT1003, NT1004, NT1005 int radio; int video_inputs; // # of inputs @@ -464,6 +463,8 @@ struct usb_usbvision { int ComprBlockTypes[4]; }; +#define call_all(usbvision, o, f, args...) \ + v4l2_device_call_all(&usbvision->v4l2_dev, 0, o, f, ##args) /* --------------------------------------------------------------- */ /* defined in usbvision-i2c.c */ @@ -475,7 +476,6 @@ struct usb_usbvision { /* ----------------------------------------------------------------------- */ int usbvision_i2c_register(struct usb_usbvision *usbvision); int usbvision_i2c_unregister(struct usb_usbvision *usbvision); -void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,void *arg); /* defined in usbvision-core.c */ int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg); diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index d2576f6..0d7e38d 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -786,7 +786,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); v4l2_ctrl->id = mapping->id; v4l2_ctrl->type = mapping->v4l2_type; - strncpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); + strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); v4l2_ctrl->flags = 0; if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR)) diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index b128732..399412d 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -314,7 +314,7 @@ static int uvc_parse_format(struct uvc_device *dev, fmtdesc = uvc_format_by_guid(&buffer[5]); if (fmtdesc != NULL) { - strncpy(format->name, fmtdesc->name, + strlcpy(format->name, fmtdesc->name, sizeof format->name); format->fcc = fmtdesc->fcc; } else { @@ -345,7 +345,7 @@ static int uvc_parse_format(struct uvc_device *dev, return -EINVAL; } - strncpy(format->name, "MJPEG", sizeof format->name); + strlcpy(format->name, "MJPEG", sizeof format->name); format->fcc = V4L2_PIX_FMT_MJPEG; format->flags = UVC_FMT_FLAG_COMPRESSED; format->bpp = 0; @@ -363,13 +363,13 @@ static int uvc_parse_format(struct uvc_device *dev, switch (buffer[8] & 0x7f) { case 0: - strncpy(format->name, "SD-DV", sizeof format->name); + strlcpy(format->name, "SD-DV", sizeof format->name); break; case 1: - strncpy(format->name, "SDL-DV", sizeof format->name); + strlcpy(format->name, "SDL-DV", sizeof format->name); break; case 2: - strncpy(format->name, "HD-DV", sizeof format->name); + strlcpy(format->name, "HD-DV", sizeof format->name); break; default: uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" @@ -379,7 +379,7 @@ static int uvc_parse_format(struct uvc_device *dev, return -EINVAL; } - strncat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", + strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz", sizeof format->name); format->fcc = V4L2_PIX_FMT_DV; @@ -1526,7 +1526,7 @@ static int uvc_register_video(struct uvc_device *dev) vdev->minor = -1; vdev->fops = &uvc_fops; vdev->release = video_device_release; - strncpy(vdev->name, dev->name, sizeof vdev->name); + strlcpy(vdev->name, dev->name, sizeof vdev->name); /* Set the driver data before calling video_register_device, otherwise * uvc_v4l2_open might race us. @@ -1621,7 +1621,7 @@ static int uvc_probe(struct usb_interface *intf, dev->quirks = id->driver_info | uvc_quirks_param; if (udev->product != NULL) - strncpy(dev->name, udev->product, sizeof dev->name); + strlcpy(dev->name, udev->product, sizeof dev->name); else snprintf(dev->name, sizeof dev->name, "UVC Camera (%04x:%04x)", @@ -1833,6 +1833,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0 }, + /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x058f, + .idProduct = 0x3820, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Apple Built-In iSight */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1852,6 +1861,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* ViMicro */ + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0ac8, + .idProduct = 0x0000, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, /* MT6227 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1879,7 +1897,7 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Asus F9SG */ + /* Syntek (Asus F9SG) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x174f, @@ -1897,6 +1915,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (JAOtech Smart Terminal) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a34, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Lenovo Thinkpad SL500 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c index c705f24..21d8712 100644 --- a/drivers/media/video/uvc/uvc_status.c +++ b/drivers/media/video/uvc/uvc_status.c @@ -24,26 +24,19 @@ #ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV static int uvc_input_init(struct uvc_device *dev) { - struct usb_device *udev = dev->udev; struct input_dev *input; - char *phys = NULL; int ret; input = input_allocate_device(); if (input == NULL) return -ENOMEM; - phys = kmalloc(6 + strlen(udev->bus->bus_name) + strlen(udev->devpath), - GFP_KERNEL); - if (phys == NULL) { - ret = -ENOMEM; - goto error; - } - sprintf(phys, "usb-%s-%s", udev->bus->bus_name, udev->devpath); + usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys)); + strlcat(dev->input_phys, "/button", sizeof(dev->input_phys)); input->name = dev->name; - input->phys = phys; - usb_to_input_id(udev, &input->id); + input->phys = dev->input_phys; + usb_to_input_id(dev->udev, &input->id); input->dev.parent = &dev->intf->dev; __set_bit(EV_KEY, input->evbit); @@ -57,7 +50,6 @@ static int uvc_input_init(struct uvc_device *dev) error: input_free_device(input); - kfree(phys); return ret; } diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index d681519..2a80caa 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -55,7 +55,7 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video, return -EINVAL; menu_info = &mapping->menu_info[query_menu->index]; - strncpy(query_menu->name, menu_info->name, 32); + strlcpy(query_menu->name, menu_info->name, sizeof query_menu->name); return 0; } @@ -486,10 +486,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_capability *cap = arg; memset(cap, 0, sizeof *cap); - strncpy(cap->driver, "uvcvideo", sizeof cap->driver); - strncpy(cap->card, vdev->name, 32); - strncpy(cap->bus_info, video->dev->udev->bus->bus_name, - sizeof cap->bus_info); + strlcpy(cap->driver, "uvcvideo", sizeof cap->driver); + strlcpy(cap->card, vdev->name, sizeof cap->card); + usb_make_path(video->dev->udev, + cap->bus_info, sizeof(cap->bus_info)); cap->version = DRIVER_VERSION_NUMBER; if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) cap->capabilities = V4L2_CAP_VIDEO_CAPTURE @@ -620,7 +620,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(input, 0, sizeof *input); input->index = index; - strncpy(input->name, iterm->name, sizeof input->name); + strlcpy(input->name, iterm->name, sizeof input->name); if (UVC_ENTITY_TYPE(iterm) == ITT_CAMERA) input->type = V4L2_INPUT_TYPE_CAMERA; break; @@ -673,16 +673,22 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_fmtdesc *fmt = arg; struct uvc_format *format; + enum v4l2_buf_type type = fmt->type; + __u32 index = fmt->index; if (fmt->type != video->streaming->type || fmt->index >= video->streaming->nformats) return -EINVAL; + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = type; + format = &video->streaming->format[fmt->index]; fmt->flags = 0; if (format->flags & UVC_FMT_FLAG_COMPRESSED) fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; - strncpy(fmt->description, format->name, + strlcpy(fmt->description, format->name, sizeof fmt->description); fmt->description[sizeof fmt->description - 1] = 0; fmt->pixelformat = format->fcc; diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 9bc4705..a95e173 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_buffer_size(struct uvc_video_device *video, +static void uvc_fixup_video_ctrl(struct uvc_video_device *video, struct uvc_streaming_control *ctrl) { struct uvc_format *format; @@ -84,6 +84,31 @@ static void uvc_fixup_buffer_size(struct uvc_video_device *video, video->dev->uvc_version < 0x0110)) ctrl->dwMaxVideoFrameSize = frame->dwMaxVideoFrameBufferSize; + + if (video->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH && + video->streaming->intf->num_altsetting > 1) { + u32 interval; + u32 bandwidth; + + interval = (ctrl->dwFrameInterval > 100000) + ? ctrl->dwFrameInterval + : frame->dwFrameInterval[0]; + + /* Compute a bandwidth estimation by multiplying the frame + * size by the number of video frames per second, divide the + * result by the number of USB frames (or micro-frames for + * high-speed devices) per second and add the UVC header size + * (assumed to be 12 bytes long). + */ + bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp; + bandwidth *= 10000000 / interval + 1; + bandwidth /= 1000; + if (video->dev->udev->speed == USB_SPEED_HIGH) + bandwidth /= 8; + bandwidth += 12; + + ctrl->dwMaxPayloadTransferSize = bandwidth; + } } static int uvc_get_video_ctrl(struct uvc_video_device *video, @@ -158,10 +183,11 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ctrl->bMaxVersion = 0; } - /* Some broken devices return a null or wrong dwMaxVideoFrameSize. - * Try to get the value from the format and frame descriptors. + /* Some broken devices return null or wrong dwMaxVideoFrameSize and + * dwMaxPayloadTransferSize fields. Try to get the value from the + * format and frame descriptors. */ - uvc_fixup_buffer_size(video, ctrl); + uvc_fixup_video_ctrl(video, ctrl); ret = 0; out: @@ -540,6 +566,9 @@ static void uvc_video_decode_bulk(struct urb *urb, u8 *mem; int len, ret; + if (urb->actual_length == 0) + return; + mem = urb->transfer_buffer; len = urb->actual_length; video->bulk.payload_size += len; @@ -699,27 +728,47 @@ static void uvc_free_urb_buffers(struct uvc_video_device *video) * already allocated when resuming from suspend, in which case it will * return without touching the buffers. * - * Return 0 on success or -ENOMEM when out of memory. + * Limit the buffer size to UVC_MAX_PACKETS bulk/isochronous packets. If the + * system is too low on memory try successively smaller numbers of packets + * until allocation succeeds. + * + * 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, - unsigned int size) + 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 0; - for (i = 0; i < UVC_URBS; ++i) { - video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev, - size, GFP_KERNEL, &video->urb_dma[i]); - if (video->urb_buffer[i] == NULL) { - uvc_free_urb_buffers(video); - return -ENOMEM; + /* Compute the number of packets. Bulk endpoints might transfer UVC + * payloads accross multiple URBs. + */ + npackets = DIV_ROUND_UP(size, psize); + if (npackets > UVC_MAX_PACKETS) + npackets = UVC_MAX_PACKETS; + + /* 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); + break; + } + } + + if (i == UVC_URBS) { + video->urb_size = psize * npackets; + return npackets; } } - video->urb_size = size; return 0; } @@ -753,29 +802,19 @@ static int uvc_init_video_isoc(struct uvc_video_device *video, { struct urb *urb; unsigned int npackets, i, j; - __u16 psize; - __u32 size; + u16 psize; + u32 size; - /* Compute the number of isochronous packets to allocate by dividing - * the maximum video frame size by the packet size. Limit the result - * to UVC_MAX_ISO_PACKETS. - */ psize = le16_to_cpu(ep->desc.wMaxPacketSize); psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); - size = video->streaming->ctrl.dwMaxVideoFrameSize; - if (size > UVC_MAX_FRAME_SIZE) - return -EINVAL; - npackets = DIV_ROUND_UP(size, psize); - if (npackets > UVC_MAX_ISO_PACKETS) - npackets = UVC_MAX_ISO_PACKETS; + npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags); + if (npackets == 0) + return -ENOMEM; size = npackets * psize; - if (uvc_alloc_urb_buffers(video, size) < 0) - return -ENOMEM; - for (i = 0; i < UVC_URBS; ++i) { urb = usb_alloc_urb(npackets, gfp_flags); if (urb == NULL) { @@ -814,25 +853,20 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, struct usb_host_endpoint *ep, gfp_t gfp_flags) { struct urb *urb; - unsigned int pipe, i; - __u16 psize; - __u32 size; - - /* Compute the bulk URB size. Some devices set the maximum payload - * size to a value too high for memory-constrained devices. We must - * then transfer the payload accross multiple URBs. To be consistant - * with isochronous mode, allocate maximum UVC_MAX_ISO_PACKETS per bulk - * URB. - */ + unsigned int npackets, pipe, i; + u16 psize; + u32 size; + psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff; size = video->streaming->ctrl.dwMaxPayloadTransferSize; video->bulk.max_payload_size = size; - if (size > psize * UVC_MAX_ISO_PACKETS) - size = psize * UVC_MAX_ISO_PACKETS; - if (uvc_alloc_urb_buffers(video, size) < 0) + npackets = uvc_alloc_urb_buffers(video, 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, ep->desc.bEndpointAddress); @@ -1021,11 +1055,20 @@ int uvc_video_init(struct uvc_video_device *video) */ usb_set_interface(video->dev->udev, video->streaming->intfnum, 0); - /* Some webcams don't suport GET_DEF requests on the probe control. We - * fall back to GET_CUR if GET_DEF fails. + /* 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); + + /* 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_DEF)) < 0 && - (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) + if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0) return ret; /* Check if the default format descriptor exists. Use the first diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 027947e..e5014e6 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -296,10 +296,8 @@ struct uvc_xu_control { /* Number of isochronous URBs. */ #define UVC_URBS 5 -/* Maximum number of packets per isochronous URB. */ -#define UVC_MAX_ISO_PACKETS 40 -/* Maximum frame size in bytes, for sanity checking. */ -#define UVC_MAX_FRAME_SIZE (16*1024*1024) +/* Maximum number of packets per URB. */ +#define UVC_MAX_PACKETS 32 /* Maximum number of video buffers. */ #define UVC_MAX_VIDEO_BUFFERS 32 /* Maximum status buffer size in bytes of interrupt URB. */ @@ -316,6 +314,7 @@ struct uvc_xu_control { #define UVC_QUIRK_STREAM_NO_FID 0x00000010 #define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 #define UVC_QUIRK_PRUNE_CONTROLS 0x00000040 +#define UVC_QUIRK_FIX_BANDWIDTH 0x00000080 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 @@ -649,6 +648,7 @@ struct uvc_device { struct urb *int_urb; __u8 *status; struct input_dev *input; + char input_phys[64]; /* Video Streaming interfaces */ struct list_head streaming; diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index b8f2be8..1da8cb8 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -334,6 +334,12 @@ const char **v4l2_ctrl_get_menu(u32 id) "Aperture Priority Mode", NULL }; + static const char *colorfx[] = { + "None", + "Black & White", + "Sepia", + NULL + }; switch (id) { case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: @@ -370,6 +376,8 @@ const char **v4l2_ctrl_get_menu(u32 id) return camera_power_line_frequency; case V4L2_CID_EXPOSURE_AUTO: return camera_exposure_auto; + case V4L2_CID_COLORFX: + return colorfx; default: return NULL; } @@ -382,16 +390,16 @@ const char *v4l2_ctrl_get_name(u32 id) switch (id) { /* USER controls */ case V4L2_CID_USER_CLASS: return "User Controls"; + case V4L2_CID_BRIGHTNESS: return "Brightness"; + case V4L2_CID_CONTRAST: return "Contrast"; + case V4L2_CID_SATURATION: return "Saturation"; + case V4L2_CID_HUE: return "Hue"; case V4L2_CID_AUDIO_VOLUME: return "Volume"; - case V4L2_CID_AUDIO_MUTE: return "Mute"; case V4L2_CID_AUDIO_BALANCE: return "Balance"; case V4L2_CID_AUDIO_BASS: return "Bass"; case V4L2_CID_AUDIO_TREBLE: return "Treble"; + case V4L2_CID_AUDIO_MUTE: return "Mute"; case V4L2_CID_AUDIO_LOUDNESS: return "Loudness"; - case V4L2_CID_BRIGHTNESS: return "Brightness"; - case V4L2_CID_CONTRAST: return "Contrast"; - case V4L2_CID_SATURATION: return "Saturation"; - case V4L2_CID_HUE: return "Hue"; case V4L2_CID_BLACK_LEVEL: return "Black Level"; case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic"; case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance"; @@ -412,6 +420,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation"; case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; case V4L2_CID_COLOR_KILLER: return "Color Killer"; + case V4L2_CID_COLORFX: return "Color Effects"; /* MPEG controls */ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; @@ -490,16 +499,25 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_HFLIP: case V4L2_CID_VFLIP: case V4L2_CID_HUE_AUTO: + case V4L2_CID_CHROMA_AGC: + case V4L2_CID_COLOR_KILLER: case V4L2_CID_MPEG_AUDIO_MUTE: case V4L2_CID_MPEG_VIDEO_MUTE: case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: case V4L2_CID_MPEG_VIDEO_PULLDOWN: case V4L2_CID_EXPOSURE_AUTO_PRIORITY: + case V4L2_CID_FOCUS_AUTO: case V4L2_CID_PRIVACY: qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; min = 0; max = step = 1; break; + case V4L2_CID_PAN_RESET: + case V4L2_CID_TILT_RESET: + qctrl->type = V4L2_CTRL_TYPE_BUTTON; + qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; + min = max = step = def = 0; + break; case V4L2_CID_POWER_LINE_FREQUENCY: case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: case V4L2_CID_MPEG_AUDIO_ENCODING: @@ -517,6 +535,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_STREAM_TYPE: case V4L2_CID_MPEG_STREAM_VBI_FMT: case V4L2_CID_EXPOSURE_AUTO: + case V4L2_CID_COLORFX: qctrl->type = V4L2_CTRL_TYPE_MENU; step = 1; break; @@ -547,161 +566,29 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_CONTRAST: case V4L2_CID_SATURATION: case V4L2_CID_HUE: + case V4L2_CID_RED_BALANCE: + case V4L2_CID_BLUE_BALANCE: + case V4L2_CID_GAMMA: + case V4L2_CID_SHARPNESS: qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; break; + case V4L2_CID_PAN_RELATIVE: + case V4L2_CID_TILT_RELATIVE: + case V4L2_CID_FOCUS_RELATIVE: + case V4L2_CID_ZOOM_RELATIVE: + qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY; + break; } qctrl->minimum = min; qctrl->maximum = max; qctrl->step = step; qctrl->default_value = def; qctrl->reserved[0] = qctrl->reserved[1] = 0; - snprintf(qctrl->name, sizeof(qctrl->name), name); + strlcpy(qctrl->name, name, sizeof(qctrl->name)); return 0; } EXPORT_SYMBOL(v4l2_ctrl_query_fill); -/* Fill in a struct v4l2_queryctrl with standard values based on - the control ID. */ -int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) -{ - switch (qctrl->id) { - /* USER controls */ - case V4L2_CID_USER_CLASS: - case V4L2_CID_MPEG_CLASS: - return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qctrl, 0, 65535, 65535 / 100, 58880); - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_LOUDNESS: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill(qctrl, 0, 65535, 65535 / 100, 32768); - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qctrl, 0, 127, 1, 64); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qctrl, -128, 127, 1, 0); - - /* MPEG controls */ - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1, - V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); - case V4L2_CID_MPEG_AUDIO_ENCODING: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_ENCODING_LAYER_1, - V4L2_MPEG_AUDIO_ENCODING_AC3, 1, - V4L2_MPEG_AUDIO_ENCODING_LAYER_2); - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_L1_BITRATE_32K, - V4L2_MPEG_AUDIO_L1_BITRATE_448K, 1, - V4L2_MPEG_AUDIO_L1_BITRATE_256K); - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_L2_BITRATE_32K, - V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, - V4L2_MPEG_AUDIO_L2_BITRATE_224K); - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_L3_BITRATE_32K, - V4L2_MPEG_AUDIO_L3_BITRATE_320K, 1, - V4L2_MPEG_AUDIO_L3_BITRATE_192K); - case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: - return v4l2_ctrl_query_fill(qctrl, 0, 6400, 1, 3200000); - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_AC3_BITRATE_32K, - V4L2_MPEG_AUDIO_AC3_BITRATE_640K, 1, - V4L2_MPEG_AUDIO_AC3_BITRATE_384K); - case V4L2_CID_MPEG_AUDIO_MODE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_MODE_STEREO, - V4L2_MPEG_AUDIO_MODE_MONO, 1, - V4L2_MPEG_AUDIO_MODE_STEREO); - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1, - V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4); - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_EMPHASIS_NONE, - V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1, - V4L2_MPEG_AUDIO_EMPHASIS_NONE); - case V4L2_CID_MPEG_AUDIO_CRC: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_AUDIO_CRC_NONE, - V4L2_MPEG_AUDIO_CRC_CRC16, 1, - V4L2_MPEG_AUDIO_CRC_NONE); - case V4L2_CID_MPEG_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - case V4L2_CID_MPEG_VIDEO_ENCODING: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2); - case V4L2_CID_MPEG_VIDEO_ASPECT: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_ASPECT_1x1, - V4L2_MPEG_VIDEO_ASPECT_221x100, 1, - V4L2_MPEG_VIDEO_ASPECT_4x3); - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, 12); - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1); - case V4L2_CID_MPEG_VIDEO_PULLDOWN: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, - V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); - case V4L2_CID_MPEG_VIDEO_BITRATE: - return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: - return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: - return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); - case V4L2_CID_MPEG_VIDEO_MUTE: - return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ - return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); - case V4L2_CID_MPEG_STREAM_TYPE: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS, - V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1, - V4L2_MPEG_STREAM_TYPE_MPEG2_PS); - case V4L2_CID_MPEG_STREAM_PID_PMT: - return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 16); - case V4L2_CID_MPEG_STREAM_PID_AUDIO: - return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 260); - case V4L2_CID_MPEG_STREAM_PID_VIDEO: - return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 256); - case V4L2_CID_MPEG_STREAM_PID_PCR: - return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 259); - case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: - return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); - case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: - return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); - case V4L2_CID_MPEG_STREAM_VBI_FMT: - return v4l2_ctrl_query_fill(qctrl, - V4L2_MPEG_STREAM_VBI_FMT_NONE, - V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1, - V4L2_MPEG_STREAM_VBI_FMT_NONE); - default: - return -EINVAL; - } -} -EXPORT_SYMBOL(v4l2_ctrl_query_fill_std); - /* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and the menu. The qctrl pointer may be NULL, in which case it is ignored. If menu_items is NULL, then the menu items are retrieved using @@ -720,7 +607,7 @@ int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qc for (i = 0; i < qmenu->index && menu_items[i]; i++) ; if (menu_items[i] == NULL || menu_items[i][0] == '\0') return -EINVAL; - snprintf(qmenu->name, sizeof(qmenu->name), menu_items[qmenu->index]); + strlcpy(qmenu->name, menu_items[qmenu->index], sizeof(qmenu->name)); return 0; } EXPORT_SYMBOL(v4l2_ctrl_query_menu); @@ -737,8 +624,8 @@ int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *id return -EINVAL; while (*ids != V4L2_CTRL_MENU_IDS_END) { if (*ids++ == qmenu->index) { - snprintf(qmenu->name, sizeof(qmenu->name), - menu_items[qmenu->index]); + strlcpy(qmenu->name, menu_items[qmenu->index], + sizeof(qmenu->name)); return 0; } } @@ -749,7 +636,7 @@ EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items); /* ctrl_classes points to an array of u32 pointers, the last element is a NULL pointer. Each u32 array is a 0-terminated array of control IDs. Each array must be sorted low to high and belong to the same control - class. The array of u32 pointer must also be sorted, from low class IDs + class. The array of u32 pointers must also be sorted, from low class IDs to high class IDs. This function returns the first ID that follows after the given ID. @@ -910,10 +797,10 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, struct i2c_board_info info; BUG_ON(!dev); -#ifdef MODULE + if (module_name) request_module(module_name); -#endif + /* Setup the i2c board info with the device type and the device address. */ memset(&info, 0, sizeof(info)); @@ -927,11 +814,11 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, We need better support from the kernel so that we can easily wait for the load to finish. */ if (client == NULL || client->driver == NULL) - return NULL; + goto error; /* Lock the module so we can safely get the v4l2_subdev pointer */ if (!try_module_get(client->driver->driver.owner)) - return NULL; + goto error; sd = i2c_get_clientdata(client); /* Register with the v4l2_device which increases the module's @@ -940,8 +827,13 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, sd = NULL; /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); - return sd; +error: + /* If we have a client but no subdev, then something went wrong and + we must unregister the client. */ + if (client && sd == NULL) + i2c_unregister_device(client); + return sd; } EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); @@ -958,10 +850,10 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, struct i2c_board_info info; BUG_ON(!dev); -#ifdef MODULE + if (module_name) request_module(module_name); -#endif + /* Setup the i2c board info with the device type and the device address. */ memset(&info, 0, sizeof(info)); @@ -974,11 +866,11 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, We need better support from the kernel so that we can easily wait for the load to finish. */ if (client == NULL || client->driver == NULL) - return NULL; + goto error; /* Lock the module so we can safely get the v4l2_subdev pointer */ if (!try_module_get(client->driver->driver.owner)) - return NULL; + goto error; sd = i2c_get_clientdata(client); /* Register with the v4l2_device which increases the module's @@ -987,8 +879,59 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, sd = NULL; /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); + +error: + /* If we have a client but no subdev, then something went wrong and + we must unregister the client. */ + if (client && sd == NULL) + i2c_unregister_device(client); return sd; } EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev); +/* Return i2c client address of v4l2_subdev. */ +unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return client ? client->addr : I2C_CLIENT_END; +} +EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr); + +/* Return a list of I2C tuner addresses to probe. Use only if the tuner + addresses are unknown. */ +const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type) +{ + static const unsigned short radio_addrs[] = { +#if defined(CONFIG_MEDIA_TUNER_TEA5761) || defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) + 0x10, +#endif + 0x60, + I2C_CLIENT_END + }; + static const unsigned short demod_addrs[] = { + 0x42, 0x43, 0x4a, 0x4b, + I2C_CLIENT_END + }; + static const unsigned short tv_addrs[] = { + 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + I2C_CLIENT_END + }; + + switch (type) { + case ADDRS_RADIO: + return radio_addrs; + case ADDRS_DEMOD: + return demod_addrs; + case ADDRS_TV: + return tv_addrs; + case ADDRS_TV_WITH_DEMOD: + return tv_addrs + 4; + } + return NULL; +} +EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs); + #endif diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 110376b..0056b11 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -1047,7 +1047,6 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_DBG_S_REGISTER: case VIDIOC_DBG_G_REGISTER: case VIDIOC_DBG_G_CHIP_IDENT: - case VIDIOC_G_CHIP_IDENT_OLD: case VIDIOC_S_HW_FREQ_SEEK: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 13f87c2..91228b3 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -198,6 +198,23 @@ static long v4l2_unlocked_ioctl(struct file *filp, return vdev->fops->unlocked_ioctl(filp, cmd, arg); } +#ifdef CONFIG_MMU +#define v4l2_get_unmapped_area NULL +#else +static unsigned long v4l2_get_unmapped_area(struct file *filp, + unsigned long addr, unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->get_unmapped_area) + return -ENOSYS; + if (video_is_unregistered(vdev)) + return -ENODEV; + return vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags); +} +#endif + static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) { struct video_device *vdev = video_devdata(filp); @@ -250,6 +267,7 @@ static const struct file_operations v4l2_unlocked_fops = { .read = v4l2_read, .write = v4l2_write, .open = v4l2_open, + .get_unmapped_area = v4l2_get_unmapped_area, .mmap = v4l2_mmap, .unlocked_ioctl = v4l2_unlocked_ioctl, #ifdef CONFIG_COMPAT @@ -265,6 +283,7 @@ static const struct file_operations v4l2_fops = { .read = v4l2_read, .write = v4l2_write, .open = v4l2_open, + .get_unmapped_area = v4l2_get_unmapped_area, .mmap = v4l2_mmap, .ioctl = v4l2_ioctl, #ifdef CONFIG_COMPAT @@ -288,37 +307,38 @@ static const struct file_operations v4l2_fops = { */ static int get_index(struct video_device *vdev, int num) { - u32 used = 0; - const int max_index = sizeof(used) * 8 - 1; + /* This can be static since this function is called with the global + videodev_lock held. */ + static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES); int i; - /* Currently a single v4l driver instance cannot create more than - 32 devices. - Increase to u64 or an array of u32 if more are needed. */ - if (num > max_index) { + if (num >= VIDEO_NUM_DEVICES) { printk(KERN_ERR "videodev: %s num is too large\n", __func__); return -EINVAL; } - /* Some drivers do not set the parent. In that case always return 0. */ + /* Some drivers do not set the parent. In that case always return + num or 0. */ if (vdev->parent == NULL) - return 0; + return num >= 0 ? num : 0; + + bitmap_zero(used, VIDEO_NUM_DEVICES); for (i = 0; i < VIDEO_NUM_DEVICES; i++) { if (video_device[i] != NULL && video_device[i]->parent == vdev->parent) { - used |= 1 << video_device[i]->index; + set_bit(video_device[i]->index, used); } } if (num >= 0) { - if (used & (1 << num)) + if (test_bit(num, used)) return -ENFILE; return num; } - i = ffz(used); - return i > max_index ? -ENFILE : i; + i = find_first_zero_bit(used, VIDEO_NUM_DEVICES); + return i == VIDEO_NUM_DEVICES ? -ENFILE : i; } int video_register_device(struct video_device *vdev, int type, int nr) @@ -365,12 +385,11 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, /* A minor value of -1 marks this video device as never having been registered */ - if (vdev) - vdev->minor = -1; + vdev->minor = -1; /* the release callback MUST be present */ - WARN_ON(!vdev || !vdev->release); - if (!vdev || !vdev->release) + WARN_ON(!vdev->release); + if (!vdev->release) return -EINVAL; /* Part 1: check device type */ @@ -395,7 +414,7 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, vdev->vfl_type = type; vdev->cdev = NULL; - if (vdev->v4l2_dev) + if (vdev->v4l2_dev && vdev->v4l2_dev->dev) vdev->parent = vdev->v4l2_dev->dev; /* Part 2: find a free minor, kernel number and device index. */ @@ -582,6 +601,7 @@ module_exit(videodev_exit) MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2"); MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR); /* diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index cf9d4c7..94aa485 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -26,48 +26,66 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) { - if (dev == NULL || v4l2_dev == NULL) + if (v4l2_dev == NULL) return -EINVAL; - /* Warn if we apparently re-register a device */ - WARN_ON(dev_get_drvdata(dev) != NULL); + INIT_LIST_HEAD(&v4l2_dev->subdevs); spin_lock_init(&v4l2_dev->lock); v4l2_dev->dev = dev; - snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", - dev->driver->name, dev->bus_id); + if (dev == NULL) { + /* If dev == NULL, then name must be filled in by the caller */ + WARN_ON(!v4l2_dev->name[0]); + return 0; + } + + /* Set name to driver name + device name if it is empty. */ + if (!v4l2_dev->name[0]) + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", + dev->driver->name, dev_name(dev)); + if (dev_get_drvdata(dev)) + v4l2_warn(v4l2_dev, "Non-NULL drvdata on register\n"); dev_set_drvdata(dev, v4l2_dev); return 0; } EXPORT_SYMBOL_GPL(v4l2_device_register); +void v4l2_device_disconnect(struct v4l2_device *v4l2_dev) +{ + if (v4l2_dev->dev) { + dev_set_drvdata(v4l2_dev->dev, NULL); + v4l2_dev->dev = NULL; + } +} +EXPORT_SYMBOL_GPL(v4l2_device_disconnect); + void v4l2_device_unregister(struct v4l2_device *v4l2_dev) { struct v4l2_subdev *sd, *next; - if (v4l2_dev == NULL || v4l2_dev->dev == NULL) + if (v4l2_dev == NULL) return; - dev_set_drvdata(v4l2_dev->dev, NULL); - /* unregister subdevs */ + v4l2_device_disconnect(v4l2_dev); + + /* Unregister subdevs */ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) v4l2_device_unregister_subdev(sd); - - v4l2_dev->dev = NULL; } EXPORT_SYMBOL_GPL(v4l2_device_unregister); -int v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd) +int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, + struct v4l2_subdev *sd) { /* Check for valid input */ - if (dev == NULL || sd == NULL || !sd->name[0]) + if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) return -EINVAL; /* Warn if we apparently re-register a subdev */ - WARN_ON(sd->dev != NULL); + WARN_ON(sd->v4l2_dev != NULL); if (!try_module_get(sd->owner)) return -ENODEV; - sd->dev = dev; - spin_lock(&dev->lock); - list_add_tail(&sd->list, &dev->subdevs); - spin_unlock(&dev->lock); + sd->v4l2_dev = v4l2_dev; + spin_lock(&v4l2_dev->lock); + list_add_tail(&sd->list, &v4l2_dev->subdevs); + spin_unlock(&v4l2_dev->lock); return 0; } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); @@ -75,12 +93,12 @@ EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) { /* return if it isn't registered */ - if (sd == NULL || sd->dev == NULL) + if (sd == NULL || sd->v4l2_dev == NULL) return; - spin_lock(&sd->dev->lock); + spin_lock(&sd->v4l2_dev->lock); list_del(&sd->list); - spin_unlock(&sd->dev->lock); - sd->dev = NULL; + spin_unlock(&sd->v4l2_dev->lock); + sd->v4l2_dev = NULL; module_put(sd->owner); } EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 52d687b..f41c6f5 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -17,6 +17,7 @@ #include <linux/kernel.h> #define __OLD_VIDIOC_ /* To allow fixing old calls */ +#include <linux/videodev.h> #include <linux/videodev2.h> #ifdef CONFIG_VIDEO_V4L1 @@ -24,7 +25,7 @@ #endif #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#include <linux/video_decoder.h> +#include <media/v4l2-chip-ident.h> #define dbgarg(cmd, fmt, arg...) \ do { \ @@ -100,25 +101,27 @@ const char *v4l2_norm_to_name(v4l2_std_id id) } EXPORT_SYMBOL(v4l2_norm_to_name); +/* Returns frame period for the given standard */ +void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod) +{ + if (id & V4L2_STD_525_60) { + frameperiod->numerator = 1001; + frameperiod->denominator = 30000; + } else { + frameperiod->numerator = 1; + frameperiod->denominator = 25; + } +} +EXPORT_SYMBOL(v4l2_video_std_frame_period); + /* Fill in the fields of a v4l2_standard structure according to the 'id' and 'transmission' parameters. Returns negative on error. */ int v4l2_video_std_construct(struct v4l2_standard *vs, int id, const char *name) { - u32 index = vs->index; - - memset(vs, 0, sizeof(struct v4l2_standard)); - vs->index = index; - vs->id = id; - if (id & V4L2_STD_525_60) { - vs->frameperiod.numerator = 1001; - vs->frameperiod.denominator = 30000; - vs->framelines = 525; - } else { - vs->frameperiod.numerator = 1; - vs->frameperiod.denominator = 25; - vs->framelines = 625; - } + vs->id = id; + v4l2_video_std_frame_period(id, &vs->frameperiod); + vs->framelines = (id & V4L2_STD_525_60) ? 525 : 625; strlcpy(vs->name, name, sizeof(vs->name)); return 0; } @@ -273,19 +276,6 @@ static const char *v4l2_ioctls[] = { #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) static const char *v4l2_int_ioctls[] = { -#ifdef CONFIG_VIDEO_V4L1_COMPAT - [_IOC_NR(DECODER_GET_CAPABILITIES)] = "DECODER_GET_CAPABILITIES", - [_IOC_NR(DECODER_GET_STATUS)] = "DECODER_GET_STATUS", - [_IOC_NR(DECODER_SET_NORM)] = "DECODER_SET_NORM", - [_IOC_NR(DECODER_SET_INPUT)] = "DECODER_SET_INPUT", - [_IOC_NR(DECODER_SET_OUTPUT)] = "DECODER_SET_OUTPUT", - [_IOC_NR(DECODER_ENABLE_OUTPUT)] = "DECODER_ENABLE_OUTPUT", - [_IOC_NR(DECODER_SET_PICTURE)] = "DECODER_SET_PICTURE", - [_IOC_NR(DECODER_SET_GPIO)] = "DECODER_SET_GPIO", - [_IOC_NR(DECODER_INIT)] = "DECODER_INIT", - [_IOC_NR(DECODER_SET_VBI_BYPASS)] = "DECODER_SET_VBI_BYPASS", - [_IOC_NR(DECODER_DUMP)] = "DECODER_DUMP", -#endif [_IOC_NR(AUDC_SET_RADIO)] = "AUDC_SET_RADIO", [_IOC_NR(TUNER_SET_TYPE_ADDR)] = "TUNER_SET_TYPE_ADDR", @@ -654,8 +644,6 @@ static long __video_do_ioctl(struct file *file, if (cmd == VIDIOCGMBUF) { struct video_mbuf *p = arg; - memset(p, 0, sizeof(*p)); - if (!ops->vidiocgmbuf) return ret; ret = ops->vidiocgmbuf(file, fh, p); @@ -682,7 +670,6 @@ static long __video_do_ioctl(struct file *file, case VIDIOC_QUERYCAP: { struct v4l2_capability *cap = (struct v4l2_capability *)arg; - memset(cap, 0, sizeof(*cap)); if (!ops->vidioc_querycap) break; @@ -725,16 +712,8 @@ static long __video_do_ioctl(struct file *file, case VIDIOC_ENUM_FMT: { struct v4l2_fmtdesc *f = arg; - enum v4l2_buf_type type; - unsigned int index; - - index = f->index; - type = f->type; - memset(f, 0, sizeof(*f)); - f->index = index; - f->type = type; - switch (type) { + switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (ops->vidioc_enum_fmt_vid_cap) ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f); @@ -771,8 +750,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_format *f = (struct v4l2_format *)arg; - memset(f->fmt.raw_data, 0, sizeof(f->fmt.raw_data)); - /* FIXME: Should be one dump per type */ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); @@ -1088,7 +1065,6 @@ static long __video_do_ioctl(struct file *file, return -EINVAL; v4l2_video_std_construct(p, curr_id, descr); - p->index = index; dbgarg(cmd, "index=%d, id=0x%Lx, name=%s, fps=%d/%d, " "framelines=%d\n", p->index, @@ -1153,12 +1129,9 @@ static long __video_do_ioctl(struct file *file, case VIDIOC_ENUMINPUT: { struct v4l2_input *p = arg; - int i = p->index; if (!ops->vidioc_enum_input) break; - memset(p, 0, sizeof(*p)); - p->index = i; ret = ops->vidioc_enum_input(file, fh, p); if (!ret) @@ -1197,12 +1170,9 @@ static long __video_do_ioctl(struct file *file, case VIDIOC_ENUMOUTPUT: { struct v4l2_output *p = arg; - int i = p->index; if (!ops->vidioc_enum_output) break; - memset(p, 0, sizeof(*p)); - p->index = i; ret = ops->vidioc_enum_output(file, fh, p); if (!ret) @@ -1378,13 +1348,10 @@ static long __video_do_ioctl(struct file *file, case VIDIOC_G_AUDIO: { struct v4l2_audio *p = arg; - __u32 index = p->index; if (!ops->vidioc_g_audio) break; - memset(p, 0, sizeof(*p)); - p->index = index; ret = ops->vidioc_g_audio(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " @@ -1426,7 +1393,7 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_g_audout) break; - dbgarg(cmd, "Enum for index=%d\n", p->index); + ret = ops->vidioc_g_audout(file, fh, p); if (!ret) dbgarg2("index=%d, name=%s, capability=%d, " @@ -1479,15 +1446,10 @@ static long __video_do_ioctl(struct file *file, case VIDIOC_G_CROP: { struct v4l2_crop *p = arg; - __u32 type; if (!ops->vidioc_g_crop) break; - type = p->type; - memset(p, 0, sizeof(*p)); - p->type = type; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); ret = ops->vidioc_g_crop(file, fh, p); if (!ret) @@ -1508,16 +1470,11 @@ static long __video_do_ioctl(struct file *file, case VIDIOC_CROPCAP: { struct v4l2_cropcap *p = arg; - __u32 type; /*FIXME: Should also show v4l2_fract pixelaspect */ if (!ops->vidioc_cropcap) break; - type = p->type; - memset(p, 0, sizeof(*p)); - p->type = type; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); ret = ops->vidioc_cropcap(file, fh, p); if (!ret) { @@ -1533,8 +1490,6 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_g_jpegcomp) break; - memset(p, 0, sizeof(*p)); - ret = ops->vidioc_g_jpegcomp(file, fh, p); if (!ret) dbgarg(cmd, "quality=%d, APPn=%d, " @@ -1575,7 +1530,6 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_encoder_cmd) break; - memset(&p->raw, 0, sizeof(p->raw)); ret = ops->vidioc_encoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1587,7 +1541,6 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_try_encoder_cmd) break; - memset(&p->raw, 0, sizeof(p->raw)); ret = ops->vidioc_try_encoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1596,23 +1549,18 @@ static long __video_do_ioctl(struct file *file, case VIDIOC_G_PARM: { struct v4l2_streamparm *p = arg; - __u32 type = p->type; - - memset(p, 0, sizeof(*p)); - p->type = type; if (ops->vidioc_g_parm) { + ret = check_fmt(ops, p->type); + if (ret) + break; ret = ops->vidioc_g_parm(file, fh, p); } else { - struct v4l2_standard s; - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - v4l2_video_std_construct(&s, vfd->current_norm, - v4l2_norm_to_name(vfd->current_norm)); - - p->parm.capture.timeperframe = s.frameperiod; + v4l2_video_std_frame_period(vfd->current_norm, + &p->parm.capture.timeperframe); ret = 0; } @@ -1625,6 +1573,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_parm) break; + ret = check_fmt(ops, p->type); + if (ret) + break; + dbgarg(cmd, "type=%d\n", p->type); ret = ops->vidioc_s_parm(file, fh, p); break; @@ -1632,14 +1584,10 @@ static long __video_do_ioctl(struct file *file, case VIDIOC_G_TUNER: { struct v4l2_tuner *p = arg; - __u32 index = p->index; if (!ops->vidioc_g_tuner) break; - memset(p, 0, sizeof(*p)); - p->index = index; - ret = ops->vidioc_g_tuner(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, type=%d, " @@ -1676,8 +1624,6 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_g_frequency) break; - memset(p->reserved, 0, sizeof(p->reserved)); - ret = ops->vidioc_g_frequency(file, fh, p); if (!ret) dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", @@ -1698,12 +1644,13 @@ static long __video_do_ioctl(struct file *file, case VIDIOC_G_SLICED_VBI_CAP: { struct v4l2_sliced_vbi_cap *p = arg; - __u32 type = p->type; if (!ops->vidioc_g_sliced_vbi_cap) break; - memset(p, 0, sizeof(*p)); - p->type = type; + + /* Clear up to type, everything after type is zerod already */ + memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type)); + dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); ret = ops->vidioc_g_sliced_vbi_cap(file, fh, p); if (!ret) @@ -1745,16 +1692,13 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_g_chip_ident) break; + p->ident = V4L2_IDENT_NONE; + p->revision = 0; ret = ops->vidioc_g_chip_ident(file, fh, p); if (!ret) dbgarg(cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision); break; } - case VIDIOC_G_CHIP_IDENT_OLD: - printk(KERN_ERR "VIDIOC_G_CHIP_IDENT has been deprecated and will disappear in 2.6.30.\n"); - printk(KERN_ERR "It is a debugging ioctl and must not be used in applications!\n"); - return -EINVAL; - case VIDIOC_S_HW_FREQ_SEEK: { struct v4l2_hw_freq_seek *p = arg; @@ -1774,8 +1718,6 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_enum_framesizes) break; - memset(p, 0, sizeof(*p)); - ret = ops->vidioc_enum_framesizes(file, fh, p); dbgarg(cmd, "index=%d, pixelformat=%d, type=%d ", @@ -1807,8 +1749,6 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_enum_frameintervals) break; - memset(p, 0, sizeof(*p)); - ret = ops->vidioc_enum_frameintervals(file, fh, p); dbgarg(cmd, "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ", @@ -1857,6 +1797,45 @@ static long __video_do_ioctl(struct file *file, return ret; } +/* In some cases, only a few fields are used as input, i.e. when the app sets + * "index" and then the driver fills in the rest of the structure for the thing + * with that index. We only need to copy up the first non-input field. */ +static unsigned long cmd_input_size(unsigned int cmd) +{ + /* Size of structure up to and including 'field' */ +#define CMDINSIZE(cmd, type, field) \ + case VIDIOC_##cmd: \ + return offsetof(struct v4l2_##type, field) + \ + sizeof(((struct v4l2_##type *)0)->field); + + switch (cmd) { + CMDINSIZE(ENUM_FMT, fmtdesc, type); + CMDINSIZE(G_FMT, format, type); + CMDINSIZE(QUERYBUF, buffer, type); + CMDINSIZE(G_PARM, streamparm, type); + CMDINSIZE(ENUMSTD, standard, index); + CMDINSIZE(ENUMINPUT, input, index); + CMDINSIZE(G_CTRL, control, id); + CMDINSIZE(G_TUNER, tuner, index); + CMDINSIZE(QUERYCTRL, queryctrl, id); + CMDINSIZE(QUERYMENU, querymenu, index); + CMDINSIZE(ENUMOUTPUT, output, index); + CMDINSIZE(G_MODULATOR, modulator, index); + CMDINSIZE(G_FREQUENCY, frequency, tuner); + CMDINSIZE(CROPCAP, cropcap, type); + CMDINSIZE(G_CROP, crop, type); + CMDINSIZE(ENUMAUDIO, audio, index); + CMDINSIZE(ENUMAUDOUT, audioout, index); + CMDINSIZE(ENCODER_CMD, encoder_cmd, flags); + CMDINSIZE(TRY_ENCODER_CMD, encoder_cmd, flags); + CMDINSIZE(G_SLICED_VBI_CAP, sliced_vbi_cap, type); + CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format); + CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height); + default: + return _IOC_SIZE(cmd); + } +} + long video_ioctl2(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1875,13 +1854,7 @@ long video_ioctl2(struct file *file, cmd == VIDIOC_TRY_EXT_CTRLS); /* Copy arguments into temp kernel buffer */ - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: - parg = NULL; - break; - case _IOC_READ: - case _IOC_WRITE: - case (_IOC_WRITE | _IOC_READ): + if (_IOC_DIR(cmd) != _IOC_NONE) { if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { parg = sbuf; } else { @@ -1893,10 +1866,19 @@ long video_ioctl2(struct file *file, } err = -EFAULT; - if (_IOC_DIR(cmd) & _IOC_WRITE) - if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) + if (_IOC_DIR(cmd) & _IOC_WRITE) { + unsigned long n = cmd_input_size(cmd); + + if (copy_from_user(parg, (void __user *)arg, n)) goto out; - break; + + /* zero out anything we don't copy from userspace */ + if (n < _IOC_SIZE(cmd)) + memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n); + } else { + /* read-only ioctl */ + memset(parg, 0, _IOC_SIZE(cmd)); + } } if (is_ext_ctrl) { diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 2120880..dc88167 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -33,6 +33,12 @@ int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) return v4l2_subdev_call(sd, core, g_ctrl, arg); case VIDIOC_S_CTRL: return v4l2_subdev_call(sd, core, s_ctrl, arg); + case VIDIOC_G_EXT_CTRLS: + return v4l2_subdev_call(sd, core, g_ext_ctrls, arg); + case VIDIOC_S_EXT_CTRLS: + return v4l2_subdev_call(sd, core, s_ext_ctrls, arg); + case VIDIOC_TRY_EXT_CTRLS: + return v4l2_subdev_call(sd, core, try_ext_ctrls, arg); case VIDIOC_QUERYMENU: return v4l2_subdev_call(sd, core, querymenu, arg); case VIDIOC_LOG_STATUS: @@ -92,16 +98,28 @@ int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) return v4l2_subdev_call(sd, video, g_vbi_data, arg); case VIDIOC_G_SLICED_VBI_CAP: return v4l2_subdev_call(sd, video, g_sliced_vbi_cap, arg); + case VIDIOC_ENUM_FMT: + return v4l2_subdev_call(sd, video, enum_fmt, arg); + case VIDIOC_TRY_FMT: + return v4l2_subdev_call(sd, video, try_fmt, arg); case VIDIOC_S_FMT: return v4l2_subdev_call(sd, video, s_fmt, arg); case VIDIOC_G_FMT: return v4l2_subdev_call(sd, video, g_fmt, arg); case VIDIOC_INT_S_STD_OUTPUT: return v4l2_subdev_call(sd, video, s_std_output, *(v4l2_std_id *)arg); + case VIDIOC_QUERYSTD: + return v4l2_subdev_call(sd, video, querystd, arg); + case VIDIOC_INT_G_INPUT_STATUS: + return v4l2_subdev_call(sd, video, g_input_status, arg); case VIDIOC_STREAMON: return v4l2_subdev_call(sd, video, s_stream, 1); case VIDIOC_STREAMOFF: return v4l2_subdev_call(sd, video, s_stream, 0); + case VIDIOC_S_PARM: + return v4l2_subdev_call(sd, video, s_parm, arg); + case VIDIOC_G_PARM: + return v4l2_subdev_call(sd, video, g_parm, arg); default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index 31944b1..6109fb5 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -400,7 +400,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q, So, it should free memory only if the memory were allocated for read() operation. */ - if ((buf->memory != V4L2_MEMORY_USERPTR) || !buf->baddr) + if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr) return; if (!mem) diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index be65a2f..30ae30f 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -425,7 +425,7 @@ void videobuf_vmalloc_free (struct videobuf_buffer *buf) So, it should free memory only if the memory were allocated for read() operation. */ - if ((buf->memory != V4L2_MEMORY_USERPTR) || (buf->baddr == 0)) + if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr) return; if (!mem) diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index 88bf845..8da4dd1 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -8,6 +8,12 @@ * * Based on the previous version of the driver for 2.4 kernels by: * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> + * + * v4l2_device/v4l2_subdev conversion by: + * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl> + * + * Note: this conversion is untested! Please contact the linux-media + * mailinglist if you can test this, together with the test results. */ /* @@ -33,12 +39,10 @@ #include <linux/kmod.h> #include <linux/i2c.h> -#include <linux/i2c-algo-sgi.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> -#include <linux/video_decoder.h> #include <linux/mutex.h> #include <asm/paccess.h> @@ -139,13 +143,23 @@ MODULE_LICENSE("GPL"); #define VINO_DATA_NORM_PAL 1 #define VINO_DATA_NORM_SECAM 2 #define VINO_DATA_NORM_D1 3 -/* The following are special entries that can be used to - * autodetect the norm. */ -#define VINO_DATA_NORM_AUTO 0xfe -#define VINO_DATA_NORM_AUTO_EXT 0xff #define VINO_DATA_NORM_COUNT 4 +/* I2C controller flags */ +#define SGI_I2C_FORCE_IDLE (0 << 0) +#define SGI_I2C_NOT_IDLE (1 << 0) +#define SGI_I2C_WRITE (0 << 1) +#define SGI_I2C_READ (1 << 1) +#define SGI_I2C_RELEASE_BUS (0 << 2) +#define SGI_I2C_HOLD_BUS (1 << 2) +#define SGI_I2C_XFER_DONE (0 << 4) +#define SGI_I2C_XFER_BUSY (1 << 4) +#define SGI_I2C_ACK (0 << 5) +#define SGI_I2C_NACK (1 << 5) +#define SGI_I2C_BUS_OK (0 << 7) +#define SGI_I2C_BUS_ERR (1 << 7) + /* Internal data structure definitions */ struct vino_input { @@ -289,22 +303,20 @@ struct vino_channel_settings { struct vino_interrupt_data int_data; /* V4L support */ - struct video_device *v4l_device; -}; - -struct vino_client { - /* the channel which owns this client: - * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */ - unsigned int owner; - struct i2c_client *driver; + struct video_device *vdev; }; struct vino_settings { + struct v4l2_device v4l2_dev; struct vino_channel_settings a; struct vino_channel_settings b; - struct vino_client decoder; - struct vino_client camera; + /* the channel which owns this client: + * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */ + unsigned int decoder_owner; + struct v4l2_subdev *decoder; + unsigned int camera_owner; + struct v4l2_subdev *camera; /* a lock for vino register access */ spinlock_t vino_lock; @@ -344,11 +356,16 @@ static struct sgi_vino *vino; static struct vino_settings *vino_drvdata; +#define camera_call(o, f, args...) \ + v4l2_subdev_call(vino_drvdata->camera, o, f, ##args) +#define decoder_call(o, f, args...) \ + v4l2_subdev_call(vino_drvdata->decoder, o, f, ##args) + static const char *vino_driver_name = "vino"; static const char *vino_driver_description = "SGI VINO"; static const char *vino_bus_name = "GIO64 bus"; -static const char *vino_v4l_device_name_a = "SGI VINO Channel A"; -static const char *vino_v4l_device_name_b = "SGI VINO Channel B"; +static const char *vino_vdev_name_a = "SGI VINO Channel A"; +static const char *vino_vdev_name_b = "SGI VINO Channel B"; static void vino_capture_tasklet(unsigned long channel); @@ -360,11 +377,11 @@ static const struct vino_input vino_inputs[] = { .name = "Composite", .std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - },{ + }, { .name = "S-Video", .std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, - },{ + }, { .name = "D1/IndyCam", .std = V4L2_STD_NTSC, } @@ -376,17 +393,17 @@ static const struct vino_data_format vino_data_formats[] = { .bpp = 1, .pixelformat = V4L2_PIX_FMT_GREY, .colorspace = V4L2_COLORSPACE_SMPTE170M, - },{ + }, { .description = "8-bit dithered RGB 3-3-2", .bpp = 1, .pixelformat = V4L2_PIX_FMT_RGB332, .colorspace = V4L2_COLORSPACE_SRGB, - },{ + }, { .description = "32-bit RGB", .bpp = 4, .pixelformat = V4L2_PIX_FMT_RGB32, .colorspace = V4L2_COLORSPACE_SRGB, - },{ + }, { .description = "YUV 4:2:2", .bpp = 2, .pixelformat = V4L2_PIX_FMT_YUYV, // XXX: swapped? @@ -417,7 +434,7 @@ static const struct vino_data_norm vino_data_norms[] = { + VINO_NTSC_HEIGHT / 2 - 1, .right = VINO_NTSC_WIDTH, }, - },{ + }, { .description = "PAL", .std = V4L2_STD_PAL, .fps_min = 5, @@ -439,7 +456,7 @@ static const struct vino_data_norm vino_data_norms[] = { + VINO_PAL_HEIGHT / 2 - 1, .right = VINO_PAL_WIDTH, }, - },{ + }, { .description = "SECAM", .std = V4L2_STD_SECAM, .fps_min = 5, @@ -461,7 +478,7 @@ static const struct vino_data_norm vino_data_norms[] = { + VINO_PAL_HEIGHT / 2 - 1, .right = VINO_PAL_WIDTH, }, - },{ + }, { .description = "NTSC/D1", .std = V4L2_STD_NTSC, .fps_min = 6, @@ -497,9 +514,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = 1, .step = 1, .default_value = INDYCAM_AGC_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_AGC, 0 }, - },{ + }, { .id = V4L2_CID_AUTO_WHITE_BALANCE, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "Automatic White Balance", @@ -507,9 +522,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = 1, .step = 1, .default_value = INDYCAM_AWB_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_AWB, 0 }, - },{ + }, { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", @@ -517,29 +530,23 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = INDYCAM_GAIN_MAX, .step = 1, .default_value = INDYCAM_GAIN_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_GAIN, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE, + }, { + .id = INDYCAM_CONTROL_RED_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Red Saturation", .minimum = INDYCAM_RED_SATURATION_MIN, .maximum = INDYCAM_RED_SATURATION_MAX, .step = 1, .default_value = INDYCAM_RED_SATURATION_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_RED_SATURATION, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 1, + }, { + .id = INDYCAM_CONTROL_BLUE_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Blue Saturation", .minimum = INDYCAM_BLUE_SATURATION_MIN, .maximum = INDYCAM_BLUE_SATURATION_MAX, .step = 1, .default_value = INDYCAM_BLUE_SATURATION_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_BLUE_SATURATION, 0 }, - },{ + }, { .id = V4L2_CID_RED_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Red Balance", @@ -547,9 +554,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = INDYCAM_RED_BALANCE_MAX, .step = 1, .default_value = INDYCAM_RED_BALANCE_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_RED_BALANCE, 0 }, - },{ + }, { .id = V4L2_CID_BLUE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Blue Balance", @@ -557,9 +562,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = INDYCAM_BLUE_BALANCE_MAX, .step = 1, .default_value = INDYCAM_BLUE_BALANCE_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_BLUE_BALANCE, 0 }, - },{ + }, { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Shutter Control", @@ -567,9 +570,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = INDYCAM_SHUTTER_MAX, .step = 1, .default_value = INDYCAM_SHUTTER_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_SHUTTER, 0 }, - },{ + }, { .id = V4L2_CID_GAMMA, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gamma", @@ -577,8 +578,6 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { .maximum = INDYCAM_GAMMA_MAX, .step = 1, .default_value = INDYCAM_GAMMA_DEFAULT, - .flags = 0, - .reserved = { INDYCAM_CONTROL_GAMMA, 0 }, } }; @@ -593,209 +592,73 @@ struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = { .maximum = SAA7191_HUE_MAX, .step = 1, .default_value = SAA7191_HUE_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_HUE, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE, + }, { + .id = SAA7191_CONTROL_BANDPASS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Luminance Bandpass", .minimum = SAA7191_BANDPASS_MIN, .maximum = SAA7191_BANDPASS_MAX, .step = 1, .default_value = SAA7191_BANDPASS_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_BANDPASS, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 1, + }, { + .id = SAA7191_CONTROL_BANDPASS_WEIGHT, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Luminance Bandpass Weight", .minimum = SAA7191_BANDPASS_WEIGHT_MIN, .maximum = SAA7191_BANDPASS_WEIGHT_MAX, .step = 1, .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_BANDPASS_WEIGHT, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 2, + }, { + .id = SAA7191_CONTROL_CORING, .type = V4L2_CTRL_TYPE_INTEGER, .name = "HF Luminance Coring", .minimum = SAA7191_CORING_MIN, .maximum = SAA7191_CORING_MAX, .step = 1, .default_value = SAA7191_CORING_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_CORING, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 3, + }, { + .id = SAA7191_CONTROL_FORCE_COLOUR, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "Force Colour", .minimum = SAA7191_FORCE_COLOUR_MIN, .maximum = SAA7191_FORCE_COLOUR_MAX, .step = 1, .default_value = SAA7191_FORCE_COLOUR_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_FORCE_COLOUR, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 4, + }, { + .id = SAA7191_CONTROL_CHROMA_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Chrominance Gain Control", .minimum = SAA7191_CHROMA_GAIN_MIN, .maximum = SAA7191_CHROMA_GAIN_MAX, .step = 1, .default_value = SAA7191_CHROMA_GAIN_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_CHROMA_GAIN, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 5, + }, { + .id = SAA7191_CONTROL_VTRC, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "VTR Time Constant", .minimum = SAA7191_VTRC_MIN, .maximum = SAA7191_VTRC_MAX, .step = 1, .default_value = SAA7191_VTRC_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_VTRC, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 6, + }, { + .id = SAA7191_CONTROL_LUMA_DELAY, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Luminance Delay Compensation", .minimum = SAA7191_LUMA_DELAY_MIN, .maximum = SAA7191_LUMA_DELAY_MAX, .step = 1, .default_value = SAA7191_LUMA_DELAY_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_LUMA_DELAY, 0 }, - },{ - .id = V4L2_CID_PRIVATE_BASE + 7, + }, { + .id = SAA7191_CONTROL_VNR, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Vertical Noise Reduction", .minimum = SAA7191_VNR_MIN, .maximum = SAA7191_VNR_MAX, .step = 1, .default_value = SAA7191_VNR_DEFAULT, - .flags = 0, - .reserved = { SAA7191_CONTROL_VNR, 0 }, } }; -/* VINO I2C bus functions */ - -unsigned i2c_vino_getctrl(void *data) -{ - return vino->i2c_control; -} - -void i2c_vino_setctrl(void *data, unsigned val) -{ - vino->i2c_control = val; -} - -unsigned i2c_vino_rdata(void *data) -{ - return vino->i2c_data; -} - -void i2c_vino_wdata(void *data, unsigned val) -{ - vino->i2c_data = val; -} - -static struct i2c_algo_sgi_data i2c_sgi_vino_data = -{ - .getctrl = &i2c_vino_getctrl, - .setctrl = &i2c_vino_setctrl, - .rdata = &i2c_vino_rdata, - .wdata = &i2c_vino_wdata, - .xfer_timeout = 200, - .ack_timeout = 1000, -}; - -/* - * There are two possible clients on VINO I2C bus, so we limit usage only - * to them. - */ -static int i2c_vino_client_reg(struct i2c_client *client) -{ - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - switch (client->driver->id) { - case I2C_DRIVERID_SAA7191: - if (vino_drvdata->decoder.driver) - ret = -EBUSY; - else - vino_drvdata->decoder.driver = client; - break; - case I2C_DRIVERID_INDYCAM: - if (vino_drvdata->camera.driver) - ret = -EBUSY; - else - vino_drvdata->camera.driver = client; - break; - default: - ret = -ENODEV; - } - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return ret; -} - -static int i2c_vino_client_unreg(struct i2c_client *client) -{ - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - if (client == vino_drvdata->decoder.driver) { - if (vino_drvdata->decoder.owner != VINO_NO_CHANNEL) - ret = -EBUSY; - else - vino_drvdata->decoder.driver = NULL; - } else if (client == vino_drvdata->camera.driver) { - if (vino_drvdata->camera.owner != VINO_NO_CHANNEL) - ret = -EBUSY; - else - vino_drvdata->camera.driver = NULL; - } - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return ret; -} - -static struct i2c_adapter vino_i2c_adapter = -{ - .name = "VINO I2C bus", - .id = I2C_HW_SGI_VINO, - .algo_data = &i2c_sgi_vino_data, - .client_register = &i2c_vino_client_reg, - .client_unregister = &i2c_vino_client_unreg, -}; - -static int vino_i2c_add_bus(void) -{ - return i2c_sgi_add_bus(&vino_i2c_adapter); -} - -static int vino_i2c_del_bus(void) -{ - return i2c_del_adapter(&vino_i2c_adapter); -} - -static int i2c_camera_command(unsigned int cmd, void *arg) -{ - return vino_drvdata->camera.driver-> - driver->command(vino_drvdata->camera.driver, - cmd, arg); -} - -static int i2c_decoder_command(unsigned int cmd, void *arg) -{ - return vino_drvdata->decoder.driver-> - driver->command(vino_drvdata->decoder.driver, - cmd, arg); -} - /* VINO framebuffer/DMA descriptor management */ static void vino_free_buffer_with_count(struct vino_framebuffer *fb, @@ -1741,6 +1604,184 @@ static inline void vino_set_default_framerate(struct vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max); } +/* VINO I2C bus functions */ + +struct i2c_algo_sgi_data { + void *data; /* private data for lowlevel routines */ + unsigned (*getctrl)(void *data); + void (*setctrl)(void *data, unsigned val); + unsigned (*rdata)(void *data); + void (*wdata)(void *data, unsigned val); + + int xfer_timeout; + int ack_timeout; +}; + +static int wait_xfer_done(struct i2c_algo_sgi_data *adap) +{ + int i; + + for (i = 0; i < adap->xfer_timeout; i++) { + if ((adap->getctrl(adap->data) & SGI_I2C_XFER_BUSY) == 0) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int wait_ack(struct i2c_algo_sgi_data *adap) +{ + int i; + + if (wait_xfer_done(adap)) + return -ETIMEDOUT; + for (i = 0; i < adap->ack_timeout; i++) { + if ((adap->getctrl(adap->data) & SGI_I2C_NACK) == 0) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int force_idle(struct i2c_algo_sgi_data *adap) +{ + int i; + + adap->setctrl(adap->data, SGI_I2C_FORCE_IDLE); + for (i = 0; i < adap->xfer_timeout; i++) { + if ((adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) == 0) + goto out; + udelay(1); + } + return -ETIMEDOUT; +out: + if (adap->getctrl(adap->data) & SGI_I2C_BUS_ERR) + return -EIO; + return 0; +} + +static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr, + int rd) +{ + if (rd) + adap->setctrl(adap->data, SGI_I2C_NOT_IDLE); + /* Check if bus is idle, eventually force it to do so */ + if (adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) + if (force_idle(adap)) + return -EIO; + /* Write out the i2c chip address and specify operation */ + adap->setctrl(adap->data, + SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE); + if (rd) + addr |= 1; + adap->wdata(adap->data, addr); + if (wait_ack(adap)) + return -EIO; + return 0; +} + +static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf, + unsigned int len) +{ + int i; + + adap->setctrl(adap->data, + SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE); + for (i = 0; i < len; i++) { + if (wait_xfer_done(adap)) + return -EIO; + buf[i] = adap->rdata(adap->data); + } + adap->setctrl(adap->data, SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE); + + return 0; + +} + +static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf, + unsigned int len) +{ + int i; + + /* We are already in write state */ + for (i = 0; i < len; i++) { + adap->wdata(adap->data, buf[i]); + if (wait_ack(adap)) + return -EIO; + } + return 0; +} + +static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, + int num) +{ + struct i2c_algo_sgi_data *adap = i2c_adap->algo_data; + struct i2c_msg *p; + int i, err = 0; + + for (i = 0; !err && i < num; i++) { + p = &msgs[i]; + err = do_address(adap, p->addr, p->flags & I2C_M_RD); + if (err || !p->len) + continue; + if (p->flags & I2C_M_RD) + err = i2c_read(adap, p->buf, p->len); + else + err = i2c_write(adap, p->buf, p->len); + } + + return (err < 0) ? err : i; +} + +static u32 sgi_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm sgi_algo = { + .master_xfer = sgi_xfer, + .functionality = sgi_func, +}; + +static unsigned i2c_vino_getctrl(void *data) +{ + return vino->i2c_control; +} + +static void i2c_vino_setctrl(void *data, unsigned val) +{ + vino->i2c_control = val; +} + +static unsigned i2c_vino_rdata(void *data) +{ + return vino->i2c_data; +} + +static void i2c_vino_wdata(void *data, unsigned val) +{ + vino->i2c_data = val; +} + +static struct i2c_algo_sgi_data i2c_sgi_vino_data = { + .getctrl = &i2c_vino_getctrl, + .setctrl = &i2c_vino_setctrl, + .rdata = &i2c_vino_rdata, + .wdata = &i2c_vino_wdata, + .xfer_timeout = 200, + .ack_timeout = 1000, +}; + +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, +}; + /* * Prepare VINO for DMA transfer... * (execute only with vino_lock and input_lock locked) @@ -2490,86 +2531,15 @@ static int vino_get_saa7191_input(int input) } } -static int vino_get_saa7191_norm(unsigned int data_norm) -{ - switch (data_norm) { - case VINO_DATA_NORM_AUTO: - return SAA7191_NORM_AUTO; - case VINO_DATA_NORM_AUTO_EXT: - return SAA7191_NORM_AUTO_EXT; - case VINO_DATA_NORM_PAL: - return SAA7191_NORM_PAL; - case VINO_DATA_NORM_NTSC: - return SAA7191_NORM_NTSC; - case VINO_DATA_NORM_SECAM: - return SAA7191_NORM_SECAM; - default: - printk(KERN_ERR "VINO: vino_get_saa7191_norm(): " - "invalid norm!\n"); - return -1; - } -} - -static int vino_get_from_saa7191_norm(int saa7191_norm) -{ - switch (saa7191_norm) { - case SAA7191_NORM_PAL: - return VINO_DATA_NORM_PAL; - case SAA7191_NORM_NTSC: - return VINO_DATA_NORM_NTSC; - case SAA7191_NORM_SECAM: - return VINO_DATA_NORM_SECAM; - default: - printk(KERN_ERR "VINO: vino_get_from_saa7191_norm(): " - "invalid norm!\n"); - return VINO_DATA_NORM_NONE; - } -} - -static int vino_saa7191_set_norm(unsigned int *data_norm) -{ - int saa7191_norm, new_data_norm; - int err = 0; - - saa7191_norm = vino_get_saa7191_norm(*data_norm); - - err = i2c_decoder_command(DECODER_SAA7191_SET_NORM, - &saa7191_norm); - if (err) - goto out; - - if ((*data_norm == VINO_DATA_NORM_AUTO) - || (*data_norm == VINO_DATA_NORM_AUTO_EXT)) { - struct saa7191_status status; - - err = i2c_decoder_command(DECODER_SAA7191_GET_STATUS, - &status); - if (err) - goto out; - - new_data_norm = - vino_get_from_saa7191_norm(status.norm); - if (new_data_norm == VINO_DATA_NORM_NONE) { - err = -EINVAL; - goto out; - } - - *data_norm = (unsigned int)new_data_norm; - } - -out: - return err; -} - /* execute with input_lock locked */ static int vino_is_input_owner(struct vino_channel_settings *vcs) { switch(vcs->input) { case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: - return (vino_drvdata->decoder.owner == vcs->channel); + return vino_drvdata->decoder_owner == vcs->channel; case VINO_INPUT_D1: - return (vino_drvdata->camera.owner == vcs->channel); + return vino_drvdata->camera_owner == vcs->channel; default: return 0; } @@ -2585,23 +2555,22 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) spin_lock_irqsave(&vino_drvdata->input_lock, flags); /* First try D1 and then SAA7191 */ - if (vino_drvdata->camera.driver - && (vino_drvdata->camera.owner == VINO_NO_CHANNEL)) { - i2c_use_client(vino_drvdata->camera.driver); - vino_drvdata->camera.owner = vcs->channel; + if (vino_drvdata->camera + && (vino_drvdata->camera_owner == VINO_NO_CHANNEL)) { + vino_drvdata->camera_owner = vcs->channel; vcs->input = VINO_INPUT_D1; vcs->data_norm = VINO_DATA_NORM_D1; - } else if (vino_drvdata->decoder.driver - && (vino_drvdata->decoder.owner == VINO_NO_CHANNEL)) { - int input, data_norm; - int saa7191_input; + } else if (vino_drvdata->decoder + && (vino_drvdata->decoder_owner == VINO_NO_CHANNEL)) { + int input; + int data_norm; + v4l2_std_id norm; + struct v4l2_routing route = { 0, 0 }; - i2c_use_client(vino_drvdata->decoder.driver); input = VINO_INPUT_COMPOSITE; - saa7191_input = vino_get_saa7191_input(input); - ret = i2c_decoder_command(DECODER_SET_INPUT, - &saa7191_input); + route.input = vino_get_saa7191_input(input); + ret = decoder_call(video, s_routing, &route); if (ret) { ret = -EINVAL; goto out; @@ -2612,12 +2581,15 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) /* Don't hold spinlocks while auto-detecting norm * as it may take a while... */ - data_norm = VINO_DATA_NORM_AUTO_EXT; - - ret = vino_saa7191_set_norm(&data_norm); - if ((ret == -EBUSY) || (ret == -EAGAIN)) { - data_norm = VINO_DATA_NORM_PAL; - ret = vino_saa7191_set_norm(&data_norm); + ret = decoder_call(video, querystd, &norm); + if (!ret) { + for (data_norm = 0; data_norm < 3; data_norm++) { + if (vino_data_norms[data_norm].std & norm) + break; + } + if (data_norm == 3) + data_norm = VINO_DATA_NORM_PAL; + ret = decoder_call(tuner, s_std, norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -2627,7 +2599,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) goto out; } - vino_drvdata->decoder.owner = vcs->channel; + vino_drvdata->decoder_owner = vcs->channel; vcs->input = input; vcs->data_norm = data_norm; @@ -2672,25 +2644,24 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) switch (input) { case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: - if (!vino_drvdata->decoder.driver) { + if (!vino_drvdata->decoder) { ret = -EINVAL; goto out; } - if (vino_drvdata->decoder.owner == VINO_NO_CHANNEL) { - i2c_use_client(vino_drvdata->decoder.driver); - vino_drvdata->decoder.owner = vcs->channel; + if (vino_drvdata->decoder_owner == VINO_NO_CHANNEL) { + vino_drvdata->decoder_owner = vcs->channel; } - if (vino_drvdata->decoder.owner == vcs->channel) { + if (vino_drvdata->decoder_owner == vcs->channel) { int data_norm; - int saa7191_input; + v4l2_std_id norm; + struct v4l2_routing route = { 0, 0 }; - saa7191_input = vino_get_saa7191_input(input); - ret = i2c_decoder_command(DECODER_SET_INPUT, - &saa7191_input); + route.input = vino_get_saa7191_input(input); + ret = decoder_call(video, s_routing, &route); if (ret) { - vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + vino_drvdata->decoder_owner = VINO_NO_CHANNEL; ret = -EINVAL; goto out; } @@ -2700,18 +2671,21 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) /* Don't hold spinlocks while auto-detecting norm * as it may take a while... */ - data_norm = VINO_DATA_NORM_AUTO_EXT; - - ret = vino_saa7191_set_norm(&data_norm); - if ((ret == -EBUSY) || (ret == -EAGAIN)) { - data_norm = VINO_DATA_NORM_PAL; - ret = vino_saa7191_set_norm(&data_norm); + ret = decoder_call(video, querystd, &norm); + if (!ret) { + for (data_norm = 0; data_norm < 3; data_norm++) { + if (vino_data_norms[data_norm].std & norm) + break; + } + if (data_norm == 3) + data_norm = VINO_DATA_NORM_PAL; + ret = decoder_call(tuner, s_std, norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); if (ret) { - vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + vino_drvdata->decoder_owner = VINO_NO_CHANNEL; ret = -EINVAL; goto out; } @@ -2728,37 +2702,31 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) vcs->data_norm = vcs2->data_norm; } - if (vino_drvdata->camera.owner == vcs->channel) { + if (vino_drvdata->camera_owner == vcs->channel) { /* Transfer the ownership or release the input */ if (vcs2->input == VINO_INPUT_D1) { - vino_drvdata->camera.owner = vcs2->channel; + vino_drvdata->camera_owner = vcs2->channel; } else { - i2c_release_client(vino_drvdata-> - camera.driver); - vino_drvdata->camera.owner = VINO_NO_CHANNEL; + vino_drvdata->camera_owner = VINO_NO_CHANNEL; } } break; case VINO_INPUT_D1: - if (!vino_drvdata->camera.driver) { + if (!vino_drvdata->camera) { ret = -EINVAL; goto out; } - if (vino_drvdata->camera.owner == VINO_NO_CHANNEL) { - i2c_use_client(vino_drvdata->camera.driver); - vino_drvdata->camera.owner = vcs->channel; - } + if (vino_drvdata->camera_owner == VINO_NO_CHANNEL) + vino_drvdata->camera_owner = vcs->channel; - if (vino_drvdata->decoder.owner == vcs->channel) { + if (vino_drvdata->decoder_owner == vcs->channel) { /* Transfer the ownership or release the input */ if ((vcs2->input == VINO_INPUT_COMPOSITE) || (vcs2->input == VINO_INPUT_SVIDEO)) { - vino_drvdata->decoder.owner = vcs2->channel; + vino_drvdata->decoder_owner = vcs2->channel; } else { - i2c_release_client(vino_drvdata-> - decoder.driver); - vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + vino_drvdata->decoder_owner = VINO_NO_CHANNEL; } } @@ -2795,20 +2763,18 @@ static void vino_release_input(struct vino_channel_settings *vcs) /* Release ownership of the channel * and if the other channel takes input from * the same source, transfer the ownership */ - if (vino_drvdata->camera.owner == vcs->channel) { + if (vino_drvdata->camera_owner == vcs->channel) { if (vcs2->input == VINO_INPUT_D1) { - vino_drvdata->camera.owner = vcs2->channel; + vino_drvdata->camera_owner = vcs2->channel; } else { - i2c_release_client(vino_drvdata->camera.driver); - vino_drvdata->camera.owner = VINO_NO_CHANNEL; + vino_drvdata->camera_owner = VINO_NO_CHANNEL; } - } else if (vino_drvdata->decoder.owner == vcs->channel) { + } else if (vino_drvdata->decoder_owner == vcs->channel) { if ((vcs2->input == VINO_INPUT_COMPOSITE) || (vcs2->input == VINO_INPUT_SVIDEO)) { - vino_drvdata->decoder.owner = vcs2->channel; + vino_drvdata->decoder_owner = vcs2->channel; } else { - i2c_release_client(vino_drvdata->decoder.driver); - vino_drvdata->decoder.owner = VINO_NO_CHANNEL; + vino_drvdata->decoder_owner = VINO_NO_CHANNEL; } } vcs->input = VINO_INPUT_NONE; @@ -2829,18 +2795,16 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs, switch (vcs->input) { case VINO_INPUT_D1: /* only one "norm" supported */ - if ((data_norm != VINO_DATA_NORM_D1) - && (data_norm != VINO_DATA_NORM_AUTO) - && (data_norm != VINO_DATA_NORM_AUTO_EXT)) + if (data_norm != VINO_DATA_NORM_D1) return -EINVAL; break; case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: { + v4l2_std_id norm; + if ((data_norm != VINO_DATA_NORM_PAL) && (data_norm != VINO_DATA_NORM_NTSC) - && (data_norm != VINO_DATA_NORM_SECAM) - && (data_norm != VINO_DATA_NORM_AUTO) - && (data_norm != VINO_DATA_NORM_AUTO_EXT)) + && (data_norm != VINO_DATA_NORM_SECAM)) return -EINVAL; spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags); @@ -2848,7 +2812,8 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs, /* Don't hold spinlocks while setting norm * as it may take a while... */ - err = vino_saa7191_set_norm(&data_norm); + norm = vino_data_norms[data_norm].std; + err = decoder_call(tuner, s_std, norm); spin_lock_irqsave(&vino_drvdata->input_lock, *flags); @@ -2884,41 +2849,13 @@ static int vino_find_data_format(__u32 pixelformat) return VINO_DATA_FMT_NONE; } -static int vino_enum_data_norm(struct vino_channel_settings *vcs, __u32 index) -{ - int data_norm = VINO_DATA_NORM_NONE; - unsigned long flags; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - switch(vcs->input) { - case VINO_INPUT_COMPOSITE: - case VINO_INPUT_SVIDEO: - if (index == 0) { - data_norm = VINO_DATA_NORM_PAL; - } else if (index == 1) { - data_norm = VINO_DATA_NORM_NTSC; - } else if (index == 2) { - data_norm = VINO_DATA_NORM_SECAM; - } - break; - case VINO_INPUT_D1: - if (index == 0) { - data_norm = VINO_DATA_NORM_D1; - } - break; - } - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return data_norm; -} - -static int vino_enum_input(struct vino_channel_settings *vcs, __u32 index) +static int vino_int_enum_input(struct vino_channel_settings *vcs, __u32 index) { int input = VINO_INPUT_NONE; unsigned long flags; spin_lock_irqsave(&vino_drvdata->input_lock, flags); - if (vino_drvdata->decoder.driver && vino_drvdata->camera.driver) { + if (vino_drvdata->decoder && vino_drvdata->camera) { switch (index) { case 0: input = VINO_INPUT_COMPOSITE; @@ -2930,7 +2867,7 @@ static int vino_enum_input(struct vino_channel_settings *vcs, __u32 index) input = VINO_INPUT_D1; break; } - } else if (vino_drvdata->decoder.driver) { + } else if (vino_drvdata->decoder) { switch (index) { case 0: input = VINO_INPUT_COMPOSITE; @@ -2939,7 +2876,7 @@ static int vino_enum_input(struct vino_channel_settings *vcs, __u32 index) input = VINO_INPUT_SVIDEO; break; } - } else if (vino_drvdata->camera.driver) { + } else if (vino_drvdata->camera) { switch (index) { case 0: input = VINO_INPUT_D1; @@ -2957,7 +2894,7 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs) __u32 index = 0; // FIXME: detect when no inputs available - if (vino_drvdata->decoder.driver && vino_drvdata->camera.driver) { + if (vino_drvdata->decoder && vino_drvdata->camera) { switch (vcs->input) { case VINO_INPUT_COMPOSITE: index = 0; @@ -2969,7 +2906,7 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs) index = 2; break; } - } else if (vino_drvdata->decoder.driver) { + } else if (vino_drvdata->decoder) { switch (vcs->input) { case VINO_INPUT_COMPOSITE: index = 0; @@ -2978,7 +2915,7 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs) index = 1; break; } - } else if (vino_drvdata->camera.driver) { + } else if (vino_drvdata->camera) { switch (vcs->input) { case VINO_INPUT_D1: index = 0; @@ -2991,7 +2928,8 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs) /* V4L2 ioctls */ -static void vino_v4l2_querycap(struct v4l2_capability *cap) +static int vino_querycap(struct file *file, void *__fh, + struct v4l2_capability *cap) { memset(cap, 0, sizeof(struct v4l2_capability)); @@ -3003,16 +2941,18 @@ static void vino_v4l2_querycap(struct v4l2_capability *cap) V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; // V4L2_CAP_OVERLAY, V4L2_CAP_READWRITE + return 0; } -static int vino_v4l2_enuminput(struct vino_channel_settings *vcs, +static int vino_enum_input(struct file *file, void *__fh, struct v4l2_input *i) { + struct vino_channel_settings *vcs = video_drvdata(file); __u32 index = i->index; int input; dprintk("requested index = %d\n", index); - input = vino_enum_input(vcs, index); + input = vino_int_enum_input(vcs, index); if (input == VINO_INPUT_NONE) return -EINVAL; @@ -3023,20 +2963,15 @@ static int vino_v4l2_enuminput(struct vino_channel_settings *vcs, i->std = vino_inputs[input].std; strcpy(i->name, vino_inputs[input].name); - if ((input == VINO_INPUT_COMPOSITE) - || (input == VINO_INPUT_SVIDEO)) { - struct saa7191_status status; - i2c_decoder_command(DECODER_SAA7191_GET_STATUS, &status); - i->status |= status.signal ? 0 : V4L2_IN_ST_NO_SIGNAL; - i->status |= status.color ? 0 : V4L2_IN_ST_NO_COLOR; - } - + if (input == VINO_INPUT_COMPOSITE || input == VINO_INPUT_SVIDEO) + decoder_call(video, g_input_status, &i->status); return 0; } -static int vino_v4l2_g_input(struct vino_channel_settings *vcs, +static int vino_g_input(struct file *file, void *__fh, unsigned int *i) { + struct vino_channel_settings *vcs = video_drvdata(file); __u32 index; int input; unsigned long flags; @@ -3057,52 +2992,24 @@ static int vino_v4l2_g_input(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_s_input(struct vino_channel_settings *vcs, - unsigned int *i) +static int vino_s_input(struct file *file, void *__fh, + unsigned int i) { + struct vino_channel_settings *vcs = video_drvdata(file); int input; - dprintk("requested input = %d\n", *i); + dprintk("requested input = %d\n", i); - input = vino_enum_input(vcs, *i); + input = vino_int_enum_input(vcs, i); if (input == VINO_INPUT_NONE) return -EINVAL; return vino_set_input(vcs, input); } -static int vino_v4l2_enumstd(struct vino_channel_settings *vcs, - struct v4l2_standard *s) -{ - int index = s->index; - int data_norm; - - data_norm = vino_enum_data_norm(vcs, index); - dprintk("standard index = %d\n", index); - - if (data_norm == VINO_DATA_NORM_NONE) - return -EINVAL; - - dprintk("standard name = %s\n", - vino_data_norms[data_norm].description); - - memset(s, 0, sizeof(struct v4l2_standard)); - s->index = index; - - s->id = vino_data_norms[data_norm].std; - s->frameperiod.numerator = 1; - s->frameperiod.denominator = - vino_data_norms[data_norm].fps_max; - s->framelines = - vino_data_norms[data_norm].framelines; - strcpy(s->name, - vino_data_norms[data_norm].description); - - return 0; -} - -static int vino_v4l2_querystd(struct vino_channel_settings *vcs, +static int vino_querystd(struct file *file, void *__fh, v4l2_std_id *std) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; int err = 0; @@ -3114,19 +3021,7 @@ static int vino_v4l2_querystd(struct vino_channel_settings *vcs, break; case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: { - struct saa7191_status status; - - i2c_decoder_command(DECODER_SAA7191_GET_STATUS, &status); - - if (status.signal) { - if (status.signal_60hz) { - *std = V4L2_STD_NTSC; - } else { - *std = V4L2_STD_PAL | V4L2_STD_SECAM; - } - } else { - *std = vino_inputs[vcs->input].std; - } + decoder_call(video, querystd, std); break; } default: @@ -3138,9 +3033,10 @@ static int vino_v4l2_querystd(struct vino_channel_settings *vcs, return err; } -static int vino_v4l2_g_std(struct vino_channel_settings *vcs, +static int vino_g_std(struct file *file, void *__fh, v4l2_std_id *std) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -3153,9 +3049,10 @@ static int vino_v4l2_g_std(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_s_std(struct vino_channel_settings *vcs, +static int vino_s_std(struct file *file, void *__fh, v4l2_std_id *std) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; int ret = 0; @@ -3176,12 +3073,7 @@ static int vino_v4l2_s_std(struct vino_channel_settings *vcs, if (vcs->input == VINO_INPUT_D1) goto out; - if (((*std) & V4L2_STD_PAL) - && ((*std) & V4L2_STD_NTSC) - && ((*std) & V4L2_STD_SECAM)) { - ret = vino_set_data_norm(vcs, VINO_DATA_NORM_AUTO_EXT, - &flags); - } else if ((*std) & V4L2_STD_PAL) { + if ((*std) & V4L2_STD_PAL) { ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL, &flags); } else if ((*std) & V4L2_STD_NTSC) { @@ -3207,185 +3099,144 @@ out: return ret; } -static int vino_v4l2_enum_fmt(struct vino_channel_settings *vcs, +static int vino_enum_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_fmtdesc *fd) { - enum v4l2_buf_type type = fd->type; - int index = fd->index; - dprintk("format index = %d\n", index); + dprintk("format index = %d\n", fd->index); - switch (fd->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if ((fd->index < 0) || - (fd->index >= VINO_DATA_FMT_COUNT)) - return -EINVAL; - dprintk("format name = %s\n", - vino_data_formats[index].description); - - memset(fd, 0, sizeof(struct v4l2_fmtdesc)); - fd->index = index; - fd->type = type; - fd->pixelformat = vino_data_formats[index].pixelformat; - strcpy(fd->description, vino_data_formats[index].description); - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: + if (fd->index >= VINO_DATA_FMT_COUNT) return -EINVAL; - } + dprintk("format name = %s\n", vino_data_formats[fd->index].description); + fd->pixelformat = vino_data_formats[fd->index].pixelformat; + strcpy(fd->description, vino_data_formats[fd->index].description); return 0; } -static int vino_v4l2_try_fmt(struct vino_channel_settings *vcs, +static int vino_try_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) { + struct vino_channel_settings *vcs = video_drvdata(file); struct vino_channel_settings tempvcs; unsigned long flags; + struct v4l2_pix_format *pf = &f->fmt.pix; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct v4l2_pix_format *pf = &f->fmt.pix; - - dprintk("requested: w = %d, h = %d\n", - pf->width, pf->height); - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings)); - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + dprintk("requested: w = %d, h = %d\n", + pf->width, pf->height); - tempvcs.data_format = vino_find_data_format(pf->pixelformat); - if (tempvcs.data_format == VINO_DATA_FMT_NONE) { - tempvcs.data_format = VINO_DATA_FMT_GREY; - pf->pixelformat = - vino_data_formats[tempvcs.data_format]. - pixelformat; - } + spin_lock_irqsave(&vino_drvdata->input_lock, flags); + memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings)); + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - /* data format must be set before clipping/scaling */ - vino_set_scaling(&tempvcs, pf->width, pf->height); + tempvcs.data_format = vino_find_data_format(pf->pixelformat); + if (tempvcs.data_format == VINO_DATA_FMT_NONE) { + tempvcs.data_format = VINO_DATA_FMT_GREY; + pf->pixelformat = + vino_data_formats[tempvcs.data_format]. + pixelformat; + } - dprintk("data format = %s\n", - vino_data_formats[tempvcs.data_format].description); + /* data format must be set before clipping/scaling */ + vino_set_scaling(&tempvcs, pf->width, pf->height); - pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) / - tempvcs.decimation; - pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) / - tempvcs.decimation; + dprintk("data format = %s\n", + vino_data_formats[tempvcs.data_format].description); - pf->field = V4L2_FIELD_INTERLACED; - pf->bytesperline = tempvcs.line_size; - pf->sizeimage = tempvcs.line_size * - (tempvcs.clipping.bottom - tempvcs.clipping.top) / - tempvcs.decimation; - pf->colorspace = - vino_data_formats[tempvcs.data_format].colorspace; + pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) / + tempvcs.decimation; + pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) / + tempvcs.decimation; - pf->priv = 0; - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } + pf->field = V4L2_FIELD_INTERLACED; + pf->bytesperline = tempvcs.line_size; + pf->sizeimage = tempvcs.line_size * + (tempvcs.clipping.bottom - tempvcs.clipping.top) / + tempvcs.decimation; + pf->colorspace = + vino_data_formats[tempvcs.data_format].colorspace; + pf->priv = 0; return 0; } -static int vino_v4l2_g_fmt(struct vino_channel_settings *vcs, +static int vino_g_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; + struct v4l2_pix_format *pf = &f->fmt.pix; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct v4l2_pix_format *pf = &f->fmt.pix; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - pf->width = (vcs->clipping.right - vcs->clipping.left) / - vcs->decimation; - pf->height = (vcs->clipping.bottom - vcs->clipping.top) / - vcs->decimation; - pf->pixelformat = - vino_data_formats[vcs->data_format].pixelformat; + spin_lock_irqsave(&vino_drvdata->input_lock, flags); - pf->field = V4L2_FIELD_INTERLACED; - pf->bytesperline = vcs->line_size; - pf->sizeimage = vcs->line_size * - (vcs->clipping.bottom - vcs->clipping.top) / - vcs->decimation; - pf->colorspace = - vino_data_formats[vcs->data_format].colorspace; + pf->width = (vcs->clipping.right - vcs->clipping.left) / + vcs->decimation; + pf->height = (vcs->clipping.bottom - vcs->clipping.top) / + vcs->decimation; + pf->pixelformat = + vino_data_formats[vcs->data_format].pixelformat; - pf->priv = 0; + pf->field = V4L2_FIELD_INTERLACED; + pf->bytesperline = vcs->line_size; + pf->sizeimage = vcs->line_size * + (vcs->clipping.bottom - vcs->clipping.top) / + vcs->decimation; + pf->colorspace = + vino_data_formats[vcs->data_format].colorspace; - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } + pf->priv = 0; + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); return 0; } -static int vino_v4l2_s_fmt(struct vino_channel_settings *vcs, +static int vino_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f) { + struct vino_channel_settings *vcs = video_drvdata(file); int data_format; unsigned long flags; + struct v4l2_pix_format *pf = &f->fmt.pix; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct v4l2_pix_format *pf = &f->fmt.pix; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); + spin_lock_irqsave(&vino_drvdata->input_lock, flags); - data_format = vino_find_data_format(pf->pixelformat); + data_format = vino_find_data_format(pf->pixelformat); - if (data_format == VINO_DATA_FMT_NONE) { - vcs->data_format = VINO_DATA_FMT_GREY; - pf->pixelformat = - vino_data_formats[vcs->data_format]. - pixelformat; - } else { - vcs->data_format = data_format; - } + if (data_format == VINO_DATA_FMT_NONE) { + vcs->data_format = VINO_DATA_FMT_GREY; + pf->pixelformat = + vino_data_formats[vcs->data_format]. + pixelformat; + } else { + vcs->data_format = data_format; + } - /* data format must be set before clipping/scaling */ - vino_set_scaling(vcs, pf->width, pf->height); + /* data format must be set before clipping/scaling */ + vino_set_scaling(vcs, pf->width, pf->height); - dprintk("data format = %s\n", - vino_data_formats[vcs->data_format].description); + dprintk("data format = %s\n", + vino_data_formats[vcs->data_format].description); - pf->width = vcs->clipping.right - vcs->clipping.left; - pf->height = vcs->clipping.bottom - vcs->clipping.top; + pf->width = vcs->clipping.right - vcs->clipping.left; + pf->height = vcs->clipping.bottom - vcs->clipping.top; - pf->field = V4L2_FIELD_INTERLACED; - pf->bytesperline = vcs->line_size; - pf->sizeimage = vcs->line_size * - (vcs->clipping.bottom - vcs->clipping.top) / - vcs->decimation; - pf->colorspace = - vino_data_formats[vcs->data_format].colorspace; + pf->field = V4L2_FIELD_INTERLACED; + pf->bytesperline = vcs->line_size; + pf->sizeimage = vcs->line_size * + (vcs->clipping.bottom - vcs->clipping.top) / + vcs->decimation; + pf->colorspace = + vino_data_formats[vcs->data_format].colorspace; - pf->priv = 0; - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } + pf->priv = 0; + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); return 0; } -static int vino_v4l2_cropcap(struct vino_channel_settings *vcs, +static int vino_cropcap(struct file *file, void *__fh, struct v4l2_cropcap *ccap) { + struct vino_channel_settings *vcs = video_drvdata(file); const struct vino_data_norm *norm; unsigned long flags; @@ -3415,9 +3266,10 @@ static int vino_v4l2_cropcap(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_g_crop(struct vino_channel_settings *vcs, +static int vino_g_crop(struct file *file, void *__fh, struct v4l2_crop *c) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; switch (c->type) { @@ -3439,9 +3291,10 @@ static int vino_v4l2_g_crop(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_s_crop(struct vino_channel_settings *vcs, +static int vino_s_crop(struct file *file, void *__fh, struct v4l2_crop *c) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; switch (c->type) { @@ -3461,108 +3314,83 @@ static int vino_v4l2_s_crop(struct vino_channel_settings *vcs, return 0; } -static int vino_v4l2_g_parm(struct vino_channel_settings *vcs, +static int vino_g_parm(struct file *file, void *__fh, struct v4l2_streamparm *sp) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; + struct v4l2_captureparm *cp = &sp->parm.capture; - switch (sp->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct v4l2_captureparm *cp = &sp->parm.capture; - memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = 1; + spin_lock_irqsave(&vino_drvdata->input_lock, flags); - spin_lock_irqsave(&vino_drvdata->input_lock, flags); + cp->timeperframe.denominator = vcs->fps; - cp->timeperframe.denominator = vcs->fps; + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - // TODO: cp->readbuffers = xxx; - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } + /* TODO: cp->readbuffers = xxx; */ return 0; } -static int vino_v4l2_s_parm(struct vino_channel_settings *vcs, +static int vino_s_parm(struct file *file, void *__fh, struct v4l2_streamparm *sp) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; + struct v4l2_captureparm *cp = &sp->parm.capture; - switch (sp->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct v4l2_captureparm *cp = &sp->parm.capture; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - if ((cp->timeperframe.numerator == 0) || - (cp->timeperframe.denominator == 0)) { - /* reset framerate */ - vino_set_default_framerate(vcs); - } else { - vino_set_framerate(vcs, cp->timeperframe.denominator / - cp->timeperframe.numerator); - } - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + spin_lock_irqsave(&vino_drvdata->input_lock, flags); - // TODO: set buffers according to cp->readbuffers - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; + if ((cp->timeperframe.numerator == 0) || + (cp->timeperframe.denominator == 0)) { + /* reset framerate */ + vino_set_default_framerate(vcs); + } else { + vino_set_framerate(vcs, cp->timeperframe.denominator / + cp->timeperframe.numerator); } + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + return 0; } -static int vino_v4l2_reqbufs(struct vino_channel_settings *vcs, +static int vino_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *rb) { + struct vino_channel_settings *vcs = video_drvdata(file); + if (vcs->reading) return -EBUSY; - switch (rb->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - // TODO: check queue type - if (rb->memory != V4L2_MEMORY_MMAP) { - dprintk("type not mmap\n"); - return -EINVAL; - } + /* TODO: check queue type */ + if (rb->memory != V4L2_MEMORY_MMAP) { + dprintk("type not mmap\n"); + return -EINVAL; + } - dprintk("count = %d\n", rb->count); - if (rb->count > 0) { - if (vino_is_capturing(vcs)) { - dprintk("busy, capturing\n"); - return -EBUSY; - } + dprintk("count = %d\n", rb->count); + if (rb->count > 0) { + if (vino_is_capturing(vcs)) { + dprintk("busy, capturing\n"); + return -EBUSY; + } - if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) { - dprintk("busy, buffers still mapped\n"); - return -EBUSY; - } else { - vcs->streaming = 0; - vino_queue_free(&vcs->fb_queue); - vino_queue_init(&vcs->fb_queue, &rb->count); - } + if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) { + dprintk("busy, buffers still mapped\n"); + return -EBUSY; } else { vcs->streaming = 0; - vino_capture_stop(vcs); vino_queue_free(&vcs->fb_queue); + vino_queue_init(&vcs->fb_queue, &rb->count); } - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; + } else { + vcs->streaming = 0; + vino_capture_stop(vcs); + vino_queue_free(&vcs->fb_queue); } return 0; @@ -3606,156 +3434,135 @@ static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs, fb->id, fb->size, fb->data_size, fb->offset); } -static int vino_v4l2_querybuf(struct vino_channel_settings *vcs, +static int vino_querybuf(struct file *file, void *__fh, struct v4l2_buffer *b) { + struct vino_channel_settings *vcs = video_drvdata(file); + struct vino_framebuffer *fb; + if (vcs->reading) return -EBUSY; - switch (b->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct vino_framebuffer *fb; - - // TODO: check queue type - if (b->index >= vino_queue_get_length(&vcs->fb_queue)) { - dprintk("invalid index = %d\n", - b->index); - return -EINVAL; - } - - fb = vino_queue_get_buffer(&vcs->fb_queue, - b->index); - if (fb == NULL) { - dprintk("vino_queue_get_buffer() failed"); - return -EINVAL; - } - - vino_v4l2_get_buffer_status(vcs, fb, b); - break; + /* TODO: check queue type */ + if (b->index >= vino_queue_get_length(&vcs->fb_queue)) { + dprintk("invalid index = %d\n", + b->index); + return -EINVAL; } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: + + fb = vino_queue_get_buffer(&vcs->fb_queue, + b->index); + if (fb == NULL) { + dprintk("vino_queue_get_buffer() failed"); return -EINVAL; } + vino_v4l2_get_buffer_status(vcs, fb, b); + return 0; } -static int vino_v4l2_qbuf(struct vino_channel_settings *vcs, +static int vino_qbuf(struct file *file, void *__fh, struct v4l2_buffer *b) { + struct vino_channel_settings *vcs = video_drvdata(file); + struct vino_framebuffer *fb; + int ret; + if (vcs->reading) return -EBUSY; - switch (b->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct vino_framebuffer *fb; - int ret; - - // TODO: check queue type - if (b->memory != V4L2_MEMORY_MMAP) { - dprintk("type not mmap\n"); - return -EINVAL; - } + /* TODO: check queue type */ + if (b->memory != V4L2_MEMORY_MMAP) { + dprintk("type not mmap\n"); + return -EINVAL; + } - fb = vino_capture_enqueue(vcs, b->index); - if (fb == NULL) - return -EINVAL; + fb = vino_capture_enqueue(vcs, b->index); + if (fb == NULL) + return -EINVAL; - vino_v4l2_get_buffer_status(vcs, fb, b); + vino_v4l2_get_buffer_status(vcs, fb, b); - if (vcs->streaming) { - ret = vino_capture_next(vcs, 1); - if (ret) - return ret; - } - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; + if (vcs->streaming) { + ret = vino_capture_next(vcs, 1); + if (ret) + return ret; } return 0; } -static int vino_v4l2_dqbuf(struct vino_channel_settings *vcs, - struct v4l2_buffer *b, - unsigned int nonblocking) +static int vino_dqbuf(struct file *file, void *__fh, + struct v4l2_buffer *b) { + struct vino_channel_settings *vcs = video_drvdata(file); + unsigned int nonblocking = file->f_flags & O_NONBLOCK; + struct vino_framebuffer *fb; + unsigned int incoming, outgoing; + int err; + if (vcs->reading) return -EBUSY; - switch (b->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct vino_framebuffer *fb; - unsigned int incoming, outgoing; - int err; + /* TODO: check queue type */ - // TODO: check queue type + err = vino_queue_get_incoming(&vcs->fb_queue, &incoming); + if (err) { + dprintk("vino_queue_get_incoming() failed\n"); + return -EINVAL; + } + err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing); + if (err) { + dprintk("vino_queue_get_outgoing() failed\n"); + return -EINVAL; + } - err = vino_queue_get_incoming(&vcs->fb_queue, &incoming); - if (err) { - dprintk("vino_queue_get_incoming() failed\n"); + dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing); + + if (outgoing == 0) { + if (incoming == 0) { + dprintk("no incoming or outgoing buffers\n"); return -EINVAL; } - err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing); - if (err) { - dprintk("vino_queue_get_outgoing() failed\n"); - return -EINVAL; + if (nonblocking) { + dprintk("non-blocking I/O was selected and " + "there are no buffers to dequeue\n"); + return -EAGAIN; } - dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing); - - if (outgoing == 0) { - if (incoming == 0) { - dprintk("no incoming or outgoing buffers\n"); - return -EINVAL; - } - if (nonblocking) { - dprintk("non-blocking I/O was selected and " - "there are no buffers to dequeue\n"); - return -EAGAIN; - } - + err = vino_wait_for_frame(vcs); + if (err) { err = vino_wait_for_frame(vcs); if (err) { - err = vino_wait_for_frame(vcs); - if (err) { - /* interrupted or - * no frames captured because - * of frame skipping */ - // vino_capture_failed(vcs); - return -EIO; - } + /* interrupted or no frames captured because of + * frame skipping */ + /* vino_capture_failed(vcs); */ + return -EIO; } } + } - fb = vino_queue_remove(&vcs->fb_queue, &b->index); - if (fb == NULL) { - dprintk("vino_queue_remove() failed\n"); - return -EINVAL; - } - - err = vino_check_buffer(vcs, fb); + fb = vino_queue_remove(&vcs->fb_queue, &b->index); + if (fb == NULL) { + dprintk("vino_queue_remove() failed\n"); + return -EINVAL; + } - vino_v4l2_get_buffer_status(vcs, fb, b); + err = vino_check_buffer(vcs, fb); - if (err) - return -EIO; + vino_v4l2_get_buffer_status(vcs, fb, b); - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } + if (err) + return -EIO; return 0; } -static int vino_v4l2_streamon(struct vino_channel_settings *vcs) +static int vino_streamon(struct file *file, void *__fh, + enum v4l2_buf_type i) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned int incoming; int ret; if (vcs->reading) @@ -3792,8 +3599,10 @@ static int vino_v4l2_streamon(struct vino_channel_settings *vcs) return 0; } -static int vino_v4l2_streamoff(struct vino_channel_settings *vcs) +static int vino_streamoff(struct file *file, void *__fh, + enum v4l2_buf_type i) { + struct vino_channel_settings *vcs = video_drvdata(file); if (vcs->reading) return -EBUSY; @@ -3806,9 +3615,10 @@ static int vino_v4l2_streamoff(struct vino_channel_settings *vcs) return 0; } -static int vino_v4l2_queryctrl(struct vino_channel_settings *vcs, +static int vino_queryctrl(struct file *file, void *__fh, struct v4l2_queryctrl *queryctrl) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; int i; int err = 0; @@ -3855,9 +3665,10 @@ static int vino_v4l2_queryctrl(struct vino_channel_settings *vcs, return err; } -static int vino_v4l2_g_ctrl(struct vino_channel_settings *vcs, +static int vino_g_ctrl(struct file *file, void *__fh, struct v4l2_control *control) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; int i; int err = 0; @@ -3866,56 +3677,38 @@ static int vino_v4l2_g_ctrl(struct vino_channel_settings *vcs, switch (vcs->input) { case VINO_INPUT_D1: { - struct indycam_control indycam_ctrl; - + err = -EINVAL; for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) { - if (vino_indycam_v4l2_controls[i].id == - control->id) { - goto found1; + if (vino_indycam_v4l2_controls[i].id == control->id) { + err = 0; + break; } } - err = -EINVAL; - goto out; - -found1: - indycam_ctrl.type = vino_indycam_v4l2_controls[i].reserved[0]; - - err = i2c_camera_command(DECODER_INDYCAM_GET_CONTROL, - &indycam_ctrl); - if (err) { - err = -EINVAL; + if (err) goto out; - } - control->value = indycam_ctrl.value; + err = camera_call(core, g_ctrl, control); + if (err) + err = -EINVAL; break; } case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: { - struct saa7191_control saa7191_ctrl; - + err = -EINVAL; for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) { - if (vino_saa7191_v4l2_controls[i].id == - control->id) { - goto found2; + if (vino_saa7191_v4l2_controls[i].id == control->id) { + err = 0; + break; } } - err = -EINVAL; - goto out; - -found2: - saa7191_ctrl.type = vino_saa7191_v4l2_controls[i].reserved[0]; - - err = i2c_decoder_command(DECODER_SAA7191_GET_CONTROL, - &saa7191_ctrl); - if (err) { - err = -EINVAL; + if (err) goto out; - } - control->value = saa7191_ctrl.value; + err = decoder_call(core, g_ctrl, control); + if (err) + err = -EINVAL; break; } default: @@ -3928,9 +3721,10 @@ out: return err; } -static int vino_v4l2_s_ctrl(struct vino_channel_settings *vcs, +static int vino_s_ctrl(struct file *file, void *__fh, struct v4l2_control *control) { + struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; int i; int err = 0; @@ -3944,65 +3738,43 @@ static int vino_v4l2_s_ctrl(struct vino_channel_settings *vcs, switch (vcs->input) { case VINO_INPUT_D1: { - struct indycam_control indycam_ctrl; - + err = -EINVAL; for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) { - if (vino_indycam_v4l2_controls[i].id == - control->id) { - if ((control->value >= - vino_indycam_v4l2_controls[i].minimum) - && (control->value <= - vino_indycam_v4l2_controls[i]. - maximum)) { - goto found1; - } else { - err = -ERANGE; - goto out; - } + if (vino_indycam_v4l2_controls[i].id == control->id) { + err = 0; + break; } } - - err = -EINVAL; - goto out; - -found1: - indycam_ctrl.type = vino_indycam_v4l2_controls[i].reserved[0]; - indycam_ctrl.value = control->value; - - err = i2c_camera_command(DECODER_INDYCAM_SET_CONTROL, - &indycam_ctrl); + if (err) + goto out; + if (control->value < vino_indycam_v4l2_controls[i].minimum || + control->value > vino_indycam_v4l2_controls[i].maximum) { + err = -ERANGE; + goto out; + } + err = camera_call(core, s_ctrl, control); if (err) err = -EINVAL; break; } case VINO_INPUT_COMPOSITE: case VINO_INPUT_SVIDEO: { - struct saa7191_control saa7191_ctrl; - + err = -EINVAL; for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) { - if (vino_saa7191_v4l2_controls[i].id == - control->id) { - if ((control->value >= - vino_saa7191_v4l2_controls[i].minimum) - && (control->value <= - vino_saa7191_v4l2_controls[i]. - maximum)) { - goto found2; - } else { - err = -ERANGE; - goto out; - } + if (vino_saa7191_v4l2_controls[i].id == control->id) { + err = 0; + break; } } - err = -EINVAL; - goto out; - -found2: - saa7191_ctrl.type = vino_saa7191_v4l2_controls[i].reserved[0]; - saa7191_ctrl.value = control->value; + if (err) + goto out; + if (control->value < vino_saa7191_v4l2_controls[i].minimum || + control->value > vino_saa7191_v4l2_controls[i].maximum) { + err = -ERANGE; + goto out; + } - err = i2c_decoder_command(DECODER_SAA7191_SET_CONTROL, - &saa7191_ctrl); + err = decoder_call(core, s_ctrl, control); if (err) err = -EINVAL; break; @@ -4233,116 +4005,9 @@ over: ret = POLLIN | POLLRDNORM; error: - return ret; } -static long vino_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - -#ifdef VINO_DEBUG - switch (_IOC_TYPE(cmd)) { - case 'v': - dprintk("ioctl(): V4L1 unsupported (0x%08x)\n", cmd); - break; - case 'V': - dprintk("ioctl(): V4L2 %s (0x%08x)\n", - v4l2_ioctl_names[_IOC_NR(cmd)], cmd); - break; - default: - dprintk("ioctl(): unsupported command 0x%08x\n", cmd); - } -#endif - - switch (cmd) { - /* V4L2 interface */ - case VIDIOC_QUERYCAP: { - vino_v4l2_querycap(arg); - break; - } - case VIDIOC_ENUMINPUT: { - return vino_v4l2_enuminput(vcs, arg); - } - case VIDIOC_G_INPUT: { - return vino_v4l2_g_input(vcs, arg); - } - case VIDIOC_S_INPUT: { - return vino_v4l2_s_input(vcs, arg); - } - case VIDIOC_ENUMSTD: { - return vino_v4l2_enumstd(vcs, arg); - } - case VIDIOC_QUERYSTD: { - return vino_v4l2_querystd(vcs, arg); - } - case VIDIOC_G_STD: { - return vino_v4l2_g_std(vcs, arg); - } - case VIDIOC_S_STD: { - return vino_v4l2_s_std(vcs, arg); - } - case VIDIOC_ENUM_FMT: { - return vino_v4l2_enum_fmt(vcs, arg); - } - case VIDIOC_TRY_FMT: { - return vino_v4l2_try_fmt(vcs, arg); - } - case VIDIOC_G_FMT: { - return vino_v4l2_g_fmt(vcs, arg); - } - case VIDIOC_S_FMT: { - return vino_v4l2_s_fmt(vcs, arg); - } - case VIDIOC_CROPCAP: { - return vino_v4l2_cropcap(vcs, arg); - } - case VIDIOC_G_CROP: { - return vino_v4l2_g_crop(vcs, arg); - } - case VIDIOC_S_CROP: { - return vino_v4l2_s_crop(vcs, arg); - } - case VIDIOC_G_PARM: { - return vino_v4l2_g_parm(vcs, arg); - } - case VIDIOC_S_PARM: { - return vino_v4l2_s_parm(vcs, arg); - } - case VIDIOC_REQBUFS: { - return vino_v4l2_reqbufs(vcs, arg); - } - case VIDIOC_QUERYBUF: { - return vino_v4l2_querybuf(vcs, arg); - } - case VIDIOC_QBUF: { - return vino_v4l2_qbuf(vcs, arg); - } - case VIDIOC_DQBUF: { - return vino_v4l2_dqbuf(vcs, arg, file->f_flags & O_NONBLOCK); - } - case VIDIOC_STREAMON: { - return vino_v4l2_streamon(vcs); - } - case VIDIOC_STREAMOFF: { - return vino_v4l2_streamoff(vcs); - } - case VIDIOC_QUERYCTRL: { - return vino_v4l2_queryctrl(vcs, arg); - } - case VIDIOC_G_CTRL: { - return vino_v4l2_g_ctrl(vcs, arg); - } - case VIDIOC_S_CTRL: { - return vino_v4l2_s_ctrl(vcs, arg); - } - default: - return -ENOIOCTLCMD; - } - - return 0; -} - static long vino_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -4352,7 +4017,7 @@ static long vino_ioctl(struct file *file, if (mutex_lock_interruptible(&vcs->mutex)) return -EINTR; - ret = video_usercopy(file, cmd, arg, vino_do_ioctl); + ret = video_ioctl2(file, cmd, arg); mutex_unlock(&vcs->mutex); @@ -4364,45 +4029,75 @@ static long vino_ioctl(struct file *file, /* __initdata */ static int vino_init_stage; +const struct v4l2_ioctl_ops vino_ioctl_ops = { + .vidioc_enum_fmt_vid_cap = vino_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vino_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vino_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vino_try_fmt_vid_cap, + .vidioc_querycap = vino_querycap, + .vidioc_enum_input = vino_enum_input, + .vidioc_g_input = vino_g_input, + .vidioc_s_input = vino_s_input, + .vidioc_g_std = vino_g_std, + .vidioc_s_std = vino_s_std, + .vidioc_querystd = vino_querystd, + .vidioc_cropcap = vino_cropcap, + .vidioc_s_crop = vino_s_crop, + .vidioc_g_crop = vino_g_crop, + .vidioc_s_parm = vino_s_parm, + .vidioc_g_parm = vino_g_parm, + .vidioc_reqbufs = vino_reqbufs, + .vidioc_querybuf = vino_querybuf, + .vidioc_qbuf = vino_qbuf, + .vidioc_dqbuf = vino_dqbuf, + .vidioc_streamon = vino_streamon, + .vidioc_streamoff = vino_streamoff, + .vidioc_queryctrl = vino_queryctrl, + .vidioc_g_ctrl = vino_g_ctrl, + .vidioc_s_ctrl = vino_s_ctrl, +}; + static const struct v4l2_file_operations vino_fops = { .owner = THIS_MODULE, .open = vino_open, .release = vino_close, - .ioctl = vino_ioctl, + .unlocked_ioctl = vino_ioctl, .mmap = vino_mmap, .poll = vino_poll, }; -static struct video_device v4l_device_template = { +static struct video_device vdev_template = { .name = "NOT SET", .fops = &vino_fops, + .ioctl_ops = &vino_ioctl_ops, + .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, .minor = -1, }; static void vino_module_cleanup(int stage) { switch(stage) { + case 11: + video_unregister_device(vino_drvdata->b.vdev); + vino_drvdata->b.vdev = NULL; case 10: - video_unregister_device(vino_drvdata->b.v4l_device); - vino_drvdata->b.v4l_device = NULL; + video_unregister_device(vino_drvdata->a.vdev); + vino_drvdata->a.vdev = NULL; case 9: - video_unregister_device(vino_drvdata->a.v4l_device); - vino_drvdata->a.v4l_device = NULL; + i2c_del_adapter(&vino_i2c_adapter); case 8: - vino_i2c_del_bus(); - case 7: free_irq(SGI_VINO_IRQ, NULL); + case 7: + if (vino_drvdata->b.vdev) { + video_device_release(vino_drvdata->b.vdev); + vino_drvdata->b.vdev = NULL; + } case 6: - if (vino_drvdata->b.v4l_device) { - video_device_release(vino_drvdata->b.v4l_device); - vino_drvdata->b.v4l_device = NULL; + if (vino_drvdata->a.vdev) { + video_device_release(vino_drvdata->a.vdev); + vino_drvdata->a.vdev = NULL; } case 5: - if (vino_drvdata->a.v4l_device) { - video_device_release(vino_drvdata->a.v4l_device); - vino_drvdata->a.v4l_device = NULL; - } - case 4: /* all entries in dma_cpu dummy table have the same address */ dma_unmap_single(NULL, vino_drvdata->dummy_desc_table.dma_cpu[0], @@ -4412,8 +4107,10 @@ static void vino_module_cleanup(int stage) (void *)vino_drvdata-> dummy_desc_table.dma_cpu, vino_drvdata->dummy_desc_table.dma); - case 3: + case 4: free_page(vino_drvdata->dummy_page); + case 3: + v4l2_device_unregister(&vino_drvdata->v4l2_dev); case 2: kfree(vino_drvdata); case 1: @@ -4468,6 +4165,7 @@ static int vino_probe(void) static int vino_init(void) { dma_addr_t dma_dummy_address; + int err; int i; vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL); @@ -4476,6 +4174,12 @@ static int vino_init(void) return -ENOMEM; } vino_init_stage++; + strlcpy(vino_drvdata->v4l2_dev.name, "vino", + sizeof(vino_drvdata->v4l2_dev.name)); + err = v4l2_device_register(NULL, &vino_drvdata->v4l2_dev); + if (err) + return err; + vino_init_stage++; /* create a dummy dma descriptor */ vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA); @@ -4542,25 +4246,27 @@ static int vino_init_channel_settings(struct vino_channel_settings *vcs, spin_lock_init(&vcs->fb_queue.queue_lock); init_waitqueue_head(&vcs->fb_queue.frame_wait_queue); - vcs->v4l_device = video_device_alloc(); - if (!vcs->v4l_device) { + vcs->vdev = video_device_alloc(); + if (!vcs->vdev) { vino_module_cleanup(vino_init_stage); return -ENOMEM; } vino_init_stage++; - memcpy(vcs->v4l_device, &v4l_device_template, + memcpy(vcs->vdev, &vdev_template, sizeof(struct video_device)); - strcpy(vcs->v4l_device->name, name); - vcs->v4l_device->release = video_device_release; + strcpy(vcs->vdev->name, name); + vcs->vdev->release = video_device_release; + vcs->vdev->v4l2_dev = &vino_drvdata->v4l2_dev; - video_set_drvdata(vcs->v4l_device, vcs); + video_set_drvdata(vcs->vdev, vcs); return 0; } static int __init vino_module_init(void) { + unsigned short addr[] = { 0, I2C_CLIENT_END }; int ret; printk(KERN_INFO "SGI VINO driver version %s\n", @@ -4580,12 +4286,12 @@ static int __init vino_module_init(void) spin_lock_init(&vino_drvdata->input_lock); ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A, - vino_v4l_device_name_a); + vino_vdev_name_a); if (ret) return ret; ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B, - vino_v4l_device_name_b); + vino_vdev_name_b); if (ret) return ret; @@ -4601,15 +4307,16 @@ static int __init vino_module_init(void) } vino_init_stage++; - ret = vino_i2c_add_bus(); + ret = i2c_add_adapter(&vino_i2c_adapter); if (ret) { printk(KERN_ERR "VINO I2C bus registration failed\n"); vino_module_cleanup(vino_init_stage); return ret; } + i2c_set_adapdata(&vino_i2c_adapter, &vino_drvdata->v4l2_dev); vino_init_stage++; - ret = video_register_device(vino_drvdata->a.v4l_device, + ret = video_register_device(vino_drvdata->a.vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { printk(KERN_ERR "VINO channel A Video4Linux-device " @@ -4619,7 +4326,7 @@ static int __init vino_module_init(void) } vino_init_stage++; - ret = video_register_device(vino_drvdata->b.v4l_device, + ret = video_register_device(vino_drvdata->b.vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { printk(KERN_ERR "VINO channel B Video4Linux-device " @@ -4629,10 +4336,12 @@ static int __init vino_module_init(void) } vino_init_stage++; -#ifdef MODULE - request_module("saa7191"); - request_module("indycam"); -#endif + addr[0] = 0x45; + vino_drvdata->decoder = v4l2_i2c_new_probed_subdev(&vino_i2c_adapter, + "saa7191", "saa7191", addr); + addr[0] = 0x2b; + vino_drvdata->camera = v4l2_i2c_new_probed_subdev(&vino_i2c_adapter, + "indycam", "indycam", addr); dprintk("init complete!\n"); diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 81d5aa5..fbfefae 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -28,17 +28,14 @@ #include <linux/mutex.h> #include <linux/videodev2.h> #include <linux/dma-mapping.h> -#ifdef CONFIG_VIDEO_V4L1_COMPAT -/* Include V4L1 specific functions. Should be removed soon */ -#include <linux/videodev.h> -#endif #include <linux/interrupt.h> -#include <media/videobuf-vmalloc.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> #include <linux/kthread.h> #include <linux/highmem.h> #include <linux/freezer.h> +#include <media/videobuf-vmalloc.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include "font.h" #define VIVI_MODULE_NAME "vivi" @@ -47,18 +44,32 @@ #define WAKE_DENOMINATOR 1001 #define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ -#include "font.h" - #define VIVI_MAJOR_VERSION 0 -#define VIVI_MINOR_VERSION 5 +#define VIVI_MINOR_VERSION 6 #define VIVI_RELEASE 0 #define VIVI_VERSION \ KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE) -/* Declare static vars that will be used as parameters */ -static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ -static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ -static int n_devs = 1; /* Number of virtual devices */ +MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board"); +MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol"); +MODULE_LICENSE("Dual BSD/GPL"); + +static unsigned video_nr = -1; +module_param(video_nr, uint, 0644); +MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect"); + +static unsigned n_devs = 1; +module_param(n_devs, uint, 0644); +MODULE_PARM_DESC(n_devs, "number of video devices to create"); + +static unsigned debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + +static unsigned int vid_limit = 16; +module_param(vid_limit, uint, 0644); +MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); + /* supported controls */ static struct v4l2_queryctrl vivi_qctrl[] = { @@ -69,7 +80,7 @@ static struct v4l2_queryctrl vivi_qctrl[] = { .maximum = 65535, .step = 65535/100, .default_value = 65535, - .flags = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, .type = V4L2_CTRL_TYPE_INTEGER, }, { .id = V4L2_CID_BRIGHTNESS, @@ -79,7 +90,7 @@ static struct v4l2_queryctrl vivi_qctrl[] = { .maximum = 255, .step = 1, .default_value = 127, - .flags = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, @@ -88,7 +99,7 @@ static struct v4l2_queryctrl vivi_qctrl[] = { .maximum = 255, .step = 0x1, .default_value = 0x10, - .flags = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, @@ -97,7 +108,7 @@ static struct v4l2_queryctrl vivi_qctrl[] = { .maximum = 255, .step = 0x1, .default_value = 127, - .flags = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_HUE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -106,17 +117,12 @@ static struct v4l2_queryctrl vivi_qctrl[] = { .maximum = 127, .step = 0x1, .default_value = 0, - .flags = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, } }; -static int qctl_regs[ARRAY_SIZE(vivi_qctrl)]; - -#define dprintk(dev, level, fmt, arg...) \ - do { \ - if (dev->vfd->debug >= (level)) \ - printk(KERN_DEBUG "vivi: " fmt , ## arg); \ - } while (0) +#define dprintk(dev, level, fmt, arg...) \ + v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg) /* ------------------------------------------------------------------ Basic structures @@ -206,6 +212,7 @@ static LIST_HEAD(vivi_devlist); struct vivi_dev { struct list_head vivi_devlist; + struct v4l2_device v4l2_dev; spinlock_t slock; struct mutex mutex; @@ -223,6 +230,12 @@ struct vivi_dev { char timestr[13]; int mv_count; /* Controls bars movement */ + + /* Input Number */ + int input; + + /* Control 'registers' */ + int qctl_regs[ARRAY_SIZE(vivi_qctrl)]; }; struct vivi_fh { @@ -235,6 +248,7 @@ struct vivi_fh { enum v4l2_buf_type type; unsigned char bars[8][3]; + int input; /* Input Number on bars */ }; /* ------------------------------------------------------------------ @@ -254,18 +268,72 @@ enum colors { BLACK, }; -static u8 bars[8][3] = { /* R G B */ - {204, 204, 204}, /* white */ - {208, 208, 0}, /* ambar */ - { 0, 206, 206}, /* cyan */ - { 0, 239, 0}, /* green */ - {239, 0, 239}, /* magenta */ - {205, 0, 0}, /* red */ - { 0, 0, 255}, /* blue */ - { 0, 0, 0}, /* black */ +#define COLOR_WHITE {204, 204, 204} +#define COLOR_AMBAR {208, 208, 0} +#define COLOR_CIAN { 0, 206, 206} +#define COLOR_GREEN { 0, 239, 0} +#define COLOR_MAGENTA {239, 0, 239} +#define COLOR_RED {205, 0, 0} +#define COLOR_BLUE { 0, 0, 255} +#define COLOR_BLACK { 0, 0, 0} + +struct bar_std { + u8 bar[8][3]; }; +/* Maximum number of bars are 10 - otherwise, the input print code + should be modified */ +static struct bar_std bars[] = { + { /* Standard ITU-R color bar sequence */ + { + COLOR_WHITE, + COLOR_AMBAR, + COLOR_CIAN, + COLOR_GREEN, + COLOR_MAGENTA, + COLOR_RED, + COLOR_BLUE, + COLOR_BLACK, + } + }, { + { + COLOR_WHITE, + COLOR_AMBAR, + COLOR_BLACK, + COLOR_WHITE, + COLOR_AMBAR, + COLOR_BLACK, + COLOR_WHITE, + COLOR_AMBAR, + } + }, { + { + COLOR_WHITE, + COLOR_CIAN, + COLOR_BLACK, + COLOR_WHITE, + COLOR_CIAN, + COLOR_BLACK, + COLOR_WHITE, + COLOR_CIAN, + } + }, { + { + COLOR_WHITE, + COLOR_GREEN, + COLOR_BLACK, + COLOR_WHITE, + COLOR_GREEN, + COLOR_BLACK, + COLOR_WHITE, + COLOR_GREEN, + } + }, +}; + +#define NUM_INPUTS ARRAY_SIZE(bars) + #define TO_Y(r, g, b) \ (((16829 * r + 33039 * g + 6416 * b + 32768) >> 16) + 16) /* RGB to V(Cr) Color transform */ @@ -275,9 +343,10 @@ static u8 bars[8][3] = { #define TO_U(r, g, b) \ (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128) -#define TSTAMP_MIN_Y 24 -#define TSTAMP_MAX_Y TSTAMP_MIN_Y+15 -#define TSTAMP_MIN_X 64 +#define TSTAMP_MIN_Y 24 +#define TSTAMP_MAX_Y (TSTAMP_MIN_Y + 15) +#define TSTAMP_INPUT_X 10 +#define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X) static void gen_twopix(struct vivi_fh *fh, unsigned char *buf, int colorpos) { @@ -392,9 +461,29 @@ static void gen_line(struct vivi_fh *fh, char *basep, int inipos, int wmax, pos += 4; /* only 16 bpp supported for now */ } - /* Checks if it is possible to show timestamp */ + /* Prints input entry number */ + + /* Checks if it is possible to input number */ if (TSTAMP_MAX_Y >= hmax) goto end; + + if (TSTAMP_INPUT_X + strlen(timestr) >= wmax) + goto end; + + if (line >= TSTAMP_MIN_Y && line <= TSTAMP_MAX_Y) { + chr = rom8x16_bits[fh->input * 16 + line - TSTAMP_MIN_Y]; + pos = TSTAMP_INPUT_X; + for (i = 0; i < 7; i++) { + /* Draw white font on black background */ + if (chr & 1 << (7 - i)) + gen_twopix(fh, basep + pos, WHITE); + else + gen_twopix(fh, basep + pos, BLACK); + pos += 2; + } + } + + /* Checks if it is possible to show timestamp */ if (TSTAMP_MIN_X + strlen(timestr) >= wmax) goto end; @@ -577,7 +666,7 @@ static int vivi_start_thread(struct vivi_fh *fh) dma_q->kthread = kthread_run(vivi_thread, fh, "vivi"); if (IS_ERR(dma_q->kthread)) { - printk(KERN_ERR "vivi: kernel_thread() failed\n"); + v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n"); return PTR_ERR(dma_q->kthread); } /* Wakes thread */ @@ -720,8 +809,12 @@ static struct videobuf_queue_ops vivi_video_qops = { static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { + struct vivi_fh *fh = priv; + struct vivi_dev *dev = fh->dev; + strcpy(cap->driver, "vivi"); strcpy(cap->card, "vivi"); + strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); cap->version = VIVI_VERSION; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | @@ -807,38 +900,19 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return 0; } -/*FIXME: This seems to be generic enough to be at videodev2 */ -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +/* precalculate color bar values to speed up rendering */ +static void precalculate_bars(struct vivi_fh *fh) { - struct vivi_fh *fh = priv; - struct videobuf_queue *q = &fh->vb_vidq; + struct vivi_dev *dev = fh->dev; unsigned char r, g, b; int k, is_yuv; - int ret = vidioc_try_fmt_vid_cap(file, fh, f); - if (ret < 0) - return (ret); - - mutex_lock(&q->vb_lock); - - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - dprintk(fh->dev, 1, "%s queue busy\n", __func__); - ret = -EBUSY; - goto out; - } - - fh->fmt = get_format(f); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vb_vidq.field = f->fmt.pix.field; - fh->type = f->type; + fh->input = dev->input; - /* precalculate color bar values to speed up rendering */ for (k = 0; k < 8; k++) { - r = bars[k][0]; - g = bars[k][1]; - b = bars[k][2]; + r = bars[fh->input].bar[k][0]; + g = bars[fh->input].bar[k][1]; + b = bars[fh->input].bar[k][2]; is_yuv = 0; switch (fh->fmt->fourcc) { @@ -871,11 +945,40 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, } } +} + +/*FIXME: This seems to be generic enough to be at videodev2 */ +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct vivi_fh *fh = priv; + struct videobuf_queue *q = &fh->vb_vidq; + + int ret = vidioc_try_fmt_vid_cap(file, fh, f); + if (ret < 0) + return ret; + + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + dprintk(fh->dev, 1, "%s queue busy\n", __func__); + ret = -EBUSY; + goto out; + } + + fh->fmt = get_format(f); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + + precalculate_bars(fh); + ret = 0; out: mutex_unlock(&q->vb_lock); - return (ret); + return ret; } static int vidioc_reqbufs(struct file *file, void *priv, @@ -950,27 +1053,36 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { - if (inp->index != 0) + if (inp->index >= NUM_INPUTS) return -EINVAL; inp->type = V4L2_INPUT_TYPE_CAMERA; inp->std = V4L2_STD_525_60; - strcpy(inp->name, "Camera"); + sprintf(inp->name, "Camera %u", inp->index); return (0); } static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { - *i = 0; + struct vivi_fh *fh = priv; + struct vivi_dev *dev = fh->dev; + + *i = dev->input; return (0); } static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { - if (i > 0) + struct vivi_fh *fh = priv; + struct vivi_dev *dev = fh->dev; + + if (i >= NUM_INPUTS) return -EINVAL; + dev->input = i; + precalculate_bars(fh); + return (0); } @@ -993,12 +1105,14 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { + struct vivi_fh *fh = priv; + struct vivi_dev *dev = fh->dev; int i; for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) if (ctrl->id == vivi_qctrl[i].id) { - ctrl->value = qctl_regs[i]; - return (0); + ctrl->value = dev->qctl_regs[i]; + return 0; } return -EINVAL; @@ -1006,16 +1120,18 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { + struct vivi_fh *fh = priv; + struct vivi_dev *dev = fh->dev; int i; for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) if (ctrl->id == vivi_qctrl[i].id) { - if (ctrl->value < vivi_qctrl[i].minimum - || ctrl->value > vivi_qctrl[i].maximum) { - return (-ERANGE); - } - qctl_regs[i] = ctrl->value; - return (0); + if (ctrl->value < vivi_qctrl[i].minimum || + ctrl->value > vivi_qctrl[i].maximum) { + return -ERANGE; + } + dev->qctl_regs[i] = ctrl->value; + return 0; } return -EINVAL; } @@ -1026,32 +1142,20 @@ static int vidioc_s_ctrl(struct file *file, void *priv, static int vivi_open(struct file *file) { - int minor = video_devdata(file)->minor; - struct vivi_dev *dev; + struct vivi_dev *dev = video_drvdata(file); struct vivi_fh *fh = NULL; - int i; int retval = 0; - printk(KERN_DEBUG "vivi: open called (minor=%d)\n", minor); - - lock_kernel(); - list_for_each_entry(dev, &vivi_devlist, vivi_devlist) - if (dev->vfd->minor == minor) - goto found; - unlock_kernel(); - return -ENODEV; - -found: mutex_lock(&dev->mutex); dev->users++; if (dev->users > 1) { dev->users--; - retval = -EBUSY; - goto unlock; + mutex_unlock(&dev->mutex); + return -EBUSY; } - dprintk(dev, 1, "open minor=%d type=%s users=%d\n", minor, + dprintk(dev, 1, "open /dev/video%d type=%s users=%d\n", dev->vfd->num, v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users); /* allocate + initialize per filehandle data */ @@ -1059,14 +1163,11 @@ found: if (NULL == fh) { dev->users--; retval = -ENOMEM; - goto unlock; } -unlock: mutex_unlock(&dev->mutex); - if (retval) { - unlock_kernel(); + + if (retval) return retval; - } file->private_data = fh; fh->dev = dev; @@ -1076,10 +1177,6 @@ unlock: fh->width = 640; fh->height = 480; - /* Put all controls at a sane state */ - for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) - qctl_regs[i] = vivi_qctrl[i].default_value; - /* Resets frame counters */ dev->h = 0; dev->m = 0; @@ -1095,7 +1192,6 @@ unlock: sizeof(struct vivi_buffer), fh); vivi_start_thread(fh); - unlock_kernel(); return 0; } @@ -1151,32 +1247,6 @@ static int vivi_close(struct file *file) return 0; } -static int vivi_release(void) -{ - struct vivi_dev *dev; - struct list_head *list; - - while (!list_empty(&vivi_devlist)) { - list = vivi_devlist.next; - list_del(list); - dev = list_entry(list, struct vivi_dev, vivi_devlist); - - if (-1 != dev->vfd->minor) { - printk(KERN_INFO "%s: unregistering /dev/video%d\n", - VIVI_MODULE_NAME, dev->vfd->num); - video_unregister_device(dev->vfd); - } else { - printk(KERN_INFO "%s: releasing /dev/video%d\n", - VIVI_MODULE_NAME, dev->vfd->num); - video_device_release(dev->vfd); - } - - kfree(dev); - } - - return 0; -} - static int vivi_mmap(struct file *file, struct vm_area_struct *vma) { struct vivi_fh *fh = file->private_data; @@ -1239,87 +1309,130 @@ static struct video_device vivi_template = { .tvnorms = V4L2_STD_525_60, .current_norm = V4L2_STD_NTSC_M, }; + /* ----------------------------------------------------------------- Initialization and module stuff ------------------------------------------------------------------*/ -/* This routine allocates from 1 to n_devs virtual drivers. +static int vivi_release(void) +{ + struct vivi_dev *dev; + struct list_head *list; - The real maximum number of virtual drivers will depend on how many drivers - will succeed. This is limited to the maximum number of devices that - videodev supports. Since there are 64 minors for video grabbers, this is - currently the theoretical maximum limit. However, a further limit does - exist at videodev that forbids any driver to register more than 32 video - grabbers. - */ -static int __init vivi_init(void) + while (!list_empty(&vivi_devlist)) { + list = vivi_devlist.next; + list_del(list); + dev = list_entry(list, struct vivi_dev, vivi_devlist); + + v4l2_info(&dev->v4l2_dev, "unregistering /dev/video%d\n", + dev->vfd->num); + video_unregister_device(dev->vfd); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + } + + return 0; +} + +static int __init vivi_create_instance(int inst) { - int ret = -ENOMEM, i; struct vivi_dev *dev; struct video_device *vfd; + int ret, i; - if (n_devs <= 0) - n_devs = 1; + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; - for (i = 0; i < n_devs; i++) { - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - break; + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), + "%s-%03d", VIVI_MODULE_NAME, inst); + ret = v4l2_device_register(NULL, &dev->v4l2_dev); + if (ret) + goto free_dev; - /* init video dma queues */ - INIT_LIST_HEAD(&dev->vidq.active); - init_waitqueue_head(&dev->vidq.wq); + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + init_waitqueue_head(&dev->vidq.wq); - /* initialize locks */ - spin_lock_init(&dev->slock); - mutex_init(&dev->mutex); + /* initialize locks */ + spin_lock_init(&dev->slock); + mutex_init(&dev->mutex); - vfd = video_device_alloc(); - if (!vfd) { - kfree(dev); - break; - } + ret = -ENOMEM; + vfd = video_device_alloc(); + if (!vfd) + goto unreg_dev; - *vfd = vivi_template; + *vfd = vivi_template; - ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); - if (ret < 0) { - video_device_release(vfd); - kfree(dev); + ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); + if (ret < 0) + goto rel_vdev; - /* If some registers succeeded, keep driver */ - if (i) - ret = 0; + video_set_drvdata(vfd, dev); - break; - } + /* Set all controls to their default value. */ + for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++) + dev->qctl_regs[i] = vivi_qctrl[i].default_value; + + /* Now that everything is fine, let's add it to device list */ + list_add_tail(&dev->vivi_devlist, &vivi_devlist); + + snprintf(vfd->name, sizeof(vfd->name), "%s (%i)", + vivi_template.name, vfd->num); + + if (video_nr >= 0) + video_nr++; - /* Now that everything is fine, let's add it to device list */ - list_add_tail(&dev->vivi_devlist, &vivi_devlist); + dev->vfd = vfd; + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as /dev/video%d\n", + vfd->num); + return 0; + +rel_vdev: + video_device_release(vfd); +unreg_dev: + v4l2_device_unregister(&dev->v4l2_dev); +free_dev: + kfree(dev); + return ret; +} + +/* This routine allocates from 1 to n_devs virtual drivers. - snprintf(vfd->name, sizeof(vfd->name), "%s (%i)", - vivi_template.name, vfd->minor); + The real maximum number of virtual drivers will depend on how many drivers + will succeed. This is limited to the maximum number of devices that + videodev supports, which is equal to VIDEO_NUM_DEVICES. + */ +static int __init vivi_init(void) +{ + int ret = 0, i; - if (video_nr >= 0) - video_nr++; + if (n_devs <= 0) + n_devs = 1; - dev->vfd = vfd; - printk(KERN_INFO "%s: V4L2 device registered as /dev/video%d\n", - VIVI_MODULE_NAME, vfd->num); + for (i = 0; i < n_devs; i++) { + ret = vivi_create_instance(i); + if (ret) { + /* If some instantiations succeeded, keep driver */ + if (i) + ret = 0; + break; + } } if (ret < 0) { - vivi_release(); printk(KERN_INFO "Error %d while loading vivi driver\n", ret); - } else { - printk(KERN_INFO "Video Technology Magazine Virtual Video " + return ret; + } + + printk(KERN_INFO "Video Technology Magazine Virtual Video " "Capture Board ver %u.%u.%u successfully loaded.\n", (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF, VIVI_VERSION & 0xFF); - /* n_devs will reflect the actual number of allocated devices */ - n_devs = i; - } + /* n_devs will reflect the actual number of allocated devices */ + n_devs = i; return ret; } @@ -1331,19 +1444,3 @@ static void __exit vivi_exit(void) module_init(vivi_init); module_exit(vivi_exit); - -MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board"); -MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol"); -MODULE_LICENSE("Dual BSD/GPL"); - -module_param(video_nr, uint, 0444); -MODULE_PARM_DESC(video_nr, "video iminor start number"); - -module_param(n_devs, uint, 0444); -MODULE_PARM_DESC(n_devs, "number of video devices to create"); - -module_param_named(debug, vivi_template.debug, int, 0444); -MODULE_PARM_DESC(debug, "activates debug info"); - -module_param(vid_limit, int, 0644); -MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c index 5d73f66..42e23a4 100644 --- a/drivers/media/video/vp27smpx.c +++ b/drivers/media/video/vp27smpx.c @@ -129,11 +129,6 @@ static int vp27smpx_log_status(struct v4l2_subdev *sd) return 0; } -static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { @@ -206,8 +201,6 @@ MODULE_DEVICE_TABLE(i2c, vp27smpx_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "vp27smpx", - .driverid = I2C_DRIVERID_VP27SMPX, - .command = vp27smpx_command, .probe = vp27smpx_probe, .remove = vp27smpx_remove, .id_table = vp27smpx_id, diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c index 67aa0db..2fa7e8b 100644 --- a/drivers/media/video/vpx3220.c +++ b/drivers/media/video/vpx3220.c @@ -24,10 +24,10 @@ #include <linux/types.h> #include <asm/uaccess.h> #include <linux/i2c.h> -#include <media/v4l2-common.h> -#include <media/v4l2-i2c-drv-legacy.h> -#include <linux/videodev.h> -#include <linux/video_decoder.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver"); MODULE_AUTHOR("Laurent Pinchart"); @@ -37,14 +37,17 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); + #define VPX_TIMEOUT_COUNT 10 /* ----------------------------------------------------------------------- */ struct vpx3220 { + struct v4l2_subdev sd; unsigned char reg[255]; - int norm; + v4l2_std_id norm; + int ident; int input; int enable; int bright; @@ -53,30 +56,38 @@ struct vpx3220 { int sat; }; +static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd) +{ + return container_of(sd, struct vpx3220, sd); +} + static char *inputs[] = { "internal", "composite", "svideo" }; /* ----------------------------------------------------------------------- */ -static inline int vpx3220_write(struct i2c_client *client, u8 reg, u8 value) +static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); struct vpx3220 *decoder = i2c_get_clientdata(client); decoder->reg[reg] = value; return i2c_smbus_write_byte_data(client, reg, value); } -static inline int vpx3220_read(struct i2c_client *client, u8 reg) +static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } -static int vpx3220_fp_status(struct i2c_client *client) +static int vpx3220_fp_status(struct v4l2_subdev *sd) { unsigned char status; unsigned int i; for (i = 0; i < VPX_TIMEOUT_COUNT; i++) { - status = vpx3220_read(client, 0x29); + status = vpx3220_read(sd, 0x29); if (!(status & 4)) return 0; @@ -90,57 +101,60 @@ static int vpx3220_fp_status(struct i2c_client *client) return -1; } -static int vpx3220_fp_write(struct i2c_client *client, u8 fpaddr, u16 data) +static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + /* Write the 16-bit address to the FPWR register */ if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) { - v4l_dbg(1, debug, client, "%s: failed\n", __func__); + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } - if (vpx3220_fp_status(client) < 0) + if (vpx3220_fp_status(sd) < 0) return -1; /* Write the 16-bit data to the FPDAT register */ if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) { - v4l_dbg(1, debug, client, "%s: failed\n", __func__); + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } return 0; } -static u16 vpx3220_fp_read(struct i2c_client *client, u16 fpaddr) +static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr) { + struct i2c_client *client = v4l2_get_subdevdata(sd); s16 data; /* Write the 16-bit address to the FPRD register */ if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) { - v4l_dbg(1, debug, client, "%s: failed\n", __func__); + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } - if (vpx3220_fp_status(client) < 0) + if (vpx3220_fp_status(sd) < 0) return -1; /* Read the 16-bit data from the FPDAT register */ data = i2c_smbus_read_word_data(client, 0x28); if (data == -1) { - v4l_dbg(1, debug, client, "%s: failed\n", __func__); + v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } return swab16(data); } -static int vpx3220_write_block(struct i2c_client *client, const u8 *data, unsigned int len) +static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) { u8 reg; int ret = -1; while (len >= 2) { reg = *data++; - ret = vpx3220_write(client, reg, *data++); + ret = vpx3220_write(sd, reg, *data++); if (ret < 0) break; len -= 2; @@ -149,7 +163,7 @@ static int vpx3220_write_block(struct i2c_client *client, const u8 *data, unsign return ret; } -static int vpx3220_write_fp_block(struct i2c_client *client, +static int vpx3220_write_fp_block(struct v4l2_subdev *sd, const u16 *data, unsigned int len) { u8 reg; @@ -157,7 +171,7 @@ static int vpx3220_write_fp_block(struct i2c_client *client, while (len > 1) { reg = *data++; - ret |= vpx3220_fp_write(client, reg, *data++); + ret |= vpx3220_fp_write(sd, reg, *data++); len -= 2; } @@ -259,276 +273,277 @@ static const unsigned short init_fp[] = { 0x4b, 0x298, /* PLL gain */ }; -static void vpx3220_dump_i2c(struct i2c_client *client) -{ - int len = sizeof(init_common); - const unsigned char *data = init_common; - while (len > 1) { - v4l_dbg(1, debug, client, "i2c reg 0x%02x data 0x%02x\n", - *data, vpx3220_read(client, *data)); - data += 2; - len -= 2; - } +static int vpx3220_init(struct v4l2_subdev *sd, u32 val) +{ + struct vpx3220 *decoder = to_vpx3220(sd); + + vpx3220_write_block(sd, init_common, sizeof(init_common)); + vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); + if (decoder->norm & V4L2_STD_NTSC) + vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); + else if (decoder->norm & V4L2_STD_PAL) + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); + else if (decoder->norm & V4L2_STD_SECAM) + vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); + else + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); + return 0; } -static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) +static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) { - struct vpx3220 *decoder = i2c_get_clientdata(client); + int res = V4L2_IN_ST_NO_SIGNAL, status; + v4l2_std_id std = 0; - switch (cmd) { - case 0: - { - vpx3220_write_block(client, init_common, - sizeof(init_common)); - vpx3220_write_fp_block(client, init_fp, - sizeof(init_fp) >> 1); - switch (decoder->norm) { - case VIDEO_MODE_NTSC: - vpx3220_write_fp_block(client, init_ntsc, - sizeof(init_ntsc) >> 1); - break; + status = vpx3220_fp_read(sd, 0x0f3); + + v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status); + + if (status < 0) + return status; - case VIDEO_MODE_PAL: - vpx3220_write_fp_block(client, init_pal, - sizeof(init_pal) >> 1); + if ((status & 0x20) == 0) { + res = 0; + + switch (status & 0x18) { + case 0x00: + case 0x10: + case 0x14: + case 0x18: + std = V4L2_STD_PAL; break; - case VIDEO_MODE_SECAM: - vpx3220_write_fp_block(client, init_secam, - sizeof(init_secam) >> 1); + + case 0x08: + std = V4L2_STD_SECAM; break; - default: - vpx3220_write_fp_block(client, init_pal, - sizeof(init_pal) >> 1); + + case 0x04: + case 0x0c: + case 0x1c: + std = V4L2_STD_NTSC; break; } - break; - } - - case DECODER_DUMP: - { - vpx3220_dump_i2c(client); - break; } + if (pstd) + *pstd = std; + if (pstatus) + *pstatus = status; + return 0; +} - case DECODER_GET_CAPABILITIES: - { - struct video_decoder_capability *cap = arg; +static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + v4l2_dbg(1, debug, sd, "querystd\n"); + return vpx3220_status(sd, NULL, std); +} - v4l_dbg(1, debug, client, "DECODER_GET_CAPABILITIES\n"); +static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + v4l2_dbg(1, debug, sd, "g_input_status\n"); + return vpx3220_status(sd, status, NULL); +} - cap->flags = VIDEO_DECODER_PAL | - VIDEO_DECODER_NTSC | - VIDEO_DECODER_SECAM | - VIDEO_DECODER_AUTO | - VIDEO_DECODER_CCIR; - cap->inputs = 3; - cap->outputs = 1; - break; +static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct vpx3220 *decoder = to_vpx3220(sd); + int temp_input; + + /* Here we back up the input selection because it gets + overwritten when we fill the registers with the + choosen video norm */ + temp_input = vpx3220_fp_read(sd, 0xf2); + + v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std); + if (std & V4L2_STD_NTSC) { + vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); + v4l2_dbg(1, debug, sd, "norm switched to NTSC\n"); + } else if (std & V4L2_STD_PAL) { + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); + v4l2_dbg(1, debug, sd, "norm switched to PAL\n"); + } else if (std & V4L2_STD_SECAM) { + vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); + v4l2_dbg(1, debug, sd, "norm switched to SECAM\n"); + } else { + return -EINVAL; } - case DECODER_GET_STATUS: - { - int res = 0, status; + decoder->norm = std; - v4l_dbg(1, debug, client, "DECODER_GET_STATUS\n"); - - status = vpx3220_fp_read(client, 0x0f3); - - v4l_dbg(1, debug, client, "status: 0x%04x\n", status); - - if (status < 0) - return status; + /* And here we set the backed up video input again */ + vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010); + udelay(10); + return 0; +} - if ((status & 0x20) == 0) { - res |= DECODER_STATUS_GOOD | DECODER_STATUS_COLOR; +static int vpx3220_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + int data; - switch (status & 0x18) { - case 0x00: - case 0x10: - case 0x14: - case 0x18: - res |= DECODER_STATUS_PAL; - break; + /* RJ: route->input = 0: ST8 (PCTV) input + route->input = 1: COMPOSITE input + route->input = 2: SVHS input */ - case 0x08: - res |= DECODER_STATUS_SECAM; - break; + const int input[3][2] = { + {0x0c, 0}, + {0x0d, 0}, + {0x0e, 1} + }; - case 0x04: - case 0x0c: - case 0x1c: - res |= DECODER_STATUS_NTSC; - break; - } - } + if (route->input < 0 || route->input > 2) + return -EINVAL; - *(int *) arg = res; - break; - } + v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[route->input]); - case DECODER_SET_NORM: - { - int *iarg = arg, data; - int temp_input; - - /* Here we back up the input selection because it gets - overwritten when we fill the registers with the - choosen video norm */ - temp_input = vpx3220_fp_read(client, 0xf2); - - v4l_dbg(1, debug, client, "DECODER_SET_NORM %d\n", *iarg); - switch (*iarg) { - case VIDEO_MODE_NTSC: - vpx3220_write_fp_block(client, init_ntsc, - sizeof(init_ntsc) >> 1); - v4l_dbg(1, debug, client, "norm switched to NTSC\n"); - break; + vpx3220_write(sd, 0x33, input[route->input][0]); - case VIDEO_MODE_PAL: - vpx3220_write_fp_block(client, init_pal, - sizeof(init_pal) >> 1); - v4l_dbg(1, debug, client, "norm switched to PAL\n"); - break; + data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020); + if (data < 0) + return data; + /* 0x0010 is required to latch the setting */ + vpx3220_fp_write(sd, 0xf2, + data | (input[route->input][1] << 5) | 0x0010); - case VIDEO_MODE_SECAM: - vpx3220_write_fp_block(client, init_secam, - sizeof(init_secam) >> 1); - v4l_dbg(1, debug, client, "norm switched to SECAM\n"); - break; + udelay(10); + return 0; +} - case VIDEO_MODE_AUTO: - /* FIXME This is only preliminary support */ - data = vpx3220_fp_read(client, 0xf2) & 0x20; - vpx3220_fp_write(client, 0xf2, 0x00c0 | data); - v4l_dbg(1, debug, client, "norm switched to AUTO\n"); - break; +static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable) +{ + v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off"); - default: - return -EINVAL; - } - decoder->norm = *iarg; + vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00)); + return 0; +} - /* And here we set the backed up video input again */ - vpx3220_fp_write(client, 0xf2, temp_input | 0x0010); - udelay(10); +static int vpx3220_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); break; - } - - case DECODER_SET_INPUT: - { - int *iarg = arg, data; - - /* RJ: *iarg = 0: ST8 (PCTV) input - *iarg = 1: COMPOSITE input - *iarg = 2: SVHS input */ - - const int input[3][2] = { - {0x0c, 0}, - {0x0d, 0}, - {0x0e, 1} - }; - if (*iarg < 0 || *iarg > 2) - return -EINVAL; - - v4l_dbg(1, debug, client, "input switched to %s\n", inputs[*iarg]); - - vpx3220_write(client, 0x33, input[*iarg][0]); - - data = vpx3220_fp_read(client, 0xf2) & ~(0x0020); - if (data < 0) - return data; - /* 0x0010 is required to latch the setting */ - vpx3220_fp_write(client, 0xf2, - data | (input[*iarg][1] << 5) | 0x0010); - - udelay(10); + case V4L2_CID_CONTRAST: + v4l2_ctrl_query_fill(qc, 0, 63, 1, 32); break; - } - case DECODER_SET_OUTPUT: - { - int *iarg = arg; + case V4L2_CID_SATURATION: + v4l2_ctrl_query_fill(qc, 0, 4095, 1, 2048); + break; - /* not much choice of outputs */ - if (*iarg != 0) { - return -EINVAL; - } + case V4L2_CID_HUE: + v4l2_ctrl_query_fill(qc, -512, 511, 1, 0); break; - } - case DECODER_ENABLE_OUTPUT: - { - int *iarg = arg; + default: + return -EINVAL; + } + return 0; +} - v4l_dbg(1, debug, client, "DECODER_ENABLE_OUTPUT %d\n", *iarg); +static int vpx3220_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct vpx3220 *decoder = to_vpx3220(sd); - vpx3220_write(client, 0xf2, (*iarg ? 0x1b : 0x00)); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = decoder->bright; + break; + case V4L2_CID_CONTRAST: + ctrl->value = decoder->contrast; break; + case V4L2_CID_SATURATION: + ctrl->value = decoder->sat; + break; + case V4L2_CID_HUE: + ctrl->value = decoder->hue; + break; + default: + return -EINVAL; } + return 0; +} - case DECODER_SET_PICTURE: - { - struct video_picture *pic = arg; +static int vpx3220_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct vpx3220 *decoder = to_vpx3220(sd); - if (decoder->bright != pic->brightness) { - /* We want -128 to 128 we get 0-65535 */ - decoder->bright = pic->brightness; - vpx3220_write(client, 0xe6, - (decoder->bright - 32768) >> 8); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (decoder->bright != ctrl->value) { + decoder->bright = ctrl->value; + vpx3220_write(sd, 0xe6, decoder->bright); } - if (decoder->contrast != pic->contrast) { - /* We want 0 to 64 we get 0-65535 */ + break; + case V4L2_CID_CONTRAST: + if (decoder->contrast != ctrl->value) { /* Bit 7 and 8 is for noise shaping */ - decoder->contrast = pic->contrast; - vpx3220_write(client, 0xe7, - (decoder->contrast >> 10) + 192); + decoder->contrast = ctrl->value; + vpx3220_write(sd, 0xe7, decoder->contrast + 192); } - if (decoder->sat != pic->colour) { - /* We want 0 to 4096 we get 0-65535 */ - decoder->sat = pic->colour; - vpx3220_fp_write(client, 0xa0, - decoder->sat >> 4); + break; + case V4L2_CID_SATURATION: + if (decoder->sat != ctrl->value) { + decoder->sat = ctrl->value; + vpx3220_fp_write(sd, 0xa0, decoder->sat); } - if (decoder->hue != pic->hue) { - /* We want -512 to 512 we get 0-65535 */ - decoder->hue = pic->hue; - vpx3220_fp_write(client, 0x1c, - ((decoder->hue - 32768) >> 6) & 0xFFF); + break; + case V4L2_CID_HUE: + if (decoder->hue != ctrl->value) { + decoder->hue = ctrl->value; + vpx3220_fp_write(sd, 0x1c, decoder->hue); } break; - } - default: return -EINVAL; } - return 0; } -static int vpx3220_init_client(struct i2c_client *client) +static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { - vpx3220_write_block(client, init_common, sizeof(init_common)); - vpx3220_write_fp_block(client, init_fp, sizeof(init_fp) >> 1); - /* Default to PAL */ - vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1); + struct vpx3220 *decoder = to_vpx3220(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - return 0; + return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); } +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops vpx3220_core_ops = { + .g_chip_ident = vpx3220_g_chip_ident, + .init = vpx3220_init, + .g_ctrl = vpx3220_g_ctrl, + .s_ctrl = vpx3220_s_ctrl, + .queryctrl = vpx3220_queryctrl, +}; + +static const struct v4l2_subdev_tuner_ops vpx3220_tuner_ops = { + .s_std = vpx3220_s_std, +}; + +static const struct v4l2_subdev_video_ops vpx3220_video_ops = { + .s_routing = vpx3220_s_routing, + .s_stream = vpx3220_s_stream, + .querystd = vpx3220_querystd, + .g_input_status = vpx3220_g_input_status, +}; + +static const struct v4l2_subdev_ops vpx3220_ops = { + .core = &vpx3220_core_ops, + .tuner = &vpx3220_tuner_ops, + .video = &vpx3220_video_ops, +}; + /* ----------------------------------------------------------------------- * Client management code */ -static unsigned short normal_i2c[] = { 0x86 >> 1, 0x8e >> 1, I2C_CLIENT_END }; - -I2C_CLIENT_INSMOD; - static int vpx3220_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct vpx3220 *decoder; + struct v4l2_subdev *sd; const char *name = NULL; u8 ver; u16 pn; @@ -541,18 +556,20 @@ static int vpx3220_probe(struct i2c_client *client, decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL); if (decoder == NULL) return -ENOMEM; - decoder->norm = VIDEO_MODE_PAL; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &vpx3220_ops); + decoder->norm = V4L2_STD_PAL; decoder->input = 0; decoder->enable = 1; decoder->bright = 32768; decoder->contrast = 32768; decoder->hue = 32768; decoder->sat = 32768; - i2c_set_clientdata(client, decoder); ver = i2c_smbus_read_byte_data(client, 0x00); pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) + i2c_smbus_read_byte_data(client, 0x01); + decoder->ident = V4L2_IDENT_VPX3220A; if (ver == 0xec) { switch (pn) { case 0x4680: @@ -560,26 +577,34 @@ static int vpx3220_probe(struct i2c_client *client, break; case 0x4260: name = "vpx3216b"; + decoder->ident = V4L2_IDENT_VPX3216B; break; case 0x4280: name = "vpx3214c"; + decoder->ident = V4L2_IDENT_VPX3214C; break; } } if (name) - v4l_info(client, "%s found @ 0x%x (%s)\n", name, + v4l2_info(sd, "%s found @ 0x%x (%s)\n", name, client->addr << 1, client->adapter->name); else - v4l_info(client, "chip (%02x:%04x) found @ 0x%x (%s)\n", + v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n", ver, pn, client->addr << 1, client->adapter->name); - vpx3220_init_client(client); + vpx3220_write_block(sd, init_common, sizeof(init_common)); + vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); + /* Default to PAL */ + vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); return 0; } static int vpx3220_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_vpx3220(sd)); return 0; } @@ -593,8 +618,6 @@ MODULE_DEVICE_TABLE(i2c, vpx3220_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "vpx3220", - .driverid = I2C_DRIVERID_VPX3220, - .command = vpx3220_command, .probe = vpx3220_probe, .remove = vpx3220_remove, .id_table = vpx3220_id, diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index 038ff32..dcade61 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -57,7 +57,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/delay.h> -#include <linux/videodev2.h> +#include <linux/videodev.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <linux/parport.h> diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c index 105a832..3b08bc4 100644 --- a/drivers/media/video/w9968cf.c +++ b/drivers/media/video/w9968cf.c @@ -42,6 +42,7 @@ #include <asm/page.h> #include <asm/uaccess.h> #include <linux/page-flags.h> +#include <linux/videodev.h> #include <media/v4l2-ioctl.h> #include "w9968cf.h" @@ -68,7 +69,6 @@ MODULE_VERSION(W9968CF_MODULE_VERSION); MODULE_LICENSE(W9968CF_MODULE_LICENSE); MODULE_SUPPORTED_DEVICE("Video"); -static int ovmod_load = W9968CF_OVMOD_LOAD; static unsigned short simcams = W9968CF_SIMCAMS; static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/ static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] = @@ -111,9 +111,6 @@ static int specific_debug = W9968CF_SPECIFIC_DEBUG; static unsigned int param_nv[24]; /* number of values per parameter */ -#ifdef CONFIG_MODULES -module_param(ovmod_load, bool, 0644); -#endif module_param(simcams, ushort, 0644); module_param_array(video_nr, short, ¶m_nv[0], 0444); module_param_array(packet_size, uint, ¶m_nv[1], 0444); @@ -144,18 +141,6 @@ module_param(debug, ushort, 0644); module_param(specific_debug, bool, 0644); #endif -#ifdef CONFIG_MODULES -MODULE_PARM_DESC(ovmod_load, - "\n<0|1> Automatic 'ovcamchip' module loading." - "\n0 disabled, 1 enabled." - "\nIf enabled,'insmod' searches for the required 'ovcamchip'" - "\nmodule in the system, according to its configuration, and" - "\nattempts to load that module automatically. This action is" - "\nperformed once as soon as the 'w9968cf' module is loaded" - "\ninto memory." - "\nDefault value is "__MODULE_STRING(W9968CF_OVMOD_LOAD)"." - "\n"); -#endif MODULE_PARM_DESC(simcams, "\n<n> Number of cameras allowed to stream simultaneously." "\nn may vary from 0 to " @@ -443,8 +428,6 @@ static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data*); static u32 w9968cf_i2c_func(struct i2c_adapter*); -static int w9968cf_i2c_attach_inform(struct i2c_client*); -static int w9968cf_i2c_detach_inform(struct i2c_client*); /* Memory management */ static void* rvmalloc(unsigned long size); @@ -1443,19 +1426,11 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { - struct w9968cf_device* cam = i2c_get_adapdata(adapter); + struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter); + struct w9968cf_device *cam = to_cam(v4l2_dev); u8 i; int err = 0; - switch (addr) { - case OV6xx0_SID: - case OV7xx0_SID: - break; - default: - DBG(4, "Rejected slave ID 0x%04X", addr) - return -EINVAL; - } - if (size == I2C_SMBUS_BYTE) { /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */ addr <<= 1; @@ -1463,8 +1438,17 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, if (read_write == I2C_SMBUS_WRITE) err = w9968cf_i2c_adap_write_byte(cam, addr, command); else if (read_write == I2C_SMBUS_READ) - err = w9968cf_i2c_adap_read_byte(cam,addr,&data->byte); - + for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) { + err = w9968cf_i2c_adap_read_byte(cam, addr, + &data->byte); + if (err) { + if (w9968cf_smbus_refresh_bus(cam)) { + err = -EIO; + break; + } + } else + break; + } } else if (size == I2C_SMBUS_BYTE_DATA) { addr <<= 1; @@ -1491,7 +1475,6 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, DBG(4, "Unsupported I2C transfer mode (%d)", size) return -EINVAL; } - return err; } @@ -1504,44 +1487,6 @@ static u32 w9968cf_i2c_func(struct i2c_adapter* adap) } -static int w9968cf_i2c_attach_inform(struct i2c_client* client) -{ - struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); - int id = client->driver->id, err = 0; - - if (id == I2C_DRIVERID_OVCAMCHIP) { - cam->sensor_client = client; - err = w9968cf_sensor_init(cam); - if (err) { - cam->sensor_client = NULL; - return err; - } - } else { - DBG(4, "Rejected client [%s] with driver [%s]", - client->name, client->driver->driver.name) - return -EINVAL; - } - - DBG(5, "I2C attach client [%s] with driver [%s]", - client->name, client->driver->driver.name) - - return 0; -} - - -static int w9968cf_i2c_detach_inform(struct i2c_client* client) -{ - struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); - - if (cam->sensor_client == client) - cam->sensor_client = NULL; - - DBG(5, "I2C detach client [%s]", client->name) - - return 0; -} - - static int w9968cf_i2c_init(struct w9968cf_device* cam) { int err = 0; @@ -1554,15 +1499,13 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam) static struct i2c_adapter adap = { .id = I2C_HW_SMBUS_W9968CF, .owner = THIS_MODULE, - .client_register = w9968cf_i2c_attach_inform, - .client_unregister = w9968cf_i2c_detach_inform, .algo = &algo, }; memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter)); strcpy(cam->i2c_adapter.name, "w9968cf"); cam->i2c_adapter.dev.parent = &cam->usbdev->dev; - i2c_set_adapdata(&cam->i2c_adapter, cam); + i2c_set_adapdata(&cam->i2c_adapter, &cam->v4l2_dev); DBG(6, "Registering I2C adapter with kernel...") @@ -2165,13 +2108,9 @@ w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int* val) static int w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg) { - struct i2c_client* c = cam->sensor_client; - int rc = 0; - - if (!c || !c->driver || !c->driver->command) - return -EINVAL; + int rc; - rc = c->driver->command(c, cmd, arg); + rc = v4l2_subdev_call(cam->sensor_sd, core, ioctl, cmd, arg); /* The I2C driver returns -EPERM on non-supported controls */ return (rc < 0 && rc != -EPERM) ? rc : 0; } @@ -2346,7 +2285,7 @@ static int w9968cf_sensor_init(struct w9968cf_device* cam) goto error; /* NOTE: Make sure width and height are a multiple of 16 */ - switch (cam->sensor_client->addr) { + switch (v4l2_i2c_subdev_addr(cam->sensor_sd)) { case OV6xx0_SID: cam->maxwidth = 352; cam->maxheight = 288; @@ -2651,6 +2590,7 @@ static void w9968cf_release_resources(struct w9968cf_device* cam) w9968cf_deallocate_memory(cam); kfree(cam->control_buffer); kfree(cam->data_buffer); + v4l2_device_unregister(&cam->v4l2_dev); mutex_unlock(&w9968cf_devlist_mutex); } @@ -3480,6 +3420,11 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) struct list_head* ptr; u8 sc = 0; /* number of simultaneous cameras */ static unsigned short dev_nr; /* 0 - we are handling device number n */ + static unsigned short addrs[] = { + OV7xx0_SID, + OV6xx0_SID, + I2C_CLIENT_END + }; if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[0].idVendor && le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[0].idProduct) @@ -3495,12 +3440,14 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) if (!cam) return -ENOMEM; + err = v4l2_device_register(&udev->dev, &cam->v4l2_dev); + if (err) + goto fail0; + mutex_init(&cam->dev_mutex); mutex_lock(&cam->dev_mutex); cam->usbdev = udev; - /* NOTE: a local copy is used to avoid possible race conditions */ - memcpy(&cam->dev, &udev->dev, sizeof(struct device)); DBG(2, "%s detected", symbolic(camlist, mod_id)) @@ -3549,7 +3496,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->minor = video_nr[dev_nr]; cam->v4ldev->release = video_device_release; video_set_drvdata(cam->v4ldev, cam); - cam->v4ldev->parent = &cam->dev; + cam->v4ldev->v4l2_dev = &cam->v4l2_dev; err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, video_nr[dev_nr]); @@ -3576,9 +3523,13 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) w9968cf_turn_on_led(cam); w9968cf_i2c_init(cam); + cam->sensor_sd = v4l2_i2c_new_probed_subdev(&cam->i2c_adapter, + "ovcamchip", "ovcamchip", addrs); usb_set_intfdata(intf, cam); mutex_unlock(&cam->dev_mutex); + + err = w9968cf_sensor_init(cam); return 0; fail: /* Free unused memory */ @@ -3587,6 +3538,8 @@ fail: /* Free unused memory */ if (cam->v4ldev) video_device_release(cam->v4ldev); mutex_unlock(&cam->dev_mutex); + v4l2_device_unregister(&cam->v4l2_dev); +fail0: kfree(cam); return err; } @@ -3597,15 +3550,16 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf) struct w9968cf_device* cam = (struct w9968cf_device*)usb_get_intfdata(intf); - down_write(&w9968cf_disconnect); - if (cam) { + down_write(&w9968cf_disconnect); /* Prevent concurrent accesses to data */ mutex_lock(&cam->dev_mutex); cam->disconnected = 1; - DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id)) + DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id)); + + v4l2_device_disconnect(&cam->v4l2_dev); wake_up_interruptible_all(&cam->open); @@ -3621,12 +3575,12 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf) w9968cf_release_resources(cam); mutex_unlock(&cam->dev_mutex); + up_write(&w9968cf_disconnect); - if (!cam->users) + if (!cam->users) { kfree(cam); + } } - - up_write(&w9968cf_disconnect); } @@ -3650,9 +3604,6 @@ static int __init w9968cf_module_init(void) KDBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION) KDBG(3, W9968CF_MODULE_AUTHOR) - if (ovmod_load) - request_module("ovcamchip"); - if ((err = usb_register(&w9968cf_usb_driver))) return err; diff --git a/drivers/media/video/w9968cf.h b/drivers/media/video/w9968cf.h index 30032e1..fdfc6a4 100644 --- a/drivers/media/video/w9968cf.h +++ b/drivers/media/video/w9968cf.h @@ -33,6 +33,7 @@ #include <linux/rwsem.h> #include <linux/mutex.h> +#include <media/v4l2-device.h> #include <media/ovcamchip.h> #include "w9968cf_vpp.h" @@ -42,7 +43,6 @@ * Default values * ****************************************************************************/ -#define W9968CF_OVMOD_LOAD 1 /* automatic 'ovcamchip' module loading */ #define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */ /* Comment/uncomment the following line to enable/disable debugging messages */ @@ -195,10 +195,9 @@ enum w9968cf_vpp_flag { /* Main device driver structure */ struct w9968cf_device { - struct device dev; /* device structure */ - enum w9968cf_model_id id; /* private device identifier */ + struct v4l2_device v4l2_dev; struct video_device* v4ldev; /* -> V4L structure */ struct list_head v4llist; /* entry of the list of V4L cameras */ @@ -265,7 +264,7 @@ struct w9968cf_device { /* I2C interface to kernel */ struct i2c_adapter i2c_adapter; - struct i2c_client* sensor_client; + struct v4l2_subdev *sensor_sd; /* Locks */ struct mutex dev_mutex, /* for probe, disconnect,open and close */ @@ -277,6 +276,11 @@ struct w9968cf_device { char command[16]; /* name of the program holding the device */ }; +static inline struct w9968cf_device *to_cam(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct w9968cf_device, v4l2_dev); +} + /**************************************************************************** * Macros for debugging * @@ -291,14 +295,14 @@ struct w9968cf_device { if ( ((specific_debug) && (debug == (level))) || \ ((!specific_debug) && (debug >= (level))) ) { \ if ((level) == 1) \ - dev_err(&cam->dev, fmt "\n", ## args); \ + v4l2_err(&cam->v4l2_dev, fmt "\n", ## args); \ else if ((level) == 2 || (level) == 3) \ - dev_info(&cam->dev, fmt "\n", ## args); \ + v4l2_info(&cam->v4l2_dev, fmt "\n", ## args); \ else if ((level) == 4) \ - dev_warn(&cam->dev, fmt "\n", ## args); \ + v4l2_warn(&cam->v4l2_dev, fmt "\n", ## args); \ else if ((level) >= 5) \ - dev_info(&cam->dev, "[%s:%d] " fmt "\n", \ - __func__, __LINE__ , ## args); \ + v4l2_info(&cam->v4l2_dev, "[%s:%d] " fmt "\n", \ + __func__, __LINE__ , ## args); \ } \ } /* For generic kernel (not device specific) messages */ @@ -321,7 +325,7 @@ struct w9968cf_device { #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->dev, "[%s:%d] " fmt "\n", __func__, __LINE__ , ## args); +v4l2_info(&cam->v4l2_dev, "[%s:%d] " fmt "\n", __func__, __LINE__ , ## args); #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */ diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index f2864d5..b572ce2 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -252,11 +252,6 @@ static int wm8739_log_status(struct v4l2_subdev *sd) return 0; } -static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops wm8739_core_ops = { @@ -343,8 +338,6 @@ MODULE_DEVICE_TABLE(i2c, wm8739_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "wm8739", - .driverid = I2C_DRIVERID_WM8739, - .command = wm8739_command, .probe = wm8739_probe, .remove = wm8739_remove, .id_table = wm8739_id, diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index 53fcd4284..eddf11a 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -34,15 +34,12 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> MODULE_DESCRIPTION("wm8775 driver"); MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); MODULE_LICENSE("GPL"); -static unsigned short normal_i2c[] = { 0x36 >> 1, I2C_CLIENT_END }; - -I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ @@ -161,11 +158,6 @@ static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fre return 0; } -static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops wm8775_core_ops = { @@ -268,8 +260,6 @@ MODULE_DEVICE_TABLE(i2c, wm8775_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "wm8775", - .driverid = I2C_DRIVERID_WM8775, - .command = wm8775_command, .probe = wm8775_probe, .remove = wm8775_remove, .id_table = wm8775_id, diff --git a/drivers/media/video/zc0301/zc0301_sensor.h b/drivers/media/video/zc0301/zc0301_sensor.h index b0cd49c..3a408de 100644 --- a/drivers/media/video/zc0301/zc0301_sensor.h +++ b/drivers/media/video/zc0301/zc0301_sensor.h @@ -58,12 +58,20 @@ zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor); .idProduct = (prod), \ .bInterfaceClass = (intclass) +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE #define ZC0301_ID_TABLE \ static const struct usb_device_id zc0301_id_table[] = { \ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ { ZC0301_USB_DEVICE(0x0ac8, 0x303b, 0xff), }, /* PB-0330 */ \ { } \ }; +#else +#define ZC0301_ID_TABLE \ +static const struct usb_device_id zc0301_id_table[] = { \ + { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ + { } \ +}; +#endif /*****************************************************************************/ diff --git a/drivers/media/video/zoran/Kconfig b/drivers/media/video/zoran/Kconfig index 4ea5fa7..fd4120e 100644 --- a/drivers/media/video/zoran/Kconfig +++ b/drivers/media/video/zoran/Kconfig @@ -1,6 +1,6 @@ config VIDEO_ZORAN tristate "Zoran ZR36057/36067 Video For Linux" - depends on PCI && I2C_ALGOBIT && VIDEO_V4L1 && VIRT_TO_BUS + depends on PCI && I2C_ALGOBIT && VIDEO_V4L2 && VIRT_TO_BUS help Say Y for support for MJPEG capture cards based on the Zoran 36057/36067 PCI controller chipset. This includes the Iomega @@ -32,7 +32,7 @@ config VIDEO_ZORAN_ZR36060 config VIDEO_ZORAN_BUZ tristate "Iomega Buz support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO help Support for the Iomega Buz MJPEG capture/playback card. @@ -58,7 +58,7 @@ config VIDEO_ZORAN_LML33 config VIDEO_ZORAN_LML33R10 tristate "Linux Media Labs LML33R10 support" depends on VIDEO_ZORAN_ZR36060 - select VIDEO_SAA7114 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO help support for the Linux Media Labs LML33R10 MJPEG capture/playback @@ -66,8 +66,9 @@ config VIDEO_ZORAN_LML33R10 config VIDEO_ZORAN_AVS6EYES tristate "AverMedia 6 Eyes support (EXPERIMENTAL)" - depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL && VIDEO_V4L1 + depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_BT866 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO help Support for the AverMedia 6 Eyes video surveillance card. diff --git a/drivers/media/video/zoran/videocodec.h b/drivers/media/video/zoran/videocodec.h index 97a3bbe..5c27b25 100644 --- a/drivers/media/video/zoran/videocodec.h +++ b/drivers/media/video/zoran/videocodec.h @@ -97,7 +97,7 @@ available) - it returns 0 if the mode is possible set_size -> this fn-ref. sets the norm and image size for compression/decompression (returns 0 on success) - the norm param is defined in videodev.h (VIDEO_MODE_*) + the norm param is defined in videodev2.h (V4L2_STD_*) additional setup may be available, too - but the codec should work with some default values even without this @@ -144,9 +144,8 @@ M zr36055[1] 0001 0000c001 00000000 (zr36050[1]) #ifndef __LINUX_VIDEOCODEC_H #define __LINUX_VIDEOCODEC_H -#include <linux/videodev.h> +#include <linux/videodev2.h> -//should be in videodev.h ??? (VID_DO_....) #define CODEC_DO_COMPRESSION 0 #define CODEC_DO_EXPANSION 1 @@ -237,10 +236,6 @@ struct vfe_settings { __u32 width, height; /* Area to capture */ __u16 decimation; /* Decimation divider */ __u16 flags; /* Flags for capture */ -/* flags are the same as in struct video_capture - see videodev.h: -#define VIDEO_CAPTURE_ODD 0 -#define VIDEO_CAPTURE_EVEN 1 -*/ __u16 quality; /* quality of the video */ }; diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h index e873a91..afecf32 100644 --- a/drivers/media/video/zoran/zoran.h +++ b/drivers/media/video/zoran/zoran.h @@ -31,6 +31,8 @@ #ifndef _BUZ_H_ #define _BUZ_H_ +#include <media/v4l2-device.h> + struct zoran_requestbuffers { unsigned long count; /* Number of buffers for MJPEG grabbing */ unsigned long size; /* Size PER BUFFER in bytes */ @@ -170,7 +172,7 @@ Private IOCTL to set up for displaying MJPEG #endif #define V4L_MASK_FRAME (V4L_MAX_FRAME - 1) -#define MAX_KMALLOC_MEM (128*1024) +#define MAX_FRAME (BUZ_MAX_FRAME > VIDEO_MAX_FRAME ? BUZ_MAX_FRAME : VIDEO_MAX_FRAME) #include "zr36057.h" @@ -240,9 +242,6 @@ enum gpcs_type { struct zoran_format { char *name; -#ifdef CONFIG_VIDEO_V4L1_COMPAT - int palette; -#endif __u32 fourcc; int colorspace; int depth; @@ -283,21 +282,21 @@ struct zoran_mapping { int count; }; -struct zoran_jpg_buffer { - struct zoran_mapping *map; - __le32 *frag_tab; /* addresses of frag table */ - u32 frag_tab_bus; /* same value cached to save time in ISR */ - enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */ - struct zoran_sync bs; /* DONE: info to return to application */ -}; - -struct zoran_v4l_buffer { +struct zoran_buffer { struct zoran_mapping *map; - char *fbuffer; /* virtual address of frame buffer */ - unsigned long fbuffer_phys; /* physical address of frame buffer */ - unsigned long fbuffer_bus; /* bus address of frame buffer */ - enum zoran_buffer_state state; /* state: unused/pending/done */ - struct zoran_sync bs; /* DONE: info to return to application */ + enum zoran_buffer_state state; /* state: unused/pending/dma/done */ + struct zoran_sync bs; /* DONE: info to return to application */ + union { + struct { + __le32 *frag_tab; /* addresses of frag table */ + u32 frag_tab_bus; /* same value cached to save time in ISR */ + } jpg; + struct { + char *fbuffer; /* virtual address of frame buffer */ + unsigned long fbuffer_phys;/* physical address of frame buffer */ + unsigned long fbuffer_bus;/* bus address of frame buffer */ + } v4l; + }; }; enum zoran_lock_activity { @@ -307,21 +306,13 @@ enum zoran_lock_activity { }; /* buffer collections */ -struct zoran_jpg_struct { +struct zoran_buffer_col { enum zoran_lock_activity active; /* feature currently in use? */ - struct zoran_jpg_buffer buffer[BUZ_MAX_FRAME]; /* buffers */ - int num_buffers, buffer_size; + unsigned int num_buffers, buffer_size; + struct zoran_buffer buffer[MAX_FRAME]; /* buffers */ u8 allocated; /* Flag if buffers are allocated */ - u8 ready_to_be_freed; /* hack - see zoran_driver.c */ u8 need_contiguous; /* Flag if contiguous buffers are needed */ -}; - -struct zoran_v4l_struct { - enum zoran_lock_activity active; /* feature currently in use? */ - struct zoran_v4l_buffer buffer[VIDEO_MAX_FRAME]; /* buffers */ - int num_buffers, buffer_size; - u8 allocated; /* Flag if buffers are allocated */ - u8 ready_to_be_freed; /* hack - see zoran_driver.c */ + /* only applies to jpg buffers, raw buffers are always contiguous */ }; struct zoran; @@ -330,23 +321,27 @@ struct zoran; struct zoran_fh { struct zoran *zr; - enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */ + enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */ struct zoran_overlay_settings overlay_settings; - u32 *overlay_mask; /* overlay mask */ - enum zoran_lock_activity overlay_active; /* feature currently in use? */ + u32 *overlay_mask; /* overlay mask */ + enum zoran_lock_activity overlay_active;/* feature currently in use? */ - struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ - struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */ + struct zoran_buffer_col buffers; /* buffers' info */ + struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */ struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */ - struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */ }; struct card_info { enum card_type type; char name[32]; - u16 i2c_decoder, i2c_encoder; /* I2C types */ + const char *i2c_decoder; /* i2c decoder device */ + const char *mod_decoder; /* i2c decoder module */ + const unsigned short *addrs_decoder; + const char *i2c_encoder; /* i2c encoder device */ + const char *mod_encoder; /* i2c encoder module */ + const unsigned short *addrs_encoder; u16 video_vfe, video_codec; /* videocodec types */ u16 audio_chip; /* audio type */ @@ -356,7 +351,7 @@ struct card_info { char name[32]; } input[BUZ_MAX_INPUT]; - int norms; + v4l2_std_id norms; struct tvnorm *tvn[3]; /* supported TV norms */ u32 jpeg_int; /* JPEG interrupt */ @@ -377,14 +372,15 @@ struct card_info { }; struct zoran { + struct v4l2_device v4l2_dev; struct video_device *video_dev; struct i2c_adapter i2c_adapter; /* */ struct i2c_algo_bit_data i2c_algo; /* */ u32 i2cbr; - struct i2c_client *decoder; /* video decoder i2c client */ - struct i2c_client *encoder; /* video encoder i2c client */ + struct v4l2_subdev *decoder; /* video decoder sub-device */ + struct v4l2_subdev *encoder; /* video encoder sub-device */ struct videocodec *codec; /* video codec */ struct videocodec *vfe; /* video front end */ @@ -405,9 +401,15 @@ struct zoran { spinlock_t spinlock; /* Spinlock */ /* Video for Linux parameters */ - int input, norm; /* card's norm and input - norm=VIDEO_MODE_* */ - int hue, saturation, contrast, brightness; /* Current picture params */ - struct video_buffer buffer; /* Current buffer params */ + int input; /* card's norm and input - norm=VIDEO_MODE_* */ + v4l2_std_id norm; + + /* Current buffer params */ + void *vbuf_base; + int vbuf_height, vbuf_width; + int vbuf_depth; + int vbuf_bytesperline; + struct zoran_overlay_settings overlay_settings; u32 *overlay_mask; /* overlay mask */ enum zoran_lock_activity overlay_active; /* feature currently in use? */ @@ -427,7 +429,7 @@ struct zoran { unsigned long v4l_pend_tail; unsigned long v4l_sync_tail; int v4l_pend[V4L_MAX_FRAME]; - struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */ + struct zoran_buffer_col v4l_buffers; /* V4L buffers' info */ /* Buz MJPEG parameters */ enum zoran_codec_mode codec_mode; /* status of codec */ @@ -454,7 +456,7 @@ struct zoran { int jpg_pend[BUZ_MAX_FRAME]; /* array indexed by frame number */ - struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */ + struct zoran_buffer_col jpg_buffers; /* MJPEG buffers' info */ /* Additional stuff for testing */ #ifdef CONFIG_PROC_FS @@ -488,6 +490,11 @@ struct zoran { wait_queue_head_t test_q; }; +static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct zoran, v4l2_dev); +} + /* There was something called _ALPHA_BUZ that used the PCI address instead of * the kernel iomapped address for btread/btwrite. */ #define btwrite(dat,adr) writel((dat), zr->zr36057_mem+(adr)) diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c index 5d2f090..f91bba4 100644 --- a/drivers/media/video/zoran/zoran_card.c +++ b/drivers/media/video/zoran/zoran_card.c @@ -38,8 +38,7 @@ #include <linux/proc_fs.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> -#include <linux/videodev.h> -#include <media/v4l2-common.h> +#include <linux/videodev2.h> #include <linux/spinlock.h> #include <linux/sem.h> #include <linux/kmod.h> @@ -47,11 +46,10 @@ #include <linux/pci.h> #include <linux/interrupt.h> -#include <linux/video_decoder.h> -#include <linux/video_encoder.h> #include <linux/mutex.h> - -#include <asm/io.h> +#include <linux/io.h> +#include <media/v4l2-common.h> +#include <media/bt819.h> #include "videocodec.h" #include "zoran.h" @@ -108,25 +106,8 @@ static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 }; module_param_array(video_nr, int, NULL, 0444); MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)"); -/* - Number and size of grab buffers for Video 4 Linux - The vast majority of applications should not need more than 2, - the very popular BTTV driver actually does ONLY have 2. - Time sensitive applications might need more, the maximum - is VIDEO_MAX_FRAME (defined in <linux/videodev.h>). - - The size is set so that the maximum possible request - can be satisfied. Decrease it, if bigphys_area alloc'd - memory is low. If you don't have the bigphys_area patch, - set it to 128 KB. Will you allow only to grab small - images with V4L, but that's better than nothing. - - v4l_bufsize has to be given in KB ! - -*/ - -int v4l_nbufs = 2; -int v4l_bufsize = 128; /* Everybody should be able to work with this setting */ +int v4l_nbufs = 4; +int v4l_bufsize = 864; /* Everybody should be able to work with this setting */ module_param(v4l_nbufs, int, 0644); MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use"); module_param(v4l_bufsize, int, 0644); @@ -273,7 +254,7 @@ zr36016_write (struct videocodec *codec, static void dc10_init (struct zoran *zr) { - dprintk(3, KERN_DEBUG "%s: dc10_init()\n", ZR_DEVNAME(zr)); + dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); /* Pixel clock selection */ GPIO(zr, 4, 0); @@ -285,13 +266,13 @@ dc10_init (struct zoran *zr) static void dc10plus_init (struct zoran *zr) { - dprintk(3, KERN_DEBUG "%s: dc10plus_init()\n", ZR_DEVNAME(zr)); + dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); } static void buz_init (struct zoran *zr) { - dprintk(3, KERN_DEBUG "%s: buz_init()\n", ZR_DEVNAME(zr)); + dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); /* some stuff from Iomega */ pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15); @@ -302,7 +283,7 @@ buz_init (struct zoran *zr) static void lml33_init (struct zoran *zr) { - dprintk(3, KERN_DEBUG "%s: lml33_init()\n", ZR_DEVNAME(zr)); + dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); GPIO(zr, 2, 1); // Set Composite input/output } @@ -332,50 +313,6 @@ avs6eyes_init (struct zoran *zr) } static char * -i2cid_to_modulename (u16 i2c_id) -{ - char *name = NULL; - - switch (i2c_id) { - case I2C_DRIVERID_SAA7110: - name = "saa7110"; - break; - case I2C_DRIVERID_SAA7111A: - name = "saa7111"; - break; - case I2C_DRIVERID_SAA7114: - name = "saa7114"; - break; - case I2C_DRIVERID_SAA7185B: - name = "saa7185"; - break; - case I2C_DRIVERID_ADV7170: - name = "adv7170"; - break; - case I2C_DRIVERID_ADV7175: - name = "adv7175"; - break; - case I2C_DRIVERID_BT819: - name = "bt819"; - break; - case I2C_DRIVERID_BT856: - name = "bt856"; - break; - case I2C_DRIVERID_BT866: - name = "bt866"; - break; - case I2C_DRIVERID_VPX3220: - name = "vpx3220"; - break; - case I2C_DRIVERID_KS0127: - name = "ks0127"; - break; - } - - return name; -} - -static char * codecid_to_modulename (u16 codecid) { char *name = NULL; @@ -425,11 +362,24 @@ static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 } static struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 }; static struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 }; +static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END }; +static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END }; +static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END }; +static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END }; +static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END }; +static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END }; +static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END }; +static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END }; +static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END }; +static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END }; + static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { { .type = DC10_old, .name = "DC10(old)", - .i2c_decoder = I2C_DRIVERID_VPX3220, + .i2c_decoder = "vpx3220a", + .mod_decoder = "vpx3220", + .addrs_decoder = vpx3220_addrs, .video_codec = CODEC_TYPE_ZR36050, .video_vfe = CODEC_TYPE_ZR36016, @@ -439,7 +389,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { { 2, "S-Video" }, { 0, "Internal/comp" } }, - .norms = 3, + .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, .tvn = { &f50sqpixel_dc10, &f60sqpixel_dc10, @@ -457,8 +407,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { }, { .type = DC10_new, .name = "DC10(new)", - .i2c_decoder = I2C_DRIVERID_SAA7110, - .i2c_encoder = I2C_DRIVERID_ADV7175, + .i2c_decoder = "saa7110", + .mod_decoder = "saa7110", + .addrs_decoder = saa7110_addrs, + .i2c_encoder = "adv7175", + .mod_encoder = "adv7175", + .addrs_encoder = adv717x_addrs, .video_codec = CODEC_TYPE_ZR36060, .inputs = 3, @@ -467,7 +421,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { { 7, "S-Video" }, { 5, "Internal/comp" } }, - .norms = 3, + .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, .tvn = { &f50sqpixel, &f60sqpixel, @@ -484,8 +438,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { }, { .type = DC10plus, .name = "DC10plus", - .i2c_decoder = I2C_DRIVERID_SAA7110, - .i2c_encoder = I2C_DRIVERID_ADV7175, + .i2c_decoder = "saa7110", + .mod_decoder = "saa7110", + .addrs_decoder = saa7110_addrs, + .i2c_encoder = "adv7175", + .mod_encoder = "adv7175", + .addrs_encoder = adv717x_addrs, .video_codec = CODEC_TYPE_ZR36060, .inputs = 3, @@ -494,7 +452,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { { 7, "S-Video" }, { 5, "Internal/comp" } }, - .norms = 3, + .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, .tvn = { &f50sqpixel, &f60sqpixel, @@ -512,8 +470,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { }, { .type = DC30, .name = "DC30", - .i2c_decoder = I2C_DRIVERID_VPX3220, - .i2c_encoder = I2C_DRIVERID_ADV7175, + .i2c_decoder = "vpx3220a", + .mod_decoder = "vpx3220", + .addrs_decoder = vpx3220_addrs, + .i2c_encoder = "adv7175", + .mod_encoder = "adv7175", + .addrs_encoder = adv717x_addrs, .video_codec = CODEC_TYPE_ZR36050, .video_vfe = CODEC_TYPE_ZR36016, @@ -523,7 +485,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { { 2, "S-Video" }, { 0, "Internal/comp" } }, - .norms = 3, + .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, .tvn = { &f50sqpixel_dc10, &f60sqpixel_dc10, @@ -541,8 +503,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { }, { .type = DC30plus, .name = "DC30plus", - .i2c_decoder = I2C_DRIVERID_VPX3220, - .i2c_encoder = I2C_DRIVERID_ADV7175, + .i2c_decoder = "vpx3220a", + .mod_decoder = "vpx3220", + .addrs_decoder = vpx3220_addrs, + .i2c_encoder = "adv7175", + .mod_encoder = "adv7175", + .addrs_encoder = adv717x_addrs, .video_codec = CODEC_TYPE_ZR36050, .video_vfe = CODEC_TYPE_ZR36016, @@ -552,7 +518,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { { 2, "S-Video" }, { 0, "Internal/comp" } }, - .norms = 3, + .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, .tvn = { &f50sqpixel_dc10, &f60sqpixel_dc10, @@ -570,8 +536,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { }, { .type = LML33, .name = "LML33", - .i2c_decoder = I2C_DRIVERID_BT819, - .i2c_encoder = I2C_DRIVERID_BT856, + .i2c_decoder = "bt819a", + .mod_decoder = "bt819", + .addrs_decoder = bt819_addrs, + .i2c_encoder = "bt856", + .mod_encoder = "bt856", + .addrs_encoder = bt856_addrs, .video_codec = CODEC_TYPE_ZR36060, .inputs = 2, @@ -579,7 +549,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { { 0, "Composite" }, { 7, "S-Video" } }, - .norms = 2, + .norms = V4L2_STD_NTSC|V4L2_STD_PAL, .tvn = { &f50ccir601_lml33, &f60ccir601_lml33, @@ -597,8 +567,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { }, { .type = LML33R10, .name = "LML33R10", - .i2c_decoder = I2C_DRIVERID_SAA7114, - .i2c_encoder = I2C_DRIVERID_ADV7170, + .i2c_decoder = "saa7114", + .mod_decoder = "saa7115", + .addrs_decoder = saa7114_addrs, + .i2c_encoder = "adv7170", + .mod_encoder = "adv7170", + .addrs_encoder = adv717x_addrs, .video_codec = CODEC_TYPE_ZR36060, .inputs = 2, @@ -606,7 +580,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { { 0, "Composite" }, { 7, "S-Video" } }, - .norms = 2, + .norms = V4L2_STD_NTSC|V4L2_STD_PAL, .tvn = { &f50ccir601_lm33r10, &f60ccir601_lm33r10, @@ -624,8 +598,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { }, { .type = BUZ, .name = "Buz", - .i2c_decoder = I2C_DRIVERID_SAA7111A, - .i2c_encoder = I2C_DRIVERID_SAA7185B, + .i2c_decoder = "saa7111", + .mod_decoder = "saa7115", + .addrs_decoder = saa7111_addrs, + .i2c_encoder = "saa7185", + .mod_encoder = "saa7185", + .addrs_encoder = saa7185_addrs, .video_codec = CODEC_TYPE_ZR36060, .inputs = 2, @@ -633,7 +611,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { { 3, "Composite" }, { 7, "S-Video" } }, - .norms = 3, + .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM, .tvn = { &f50ccir601, &f60ccir601, @@ -653,8 +631,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { .name = "6-Eyes", /* AverMedia chose not to brand the 6-Eyes. Thus it can't be autodetected, and requires card=x. */ - .i2c_decoder = I2C_DRIVERID_KS0127, - .i2c_encoder = I2C_DRIVERID_BT866, + .i2c_decoder = "ks0127", + .mod_decoder = "ks0127", + .addrs_decoder = ks0127_addrs, + .i2c_encoder = "bt866", + .mod_encoder = "bt866", + .addrs_encoder = bt866_addrs, .video_codec = CODEC_TYPE_ZR36060, .inputs = 10, @@ -670,7 +652,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = { {10, "S-Video 3" }, {15, "YCbCr" } }, - .norms = 2, + .norms = V4L2_STD_NTSC|V4L2_STD_PAL, .tvn = { &f50ccir601_avs6eyes, &f60ccir601_avs6eyes, @@ -735,69 +717,6 @@ zoran_i2c_setscl (void *data, btwrite(zr->i2cbr, ZR36057_I2CBR); } -static int -zoran_i2c_client_register (struct i2c_client *client) -{ - struct zoran *zr = (struct zoran *) i2c_get_adapdata(client->adapter); - int res = 0; - - dprintk(2, - KERN_DEBUG "%s: i2c_client_register() - driver id = %d\n", - ZR_DEVNAME(zr), client->driver->id); - - mutex_lock(&zr->resource_lock); - - if (zr->user > 0) { - /* we're already busy, so we keep a reference to - * them... Could do a lot of stuff here, but this - * is easiest. (Did I ever mention I'm a lazy ass?) - */ - res = -EBUSY; - goto clientreg_unlock_and_return; - } - - if (client->driver->id == zr->card.i2c_decoder) - zr->decoder = client; - else if (client->driver->id == zr->card.i2c_encoder) - zr->encoder = client; - else { - res = -ENODEV; - goto clientreg_unlock_and_return; - } - -clientreg_unlock_and_return: - mutex_unlock(&zr->resource_lock); - - return res; -} - -static int -zoran_i2c_client_unregister (struct i2c_client *client) -{ - struct zoran *zr = (struct zoran *) i2c_get_adapdata(client->adapter); - int res = 0; - - dprintk(2, KERN_DEBUG "%s: i2c_client_unregister()\n", ZR_DEVNAME(zr)); - - mutex_lock(&zr->resource_lock); - - if (zr->user > 0) { - res = -EBUSY; - goto clientunreg_unlock_and_return; - } - - /* try to locate it */ - if (client == zr->encoder) { - zr->encoder = NULL; - } else if (client == zr->decoder) { - zr->decoder = NULL; - snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%d]", zr->id); - } -clientunreg_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; -} - static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = { .setsda = zoran_i2c_setsda, .setscl = zoran_i2c_setscl, @@ -813,13 +732,10 @@ 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.class = I2C_CLASS_TV_ANALOG; zr->i2c_adapter.id = I2C_HW_B_ZR36067; - zr->i2c_adapter.client_register = zoran_i2c_client_register; - zr->i2c_adapter.client_unregister = zoran_i2c_client_unregister; strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr), sizeof(zr->i2c_adapter.name)); - i2c_set_adapdata(&zr->i2c_adapter, zr); + i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev); zr->i2c_adapter.algo_data = &zr->i2c_algo; zr->i2c_adapter.dev.parent = &zr->pci_dev->dev; return i2c_bit_add_bus(&zr->i2c_adapter); @@ -835,19 +751,20 @@ zoran_unregister_i2c (struct zoran *zr) int zoran_check_jpg_settings (struct zoran *zr, - struct zoran_jpg_settings *settings) + struct zoran_jpg_settings *settings, + int try) { int err = 0, err0 = 0; dprintk(4, KERN_DEBUG - "%s: check_jpg_settings() - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n", - ZR_DEVNAME(zr), settings->decimation, settings->HorDcm, + "%s: %s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n", + ZR_DEVNAME(zr), __func__, settings->decimation, settings->HorDcm, settings->VerDcm, settings->TmpDcm); dprintk(4, KERN_DEBUG - "%s: check_jpg_settings() - x: %d, y: %d, w: %d, y: %d\n", - ZR_DEVNAME(zr), settings->img_x, settings->img_y, + "%s: %s - x: %d, y: %d, w: %d, y: %d\n", + ZR_DEVNAME(zr), __func__, settings->img_x, settings->img_y, settings->img_width, settings->img_height); /* Check decimation, set default values for decimation = 1, 2, 4 */ switch (settings->decimation) { @@ -879,8 +796,8 @@ zoran_check_jpg_settings (struct zoran *zr, if (zr->card.type == DC10_new) { dprintk(1, KERN_DEBUG - "%s: check_jpg_settings() - HDec by 4 is not supported on the DC10\n", - ZR_DEVNAME(zr)); + "%s: %s - HDec by 4 is not supported on the DC10\n", + ZR_DEVNAME(zr), __func__); err0++; break; } @@ -900,50 +817,73 @@ zoran_check_jpg_settings (struct zoran *zr, /* We have to check the data the user has set */ if (settings->HorDcm != 1 && settings->HorDcm != 2 && - (zr->card.type == DC10_new || settings->HorDcm != 4)) + (zr->card.type == DC10_new || settings->HorDcm != 4)) { + settings->HorDcm = clamp(settings->HorDcm, 1, 2); err0++; - if (settings->VerDcm != 1 && settings->VerDcm != 2) + } + if (settings->VerDcm != 1 && settings->VerDcm != 2) { + settings->VerDcm = clamp(settings->VerDcm, 1, 2); err0++; - if (settings->TmpDcm != 1 && settings->TmpDcm != 2) + } + if (settings->TmpDcm != 1 && settings->TmpDcm != 2) { + settings->TmpDcm = clamp(settings->TmpDcm, 1, 2); err0++; + } if (settings->field_per_buff != 1 && - settings->field_per_buff != 2) + settings->field_per_buff != 2) { + settings->field_per_buff = clamp(settings->field_per_buff, 1, 2); err0++; - if (settings->img_x < 0) + } + if (settings->img_x < 0) { + settings->img_x = 0; err0++; - if (settings->img_y < 0) + } + if (settings->img_y < 0) { + settings->img_y = 0; err0++; - if (settings->img_width < 0) + } + if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) { + settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH); err0++; - if (settings->img_height < 0) + } + if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) { + settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2); err0++; - if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) + } + if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) { + settings->img_x = BUZ_MAX_WIDTH - settings->img_width; err0++; - if (settings->img_y + settings->img_height > - BUZ_MAX_HEIGHT / 2) + } + if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) { + settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height; + err0++; + } + if (settings->img_width % (16 * settings->HorDcm) != 0) { + settings->img_width -= settings->img_width % (16 * settings->HorDcm); + if (settings->img_width == 0) + settings->img_width = 16 * settings->HorDcm; + err0++; + } + if (settings->img_height % (8 * settings->VerDcm) != 0) { + settings->img_height -= settings->img_height % (8 * settings->VerDcm); + if (settings->img_height == 0) + settings->img_height = 8 * settings->VerDcm; err0++; - if (settings->HorDcm && settings->VerDcm) { - if (settings->img_width % - (16 * settings->HorDcm) != 0) - err0++; - if (settings->img_height % - (8 * settings->VerDcm) != 0) - err0++; } - if (err0) { + if (!try && err0) { dprintk(1, KERN_ERR - "%s: check_jpg_settings() - error in params for decimation = 0\n", - ZR_DEVNAME(zr)); + "%s: %s - error in params for decimation = 0\n", + ZR_DEVNAME(zr), __func__); err++; } break; default: dprintk(1, KERN_ERR - "%s: check_jpg_settings() - decimation = %d, must be 0, 1, 2 or 4\n", - ZR_DEVNAME(zr), settings->decimation); + "%s: %s - decimation = %d, must be 0, 1, 2 or 4\n", + ZR_DEVNAME(zr), __func__, settings->decimation); err++; break; } @@ -1021,12 +961,10 @@ zoran_open_init_params (struct zoran *zr) sizeof(zr->jpg_settings.jpg_comp.COM_data)); zr->jpg_settings.jpg_comp.jpeg_markers = JPEG_MARKER_DHT | JPEG_MARKER_DQT; - i = zoran_check_jpg_settings(zr, &zr->jpg_settings); + i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0); if (i) - dprintk(1, - KERN_ERR - "%s: zoran_open_init_params() internal error\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s internal error\n", + ZR_DEVNAME(zr), __func__); clear_interrupt_counters(zr); zr->testing = 0; @@ -1062,13 +1000,11 @@ static int __devinit zr36057_init (struct zoran *zr) { int j, err; - int two = 2; - int zero = 0; dprintk(1, KERN_INFO - "%s: zr36057_init() - initializing card[%d], zr=%p\n", - ZR_DEVNAME(zr), zr->id, zr); + "%s: %s - initializing card[%d], zr=%p\n", + ZR_DEVNAME(zr), __func__, zr->id, zr); /* default setup of all parameters which will persist between opens */ zr->user = 0; @@ -1079,24 +1015,32 @@ zr36057_init (struct zoran *zr) zr->jpg_buffers.allocated = 0; zr->v4l_buffers.allocated = 0; - zr->buffer.base = (void *) vidmem; - zr->buffer.width = 0; - zr->buffer.height = 0; - zr->buffer.depth = 0; - zr->buffer.bytesperline = 0; + zr->vbuf_base = (void *) vidmem; + zr->vbuf_width = 0; + zr->vbuf_height = 0; + zr->vbuf_depth = 0; + zr->vbuf_bytesperline = 0; /* Avoid nonsense settings from user for default input/norm */ - if (default_norm < VIDEO_MODE_PAL && - default_norm > VIDEO_MODE_SECAM) - default_norm = VIDEO_MODE_PAL; - zr->norm = default_norm; - if (!(zr->timing = zr->card.tvn[zr->norm])) { + if (default_norm < 0 && default_norm > 2) + default_norm = 0; + if (default_norm == 0) { + zr->norm = V4L2_STD_PAL; + zr->timing = zr->card.tvn[0]; + } else if (default_norm == 1) { + zr->norm = V4L2_STD_NTSC; + zr->timing = zr->card.tvn[1]; + } else { + zr->norm = V4L2_STD_SECAM; + zr->timing = zr->card.tvn[2]; + } + if (zr->timing == NULL) { dprintk(1, KERN_WARNING - "%s: zr36057_init() - default TV standard not supported by hardware. PAL will be used.\n", - ZR_DEVNAME(zr)); - zr->norm = VIDEO_MODE_PAL; - zr->timing = zr->card.tvn[zr->norm]; + "%s: %s - default TV standard not supported by hardware. PAL will be used.\n", + ZR_DEVNAME(zr), __func__); + zr->norm = V4L2_STD_PAL; + zr->timing = zr->card.tvn[0]; } if (default_input > zr->card.inputs-1) { @@ -1108,12 +1052,6 @@ zr36057_init (struct zoran *zr) } zr->input = default_input; - /* Should the following be reset at every open ? */ - zr->hue = 32768; - zr->contrast = 32768; - zr->saturation = 32768; - zr->brightness = 32768; - /* default setup (will be repeated at every open) */ zoran_open_init_params(zr); @@ -1124,8 +1062,8 @@ zr36057_init (struct zoran *zr) if (!zr->stat_com || !zr->video_dev) { dprintk(1, KERN_ERR - "%s: zr36057_init() - kmalloc (STAT_COM) failed\n", - ZR_DEVNAME(zr)); + "%s: %s - kmalloc (STAT_COM) failed\n", + ZR_DEVNAME(zr), __func__); err = -ENOMEM; goto exit_free; } @@ -1137,6 +1075,7 @@ zr36057_init (struct zoran *zr) * Now add the template and register the device unit. */ memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template)); + zr->video_dev->parent = &zr->pci_dev->dev; strcpy(zr->video_dev->name, ZR_DEVNAME(zr)); err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr[zr->id]); if (err < 0) @@ -1148,8 +1087,10 @@ zr36057_init (struct zoran *zr) detect_guest_activity(zr); test_interrupts(zr); if (!pass_through) { - decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero); - encoder_command(zr, ENCODER_SET_INPUT, &two); + struct v4l2_routing route = { 2, 0 }; + + decoder_call(zr, video, s_stream, 0); + encoder_call(zr, video, s_routing, &route); } zr->zoran_proc = NULL; @@ -1164,7 +1105,8 @@ exit_free: static void __devexit zoran_remove(struct pci_dev *pdev) { - struct zoran *zr = pci_get_drvdata(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct zoran *zr = to_zoran(v4l2_dev); if (!zr->initialized) goto exit_free; @@ -1197,7 +1139,7 @@ static void __devexit zoran_remove(struct pci_dev *pdev) pci_disable_device(zr->pci_dev); video_unregister_device(zr->video_dev); exit_free: - pci_set_drvdata(pdev, NULL); + v4l2_device_unregister(&zr->v4l2_dev); kfree(zr); } @@ -1215,10 +1157,8 @@ zoran_setup_videocodec (struct zoran *zr, m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL); if (!m) { - dprintk(1, - KERN_ERR - "%s: zoran_setup_videocodec() - no memory\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s - no memory\n", + ZR_DEVNAME(zr), __func__); return m; } @@ -1256,6 +1196,18 @@ zoran_setup_videocodec (struct zoran *zr, return m; } +static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct zoran *zr = to_zoran(sd->v4l2_dev); + + /* Bt819 needs to reset its FIFO buffer using #FRST pin and + LML33 card uses GPIO(7) for that. */ + if (cmd == BT819_FIFO_RESET_LOW) + GPIO(zr, 7, 0); + else if (cmd == BT819_FIFO_RESET_HIGH) + GPIO(zr, 7, 1); +} + /* * Scan for a Buz card (actually for the PCI controller ZR36057), * request the irq and map the io memory @@ -1269,34 +1221,33 @@ static int __devinit zoran_probe(struct pci_dev *pdev, struct videocodec_master *master_vfe = NULL; struct videocodec_master *master_codec = NULL; int card_num; - char *i2c_enc_name, *i2c_dec_name, *codec_name, *vfe_name; + char *codec_name, *vfe_name; unsigned int nr; nr = zoran_num++; if (nr >= BUZ_MAX) { - dprintk(1, - KERN_ERR - "%s: driver limited to %d card(s) maximum\n", + dprintk(1, KERN_ERR "%s: driver limited to %d card(s) maximum\n", ZORAN_NAME, BUZ_MAX); return -ENOENT; } zr = kzalloc(sizeof(struct zoran), GFP_KERNEL); if (!zr) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() - kzalloc failed\n", - ZORAN_NAME); + dprintk(1, KERN_ERR "%s: %s - kzalloc failed\n", + ZORAN_NAME, __func__); return -ENOMEM; } + zr->v4l2_dev.notify = zoran_subdev_notify; + if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev)) + goto zr_free_mem; zr->pci_dev = pdev; zr->id = nr; snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id); spin_lock_init(&zr->spinlock); mutex_init(&zr->resource_lock); if (pci_enable_device(pdev)) - goto zr_free_mem; + goto zr_unreg; pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION, &zr->revision); dprintk(1, @@ -1323,7 +1274,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev, KERN_ERR "%s: It is not possible to auto-detect ZR36057 based cards\n", ZR_DEVNAME(zr)); - goto zr_free_mem; + goto zr_unreg; } card_num = ent->driver_data; @@ -1332,7 +1283,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev, KERN_ERR "%s: Unknown card, try specifying card=X module parameter\n", ZR_DEVNAME(zr)); - goto zr_free_mem; + goto zr_unreg; } dprintk(3, KERN_DEBUG @@ -1345,7 +1296,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev, KERN_ERR "%s: User specified card type %d out of range (0 .. %d)\n", ZR_DEVNAME(zr), card_num, NUM_CARDS - 1); - goto zr_free_mem; + goto zr_unreg; } } @@ -1360,11 +1311,9 @@ static int __devinit zoran_probe(struct pci_dev *pdev, zr->zr36057_mem = pci_ioremap_bar(zr->pci_dev, 0); if (!zr->zr36057_mem) { - dprintk(1, - KERN_ERR - "%s: %s() - ioremap failed\n", + dprintk(1, KERN_ERR "%s: %s() - ioremap failed\n", ZR_DEVNAME(zr), __func__); - goto zr_free_mem; + goto zr_unreg; } result = request_irq(zr->pci_dev->irq, zoran_irq, @@ -1373,18 +1322,18 @@ static int __devinit zoran_probe(struct pci_dev *pdev, if (result == -EINVAL) { dprintk(1, KERN_ERR - "%s: find_zr36057() - bad irq number or handler\n", - ZR_DEVNAME(zr)); + "%s: %s - bad irq number or handler\n", + ZR_DEVNAME(zr), __func__); } else if (result == -EBUSY) { dprintk(1, KERN_ERR - "%s: find_zr36057() - IRQ %d busy, change your PnP config in BIOS\n", - ZR_DEVNAME(zr), zr->pci_dev->irq); + "%s: %s - IRQ %d busy, change your PnP config in BIOS\n", + ZR_DEVNAME(zr), __func__, zr->pci_dev->irq); } else { dprintk(1, KERN_ERR - "%s: find_zr36057() - can't assign irq, error code %d\n", - ZR_DEVNAME(zr), result); + "%s: %s - can't assign irq, error code %d\n", + ZR_DEVNAME(zr), __func__, result); } goto zr_unmap; } @@ -1394,9 +1343,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev, &latency); need_latency = zr->revision > 1 ? 32 : 48; if (latency != need_latency) { - dprintk(2, - KERN_INFO - "%s: Changing PCI latency from %d to %d\n", + dprintk(2, KERN_INFO "%s: Changing PCI latency from %d to %d\n", ZR_DEVNAME(zr), latency, need_latency); pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, need_latency); @@ -1407,54 +1354,20 @@ static int __devinit zoran_probe(struct pci_dev *pdev, dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n", ZR_DEVNAME(zr)); - /* i2c decoder */ - if (decoder[zr->id] != -1) { - i2c_dec_name = i2cid_to_modulename(decoder[zr->id]); - zr->card.i2c_decoder = decoder[zr->id]; - } else if (zr->card.i2c_decoder != 0) { - i2c_dec_name = i2cid_to_modulename(zr->card.i2c_decoder); - } else { - i2c_dec_name = NULL; - } - - if (i2c_dec_name) { - result = request_module(i2c_dec_name); - if (result < 0) { - dprintk(1, - KERN_ERR - "%s: failed to load module %s: %d\n", - ZR_DEVNAME(zr), i2c_dec_name, result); - } - } - - /* i2c encoder */ - if (encoder[zr->id] != -1) { - i2c_enc_name = i2cid_to_modulename(encoder[zr->id]); - zr->card.i2c_encoder = encoder[zr->id]; - } else if (zr->card.i2c_encoder != 0) { - i2c_enc_name = i2cid_to_modulename(zr->card.i2c_encoder); - } else { - i2c_enc_name = NULL; - } - - if (i2c_enc_name) { - result = request_module(i2c_enc_name); - if (result < 0) { - dprintk(1, - KERN_ERR - "%s: failed to load module %s: %d\n", - ZR_DEVNAME(zr), i2c_enc_name, result); - } - } - if (zoran_register_i2c(zr) < 0) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() - can't initialize i2c bus\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s - can't initialize i2c bus\n", + ZR_DEVNAME(zr), __func__); goto zr_free_irq; } + zr->decoder = v4l2_i2c_new_probed_subdev(&zr->i2c_adapter, + zr->card.mod_decoder, zr->card.i2c_decoder, zr->card.addrs_decoder); + + if (zr->card.mod_encoder) + zr->encoder = v4l2_i2c_new_probed_subdev(&zr->i2c_adapter, + zr->card.mod_encoder, zr->card.i2c_encoder, + zr->card.addrs_encoder); + dprintk(2, KERN_INFO "%s: Initializing videocodec bus...\n", ZR_DEVNAME(zr)); @@ -1495,17 +1408,13 @@ static int __devinit zoran_probe(struct pci_dev *pdev, goto zr_unreg_i2c; zr->codec = videocodec_attach(master_codec); if (!zr->codec) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() - no codec found\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s - no codec found\n", + ZR_DEVNAME(zr), __func__); goto zr_free_codec; } if (zr->codec->type != zr->card.video_codec) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() - wrong codec\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s - wrong codec\n", + ZR_DEVNAME(zr), __func__); goto zr_detach_codec; } } @@ -1515,17 +1424,13 @@ static int __devinit zoran_probe(struct pci_dev *pdev, goto zr_detach_codec; zr->vfe = videocodec_attach(master_vfe); if (!zr->vfe) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() - no VFE found\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s - no VFE found\n", + ZR_DEVNAME(zr), __func__); goto zr_free_vfe; } if (zr->vfe->type != zr->card.video_vfe) { - dprintk(1, - KERN_ERR - "%s: find_zr36057() = wrong VFE\n", - ZR_DEVNAME(zr)); + dprintk(1, KERN_ERR "%s: %s = wrong VFE\n", + ZR_DEVNAME(zr), __func__); goto zr_detach_vfe; } } @@ -1533,8 +1438,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev, /* take care of Natoma chipset and a revision 1 zr36057 */ if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) { zr->jpg_buffers.need_contiguous = 1; - dprintk(1, - KERN_INFO + dprintk(1, KERN_INFO "%s: ZR36057/Natoma bug, max. buffer size is 128K\n", ZR_DEVNAME(zr)); } @@ -1544,8 +1448,6 @@ static int __devinit zoran_probe(struct pci_dev *pdev, zoran_proc_init(zr); - pci_set_drvdata(pdev, zr); - return 0; zr_detach_vfe: @@ -1563,6 +1465,8 @@ zr_free_irq: free_irq(zr->pci_dev->irq, zr); zr_unmap: iounmap(zr->zr36057_mem); +zr_unreg: + v4l2_device_unregister(&zr->v4l2_dev); zr_free_mem: kfree(zr); @@ -1613,9 +1517,6 @@ static int __init zoran_init(void) ZORAN_NAME, vidmem); } - /* random nonsense */ - dprintk(6, KERN_DEBUG "Jotti is een held!\n"); - /* some mainboards might not do PCI-PCI data transfer well */ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) { dprintk(1, diff --git a/drivers/media/video/zoran/zoran_card.h b/drivers/media/video/zoran/zoran_card.h index 4507bdc..4936fea 100644 --- a/drivers/media/video/zoran/zoran_card.h +++ b/drivers/media/video/zoran/zoran_card.h @@ -44,7 +44,8 @@ extern int zr36067_debug; extern struct video_device zoran_template; extern int zoran_check_jpg_settings(struct zoran *zr, - struct zoran_jpg_settings *settings); + struct zoran_jpg_settings *settings, + int try); extern void zoran_open_init_params(struct zoran *zr); extern void zoran_vdev_release(struct video_device *vdev); diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c index 5d948ff..e0223de 100644 --- a/drivers/media/video/zoran/zoran_device.c +++ b/drivers/media/video/zoran/zoran_device.c @@ -36,13 +36,12 @@ #include <linux/proc_fs.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> #include <linux/spinlock.h> #include <linux/sem.h> #include <linux/pci.h> -#include <linux/video_decoder.h> -#include <linux/video_encoder.h> #include <linux/delay.h> #include <linux/wait.h> @@ -312,9 +311,9 @@ zr36057_adjust_vfe (struct zoran *zr, case BUZ_MODE_MOTION_COMPRESS: case BUZ_MODE_IDLE: default: - if (zr->norm == VIDEO_MODE_NTSC || + if ((zr->norm & V4L2_STD_NTSC) || (zr->card.type == LML33R10 && - zr->norm == VIDEO_MODE_PAL)) + (zr->norm & V4L2_STD_PAL))) btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); else btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR); @@ -355,14 +354,6 @@ zr36057_set_vfe (struct zoran *zr, dprintk(2, KERN_INFO "%s: set_vfe() - width = %d, height = %d\n", ZR_DEVNAME(zr), video_width, video_height); - if (zr->norm != VIDEO_MODE_PAL && - zr->norm != VIDEO_MODE_NTSC && - zr->norm != VIDEO_MODE_SECAM) { - dprintk(1, - KERN_ERR "%s: set_vfe() - norm = %d not valid\n", - ZR_DEVNAME(zr), zr->norm); - return; - } if (video_width < BUZ_MIN_WIDTH || video_height < BUZ_MIN_HEIGHT || video_width > Wa || video_height > Ha) { @@ -426,7 +417,7 @@ zr36057_set_vfe (struct zoran *zr, * we get the correct colors when uncompressing to the screen */ //reg |= ZR36057_VFESPFR_VCLKPol; /**/ /* RJ: Don't know if that is needed for NTSC also */ - if (zr->norm != VIDEO_MODE_NTSC) + if (!(zr->norm & V4L2_STD_NTSC)) reg |= ZR36057_VFESPFR_ExtFl; // NEEDED!!!!!!! Wolfgang reg |= ZR36057_VFESPFR_TopField; if (HorDcm >= 48) { @@ -497,11 +488,11 @@ zr36057_overlay (struct zoran *zr, * All error messages are internal driver checking only! */ /* video display top and bottom registers */ - reg = (long) zr->buffer.base + + reg = (long) zr->vbuf_base + zr->overlay_settings.x * ((zr->overlay_settings.format->depth + 7) / 8) + zr->overlay_settings.y * - zr->buffer.bytesperline; + zr->vbuf_bytesperline; btwrite(reg, ZR36057_VDTR); if (reg & 3) dprintk(1, @@ -509,15 +500,15 @@ zr36057_overlay (struct zoran *zr, "%s: zr36057_overlay() - video_address not aligned\n", ZR_DEVNAME(zr)); if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2) - reg += zr->buffer.bytesperline; + reg += zr->vbuf_bytesperline; btwrite(reg, ZR36057_VDBR); /* video stride, status, and frame grab register */ - reg = zr->buffer.bytesperline - + reg = zr->vbuf_bytesperline - zr->overlay_settings.width * ((zr->overlay_settings.format->depth + 7) / 8); if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2) - reg += zr->buffer.bytesperline; + reg += zr->vbuf_bytesperline; if (reg & 3) dprintk(1, KERN_ERR @@ -544,12 +535,8 @@ zr36057_overlay (struct zoran *zr, * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels. */ -void -write_overlay_mask (struct file *file, - struct video_clip *vp, - int count) +void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32; u32 *mask; @@ -563,10 +550,10 @@ write_overlay_mask (struct file *file, for (i = 0; i < count; ++i) { /* pick up local copy of clip */ - x = vp[i].x; - y = vp[i].y; - width = vp[i].width; - height = vp[i].height; + x = vp[i].c.left; + y = vp[i].c.top; + width = vp[i].c.width; + height = vp[i].c.height; /* trim clips that extend beyond the window */ if (x < 0) { @@ -981,11 +968,10 @@ void zr36057_enable_jpg (struct zoran *zr, enum zoran_codec_mode mode) { - static int zero; - static int one = 1; struct vfe_settings cap; int field_size = zr->jpg_buffers.buffer_size / zr->jpg_settings.field_per_buff; + struct v4l2_routing route = { 0, 0 }; zr->codec_mode = mode; @@ -1007,8 +993,9 @@ zr36057_enable_jpg (struct zoran *zr, * the video bus direction set to input. */ set_videobus_dir(zr, 0); - decoder_command(zr, DECODER_ENABLE_OUTPUT, &one); - encoder_command(zr, ENCODER_SET_INPUT, &zero); + decoder_call(zr, video, s_stream, 1); + route.input = 0; + encoder_call(zr, video, s_routing, &route); /* Take the JPEG codec and the VFE out of sleep */ jpeg_codec_sleep(zr, 0); @@ -1054,9 +1041,10 @@ zr36057_enable_jpg (struct zoran *zr, /* In motion decompression mode, the decoder output must be disabled, and * the video bus direction set to output. */ - decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero); + decoder_call(zr, video, s_stream, 0); set_videobus_dir(zr, 1); - encoder_command(zr, ENCODER_SET_INPUT, &one); + route.input = 1; + encoder_call(zr, video, s_routing, &route); /* Take the JPEG codec and the VFE out of sleep */ jpeg_codec_sleep(zr, 0); @@ -1100,8 +1088,9 @@ zr36057_enable_jpg (struct zoran *zr, jpeg_codec_sleep(zr, 1); zr36057_adjust_vfe(zr, mode); - decoder_command(zr, DECODER_ENABLE_OUTPUT, &one); - encoder_command(zr, ENCODER_SET_INPUT, &zero); + decoder_call(zr, video, s_stream, 1); + route.input = 0; + encoder_call(zr, video, s_routing, &route); dprintk(2, KERN_INFO "%s: enable_jpg(IDLE)\n", ZR_DEVNAME(zr)); break; @@ -1132,7 +1121,7 @@ zoran_feed_stat_com (struct zoran *zr) if (!(zr->stat_com[i] & cpu_to_le32(1))) break; zr->stat_com[i] = - cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus); + cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); } else { /* fill 2 stat_com entries */ i = ((zr->jpg_dma_head - @@ -1140,9 +1129,9 @@ zoran_feed_stat_com (struct zoran *zr) if (!(zr->stat_com[i] & cpu_to_le32(1))) break; zr->stat_com[i] = - cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus); + cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); zr->stat_com[i + 1] = - cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus); + cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus); } zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA; zr->jpg_dma_head++; @@ -1162,7 +1151,7 @@ zoran_reap_stat_com (struct zoran *zr) u32 stat_com; unsigned int seq; unsigned int dif; - struct zoran_jpg_buffer *buffer; + struct zoran_buffer *buffer; int frame; /* In motion decompress we don't have a hardware frame counter, @@ -1208,22 +1197,52 @@ zoran_reap_stat_com (struct zoran *zr) } } +static void zoran_restart(struct zoran *zr) +{ + /* Now the stat_comm buffer is ready for restart */ + int status = 0, mode; + + if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { + decoder_call(zr, video, g_input_status, &status); + mode = CODEC_DO_COMPRESSION; + } else { + status = V4L2_IN_ST_NO_SIGNAL; + mode = CODEC_DO_EXPANSION; + } + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || + !(status & V4L2_IN_ST_NO_SIGNAL)) { + /********** RESTART code *************/ + jpeg_codec_reset(zr); + zr->codec->set_mode(zr->codec, mode); + zr36057_set_jpg(zr, zr->codec_mode); + jpeg_start(zr); + + if (zr->num_errors <= 8) + dprintk(2, KERN_INFO "%s: Restart\n", + ZR_DEVNAME(zr)); + + zr->JPEG_missed = 0; + zr->JPEG_error = 2; + /********** End RESTART code ***********/ + } +} + static void error_handler (struct zoran *zr, u32 astat, u32 stat) { + int i, j; + /* This is JPEG error handling part */ - if ((zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) && - (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS)) { - //dprintk(1, KERN_ERR "%s: Internal error: error handling request in mode %d\n", ZR_DEVNAME(zr), zr->codec_mode); + if (zr->codec_mode != BUZ_MODE_MOTION_COMPRESS && + zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS) { return; } if ((stat & 1) == 0 && zr->codec_mode == BUZ_MODE_MOTION_COMPRESS && - zr->jpg_dma_tail - zr->jpg_que_tail >= - zr->jpg_buffers.num_buffers) { + zr->jpg_dma_tail - zr->jpg_que_tail >= zr->jpg_buffers.num_buffers) { /* No free buffers... */ zoran_reap_stat_com(zr); zoran_feed_stat_com(zr); @@ -1232,142 +1251,95 @@ error_handler (struct zoran *zr, return; } - if (zr->JPEG_error != 1) { - /* - * First entry: error just happened during normal operation - * - * In BUZ_MODE_MOTION_COMPRESS: - * - * Possible glitch in TV signal. In this case we should - * stop the codec and wait for good quality signal before - * restarting it to avoid further problems - * - * In BUZ_MODE_MOTION_DECOMPRESS: - * - * Bad JPEG frame: we have to mark it as processed (codec crashed - * and was not able to do it itself), and to remove it from queue. - */ - btand(~ZR36057_JMC_Go_en, ZR36057_JMC); - udelay(1); - stat = stat | (post_office_read(zr, 7, 0) & 3) << 8; - btwrite(0, ZR36057_JPC); - btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); - jpeg_codec_reset(zr); - jpeg_codec_sleep(zr, 1); - zr->JPEG_error = 1; - zr->num_errors++; - - /* Report error */ - if (zr36067_debug > 1 && zr->num_errors <= 8) { - long frame; - frame = - zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; - printk(KERN_ERR - "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ", - ZR_DEVNAME(zr), stat, zr->last_isr, - zr->jpg_que_tail, zr->jpg_dma_tail, - zr->jpg_dma_head, zr->jpg_que_head, - zr->jpg_seq_num, frame); - printk("stat_com frames:"); - { - int i, j; - for (j = 0; j < BUZ_NUM_STAT_COM; j++) { - for (i = 0; - i < zr->jpg_buffers.num_buffers; - i++) { - if (le32_to_cpu(zr->stat_com[j]) == - zr->jpg_buffers. - buffer[i]. - frag_tab_bus) { - printk("% d->%d", - j, i); - } - } - } - printk("\n"); - } - } - /* Find an entry in stat_com and rotate contents */ - { - int i; - - if (zr->jpg_settings.TmpDcm == 1) - i = (zr->jpg_dma_tail - - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; - else - i = ((zr->jpg_dma_tail - - zr->jpg_err_shift) & 1) * 2; - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) { - /* Mimic zr36067 operation */ - zr->stat_com[i] |= cpu_to_le32(1); - if (zr->jpg_settings.TmpDcm != 1) - zr->stat_com[i + 1] |= cpu_to_le32(1); - /* Refill */ - zoran_reap_stat_com(zr); - zoran_feed_stat_com(zr); - wake_up_interruptible(&zr->jpg_capq); - /* Find an entry in stat_com again after refill */ - if (zr->jpg_settings.TmpDcm == 1) - i = (zr->jpg_dma_tail - - zr->jpg_err_shift) & - BUZ_MASK_STAT_COM; - else - i = ((zr->jpg_dma_tail - - zr->jpg_err_shift) & 1) * 2; - } - if (i) { - /* Rotate stat_comm entries to make current entry first */ - int j; - __le32 bus_addr[BUZ_NUM_STAT_COM]; - - /* Here we are copying the stat_com array, which - * is already in little endian format, so - * no endian conversions here - */ - memcpy(bus_addr, zr->stat_com, - sizeof(bus_addr)); - for (j = 0; j < BUZ_NUM_STAT_COM; j++) { - zr->stat_com[j] = - bus_addr[(i + j) & - BUZ_MASK_STAT_COM]; + if (zr->JPEG_error == 1) { + zoran_restart(zr); + return; + } - } - zr->jpg_err_shift += i; - zr->jpg_err_shift &= BUZ_MASK_STAT_COM; + /* + * First entry: error just happened during normal operation + * + * In BUZ_MODE_MOTION_COMPRESS: + * + * Possible glitch in TV signal. In this case we should + * stop the codec and wait for good quality signal before + * restarting it to avoid further problems + * + * In BUZ_MODE_MOTION_DECOMPRESS: + * + * Bad JPEG frame: we have to mark it as processed (codec crashed + * and was not able to do it itself), and to remove it from queue. + */ + btand(~ZR36057_JMC_Go_en, ZR36057_JMC); + udelay(1); + stat = stat | (post_office_read(zr, 7, 0) & 3) << 8; + btwrite(0, ZR36057_JPC); + btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR); + jpeg_codec_reset(zr); + jpeg_codec_sleep(zr, 1); + zr->JPEG_error = 1; + zr->num_errors++; + + /* Report error */ + if (zr36067_debug > 1 && zr->num_errors <= 8) { + long frame; + + frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; + printk(KERN_ERR + "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ", + ZR_DEVNAME(zr), stat, zr->last_isr, + zr->jpg_que_tail, zr->jpg_dma_tail, + zr->jpg_dma_head, zr->jpg_que_head, + zr->jpg_seq_num, frame); + printk(KERN_INFO "stat_com frames:"); + for (j = 0; j < BUZ_NUM_STAT_COM; j++) { + for (i = 0; i < zr->jpg_buffers.num_buffers; i++) { + if (le32_to_cpu(zr->stat_com[j]) == zr->jpg_buffers.buffer[i].jpg.frag_tab_bus) + printk(KERN_CONT "% d->%d", j, i); } - if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) - zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */ } + printk(KERN_CONT "\n"); } + /* Find an entry in stat_com and rotate contents */ + if (zr->jpg_settings.TmpDcm == 1) + i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; + else + i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2; + if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) { + /* Mimic zr36067 operation */ + zr->stat_com[i] |= cpu_to_le32(1); + if (zr->jpg_settings.TmpDcm != 1) + zr->stat_com[i + 1] |= cpu_to_le32(1); + /* Refill */ + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + wake_up_interruptible(&zr->jpg_capq); + /* Find an entry in stat_com again after refill */ + if (zr->jpg_settings.TmpDcm == 1) + i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM; + else + i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2; + } + if (i) { + /* Rotate stat_comm entries to make current entry first */ + int j; + __le32 bus_addr[BUZ_NUM_STAT_COM]; + + /* Here we are copying the stat_com array, which + * is already in little endian format, so + * no endian conversions here + */ + memcpy(bus_addr, zr->stat_com, sizeof(bus_addr)); - /* Now the stat_comm buffer is ready for restart */ - do { - int status, mode; - - if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { - decoder_command(zr, DECODER_GET_STATUS, &status); - mode = CODEC_DO_COMPRESSION; - } else { - status = 0; - mode = CODEC_DO_EXPANSION; - } - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || - (status & DECODER_STATUS_GOOD)) { - /********** RESTART code *************/ - jpeg_codec_reset(zr); - zr->codec->set_mode(zr->codec, mode); - zr36057_set_jpg(zr, zr->codec_mode); - jpeg_start(zr); - - if (zr->num_errors <= 8) - dprintk(2, KERN_INFO "%s: Restart\n", - ZR_DEVNAME(zr)); + for (j = 0; j < BUZ_NUM_STAT_COM; j++) + zr->stat_com[j] = bus_addr[(i + j) & BUZ_MASK_STAT_COM]; - zr->JPEG_missed = 0; - zr->JPEG_error = 2; - /********** End RESTART code ***********/ - } - } while (0); + zr->jpg_err_shift += i; + zr->jpg_err_shift &= BUZ_MASK_STAT_COM; + } + if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) + zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */ + zoran_restart(zr); } irqreturn_t @@ -1425,10 +1397,8 @@ zoran_irq (int irq, * We simply ignore them */ if (zr->v4l_memgrab_active) { - /* A lot more checks should be here ... */ - if ((btread(ZR36057_VSSFGR) & - ZR36057_VSSFGR_SnapShot) == 0) + if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) == 0) dprintk(1, KERN_WARNING "%s: BuzIRQ with SnapShot off ???\n", @@ -1436,10 +1406,7 @@ zoran_irq (int irq, if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) { /* There is a grab on a frame going on, check if it has finished */ - - if ((btread(ZR36057_VSSFGR) & - ZR36057_VSSFGR_FrameGrab) == - 0) { + if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FrameGrab) == 0) { /* it is finished, notify the user */ zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE; @@ -1457,9 +1424,7 @@ zoran_irq (int irq, if (zr->v4l_grab_frame == NO_GRAB_ACTIVE && zr->v4l_pend_tail != zr->v4l_pend_head) { - - int frame = zr->v4l_pend[zr->v4l_pend_tail & - V4L_MASK_FRAME]; + int frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME]; u32 reg; zr->v4l_grab_frame = frame; @@ -1468,27 +1433,17 @@ zoran_irq (int irq, /* Buffer address */ - reg = - zr->v4l_buffers.buffer[frame]. - fbuffer_bus; + reg = zr->v4l_buffers.buffer[frame].v4l.fbuffer_bus; btwrite(reg, ZR36057_VDTR); - if (zr->v4l_settings.height > - BUZ_MAX_HEIGHT / 2) - reg += - zr->v4l_settings. - bytesperline; + if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) + reg += zr->v4l_settings.bytesperline; btwrite(reg, ZR36057_VDBR); /* video stride, status, and frame grab register */ reg = 0; - if (zr->v4l_settings.height > - BUZ_MAX_HEIGHT / 2) - reg += - zr->v4l_settings. - bytesperline; - reg = - (reg << - ZR36057_VSSFGR_DispStride); + if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2) + reg += zr->v4l_settings.bytesperline; + reg = (reg << ZR36057_VSSFGR_DispStride); reg |= ZR36057_VSSFGR_VidOvf; reg |= ZR36057_VSSFGR_SnapShot; reg |= ZR36057_VSSFGR_FrameGrab; @@ -1506,77 +1461,66 @@ zoran_irq (int irq, #if (IRQ_MASK & ZR36057_ISR_CodRepIRQ) if (astat & ZR36057_ISR_CodRepIRQ) { zr->intr_counter_CodRepIRQ++; - IDEBUG(printk - (KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n", + IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n", ZR_DEVNAME(zr))); btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR); } #endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */ #if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) - if (astat & ZR36057_ISR_JPEGRepIRQ) { - - if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || - zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { - if (zr36067_debug > 1 && - (!zr->frame_num || zr->JPEG_error)) { - printk(KERN_INFO - "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n", - ZR_DEVNAME(zr), stat, - zr->jpg_settings.odd_even, - zr->jpg_settings. - field_per_buff, - zr->JPEG_missed); - { - char sc[] = "0000"; - char sv[5]; - int i; - strcpy(sv, sc); - for (i = 0; i < 4; i++) { - if (le32_to_cpu(zr->stat_com[i]) & 1) - sv[i] = '1'; - } - sv[4] = 0; - printk(KERN_INFO - "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n", - ZR_DEVNAME(zr), sv, - zr->jpg_que_tail, - zr->jpg_dma_tail, - zr->jpg_dma_head, - zr->jpg_que_head); - } - } else { - if (zr->JPEG_missed > zr->JPEG_max_missed) // Get statistics - zr->JPEG_max_missed = - zr->JPEG_missed; - if (zr->JPEG_missed < - zr->JPEG_min_missed) - zr->JPEG_min_missed = - zr->JPEG_missed; + if ((astat & ZR36057_ISR_JPEGRepIRQ) && + (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS || + zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) { + if (zr36067_debug > 1 && (!zr->frame_num || zr->JPEG_error)) { + char sc[] = "0000"; + char sv[5]; + int i; + + printk(KERN_INFO + "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n", + ZR_DEVNAME(zr), stat, + zr->jpg_settings.odd_even, + zr->jpg_settings.field_per_buff, + zr->JPEG_missed); + + strcpy(sv, sc); + for (i = 0; i < 4; i++) { + if (le32_to_cpu(zr->stat_com[i]) & 1) + sv[i] = '1'; } + sv[4] = 0; + printk(KERN_INFO + "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n", + ZR_DEVNAME(zr), sv, + zr->jpg_que_tail, + zr->jpg_dma_tail, + zr->jpg_dma_head, + zr->jpg_que_head); + } else { + /* Get statistics */ + if (zr->JPEG_missed > zr->JPEG_max_missed) + zr->JPEG_max_missed = zr->JPEG_missed; + if (zr->JPEG_missed < zr->JPEG_min_missed) + zr->JPEG_min_missed = zr->JPEG_missed; + } - if (zr36067_debug > 2 && zr->frame_num < 6) { - int i; - printk("%s: seq=%ld stat_com:", - ZR_DEVNAME(zr), zr->jpg_seq_num); - for (i = 0; i < 4; i++) { - printk(" %08x", - le32_to_cpu(zr->stat_com[i])); - } - printk("\n"); + if (zr36067_debug > 2 && zr->frame_num < 6) { + int i; + + printk(KERN_INFO "%s: seq=%ld stat_com:", + ZR_DEVNAME(zr), zr->jpg_seq_num); + for (i = 0; i < 4; i++) { + printk(KERN_CONT " %08x", + le32_to_cpu(zr->stat_com[i])); } - zr->frame_num++; - zr->JPEG_missed = 0; - zr->JPEG_error = 0; - zoran_reap_stat_com(zr); - zoran_feed_stat_com(zr); - wake_up_interruptible(&zr->jpg_capq); - } /*else { - dprintk(1, - KERN_ERR - "%s: JPEG interrupt while not in motion (de)compress mode!\n", - ZR_DEVNAME(zr)); - }*/ + printk(KERN_CONT "\n"); + } + zr->frame_num++; + zr->JPEG_missed = 0; + zr->JPEG_error = 0; + zoran_reap_stat_com(zr); + zoran_feed_stat_com(zr); + wake_up_interruptible(&zr->jpg_capq); } #endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */ @@ -1585,8 +1529,7 @@ zoran_irq (int irq, zr->JPEG_missed > 25 || zr->JPEG_error == 1 || ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) && - (zr->frame_num & (zr->JPEG_missed > - zr->jpg_settings.field_per_buff)))) { + (zr->frame_num & (zr->JPEG_missed > zr->jpg_settings.field_per_buff)))) { error_handler(zr, astat, stat); } @@ -1628,7 +1571,7 @@ zoran_set_pci_master (struct zoran *zr, void zoran_init_hardware (struct zoran *zr) { - int j, zero = 0; + struct v4l2_routing route = { 0, 0 }; /* Enable bus-mastering */ zoran_set_pci_master(zr, 1); @@ -1638,15 +1581,16 @@ zoran_init_hardware (struct zoran *zr) zr->card.init(zr); } - j = zr->card.input[zr->input].muxsel; + route.input = zr->card.input[zr->input].muxsel; - decoder_command(zr, 0, NULL); - decoder_command(zr, DECODER_SET_NORM, &zr->norm); - decoder_command(zr, DECODER_SET_INPUT, &j); + decoder_call(zr, core, init, 0); + decoder_call(zr, tuner, s_std, zr->norm); + decoder_call(zr, video, s_routing, &route); - encoder_command(zr, 0, NULL); - encoder_command(zr, ENCODER_SET_NORM, &zr->norm); - encoder_command(zr, ENCODER_SET_INPUT, &zero); + encoder_call(zr, core, init, 0); + encoder_call(zr, video, s_std_output, zr->norm); + route.input = 0; + encoder_call(zr, video, s_routing, &route); /* toggle JPEG codec sleep to sync PLL */ jpeg_codec_sleep(zr, 1); @@ -1706,42 +1650,3 @@ zr36057_init_vfe (struct zoran *zr) reg |= ZR36057_VDCR_Triton; btwrite(reg, ZR36057_VDCR); } - -/* - * Interface to decoder and encoder chips using i2c bus - */ - -int -decoder_command (struct zoran *zr, - int cmd, - void *data) -{ - if (zr->decoder == NULL) - return -EIO; - - if (zr->card.type == LML33 && - (cmd == DECODER_SET_NORM || cmd == DECODER_SET_INPUT)) { - int res; - - // Bt819 needs to reset its FIFO buffer using #FRST pin and - // LML33 card uses GPIO(7) for that. - GPIO(zr, 7, 0); - res = zr->decoder->driver->command(zr->decoder, cmd, data); - // Pull #FRST high. - GPIO(zr, 7, 1); - return res; - } else - return zr->decoder->driver->command(zr->decoder, cmd, - data); -} - -int -encoder_command (struct zoran *zr, - int cmd, - void *data) -{ - if (zr->encoder == NULL) - return -1; - - return zr->encoder->driver->command(zr->encoder, cmd, data); -} diff --git a/drivers/media/video/zoran/zoran_device.h b/drivers/media/video/zoran/zoran_device.h index 74c6c8e..07f2c23 100644 --- a/drivers/media/video/zoran/zoran_device.h +++ b/drivers/media/video/zoran/zoran_device.h @@ -54,8 +54,8 @@ extern int jpeg_codec_reset(struct zoran *zr); /* zr360x7 access to raw capture */ extern void zr36057_overlay(struct zoran *zr, int on); -extern void write_overlay_mask(struct file *file, - struct video_clip *vp, +extern void write_overlay_mask(struct zoran_fh *fh, + struct v4l2_clip *vp, int count); extern void zr36057_set_memgrab(struct zoran *zr, int mode); @@ -87,11 +87,9 @@ extern int jpg_bufsize; extern int pass_through; /* i2c */ -extern int decoder_command(struct zoran *zr, - int cmd, - void *data); -extern int encoder_command(struct zoran *zr, - int cmd, - void *data); +#define decoder_call(zr, o, f, args...) \ + v4l2_subdev_call(zr->decoder, o, f, ##args) +#define encoder_call(zr, o, f, args...) \ + v4l2_subdev_call(zr->encoder, o, f, ##args) #endif /* __ZORAN_DEVICE_H__ */ diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 120ef23..f16e57c 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -58,16 +58,6 @@ #include <linux/i2c-algo-bit.h> #include <linux/spinlock.h> -#define MAP_NR(x) virt_to_page(x) -#define ZORAN_VID_TYPE ( \ - VID_TYPE_CAPTURE | \ - VID_TYPE_OVERLAY | \ - VID_TYPE_CLIPPING | \ - VID_TYPE_FRAMERAM | \ - VID_TYPE_SCALES | \ - VID_TYPE_MJPEG_DECODER | \ - VID_TYPE_MJPEG_ENCODER \ - ) #include <linux/videodev.h> #include <media/v4l2-common.h> @@ -79,36 +69,17 @@ #include <asm/uaccess.h> #include <linux/proc_fs.h> -#include <linux/video_decoder.h> -#include <linux/video_encoder.h> #include <linux/mutex.h> #include "zoran.h" #include "zoran_device.h" #include "zoran_card.h" - /* we declare some card type definitions here, they mean - * the same as the v4l1 ZORAN_VID_TYPE above, except it's v4l2 */ -#define ZORAN_V4L2_VID_FLAGS ( \ - V4L2_CAP_STREAMING |\ - V4L2_CAP_VIDEO_CAPTURE |\ - V4L2_CAP_VIDEO_OUTPUT |\ - V4L2_CAP_VIDEO_OVERLAY \ - ) - - -#if defined(CONFIG_VIDEO_V4L1_COMPAT) -#define ZFMT(pal, fcc, cs) \ - .palette = (pal), .fourcc = (fcc), .colorspace = (cs) -#else -#define ZFMT(pal, fcc, cs) \ - .fourcc = (fcc), .colorspace = (cs) -#endif const struct zoran_format zoran_formats[] = { { .name = "15-bit RGB LE", - ZFMT(VIDEO_PALETTE_RGB555, - V4L2_PIX_FMT_RGB555, V4L2_COLORSPACE_SRGB), + .fourcc = V4L2_PIX_FMT_RGB555, + .colorspace = V4L2_COLORSPACE_SRGB, .depth = 15, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, @@ -116,16 +87,16 @@ const struct zoran_format zoran_formats[] = { ZR36057_VFESPFR_LittleEndian, }, { .name = "15-bit RGB BE", - ZFMT(-1, - V4L2_PIX_FMT_RGB555X, V4L2_COLORSPACE_SRGB), + .fourcc = V4L2_PIX_FMT_RGB555X, + .colorspace = V4L2_COLORSPACE_SRGB, .depth = 15, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif, }, { .name = "16-bit RGB LE", - ZFMT(VIDEO_PALETTE_RGB565, - V4L2_PIX_FMT_RGB565, V4L2_COLORSPACE_SRGB), + .fourcc = V4L2_PIX_FMT_RGB565, + .colorspace = V4L2_COLORSPACE_SRGB, .depth = 16, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, @@ -133,56 +104,56 @@ const struct zoran_format zoran_formats[] = { ZR36057_VFESPFR_LittleEndian, }, { .name = "16-bit RGB BE", - ZFMT(-1, - V4L2_PIX_FMT_RGB565X, V4L2_COLORSPACE_SRGB), + .fourcc = V4L2_PIX_FMT_RGB565X, + .colorspace = V4L2_COLORSPACE_SRGB, .depth = 16, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif, }, { .name = "24-bit RGB", - ZFMT(VIDEO_PALETTE_RGB24, - V4L2_PIX_FMT_BGR24, V4L2_COLORSPACE_SRGB), + .fourcc = V4L2_PIX_FMT_BGR24, + .colorspace = V4L2_COLORSPACE_SRGB, .depth = 24, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_Pack24, }, { .name = "32-bit RGB LE", - ZFMT(VIDEO_PALETTE_RGB32, - V4L2_PIX_FMT_BGR32, V4L2_COLORSPACE_SRGB), + .fourcc = V4L2_PIX_FMT_BGR32, + .colorspace = V4L2_COLORSPACE_SRGB, .depth = 32, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_LittleEndian, }, { .name = "32-bit RGB BE", - ZFMT(-1, - V4L2_PIX_FMT_RGB32, V4L2_COLORSPACE_SRGB), + .fourcc = V4L2_PIX_FMT_RGB32, + .colorspace = V4L2_COLORSPACE_SRGB, .depth = 32, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, .vfespfr = ZR36057_VFESPFR_RGB888, }, { .name = "4:2:2, packed, YUYV", - ZFMT(VIDEO_PALETTE_YUV422, - V4L2_PIX_FMT_YUYV, V4L2_COLORSPACE_SMPTE170M), + .fourcc = V4L2_PIX_FMT_YUYV, + .colorspace = V4L2_COLORSPACE_SMPTE170M, .depth = 16, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, .vfespfr = ZR36057_VFESPFR_YUV422, }, { .name = "4:2:2, packed, UYVY", - ZFMT(VIDEO_PALETTE_UYVY, - V4L2_PIX_FMT_UYVY, V4L2_COLORSPACE_SMPTE170M), + .fourcc = V4L2_PIX_FMT_UYVY, + .colorspace = V4L2_COLORSPACE_SMPTE170M, .depth = 16, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_OVERLAY, .vfespfr = ZR36057_VFESPFR_YUV422|ZR36057_VFESPFR_LittleEndian, }, { .name = "Hardware-encoded Motion-JPEG", - ZFMT(-1, - V4L2_PIX_FMT_MJPEG, V4L2_COLORSPACE_SMPTE170M), + .fourcc = V4L2_PIX_FMT_MJPEG, + .colorspace = V4L2_COLORSPACE_SMPTE170M, .depth = 0, .flags = ZORAN_FORMAT_CAPTURE | ZORAN_FORMAT_PLAYBACK | @@ -191,13 +162,6 @@ const struct zoran_format zoran_formats[] = { }; #define NUM_FORMATS ARRAY_SIZE(zoran_formats) -// RJ: Test only - want to test BUZ_USE_HIMEM even when CONFIG_BIGPHYS_AREA is defined - - -static int lock_norm; /* 0 = default 1 = Don't change TV standard (norm) */ -module_param(lock_norm, int, 0644); -MODULE_PARM_DESC(lock_norm, "Prevent norm changes (1 = ignore, >1 = fail)"); - /* small helper function for calculating buffersizes for v4l2 * we calculate the nearest higher power-of-two, which * will be the recommended buffersize */ @@ -222,221 +186,106 @@ zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings) } /* forward references */ -static void v4l_fbuffer_free(struct file *file); -static void jpg_fbuffer_free(struct file *file); +static void v4l_fbuffer_free(struct zoran_fh *fh); +static void jpg_fbuffer_free(struct zoran_fh *fh); + +/* Set mapping mode */ +static void map_mode_raw(struct zoran_fh *fh) +{ + fh->map_mode = ZORAN_MAP_MODE_RAW; + fh->buffers.buffer_size = v4l_bufsize; + fh->buffers.num_buffers = v4l_nbufs; +} +static void map_mode_jpg(struct zoran_fh *fh, int play) +{ + fh->map_mode = play ? ZORAN_MAP_MODE_JPG_PLAY : ZORAN_MAP_MODE_JPG_REC; + fh->buffers.buffer_size = jpg_bufsize; + fh->buffers.num_buffers = jpg_nbufs; +} +static inline const char *mode_name(enum zoran_map_mode mode) +{ + return mode == ZORAN_MAP_MODE_RAW ? "V4L" : "JPG"; +} /* * Allocate the V4L grab buffers * * These have to be pysically contiguous. - * If v4l_bufsize <= MAX_KMALLOC_MEM we use kmalloc - * else we try to allocate them with bigphysarea_alloc_pages - * if the bigphysarea patch is present in the kernel, - * else we try to use high memory (if the user has bootet - * Linux with the necessary memory left over). */ -static unsigned long -get_high_mem (unsigned long size) +static int v4l_fbuffer_alloc(struct zoran_fh *fh) { -/* - * Check if there is usable memory at the end of Linux memory - * of at least size. Return the physical address of this memory, - * return 0 on failure. - * - * The idea is from Alexandro Rubini's book "Linux device drivers". - * The driver from him which is downloadable from O'Reilly's - * web site misses the "virt_to_phys(high_memory)" part - * (and therefore doesn't work at all - at least with 2.2.x kernels). - * - * It should be unnecessary to mention that THIS IS DANGEROUS, - * if more than one driver at a time has the idea to use this memory!!!! - */ - - volatile unsigned char __iomem *mem; - unsigned char c; - unsigned long hi_mem_ph; - unsigned long i; - - /* Map the high memory to user space */ - - hi_mem_ph = virt_to_phys(high_memory); - - mem = ioremap(hi_mem_ph, size); - if (!mem) { - dprintk(1, - KERN_ERR "%s: get_high_mem() - ioremap failed\n", - ZORAN_NAME); - return 0; - } - - for (i = 0; i < size; i++) { - /* Check if it is memory */ - c = i & 0xff; - writeb(c, mem + i); - if (readb(mem + i) != c) - break; - c = 255 - c; - writeb(c, mem + i); - if (readb(mem + i) != c) - break; - writeb(0, mem + i); /* zero out memory */ - - /* give the kernel air to breath */ - if ((i & 0x3ffff) == 0x3ffff) - schedule(); - } - - iounmap(mem); - - if (i != size) { - dprintk(1, - KERN_ERR - "%s: get_high_mem() - requested %lu, avail %lu\n", - ZORAN_NAME, size, i); - return 0; - } - - return hi_mem_ph; -} - -static int -v4l_fbuffer_alloc (struct file *file) -{ - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int i, off; unsigned char *mem; - unsigned long pmem = 0; - /* we might have old buffers lying around... */ - if (fh->v4l_buffers.ready_to_be_freed) { - v4l_fbuffer_free(file); - } - - for (i = 0; i < fh->v4l_buffers.num_buffers; i++) { - if (fh->v4l_buffers.buffer[i].fbuffer) + for (i = 0; i < fh->buffers.num_buffers; i++) { + if (fh->buffers.buffer[i].v4l.fbuffer) dprintk(2, KERN_WARNING - "%s: v4l_fbuffer_alloc() - buffer %d allready allocated!?\n", - ZR_DEVNAME(zr), i); + "%s: %s - buffer %d already allocated!?\n", + ZR_DEVNAME(zr), __func__, i); //udelay(20); - if (fh->v4l_buffers.buffer_size <= MAX_KMALLOC_MEM) { - /* Use kmalloc */ - - mem = kmalloc(fh->v4l_buffers.buffer_size, GFP_KERNEL); - if (!mem) { - dprintk(1, - KERN_ERR - "%s: v4l_fbuffer_alloc() - kmalloc for V4L buf %d failed\n", - ZR_DEVNAME(zr), i); - v4l_fbuffer_free(file); - return -ENOBUFS; - } - fh->v4l_buffers.buffer[i].fbuffer = mem; - fh->v4l_buffers.buffer[i].fbuffer_phys = - virt_to_phys(mem); - fh->v4l_buffers.buffer[i].fbuffer_bus = - virt_to_bus(mem); - for (off = 0; off < fh->v4l_buffers.buffer_size; - off += PAGE_SIZE) - SetPageReserved(MAP_NR(mem + off)); - dprintk(4, - KERN_INFO - "%s: v4l_fbuffer_alloc() - V4L frame %d mem 0x%lx (bus: 0x%lx)\n", - ZR_DEVNAME(zr), i, (unsigned long) mem, - virt_to_bus(mem)); - } else { - - /* Use high memory which has been left at boot time */ - - /* Ok., Ok. this is an evil hack - we make - * the assumption that physical addresses are - * the same as bus addresses (true at least - * for Intel processors). The whole method of - * obtaining and using this memory is not very - * nice - but I hope it saves some poor users - * from kernel hacking, which might have even - * more evil results */ - - if (i == 0) { - int size = - fh->v4l_buffers.num_buffers * - fh->v4l_buffers.buffer_size; - - pmem = get_high_mem(size); - if (pmem == 0) { - dprintk(1, - KERN_ERR - "%s: v4l_fbuffer_alloc() - get_high_mem (size = %d KB) for V4L bufs failed\n", - ZR_DEVNAME(zr), size >> 10); - return -ENOBUFS; - } - fh->v4l_buffers.buffer[0].fbuffer = NULL; - fh->v4l_buffers.buffer[0].fbuffer_phys = pmem; - fh->v4l_buffers.buffer[0].fbuffer_bus = pmem; - dprintk(4, - KERN_INFO - "%s: v4l_fbuffer_alloc() - using %d KB high memory\n", - ZR_DEVNAME(zr), size >> 10); - } else { - fh->v4l_buffers.buffer[i].fbuffer = NULL; - fh->v4l_buffers.buffer[i].fbuffer_phys = - pmem + i * fh->v4l_buffers.buffer_size; - fh->v4l_buffers.buffer[i].fbuffer_bus = - pmem + i * fh->v4l_buffers.buffer_size; - } + mem = kmalloc(fh->buffers.buffer_size, + GFP_KERNEL | __GFP_NOWARN); + if (!mem) { + dprintk(1, + KERN_ERR + "%s: %s - kmalloc for V4L buf %d failed\n", + ZR_DEVNAME(zr), __func__, i); + v4l_fbuffer_free(fh); + return -ENOBUFS; } + fh->buffers.buffer[i].v4l.fbuffer = mem; + fh->buffers.buffer[i].v4l.fbuffer_phys = virt_to_phys(mem); + fh->buffers.buffer[i].v4l.fbuffer_bus = virt_to_bus(mem); + for (off = 0; off < fh->buffers.buffer_size; + off += PAGE_SIZE) + SetPageReserved(virt_to_page(mem + off)); + dprintk(4, + KERN_INFO + "%s: %s - V4L frame %d mem 0x%lx (bus: 0x%llx)\n", + ZR_DEVNAME(zr), __func__, i, (unsigned long) mem, + (unsigned long long)virt_to_bus(mem)); } - fh->v4l_buffers.allocated = 1; + fh->buffers.allocated = 1; return 0; } /* free the V4L grab buffers */ -static void -v4l_fbuffer_free (struct file *file) +static void v4l_fbuffer_free(struct zoran_fh *fh) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int i, off; unsigned char *mem; - dprintk(4, KERN_INFO "%s: v4l_fbuffer_free()\n", ZR_DEVNAME(zr)); + dprintk(4, KERN_INFO "%s: %s\n", ZR_DEVNAME(zr), __func__); - for (i = 0; i < fh->v4l_buffers.num_buffers; i++) { - if (!fh->v4l_buffers.buffer[i].fbuffer) + for (i = 0; i < fh->buffers.num_buffers; i++) { + if (!fh->buffers.buffer[i].v4l.fbuffer) continue; - if (fh->v4l_buffers.buffer_size <= MAX_KMALLOC_MEM) { - mem = fh->v4l_buffers.buffer[i].fbuffer; - for (off = 0; off < fh->v4l_buffers.buffer_size; - off += PAGE_SIZE) - ClearPageReserved(MAP_NR(mem + off)); - kfree((void *) fh->v4l_buffers.buffer[i].fbuffer); - } - fh->v4l_buffers.buffer[i].fbuffer = NULL; + mem = fh->buffers.buffer[i].v4l.fbuffer; + for (off = 0; off < fh->buffers.buffer_size; + off += PAGE_SIZE) + ClearPageReserved(virt_to_page(mem + off)); + kfree(fh->buffers.buffer[i].v4l.fbuffer); + fh->buffers.buffer[i].v4l.fbuffer = NULL; } - fh->v4l_buffers.allocated = 0; - fh->v4l_buffers.ready_to_be_freed = 0; + fh->buffers.allocated = 0; } /* * Allocate the MJPEG grab buffers. * - * If the requested buffer size is smaller than MAX_KMALLOC_MEM, - * kmalloc is used to request a physically contiguous area, - * else we allocate the memory in framgents with get_zeroed_page. - * * If a Natoma chipset is present and this is a revision 1 zr36057, * each MJPEG buffer needs to be physically contiguous. * (RJ: This statement is from Dave Perks' original driver, * I could never check it because I have a zr36067) - * The driver cares about this because it reduces the buffer - * size to MAX_KMALLOC_MEM in that case (which forces contiguous allocation). * * RJ: The contents grab buffers needs never be accessed in the driver. * Therefore there is no need to allocate them with vmalloc in order @@ -458,162 +307,128 @@ v4l_fbuffer_free (struct file *file) * and fragment buffers are not little-endian. */ -static int -jpg_fbuffer_alloc (struct file *file) +static int jpg_fbuffer_alloc(struct zoran_fh *fh) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int i, j, off; - unsigned long mem; - - /* we might have old buffers lying around */ - if (fh->jpg_buffers.ready_to_be_freed) { - jpg_fbuffer_free(file); - } + u8 *mem; - for (i = 0; i < fh->jpg_buffers.num_buffers; i++) { - if (fh->jpg_buffers.buffer[i].frag_tab) + for (i = 0; i < fh->buffers.num_buffers; i++) { + if (fh->buffers.buffer[i].jpg.frag_tab) dprintk(2, KERN_WARNING - "%s: jpg_fbuffer_alloc() - buffer %d allready allocated!?\n", - ZR_DEVNAME(zr), i); + "%s: %s - buffer %d already allocated!?\n", + ZR_DEVNAME(zr), __func__, i); /* Allocate fragment table for this buffer */ - mem = get_zeroed_page(GFP_KERNEL); + mem = (void *)get_zeroed_page(GFP_KERNEL); if (mem == 0) { dprintk(1, KERN_ERR - "%s: jpg_fbuffer_alloc() - get_zeroed_page (frag_tab) failed for buffer %d\n", - ZR_DEVNAME(zr), i); - jpg_fbuffer_free(file); + "%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n", + ZR_DEVNAME(zr), __func__, i); + jpg_fbuffer_free(fh); return -ENOBUFS; } - fh->jpg_buffers.buffer[i].frag_tab = (__le32 *) mem; - fh->jpg_buffers.buffer[i].frag_tab_bus = - virt_to_bus((void *) mem); - - //if (alloc_contig) { - if (fh->jpg_buffers.need_contiguous) { - mem = - (unsigned long) kmalloc(fh->jpg_buffers. - buffer_size, - GFP_KERNEL); - if (mem == 0) { + fh->buffers.buffer[i].jpg.frag_tab = (__le32 *)mem; + fh->buffers.buffer[i].jpg.frag_tab_bus = virt_to_bus(mem); + + if (fh->buffers.need_contiguous) { + mem = kmalloc(fh->buffers.buffer_size, GFP_KERNEL); + if (mem == NULL) { dprintk(1, KERN_ERR - "%s: jpg_fbuffer_alloc() - kmalloc failed for buffer %d\n", - ZR_DEVNAME(zr), i); - jpg_fbuffer_free(file); + "%s: %s - kmalloc failed for buffer %d\n", + ZR_DEVNAME(zr), __func__, i); + jpg_fbuffer_free(fh); return -ENOBUFS; } - fh->jpg_buffers.buffer[i].frag_tab[0] = - cpu_to_le32(virt_to_bus((void *) mem)); - fh->jpg_buffers.buffer[i].frag_tab[1] = - cpu_to_le32(((fh->jpg_buffers.buffer_size / 4) << 1) | 1); - for (off = 0; off < fh->jpg_buffers.buffer_size; - off += PAGE_SIZE) - SetPageReserved(MAP_NR(mem + off)); + fh->buffers.buffer[i].jpg.frag_tab[0] = + cpu_to_le32(virt_to_bus(mem)); + fh->buffers.buffer[i].jpg.frag_tab[1] = + cpu_to_le32((fh->buffers.buffer_size >> 1) | 1); + for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE) + SetPageReserved(virt_to_page(mem + off)); } else { - /* jpg_bufsize is allreay page aligned */ - for (j = 0; - j < fh->jpg_buffers.buffer_size / PAGE_SIZE; - j++) { - mem = get_zeroed_page(GFP_KERNEL); - if (mem == 0) { + /* jpg_bufsize is already page aligned */ + for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) { + mem = (void *)get_zeroed_page(GFP_KERNEL); + if (mem == NULL) { dprintk(1, KERN_ERR - "%s: jpg_fbuffer_alloc() - get_zeroed_page failed for buffer %d\n", - ZR_DEVNAME(zr), i); - jpg_fbuffer_free(file); + "%s: %s - get_zeroed_page failed for buffer %d\n", + ZR_DEVNAME(zr), __func__, i); + jpg_fbuffer_free(fh); return -ENOBUFS; } - fh->jpg_buffers.buffer[i].frag_tab[2 * j] = - cpu_to_le32(virt_to_bus((void *) mem)); - fh->jpg_buffers.buffer[i].frag_tab[2 * j + - 1] = - cpu_to_le32((PAGE_SIZE / 4) << 1); - SetPageReserved(MAP_NR(mem)); + fh->buffers.buffer[i].jpg.frag_tab[2 * j] = + cpu_to_le32(virt_to_bus(mem)); + fh->buffers.buffer[i].jpg.frag_tab[2 * j + 1] = + cpu_to_le32((PAGE_SIZE >> 2) << 1); + SetPageReserved(virt_to_page(mem)); } - fh->jpg_buffers.buffer[i].frag_tab[2 * j - 1] |= cpu_to_le32(1); + fh->buffers.buffer[i].jpg.frag_tab[2 * j - 1] |= cpu_to_le32(1); } } dprintk(4, - KERN_DEBUG "%s: jpg_fbuffer_alloc() - %d KB allocated\n", - ZR_DEVNAME(zr), - (fh->jpg_buffers.num_buffers * - fh->jpg_buffers.buffer_size) >> 10); + KERN_DEBUG "%s: %s - %d KB allocated\n", + ZR_DEVNAME(zr), __func__, + (fh->buffers.num_buffers * fh->buffers.buffer_size) >> 10); - fh->jpg_buffers.allocated = 1; + fh->buffers.allocated = 1; return 0; } /* free the MJPEG grab buffers */ -static void -jpg_fbuffer_free (struct file *file) +static void jpg_fbuffer_free(struct zoran_fh *fh) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int i, j, off; unsigned char *mem; + __le32 frag_tab; + struct zoran_buffer *buffer; - dprintk(4, KERN_DEBUG "%s: jpg_fbuffer_free()\n", ZR_DEVNAME(zr)); + dprintk(4, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__); - for (i = 0; i < fh->jpg_buffers.num_buffers; i++) { - if (!fh->jpg_buffers.buffer[i].frag_tab) + for (i = 0, buffer = &fh->buffers.buffer[0]; + i < fh->buffers.num_buffers; i++, buffer++) { + if (!buffer->jpg.frag_tab) continue; - //if (alloc_contig) { - if (fh->jpg_buffers.need_contiguous) { - if (fh->jpg_buffers.buffer[i].frag_tab[0]) { - mem = (unsigned char *) bus_to_virt(le32_to_cpu( - fh->jpg_buffers.buffer[i].frag_tab[0])); - for (off = 0; - off < fh->jpg_buffers.buffer_size; - off += PAGE_SIZE) - ClearPageReserved(MAP_NR - (mem + off)); + if (fh->buffers.need_contiguous) { + frag_tab = buffer->jpg.frag_tab[0]; + + if (frag_tab) { + mem = bus_to_virt(le32_to_cpu(frag_tab)); + for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE) + ClearPageReserved(virt_to_page(mem + off)); kfree(mem); - fh->jpg_buffers.buffer[i].frag_tab[0] = 0; - fh->jpg_buffers.buffer[i].frag_tab[1] = 0; + buffer->jpg.frag_tab[0] = 0; + buffer->jpg.frag_tab[1] = 0; } } else { - for (j = 0; - j < fh->jpg_buffers.buffer_size / PAGE_SIZE; - j++) { - if (!fh->jpg_buffers.buffer[i]. - frag_tab[2 * j]) + for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) { + frag_tab = buffer->jpg.frag_tab[2 * j]; + + if (!frag_tab) break; - ClearPageReserved(MAP_NR - (bus_to_virt - (le32_to_cpu - (fh->jpg_buffers. - buffer[i].frag_tab[2 * - j])))); - free_page((unsigned long) - bus_to_virt - (le32_to_cpu - (fh->jpg_buffers. - buffer[i]. - frag_tab[2 * j]))); - fh->jpg_buffers.buffer[i].frag_tab[2 * j] = - 0; - fh->jpg_buffers.buffer[i].frag_tab[2 * j + - 1] = 0; + ClearPageReserved(virt_to_page(bus_to_virt(le32_to_cpu(frag_tab)))); + free_page((unsigned long)bus_to_virt(le32_to_cpu(frag_tab))); + buffer->jpg.frag_tab[2 * j] = 0; + buffer->jpg.frag_tab[2 * j + 1] = 0; } } - free_page((unsigned long) fh->jpg_buffers.buffer[i]. - frag_tab); - fh->jpg_buffers.buffer[i].frag_tab = NULL; + free_page((unsigned long)buffer->jpg.frag_tab); + buffer->jpg.frag_tab = NULL; } - fh->jpg_buffers.allocated = 0; - fh->jpg_buffers.ready_to_be_freed = 0; + fh->buffers.allocated = 0; } /* @@ -621,12 +436,11 @@ jpg_fbuffer_free (struct file *file) */ static int -zoran_v4l_set_format (struct file *file, +zoran_v4l_set_format (struct zoran_fh *fh, int width, int height, const struct zoran_format *format) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int bpp; @@ -636,19 +450,19 @@ zoran_v4l_set_format (struct file *file, height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) { dprintk(1, KERN_ERR - "%s: v4l_set_format() - wrong frame size (%dx%d)\n", - ZR_DEVNAME(zr), width, height); + "%s: %s - wrong frame size (%dx%d)\n", + ZR_DEVNAME(zr), __func__, width, height); return -EINVAL; } bpp = (format->depth + 7) / 8; /* Check against available buffer size */ - if (height * width * bpp > fh->v4l_buffers.buffer_size) { + if (height * width * bpp > fh->buffers.buffer_size) { dprintk(1, KERN_ERR - "%s: v4l_set_format() - video buffer size (%d kB) is too small\n", - ZR_DEVNAME(zr), fh->v4l_buffers.buffer_size >> 10); + "%s: %s - video buffer size (%d kB) is too small\n", + ZR_DEVNAME(zr), __func__, fh->buffers.buffer_size >> 10); return -EINVAL; } @@ -657,8 +471,8 @@ zoran_v4l_set_format (struct file *file, if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) { dprintk(1, KERN_ERR - "%s: v4l_set_format() - wrong frame alingment\n", - ZR_DEVNAME(zr)); + "%s: %s - wrong frame alignment\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } @@ -670,43 +484,40 @@ zoran_v4l_set_format (struct file *file, return 0; } -static int -zoran_v4l_queue_frame (struct file *file, - int num) +static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; unsigned long flags; int res = 0; - if (!fh->v4l_buffers.allocated) { + if (!fh->buffers.allocated) { dprintk(1, KERN_ERR - "%s: v4l_queue_frame() - buffers not yet allocated\n", - ZR_DEVNAME(zr)); + "%s: %s - buffers not yet allocated\n", + ZR_DEVNAME(zr), __func__); res = -ENOMEM; } /* No grabbing outside the buffer range! */ - if (num >= fh->v4l_buffers.num_buffers || num < 0) { + if (num >= fh->buffers.num_buffers || num < 0) { dprintk(1, KERN_ERR - "%s: v4l_queue_frame() - buffer %d is out of range\n", - ZR_DEVNAME(zr), num); + "%s: %s - buffer %d is out of range\n", + ZR_DEVNAME(zr), __func__, num); res = -EINVAL; } spin_lock_irqsave(&zr->spinlock, flags); - if (fh->v4l_buffers.active == ZORAN_FREE) { + if (fh->buffers.active == ZORAN_FREE) { if (zr->v4l_buffers.active == ZORAN_FREE) { - zr->v4l_buffers = fh->v4l_buffers; - fh->v4l_buffers.active = ZORAN_ACTIVE; + zr->v4l_buffers = fh->buffers; + fh->buffers.active = ZORAN_ACTIVE; } else { dprintk(1, KERN_ERR - "%s: v4l_queue_frame() - another session is already capturing\n", - ZR_DEVNAME(zr)); + "%s: %s - another session is already capturing\n", + ZR_DEVNAME(zr), __func__); res = -EBUSY; } } @@ -717,7 +528,7 @@ zoran_v4l_queue_frame (struct file *file, default: case BUZ_STATE_PEND: if (zr->v4l_buffers.active == ZORAN_FREE) { - fh->v4l_buffers.active = ZORAN_FREE; + fh->buffers.active = ZORAN_FREE; zr->v4l_buffers.allocated = 0; } res = -EBUSY; /* what are you doing? */ @@ -725,19 +536,17 @@ zoran_v4l_queue_frame (struct file *file, case BUZ_STATE_DONE: dprintk(2, KERN_WARNING - "%s: v4l_queue_frame() - queueing buffer %d in state DONE!?\n", - ZR_DEVNAME(zr), num); + "%s: %s - queueing buffer %d in state DONE!?\n", + ZR_DEVNAME(zr), __func__, num); case BUZ_STATE_USER: /* since there is at least one unused buffer there's room for at least * one more pend[] entry */ - zr->v4l_pend[zr->v4l_pend_head++ & - V4L_MASK_FRAME] = num; + zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = num; zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND; zr->v4l_buffers.buffer[num].bs.length = fh->v4l_settings.bytesperline * zr->v4l_settings.height; - fh->v4l_buffers.buffer[num] = - zr->v4l_buffers.buffer[num]; + fh->buffers.buffer[num] = zr->v4l_buffers.buffer[num]; break; } } @@ -745,65 +554,7 @@ zoran_v4l_queue_frame (struct file *file, spin_unlock_irqrestore(&zr->spinlock, flags); if (!res && zr->v4l_buffers.active == ZORAN_FREE) - zr->v4l_buffers.active = fh->v4l_buffers.active; - - return res; -} - -static int -v4l_grab (struct file *file, - struct video_mmap *mp) -{ - struct zoran_fh *fh = file->private_data; - struct zoran *zr = fh->zr; - int res = 0, i; - - for (i = 0; i < NUM_FORMATS; i++) { - if (zoran_formats[i].palette == mp->format && - zoran_formats[i].flags & ZORAN_FORMAT_CAPTURE && - !(zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)) - break; - } - if (i == NUM_FORMATS || zoran_formats[i].depth == 0) { - dprintk(1, - KERN_ERR - "%s: v4l_grab() - wrong bytes-per-pixel format\n", - ZR_DEVNAME(zr)); - return -EINVAL; - } - - /* - * To minimize the time spent in the IRQ routine, we avoid setting up - * the video front end there. - * If this grab has different parameters from a running streaming capture - * we stop the streaming capture and start it over again. - */ - if (zr->v4l_memgrab_active && - (zr->v4l_settings.width != mp->width || - zr->v4l_settings.height != mp->height || - zr->v4l_settings.format->palette != mp->format)) { - res = wait_grab_pending(zr); - if (res) - return res; - } - if ((res = zoran_v4l_set_format(file, - mp->width, - mp->height, - &zoran_formats[i]))) - return res; - zr->v4l_settings = fh->v4l_settings; - - /* queue the frame in the pending queue */ - if ((res = zoran_v4l_queue_frame(file, mp->frame))) { - fh->v4l_buffers.active = ZORAN_FREE; - return res; - } - - /* put the 36057 into frame grabbing mode */ - if (!res && !zr->v4l_memgrab_active) - zr36057_set_memgrab(zr, 1); - - //dprintk(4, KERN_INFO "%s: Frame grab 3...\n", ZR_DEVNAME(zr)); + zr->v4l_buffers.active = fh->buffers.active; return res; } @@ -812,27 +563,24 @@ v4l_grab (struct file *file, * Sync on a V4L buffer */ -static int -v4l_sync (struct file *file, - int frame) +static int v4l_sync(struct zoran_fh *fh, int frame) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; unsigned long flags; - if (fh->v4l_buffers.active == ZORAN_FREE) { + if (fh->buffers.active == ZORAN_FREE) { dprintk(1, KERN_ERR - "%s: v4l_sync() - no grab active for this session\n", - ZR_DEVNAME(zr)); + "%s: %s - no grab active for this session\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } /* check passed-in frame number */ - if (frame >= fh->v4l_buffers.num_buffers || frame < 0) { + if (frame >= fh->buffers.num_buffers || frame < 0) { dprintk(1, - KERN_ERR "%s: v4l_sync() - frame %d is invalid\n", - ZR_DEVNAME(zr), frame); + KERN_ERR "%s: %s - frame %d is invalid\n", + ZR_DEVNAME(zr), __func__, frame); return -EINVAL; } @@ -840,15 +588,14 @@ v4l_sync (struct file *file, if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) { dprintk(1, KERN_ERR - "%s: v4l_sync() - attempt to sync on a buffer which was not queued?\n", - ZR_DEVNAME(zr)); + "%s: %s - attempt to sync on a buffer which was not queued?\n", + ZR_DEVNAME(zr), __func__); return -EPROTO; } /* wait on this buffer to get ready */ if (!wait_event_interruptible_timeout(zr->v4l_capq, - (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), - 10*HZ)) + (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ)) return -ETIME; if (signal_pending(current)) return -ERESTARTSYS; @@ -856,11 +603,11 @@ v4l_sync (struct file *file, /* buffer should now be in BUZ_STATE_DONE */ if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE) dprintk(2, - KERN_ERR "%s: v4l_sync() - internal state error\n", - ZR_DEVNAME(zr)); + KERN_ERR "%s: %s - internal state error\n", + ZR_DEVNAME(zr), __func__); zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER; - fh->v4l_buffers.buffer[frame] = zr->v4l_buffers.buffer[frame]; + fh->buffers.buffer[frame] = zr->v4l_buffers.buffer[frame]; spin_lock_irqsave(&zr->spinlock, flags); @@ -868,8 +615,7 @@ v4l_sync (struct file *file, if (zr->v4l_pend_tail == zr->v4l_pend_head) { zr36057_set_memgrab(zr, 0); if (zr->v4l_buffers.active == ZORAN_ACTIVE) { - fh->v4l_buffers.active = zr->v4l_buffers.active = - ZORAN_FREE; + fh->buffers.active = zr->v4l_buffers.active = ZORAN_FREE; zr->v4l_buffers.allocated = 0; } } @@ -883,31 +629,28 @@ v4l_sync (struct file *file, * Queue a MJPEG buffer for capture/playback */ -static int -zoran_jpg_queue_frame (struct file *file, - int num, - enum zoran_codec_mode mode) +static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num, + enum zoran_codec_mode mode) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; unsigned long flags; int res = 0; /* Check if buffers are allocated */ - if (!fh->jpg_buffers.allocated) { + if (!fh->buffers.allocated) { dprintk(1, KERN_ERR - "%s: jpg_queue_frame() - buffers not yet allocated\n", - ZR_DEVNAME(zr)); + "%s: %s - buffers not yet allocated\n", + ZR_DEVNAME(zr), __func__); return -ENOMEM; } /* No grabbing outside the buffer range! */ - if (num >= fh->jpg_buffers.num_buffers || num < 0) { + if (num >= fh->buffers.num_buffers || num < 0) { dprintk(1, KERN_ERR - "%s: jpg_queue_frame() - buffer %d out of range\n", - ZR_DEVNAME(zr), num); + "%s: %s - buffer %d out of range\n", + ZR_DEVNAME(zr), __func__, num); return -EINVAL; } @@ -918,20 +661,20 @@ zoran_jpg_queue_frame (struct file *file, /* wrong codec mode active - invalid */ dprintk(1, KERN_ERR - "%s: jpg_queue_frame() - codec in wrong mode\n", - ZR_DEVNAME(zr)); + "%s: %s - codec in wrong mode\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } - if (fh->jpg_buffers.active == ZORAN_FREE) { + if (fh->buffers.active == ZORAN_FREE) { if (zr->jpg_buffers.active == ZORAN_FREE) { - zr->jpg_buffers = fh->jpg_buffers; - fh->jpg_buffers.active = ZORAN_ACTIVE; + zr->jpg_buffers = fh->buffers; + fh->buffers.active = ZORAN_ACTIVE; } else { dprintk(1, KERN_ERR - "%s: jpg_queue_frame() - another session is already capturing\n", - ZR_DEVNAME(zr)); + "%s: %s - another session is already capturing\n", + ZR_DEVNAME(zr), __func__); res = -EBUSY; } } @@ -948,23 +691,21 @@ zoran_jpg_queue_frame (struct file *file, case BUZ_STATE_DONE: dprintk(2, KERN_WARNING - "%s: jpg_queue_frame() - queing frame in BUZ_STATE_DONE state!?\n", - ZR_DEVNAME(zr)); + "%s: %s - queing frame in BUZ_STATE_DONE state!?\n", + ZR_DEVNAME(zr), __func__); case BUZ_STATE_USER: /* since there is at least one unused buffer there's room for at *least one more pend[] entry */ - zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = - num; + zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = num; zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND; - fh->jpg_buffers.buffer[num] = - zr->jpg_buffers.buffer[num]; + fh->buffers.buffer[num] = zr->jpg_buffers.buffer[num]; zoran_feed_stat_com(zr); break; default: case BUZ_STATE_DMA: case BUZ_STATE_PEND: if (zr->jpg_buffers.active == ZORAN_FREE) { - fh->jpg_buffers.active = ZORAN_FREE; + fh->buffers.active = ZORAN_FREE; zr->jpg_buffers.allocated = 0; } res = -EBUSY; /* what are you doing? */ @@ -974,47 +715,41 @@ zoran_jpg_queue_frame (struct file *file, spin_unlock_irqrestore(&zr->spinlock, flags); - if (!res && zr->jpg_buffers.active == ZORAN_FREE) { - zr->jpg_buffers.active = fh->jpg_buffers.active; - } + if (!res && zr->jpg_buffers.active == ZORAN_FREE) + zr->jpg_buffers.active = fh->buffers.active; return res; } -static int -jpg_qbuf (struct file *file, - int frame, - enum zoran_codec_mode mode) +static int jpg_qbuf(struct zoran_fh *fh, int frame, enum zoran_codec_mode mode) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; int res = 0; /* Does the user want to stop streaming? */ if (frame < 0) { if (zr->codec_mode == mode) { - if (fh->jpg_buffers.active == ZORAN_FREE) { + if (fh->buffers.active == ZORAN_FREE) { dprintk(1, KERN_ERR - "%s: jpg_qbuf(-1) - session not active\n", - ZR_DEVNAME(zr)); + "%s: %s(-1) - session not active\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } - fh->jpg_buffers.active = zr->jpg_buffers.active = - ZORAN_FREE; + fh->buffers.active = zr->jpg_buffers.active = ZORAN_FREE; zr->jpg_buffers.allocated = 0; zr36057_enable_jpg(zr, BUZ_MODE_IDLE); return 0; } else { dprintk(1, KERN_ERR - "%s: jpg_qbuf() - stop streaming but not in streaming mode\n", - ZR_DEVNAME(zr)); + "%s: %s - stop streaming but not in streaming mode\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } } - if ((res = zoran_jpg_queue_frame(file, frame, mode))) + if ((res = zoran_jpg_queue_frame(fh, frame, mode))) return res; /* Start the jpeg codec when the first frame is queued */ @@ -1028,28 +763,25 @@ jpg_qbuf (struct file *file, * Sync on a MJPEG buffer */ -static int -jpg_sync (struct file *file, - struct zoran_sync *bs) +static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; unsigned long flags; int frame; - if (fh->jpg_buffers.active == ZORAN_FREE) { + if (fh->buffers.active == ZORAN_FREE) { dprintk(1, KERN_ERR - "%s: jpg_sync() - capture is not currently active\n", - ZR_DEVNAME(zr)); + "%s: %s - capture is not currently active\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS && zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) { dprintk(1, KERN_ERR - "%s: jpg_sync() - codec not in streaming mode\n", - ZR_DEVNAME(zr)); + "%s: %s - codec not in streaming mode\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } if (!wait_event_interruptible_timeout(zr->jpg_capq, @@ -1064,8 +796,8 @@ jpg_sync (struct file *file, sizeof(isr), &isr); dprintk(1, KERN_ERR - "%s: jpg_sync() - timeout: codec isr=0x%02x\n", - ZR_DEVNAME(zr), isr); + "%s: %s - timeout: codec isr=0x%02x\n", + ZR_DEVNAME(zr), __func__, isr); return -ETIME; @@ -1083,28 +815,26 @@ jpg_sync (struct file *file, /* buffer should now be in BUZ_STATE_DONE */ if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE) dprintk(2, - KERN_ERR "%s: jpg_sync() - internal state error\n", - ZR_DEVNAME(zr)); + KERN_ERR "%s: %s - internal state error\n", + ZR_DEVNAME(zr), __func__); *bs = zr->jpg_buffers.buffer[frame].bs; bs->frame = frame; zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER; - fh->jpg_buffers.buffer[frame] = zr->jpg_buffers.buffer[frame]; + fh->buffers.buffer[frame] = zr->jpg_buffers.buffer[frame]; spin_unlock_irqrestore(&zr->spinlock, flags); return 0; } -static void -zoran_open_init_session (struct file *file) +static void zoran_open_init_session(struct zoran_fh *fh) { int i; - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; /* Per default, map the V4L Buffers */ - fh->map_mode = ZORAN_MAP_MODE_RAW; + map_mode_raw(fh); /* take over the card's current settings */ fh->overlay_settings = zr->overlay_settings; @@ -1114,40 +844,21 @@ zoran_open_init_session (struct file *file) /* v4l settings */ fh->v4l_settings = zr->v4l_settings; - - /* v4l_buffers */ - memset(&fh->v4l_buffers, 0, sizeof(struct zoran_v4l_struct)); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - fh->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ - fh->v4l_buffers.buffer[i].bs.frame = i; - } - fh->v4l_buffers.allocated = 0; - fh->v4l_buffers.ready_to_be_freed = 0; - fh->v4l_buffers.active = ZORAN_FREE; - fh->v4l_buffers.buffer_size = v4l_bufsize; - fh->v4l_buffers.num_buffers = v4l_nbufs; - /* jpg settings */ fh->jpg_settings = zr->jpg_settings; - /* jpg_buffers */ - memset(&fh->jpg_buffers, 0, sizeof(struct zoran_jpg_struct)); - for (i = 0; i < BUZ_MAX_FRAME; i++) { - fh->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ - fh->jpg_buffers.buffer[i].bs.frame = i; + /* buffers */ + memset(&fh->buffers, 0, sizeof(fh->buffers)); + for (i = 0; i < MAX_FRAME; i++) { + fh->buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */ + fh->buffers.buffer[i].bs.frame = i; } - fh->jpg_buffers.need_contiguous = zr->jpg_buffers.need_contiguous; - fh->jpg_buffers.allocated = 0; - fh->jpg_buffers.ready_to_be_freed = 0; - fh->jpg_buffers.active = ZORAN_FREE; - fh->jpg_buffers.buffer_size = jpg_bufsize; - fh->jpg_buffers.num_buffers = jpg_nbufs; + fh->buffers.allocated = 0; + fh->buffers.active = ZORAN_FREE; } -static void -zoran_close_end_session (struct file *file) +static void zoran_close_end_session(struct zoran_fh *fh) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; /* overlay */ @@ -1159,36 +870,32 @@ zoran_close_end_session (struct file *file) zr->overlay_mask = NULL; } - /* v4l capture */ - if (fh->v4l_buffers.active != ZORAN_FREE) { - unsigned long flags; + if (fh->map_mode == ZORAN_MAP_MODE_RAW) { + /* v4l capture */ + if (fh->buffers.active != ZORAN_FREE) { + unsigned long flags; - spin_lock_irqsave(&zr->spinlock, flags); - zr36057_set_memgrab(zr, 0); - zr->v4l_buffers.allocated = 0; - zr->v4l_buffers.active = fh->v4l_buffers.active = - ZORAN_FREE; - spin_unlock_irqrestore(&zr->spinlock, flags); - } - - /* v4l buffers */ - if (fh->v4l_buffers.allocated || - fh->v4l_buffers.ready_to_be_freed) { - v4l_fbuffer_free(file); - } + spin_lock_irqsave(&zr->spinlock, flags); + zr36057_set_memgrab(zr, 0); + zr->v4l_buffers.allocated = 0; + zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; + spin_unlock_irqrestore(&zr->spinlock, flags); + } - /* jpg capture */ - if (fh->jpg_buffers.active != ZORAN_FREE) { - zr36057_enable_jpg(zr, BUZ_MODE_IDLE); - zr->jpg_buffers.allocated = 0; - zr->jpg_buffers.active = fh->jpg_buffers.active = - ZORAN_FREE; - } + /* v4l buffers */ + if (fh->buffers.allocated) + v4l_fbuffer_free(fh); + } else { + /* jpg capture */ + if (fh->buffers.active != ZORAN_FREE) { + zr36057_enable_jpg(zr, BUZ_MODE_IDLE); + zr->jpg_buffers.allocated = 0; + zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE; + } - /* jpg buffers */ - if (fh->jpg_buffers.allocated || - fh->jpg_buffers.ready_to_be_freed) { - jpg_fbuffer_free(file); + /* jpg buffers */ + if (fh->buffers.allocated) + jpg_fbuffer_free(fh); } } @@ -1202,15 +909,11 @@ static int zoran_open(struct file *file) struct zoran_fh *fh; int res, first_open = 0; - dprintk(2, KERN_INFO "%s: zoran_open(%s, pid=[%d]), users(-)=%d\n", - ZR_DEVNAME(zr), current->comm, task_pid_nr(current), zr->user + 1); + dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n", + ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1); lock_kernel(); - /* see fs/device.c - the kernel already locks during open(), - * so locking ourselves only causes deadlocks */ - /*mutex_lock(&zr->resource_lock);*/ - if (zr->user >= 2048) { dprintk(1, KERN_ERR "%s: too many users (%d) on device\n", ZR_DEVNAME(zr), zr->user); @@ -1218,41 +921,15 @@ static int zoran_open(struct file *file) goto fail_unlock; } - if (!zr->decoder) { - dprintk(1, - KERN_ERR "%s: no TV decoder loaded for device!\n", - ZR_DEVNAME(zr)); - res = -EIO; - goto fail_unlock; - } - - if (!try_module_get(zr->decoder->driver->driver.owner)) { - dprintk(1, - KERN_ERR - "%s: failed to grab ownership of video decoder\n", - ZR_DEVNAME(zr)); - res = -EIO; - goto fail_unlock; - } - if (zr->encoder && - !try_module_get(zr->encoder->driver->driver.owner)) { - dprintk(1, - KERN_ERR - "%s: failed to grab ownership of video encoder\n", - ZR_DEVNAME(zr)); - res = -EIO; - goto fail_decoder; - } - /* now, create the open()-specific file_ops struct */ fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL); if (!fh) { dprintk(1, KERN_ERR - "%s: zoran_open() - allocation of zoran_fh failed\n", - ZR_DEVNAME(zr)); + "%s: %s - allocation of zoran_fh failed\n", + ZR_DEVNAME(zr), __func__); res = -ENOMEM; - goto fail_encoder; + goto fail_unlock; } /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows * on norm-change! */ @@ -1261,8 +938,8 @@ static int zoran_open(struct file *file) if (!fh->overlay_mask) { dprintk(1, KERN_ERR - "%s: zoran_open() - allocation of overlay_mask failed\n", - ZR_DEVNAME(zr)); + "%s: %s - allocation of overlay_mask failed\n", + ZR_DEVNAME(zr), __func__); res = -ENOMEM; goto fail_fh; } @@ -1284,18 +961,13 @@ static int zoran_open(struct file *file) /* set file_ops stuff */ file->private_data = fh; fh->zr = zr; - zoran_open_init_session(file); + zoran_open_init_session(fh); unlock_kernel(); return 0; fail_fh: kfree(fh); -fail_encoder: - if (zr->encoder) - module_put(zr->encoder->driver->driver.owner); -fail_decoder: - module_put(zr->decoder->driver->driver.owner); fail_unlock: unlock_kernel(); @@ -1311,14 +983,14 @@ zoran_close(struct file *file) struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; - dprintk(2, KERN_INFO "%s: zoran_close(%s, pid=[%d]), users(+)=%d\n", - ZR_DEVNAME(zr), current->comm, task_pid_nr(current), zr->user - 1); + dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(+)=%d\n", + ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user - 1); /* kernel locks (fs/device.c), so don't do that ourselves * (prevents deadlocks) */ /*mutex_lock(&zr->resource_lock);*/ - zoran_close_end_session(file); + zoran_close_end_session(fh); if (zr->user-- == 1) { /* Last process */ /* Clean up JPEG process */ @@ -1346,9 +1018,10 @@ zoran_close(struct file *file) zoran_set_pci_master(zr, 0); if (!pass_through) { /* Switch to color bar */ - int zero = 0, two = 2; - decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero); - encoder_command(zr, ENCODER_SET_INPUT, &two); + struct v4l2_routing route = { 2, 0 }; + + decoder_call(zr, video, s_stream, 0); + encoder_call(zr, video, s_routing, &route); } } @@ -1356,14 +1029,7 @@ zoran_close(struct file *file) kfree(fh->overlay_mask); kfree(fh); - /* release locks on the i2c modules */ - module_put(zr->decoder->driver->driver.owner); - if (zr->encoder) - module_put(zr->encoder->driver->driver.owner); - - /*mutex_unlock(&zr->resource_lock);*/ - - dprintk(4, KERN_INFO "%s: zoran_close() done\n", ZR_DEVNAME(zr)); + dprintk(4, KERN_INFO "%s: %s done\n", ZR_DEVNAME(zr), __func__); return 0; } @@ -1391,15 +1057,13 @@ zoran_write (struct file *file, return -EINVAL; } -static int -setup_fbuffer (struct file *file, +static int setup_fbuffer(struct zoran_fh *fh, void *base, const struct zoran_format *fmt, int width, int height, int bytesperline) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; /* (Ronald) v4l/v4l2 guidelines */ @@ -1427,8 +1091,8 @@ setup_fbuffer (struct file *file, * friendly and silently do as if nothing went wrong */ dprintk(3, KERN_ERR - "%s: setup_fbuffer() - forced overlay turnoff because framebuffer changed\n", - ZR_DEVNAME(zr)); + "%s: %s - forced overlay turnoff because framebuffer changed\n", + ZR_DEVNAME(zr), __func__); zr36057_overlay(zr, 0); } #endif @@ -1436,31 +1100,31 @@ setup_fbuffer (struct file *file, if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) { dprintk(1, KERN_ERR - "%s: setup_fbuffer() - no valid overlay format given\n", - ZR_DEVNAME(zr)); + "%s: %s - no valid overlay format given\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } if (height <= 0 || width <= 0 || bytesperline <= 0) { dprintk(1, KERN_ERR - "%s: setup_fbuffer() - invalid height/width/bpl value (%d|%d|%d)\n", - ZR_DEVNAME(zr), width, height, bytesperline); + "%s: %s - invalid height/width/bpl value (%d|%d|%d)\n", + ZR_DEVNAME(zr), __func__, width, height, bytesperline); return -EINVAL; } if (bytesperline & 3) { dprintk(1, KERN_ERR - "%s: setup_fbuffer() - bytesperline (%d) must be 4-byte aligned\n", - ZR_DEVNAME(zr), bytesperline); + "%s: %s - bytesperline (%d) must be 4-byte aligned\n", + ZR_DEVNAME(zr), __func__, bytesperline); return -EINVAL; } - zr->buffer.base = (void *) ((unsigned long) base & ~3); - zr->buffer.height = height; - zr->buffer.width = width; - zr->buffer.depth = fmt->depth; + zr->vbuf_base = (void *) ((unsigned long) base & ~3); + zr->vbuf_height = height; + zr->vbuf_width = width; + zr->vbuf_depth = fmt->depth; zr->overlay_settings.format = fmt; - zr->buffer.bytesperline = bytesperline; + zr->vbuf_bytesperline = bytesperline; /* The user should set new window parameters */ zr->overlay_settings.is_set = 0; @@ -1469,35 +1133,27 @@ setup_fbuffer (struct file *file, } -static int -setup_window (struct file *file, - int x, - int y, - int width, - int height, - struct video_clip __user *clips, - int clipcount, - void __user *bitmap) +static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height, + struct v4l2_clip __user *clips, int clipcount, void __user *bitmap) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; - struct video_clip *vcp = NULL; + struct v4l2_clip *vcp = NULL; int on, end; - if (!zr->buffer.base) { + if (!zr->vbuf_base) { dprintk(1, KERN_ERR - "%s: setup_window() - frame buffer has to be set first\n", - ZR_DEVNAME(zr)); + "%s: %s - frame buffer has to be set first\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } if (!fh->overlay_settings.format) { dprintk(1, KERN_ERR - "%s: setup_window() - no overlay format set\n", - ZR_DEVNAME(zr)); + "%s: %s - no overlay format set\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } @@ -1505,13 +1161,13 @@ setup_window (struct file *file, * The video front end needs 4-byte alinged line sizes, we correct that * silently here if necessary */ - if (zr->buffer.depth == 15 || zr->buffer.depth == 16) { + if (zr->vbuf_depth == 15 || zr->vbuf_depth == 16) { end = (x + width) & ~1; /* round down */ x = (x + 1) & ~1; /* round up */ width = end - x; } - if (zr->buffer.depth == 24) { + if (zr->vbuf_depth == 24) { end = (x + width) & ~3; /* round down */ x = (x + 3) & ~3; /* round up */ width = end - x; @@ -1527,8 +1183,8 @@ setup_window (struct file *file, width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) { dprintk(1, KERN_ERR - "%s: setup_window() - width = %d or height = %d invalid\n", - ZR_DEVNAME(zr), width, height); + "%s: %s - width = %d or height = %d invalid\n", + ZR_DEVNAME(zr), __func__, width, height); return -EINVAL; } @@ -1566,20 +1222,20 @@ setup_window (struct file *file, } } else if (clipcount > 0) { /* write our own bitmap from the clips */ - vcp = vmalloc(sizeof(struct video_clip) * (clipcount + 4)); + vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4)); if (vcp == NULL) { dprintk(1, KERN_ERR - "%s: setup_window() - Alloc of clip mask failed\n", - ZR_DEVNAME(zr)); + "%s: %s - Alloc of clip mask failed\n", + ZR_DEVNAME(zr), __func__); return -ENOMEM; } if (copy_from_user - (vcp, clips, sizeof(struct video_clip) * clipcount)) { + (vcp, clips, sizeof(struct v4l2_clip) * clipcount)) { vfree(vcp); return -EFAULT; } - write_overlay_mask(file, vcp, clipcount); + write_overlay_mask(fh, vcp, clipcount); vfree(vcp); } @@ -1595,11 +1251,8 @@ setup_window (struct file *file, return wait_grab_pending(zr); } -static int -setup_overlay (struct file *file, - int on) +static int setup_overlay(struct zoran_fh *fh, int on) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; /* If there is nothing to do, return immediatly */ @@ -1612,16 +1265,16 @@ setup_overlay (struct file *file, fh->overlay_active == ZORAN_FREE) { dprintk(1, KERN_ERR - "%s: setup_overlay() - overlay is already active for another session\n", - ZR_DEVNAME(zr)); + "%s: %s - overlay is already active for another session\n", + ZR_DEVNAME(zr), __func__); return -EBUSY; } if (!on && zr->overlay_active != ZORAN_FREE && fh->overlay_active == ZORAN_FREE) { dprintk(1, KERN_ERR - "%s: setup_overlay() - you cannot cancel someone else's session\n", - ZR_DEVNAME(zr)); + "%s: %s - you cannot cancel someone else's session\n", + ZR_DEVNAME(zr), __func__); return -EPERM; } @@ -1634,18 +1287,18 @@ setup_overlay (struct file *file, zr36057_overlay(zr, 0); zr->overlay_mask = NULL; } else { - if (!zr->buffer.base || !fh->overlay_settings.is_set) { + if (!zr->vbuf_base || !fh->overlay_settings.is_set) { dprintk(1, KERN_ERR - "%s: setup_overlay() - buffer or window not set\n", - ZR_DEVNAME(zr)); + "%s: %s - buffer or window not set\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } if (!fh->overlay_settings.format) { dprintk(1, KERN_ERR - "%s: setup_overlay() - no overlay format set\n", - ZR_DEVNAME(zr)); + "%s: %s - no overlay format set\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } zr->overlay_active = fh->overlay_active = ZORAN_LOCKED; @@ -1662,41 +1315,47 @@ setup_overlay (struct file *file, return wait_grab_pending(zr); } - /* get the status of a buffer in the clients buffer queue */ -static int -zoran_v4l2_buffer_status (struct file *file, - struct v4l2_buffer *buf, - int num) +/* get the status of a buffer in the clients buffer queue */ +static int zoran_v4l2_buffer_status(struct zoran_fh *fh, + struct v4l2_buffer *buf, int num) { - struct zoran_fh *fh = file->private_data; struct zoran *zr = fh->zr; + unsigned long flags; buf->flags = V4L2_BUF_FLAG_MAPPED; switch (fh->map_mode) { case ZORAN_MAP_MODE_RAW: - /* check range */ - if (num < 0 || num >= fh->v4l_buffers.num_buffers || - !fh->v4l_buffers.allocated) { + if (num < 0 || num >= fh->buffers.num_buffers || + !fh->buffers.allocated) { dprintk(1, KERN_ERR - "%s: v4l2_buffer_status() - wrong number or buffers not allocated\n", - ZR_DEVNAME(zr)); + "%s: %s - wrong number or buffers not allocated\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } + spin_lock_irqsave(&zr->spinlock, flags); + dprintk(3, + KERN_DEBUG + "%s: %s() - raw active=%c, buffer %d: state=%c, map=%c\n", + ZR_DEVNAME(zr), __func__, + "FAL"[fh->buffers.active], num, + "UPMD"[zr->v4l_buffers.buffer[num].state], + fh->buffers.buffer[num].map ? 'Y' : 'N'); + spin_unlock_irqrestore(&zr->spinlock, flags); + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->length = fh->v4l_buffers.buffer_size; + buf->length = fh->buffers.buffer_size; /* get buffer */ - buf->bytesused = fh->v4l_buffers.buffer[num].bs.length; - if (fh->v4l_buffers.buffer[num].state == BUZ_STATE_DONE || - fh->v4l_buffers.buffer[num].state == BUZ_STATE_USER) { - buf->sequence = fh->v4l_buffers.buffer[num].bs.seq; + buf->bytesused = fh->buffers.buffer[num].bs.length; + if (fh->buffers.buffer[num].state == BUZ_STATE_DONE || + fh->buffers.buffer[num].state == BUZ_STATE_USER) { + buf->sequence = fh->buffers.buffer[num].bs.seq; buf->flags |= V4L2_BUF_FLAG_DONE; - buf->timestamp = - fh->v4l_buffers.buffer[num].bs.timestamp; + buf->timestamp = fh->buffers.buffer[num].bs.timestamp; } else { buf->flags |= V4L2_BUF_FLAG_QUEUED; } @@ -1712,28 +1371,26 @@ zoran_v4l2_buffer_status (struct file *file, case ZORAN_MAP_MODE_JPG_PLAY: /* check range */ - if (num < 0 || num >= fh->jpg_buffers.num_buffers || - !fh->jpg_buffers.allocated) { + if (num < 0 || num >= fh->buffers.num_buffers || + !fh->buffers.allocated) { dprintk(1, KERN_ERR - "%s: v4l2_buffer_status() - wrong number or buffers not allocated\n", - ZR_DEVNAME(zr)); + "%s: %s - wrong number or buffers not allocated\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ? V4L2_BUF_TYPE_VIDEO_CAPTURE : V4L2_BUF_TYPE_VIDEO_OUTPUT; - buf->length = fh->jpg_buffers.buffer_size; + buf->length = fh->buffers.buffer_size; /* these variables are only written after frame has been captured */ - if (fh->jpg_buffers.buffer[num].state == BUZ_STATE_DONE || - fh->jpg_buffers.buffer[num].state == BUZ_STATE_USER) { - buf->sequence = fh->jpg_buffers.buffer[num].bs.seq; - buf->timestamp = - fh->jpg_buffers.buffer[num].bs.timestamp; - buf->bytesused = - fh->jpg_buffers.buffer[num].bs.length; + if (fh->buffers.buffer[num].state == BUZ_STATE_DONE || + fh->buffers.buffer[num].state == BUZ_STATE_USER) { + buf->sequence = fh->buffers.buffer[num].bs.seq; + buf->timestamp = fh->buffers.buffer[num].bs.timestamp; + buf->bytesused = fh->buffers.buffer[num].bs.length; buf->flags |= V4L2_BUF_FLAG_DONE; } else { buf->flags |= V4L2_BUF_FLAG_QUEUED; @@ -1741,14 +1398,11 @@ zoran_v4l2_buffer_status (struct file *file, /* which fields are these? */ if (fh->jpg_settings.TmpDcm != 1) - buf->field = - fh->jpg_settings. - odd_even ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; + buf->field = fh->jpg_settings.odd_even ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM; else - buf->field = - fh->jpg_settings. - odd_even ? V4L2_FIELD_SEQ_TB : - V4L2_FIELD_SEQ_BT; + buf->field = fh->jpg_settings.odd_even ? + V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT; break; @@ -1756,8 +1410,8 @@ zoran_v4l2_buffer_status (struct file *file, dprintk(5, KERN_ERR - "%s: v4l2_buffer_status() - invalid buffer type|map_mode (%d|%d)\n", - ZR_DEVNAME(zr), buf->type, fh->map_mode); + "%s: %s - invalid buffer type|map_mode (%d|%d)\n", + ZR_DEVNAME(zr), __func__, buf->type, fh->map_mode); return -EINVAL; } @@ -1770,81 +1424,55 @@ zoran_v4l2_buffer_status (struct file *file, static int zoran_set_norm (struct zoran *zr, - int norm) /* VIDEO_MODE_* */ + v4l2_std_id norm) { - int norm_encoder, on; + int on; if (zr->v4l_buffers.active != ZORAN_FREE || zr->jpg_buffers.active != ZORAN_FREE) { dprintk(1, KERN_WARNING - "%s: set_norm() called while in playback/capture mode\n", - ZR_DEVNAME(zr)); + "%s: %s called while in playback/capture mode\n", + ZR_DEVNAME(zr), __func__); return -EBUSY; } - if (lock_norm && norm != zr->norm) { - if (lock_norm > 1) { - dprintk(1, - KERN_WARNING - "%s: set_norm() - TV standard is locked, can not switch norm\n", - ZR_DEVNAME(zr)); - return -EPERM; - } else { - dprintk(1, - KERN_WARNING - "%s: set_norm() - TV standard is locked, norm was not changed\n", - ZR_DEVNAME(zr)); - norm = zr->norm; - } - } - - if (norm != VIDEO_MODE_AUTO && - (norm < 0 || norm >= zr->card.norms || - !zr->card.tvn[norm])) { + if (!(norm & zr->card.norms)) { dprintk(1, - KERN_ERR "%s: set_norm() - unsupported norm %d\n", - ZR_DEVNAME(zr), norm); + KERN_ERR "%s: %s - unsupported norm %llx\n", + ZR_DEVNAME(zr), __func__, norm); return -EINVAL; } - if (norm == VIDEO_MODE_AUTO) { - int status; + if (norm == V4L2_STD_ALL) { + int status = 0; + v4l2_std_id std = 0; - /* if we have autodetect, ... */ - struct video_decoder_capability caps; - decoder_command(zr, DECODER_GET_CAPABILITIES, &caps); - if (!(caps.flags & VIDEO_DECODER_AUTO)) { - dprintk(1, KERN_ERR "%s: norm=auto unsupported\n", - ZR_DEVNAME(zr)); - return -EINVAL; - } - - decoder_command(zr, DECODER_SET_NORM, &norm); + decoder_call(zr, video, querystd, &std); + decoder_call(zr, tuner, s_std, std); /* let changes come into effect */ ssleep(2); - decoder_command(zr, DECODER_GET_STATUS, &status); - if (!(status & DECODER_STATUS_GOOD)) { + decoder_call(zr, video, g_input_status, &status); + if (status & V4L2_IN_ST_NO_SIGNAL) { dprintk(1, KERN_ERR - "%s: set_norm() - no norm detected\n", - ZR_DEVNAME(zr)); + "%s: %s - no norm detected\n", + ZR_DEVNAME(zr), __func__); /* reset norm */ - decoder_command(zr, DECODER_SET_NORM, &zr->norm); + decoder_call(zr, tuner, s_std, zr->norm); return -EIO; } - if (status & DECODER_STATUS_NTSC) - norm = VIDEO_MODE_NTSC; - else if (status & DECODER_STATUS_SECAM) - norm = VIDEO_MODE_SECAM; - else - norm = VIDEO_MODE_PAL; + norm = std; } - zr->timing = zr->card.tvn[norm]; - norm_encoder = norm; + if (norm & V4L2_STD_SECAM) + zr->timing = zr->card.tvn[2]; + else if (norm & V4L2_STD_NTSC) + zr->timing = zr->card.tvn[1]; + else + zr->timing = zr->card.tvn[0]; /* We switch overlay off and on since a change in the * norm needs different VFE settings */ @@ -1852,8 +1480,8 @@ zoran_set_norm (struct zoran *zr, if (on) zr36057_overlay(zr, 0); - decoder_command(zr, DECODER_SET_NORM, &norm); - encoder_command(zr, ENCODER_SET_NORM, &norm_encoder); + decoder_call(zr, tuner, s_std, norm); + encoder_call(zr, video, s_std_output, norm); if (on) zr36057_overlay(zr, 1); @@ -1868,7 +1496,7 @@ static int zoran_set_input (struct zoran *zr, int input) { - int realinput; + struct v4l2_routing route = { 0, 0 }; if (input == zr->input) { return 0; @@ -1878,23 +1506,23 @@ zoran_set_input (struct zoran *zr, zr->jpg_buffers.active != ZORAN_FREE) { dprintk(1, KERN_WARNING - "%s: set_input() called while in playback/capture mode\n", - ZR_DEVNAME(zr)); + "%s: %s called while in playback/capture mode\n", + ZR_DEVNAME(zr), __func__); return -EBUSY; } if (input < 0 || input >= zr->card.inputs) { dprintk(1, KERN_ERR - "%s: set_input() - unnsupported input %d\n", - ZR_DEVNAME(zr), input); + "%s: %s - unnsupported input %d\n", + ZR_DEVNAME(zr), __func__, input); return -EINVAL; } - realinput = zr->card.input[input].muxsel; + route.input = zr->card.input[input].muxsel; zr->input = input; - decoder_command(zr, DECODER_SET_INPUT, &realinput); + decoder_call(zr, video, s_routing, &route); return 0; } @@ -1903,410 +1531,14 @@ zoran_set_input (struct zoran *zr, * ioctl routine */ -static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static long zoran_default(struct file *file, void *__fh, int cmd, void *arg) { - struct zoran_fh *fh = file->private_data; + struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - /* CAREFUL: used in multiple places here */ struct zoran_jpg_settings settings; - /* we might have older buffers lying around... We don't want - * to wait, but we do want to try cleaning them up ASAP. So - * we try to obtain the lock and free them. If that fails, we - * don't do anything and wait for the next turn. In the end, - * zoran_close() or a new allocation will still free them... - * This is just a 'the sooner the better' extra 'feature' - * - * We don't free the buffers right on munmap() because that - * causes oopses (kfree() inside munmap() oopses for no - * apparent reason - it's also not reproduceable in any way, - * but moving the free code outside the munmap() handler fixes - * all this... If someone knows why, please explain me (Ronald) - */ - if (mutex_trylock(&zr->resource_lock)) { - /* we obtained it! Let's try to free some things */ - if (fh->jpg_buffers.ready_to_be_freed) - jpg_fbuffer_free(file); - if (fh->v4l_buffers.ready_to_be_freed) - v4l_fbuffer_free(file); - - mutex_unlock(&zr->resource_lock); - } - switch (cmd) { - - case VIDIOCGCAP: - { - struct video_capability *vcap = arg; - - dprintk(3, KERN_DEBUG "%s: VIDIOCGCAP\n", ZR_DEVNAME(zr)); - - memset(vcap, 0, sizeof(struct video_capability)); - strncpy(vcap->name, ZR_DEVNAME(zr), sizeof(vcap->name)-1); - vcap->type = ZORAN_VID_TYPE; - - vcap->channels = zr->card.inputs; - vcap->audios = 0; - mutex_lock(&zr->resource_lock); - vcap->maxwidth = BUZ_MAX_WIDTH; - vcap->maxheight = BUZ_MAX_HEIGHT; - vcap->minwidth = BUZ_MIN_WIDTH; - vcap->minheight = BUZ_MIN_HEIGHT; - mutex_unlock(&zr->resource_lock); - - return 0; - } - break; - - case VIDIOCGCHAN: - { - struct video_channel *vchan = arg; - int channel = vchan->channel; - - dprintk(3, KERN_DEBUG "%s: VIDIOCGCHAN - channel=%d\n", - ZR_DEVNAME(zr), vchan->channel); - - memset(vchan, 0, sizeof(struct video_channel)); - if (channel > zr->card.inputs || channel < 0) { - dprintk(1, - KERN_ERR - "%s: VIDIOCGCHAN on not existing channel %d\n", - ZR_DEVNAME(zr), channel); - return -EINVAL; - } - - strcpy(vchan->name, zr->card.input[channel].name); - - vchan->tuners = 0; - vchan->flags = 0; - vchan->type = VIDEO_TYPE_CAMERA; - mutex_lock(&zr->resource_lock); - vchan->norm = zr->norm; - mutex_unlock(&zr->resource_lock); - vchan->channel = channel; - - return 0; - } - break; - - /* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says: - * - * * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input." - * * ^^^^^^^ - * * The famos BTTV driver has it implemented with a struct video_channel argument - * * and we follow it for compatibility reasons - * * - * * BTW: this is the only way the user can set the norm! - */ - - case VIDIOCSCHAN: - { - struct video_channel *vchan = arg; - int res; - - dprintk(3, - KERN_DEBUG - "%s: VIDIOCSCHAN - channel=%d, norm=%d\n", - ZR_DEVNAME(zr), vchan->channel, vchan->norm); - - mutex_lock(&zr->resource_lock); - if ((res = zoran_set_input(zr, vchan->channel))) - goto schan_unlock_and_return; - if ((res = zoran_set_norm(zr, vchan->norm))) - goto schan_unlock_and_return; - - /* Make sure the changes come into effect */ - res = wait_grab_pending(zr); - schan_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; - } - break; - - case VIDIOCGPICT: - { - struct video_picture *vpict = arg; - - dprintk(3, KERN_DEBUG "%s: VIDIOCGPICT\n", ZR_DEVNAME(zr)); - - memset(vpict, 0, sizeof(struct video_picture)); - mutex_lock(&zr->resource_lock); - vpict->hue = zr->hue; - vpict->brightness = zr->brightness; - vpict->contrast = zr->contrast; - vpict->colour = zr->saturation; - if (fh->overlay_settings.format) { - vpict->depth = fh->overlay_settings.format->depth; - vpict->palette = fh->overlay_settings.format->palette; - } else { - vpict->depth = 0; - } - mutex_unlock(&zr->resource_lock); - - return 0; - } - break; - - case VIDIOCSPICT: - { - struct video_picture *vpict = arg; - int i; - - dprintk(3, - KERN_DEBUG - "%s: VIDIOCSPICT - bri=%d, hue=%d, col=%d, con=%d, dep=%d, pal=%d\n", - ZR_DEVNAME(zr), vpict->brightness, vpict->hue, - vpict->colour, vpict->contrast, vpict->depth, - vpict->palette); - - for (i = 0; i < NUM_FORMATS; i++) { - const struct zoran_format *fmt = &zoran_formats[i]; - - if (fmt->palette != -1 && - fmt->flags & ZORAN_FORMAT_OVERLAY && - fmt->palette == vpict->palette && - fmt->depth == vpict->depth) - break; - } - if (i == NUM_FORMATS) { - dprintk(1, - KERN_ERR - "%s: VIDIOCSPICT - Invalid palette %d\n", - ZR_DEVNAME(zr), vpict->palette); - return -EINVAL; - } - - mutex_lock(&zr->resource_lock); - - decoder_command(zr, DECODER_SET_PICTURE, vpict); - - zr->hue = vpict->hue; - zr->contrast = vpict->contrast; - zr->saturation = vpict->colour; - zr->brightness = vpict->brightness; - - fh->overlay_settings.format = &zoran_formats[i]; - - mutex_unlock(&zr->resource_lock); - - return 0; - } - break; - - case VIDIOCCAPTURE: - { - int *on = arg, res; - - dprintk(3, KERN_DEBUG "%s: VIDIOCCAPTURE - on=%d\n", - ZR_DEVNAME(zr), *on); - - mutex_lock(&zr->resource_lock); - res = setup_overlay(file, *on); - mutex_unlock(&zr->resource_lock); - - return res; - } - break; - - case VIDIOCGWIN: - { - struct video_window *vwin = arg; - - dprintk(3, KERN_DEBUG "%s: VIDIOCGWIN\n", ZR_DEVNAME(zr)); - - memset(vwin, 0, sizeof(struct video_window)); - mutex_lock(&zr->resource_lock); - vwin->x = fh->overlay_settings.x; - vwin->y = fh->overlay_settings.y; - vwin->width = fh->overlay_settings.width; - vwin->height = fh->overlay_settings.height; - mutex_unlock(&zr->resource_lock); - vwin->clipcount = 0; - return 0; - } - break; - - case VIDIOCSWIN: - { - struct video_window *vwin = arg; - int res; - - dprintk(3, - KERN_DEBUG - "%s: VIDIOCSWIN - x=%d, y=%d, w=%d, h=%d, clipcount=%d\n", - ZR_DEVNAME(zr), vwin->x, vwin->y, vwin->width, - vwin->height, vwin->clipcount); - - mutex_lock(&zr->resource_lock); - res = - setup_window(file, vwin->x, vwin->y, vwin->width, - vwin->height, vwin->clips, - vwin->clipcount, NULL); - mutex_unlock(&zr->resource_lock); - - return res; - } - break; - - case VIDIOCGFBUF: - { - struct video_buffer *vbuf = arg; - - dprintk(3, KERN_DEBUG "%s: VIDIOCGFBUF\n", ZR_DEVNAME(zr)); - - mutex_lock(&zr->resource_lock); - *vbuf = zr->buffer; - mutex_unlock(&zr->resource_lock); - return 0; - } - break; - - case VIDIOCSFBUF: - { - struct video_buffer *vbuf = arg; - int i, res = 0; - - dprintk(3, - KERN_DEBUG - "%s: VIDIOCSFBUF - base=%p, w=%d, h=%d, depth=%d, bpl=%d\n", - ZR_DEVNAME(zr), vbuf->base, vbuf->width, - vbuf->height, vbuf->depth, vbuf->bytesperline); - - for (i = 0; i < NUM_FORMATS; i++) - if (zoran_formats[i].depth == vbuf->depth) - break; - if (i == NUM_FORMATS) { - dprintk(1, - KERN_ERR - "%s: VIDIOCSFBUF - invalid fbuf depth %d\n", - ZR_DEVNAME(zr), vbuf->depth); - return -EINVAL; - } - - mutex_lock(&zr->resource_lock); - res = - setup_fbuffer(file, vbuf->base, &zoran_formats[i], - vbuf->width, vbuf->height, - vbuf->bytesperline); - mutex_unlock(&zr->resource_lock); - - return res; - } - break; - - case VIDIOCSYNC: - { - int *frame = arg, res; - - dprintk(3, KERN_DEBUG "%s: VIDIOCSYNC - frame=%d\n", - ZR_DEVNAME(zr), *frame); - - mutex_lock(&zr->resource_lock); - res = v4l_sync(file, *frame); - mutex_unlock(&zr->resource_lock); - if (!res) - zr->v4l_sync_tail++; - return res; - } - break; - - case VIDIOCMCAPTURE: - { - struct video_mmap *vmap = arg; - int res; - - dprintk(3, - KERN_DEBUG - "%s: VIDIOCMCAPTURE - frame=%d, geom=%dx%d, fmt=%d\n", - ZR_DEVNAME(zr), vmap->frame, vmap->width, vmap->height, - vmap->format); - - mutex_lock(&zr->resource_lock); - res = v4l_grab(file, vmap); - mutex_unlock(&zr->resource_lock); - return res; - } - break; - - case VIDIOCGMBUF: - { - struct video_mbuf *vmbuf = arg; - int i, res = 0; - - dprintk(3, KERN_DEBUG "%s: VIDIOCGMBUF\n", ZR_DEVNAME(zr)); - - vmbuf->size = - fh->v4l_buffers.num_buffers * - fh->v4l_buffers.buffer_size; - vmbuf->frames = fh->v4l_buffers.num_buffers; - for (i = 0; i < vmbuf->frames; i++) { - vmbuf->offsets[i] = - i * fh->v4l_buffers.buffer_size; - } - - mutex_lock(&zr->resource_lock); - - if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) { - dprintk(1, - KERN_ERR - "%s: VIDIOCGMBUF - buffers already allocated\n", - ZR_DEVNAME(zr)); - res = -EINVAL; - goto v4l1reqbuf_unlock_and_return; - } - - if (v4l_fbuffer_alloc(file)) { - res = -ENOMEM; - goto v4l1reqbuf_unlock_and_return; - } - - /* The next mmap will map the V4L buffers */ - fh->map_mode = ZORAN_MAP_MODE_RAW; - v4l1reqbuf_unlock_and_return: - mutex_unlock(&zr->resource_lock); - - return res; - } - break; - - case VIDIOCGUNIT: - { - struct video_unit *vunit = arg; - - dprintk(3, KERN_DEBUG "%s: VIDIOCGUNIT\n", ZR_DEVNAME(zr)); - - vunit->video = zr->video_dev->minor; - vunit->vbi = VIDEO_NO_UNIT; - vunit->radio = VIDEO_NO_UNIT; - vunit->audio = VIDEO_NO_UNIT; - vunit->teletext = VIDEO_NO_UNIT; - - return 0; - } - break; - - /* - * RJ: In principal we could support subcaptures for V4L grabbing. - * Not even the famous BTTV driver has them, however. - * If there should be a strong demand, one could consider - * to implement them. - */ - case VIDIOCGCAPTURE: - { - dprintk(3, KERN_ERR "%s: VIDIOCGCAPTURE not supported\n", - ZR_DEVNAME(zr)); - return -EINVAL; - } - break; - - case VIDIOCSCAPTURE: - { - dprintk(3, KERN_ERR "%s: VIDIOCSCAPTURE not supported\n", - ZR_DEVNAME(zr)); - return -EINVAL; - } - break; - case BUZIOC_G_PARAMS: { struct zoran_params *bparams = arg; @@ -2319,7 +1551,13 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) mutex_lock(&zr->resource_lock); - bparams->norm = zr->norm; + if (zr->norm & V4L2_STD_NTSC) + bparams->norm = VIDEO_MODE_NTSC; + else if (zr->norm & V4L2_STD_PAL) + bparams->norm = VIDEO_MODE_PAL; + else + bparams->norm = VIDEO_MODE_SECAM; + bparams->input = zr->input; bparams->decimation = fh->jpg_settings.decimation; @@ -2352,7 +1590,6 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) return 0; } - break; case BUZIOC_S_PARAMS: { @@ -2395,18 +1632,17 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) /* Check the params first before overwriting our * nternal values */ - if (zoran_check_jpg_settings(zr, &settings)) { + if (zoran_check_jpg_settings(zr, &settings, 0)) { res = -EINVAL; goto sparams_unlock_and_return; } fh->jpg_settings = settings; - sparams_unlock_and_return: +sparams_unlock_and_return: mutex_unlock(&zr->resource_lock); return res; } - break; case BUZIOC_REQBUFS: { @@ -2430,38 +1666,34 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) * tables to a Maximum of 2 MB */ if (breq->size > jpg_bufsize) breq->size = jpg_bufsize; - if (fh->jpg_buffers.need_contiguous && - breq->size > MAX_KMALLOC_MEM) - breq->size = MAX_KMALLOC_MEM; mutex_lock(&zr->resource_lock); - if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) { + if (fh->buffers.allocated) { dprintk(1, KERN_ERR - "%s: BUZIOC_REQBUFS - buffers allready allocated\n", + "%s: BUZIOC_REQBUFS - buffers already allocated\n", ZR_DEVNAME(zr)); res = -EBUSY; goto jpgreqbuf_unlock_and_return; } - fh->jpg_buffers.num_buffers = breq->count; - fh->jpg_buffers.buffer_size = breq->size; + /* The next mmap will map the MJPEG buffers - could + * also be *_PLAY, but it doesn't matter here */ + map_mode_jpg(fh, 0); + fh->buffers.num_buffers = breq->count; + fh->buffers.buffer_size = breq->size; - if (jpg_fbuffer_alloc(file)) { + if (jpg_fbuffer_alloc(fh)) { res = -ENOMEM; goto jpgreqbuf_unlock_and_return; } - /* The next mmap will map the MJPEG buffers - could - * also be *_PLAY, but it doesn't matter here */ - fh->map_mode = ZORAN_MAP_MODE_JPG_REC; - jpgreqbuf_unlock_and_return: +jpgreqbuf_unlock_and_return: mutex_unlock(&zr->resource_lock); return res; } - break; case BUZIOC_QBUF_CAPT: { @@ -2471,12 +1703,11 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) ZR_DEVNAME(zr), *frame); mutex_lock(&zr->resource_lock); - res = jpg_qbuf(file, *frame, BUZ_MODE_MOTION_COMPRESS); + res = jpg_qbuf(fh, *frame, BUZ_MODE_MOTION_COMPRESS); mutex_unlock(&zr->resource_lock); return res; } - break; case BUZIOC_QBUF_PLAY: { @@ -2486,12 +1717,11 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) ZR_DEVNAME(zr), *frame); mutex_lock(&zr->resource_lock); - res = jpg_qbuf(file, *frame, BUZ_MODE_MOTION_DECOMPRESS); + res = jpg_qbuf(fh, *frame, BUZ_MODE_MOTION_DECOMPRESS); mutex_unlock(&zr->resource_lock); return res; } - break; case BUZIOC_SYNC: { @@ -2501,17 +1731,26 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) dprintk(3, KERN_DEBUG "%s: BUZIOC_SYNC\n", ZR_DEVNAME(zr)); mutex_lock(&zr->resource_lock); - res = jpg_sync(file, bsync); + + if (fh->map_mode == ZORAN_MAP_MODE_RAW) { + dprintk(2, KERN_WARNING + "%s: %s - not in jpg capture mode\n", + ZR_DEVNAME(zr), __func__); + res = -EINVAL; + } else { + res = jpg_sync(fh, bsync); + } mutex_unlock(&zr->resource_lock); return res; } - break; case BUZIOC_G_STATUS: { struct zoran_status *bstat = arg; - int norm, input, status, res = 0; + struct v4l2_routing route = { 0, 0 }; + int status = 0, res = 0; + v4l2_std_id norm; dprintk(3, KERN_DEBUG "%s: BUZIOC_G_STATUS\n", ZR_DEVNAME(zr)); @@ -2523,8 +1762,7 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) return -EINVAL; } - input = zr->card.input[bstat->input].muxsel; - norm = VIDEO_MODE_AUTO; + route.input = zr->card.input[bstat->input].muxsel; mutex_lock(&zr->resource_lock); @@ -2537,1629 +1775,1262 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg) goto gstat_unlock_and_return; } - decoder_command(zr, DECODER_SET_INPUT, &input); - decoder_command(zr, DECODER_SET_NORM, &norm); + decoder_call(zr, video, s_routing, &route); /* sleep 1 second */ ssleep(1); /* Get status of video decoder */ - decoder_command(zr, DECODER_GET_STATUS, &status); + decoder_call(zr, video, querystd, &norm); + decoder_call(zr, video, g_input_status, &status); /* restore previous input and norm */ - input = zr->card.input[zr->input].muxsel; - decoder_command(zr, DECODER_SET_INPUT, &input); - decoder_command(zr, DECODER_SET_NORM, &zr->norm); - gstat_unlock_and_return: + route.input = zr->card.input[zr->input].muxsel; + decoder_call(zr, video, s_routing, &route); +gstat_unlock_and_return: mutex_unlock(&zr->resource_lock); if (!res) { bstat->signal = - (status & DECODER_STATUS_GOOD) ? 1 : 0; - if (status & DECODER_STATUS_NTSC) + (status & V4L2_IN_ST_NO_SIGNAL) ? 0 : 1; + if (norm & V4L2_STD_NTSC) bstat->norm = VIDEO_MODE_NTSC; - else if (status & DECODER_STATUS_SECAM) + else if (norm & V4L2_STD_SECAM) bstat->norm = VIDEO_MODE_SECAM; else bstat->norm = VIDEO_MODE_PAL; bstat->color = - (status & DECODER_STATUS_COLOR) ? 1 : 0; + (status & V4L2_IN_ST_NO_COLOR) ? 0 : 1; } return res; } - break; - - /* The new video4linux2 capture interface - much nicer than video4linux1, since - * it allows for integrating the JPEG capturing calls inside standard v4l2 - */ - - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - dprintk(3, KERN_DEBUG "%s: VIDIOC_QUERYCAP\n", ZR_DEVNAME(zr)); - - memset(cap, 0, sizeof(*cap)); - strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1); - strncpy(cap->driver, "zoran", sizeof(cap->driver)-1); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", - pci_name(zr->pci_dev)); - cap->version = - KERNEL_VERSION(MAJOR_VERSION, MINOR_VERSION, - RELEASE_VERSION); - cap->capabilities = ZORAN_V4L2_VID_FLAGS; - - return 0; + default: + return -EINVAL; } - break; +} - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *fmt = arg; - int index = fmt->index, num = -1, i, flag = 0, type = - fmt->type; +static int zoran_vidiocgmbuf(struct file *file, void *__fh, struct video_mbuf *vmbuf) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int i, res = 0; - dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUM_FMT - index=%d\n", - ZR_DEVNAME(zr), fmt->index); - switch (fmt->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - flag = ZORAN_FORMAT_CAPTURE; - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - flag = ZORAN_FORMAT_PLAYBACK; - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - flag = ZORAN_FORMAT_OVERLAY; - break; - default: - dprintk(1, - KERN_ERR - "%s: VIDIOC_ENUM_FMT - unknown type %d\n", - ZR_DEVNAME(zr), fmt->type); - return -EINVAL; - } + mutex_lock(&zr->resource_lock); - for (i = 0; i < NUM_FORMATS; i++) { - if (zoran_formats[i].flags & flag) - num++; - if (num == fmt->index) - break; - } - if (fmt->index < 0 /* late, but not too late */ || - i == NUM_FORMATS) - return -EINVAL; + if (fh->buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: VIDIOCGMBUF - buffers already allocated\n", + ZR_DEVNAME(zr)); + res = -EINVAL; + goto v4l1reqbuf_unlock_and_return; + } - memset(fmt, 0, sizeof(*fmt)); - fmt->index = index; - fmt->type = type; - strncpy(fmt->description, zoran_formats[i].name, sizeof(fmt->description)-1); - fmt->pixelformat = zoran_formats[i].fourcc; - if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED) - fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; + /* The next mmap will map the V4L buffers */ + map_mode_raw(fh); - return 0; + if (v4l_fbuffer_alloc(fh)) { + res = -ENOMEM; + goto v4l1reqbuf_unlock_and_return; } - break; - - case VIDIOC_G_FMT: - { - struct v4l2_format *fmt = arg; - int type = fmt->type; - dprintk(5, KERN_DEBUG "%s: VIDIOC_G_FMT\n", ZR_DEVNAME(zr)); + vmbuf->size = fh->buffers.num_buffers * fh->buffers.buffer_size; + vmbuf->frames = fh->buffers.num_buffers; + for (i = 0; i < vmbuf->frames; i++) + vmbuf->offsets[i] = i * fh->buffers.buffer_size; - memset(fmt, 0, sizeof(*fmt)); - fmt->type = type; +v4l1reqbuf_unlock_and_return: + mutex_unlock(&zr->resource_lock); - switch (fmt->type) { - case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return res; +} +#endif - mutex_lock(&zr->resource_lock); +static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - fmt->fmt.win.w.left = fh->overlay_settings.x; - fmt->fmt.win.w.top = fh->overlay_settings.y; - fmt->fmt.win.w.width = fh->overlay_settings.width; - fmt->fmt.win.w.height = - fh->overlay_settings.height; - if (fh->overlay_settings.width * 2 > - BUZ_MAX_HEIGHT) - fmt->fmt.win.field = V4L2_FIELD_INTERLACED; - else - fmt->fmt.win.field = V4L2_FIELD_TOP; + memset(cap, 0, sizeof(*cap)); + strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1); + strncpy(cap->driver, "zoran", sizeof(cap->driver)-1); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", + pci_name(zr->pci_dev)); + cap->version = KERNEL_VERSION(MAJOR_VERSION, MINOR_VERSION, + RELEASE_VERSION); + cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY; + return 0; +} - mutex_unlock(&zr->resource_lock); +static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag) +{ + int num = -1, i; + for (i = 0; i < NUM_FORMATS; i++) { + if (zoran_formats[i].flags & flag) + num++; + if (num == fmt->index) break; + } + if (fmt->index < 0 /* late, but not too late */ || i == NUM_FORMATS) + return -EINVAL; - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - - mutex_lock(&zr->resource_lock); - - if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && - fh->map_mode == ZORAN_MAP_MODE_RAW) { - - fmt->fmt.pix.width = - fh->v4l_settings.width; - fmt->fmt.pix.height = - fh->v4l_settings.height; - fmt->fmt.pix.sizeimage = - fh->v4l_settings.bytesperline * - fh->v4l_settings.height; - fmt->fmt.pix.pixelformat = - fh->v4l_settings.format->fourcc; - fmt->fmt.pix.colorspace = - fh->v4l_settings.format->colorspace; - fmt->fmt.pix.bytesperline = - fh->v4l_settings.bytesperline; - if (BUZ_MAX_HEIGHT < - (fh->v4l_settings.height * 2)) - fmt->fmt.pix.field = - V4L2_FIELD_INTERLACED; - else - fmt->fmt.pix.field = - V4L2_FIELD_TOP; - - } else { - - fmt->fmt.pix.width = - fh->jpg_settings.img_width / - fh->jpg_settings.HorDcm; - fmt->fmt.pix.height = - fh->jpg_settings.img_height / - (fh->jpg_settings.VerDcm * - fh->jpg_settings.TmpDcm); - fmt->fmt.pix.sizeimage = - zoran_v4l2_calc_bufsize(&fh-> - jpg_settings); - fmt->fmt.pix.pixelformat = - V4L2_PIX_FMT_MJPEG; - if (fh->jpg_settings.TmpDcm == 1) - fmt->fmt.pix.field = - (fh->jpg_settings. - odd_even ? V4L2_FIELD_SEQ_BT : - V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = - (fh->jpg_settings. - odd_even ? V4L2_FIELD_TOP : - V4L2_FIELD_BOTTOM); - - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.colorspace = - V4L2_COLORSPACE_SMPTE170M; - } - - mutex_unlock(&zr->resource_lock); + strncpy(fmt->description, zoran_formats[i].name, sizeof(fmt->description)-1); + fmt->pixelformat = zoran_formats[i].fourcc; + if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED) + fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; + return 0; +} - break; +static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh, + struct v4l2_fmtdesc *f) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - default: - dprintk(1, - KERN_ERR - "%s: VIDIOC_G_FMT - unsupported type %d\n", - ZR_DEVNAME(zr), fmt->type); - return -EINVAL; - } - return 0; - } - break; + return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE); +} - case VIDIOC_S_FMT: - { - struct v4l2_format *fmt = arg; - int i, res = 0; - __le32 printformat; - - dprintk(3, KERN_DEBUG "%s: VIDIOC_S_FMT - type=%d, ", - ZR_DEVNAME(zr), fmt->type); - - switch (fmt->type) { - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - - dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n", - fmt->fmt.win.w.left, fmt->fmt.win.w.top, - fmt->fmt.win.w.width, - fmt->fmt.win.w.height, - fmt->fmt.win.clipcount, - fmt->fmt.win.bitmap); - mutex_lock(&zr->resource_lock); - res = - setup_window(file, fmt->fmt.win.w.left, - fmt->fmt.win.w.top, - fmt->fmt.win.w.width, - fmt->fmt.win.w.height, - (struct video_clip __user *) - fmt->fmt.win.clips, - fmt->fmt.win.clipcount, - fmt->fmt.win.bitmap); - mutex_unlock(&zr->resource_lock); - return res; - break; +static int zoran_enum_fmt_vid_out(struct file *file, void *__fh, + struct v4l2_fmtdesc *f) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - - printformat = - __cpu_to_le32(fmt->fmt.pix.pixelformat); - dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n", - fmt->fmt.pix.width, fmt->fmt.pix.height, - fmt->fmt.pix.pixelformat, - (char *) &printformat); - - /* we can be requested to do JPEG/raw playback/capture */ - if (! - (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE || - (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && - fmt->fmt.pix.pixelformat == - V4L2_PIX_FMT_MJPEG))) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_S_FMT - unknown type %d/0x%x(%4.4s) combination\n", - ZR_DEVNAME(zr), fmt->type, - fmt->fmt.pix.pixelformat, - (char *) &printformat); - return -EINVAL; - } + return zoran_enum_fmt(zr, f, ZORAN_FORMAT_PLAYBACK); +} - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) { - mutex_lock(&zr->resource_lock); +static int zoran_enum_fmt_vid_overlay(struct file *file, void *__fh, + struct v4l2_fmtdesc *f) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - settings = fh->jpg_settings; + return zoran_enum_fmt(zr, f, ZORAN_FORMAT_OVERLAY); +} - if (fh->v4l_buffers.allocated || - fh->jpg_buffers.allocated) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_S_FMT - cannot change capture mode\n", - ZR_DEVNAME(zr)); - res = -EBUSY; - goto sfmtjpg_unlock_and_return; - } +static int zoran_g_fmt_vid_out(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - /* we actually need to set 'real' parameters now */ - if ((fmt->fmt.pix.height * 2) > - BUZ_MAX_HEIGHT) - settings.TmpDcm = 1; - else - settings.TmpDcm = 2; - settings.decimation = 0; - if (fmt->fmt.pix.height <= - fh->jpg_settings.img_height / 2) - settings.VerDcm = 2; - else - settings.VerDcm = 1; - if (fmt->fmt.pix.width <= - fh->jpg_settings.img_width / 4) - settings.HorDcm = 4; - else if (fmt->fmt.pix.width <= - fh->jpg_settings.img_width / 2) - settings.HorDcm = 2; - else - settings.HorDcm = 1; - if (settings.TmpDcm == 1) - settings.field_per_buff = 2; - else - settings.field_per_buff = 1; - - /* check */ - if ((res = - zoran_check_jpg_settings(zr, - &settings))) - goto sfmtjpg_unlock_and_return; - - /* it's ok, so set them */ - fh->jpg_settings = settings; - - /* tell the user what we actually did */ - fmt->fmt.pix.width = - settings.img_width / settings.HorDcm; - fmt->fmt.pix.height = - settings.img_height * 2 / - (settings.TmpDcm * settings.VerDcm); - if (settings.TmpDcm == 1) - fmt->fmt.pix.field = - (fh->jpg_settings. - odd_even ? V4L2_FIELD_SEQ_TB : - V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = - (fh->jpg_settings. - odd_even ? V4L2_FIELD_TOP : - V4L2_FIELD_BOTTOM); - fh->jpg_buffers.buffer_size = - zoran_v4l2_calc_bufsize(&fh-> - jpg_settings); - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.sizeimage = - fh->jpg_buffers.buffer_size; - fmt->fmt.pix.colorspace = - V4L2_COLORSPACE_SMPTE170M; - - /* we hereby abuse this variable to show that - * we're gonna do mjpeg capture */ - fh->map_mode = - (fmt->type == - V4L2_BUF_TYPE_VIDEO_CAPTURE) ? - ZORAN_MAP_MODE_JPG_REC : - ZORAN_MAP_MODE_JPG_PLAY; - sfmtjpg_unlock_and_return: - mutex_unlock(&zr->resource_lock); - } else { - for (i = 0; i < NUM_FORMATS; i++) - if (fmt->fmt.pix.pixelformat == - zoran_formats[i].fourcc) - break; - if (i == NUM_FORMATS) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x (%4.4s)\n", - ZR_DEVNAME(zr), - fmt->fmt.pix.pixelformat, - (char *) &printformat); - return -EINVAL; - } - mutex_lock(&zr->resource_lock); - if (fh->jpg_buffers.allocated || - (fh->v4l_buffers.allocated && - fh->v4l_buffers.active != - ZORAN_FREE)) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_S_FMT - cannot change capture mode\n", - ZR_DEVNAME(zr)); - res = -EBUSY; - goto sfmtv4l_unlock_and_return; - } - if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) - fmt->fmt.pix.height = - BUZ_MAX_HEIGHT; - if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) - fmt->fmt.pix.width = BUZ_MAX_WIDTH; - - if ((res = - zoran_v4l_set_format(file, - fmt->fmt.pix. - width, - fmt->fmt.pix. - height, - &zoran_formats - [i]))) - goto sfmtv4l_unlock_and_return; - - /* tell the user the - * results/missing stuff */ - fmt->fmt.pix.bytesperline = - fh->v4l_settings.bytesperline; - fmt->fmt.pix.sizeimage = - fh->v4l_settings.height * - fh->v4l_settings.bytesperline; - fmt->fmt.pix.colorspace = - fh->v4l_settings.format->colorspace; - if (BUZ_MAX_HEIGHT < - (fh->v4l_settings.height * 2)) - fmt->fmt.pix.field = - V4L2_FIELD_INTERLACED; - else - fmt->fmt.pix.field = - V4L2_FIELD_TOP; - - fh->map_mode = ZORAN_MAP_MODE_RAW; - sfmtv4l_unlock_and_return: - mutex_unlock(&zr->resource_lock); - } + mutex_lock(&zr->resource_lock); - break; + fmt->fmt.pix.width = fh->jpg_settings.img_width / fh->jpg_settings.HorDcm; + fmt->fmt.pix.height = fh->jpg_settings.img_height * 2 / + (fh->jpg_settings.VerDcm * fh->jpg_settings.TmpDcm); + fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&fh->jpg_settings); + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; + if (fh->jpg_settings.TmpDcm == 1) + fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? + V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); + else + fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - default: - dprintk(1, - KERN_ERR - "%s: VIDIOC_S_FMT - unsupported type %d\n", - ZR_DEVNAME(zr), fmt->type); - return -EINVAL; - } + mutex_unlock(&zr->resource_lock); + return 0; +} - return res; - } - break; +static int zoran_g_fmt_vid_cap(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - case VIDIOC_G_FBUF: - { - struct v4l2_framebuffer *fb = arg; + if (fh->map_mode != ZORAN_MAP_MODE_RAW) + return zoran_g_fmt_vid_out(file, fh, fmt); - dprintk(3, KERN_DEBUG "%s: VIDIOC_G_FBUF\n", ZR_DEVNAME(zr)); + mutex_lock(&zr->resource_lock); + fmt->fmt.pix.width = fh->v4l_settings.width; + fmt->fmt.pix.height = fh->v4l_settings.height; + fmt->fmt.pix.sizeimage = fh->v4l_settings.bytesperline * + fh->v4l_settings.height; + fmt->fmt.pix.pixelformat = fh->v4l_settings.format->fourcc; + fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace; + fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline; + if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2)) + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + else + fmt->fmt.pix.field = V4L2_FIELD_TOP; + mutex_unlock(&zr->resource_lock); + return 0; +} - memset(fb, 0, sizeof(*fb)); - mutex_lock(&zr->resource_lock); - fb->base = zr->buffer.base; - fb->fmt.width = zr->buffer.width; - fb->fmt.height = zr->buffer.height; - if (zr->overlay_settings.format) { - fb->fmt.pixelformat = - fh->overlay_settings.format->fourcc; - } - fb->fmt.bytesperline = zr->buffer.bytesperline; - mutex_unlock(&zr->resource_lock); - fb->fmt.colorspace = V4L2_COLORSPACE_SRGB; - fb->fmt.field = V4L2_FIELD_INTERLACED; - fb->flags = V4L2_FBUF_FLAG_OVERLAY; - fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; +static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - return 0; - } - break; + mutex_lock(&zr->resource_lock); - case VIDIOC_S_FBUF: - { - int i, res = 0; - struct v4l2_framebuffer *fb = arg; - __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat); + fmt->fmt.win.w.left = fh->overlay_settings.x; + fmt->fmt.win.w.top = fh->overlay_settings.y; + fmt->fmt.win.w.width = fh->overlay_settings.width; + fmt->fmt.win.w.height = fh->overlay_settings.height; + if (fh->overlay_settings.width * 2 > BUZ_MAX_HEIGHT) + fmt->fmt.win.field = V4L2_FIELD_INTERLACED; + else + fmt->fmt.win.field = V4L2_FIELD_TOP; - dprintk(3, - KERN_DEBUG - "%s: VIDIOC_S_FBUF - base=0x%p, size=%dx%d, bpl=%d, fmt=0x%x (%4.4s)\n", - ZR_DEVNAME(zr), fb->base, fb->fmt.width, fb->fmt.height, - fb->fmt.bytesperline, fb->fmt.pixelformat, - (char *) &printformat); + mutex_unlock(&zr->resource_lock); + return 0; +} - for (i = 0; i < NUM_FORMATS; i++) - if (zoran_formats[i].fourcc == fb->fmt.pixelformat) - break; - if (i == NUM_FORMATS) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n", - ZR_DEVNAME(zr), fb->fmt.pixelformat, - (char *) &printformat); - return -EINVAL; - } +static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - mutex_lock(&zr->resource_lock); - res = - setup_fbuffer(file, fb->base, &zoran_formats[i], - fb->fmt.width, fb->fmt.height, - fb->fmt.bytesperline); - mutex_unlock(&zr->resource_lock); + mutex_lock(&zr->resource_lock); - return res; - } - break; + if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH) + fmt->fmt.win.w.width = BUZ_MAX_WIDTH; + if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH) + fmt->fmt.win.w.width = BUZ_MIN_WIDTH; + if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT) + fmt->fmt.win.w.height = BUZ_MAX_HEIGHT; + if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT) + fmt->fmt.win.w.height = BUZ_MIN_HEIGHT; - case VIDIOC_OVERLAY: - { - int *on = arg, res; + mutex_unlock(&zr->resource_lock); + return 0; +} - dprintk(3, KERN_DEBUG "%s: VIDIOC_PREVIEW - on=%d\n", - ZR_DEVNAME(zr), *on); +static int zoran_try_fmt_vid_out(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + struct zoran_jpg_settings settings; + int res = 0; - mutex_lock(&zr->resource_lock); - res = setup_overlay(file, *on); - mutex_unlock(&zr->resource_lock); + if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; - return res; - } - break; + mutex_lock(&zr->resource_lock); + settings = fh->jpg_settings; - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *req = arg; - int res = 0; + /* we actually need to set 'real' parameters now */ + if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT) + settings.TmpDcm = 1; + else + settings.TmpDcm = 2; + settings.decimation = 0; + if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2) + settings.VerDcm = 2; + else + settings.VerDcm = 1; + if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4) + settings.HorDcm = 4; + else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2) + settings.HorDcm = 2; + else + settings.HorDcm = 1; + if (settings.TmpDcm == 1) + settings.field_per_buff = 2; + else + settings.field_per_buff = 1; - dprintk(3, KERN_DEBUG "%s: VIDIOC_REQBUFS - type=%d\n", - ZR_DEVNAME(zr), req->type); + if (settings.HorDcm > 1) { + settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; + settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; + } else { + settings.img_x = 0; + settings.img_width = BUZ_MAX_WIDTH; + } + + /* check */ + res = zoran_check_jpg_settings(zr, &settings, 1); + if (res) + goto tryfmt_unlock_and_return; + + /* tell the user what we actually did */ + fmt->fmt.pix.width = settings.img_width / settings.HorDcm; + fmt->fmt.pix.height = settings.img_height * 2 / + (settings.TmpDcm * settings.VerDcm); + if (settings.TmpDcm == 1) + fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? + V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); + else + fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); - if (req->memory != V4L2_MEMORY_MMAP) { - dprintk(1, - KERN_ERR - "%s: only MEMORY_MMAP capture is supported, not %d\n", - ZR_DEVNAME(zr), req->memory); - return -EINVAL; - } + fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings); + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; +tryfmt_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; +} - mutex_lock(&zr->resource_lock); +static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int bpp; + int i; - if (fh->v4l_buffers.allocated || fh->jpg_buffers.allocated) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_REQBUFS - buffers allready allocated\n", - ZR_DEVNAME(zr)); - res = -EBUSY; - goto v4l2reqbuf_unlock_and_return; - } + if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) + return zoran_try_fmt_vid_out(file, fh, fmt); - if (fh->map_mode == ZORAN_MAP_MODE_RAW && - req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + mutex_lock(&zr->resource_lock); - /* control user input */ - if (req->count < 2) - req->count = 2; - if (req->count > v4l_nbufs) - req->count = v4l_nbufs; - fh->v4l_buffers.num_buffers = req->count; + for (i = 0; i < NUM_FORMATS; i++) + if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat) + break; - if (v4l_fbuffer_alloc(file)) { - res = -ENOMEM; - goto v4l2reqbuf_unlock_and_return; - } + if (i == NUM_FORMATS) { + mutex_unlock(&zr->resource_lock); + return -EINVAL; + } - /* The next mmap will map the V4L buffers */ - fh->map_mode = ZORAN_MAP_MODE_RAW; + bpp = (zoran_formats[i].depth + 7) / 8; + fmt->fmt.pix.width &= ~((bpp == 2) ? 1 : 3); + if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) + fmt->fmt.pix.width = BUZ_MAX_WIDTH; + if (fmt->fmt.pix.width < BUZ_MIN_WIDTH) + fmt->fmt.pix.width = BUZ_MIN_WIDTH; + if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) + fmt->fmt.pix.height = BUZ_MAX_HEIGHT; + if (fmt->fmt.pix.height < BUZ_MIN_HEIGHT) + fmt->fmt.pix.height = BUZ_MIN_HEIGHT; + mutex_unlock(&zr->resource_lock); - } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC || - fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { + return 0; +} - /* we need to calculate size ourselves now */ - if (req->count < 4) - req->count = 4; - if (req->count > jpg_nbufs) - req->count = jpg_nbufs; - fh->jpg_buffers.num_buffers = req->count; - fh->jpg_buffers.buffer_size = - zoran_v4l2_calc_bufsize(&fh->jpg_settings); +static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int res; + + dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n", + fmt->fmt.win.w.left, fmt->fmt.win.w.top, + fmt->fmt.win.w.width, + fmt->fmt.win.w.height, + fmt->fmt.win.clipcount, + fmt->fmt.win.bitmap); + mutex_lock(&zr->resource_lock); + res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top, + fmt->fmt.win.w.width, fmt->fmt.win.w.height, + (struct v4l2_clip __user *)fmt->fmt.win.clips, + fmt->fmt.win.clipcount, fmt->fmt.win.bitmap); + mutex_unlock(&zr->resource_lock); + return res; +} - if (jpg_fbuffer_alloc(file)) { - res = -ENOMEM; - goto v4l2reqbuf_unlock_and_return; - } +static int zoran_s_fmt_vid_out(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + __le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat); + struct zoran_jpg_settings settings; + int res = 0; - /* The next mmap will map the MJPEG buffers */ - if (req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - fh->map_mode = ZORAN_MAP_MODE_JPG_REC; - else - fh->map_mode = ZORAN_MAP_MODE_JPG_PLAY; + dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n", + fmt->fmt.pix.width, fmt->fmt.pix.height, + fmt->fmt.pix.pixelformat, + (char *) &printformat); + if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; - } else { - dprintk(1, - KERN_ERR - "%s: VIDIOC_REQBUFS - unknown type %d\n", - ZR_DEVNAME(zr), req->type); - res = -EINVAL; - goto v4l2reqbuf_unlock_and_return; - } - v4l2reqbuf_unlock_and_return: - mutex_unlock(&zr->resource_lock); + mutex_lock(&zr->resource_lock); - return 0; + if (fh->buffers.allocated) { + dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + goto sfmtjpg_unlock_and_return; } - break; - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *buf = arg; - __u32 type = buf->type; - int index = buf->index, res; + settings = fh->jpg_settings; - dprintk(3, - KERN_DEBUG - "%s: VIDIOC_QUERYBUF - index=%d, type=%d\n", - ZR_DEVNAME(zr), buf->index, buf->type); - - memset(buf, 0, sizeof(*buf)); - buf->type = type; - buf->index = index; - - mutex_lock(&zr->resource_lock); - res = zoran_v4l2_buffer_status(file, buf, buf->index); - mutex_unlock(&zr->resource_lock); + /* we actually need to set 'real' parameters now */ + if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT) + settings.TmpDcm = 1; + else + settings.TmpDcm = 2; + settings.decimation = 0; + if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2) + settings.VerDcm = 2; + else + settings.VerDcm = 1; + if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4) + settings.HorDcm = 4; + else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2) + settings.HorDcm = 2; + else + settings.HorDcm = 1; + if (settings.TmpDcm == 1) + settings.field_per_buff = 2; + else + settings.field_per_buff = 1; - return res; + if (settings.HorDcm > 1) { + settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0; + settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH; + } else { + settings.img_x = 0; + settings.img_width = BUZ_MAX_WIDTH; } - break; - case VIDIOC_QBUF: - { - struct v4l2_buffer *buf = arg; - int res = 0, codec_mode, buf_type; + /* check */ + res = zoran_check_jpg_settings(zr, &settings, 0); + if (res) + goto sfmtjpg_unlock_and_return; - dprintk(3, - KERN_DEBUG "%s: VIDIOC_QBUF - type=%d, index=%d\n", - ZR_DEVNAME(zr), buf->type, buf->index); + /* it's ok, so set them */ + fh->jpg_settings = settings; - mutex_lock(&zr->resource_lock); + map_mode_jpg(fh, fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT); + fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings); - switch (fh->map_mode) { - case ZORAN_MAP_MODE_RAW: - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", - ZR_DEVNAME(zr), buf->type, fh->map_mode); - res = -EINVAL; - goto qbuf_unlock_and_return; - } + /* tell the user what we actually did */ + fmt->fmt.pix.width = settings.img_width / settings.HorDcm; + fmt->fmt.pix.height = settings.img_height * 2 / + (settings.TmpDcm * settings.VerDcm); + if (settings.TmpDcm == 1) + fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? + V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT); + else + fmt->fmt.pix.field = (fh->jpg_settings.odd_even ? + V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.sizeimage = fh->buffers.buffer_size; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - res = zoran_v4l_queue_frame(file, buf->index); - if (res) - goto qbuf_unlock_and_return; - if (!zr->v4l_memgrab_active && - fh->v4l_buffers.active == ZORAN_LOCKED) - zr36057_set_memgrab(zr, 1); - break; +sfmtjpg_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; +} - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: - if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { - buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - codec_mode = BUZ_MODE_MOTION_DECOMPRESS; - } else { - buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - codec_mode = BUZ_MODE_MOTION_COMPRESS; - } +static int zoran_s_fmt_vid_cap(struct file *file, void *__fh, + struct v4l2_format *fmt) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int i; + int res = 0; - if (buf->type != buf_type) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", - ZR_DEVNAME(zr), buf->type, fh->map_mode); - res = -EINVAL; - goto qbuf_unlock_and_return; - } + if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) + return zoran_s_fmt_vid_out(file, fh, fmt); - res = - zoran_jpg_queue_frame(file, buf->index, - codec_mode); - if (res != 0) - goto qbuf_unlock_and_return; - if (zr->codec_mode == BUZ_MODE_IDLE && - fh->jpg_buffers.active == ZORAN_LOCKED) { - zr36057_enable_jpg(zr, codec_mode); - } + for (i = 0; i < NUM_FORMATS; i++) + if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc) break; - - default: - dprintk(1, - KERN_ERR - "%s: VIDIOC_QBUF - unsupported type %d\n", - ZR_DEVNAME(zr), buf->type); - res = -EINVAL; - goto qbuf_unlock_and_return; - } - qbuf_unlock_and_return: - mutex_unlock(&zr->resource_lock); - - return res; + if (i == NUM_FORMATS) { + dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x\n", + ZR_DEVNAME(zr), fmt->fmt.pix.pixelformat); + return -EINVAL; } - break; - case VIDIOC_DQBUF: - { - struct v4l2_buffer *buf = arg; - int res = 0, buf_type, num = -1; /* compiler borks here (?) */ - - dprintk(3, KERN_DEBUG "%s: VIDIOC_DQBUF - type=%d\n", - ZR_DEVNAME(zr), buf->type); - - mutex_lock(&zr->resource_lock); + mutex_lock(&zr->resource_lock); - switch (fh->map_mode) { - case ZORAN_MAP_MODE_RAW: - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", - ZR_DEVNAME(zr), buf->type, fh->map_mode); - res = -EINVAL; - goto dqbuf_unlock_and_return; - } + if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) || + fh->buffers.active != ZORAN_FREE) { + dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + goto sfmtv4l_unlock_and_return; + } + if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) + fmt->fmt.pix.height = BUZ_MAX_HEIGHT; + if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) + fmt->fmt.pix.width = BUZ_MAX_WIDTH; + + map_mode_raw(fh); + + res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height, + &zoran_formats[i]); + if (res) + goto sfmtv4l_unlock_and_return; + + /* tell the user the results/missing stuff */ + fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline; + fmt->fmt.pix.sizeimage = fh->v4l_settings.height * fh->v4l_settings.bytesperline; + fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace; + if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2)) + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + else + fmt->fmt.pix.field = V4L2_FIELD_TOP; - num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME]; - if (file->f_flags & O_NONBLOCK && - zr->v4l_buffers.buffer[num].state != - BUZ_STATE_DONE) { - res = -EAGAIN; - goto dqbuf_unlock_and_return; - } - res = v4l_sync(file, num); - if (res) - goto dqbuf_unlock_and_return; - else - zr->v4l_sync_tail++; - res = zoran_v4l2_buffer_status(file, buf, num); - break; +sfmtv4l_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; +} - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: - { - struct zoran_sync bs; +static int zoran_g_fbuf(struct file *file, void *__fh, + struct v4l2_framebuffer *fb) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) - buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - else - buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + memset(fb, 0, sizeof(*fb)); + mutex_lock(&zr->resource_lock); + fb->base = zr->vbuf_base; + fb->fmt.width = zr->vbuf_width; + fb->fmt.height = zr->vbuf_height; + if (zr->overlay_settings.format) + fb->fmt.pixelformat = fh->overlay_settings.format->fourcc; + fb->fmt.bytesperline = zr->vbuf_bytesperline; + mutex_unlock(&zr->resource_lock); + fb->fmt.colorspace = V4L2_COLORSPACE_SRGB; + fb->fmt.field = V4L2_FIELD_INTERLACED; + fb->flags = V4L2_FBUF_FLAG_OVERLAY; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; - if (buf->type != buf_type) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", - ZR_DEVNAME(zr), buf->type, fh->map_mode); - res = -EINVAL; - goto dqbuf_unlock_and_return; - } + return 0; +} - num = - zr->jpg_pend[zr-> - jpg_que_tail & BUZ_MASK_FRAME]; +static int zoran_s_fbuf(struct file *file, void *__fh, + struct v4l2_framebuffer *fb) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int i, res = 0; + __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat); - if (file->f_flags & O_NONBLOCK && - zr->jpg_buffers.buffer[num].state != - BUZ_STATE_DONE) { - res = -EAGAIN; - goto dqbuf_unlock_and_return; - } - res = jpg_sync(file, &bs); - if (res) - goto dqbuf_unlock_and_return; - res = - zoran_v4l2_buffer_status(file, buf, bs.frame); + for (i = 0; i < NUM_FORMATS; i++) + if (zoran_formats[i].fourcc == fb->fmt.pixelformat) break; - } - - default: - dprintk(1, - KERN_ERR - "%s: VIDIOC_DQBUF - unsupported type %d\n", - ZR_DEVNAME(zr), buf->type); - res = -EINVAL; - goto dqbuf_unlock_and_return; - } - dqbuf_unlock_and_return: - mutex_unlock(&zr->resource_lock); - - return res; + if (i == NUM_FORMATS) { + dprintk(1, KERN_ERR "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n", + ZR_DEVNAME(zr), fb->fmt.pixelformat, + (char *)&printformat); + return -EINVAL; } - break; - case VIDIOC_STREAMON: - { - int res = 0; + mutex_lock(&zr->resource_lock); + res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width, + fb->fmt.height, fb->fmt.bytesperline); + mutex_unlock(&zr->resource_lock); - dprintk(3, KERN_DEBUG "%s: VIDIOC_STREAMON\n", ZR_DEVNAME(zr)); + return res; +} - mutex_lock(&zr->resource_lock); +static int zoran_overlay(struct file *file, void *__fh, unsigned int on) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int res; - switch (fh->map_mode) { - case ZORAN_MAP_MODE_RAW: /* raw capture */ - if (zr->v4l_buffers.active != ZORAN_ACTIVE || - fh->v4l_buffers.active != ZORAN_ACTIVE) { - res = -EBUSY; - goto strmon_unlock_and_return; - } + mutex_lock(&zr->resource_lock); + res = setup_overlay(fh, on); + mutex_unlock(&zr->resource_lock); - zr->v4l_buffers.active = fh->v4l_buffers.active = - ZORAN_LOCKED; - zr->v4l_settings = fh->v4l_settings; + return res; +} - zr->v4l_sync_tail = zr->v4l_pend_tail; - if (!zr->v4l_memgrab_active && - zr->v4l_pend_head != zr->v4l_pend_tail) { - zr36057_set_memgrab(zr, 1); - } - break; +static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type); - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: - /* what is the codec mode right now? */ - if (zr->jpg_buffers.active != ZORAN_ACTIVE || - fh->jpg_buffers.active != ZORAN_ACTIVE) { - res = -EBUSY; - goto strmon_unlock_and_return; - } +static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *req) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int res = 0; - zr->jpg_buffers.active = fh->jpg_buffers.active = - ZORAN_LOCKED; + if (req->memory != V4L2_MEMORY_MMAP) { + dprintk(2, + KERN_ERR + "%s: only MEMORY_MMAP capture is supported, not %d\n", + ZR_DEVNAME(zr), req->memory); + return -EINVAL; + } - if (zr->jpg_que_head != zr->jpg_que_tail) { - /* Start the jpeg codec when the first frame is queued */ - jpeg_start(zr); - } + if (req->count == 0) + return zoran_streamoff(file, fh, req->type); - break; - default: - dprintk(1, + mutex_lock(&zr->resource_lock); + if (fh->buffers.allocated) { + dprintk(2, KERN_ERR - "%s: VIDIOC_STREAMON - invalid map mode %d\n", - ZR_DEVNAME(zr), fh->map_mode); - res = -EINVAL; - goto strmon_unlock_and_return; - } - strmon_unlock_and_return: - mutex_unlock(&zr->resource_lock); - - return res; + "%s: VIDIOC_REQBUFS - buffers already allocated\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + goto v4l2reqbuf_unlock_and_return; } - break; - case VIDIOC_STREAMOFF: - { - int i, res = 0; + if (fh->map_mode == ZORAN_MAP_MODE_RAW && + req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + /* control user input */ + if (req->count < 2) + req->count = 2; + if (req->count > v4l_nbufs) + req->count = v4l_nbufs; - dprintk(3, KERN_DEBUG "%s: VIDIOC_STREAMOFF\n", ZR_DEVNAME(zr)); + /* The next mmap will map the V4L buffers */ + map_mode_raw(fh); + fh->buffers.num_buffers = req->count; - mutex_lock(&zr->resource_lock); + if (v4l_fbuffer_alloc(fh)) { + res = -ENOMEM; + goto v4l2reqbuf_unlock_and_return; + } + } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC || + fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { + /* we need to calculate size ourselves now */ + if (req->count < 4) + req->count = 4; + if (req->count > jpg_nbufs) + req->count = jpg_nbufs; - switch (fh->map_mode) { - case ZORAN_MAP_MODE_RAW: /* raw capture */ - if (fh->v4l_buffers.active == ZORAN_FREE && - zr->v4l_buffers.active != ZORAN_FREE) { - res = -EPERM; /* stay off other's settings! */ - goto strmoff_unlock_and_return; - } - if (zr->v4l_buffers.active == ZORAN_FREE) - goto strmoff_unlock_and_return; + /* The next mmap will map the MJPEG buffers */ + map_mode_jpg(fh, req->type == V4L2_BUF_TYPE_VIDEO_OUTPUT); + fh->buffers.num_buffers = req->count; + fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings); - /* unload capture */ - if (zr->v4l_memgrab_active) { - unsigned long flags; + if (jpg_fbuffer_alloc(fh)) { + res = -ENOMEM; + goto v4l2reqbuf_unlock_and_return; + } + } else { + dprintk(1, + KERN_ERR + "%s: VIDIOC_REQBUFS - unknown type %d\n", + ZR_DEVNAME(zr), req->type); + res = -EINVAL; + goto v4l2reqbuf_unlock_and_return; + } +v4l2reqbuf_unlock_and_return: + mutex_unlock(&zr->resource_lock); - spin_lock_irqsave(&zr->spinlock, flags); - zr36057_set_memgrab(zr, 0); - spin_unlock_irqrestore(&zr->spinlock, flags); - } + return res; +} - for (i = 0; i < fh->v4l_buffers.num_buffers; i++) - zr->v4l_buffers.buffer[i].state = - BUZ_STATE_USER; - fh->v4l_buffers = zr->v4l_buffers; +static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int res; - zr->v4l_buffers.active = fh->v4l_buffers.active = - ZORAN_FREE; + mutex_lock(&zr->resource_lock); + res = zoran_v4l2_buffer_status(fh, buf, buf->index); + mutex_unlock(&zr->resource_lock); - zr->v4l_grab_seq = 0; - zr->v4l_pend_head = zr->v4l_pend_tail = 0; - zr->v4l_sync_tail = 0; + return res; +} - break; +static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int res = 0, codec_mode, buf_type; - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: - if (fh->jpg_buffers.active == ZORAN_FREE && - zr->jpg_buffers.active != ZORAN_FREE) { - res = -EPERM; /* stay off other's settings! */ - goto strmoff_unlock_and_return; - } - if (zr->jpg_buffers.active == ZORAN_FREE) - goto strmoff_unlock_and_return; - - res = - jpg_qbuf(file, -1, - (fh->map_mode == - ZORAN_MAP_MODE_JPG_REC) ? - BUZ_MODE_MOTION_COMPRESS : - BUZ_MODE_MOTION_DECOMPRESS); - if (res) - goto strmoff_unlock_and_return; - break; - default: - dprintk(1, - KERN_ERR - "%s: VIDIOC_STREAMOFF - invalid map mode %d\n", - ZR_DEVNAME(zr), fh->map_mode); + mutex_lock(&zr->resource_lock); + + switch (fh->map_mode) { + case ZORAN_MAP_MODE_RAW: + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dprintk(1, KERN_ERR + "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", + ZR_DEVNAME(zr), buf->type, fh->map_mode); res = -EINVAL; - goto strmoff_unlock_and_return; + goto qbuf_unlock_and_return; } - strmoff_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; - } + res = zoran_v4l_queue_frame(fh, buf->index); + if (res) + goto qbuf_unlock_and_return; + if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED) + zr36057_set_memgrab(zr, 1); break; - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - - dprintk(3, KERN_DEBUG "%s: VIDIOC_QUERYCTRL - id=%d\n", - ZR_DEVNAME(zr), ctrl->id); - - /* we only support hue/saturation/contrast/brightness */ - if (ctrl->id < V4L2_CID_BRIGHTNESS || - ctrl->id > V4L2_CID_HUE) - return -EINVAL; - else { - int id = ctrl->id; - memset(ctrl, 0, sizeof(*ctrl)); - ctrl->id = id; + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: + if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) { + buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + codec_mode = BUZ_MODE_MOTION_DECOMPRESS; + } else { + buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + codec_mode = BUZ_MODE_MOTION_COMPRESS; } - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)-1); - break; - case V4L2_CID_CONTRAST: - strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)-1); - break; - case V4L2_CID_SATURATION: - strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)-1); - break; - case V4L2_CID_HUE: - strncpy(ctrl->name, "Hue", sizeof(ctrl->name)-1); - break; + if (buf->type != buf_type) { + dprintk(1, KERN_ERR + "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", + ZR_DEVNAME(zr), buf->type, fh->map_mode); + res = -EINVAL; + goto qbuf_unlock_and_return; } - ctrl->minimum = 0; - ctrl->maximum = 65535; - ctrl->step = 1; - ctrl->default_value = 32768; - ctrl->type = V4L2_CTRL_TYPE_INTEGER; + res = zoran_jpg_queue_frame(fh, buf->index, codec_mode); + if (res != 0) + goto qbuf_unlock_and_return; + if (zr->codec_mode == BUZ_MODE_IDLE && + fh->buffers.active == ZORAN_LOCKED) + zr36057_enable_jpg(zr, codec_mode); - return 0; - } break; - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - dprintk(3, KERN_DEBUG "%s: VIDIOC_G_CTRL - id=%d\n", - ZR_DEVNAME(zr), ctrl->id); - - /* we only support hue/saturation/contrast/brightness */ - if (ctrl->id < V4L2_CID_BRIGHTNESS || - ctrl->id > V4L2_CID_HUE) - return -EINVAL; - - mutex_lock(&zr->resource_lock); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = zr->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = zr->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = zr->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = zr->hue; - break; - } - mutex_unlock(&zr->resource_lock); - - return 0; - } + default: + dprintk(1, KERN_ERR + "%s: VIDIOC_QBUF - unsupported type %d\n", + ZR_DEVNAME(zr), buf->type); + res = -EINVAL; break; + } +qbuf_unlock_and_return: + mutex_unlock(&zr->resource_lock); - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - struct video_picture pict; + return res; +} - dprintk(3, KERN_DEBUG "%s: VIDIOC_S_CTRL - id=%d\n", - ZR_DEVNAME(zr), ctrl->id); +static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int res = 0, buf_type, num = -1; /* compiler borks here (?) */ - /* we only support hue/saturation/contrast/brightness */ - if (ctrl->id < V4L2_CID_BRIGHTNESS || - ctrl->id > V4L2_CID_HUE) - return -EINVAL; + mutex_lock(&zr->resource_lock); - if (ctrl->value < 0 || ctrl->value > 65535) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_S_CTRL - invalid value %d for id=%d\n", - ZR_DEVNAME(zr), ctrl->value, ctrl->id); - return -EINVAL; + switch (fh->map_mode) { + case ZORAN_MAP_MODE_RAW: + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dprintk(1, KERN_ERR + "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", + ZR_DEVNAME(zr), buf->type, fh->map_mode); + res = -EINVAL; + goto dqbuf_unlock_and_return; } - mutex_lock(&zr->resource_lock); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - zr->brightness = ctrl->value; - break; - case V4L2_CID_CONTRAST: - zr->contrast = ctrl->value; - break; - case V4L2_CID_SATURATION: - zr->saturation = ctrl->value; - break; - case V4L2_CID_HUE: - zr->hue = ctrl->value; - break; + num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME]; + if (file->f_flags & O_NONBLOCK && + zr->v4l_buffers.buffer[num].state != BUZ_STATE_DONE) { + res = -EAGAIN; + goto dqbuf_unlock_and_return; } - pict.brightness = zr->brightness; - pict.contrast = zr->contrast; - pict.colour = zr->saturation; - pict.hue = zr->hue; - - decoder_command(zr, DECODER_SET_PICTURE, &pict); - - mutex_unlock(&zr->resource_lock); - - return 0; - } + res = v4l_sync(fh, num); + if (res) + goto dqbuf_unlock_and_return; + zr->v4l_sync_tail++; + res = zoran_v4l2_buffer_status(fh, buf, num); break; - case VIDIOC_ENUMSTD: + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: { - struct v4l2_standard *std = arg; + struct zoran_sync bs; - dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMSTD - index=%d\n", - ZR_DEVNAME(zr), std->index); + if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) + buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + else + buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (std->index < 0 || std->index >= (zr->card.norms + 1)) - return -EINVAL; - else { - int id = std->index; - memset(std, 0, sizeof(*std)); - std->index = id; + if (buf->type != buf_type) { + dprintk(1, KERN_ERR + "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n", + ZR_DEVNAME(zr), buf->type, fh->map_mode); + res = -EINVAL; + goto dqbuf_unlock_and_return; } - if (std->index == zr->card.norms) { - /* if we have autodetect, ... */ - struct video_decoder_capability caps; - decoder_command(zr, DECODER_GET_CAPABILITIES, - &caps); - if (caps.flags & VIDEO_DECODER_AUTO) { - std->id = V4L2_STD_ALL; - strncpy(std->name, "Autodetect", sizeof(std->name)-1); - return 0; - } else - return -EINVAL; - } - switch (std->index) { - case 0: - std->id = V4L2_STD_PAL; - strncpy(std->name, "PAL", sizeof(std->name)-1); - std->frameperiod.numerator = 1; - std->frameperiod.denominator = 25; - std->framelines = zr->card.tvn[0]->Ht; - break; - case 1: - std->id = V4L2_STD_NTSC; - strncpy(std->name, "NTSC", sizeof(std->name)-1); - std->frameperiod.numerator = 1001; - std->frameperiod.denominator = 30000; - std->framelines = zr->card.tvn[1]->Ht; - break; - case 2: - std->id = V4L2_STD_SECAM; - strncpy(std->name, "SECAM", sizeof(std->name)-1); - std->frameperiod.numerator = 1; - std->frameperiod.denominator = 25; - std->framelines = zr->card.tvn[2]->Ht; - break; - } + num = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME]; - return 0; + if (file->f_flags & O_NONBLOCK && + zr->jpg_buffers.buffer[num].state != BUZ_STATE_DONE) { + res = -EAGAIN; + goto dqbuf_unlock_and_return; + } + res = jpg_sync(fh, &bs); + if (res) + goto dqbuf_unlock_and_return; + res = zoran_v4l2_buffer_status(fh, buf, bs.frame); + break; } + + default: + dprintk(1, KERN_ERR + "%s: VIDIOC_DQBUF - unsupported type %d\n", + ZR_DEVNAME(zr), buf->type); + res = -EINVAL; break; + } +dqbuf_unlock_and_return: + mutex_unlock(&zr->resource_lock); - case VIDIOC_G_STD: - { - v4l2_std_id *std = arg; - int norm; + return res; +} - dprintk(3, KERN_DEBUG "%s: VIDIOC_G_STD\n", ZR_DEVNAME(zr)); +static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int res = 0; - mutex_lock(&zr->resource_lock); - norm = zr->norm; - mutex_unlock(&zr->resource_lock); + mutex_lock(&zr->resource_lock); - switch (norm) { - case VIDEO_MODE_PAL: - *std = V4L2_STD_PAL; - break; - case VIDEO_MODE_NTSC: - *std = V4L2_STD_NTSC; - break; - case VIDEO_MODE_SECAM: - *std = V4L2_STD_SECAM; - break; + switch (fh->map_mode) { + case ZORAN_MAP_MODE_RAW: /* raw capture */ + if (zr->v4l_buffers.active != ZORAN_ACTIVE || + fh->buffers.active != ZORAN_ACTIVE) { + res = -EBUSY; + goto strmon_unlock_and_return; } - return 0; - } + zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED; + zr->v4l_settings = fh->v4l_settings; + + zr->v4l_sync_tail = zr->v4l_pend_tail; + if (!zr->v4l_memgrab_active && + zr->v4l_pend_head != zr->v4l_pend_tail) { + zr36057_set_memgrab(zr, 1); + } break; - case VIDIOC_S_STD: - { - int norm = -1, res = 0; - v4l2_std_id *std = arg; - - dprintk(3, KERN_DEBUG "%s: VIDIOC_S_STD - norm=0x%llx\n", - ZR_DEVNAME(zr), (unsigned long long)*std); - - if ((*std & V4L2_STD_PAL) && !(*std & ~V4L2_STD_PAL)) - norm = VIDEO_MODE_PAL; - else if ((*std & V4L2_STD_NTSC) && !(*std & ~V4L2_STD_NTSC)) - norm = VIDEO_MODE_NTSC; - else if ((*std & V4L2_STD_SECAM) && !(*std & ~V4L2_STD_SECAM)) - norm = VIDEO_MODE_SECAM; - else if (*std == V4L2_STD_ALL) - norm = VIDEO_MODE_AUTO; - else { - dprintk(1, - KERN_ERR - "%s: VIDIOC_S_STD - invalid norm 0x%llx\n", - ZR_DEVNAME(zr), (unsigned long long)*std); - return -EINVAL; + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: + /* what is the codec mode right now? */ + if (zr->jpg_buffers.active != ZORAN_ACTIVE || + fh->buffers.active != ZORAN_ACTIVE) { + res = -EBUSY; + goto strmon_unlock_and_return; } - mutex_lock(&zr->resource_lock); - if ((res = zoran_set_norm(zr, norm))) - goto sstd_unlock_and_return; + zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED; - res = wait_grab_pending(zr); - sstd_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; - } + if (zr->jpg_que_head != zr->jpg_que_tail) { + /* Start the jpeg codec when the first frame is queued */ + jpeg_start(zr); + } break; - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *inp = arg; - int status; - - dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMINPUT - index=%d\n", - ZR_DEVNAME(zr), inp->index); + default: + dprintk(1, + KERN_ERR + "%s: VIDIOC_STREAMON - invalid map mode %d\n", + ZR_DEVNAME(zr), fh->map_mode); + res = -EINVAL; + break; + } +strmon_unlock_and_return: + mutex_unlock(&zr->resource_lock); - if (inp->index < 0 || inp->index >= zr->card.inputs) - return -EINVAL; - else { - int id = inp->index; - memset(inp, 0, sizeof(*inp)); - inp->index = id; - } + return res; +} - strncpy(inp->name, zr->card.input[inp->index].name, - sizeof(inp->name) - 1); - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_ALL; +static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int i, res = 0; + unsigned long flags; - /* Get status of video decoder */ - mutex_lock(&zr->resource_lock); - decoder_command(zr, DECODER_GET_STATUS, &status); - mutex_unlock(&zr->resource_lock); + mutex_lock(&zr->resource_lock); - if (!(status & DECODER_STATUS_GOOD)) { - inp->status |= V4L2_IN_ST_NO_POWER; - inp->status |= V4L2_IN_ST_NO_SIGNAL; + switch (fh->map_mode) { + case ZORAN_MAP_MODE_RAW: /* raw capture */ + if (fh->buffers.active == ZORAN_FREE && + zr->v4l_buffers.active != ZORAN_FREE) { + res = -EPERM; /* stay off other's settings! */ + goto strmoff_unlock_and_return; } - if (!(status & DECODER_STATUS_COLOR)) - inp->status |= V4L2_IN_ST_NO_COLOR; + if (zr->v4l_buffers.active == ZORAN_FREE) + goto strmoff_unlock_and_return; - return 0; - } - break; + spin_lock_irqsave(&zr->spinlock, flags); + /* unload capture */ + if (zr->v4l_memgrab_active) { - case VIDIOC_G_INPUT: - { - int *input = arg; + zr36057_set_memgrab(zr, 0); + } - dprintk(3, KERN_DEBUG "%s: VIDIOC_G_INPUT\n", ZR_DEVNAME(zr)); + for (i = 0; i < fh->buffers.num_buffers; i++) + zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER; + fh->buffers = zr->v4l_buffers; - mutex_lock(&zr->resource_lock); - *input = zr->input; - mutex_unlock(&zr->resource_lock); + zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; - return 0; - } - break; + zr->v4l_grab_seq = 0; + zr->v4l_pend_head = zr->v4l_pend_tail = 0; + zr->v4l_sync_tail = 0; - case VIDIOC_S_INPUT: - { - int *input = arg, res = 0; + spin_unlock_irqrestore(&zr->spinlock, flags); - dprintk(3, KERN_DEBUG "%s: VIDIOC_S_INPUT - input=%d\n", - ZR_DEVNAME(zr), *input); + break; - mutex_lock(&zr->resource_lock); - if ((res = zoran_set_input(zr, *input))) - goto sinput_unlock_and_return; + case ZORAN_MAP_MODE_JPG_REC: + case ZORAN_MAP_MODE_JPG_PLAY: + if (fh->buffers.active == ZORAN_FREE && + zr->jpg_buffers.active != ZORAN_FREE) { + res = -EPERM; /* stay off other's settings! */ + goto strmoff_unlock_and_return; + } + if (zr->jpg_buffers.active == ZORAN_FREE) + goto strmoff_unlock_and_return; - /* Make sure the changes come into effect */ - res = wait_grab_pending(zr); - sinput_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; - } + res = jpg_qbuf(fh, -1, + (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ? + BUZ_MODE_MOTION_COMPRESS : + BUZ_MODE_MOTION_DECOMPRESS); + if (res) + goto strmoff_unlock_and_return; + break; + default: + dprintk(1, KERN_ERR + "%s: VIDIOC_STREAMOFF - invalid map mode %d\n", + ZR_DEVNAME(zr), fh->map_mode); + res = -EINVAL; break; + } +strmoff_unlock_and_return: + mutex_unlock(&zr->resource_lock); - case VIDIOC_ENUMOUTPUT: - { - struct v4l2_output *outp = arg; + return res; +} - dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMOUTPUT - index=%d\n", - ZR_DEVNAME(zr), outp->index); +static int zoran_queryctrl(struct file *file, void *__fh, + struct v4l2_queryctrl *ctrl) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - if (outp->index != 0) - return -EINVAL; + /* we only support hue/saturation/contrast/brightness */ + if (ctrl->id < V4L2_CID_BRIGHTNESS || + ctrl->id > V4L2_CID_HUE) + return -EINVAL; - memset(outp, 0, sizeof(*outp)); - outp->index = 0; - outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY; - strncpy(outp->name, "Autodetect", sizeof(outp->name)-1); + decoder_call(zr, core, queryctrl, ctrl); - return 0; - } - break; + return 0; +} - case VIDIOC_G_OUTPUT: - { - int *output = arg; +static int zoran_g_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - dprintk(3, KERN_DEBUG "%s: VIDIOC_G_OUTPUT\n", ZR_DEVNAME(zr)); + /* we only support hue/saturation/contrast/brightness */ + if (ctrl->id < V4L2_CID_BRIGHTNESS || + ctrl->id > V4L2_CID_HUE) + return -EINVAL; - *output = 0; + mutex_lock(&zr->resource_lock); + decoder_call(zr, core, g_ctrl, ctrl); + mutex_unlock(&zr->resource_lock); - return 0; - } - break; + return 0; +} - case VIDIOC_S_OUTPUT: - { - int *output = arg; +static int zoran_s_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - dprintk(3, KERN_DEBUG "%s: VIDIOC_S_OUTPUT - output=%d\n", - ZR_DEVNAME(zr), *output); + /* we only support hue/saturation/contrast/brightness */ + if (ctrl->id < V4L2_CID_BRIGHTNESS || + ctrl->id > V4L2_CID_HUE) + return -EINVAL; - if (*output != 0) - return -EINVAL; + mutex_lock(&zr->resource_lock); + decoder_call(zr, core, s_ctrl, ctrl); + mutex_unlock(&zr->resource_lock); - return 0; - } - break; + return 0; +} - /* cropping (sub-frame capture) */ - case VIDIOC_CROPCAP: - { - struct v4l2_cropcap *cropcap = arg; - int type = cropcap->type, res = 0; +static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - dprintk(3, KERN_ERR "%s: VIDIOC_CROPCAP - type=%d\n", - ZR_DEVNAME(zr), cropcap->type); + mutex_lock(&zr->resource_lock); + *std = zr->norm; + mutex_unlock(&zr->resource_lock); + return 0; +} - memset(cropcap, 0, sizeof(*cropcap)); - cropcap->type = type; +static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id *std) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int res = 0; - mutex_lock(&zr->resource_lock); + mutex_lock(&zr->resource_lock); + res = zoran_set_norm(zr, *std); + if (res) + goto sstd_unlock_and_return; - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - fh->map_mode == ZORAN_MAP_MODE_RAW)) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n", - ZR_DEVNAME(zr)); - res = -EINVAL; - goto cropcap_unlock_and_return; - } + res = wait_grab_pending(zr); +sstd_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; +} - cropcap->bounds.top = cropcap->bounds.left = 0; - cropcap->bounds.width = BUZ_MAX_WIDTH; - cropcap->bounds.height = BUZ_MAX_HEIGHT; - cropcap->defrect.top = cropcap->defrect.left = 0; - cropcap->defrect.width = BUZ_MIN_WIDTH; - cropcap->defrect.height = BUZ_MIN_HEIGHT; - cropcap_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; - } - break; +static int zoran_enum_input(struct file *file, void *__fh, + struct v4l2_input *inp) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - case VIDIOC_G_CROP: - { - struct v4l2_crop *crop = arg; - int type = crop->type, res = 0; + if (inp->index < 0 || inp->index >= zr->card.inputs) + return -EINVAL; + else { + int id = inp->index; + memset(inp, 0, sizeof(*inp)); + inp->index = id; + } - dprintk(3, KERN_ERR "%s: VIDIOC_G_CROP - type=%d\n", - ZR_DEVNAME(zr), crop->type); + strncpy(inp->name, zr->card.input[inp->index].name, + sizeof(inp->name) - 1); + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_ALL; - memset(crop, 0, sizeof(*crop)); - crop->type = type; + /* Get status of video decoder */ + mutex_lock(&zr->resource_lock); + decoder_call(zr, video, g_input_status, &inp->status); + mutex_unlock(&zr->resource_lock); + return 0; +} - mutex_lock(&zr->resource_lock); +static int zoran_g_input(struct file *file, void *__fh, unsigned int *input) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - fh->map_mode == ZORAN_MAP_MODE_RAW)) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", - ZR_DEVNAME(zr)); - res = -EINVAL; - goto gcrop_unlock_and_return; - } + mutex_lock(&zr->resource_lock); + *input = zr->input; + mutex_unlock(&zr->resource_lock); - crop->c.top = fh->jpg_settings.img_y; - crop->c.left = fh->jpg_settings.img_x; - crop->c.width = fh->jpg_settings.img_width; - crop->c.height = fh->jpg_settings.img_height; + return 0; +} - gcrop_unlock_and_return: - mutex_unlock(&zr->resource_lock); +static int zoran_s_input(struct file *file, void *__fh, unsigned int input) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int res; - return res; - } - break; + mutex_lock(&zr->resource_lock); + res = zoran_set_input(zr, input); + if (res) + goto sinput_unlock_and_return; - case VIDIOC_S_CROP: - { - struct v4l2_crop *crop = arg; - int res = 0; + /* Make sure the changes come into effect */ + res = wait_grab_pending(zr); +sinput_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; +} - settings = fh->jpg_settings; +static int zoran_enum_output(struct file *file, void *__fh, + struct v4l2_output *outp) +{ + if (outp->index != 0) + return -EINVAL; - dprintk(3, - KERN_ERR - "%s: VIDIOC_S_CROP - type=%d, x=%d,y=%d,w=%d,h=%d\n", - ZR_DEVNAME(zr), crop->type, crop->c.left, crop->c.top, - crop->c.width, crop->c.height); + memset(outp, 0, sizeof(*outp)); + outp->index = 0; + outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY; + strncpy(outp->name, "Autodetect", sizeof(outp->name)-1); - mutex_lock(&zr->resource_lock); + return 0; +} - if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_S_CROP - cannot change settings while active\n", - ZR_DEVNAME(zr)); - res = -EBUSY; - goto scrop_unlock_and_return; - } +static int zoran_g_output(struct file *file, void *__fh, unsigned int *output) +{ + *output = 0; - if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && - (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - fh->map_mode == ZORAN_MAP_MODE_RAW)) { - dprintk(1, - KERN_ERR - "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", - ZR_DEVNAME(zr)); - res = -EINVAL; - goto scrop_unlock_and_return; - } + return 0; +} - /* move into a form that we understand */ - settings.img_x = crop->c.left; - settings.img_y = crop->c.top; - settings.img_width = crop->c.width; - settings.img_height = crop->c.height; +static int zoran_s_output(struct file *file, void *__fh, unsigned int output) +{ + if (output != 0) + return -EINVAL; - /* check validity */ - if ((res = zoran_check_jpg_settings(zr, &settings))) - goto scrop_unlock_and_return; + return 0; +} - /* accept */ - fh->jpg_settings = settings; +/* cropping (sub-frame capture) */ +static int zoran_cropcap(struct file *file, void *__fh, + struct v4l2_cropcap *cropcap) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int type = cropcap->type, res = 0; - scrop_unlock_and_return: - mutex_unlock(&zr->resource_lock); - return res; - } - break; + memset(cropcap, 0, sizeof(*cropcap)); + cropcap->type = type; - case VIDIOC_G_JPEGCOMP: - { - struct v4l2_jpegcompression *params = arg; + mutex_lock(&zr->resource_lock); - dprintk(3, KERN_DEBUG "%s: VIDIOC_G_JPEGCOMP\n", + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + fh->map_mode == ZORAN_MAP_MODE_RAW)) { + dprintk(1, KERN_ERR + "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n", ZR_DEVNAME(zr)); + res = -EINVAL; + goto cropcap_unlock_and_return; + } - memset(params, 0, sizeof(*params)); + cropcap->bounds.top = cropcap->bounds.left = 0; + cropcap->bounds.width = BUZ_MAX_WIDTH; + cropcap->bounds.height = BUZ_MAX_HEIGHT; + cropcap->defrect.top = cropcap->defrect.left = 0; + cropcap->defrect.width = BUZ_MIN_WIDTH; + cropcap->defrect.height = BUZ_MIN_HEIGHT; +cropcap_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; +} - mutex_lock(&zr->resource_lock); +static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int type = crop->type, res = 0; - params->quality = fh->jpg_settings.jpg_comp.quality; - params->APPn = fh->jpg_settings.jpg_comp.APPn; - memcpy(params->APP_data, - fh->jpg_settings.jpg_comp.APP_data, - fh->jpg_settings.jpg_comp.APP_len); - params->APP_len = fh->jpg_settings.jpg_comp.APP_len; - memcpy(params->COM_data, - fh->jpg_settings.jpg_comp.COM_data, - fh->jpg_settings.jpg_comp.COM_len); - params->COM_len = fh->jpg_settings.jpg_comp.COM_len; - params->jpeg_markers = - fh->jpg_settings.jpg_comp.jpeg_markers; + memset(crop, 0, sizeof(*crop)); + crop->type = type; - mutex_unlock(&zr->resource_lock); + mutex_lock(&zr->resource_lock); - return 0; + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + fh->map_mode == ZORAN_MAP_MODE_RAW)) { + dprintk(1, + KERN_ERR + "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", + ZR_DEVNAME(zr)); + res = -EINVAL; + goto gcrop_unlock_and_return; } - break; - - case VIDIOC_S_JPEGCOMP: - { - struct v4l2_jpegcompression *params = arg; - int res = 0; - settings = fh->jpg_settings; + crop->c.top = fh->jpg_settings.img_y; + crop->c.left = fh->jpg_settings.img_x; + crop->c.width = fh->jpg_settings.img_width; + crop->c.height = fh->jpg_settings.img_height; - dprintk(3, - KERN_DEBUG - "%s: VIDIOC_S_JPEGCOMP - quality=%d, APPN=%d, APP_len=%d, COM_len=%d\n", - ZR_DEVNAME(zr), params->quality, params->APPn, - params->APP_len, params->COM_len); +gcrop_unlock_and_return: + mutex_unlock(&zr->resource_lock); - settings.jpg_comp = *params; + return res; +} - mutex_lock(&zr->resource_lock); +static int zoran_s_crop(struct file *file, void *__fh, struct v4l2_crop *crop) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int res = 0; + struct zoran_jpg_settings settings; - if (fh->v4l_buffers.active != ZORAN_FREE || - fh->jpg_buffers.active != ZORAN_FREE) { - dprintk(1, - KERN_WARNING - "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n", - ZR_DEVNAME(zr)); - res = -EBUSY; - goto sjpegc_unlock_and_return; - } + settings = fh->jpg_settings; - if ((res = zoran_check_jpg_settings(zr, &settings))) - goto sjpegc_unlock_and_return; - if (!fh->jpg_buffers.allocated) - fh->jpg_buffers.buffer_size = - zoran_v4l2_calc_bufsize(&fh->jpg_settings); - fh->jpg_settings.jpg_comp = *params = settings.jpg_comp; - sjpegc_unlock_and_return: - mutex_unlock(&zr->resource_lock); + mutex_lock(&zr->resource_lock); - return 0; + if (fh->buffers.allocated) { + dprintk(1, KERN_ERR + "%s: VIDIOC_S_CROP - cannot change settings while active\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + goto scrop_unlock_and_return; } - break; - case VIDIOC_QUERYSTD: /* why is this useful? */ - { - v4l2_std_id *std = arg; - - dprintk(3, - KERN_DEBUG "%s: VIDIOC_QUERY_STD - std=0x%llx\n", - ZR_DEVNAME(zr), (unsigned long long)*std); - - if (*std == V4L2_STD_ALL || *std == V4L2_STD_NTSC || - *std == V4L2_STD_PAL || (*std == V4L2_STD_SECAM && - zr->card.norms == 3)) { - return 0; - } - - return -EINVAL; + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && + (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + fh->map_mode == ZORAN_MAP_MODE_RAW)) { + dprintk(1, KERN_ERR + "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n", + ZR_DEVNAME(zr)); + res = -EINVAL; + goto scrop_unlock_and_return; } - break; - case VIDIOC_TRY_FMT: - { - struct v4l2_format *fmt = arg; - int res = 0; + /* move into a form that we understand */ + settings.img_x = crop->c.left; + settings.img_y = crop->c.top; + settings.img_width = crop->c.width; + settings.img_height = crop->c.height; - dprintk(3, KERN_DEBUG "%s: VIDIOC_TRY_FMT - type=%d\n", - ZR_DEVNAME(zr), fmt->type); + /* check validity */ + res = zoran_check_jpg_settings(zr, &settings, 0); + if (res) + goto scrop_unlock_and_return; - switch (fmt->type) { - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - mutex_lock(&zr->resource_lock); + /* accept */ + fh->jpg_settings = settings; - if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH) - fmt->fmt.win.w.width = BUZ_MAX_WIDTH; - if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH) - fmt->fmt.win.w.width = BUZ_MIN_WIDTH; - if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT) - fmt->fmt.win.w.height = BUZ_MAX_HEIGHT; - if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT) - fmt->fmt.win.w.height = BUZ_MIN_HEIGHT; +scrop_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return res; +} - mutex_unlock(&zr->resource_lock); - break; +static int zoran_g_jpegcomp(struct file *file, void *__fh, + struct v4l2_jpegcompression *params) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + memset(params, 0, sizeof(*params)); - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (fmt->fmt.pix.bytesperline > 0) - return -EINVAL; + mutex_lock(&zr->resource_lock); - mutex_lock(&zr->resource_lock); - - if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) { - settings = fh->jpg_settings; - - /* we actually need to set 'real' parameters now */ - if ((fmt->fmt.pix.height * 2) > - BUZ_MAX_HEIGHT) - settings.TmpDcm = 1; - else - settings.TmpDcm = 2; - settings.decimation = 0; - if (fmt->fmt.pix.height <= - fh->jpg_settings.img_height / 2) - settings.VerDcm = 2; - else - settings.VerDcm = 1; - if (fmt->fmt.pix.width <= - fh->jpg_settings.img_width / 4) - settings.HorDcm = 4; - else if (fmt->fmt.pix.width <= - fh->jpg_settings.img_width / 2) - settings.HorDcm = 2; - else - settings.HorDcm = 1; - if (settings.TmpDcm == 1) - settings.field_per_buff = 2; - else - settings.field_per_buff = 1; - - /* check */ - if ((res = - zoran_check_jpg_settings(zr, - &settings))) - goto tryfmt_unlock_and_return; - - /* tell the user what we actually did */ - fmt->fmt.pix.width = - settings.img_width / settings.HorDcm; - fmt->fmt.pix.height = - settings.img_height * 2 / - (settings.TmpDcm * settings.VerDcm); - if (settings.TmpDcm == 1) - fmt->fmt.pix.field = - (fh->jpg_settings. - odd_even ? V4L2_FIELD_SEQ_TB : - V4L2_FIELD_SEQ_BT); - else - fmt->fmt.pix.field = - (fh->jpg_settings. - odd_even ? V4L2_FIELD_TOP : - V4L2_FIELD_BOTTOM); - - fmt->fmt.pix.sizeimage = - zoran_v4l2_calc_bufsize(&settings); - } else if (fmt->type == - V4L2_BUF_TYPE_VIDEO_CAPTURE) { - int i; - - for (i = 0; i < NUM_FORMATS; i++) - if (zoran_formats[i].fourcc == - fmt->fmt.pix.pixelformat) - break; - if (i == NUM_FORMATS) { - res = -EINVAL; - goto tryfmt_unlock_and_return; - } + params->quality = fh->jpg_settings.jpg_comp.quality; + params->APPn = fh->jpg_settings.jpg_comp.APPn; + memcpy(params->APP_data, + fh->jpg_settings.jpg_comp.APP_data, + fh->jpg_settings.jpg_comp.APP_len); + params->APP_len = fh->jpg_settings.jpg_comp.APP_len; + memcpy(params->COM_data, + fh->jpg_settings.jpg_comp.COM_data, + fh->jpg_settings.jpg_comp.COM_len); + params->COM_len = fh->jpg_settings.jpg_comp.COM_len; + params->jpeg_markers = + fh->jpg_settings.jpg_comp.jpeg_markers; - if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) - fmt->fmt.pix.width = BUZ_MAX_WIDTH; - if (fmt->fmt.pix.width < BUZ_MIN_WIDTH) - fmt->fmt.pix.width = BUZ_MIN_WIDTH; - if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) - fmt->fmt.pix.height = - BUZ_MAX_HEIGHT; - if (fmt->fmt.pix.height < BUZ_MIN_HEIGHT) - fmt->fmt.pix.height = - BUZ_MIN_HEIGHT; - } else { - res = -EINVAL; - goto tryfmt_unlock_and_return; - } - tryfmt_unlock_and_return: - mutex_unlock(&zr->resource_lock); + mutex_unlock(&zr->resource_lock); - return res; - break; + return 0; +} - default: - return -EINVAL; - } +static int zoran_s_jpegcomp(struct file *file, void *__fh, + struct v4l2_jpegcompression *params) +{ + struct zoran_fh *fh = __fh; + struct zoran *zr = fh->zr; + int res = 0; + struct zoran_jpg_settings settings; - return 0; - } - break; + settings = fh->jpg_settings; - default: - dprintk(1, KERN_DEBUG "%s: UNKNOWN ioctl cmd: 0x%x\n", - ZR_DEVNAME(zr), cmd); - return -ENOIOCTLCMD; - break; + settings.jpg_comp = *params; + + mutex_lock(&zr->resource_lock); + if (fh->buffers.active != ZORAN_FREE) { + dprintk(1, KERN_WARNING + "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n", + ZR_DEVNAME(zr)); + res = -EBUSY; + goto sjpegc_unlock_and_return; } - return 0; -} + res = zoran_check_jpg_settings(zr, &settings, 0); + if (res) + goto sjpegc_unlock_and_return; + if (!fh->buffers.allocated) + fh->buffers.buffer_size = + zoran_v4l2_calc_bufsize(&fh->jpg_settings); + fh->jpg_settings.jpg_comp = *params = settings.jpg_comp; +sjpegc_unlock_and_return: + mutex_unlock(&zr->resource_lock); -static long -zoran_ioctl(struct file *file, - unsigned int cmd, - unsigned long arg) -{ - return video_usercopy(file, cmd, arg, zoran_do_ioctl); + return res; } static unsigned int @@ -4191,11 +3062,11 @@ zoran_poll (struct file *file, KERN_DEBUG "%s: %s() raw - active=%c, sync_tail=%lu/%c, pend_tail=%lu, pend_head=%lu\n", ZR_DEVNAME(zr), __func__, - "FAL"[fh->v4l_buffers.active], zr->v4l_sync_tail, + "FAL"[fh->buffers.active], zr->v4l_sync_tail, "UPMD"[zr->v4l_buffers.buffer[frame].state], zr->v4l_pend_tail, zr->v4l_pend_head); /* Process is the one capturing? */ - if (fh->v4l_buffers.active != ZORAN_FREE && + if (fh->buffers.active != ZORAN_FREE && /* Buffer ready to DQBUF? */ zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE) res = POLLIN | POLLRDNORM; @@ -4213,10 +3084,10 @@ zoran_poll (struct file *file, KERN_DEBUG "%s: %s() jpg - active=%c, que_tail=%lu/%c, que_head=%lu, dma=%lu/%lu\n", ZR_DEVNAME(zr), __func__, - "FAL"[fh->jpg_buffers.active], zr->jpg_que_tail, + "FAL"[fh->buffers.active], zr->jpg_que_tail, "UPMD"[zr->jpg_buffers.buffer[frame].state], zr->jpg_que_head, zr->jpg_dma_tail, zr->jpg_dma_head); - if (fh->jpg_buffers.active != ZORAN_FREE && + if (fh->buffers.active != ZORAN_FREE && zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) { if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) res = POLLIN | POLLRDNORM; @@ -4230,8 +3101,8 @@ zoran_poll (struct file *file, default: dprintk(1, KERN_ERR - "%s: zoran_poll() - internal error, unknown map_mode=%d\n", - ZR_DEVNAME(zr), fh->map_mode); + "%s: %s - internal error, unknown map_mode=%d\n", + ZR_DEVNAME(zr), __func__, fh->map_mode); res = POLLNVAL; } @@ -4265,98 +3136,53 @@ static void zoran_vm_close (struct vm_area_struct *vma) { struct zoran_mapping *map = vma->vm_private_data; - struct file *file = map->file; - struct zoran_fh *fh = file->private_data; + struct zoran_fh *fh = map->file->private_data; struct zoran *zr = fh->zr; int i; - map->count--; - if (map->count == 0) { - switch (fh->map_mode) { - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: - - dprintk(3, KERN_INFO "%s: munmap(MJPEG)\n", - ZR_DEVNAME(zr)); + if (--map->count > 0) + return; - for (i = 0; i < fh->jpg_buffers.num_buffers; i++) { - if (fh->jpg_buffers.buffer[i].map == map) { - fh->jpg_buffers.buffer[i].map = - NULL; - } - } - kfree(map); - - for (i = 0; i < fh->jpg_buffers.num_buffers; i++) - if (fh->jpg_buffers.buffer[i].map) - break; - if (i == fh->jpg_buffers.num_buffers) { - mutex_lock(&zr->resource_lock); - - if (fh->jpg_buffers.active != ZORAN_FREE) { - jpg_qbuf(file, -1, zr->codec_mode); - zr->jpg_buffers.allocated = 0; - zr->jpg_buffers.active = - fh->jpg_buffers.active = - ZORAN_FREE; - } - //jpg_fbuffer_free(file); - fh->jpg_buffers.allocated = 0; - fh->jpg_buffers.ready_to_be_freed = 1; - - mutex_unlock(&zr->resource_lock); - } + dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr), + __func__, mode_name(fh->map_mode)); - break; - - case ZORAN_MAP_MODE_RAW: - - dprintk(3, KERN_INFO "%s: munmap(V4L)\n", - ZR_DEVNAME(zr)); - - for (i = 0; i < fh->v4l_buffers.num_buffers; i++) { - if (fh->v4l_buffers.buffer[i].map == map) { - /* unqueue/unmap */ - fh->v4l_buffers.buffer[i].map = - NULL; - } - } - kfree(map); + for (i = 0; i < fh->buffers.num_buffers; i++) { + if (fh->buffers.buffer[i].map == map) + fh->buffers.buffer[i].map = NULL; + } + kfree(map); - for (i = 0; i < fh->v4l_buffers.num_buffers; i++) - if (fh->v4l_buffers.buffer[i].map) - break; - if (i == fh->v4l_buffers.num_buffers) { - mutex_lock(&zr->resource_lock); - - if (fh->v4l_buffers.active != ZORAN_FREE) { - unsigned long flags; - - spin_lock_irqsave(&zr->spinlock, flags); - zr36057_set_memgrab(zr, 0); - zr->v4l_buffers.allocated = 0; - zr->v4l_buffers.active = - fh->v4l_buffers.active = - ZORAN_FREE; - spin_unlock_irqrestore(&zr->spinlock, flags); - } - //v4l_fbuffer_free(file); - fh->v4l_buffers.allocated = 0; - fh->v4l_buffers.ready_to_be_freed = 1; + /* Any buffers still mapped? */ + for (i = 0; i < fh->buffers.num_buffers; i++) + if (fh->buffers.buffer[i].map) + return; - mutex_unlock(&zr->resource_lock); - } + dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr), + __func__, mode_name(fh->map_mode)); - break; + mutex_lock(&zr->resource_lock); - default: - printk(KERN_ERR - "%s: munmap() - internal error - unknown map mode %d\n", - ZR_DEVNAME(zr), fh->map_mode); - break; + if (fh->map_mode == ZORAN_MAP_MODE_RAW) { + if (fh->buffers.active != ZORAN_FREE) { + unsigned long flags; + spin_lock_irqsave(&zr->spinlock, flags); + zr36057_set_memgrab(zr, 0); + zr->v4l_buffers.allocated = 0; + zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE; + spin_unlock_irqrestore(&zr->spinlock, flags); } + v4l_fbuffer_free(fh); + } else { + if (fh->buffers.active != ZORAN_FREE) { + jpg_qbuf(fh, -1, zr->codec_mode); + zr->jpg_buffers.allocated = 0; + zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE; + } + jpg_fbuffer_free(fh); } + + mutex_unlock(&zr->resource_lock); } static struct vm_operations_struct zoran_vm_ops = { @@ -4379,90 +3205,106 @@ zoran_mmap (struct file *file, int res = 0; dprintk(3, - KERN_INFO "%s: mmap(%s) of 0x%08lx-0x%08lx (size=%lu)\n", - ZR_DEVNAME(zr), - fh->map_mode == ZORAN_MAP_MODE_RAW ? "V4L" : "MJPEG", - vma->vm_start, vma->vm_end, size); + KERN_INFO "%s: %s(%s) of 0x%08lx-0x%08lx (size=%lu)\n", + ZR_DEVNAME(zr), __func__, + mode_name(fh->map_mode), vma->vm_start, vma->vm_end, size); if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) || !(vma->vm_flags & VM_WRITE)) { dprintk(1, KERN_ERR - "%s: mmap() - no MAP_SHARED/PROT_{READ,WRITE} given\n", - ZR_DEVNAME(zr)); + "%s: %s - no MAP_SHARED/PROT_{READ,WRITE} given\n", + ZR_DEVNAME(zr), __func__); return -EINVAL; } - switch (fh->map_mode) { + mutex_lock(&zr->resource_lock); - case ZORAN_MAP_MODE_JPG_REC: - case ZORAN_MAP_MODE_JPG_PLAY: + if (!fh->buffers.allocated) { + dprintk(1, + KERN_ERR + "%s: %s(%s) - buffers not yet allocated\n", + ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode)); + res = -ENOMEM; + goto mmap_unlock_and_return; + } - /* lock */ - mutex_lock(&zr->resource_lock); + first = offset / fh->buffers.buffer_size; + last = first - 1 + size / fh->buffers.buffer_size; + if (offset % fh->buffers.buffer_size != 0 || + size % fh->buffers.buffer_size != 0 || first < 0 || + last < 0 || first >= fh->buffers.num_buffers || + last >= fh->buffers.buffer_size) { + dprintk(1, + KERN_ERR + "%s: %s(%s) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n", + ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), offset, size, + fh->buffers.buffer_size, + fh->buffers.num_buffers); + res = -EINVAL; + goto mmap_unlock_and_return; + } - /* Map the MJPEG buffers */ - if (!fh->jpg_buffers.allocated) { + /* Check if any buffers are already mapped */ + for (i = first; i <= last; i++) { + if (fh->buffers.buffer[i].map) { dprintk(1, KERN_ERR - "%s: zoran_mmap(MJPEG) - buffers not yet allocated\n", - ZR_DEVNAME(zr)); - res = -ENOMEM; - goto jpg_mmap_unlock_and_return; + "%s: %s(%s) - buffer %d already mapped\n", + ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i); + res = -EBUSY; + goto mmap_unlock_and_return; } + } - first = offset / fh->jpg_buffers.buffer_size; - last = first - 1 + size / fh->jpg_buffers.buffer_size; - if (offset % fh->jpg_buffers.buffer_size != 0 || - size % fh->jpg_buffers.buffer_size != 0 || first < 0 || - last < 0 || first >= fh->jpg_buffers.num_buffers || - last >= fh->jpg_buffers.num_buffers) { - dprintk(1, - KERN_ERR - "%s: mmap(MJPEG) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n", - ZR_DEVNAME(zr), offset, size, - fh->jpg_buffers.buffer_size, - fh->jpg_buffers.num_buffers); - res = -EINVAL; - goto jpg_mmap_unlock_and_return; - } + /* map these buffers */ + map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); + if (!map) { + res = -ENOMEM; + goto mmap_unlock_and_return; + } + map->file = file; + map->count = 1; + + vma->vm_ops = &zoran_vm_ops; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = map; + + if (fh->map_mode == ZORAN_MAP_MODE_RAW) { for (i = first; i <= last; i++) { - if (fh->jpg_buffers.buffer[i].map) { + todo = size; + if (todo > fh->buffers.buffer_size) + todo = fh->buffers.buffer_size; + page = fh->buffers.buffer[i].v4l.fbuffer_phys; + if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, + todo, PAGE_SHARED)) { dprintk(1, KERN_ERR - "%s: mmap(MJPEG) - buffer %d already mapped\n", - ZR_DEVNAME(zr), i); - res = -EBUSY; - goto jpg_mmap_unlock_and_return; + "%s: %s(V4L) - remap_pfn_range failed\n", + ZR_DEVNAME(zr), __func__); + res = -EAGAIN; + goto mmap_unlock_and_return; } + size -= todo; + start += todo; + fh->buffers.buffer[i].map = map; + if (size == 0) + break; } - - /* map these buffers (v4l_buffers[i]) */ - map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); - if (!map) { - res = -ENOMEM; - goto jpg_mmap_unlock_and_return; - } - map->file = file; - map->count = 1; - - vma->vm_ops = &zoran_vm_ops; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_private_data = map; - + } else { for (i = first; i <= last; i++) { for (j = 0; - j < fh->jpg_buffers.buffer_size / PAGE_SIZE; + j < fh->buffers.buffer_size / PAGE_SIZE; j++) { fraglen = - (le32_to_cpu(fh->jpg_buffers.buffer[i]. + (le32_to_cpu(fh->buffers.buffer[i].jpg. frag_tab[2 * j + 1]) & ~1) << 1; todo = size; if (todo > fraglen) todo = fraglen; pos = - le32_to_cpu(fh->jpg_buffers. - buffer[i].frag_tab[2 * j]); + le32_to_cpu(fh->buffers. + buffer[i].jpg.frag_tab[2 * j]); /* should just be pos on i386 */ page = virt_to_phys(bus_to_virt(pos)) >> PAGE_SHIFT; @@ -4470,123 +3312,82 @@ zoran_mmap (struct file *file, todo, PAGE_SHARED)) { dprintk(1, KERN_ERR - "%s: zoran_mmap(V4L) - remap_pfn_range failed\n", - ZR_DEVNAME(zr)); + "%s: %s(V4L) - remap_pfn_range failed\n", + ZR_DEVNAME(zr), __func__); res = -EAGAIN; - goto jpg_mmap_unlock_and_return; + goto mmap_unlock_and_return; } size -= todo; start += todo; if (size == 0) break; - if (le32_to_cpu(fh->jpg_buffers.buffer[i]. + if (le32_to_cpu(fh->buffers.buffer[i].jpg. frag_tab[2 * j + 1]) & 1) break; /* was last fragment */ } - fh->jpg_buffers.buffer[i].map = map; + fh->buffers.buffer[i].map = map; if (size == 0) break; } - jpg_mmap_unlock_and_return: - mutex_unlock(&zr->resource_lock); - - break; - - case ZORAN_MAP_MODE_RAW: - - mutex_lock(&zr->resource_lock); - - /* Map the V4L buffers */ - if (!fh->v4l_buffers.allocated) { - dprintk(1, - KERN_ERR - "%s: zoran_mmap(V4L) - buffers not yet allocated\n", - ZR_DEVNAME(zr)); - res = -ENOMEM; - goto v4l_mmap_unlock_and_return; - } - - first = offset / fh->v4l_buffers.buffer_size; - last = first - 1 + size / fh->v4l_buffers.buffer_size; - if (offset % fh->v4l_buffers.buffer_size != 0 || - size % fh->v4l_buffers.buffer_size != 0 || first < 0 || - last < 0 || first >= fh->v4l_buffers.num_buffers || - last >= fh->v4l_buffers.buffer_size) { - dprintk(1, - KERN_ERR - "%s: mmap(V4L) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n", - ZR_DEVNAME(zr), offset, size, - fh->v4l_buffers.buffer_size, - fh->v4l_buffers.num_buffers); - res = -EINVAL; - goto v4l_mmap_unlock_and_return; - } - for (i = first; i <= last; i++) { - if (fh->v4l_buffers.buffer[i].map) { - dprintk(1, - KERN_ERR - "%s: mmap(V4L) - buffer %d already mapped\n", - ZR_DEVNAME(zr), i); - res = -EBUSY; - goto v4l_mmap_unlock_and_return; - } - } - - /* map these buffers (v4l_buffers[i]) */ - map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL); - if (!map) { - res = -ENOMEM; - goto v4l_mmap_unlock_and_return; - } - map->file = file; - map->count = 1; - - vma->vm_ops = &zoran_vm_ops; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_private_data = map; - - for (i = first; i <= last; i++) { - todo = size; - if (todo > fh->v4l_buffers.buffer_size) - todo = fh->v4l_buffers.buffer_size; - page = fh->v4l_buffers.buffer[i].fbuffer_phys; - if (remap_pfn_range(vma, start, page >> PAGE_SHIFT, - todo, PAGE_SHARED)) { - dprintk(1, - KERN_ERR - "%s: zoran_mmap(V4L)i - remap_pfn_range failed\n", - ZR_DEVNAME(zr)); - res = -EAGAIN; - goto v4l_mmap_unlock_and_return; - } - size -= todo; - start += todo; - fh->v4l_buffers.buffer[i].map = map; - if (size == 0) - break; - } - v4l_mmap_unlock_and_return: - mutex_unlock(&zr->resource_lock); - - break; - - default: - dprintk(1, - KERN_ERR - "%s: zoran_mmap() - internal error - unknown map mode %d\n", - ZR_DEVNAME(zr), fh->map_mode); - break; } +mmap_unlock_and_return: + mutex_unlock(&zr->resource_lock); + return 0; } +static const struct v4l2_ioctl_ops zoran_ioctl_ops = { + .vidioc_querycap = zoran_querycap, + .vidioc_cropcap = zoran_cropcap, + .vidioc_s_crop = zoran_s_crop, + .vidioc_g_crop = zoran_g_crop, + .vidioc_enum_input = zoran_enum_input, + .vidioc_g_input = zoran_g_input, + .vidioc_s_input = zoran_s_input, + .vidioc_enum_output = zoran_enum_output, + .vidioc_g_output = zoran_g_output, + .vidioc_s_output = zoran_s_output, + .vidioc_g_fbuf = zoran_g_fbuf, + .vidioc_s_fbuf = zoran_s_fbuf, + .vidioc_g_std = zoran_g_std, + .vidioc_s_std = zoran_s_std, + .vidioc_g_jpegcomp = zoran_g_jpegcomp, + .vidioc_s_jpegcomp = zoran_s_jpegcomp, + .vidioc_overlay = zoran_overlay, + .vidioc_reqbufs = zoran_reqbufs, + .vidioc_querybuf = zoran_querybuf, + .vidioc_qbuf = zoran_qbuf, + .vidioc_dqbuf = zoran_dqbuf, + .vidioc_streamon = zoran_streamon, + .vidioc_streamoff = zoran_streamoff, + .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = zoran_enum_fmt_vid_out, + .vidioc_enum_fmt_vid_overlay = zoran_enum_fmt_vid_overlay, + .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap, + .vidioc_g_fmt_vid_out = zoran_g_fmt_vid_out, + .vidioc_g_fmt_vid_overlay = zoran_g_fmt_vid_overlay, + .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap, + .vidioc_s_fmt_vid_out = zoran_s_fmt_vid_out, + .vidioc_s_fmt_vid_overlay = zoran_s_fmt_vid_overlay, + .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap, + .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out, + .vidioc_try_fmt_vid_overlay = zoran_try_fmt_vid_overlay, + .vidioc_queryctrl = zoran_queryctrl, + .vidioc_s_ctrl = zoran_s_ctrl, + .vidioc_g_ctrl = zoran_g_ctrl, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidioc_default = zoran_default, + .vidiocgmbuf = zoran_vidiocgmbuf, +#endif +}; + static const struct v4l2_file_operations zoran_fops = { .owner = THIS_MODULE, .open = zoran_open, .release = zoran_close, - .ioctl = zoran_ioctl, + .ioctl = video_ioctl2, .read = zoran_read, .write = zoran_write, .mmap = zoran_mmap, @@ -4596,7 +3397,9 @@ static const struct v4l2_file_operations zoran_fops = { struct video_device zoran_template __devinitdata = { .name = ZORAN_NAME, .fops = &zoran_fops, + .ioctl_ops = &zoran_ioctl_ops, .release = &zoran_vdev_release, + .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, .minor = -1 }; diff --git a/drivers/media/video/zoran/zoran_procfs.c b/drivers/media/video/zoran/zoran_procfs.c index 870bc5a..f1423b7 100644 --- a/drivers/media/video/zoran/zoran_procfs.c +++ b/drivers/media/video/zoran/zoran_procfs.c @@ -36,7 +36,7 @@ #include <linux/pci.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <linux/spinlock.h> #include <linux/sem.h> #include <linux/seq_file.h> diff --git a/drivers/media/video/zoran/zr36016.c b/drivers/media/video/zoran/zr36016.c index 00d132b..21c088e 100644 --- a/drivers/media/video/zoran/zr36016.c +++ b/drivers/media/video/zoran/zr36016.c @@ -34,15 +34,10 @@ #include <linux/types.h> #include <linux/wait.h> -/* includes for structures and defines regarding video - #include<linux/videodev.h> */ - /* I/O commands, error codes */ #include <asm/io.h> -//#include<errno.h> /* v4l API */ -#include <linux/videodev.h> /* headerfile of this module */ #include"zr36016.h" diff --git a/drivers/media/video/zoran/zr36050.c b/drivers/media/video/zoran/zr36050.c index cf8b271..639dd87 100644 --- a/drivers/media/video/zoran/zr36050.c +++ b/drivers/media/video/zoran/zr36050.c @@ -34,12 +34,8 @@ #include <linux/types.h> #include <linux/wait.h> -/* includes for structures and defines regarding video - #include<linux/videodev.h> */ - /* I/O commands, error codes */ #include <asm/io.h> -//#include<errno.h> /* headerfile of this module */ #include "zr36050.h" diff --git a/drivers/media/video/zoran/zr36060.c b/drivers/media/video/zoran/zr36060.c index 8e74054..008746f 100644 --- a/drivers/media/video/zoran/zr36060.c +++ b/drivers/media/video/zoran/zr36060.c @@ -34,12 +34,8 @@ #include <linux/types.h> #include <linux/wait.h> -/* includes for structures and defines regarding video - #include<linux/videodev.h> */ - /* I/O commands, error codes */ #include <asm/io.h> -//#include<errno.h> /* headerfile of this module */ #include "zr36060.h" diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index 9302356..221409f 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -96,6 +96,7 @@ static struct usb_device_id device_table[] = { {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 }, {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, + {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD2 }, {} /* Terminating entry */ }; @@ -425,7 +426,6 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt, static int zr364xx_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - memset(cap, 0, sizeof(*cap)); strcpy(cap->driver, DRIVER_DESC); cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; return 0; @@ -436,8 +436,6 @@ static int zr364xx_vidioc_enum_input(struct file *file, void *priv, { if (i->index != 0) return -EINVAL; - memset(i, 0, sizeof(*i)); - i->index = 0; strcpy(i->name, DRIVER_DESC " Camera"); i->type = V4L2_INPUT_TYPE_CAMERA; return 0; @@ -529,11 +527,6 @@ static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, { if (f->index > 0) return -EINVAL; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(f, 0, sizeof(*f)); - f->index = 0; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->flags = V4L2_FMT_FLAG_COMPRESSED; strcpy(f->description, "JPEG"); f->pixelformat = V4L2_PIX_FMT_JPEG; @@ -550,8 +543,6 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -ENODEV; cam = video_get_drvdata(vdev); - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) return -EINVAL; if (f->fmt.pix.field != V4L2_FIELD_ANY && @@ -577,10 +568,6 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, return -ENODEV; cam = video_get_drvdata(vdev); - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.width = cam->width; @@ -602,8 +589,6 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, return -ENODEV; cam = video_get_drvdata(vdev); - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) return -EINVAL; if (f->fmt.pix.field != V4L2_FIELD_ANY && |