summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media/video')
-rw-r--r--linux/drivers/media/video/Kconfig18
-rw-r--r--linux/drivers/media/video/Makefile6
-rw-r--r--linux/drivers/media/video/cs53l32a.c188
-rw-r--r--linux/drivers/media/video/cx18/Kconfig2
-rw-r--r--linux/drivers/media/video/cx18/cx18-av-vbi.c5
-rw-r--r--linux/drivers/media/video/cx18/cx18-cards.c8
-rw-r--r--linux/drivers/media/video/cx18/cx18-cards.h5
-rw-r--r--linux/drivers/media/video/cx18/cx18-controls.c5
-rw-r--r--linux/drivers/media/video/cx18/cx18-driver.c214
-rw-r--r--linux/drivers/media/video/cx18/cx18-driver.h25
-rw-r--r--linux/drivers/media/video/cx18/cx18-dvb.c4
-rw-r--r--linux/drivers/media/video/cx18/cx18-fileops.c37
-rw-r--r--linux/drivers/media/video/cx18/cx18-firmware.c101
-rw-r--r--linux/drivers/media/video/cx18/cx18-ioctl.c14
-rw-r--r--linux/drivers/media/video/cx18/cx18-mailbox.c34
-rw-r--r--linux/drivers/media/video/cx18/cx18-mailbox.h7
-rw-r--r--linux/drivers/media/video/cx18/cx18-queue.c82
-rw-r--r--linux/drivers/media/video/cx18/cx18-queue.h19
-rw-r--r--linux/drivers/media/video/cx18/cx18-scb.c1
-rw-r--r--linux/drivers/media/video/cx18/cx18-streams.c113
-rw-r--r--linux/drivers/media/video/cx18/cx18-streams.h3
-rw-r--r--linux/drivers/media/video/cx18/cx18-vbi.c5
-rw-r--r--linux/drivers/media/video/cx18/cx18-version.h2
-rw-r--r--linux/drivers/media/video/cx18/cx23418.h6
-rw-r--r--linux/drivers/media/video/cx23885/Kconfig4
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-cards.c12
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-dvb.c1
-rw-r--r--linux/drivers/media/video/cx23885/cx23885.h1
-rw-r--r--linux/drivers/media/video/cx25840/cx25840-audio.c14
-rw-r--r--linux/drivers/media/video/cx25840/cx25840-core.c447
-rw-r--r--linux/drivers/media/video/cx25840/cx25840-core.h7
-rw-r--r--linux/drivers/media/video/cx25840/cx25840-firmware.c2
-rw-r--r--linux/drivers/media/video/cx25840/cx25840-vbi.c2
-rw-r--r--linux/drivers/media/video/cx88/Kconfig2
-rw-r--r--linux/drivers/media/video/cx88/cx88-alsa.c3
-rw-r--r--linux/drivers/media/video/cx88/cx88-blackbird.c12
-rw-r--r--linux/drivers/media/video/cx88/cx88-dvb.c48
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-cards.c8
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-dvb.c1
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-video.c107
-rw-r--r--linux/drivers/media/video/gspca/gspca.c65
-rw-r--r--linux/drivers/media/video/gspca/gspca.h4
-rw-r--r--linux/drivers/media/video/gspca/ov519.c13
-rw-r--r--linux/drivers/media/video/gspca/ov534.c588
-rw-r--r--linux/drivers/media/video/gspca/pac7311.c1
-rw-r--r--linux/drivers/media/video/gspca/sonixj.c281
-rw-r--r--linux/drivers/media/video/gspca/stk014.c6
-rw-r--r--linux/drivers/media/video/gspca/vc032x.c1025
-rw-r--r--linux/drivers/media/video/gspca/zc3xx-reg.h8
-rw-r--r--linux/drivers/media/video/gspca/zc3xx.c807
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-controls.c16
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-driver.c214
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-driver.h52
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-fileops.c44
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-gpio.c324
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-gpio.h3
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-i2c.c338
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-i2c.h13
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-ioctl.c73
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-routing.c12
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-streams.c13
-rw-r--r--linux/drivers/media/video/ivtv/ivtv-vbi.c17
-rw-r--r--linux/drivers/media/video/ivtv/ivtvfb.c91
-rw-r--r--linux/drivers/media/video/m52790.c176
-rw-r--r--linux/drivers/media/video/msp3400-driver.c406
-rw-r--r--linux/drivers/media/video/msp3400-driver.h7
-rw-r--r--linux/drivers/media/video/msp3400-kthreads.c34
-rw-r--r--linux/drivers/media/video/omap24xxcam-dma.c601
-rw-r--r--linux/drivers/media/video/omap24xxcam.c1908
-rw-r--r--linux/drivers/media/video/omap24xxcam.h593
-rw-r--r--linux/drivers/media/video/pvrusb2/Kconfig2
-rw-r--r--linux/drivers/media/video/saa7115.c763
-rw-r--r--linux/drivers/media/video/saa7127.c421
-rw-r--r--linux/drivers/media/video/saa7134/Kconfig4
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-cards.c6
-rw-r--r--linux/drivers/media/video/saa717x.c610
-rw-r--r--linux/drivers/media/video/sh_mobile_ceu_camera.c2
-rw-r--r--linux/drivers/media/video/tuner-core.c391
-rw-r--r--linux/drivers/media/video/tvp514x.c1569
-rw-r--r--linux/drivers/media/video/tvp514x_regs.h297
-rw-r--r--linux/drivers/media/video/upd64031a.c193
-rw-r--r--linux/drivers/media/video/upd64083.c166
-rw-r--r--linux/drivers/media/video/uvc/uvc_ctrl.c143
-rw-r--r--linux/drivers/media/video/uvc/uvc_driver.c6
-rw-r--r--linux/drivers/media/video/uvc/uvc_v4l2.c4
-rw-r--r--linux/drivers/media/video/uvc/uvc_video.c3
-rw-r--r--linux/drivers/media/video/uvc/uvcvideo.h10
-rw-r--r--linux/drivers/media/video/v4l2-common.c258
-rw-r--r--linux/drivers/media/video/v4l2-compat-ioctl32.c4
-rw-r--r--linux/drivers/media/video/v4l2-device.c86
-rw-r--r--linux/drivers/media/video/v4l2-subdev.c108
-rw-r--r--linux/drivers/media/video/vp27smpx.c126
-rw-r--r--linux/drivers/media/video/wm8739.c188
-rw-r--r--linux/drivers/media/video/wm8775.c221
94 files changed, 10899 insertions, 3990 deletions
diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig
index c4ee10c37..cc224744b 100644
--- a/linux/drivers/media/video/Kconfig
+++ b/linux/drivers/media/video/Kconfig
@@ -361,6 +361,17 @@ config VIDEO_SAA7191
To compile this driver as a module, choose M here: the
module will be called saa7191.
+config VIDEO_TVP514X
+ tristate "Texas Instruments TVP514x video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the TI TVP5146/47
+ decoder. It is currently working with the TI OMAP3 camera
+ controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp514x.
+
config VIDEO_TVP5150
tristate "Texas Instruments TVP5150 video decoder"
depends on VIDEO_V4L2 && I2C
@@ -770,6 +781,13 @@ config VIDEO_SH_MOBILE_CEU
---help---
This is a v4l2 driver for the SuperH Mobile CEU Interface
+config VIDEO_OMAP2
+ tristate "OMAP2 Camera Capture Interface driver"
+ depends on VIDEO_DEV && ARCH_OMAP2
+ select VIDEOBUF_DMA_SG
+ ---help---
+ This is a v4l2 driver for the TI OMAP2 camera capture interface
+
#
# USB Multimedia device configuration
#
diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile
index 53d43f9e0..84a2be0cb 100644
--- a/linux/drivers/media/video/Makefile
+++ b/linux/drivers/media/video/Makefile
@@ -8,7 +8,9 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o
stkwebcam-objs := stk-webcam.o stk-sensor.o
-videodev-objs := v4l2-dev.o v4l2-ioctl.o
+omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
+
+videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-subdev.o
obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-compat-ioctl32.o v4l2-int-device.o
@@ -67,6 +69,7 @@ obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
+obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
obj-$(CONFIG_VIDEO_CS5345) += cs5345.o
@@ -130,6 +133,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/
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_MT9M001) += mt9m001.o
obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
diff --git a/linux/drivers/media/video/cs53l32a.c b/linux/drivers/media/video/cs53l32a.c
index 68f31270c..0620c4af4 100644
--- a/linux/drivers/media/video/cs53l32a.c
+++ b/linux/drivers/media/video/cs53l32a.c
@@ -27,7 +27,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "compat.h"
@@ -48,84 +48,104 @@ I2C_CLIENT_INSMOD;
/* ----------------------------------------------------------------------- */
-static int cs53l32a_write(struct i2c_client *client, u8 reg, u8 value)
+static int cs53l32a_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 int cs53l32a_read(struct i2c_client *client, u8 reg)
+static int cs53l32a_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 cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int cs53l32a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct v4l2_routing *route = arg;
- struct v4l2_control *ctrl = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- route->input = (cs53l32a_read(client, 0x01) >> 4) & 3;
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- /* There are 2 physical inputs, but the second input can be
- placed in two modes, the first mode bypasses the PGA (gain),
- the second goes through the PGA. Hence there are three
- possible inputs to choose from. */
- if (route->input > 2) {
- v4l_err(client, "Invalid input %d.\n", route->input);
- return -EINVAL;
- }
- cs53l32a_write(client, 0x01, 0x01 + (route->input << 4));
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- ctrl->value = (cs53l32a_read(client, 0x03) & 0xc0) != 0;
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- ctrl->value = (s8)cs53l32a_read(client, 0x04);
- break;
-
- case VIDIOC_S_CTRL:
- if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
- cs53l32a_write(client, 0x03, ctrl->value ? 0xf0 : 0x30);
- break;
- }
- if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
- return -EINVAL;
- if (ctrl->value > 12 || ctrl->value < -96)
- return -EINVAL;
- cs53l32a_write(client, 0x04, (u8) ctrl->value);
- cs53l32a_write(client, 0x05, (u8) ctrl->value);
- break;
-
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_CS53l32A, 0);
-
- case VIDIOC_LOG_STATUS:
- {
- u8 v = cs53l32a_read(client, 0x01);
- u8 m = cs53l32a_read(client, 0x03);
- s8 vol = cs53l32a_read(client, 0x04);
-
- v4l_info(client, "Input: %d%s\n", (v >> 4) & 3,
- (m & 0xC0) ? " (muted)" : "");
- v4l_info(client, "Volume: %d dB\n", vol);
- break;
- }
-
- default:
+ /* There are 2 physical inputs, but the second input can be
+ placed in two modes, the first mode bypasses the PGA (gain),
+ the second goes through the PGA. Hence there are three
+ possible inputs to choose from. */
+ if (route->input > 2) {
+ v4l2_err(sd, "Invalid input %d.\n", route->input);
return -EINVAL;
}
+ cs53l32a_write(sd, 0x01, 0x01 + (route->input << 4));
+ return 0;
+}
+
+static int cs53l32a_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ ctrl->value = (cs53l32a_read(sd, 0x03) & 0xc0) != 0;
+ return 0;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ ctrl->value = (s8)cs53l32a_read(sd, 0x04);
+ return 0;
+}
+
+static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ if (ctrl->id == V4L2_CID_AUDIO_MUTE) {
+ cs53l32a_write(sd, 0x03, ctrl->value ? 0xf0 : 0x30);
+ return 0;
+ }
+ if (ctrl->id != V4L2_CID_AUDIO_VOLUME)
+ return -EINVAL;
+ if (ctrl->value > 12 || ctrl->value < -96)
+ return -EINVAL;
+ cs53l32a_write(sd, 0x04, (u8) ctrl->value);
+ cs53l32a_write(sd, 0x05, (u8) ctrl->value);
return 0;
}
+static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client,
+ chip, V4L2_IDENT_CS53l32A, 0);
+}
+
+static int cs53l32a_log_status(struct v4l2_subdev *sd)
+{
+ u8 v = cs53l32a_read(sd, 0x01);
+ u8 m = cs53l32a_read(sd, 0x03);
+ s8 vol = cs53l32a_read(sd, 0x04);
+
+ v4l2_info(sd, "Input: %d%s\n", (v >> 4) & 3,
+ (m & 0xC0) ? " (muted)" : "");
+ v4l2_info(sd, "Volume: %d dB\n", vol);
+ 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 = {
+ .log_status = cs53l32a_log_status,
+ .g_chip_ident = cs53l32a_g_chip_ident,
+ .g_ctrl = cs53l32a_g_ctrl,
+ .s_ctrl = cs53l32a_s_ctrl,
+};
+
+static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = {
+ .s_routing = cs53l32a_s_routing,
+};
+
+static const struct v4l2_subdev_ops cs53l32a_ops = {
+ .core = &cs53l32a_core_ops,
+ .audio = &cs53l32a_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -138,6 +158,7 @@ static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg)
static int cs53l32a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ struct v4l2_subdev *sd;
int i;
/* Check if the adapter supports the needed features */
@@ -154,32 +175,46 @@ static int cs53l32a_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, &cs53l32a_ops);
+
for (i = 1; i <= 7; i++) {
- u8 v = cs53l32a_read(client, i);
+ u8 v = cs53l32a_read(sd, i);
- v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v);
+ v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
}
/* Set cs53l32a internal register for Adaptec 2010/2410 setup */
- cs53l32a_write(client, 0x01, (u8) 0x21);
- cs53l32a_write(client, 0x02, (u8) 0x29);
- cs53l32a_write(client, 0x03, (u8) 0x30);
- cs53l32a_write(client, 0x04, (u8) 0x00);
- cs53l32a_write(client, 0x05, (u8) 0x00);
- cs53l32a_write(client, 0x06, (u8) 0x00);
- cs53l32a_write(client, 0x07, (u8) 0x00);
+ cs53l32a_write(sd, 0x01, (u8) 0x21);
+ cs53l32a_write(sd, 0x02, (u8) 0x29);
+ cs53l32a_write(sd, 0x03, (u8) 0x30);
+ cs53l32a_write(sd, 0x04, (u8) 0x00);
+ cs53l32a_write(sd, 0x05, (u8) 0x00);
+ cs53l32a_write(sd, 0x06, (u8) 0x00);
+ cs53l32a_write(sd, 0x07, (u8) 0x00);
/* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */
for (i = 1; i <= 7; i++) {
- u8 v = cs53l32a_read(client, i);
+ u8 v = cs53l32a_read(sd, i);
- v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v);
+ v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v);
}
return 0;
}
+static int cs53l32a_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(sd);
+ return 0;
+}
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
static const struct i2c_device_id cs53l32a_id[] = {
{ "cs53l32a", 0 },
@@ -192,6 +227,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "cs53l32a",
.driverid = I2C_DRIVERID_CS53L32A,
.command = cs53l32a_command,
+ .remove = cs53l32a_remove,
.probe = cs53l32a_probe,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
.id_table = cs53l32a_id,
diff --git a/linux/drivers/media/video/cx18/Kconfig b/linux/drivers/media/video/cx18/Kconfig
index ef48565de..8940b5387 100644
--- a/linux/drivers/media/video/cx18/Kconfig
+++ b/linux/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 !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE
---help---
This is a video4linux driver for Conexant cx23418 based
PCI combo video recorder devices.
diff --git a/linux/drivers/media/video/cx18/cx18-av-vbi.c b/linux/drivers/media/video/cx18/cx18-av-vbi.c
index 02fdf57bb..1527ea4f6 100644
--- a/linux/drivers/media/video/cx18/cx18-av-vbi.c
+++ b/linux/drivers/media/video/cx18/cx18-av-vbi.c
@@ -141,10 +141,11 @@ int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
u8 lcr[24];
fmt = arg;
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE &&
+ fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE)
return -EINVAL;
svbi = &fmt->fmt.sliced;
- if (svbi->service_set == 0) {
+ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
/* raw VBI */
memset(svbi, 0, sizeof(*svbi));
diff --git a/linux/drivers/media/video/cx18/cx18-cards.c b/linux/drivers/media/video/cx18/cx18-cards.c
index 23bd871b7..10cddba3e 100644
--- a/linux/drivers/media/video/cx18/cx18-cards.c
+++ b/linux/drivers/media/video/cx18/cx18-cards.c
@@ -51,7 +51,7 @@ 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 = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_CS5345,
@@ -97,7 +97,7 @@ 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 = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_CS5345,
@@ -152,7 +152,7 @@ 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 = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_all = CX18_HW_TUNER,
@@ -249,7 +249,7 @@ 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 = "VBI is not yet supported\n",
+ .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_muxer = CX18_HW_GPIO,
diff --git a/linux/drivers/media/video/cx18/cx18-cards.h b/linux/drivers/media/video/cx18/cx18-cards.h
index a54aae9ed..6fa7bcb42 100644
--- a/linux/drivers/media/video/cx18/cx18-cards.h
+++ b/linux/drivers/media/video/cx18/cx18-cards.h
@@ -48,8 +48,9 @@
/* 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_AUDIO | V4L2_CAP_READWRITE | \
+ V4L2_CAP_VBI_CAPTURE)
+/* | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
struct cx18_card_video_input {
u8 video_type; /* video input type */
diff --git a/linux/drivers/media/video/cx18/cx18-controls.c b/linux/drivers/media/video/cx18/cx18-controls.c
index f46c7e5ed..17edf305d 100644
--- a/linux/drivers/media/video/cx18/cx18-controls.c
+++ b/linux/drivers/media/video/cx18/cx18-controls.c
@@ -259,6 +259,7 @@ 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) {
+ 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);
@@ -278,7 +279,9 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
fmt.fmt.pix.height = cx->params.height;
cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
}
- err = cx2341x_update(cx, cx18_api_func, &cx->params, &p);
+ 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);
cx->params = p;
diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c
index 57fcce84b..e447b4ce7 100644
--- a/linux/drivers/media/video/cx18/cx18-driver.c
+++ b/linux/drivers/media/video/cx18/cx18-driver.c
@@ -83,12 +83,28 @@ static char secam[] = "--";
static char ntsc[] = "-";
/* Buffers */
-static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
+static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
+static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS;
static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
+static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
+static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
+static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
+static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
+/* VBI bufsize based on standards supported by card tuner for now */
+static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
+
+static int enc_ts_bufs = -1;
+static int enc_mpg_bufs = -1;
+static int enc_idx_bufs = -1;
+static int enc_yuv_bufs = -1;
+static int enc_vbi_bufs = -1;
+static int enc_pcm_bufs = -1;
+
+
static int cx18_pci_latency = 1;
static int mmio_ndelay;
@@ -108,12 +124,27 @@ module_param(retry_mmio, int, 0644);
module_param(cx18_pci_latency, int, 0644);
module_param(cx18_first_minor, int, 0644);
-module_param(enc_mpg_buffers, int, 0644);
module_param(enc_ts_buffers, int, 0644);
+module_param(enc_mpg_buffers, int, 0644);
+module_param(enc_idx_buffers, int, 0644);
module_param(enc_yuv_buffers, int, 0644);
module_param(enc_vbi_buffers, int, 0644);
module_param(enc_pcm_buffers, int, 0644);
+module_param(enc_ts_bufsize, int, 0644);
+module_param(enc_mpg_bufsize, int, 0644);
+module_param(enc_idx_bufsize, int, 0644);
+module_param(enc_yuv_bufsize, int, 0644);
+/* VBI bufsize based on standards supported by card tuner for now */
+module_param(enc_pcm_bufsize, int, 0644);
+
+module_param(enc_ts_bufs, int, 0644);
+module_param(enc_mpg_bufs, int, 0644);
+module_param(enc_idx_bufs, int, 0644);
+module_param(enc_yuv_bufs, int, 0644);
+module_param(enc_vbi_bufs, int, 0644);
+module_param(enc_pcm_bufs, int, 0644);
+
MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
"\t\t\tsee tuner.h for values");
MODULE_PARM_DESC(radio,
@@ -154,21 +185,57 @@ MODULE_PARM_DESC(retry_mmio,
MODULE_PARM_DESC(mmio_ndelay,
"(Deprecated) MMIO accesses are now never purposely delayed\n"
"\t\t\tEffectively: 0 ns");
-MODULE_PARM_DESC(enc_mpg_buffers,
- "Encoder MPG Buffers (in MB)\n"
- "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
MODULE_PARM_DESC(enc_ts_buffers,
- "Encoder TS Buffers (in MB)\n"
+ "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
+MODULE_PARM_DESC(enc_ts_bufsize,
+ "Size of an encoder TS buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE));
+MODULE_PARM_DESC(enc_ts_bufs,
+ "Number of encoder TS buffers\n"
+ "\t\t\tDefault is computed from other enc_ts_* parameters");
+MODULE_PARM_DESC(enc_mpg_buffers,
+ "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
+MODULE_PARM_DESC(enc_mpg_bufsize,
+ "Size of an encoder MPG buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE));
+MODULE_PARM_DESC(enc_mpg_bufs,
+ "Number of encoder MPG buffers\n"
+ "\t\t\tDefault is computed from other enc_mpg_* parameters");
+MODULE_PARM_DESC(enc_idx_buffers,
+ "Encoder IDX buffer memory (MB). (enc_idx_bufs can override)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFFERS));
+MODULE_PARM_DESC(enc_idx_bufsize,
+ "Size of an encoder IDX buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFSIZE));
+MODULE_PARM_DESC(enc_idx_bufs,
+ "Number of encoder IDX buffers\n"
+ "\t\t\tDefault is computed from other enc_idx_* parameters");
MODULE_PARM_DESC(enc_yuv_buffers,
- "Encoder YUV Buffers (in MB)\n"
+ "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
+MODULE_PARM_DESC(enc_yuv_bufsize,
+ "Size of an encoder YUV buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFSIZE));
+MODULE_PARM_DESC(enc_yuv_bufs,
+ "Number of encoder YUV buffers\n"
+ "\t\t\tDefault is computed from other enc_yuv_* parameters");
MODULE_PARM_DESC(enc_vbi_buffers,
- "Encoder VBI Buffers (in MB)\n"
+ "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
+MODULE_PARM_DESC(enc_vbi_bufs,
+ "Number of encoder VBI buffers\n"
+ "\t\t\tDefault is computed from enc_vbi_buffers & tuner std");
MODULE_PARM_DESC(enc_pcm_buffers,
- "Encoder PCM buffers (in MB)\n"
+ "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
+MODULE_PARM_DESC(enc_pcm_bufsize,
+ "Size of an encoder PCM buffer (kB)\n"
+ "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE));
+MODULE_PARM_DESC(enc_pcm_bufs,
+ "Number of encoder PCM buffers\n"
+ "\t\t\tDefault is computed from other enc_pcm_* parameters");
MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card");
@@ -361,11 +428,65 @@ static void cx18_process_options(struct cx18 *cx)
{
int i, j;
- cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
+ cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */
+
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs;
+ cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */
+
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize;
+ 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_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 */
+ 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) {
+ 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;
+ }
+ continue;
+ }
+ if (cx->stream_buffers[i] < 0) {
+ cx->stream_buffers[i] = cx->options.megabytes[i] * 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;
+ }
+ 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];
@@ -478,15 +599,52 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
init_waitqueue_head(&cx->dma_waitq);
/* VBI */
- cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
- cx->vbi.raw_size = 1456;
- cx->vbi.raw_decoder_line_size = 1456;
- cx->vbi.raw_decoder_sav_odd_field = 0x20;
- cx->vbi.raw_decoder_sav_even_field = 0x60;
- cx->vbi.sliced_decoder_line_size = 272;
- cx->vbi.sliced_decoder_sav_odd_field = 0xB0;
- cx->vbi.sliced_decoder_sav_even_field = 0xF0;
+
+ /*
+ * 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;
}
@@ -519,6 +677,7 @@ static void __devinit cx18_init_struct2(struct cx18 *cx)
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;
}
@@ -724,6 +883,7 @@ static int __devinit cx18_probe(struct pci_dev *dev,
goto free_i2c;
}
cx18_init_memory(cx);
+ cx18_init_scb(cx);
/* Register IRQ */
retval = request_irq(cx->dev->irq, cx18_irq_handler,
@@ -773,13 +933,23 @@ static int __devinit cx18_probe(struct pci_dev *dev,
}
cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200;
- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000;
+ /*
+ * 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);
+
if (cx->options.radio > 0)
cx->v4l2_cap |= V4L2_CAP_RADIO;
diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h
index 94c196a91..0d2edebc3 100644
--- a/linux/drivers/media/video/cx18/cx18-driver.h
+++ b/linux/drivers/media/video/cx18/cx18-driver.h
@@ -115,6 +115,17 @@
#define CX18_DEFAULT_ENC_VBI_BUFFERS 1
#define CX18_DEFAULT_ENC_PCM_BUFFERS 1
+/* Maximum firmware DMA buffers per stream */
+#define CX18_MAX_FW_MDLS_PER_STREAM 63
+
+/* DMA buffer, default size in kB allocated */
+#define CX18_DEFAULT_ENC_TS_BUFSIZE 32
+#define CX18_DEFAULT_ENC_MPG_BUFSIZE 32
+#define CX18_DEFAULT_ENC_IDX_BUFSIZE 32
+#define CX18_DEFAULT_ENC_YUV_BUFSIZE 128
+/* Default VBI bufsize based on standards supported by card tuner for now */
+#define CX18_DEFAULT_ENC_PCM_BUFSIZE 4
+
/* i2c stuff */
#define I2C_CLIENTS_MAX 16
@@ -244,7 +255,8 @@ struct cx18_scb; /* forward reference */
#define CX18_MAX_MDL_ACKS 2
-#define CX18_MAX_EPU_WORK_ORDERS 70 /* CPU_DE_RELEASE_MDL bursts 63 commands */
+#define CX18_MAX_EPU_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7)
+/* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */
#define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1
#define CX18_F_EWO_MB_STALE_WHILE_PROC 0x2
@@ -289,8 +301,8 @@ struct cx18_stream {
/* Buffer Queues */
struct cx18_queue q_free; /* free buffers */
- struct cx18_queue q_full; /* full buffers */
- struct cx18_queue q_io; /* waiting for I/O */
+ struct cx18_queue q_busy; /* busy buffers - in use by firmware */
+ struct cx18_queue q_full; /* full buffers - data for user apps */
/* DVB / Digital Transport */
struct cx18_dvb dvb;
@@ -408,6 +420,7 @@ struct cx18 {
struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
struct cx18_options options; /* User options */
+ int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */
int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */
unsigned long i_flags; /* global cx18 flags */
@@ -492,4 +505,10 @@ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv);
/* First-open initialization: load firmware, etc. */
int cx18_init_on_first_open(struct cx18 *cx);
+/* Test if the current VBI mode is raw (1) or sliced (0) */
+static inline int cx18_raw_vbi(const struct cx18 *cx)
+{
+ return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
+}
+
#endif /* CX18_DRIVER_H */
diff --git a/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c
index 034e09a37..bd5e6f3fd 100644
--- a/linux/drivers/media/video/cx18/cx18-dvb.c
+++ b/linux/drivers/media/video/cx18/cx18-dvb.c
@@ -217,6 +217,10 @@ int cx18_dvb_register(struct cx18_stream *stream)
dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
CX18_INFO("DVB Frontend registered\n");
+ CX18_INFO("Registered DVB adapter%d for %s (%d x %d kB)\n",
+ stream->dvb.dvb_adapter.num, stream->name,
+ stream->buffers, stream->buf_size/1024);
+
mutex_init(&dvb->feedlock);
dvb->enabled = 1;
return ret;
diff --git a/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c
index 45b5f402e..ef4589ef6 100644
--- a/linux/drivers/media/video/cx18/cx18-fileops.c
+++ b/linux/drivers/media/video/cx18/cx18-fileops.c
@@ -67,12 +67,11 @@ static int cx18_claim_stream(struct cx18_open_id *id, int type)
}
s->id = id->open_id;
- /* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI,
- CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
+ /* CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
(provided VBI insertion is on and sliced VBI is selected), for all
other streams we're done */
if (type == CX18_ENC_STREAM_TYPE_MPG &&
- cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) {
+ cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) {
vbi_type = CX18_ENC_STREAM_TYPE_VBI;
} else {
return 0;
@@ -224,8 +223,10 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
!test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
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_enqueue(s_vbi, buf, &s_vbi->q_free);
+ cx18_process_vbi_data(cx, buf,
+ s_vbi->dma_pts,
+ s_vbi->type);
+ cx18_stream_put_buf_fw(s_vbi, buf);
}
}
buf = &cx->vbi.sliced_mpeg_buf;
@@ -233,11 +234,6 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
return buf;
}
- /* do we have leftover data? */
- buf = cx18_dequeue(s, &s->q_io);
- if (buf)
- return buf;
-
/* do we have new data? */
buf = cx18_dequeue(s, &s->q_full);
if (buf) {
@@ -301,7 +297,7 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
if (len > ucount)
len = ucount;
if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
- cx->vbi.sliced_in->service_set && buf != &cx->vbi.sliced_mpeg_buf) {
+ !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) {
const char *start = buf->buf + buf->readpos;
const char *p = start + 1;
const u8 *q;
@@ -376,8 +372,7 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
/* Each VBI buffer is one frame, the v4l2 API says that for VBI the
frames should arrive one-by-one, so make sure we never output more
than one VBI frame at a time */
- if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
- cx->vbi.sliced_in->service_set)
+ if (s->type == CX18_ENC_STREAM_TYPE_VBI && !cx18_raw_vbi(cx))
single_frame = 1;
for (;;) {
@@ -404,16 +399,10 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
tot_count - tot_written);
if (buf != &cx->vbi.sliced_mpeg_buf) {
- if (buf->readpos == buf->bytesused) {
- cx18_buf_sync_for_device(s, buf);
- cx18_enqueue(s, buf, &s->q_free);
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5,
- s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] -
- cx->enc_mem,
- 1, buf->id, s->buf_size);
- } else
- cx18_enqueue(s, buf, &s->q_io);
+ if (buf->readpos == buf->bytesused)
+ cx18_stream_put_buf_fw(s, buf);
+ else
+ cx18_push(s, buf, &s->q_full);
} else if (buf->readpos == buf->bytesused) {
int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
@@ -557,7 +546,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
CX18_DEBUG_HI_FILE("Encoder poll\n");
poll_wait(filp, &s->waitq, wait);
- if (atomic_read(&s->q_full.buffers) || atomic_read(&s->q_io.buffers))
+ if (atomic_read(&s->q_full.buffers))
return POLLIN | POLLRDNORM;
if (eof)
return POLLHUP;
diff --git a/linux/drivers/media/video/cx18/cx18-firmware.c b/linux/drivers/media/video/cx18/cx18-firmware.c
index 8eac84314..e74f76d47 100644
--- a/linux/drivers/media/video/cx18/cx18-firmware.c
+++ b/linux/drivers/media/video/cx18/cx18-firmware.c
@@ -332,6 +332,10 @@ void cx18_init_memory(struct cx18 *cx)
int cx18_firmware_init(struct cx18 *cx)
{
+ u32 fw_entry_addr;
+ int sz, retries;
+ u32 api_args[MAX_MB_ARGUMENTS];
+
/* Allow chip to control CLKRUN */
cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK);
@@ -341,65 +345,62 @@ int cx18_firmware_init(struct cx18 *cx)
cx18_msleep_timeout(1, 0);
+ /* If the CPU is still running */
+ if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) {
+ CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__);
+ return -EIO;
+ }
+
cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
- /* Only if the processor is not running */
- if (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) {
- u32 fw_entry_addr = 0;
- int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
- cx->enc_mem, cx, &fw_entry_addr);
-
- if (sz <= 0)
- return sz;
-
- /* Clear bit0 for APU to start from 0 */
- cx18_write_reg(cx, cx18_read_reg(cx, 0xc72030) & ~1, 0xc72030);
-
- cx18_write_enc(cx, 0xE51FF004, 0); /* ldr pc, [pc, #-4] */
- cx18_write_enc(cx, fw_entry_addr, 4);
-
- /* Start APU */
- cx18_write_reg_expect(cx, 0x00010000, CX18_PROC_SOFT_RESET,
- 0x00000000, 0x00010001);
- cx18_msleep_timeout(500, 0);
-
- sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
- cx->enc_mem, cx);
-
- if (sz > 0) {
- int retries = 0;
-
- /* start the CPU */
- cx18_write_reg_expect(cx,
- 0x00080000, CX18_PROC_SOFT_RESET,
- 0x00000000, 0x00080008);
- while (retries++ < 50) { /* Loop for max 500mS */
- if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET)
- & 1) == 0)
- break;
- cx18_msleep_timeout(10, 0);
- }
- cx18_msleep_timeout(200, 0);
- if (retries == 51) {
- CX18_ERR("Could not start the CPU\n");
- return -EIO;
- }
- }
- if (sz <= 0)
- return -EIO;
+ sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx);
+ if (sz <= 0)
+ return sz;
+
+ /* The SCB & IPC area *must* be correct before starting the firmwares */
+ cx18_init_scb(cx);
+
+ fw_entry_addr = 0;
+ sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx,
+ &fw_entry_addr);
+ if (sz <= 0)
+ return sz;
+
+ /* Start the CPU. The CPU will take care of the APU for us. */
+ cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET,
+ 0x00000000, 0x00080008);
+
+ /* Wait up to 500 ms for the APU to come out of reset */
+ for (retries = 0;
+ retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1;
+ retries++)
+ cx18_msleep_timeout(10, 0);
+
+ cx18_msleep_timeout(200, 0);
+
+ if (retries == 50 &&
+ (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) {
+ CX18_ERR("Could not start the CPU\n");
+ return -EIO;
}
/*
- * The CPU firmware apparently sets up to receive an interrupt for it's
- * outgoing IRQ_CPU_TO_EPU_ACK to us (*boggle*). We get an interrupt
- * when it sends us an ack, but by the time we process it, that flag in
- * the SW2 status register has been cleared by the CPU firmware.
- * We'll prevent that not so useful behavior by clearing the CPU's
- * interrupt enables for Ack IRQ's we want to process.
+ * The CPU had once before set up to receive an interrupt for it's
+ * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an
+ * interrupt when it sends us an ack, but by the time we process it,
+ * that flag in the SW2 status register has been cleared by the CPU
+ * firmware. We'll prevent that not so useful condition from happening
+ * by clearing the CPU's interrupt enables for Ack IRQ's we want to
+ * process.
*/
cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
+ /* Try a benign command to see if the CPU is alive and well */
+ sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0);
+ if (sz < 0)
+ return sz;
+
/* initialize GPIO */
cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400);
return 0;
diff --git a/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c
index 5d9c1146e..c83fcc0c5 100644
--- a/linux/drivers/media/video/cx18/cx18-ioctl.c
+++ b/linux/drivers/media/video/cx18/cx18-ioctl.c
@@ -284,13 +284,12 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
if (ret)
return ret;
- if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
- cx->vbi.sliced_in->service_set &&
- atomic_read(&cx->ana_capturing) > 0)
+ if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
return -EBUSY;
cx->vbi.sliced_in->service_set = 0;
- cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
+ 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);
}
@@ -315,9 +314,9 @@ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
if (check_service_set(vbifmt, cx->is_50hz) == 0)
return -EINVAL;
- if (atomic_read(&cx->ana_capturing) > 0 &&
- cx->vbi.sliced_in->service_set == 0)
+ if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
return -EBUSY;
+ cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
return 0;
@@ -851,8 +850,7 @@ static int cx18_log_status(struct file *file, void *fh)
continue;
CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
s->name, s->s_flags,
- (s->buffers - atomic_read(&s->q_free.buffers))
- * 100 / s->buffers,
+ atomic_read(&s->q_full.buffers) * 100 / s->buffers,
(s->buffers * s->buf_size) / 1024, s->buffers);
}
CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c
index f62aee719..89a45f51f 100644
--- a/linux/drivers/media/video/cx18/cx18-mailbox.c
+++ b/linux/drivers/media/video/cx18/cx18-mailbox.c
@@ -82,8 +82,9 @@ static const struct cx18_api_info api_info[] = {
API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0),
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),
- API_ENTRY(CPU, CX18_APU_RESETAI, API_FAST),
API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW),
+ API_ENTRY(APU, CX18_APU_RESETAI, 0),
+ API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0),
API_ENTRY(0, 0, 0),
};
@@ -163,7 +164,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order)
* it's filled in).
*
* cx18_queue_get buf() will detect the lost buffers
- * and put them back in rotation eventually.
+ * and send them back to q_free for fw rotation eventually.
*/
if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) &&
!(id >= s->mdl_offset &&
@@ -174,33 +175,27 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order)
break;
}
buf = cx18_queue_get_buf(s, id, mdl_ack->data_used);
+
CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
if (buf == NULL) {
CX18_WARN("Could not find buf %d for stream %s\n",
id, s->name);
+ /* Put as many buffers as possible back into fw use */
+ cx18_stream_load_fw_queue(s);
continue;
}
- cx18_buf_sync_for_cpu(s, buf);
if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n",
buf->bytesused);
-
dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
buf->bytesused);
-
- cx18_buf_sync_for_device(s, buf);
- cx18_enqueue(s, buf, &s->q_free);
-
- if (s->handle != CX18_INVALID_TASK_HANDLE &&
- test_bit(CX18_F_S_STREAMING, &s->s_flags))
- cx18_vapi(cx,
- CX18_CPU_DE_SET_MDL, 5, s->handle,
- (void __iomem *)
- &cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
- 1, buf->id, s->buf_size);
- } else
- set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
+ }
+ /* Put as many buffers as possible back into fw use */
+ cx18_stream_load_fw_queue(s);
+ /* Put back TS buffer, since it was removed from all queues */
+ if (s->type == CX18_ENC_STREAM_TYPE_TS)
+ cx18_stream_put_buf_fw(s, buf);
}
wake_up(&cx->dma_waitq);
if (s->id != -1)
@@ -618,8 +613,9 @@ static int cx18_set_filter_param(struct cx18_stream *s)
int cx18_api_func(void *priv, u32 cmd, int in, int out,
u32 data[CX2341X_MBOX_MAX_DATA])
{
- struct cx18 *cx = priv;
- struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+ struct cx18_api_func_private *api_priv = priv;
+ struct cx18 *cx = api_priv->cx;
+ struct cx18_stream *s = api_priv->s;
switch (cmd) {
case CX2341X_ENC_SET_OUTPUT_PORT:
diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.h b/linux/drivers/media/video/cx18/cx18-mailbox.h
index 35104458e..a667f1ae4 100644
--- a/linux/drivers/media/video/cx18/cx18-mailbox.h
+++ b/linux/drivers/media/video/cx18/cx18-mailbox.h
@@ -79,6 +79,13 @@ struct cx18_mailbox {
u32 error;
};
+struct cx18_stream;
+
+struct cx18_api_func_private {
+ struct cx18 *cx;
+ struct cx18_stream *s;
+};
+
int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]);
int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
int args, ...);
diff --git a/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c
index 7b09c9a3e..8d9441e88 100644
--- a/linux/drivers/media/video/cx18/cx18-queue.c
+++ b/linux/drivers/media/video/cx18/cx18-queue.c
@@ -42,21 +42,33 @@ void cx18_queue_init(struct cx18_queue *q)
q->bytesused = 0;
}
-void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
- struct cx18_queue *q)
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q, int to_front)
{
- /* clear the buffer if it is going to be enqueued to the free queue */
- if (q == &s->q_free) {
+ /* clear the buffer if it is not to be enqueued to the full queue */
+ if (q != &s->q_full) {
buf->bytesused = 0;
buf->readpos = 0;
buf->b_flags = 0;
buf->skipped = 0;
}
+
mutex_lock(&s->qlock);
- list_add_tail(&buf->list, &q->list);
- atomic_inc(&q->buffers);
+
+ /* q_busy is restricted to a max buffer count imposed by firmware */
+ if (q == &s->q_busy &&
+ atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ q = &s->q_free;
+
+ if (to_front)
+ list_add(&buf->list, &q->list); /* LIFO */
+ else
+ list_add_tail(&buf->list, &q->list); /* FIFO */
q->bytesused += buf->bytesused - buf->readpos;
+ atomic_inc(&q->buffers);
+
mutex_unlock(&s->qlock);
+ return q;
}
struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
@@ -65,11 +77,11 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
mutex_lock(&s->qlock);
if (!list_empty(&q->list)) {
- buf = list_entry(q->list.next, struct cx18_buffer, list);
- list_del_init(q->list.next);
- atomic_dec(&q->buffers);
+ buf = list_first_entry(&q->list, struct cx18_buffer, list);
+ list_del_init(&buf->list);
q->bytesused -= buf->bytesused - buf->readpos;
buf->skipped = 0;
+ atomic_dec(&q->buffers);
}
mutex_unlock(&s->qlock);
return buf;
@@ -80,61 +92,51 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
{
struct cx18 *cx = s->cx;
struct cx18_buffer *buf;
+ struct cx18_buffer *tmp;
struct cx18_buffer *ret = NULL;
- struct list_head *p, *t;
- LIST_HEAD(r);
mutex_lock(&s->qlock);
- list_for_each_safe(p, t, &s->q_free.list) {
- buf = list_entry(p, struct cx18_buffer, list);
-
+ list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) {
if (buf->id != id) {
buf->skipped++;
- if (buf->skipped >= atomic_read(&s->q_free.buffers)-1) {
+ if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) {
/* buffer must have fallen out of rotation */
- atomic_dec(&s->q_free.buffers);
- list_move_tail(&buf->list, &r);
CX18_WARN("Skipped %s, buffer %d, %d "
"times - it must have dropped out of "
"rotation\n", s->name, buf->id,
buf->skipped);
+ /* move it to q_free */
+ list_move_tail(&buf->list, &s->q_free.list);
+ buf->bytesused = buf->readpos = buf->b_flags =
+ buf->skipped = 0;
+ atomic_dec(&s->q_busy.buffers);
+ atomic_inc(&s->q_free.buffers);
}
continue;
}
buf->bytesused = bytesused;
- atomic_dec(&s->q_free.buffers);
+ /* Sync the buffer before we release the qlock */
+ cx18_buf_sync_for_cpu(s, buf);
if (s->type == CX18_ENC_STREAM_TYPE_TS) {
/*
- * TS doesn't use q_full, but for sweeping up lost
- * buffers, we want the TS to requeue the buffer just
- * before sending the MDL back to the firmware, so we
- * pull it off the list here.
+ * TS doesn't use q_full. As we pull the buffer off of
+ * the queue here, the caller will have to put it back.
*/
list_del_init(&buf->list);
} else {
- atomic_inc(&s->q_full.buffers);
- s->q_full.bytesused += buf->bytesused;
+ /* Move buffer from q_busy to q_full */
list_move_tail(&buf->list, &s->q_full.list);
+ set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
+ s->q_full.bytesused += buf->bytesused;
+ atomic_inc(&s->q_full.buffers);
}
+ atomic_dec(&s->q_busy.buffers);
ret = buf;
break;
}
mutex_unlock(&s->qlock);
-
- /* Put lost buffers back into firmware transfer rotation */
- while (!list_empty(&r)) {
- buf = list_entry(r.next, struct cx18_buffer, list);
- list_del_init(r.next);
- cx18_enqueue(s, buf, &s->q_free);
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
- 1, buf->id, s->buf_size);
- CX18_INFO("Returning %s, buffer %d back to transfer rotation\n",
- s->name, buf->id);
- /* and there was much rejoicing... */
- }
return ret;
}
@@ -148,8 +150,8 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
mutex_lock(&s->qlock);
while (!list_empty(&q->list)) {
- buf = list_entry(q->list.next, struct cx18_buffer, list);
- list_move_tail(q->list.next, &s->q_free.list);
+ buf = list_first_entry(&q->list, struct cx18_buffer, list);
+ list_move_tail(&buf->list, &s->q_free.list);
buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0;
atomic_inc(&s->q_free.buffers);
}
@@ -159,7 +161,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
void cx18_flush_queues(struct cx18_stream *s)
{
- cx18_queue_flush(s, &s->q_io);
+ cx18_queue_flush(s, &s->q_busy);
cx18_queue_flush(s, &s->q_full);
}
diff --git a/linux/drivers/media/video/cx18/cx18-queue.h b/linux/drivers/media/video/cx18/cx18-queue.h
index ff50a2b7e..456cec3bc 100644
--- a/linux/drivers/media/video/cx18/cx18-queue.h
+++ b/linux/drivers/media/video/cx18/cx18-queue.h
@@ -43,9 +43,24 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
void cx18_buf_swap(struct cx18_buffer *buf);
/* cx18_queue utility functions */
+struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q, int to_front);
+
+static inline
+struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q)
+{
+ return _cx18_enqueue(s, buf, q, 0); /* FIFO */
+}
+
+static inline
+struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf,
+ struct cx18_queue *q)
+{
+ return _cx18_enqueue(s, buf, q, 1); /* LIFO */
+}
+
void cx18_queue_init(struct cx18_queue *q);
-void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
- struct cx18_queue *q);
struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
u32 bytesused);
diff --git a/linux/drivers/media/video/cx18/cx18-scb.c b/linux/drivers/media/video/cx18/cx18-scb.c
index ac18bd932..34b4d03c5 100644
--- a/linux/drivers/media/video/cx18/cx18-scb.c
+++ b/linux/drivers/media/video/cx18/cx18-scb.c
@@ -118,6 +118,5 @@ void cx18_init_scb(struct cx18 *cx)
cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
&cx->scb->ipc_offset);
- cx18_writel(cx, 1, &cx->scb->hpu_state);
cx18_writel(cx, 1, &cx->scb->epu_state);
}
diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c
index f7a7f38d8..f9e44c0cf 100644
--- a/linux/drivers/media/video/cx18/cx18-streams.c
+++ b/linux/drivers/media/video/cx18/cx18-streams.c
@@ -111,7 +111,6 @@ static void cx18_stream_init(struct cx18 *cx, int type)
{
struct cx18_stream *s = &cx->streams[type];
struct video_device *dev = s->v4l2dev;
- u32 max_size = cx->options.megabytes[type] * 1024 * 1024;
/* we need to keep v4l2dev, so restore it afterwards */
memset(s, 0, sizeof(*s));
@@ -124,21 +123,15 @@ static void cx18_stream_init(struct cx18 *cx, int type)
s->handle = CX18_INVALID_TASK_HANDLE;
s->dma = cx18_stream_info[type].dma;
+ s->buffers = cx->stream_buffers[type];
s->buf_size = cx->stream_buf_size[type];
- if (s->buf_size)
- s->buffers = max_size / s->buf_size;
- if (s->buffers > 63) {
- /* Each stream has a maximum of 63 buffers,
- ensure we do not exceed that. */
- s->buffers = 63;
- s->buf_size = (max_size / s->buffers) & ~0xfff;
- }
+
mutex_init(&s->qlock);
init_waitqueue_head(&s->waitq);
s->id = -1;
cx18_queue_init(&s->q_free);
+ cx18_queue_init(&s->q_busy);
cx18_queue_init(&s->q_full);
- cx18_queue_init(&s->q_io);
}
static int cx18_prep_dev(struct cx18 *cx, int type)
@@ -168,7 +161,7 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
/* User explicitly selected 0 buffers for these streams, so don't
create them. */
if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
- cx->options.megabytes[type] == 0) {
+ cx->stream_buffers[type] == 0) {
CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
return 0;
}
@@ -268,8 +261,9 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
switch (vfl_type) {
case VFL_TYPE_GRABBER:
- CX18_INFO("Registered device video%d for %s (%d MB)\n",
- num, s->name, cx->options.megabytes[type]);
+ CX18_INFO("Registered device video%d for %s (%d x %d kB)\n",
+ num, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type]/1024);
break;
case VFL_TYPE_RADIO:
@@ -278,10 +272,11 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
break;
case VFL_TYPE_VBI:
- if (cx->options.megabytes[type])
- CX18_INFO("Registered device vbi%d for %s (%d MB)\n",
- num,
- s->name, cx->options.megabytes[type]);
+ if (cx->stream_buffers[type])
+ CX18_INFO("Registered device vbi%d for %s "
+ "(%d x %d bytes)\n",
+ num, s->name, cx->stream_buffers[type],
+ cx->stream_buf_size[type]);
else
CX18_INFO("Registered device vbi%d for %s\n",
num, s->name);
@@ -345,7 +340,7 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister)
static void cx18_vbi_setup(struct cx18_stream *s)
{
struct cx18 *cx = s->cx;
- int raw = cx->vbi.sliced_in->service_set == 0;
+ int raw = cx18_raw_vbi(cx);
u32 data[CX2341X_MBOX_MAX_DATA];
int lines;
@@ -363,8 +358,7 @@ static void cx18_vbi_setup(struct cx18_stream *s)
cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
/* determine number of lines and total number of VBI bytes.
- A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
- The '- 1' byte is probably an unused U or V byte. Or something...
+ 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) */
if (raw) {
@@ -382,14 +376,15 @@ static void cx18_vbi_setup(struct cx18_stream *s)
/* Lines per field */
data[1] = (lines / 2) | ((lines / 2) << 16);
/* bytes per line */
- data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
+ data[2] = (raw ? cx->vbi.raw_decoder_line_size
+ : cx->vbi.sliced_decoder_line_size);
/* 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 */
if (raw) {
data[4] = 0x20602060;
- data[5] = 0x30703070;
+ data[5] = 0x307090d0;
} else {
data[4] = 0xB0F0B0F0;
data[5] = 0xA0E0A0E0;
@@ -402,11 +397,52 @@ static void cx18_vbi_setup(struct cx18_stream *s)
cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
}
+struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s,
+ struct cx18_buffer *buf)
+{
+ struct cx18 *cx = s->cx;
+ struct cx18_queue *q;
+
+ /* Don't give it to the firmware, if we're not running a capture */
+ if (s->handle == CX18_INVALID_TASK_HANDLE ||
+ !test_bit(CX18_F_S_STREAMING, &s->s_flags))
+ return cx18_enqueue(s, buf, &s->q_free);
+
+ q = cx18_enqueue(s, buf, &s->q_busy);
+ if (q != &s->q_busy)
+ return q; /* The firmware has the max buffers it can handle */
+
+ cx18_buf_sync_for_device(s, buf);
+ cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
+ (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
+ 1, buf->id, s->buf_size);
+ return q;
+}
+
+void cx18_stream_load_fw_queue(struct cx18_stream *s)
+{
+ struct cx18_queue *q;
+ struct cx18_buffer *buf;
+
+ if (atomic_read(&s->q_free.buffers) == 0 ||
+ atomic_read(&s->q_busy.buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
+ return;
+
+ /* Move from q_free to q_busy notifying the firmware, until the limit */
+ do {
+ buf = cx18_dequeue(s, &s->q_free);
+ if (buf == NULL)
+ break;
+ q = cx18_stream_put_buf_fw(s, buf);
+ } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM
+ && q == &s->q_busy);
+}
+
int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
{
u32 data[MAX_MB_ARGUMENTS];
struct cx18 *cx = s->cx;
- struct list_head *p;
+ struct cx18_buffer *buf;
int ts = 0;
int captype = 0;
@@ -435,8 +471,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
captype = CAPTURE_CHANNEL_TYPE_PCM;
break;
case CX18_ENC_STREAM_TYPE_VBI:
- captype = cx->vbi.sliced_in->service_set ?
- CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI;
+ captype = cx18_raw_vbi(cx) ?
+ CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
cx->vbi.frame = 0;
cx->vbi.inserted_frame = 0;
memset(cx->vbi.sliced_mpeg_size,
@@ -458,6 +494,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
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 */
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);
@@ -477,7 +515,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0);
/* Setup API for Stream */
- cx2341x_update(cx, cx18_api_func, NULL, &cx->params);
+ priv.cx = cx;
+ priv.s = s;
+ cx2341x_update(&priv, cx18_api_func, NULL, &cx->params);
}
if (atomic_read(&cx->tot_capturing) == 0) {
@@ -489,16 +529,17 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
(void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
- list_for_each(p, &s->q_free.list) {
- struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list);
-
+ /* Init all the cpu_mdls for this stream */
+ cx18_flush_queues(s);
+ mutex_lock(&s->qlock);
+ list_for_each_entry(buf, &s->q_free.list, list) {
cx18_writel(cx, buf->dma_handle,
&cx->scb->cpu_mdl[buf->id].paddr);
cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
- cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
- (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
- 1, buf->id, s->buf_size);
}
+ mutex_unlock(&s->qlock);
+ cx18_stream_load_fw_queue(s);
+
/* begin_capture */
if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
CX18_DEBUG_WARN("Error starting capture!\n");
@@ -507,9 +548,15 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1);
else
cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
+ clear_bit(CX18_F_S_STREAMING, &s->s_flags);
+ /* FIXME - CX18_F_S_STREAMOFF as well? */
cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle);
cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
- /* FIXME - clean-up DSP0_INT mask, i_flags, s_flags, etc. */
+ s->handle = CX18_INVALID_TASK_HANDLE;
+ if (atomic_read(&cx->tot_capturing) == 0) {
+ set_bit(CX18_F_I_EOS, &cx->i_flags);
+ cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK);
+ }
return -EINVAL;
}
diff --git a/linux/drivers/media/video/cx18/cx18-streams.h b/linux/drivers/media/video/cx18/cx18-streams.h
index 7218b1504..420e0a172 100644
--- a/linux/drivers/media/video/cx18/cx18-streams.h
+++ b/linux/drivers/media/video/cx18/cx18-streams.h
@@ -29,6 +29,9 @@ int cx18_streams_register(struct cx18 *cx);
void cx18_streams_cleanup(struct cx18 *cx, int unregister);
/* Capture related */
+void cx18_stream_load_fw_queue(struct cx18_stream *s);
+struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s,
+ struct cx18_buffer *buf);
int cx18_start_v4l2_encode_stream(struct cx18_stream *s);
int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end);
diff --git a/linux/drivers/media/video/cx18/cx18-vbi.c b/linux/drivers/media/video/cx18/cx18-vbi.c
index 22e76ee3f..fb595bd54 100644
--- a/linux/drivers/media/video/cx18/cx18-vbi.c
+++ b/linux/drivers/media/video/cx18/cx18-vbi.c
@@ -160,11 +160,14 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
return;
/* Raw VBI data */
- if (cx->vbi.sliced_in->service_set == 0) {
+ 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);
diff --git a/linux/drivers/media/video/cx18/cx18-version.h b/linux/drivers/media/video/cx18/cx18-version.h
index eb043d599..84c0ff13b 100644
--- a/linux/drivers/media/video/cx18/cx18-version.h
+++ b/linux/drivers/media/video/cx18/cx18-version.h
@@ -25,7 +25,7 @@
#define CX18_DRIVER_NAME "cx18"
#define CX18_DRIVER_VERSION_MAJOR 1
#define CX18_DRIVER_VERSION_MINOR 0
-#define CX18_DRIVER_VERSION_PATCHLEVEL 3
+#define CX18_DRIVER_VERSION_PATCHLEVEL 4
#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/linux/drivers/media/video/cx18/cx23418.h b/linux/drivers/media/video/cx18/cx23418.h
index 668f968d7..601f3a2ab 100644
--- a/linux/drivers/media/video/cx18/cx23418.h
+++ b/linux/drivers/media/video/cx18/cx23418.h
@@ -44,6 +44,7 @@
/* All commands for CPU have the following mask set */
#define CPU_CMD_MASK 0x20000000
+#define CPU_CMD_MASK_DEBUG (CPU_CMD_MASK | 0x00000000)
#define CPU_CMD_MASK_ACK (CPU_CMD_MASK | 0x80000000)
#define CPU_CMD_MASK_CAPTURE (CPU_CMD_MASK | 0x00020000)
#define CPU_CMD_MASK_TS (CPU_CMD_MASK | 0x00040000)
@@ -71,6 +72,11 @@
0/zero/NULL means "I have nothing to say" */
#define CX18_EPU_DEBUG (EPU_CMD_MASK_DEBUG | 0x0003)
+/* Reads memory/registers (32-bit)
+ IN[0] - Address
+ OUT[1] - Value */
+#define CX18_CPU_DEBUG_PEEK32 (CPU_CMD_MASK_DEBUG | 0x0003)
+
/* Description: This command starts streaming with the set channel type
IN[0] - Task handle. Handle of the task to start
ReturnCode - One of the ERR_CAPTURE_... */
diff --git a/linux/drivers/media/video/cx23885/Kconfig b/linux/drivers/media/video/cx23885/Kconfig
index 8c1b7fa47..00f1e2e88 100644
--- a/linux/drivers/media/video/cx23885/Kconfig
+++ b/linux/drivers/media/video/cx23885/Kconfig
@@ -11,16 +11,16 @@ config VIDEO_CX23885
select VIDEO_CX25840
select VIDEO_CX2341X
select DVB_DIB7000P if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_MT2131 if !DVB_FE_CUSTOMISE
select DVB_S5H1409 if !DVB_FE_CUSTOMISE
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_CUSTOMIZE
---help---
This is a video4linux driver for Conexant 23885 based
TV cards.
diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c
index dc3f79638..db1e8bdab 100644
--- a/linux/drivers/media/video/cx23885/cx23885-cards.c
+++ b/linux/drivers/media/video/cx23885/cx23885-cards.c
@@ -159,6 +159,10 @@ struct cx23885_board cx23885_boards[] = {
.name = "Leadtek Winfast PxDVR3200 H",
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_COMPRO_VIDEOMATE_E650F] = {
+ .name = "Compro VideoMate E650F",
+ .portc = CX23885_MPEG_DVB,
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -238,6 +242,10 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x107d,
.subdevice = 0x6681,
.card = CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H,
+ }, {
+ .subvendor = 0x185b,
+ .subdevice = 0xe800,
+ .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -391,6 +399,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg)
case CX23885_BOARD_HAUPPAUGE_HVR1500:
case CX23885_BOARD_HAUPPAUGE_HVR1500Q:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
/* Tuner Reset Command */
bitmask = 0x04;
break;
@@ -531,6 +540,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
cx_set(GP0_IO, 0x000f000f);
break;
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
/* GPIO-2 xc3028 tuner reset */
/* The following GPIO's are on the internal AVCore (cx25840) */
@@ -631,6 +641,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1700:
case CX23885_BOARD_HAUPPAUGE_HVR1400:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
default:
ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
@@ -645,6 +656,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
case CX23885_BOARD_HAUPPAUGE_HVR1700:
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
request_module("cx25840");
break;
}
diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c
index a11fdbfb5..990fd217b 100644
--- a/linux/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c
@@ -503,6 +503,7 @@ static int dvb_register(struct cx23885_tsport *port)
break;
}
case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
i2c_bus = &dev->i2c_bus[0];
fe0->dvb.frontend = dvb_attach(zl10353_attach,
diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h
index 26f755354..ad93e0e88 100644
--- a/linux/drivers/media/video/cx23885/cx23885.h
+++ b/linux/drivers/media/video/cx23885/cx23885.h
@@ -67,6 +67,7 @@
#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 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
/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
#define CX23885_NORMS (\
diff --git a/linux/drivers/media/video/cx25840/cx25840-audio.c b/linux/drivers/media/video/cx25840/cx25840-audio.c
index 8e134312c..3ee7d7a7d 100644
--- a/linux/drivers/media/video/cx25840/cx25840-audio.c
+++ b/linux/drivers/media/video/cx25840/cx25840-audio.c
@@ -26,7 +26,7 @@
static int set_audclk_freq(struct i2c_client *client, u32 freq)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
if (freq != 32000 && freq != 44100 && freq != 48000)
return -EINVAL;
@@ -194,7 +194,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
void cx25840_audio_set_path(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
/* assert soft reset */
cx25840_and_or(client, 0x810, ~0x1, 0x01);
@@ -236,7 +236,7 @@ void cx25840_audio_set_path(struct i2c_client *client)
static int get_volume(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
if (state->unmute_volume >= 0)
@@ -253,7 +253,7 @@ static int get_volume(struct i2c_client *client)
static void set_volume(struct i2c_client *client, int volume)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
int vol;
if (state->unmute_volume >= 0) {
@@ -341,14 +341,14 @@ static void set_balance(struct i2c_client *client, int balance)
static int get_mute(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
return state->unmute_volume >= 0;
}
static void set_mute(struct i2c_client *client, int mute)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
if (mute && state->unmute_volume == -1) {
int vol = get_volume(client);
@@ -366,7 +366,7 @@ static void set_mute(struct i2c_client *client, int mute)
int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct v4l2_control *ctrl = arg;
int retval;
diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c
index cb4a9c64e..52a2a5799 100644
--- a/linux/drivers/media/video/cx25840/cx25840-core.c
+++ b/linux/drivers/media/video/cx25840/cx25840-core.c
@@ -198,7 +198,7 @@ void cx25840_work_handler(void *arg)
static void cx25840_initialize(struct i2c_client *client)
{
DEFINE_WAIT(wait);
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct workqueue_struct *q;
/* datasheet startup in numbered steps, refer to page 3-77 */
@@ -270,7 +270,7 @@ static void cx25840_initialize(struct i2c_client *client)
static void cx23885_initialize(struct i2c_client *client)
{
DEFINE_WAIT(wait);
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct workqueue_struct *q;
/* Internal Reset */
@@ -369,7 +369,7 @@ static void cx23885_initialize(struct i2c_client *client)
void cx25840_std_setup(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
v4l2_std_id std = state->std;
int hblank, hactive, burst, vblank, vactive, sc;
int vblank656, src_decimation;
@@ -516,7 +516,7 @@ void cx25840_std_setup(struct i2c_client *client)
static void input_change(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
v4l2_std_id std = state->std;
/* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */
@@ -570,7 +570,7 @@ static void input_change(struct i2c_client *client)
static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input,
enum cx25840_audio_input aud_input)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 is_composite = (vid_input >= CX25840_COMPOSITE1 &&
vid_input <= CX25840_COMPOSITE8);
u8 reg;
@@ -690,7 +690,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
static int set_v4lstd(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 fmt = 0; /* zero is autodetect */
u8 pal_m = 0;
@@ -739,9 +739,10 @@ static int set_v4lstd(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case CX25840_CID_ENABLE_PVR150_WORKAROUND:
@@ -805,9 +806,10 @@ static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case CX25840_CID_ENABLE_PVR150_WORKAROUND:
@@ -842,21 +844,23 @@ static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
/* ----------------------------------------------------------------------- */
-static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+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);
default:
return -EINVAL;
}
-
return 0;
}
-static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = 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);
@@ -933,7 +937,7 @@ static void log_video_status(struct i2c_client *client)
"0xD", "0xE", "0xF"
};
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf;
u8 gen_stat1 = cx25840_read(client, 0x40d);
u8 gen_stat2 = cx25840_read(client, 0x40e);
@@ -963,7 +967,7 @@ static void log_video_status(struct i2c_client *client)
static void log_audio_status(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
u8 download_ctl = cx25840_read(client, 0x803);
u8 mod_det_stat0 = cx25840_read(client, 0x804);
u8 mod_det_stat1 = cx25840_read(client, 0x805);
@@ -1116,21 +1120,12 @@ static void log_audio_status(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
-static int cx25840_command(struct i2c_client *client, unsigned int cmd,
- void *arg)
+static int cx25840_init(struct v4l2_subdev *sd, u32 val)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
- struct v4l2_tuner *vt = arg;
- struct v4l2_routing *route = arg;
-
- /* ignore these commands */
- switch (cmd) {
- case TUNER_SET_TYPE_ADDR:
- return 0;
- }
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
if (!state->is_initialized) {
- v4l_dbg(1, cx25840_debug, client, "cmd %08x triggered fw load\n", cmd);
/* initialize on first use */
state->is_initialized = 1;
if (state->is_cx25836)
@@ -1140,50 +1135,69 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
else
cx25840_initialize(client);
}
+ return 0;
+}
- switch (cmd) {
#ifdef CONFIG_VIDEO_ADV_DEBUG
- /* ioctls to allow direct access to the
- * cx25840 registers for testing */
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
+static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = cx25840_read(client, reg->reg & 0x0fff);
- else
- cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
- break;
- }
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = cx25840_read(client, reg->reg & 0x0fff);
+ return 0;
+}
+
+static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff);
+ return 0;
+}
#endif
- case VIDIOC_INT_DECODE_VBI_LINE:
- return cx25840_vbi(client, cmd, arg);
+static int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- return cx25840_audio(client, cmd, arg);
+ 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);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_STREAMON:
- v4l_dbg(1, cx25840_debug, client, "enable output\n");
+ v4l_dbg(1, cx25840_debug, client, "%s output\n",
+ enable ? "enable" : "disable");
+ if (enable) {
if (state->is_cx23885) {
u8 v = (cx25840_read(client, 0x421) | 0x0b);
cx25840_write(client, 0x421, v);
} else {
cx25840_write(client, 0x115,
- state->is_cx25836 ? 0x0c : 0x8c);
+ state->is_cx25836 ? 0x0c : 0x8c);
cx25840_write(client, 0x116,
- state->is_cx25836 ? 0x04 : 0x07);
+ state->is_cx25836 ? 0x04 : 0x07);
}
- break;
-
- case VIDIOC_STREAMOFF:
- v4l_dbg(1, cx25840_debug, client, "disable output\n");
+ } else {
if (state->is_cx23885) {
u8 v = cx25840_read(client, 0x421) & ~(0x0b);
cx25840_write(client, 0x421, v);
@@ -1191,133 +1205,136 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
cx25840_write(client, 0x115, 0x00);
cx25840_write(client, 0x116, 0x00);
}
- break;
+ }
+ return 0;
+}
- case VIDIOC_LOG_STATUS:
- log_video_status(client);
- if (!state->is_cx25836)
- log_audio_status(client);
- break;
+static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct cx25840_state *state = to_state(sd);
- case VIDIOC_G_CTRL:
- return get_v4lctrl(client, (struct v4l2_control *)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;
+ }
+ if (state->is_cx25836)
+ return -EINVAL;
- case VIDIOC_S_CTRL:
- return set_v4lctrl(client, (struct v4l2_control *)arg);
+ 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_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- 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;
- }
- if (state->is_cx25836)
- return -EINVAL;
+ if (state->radio == 0 && state->std == std)
+ return 0;
+ state->radio = 0;
+ state->std = std;
+ return set_v4lstd(client);
+}
- 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;
- }
+static int cx25840_s_radio(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
- case VIDIOC_G_STD:
- *(v4l2_std_id *)arg = state->std;
- break;
+ state->radio = 1;
+ return 0;
+}
- 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(client);
+static int cx25840_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case AUDC_SET_RADIO:
- state->radio = 1;
- break;
+ return set_input(client, route->input, state->aud_input);
+}
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = state->vid_input;
- route->output = 0;
- break;
+static int cx25840_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_VIDEO_ROUTING:
- return set_input(client, route->input, state->aud_input);
+ if (state->is_cx25836)
+ return -EINVAL;
+ return set_input(client, state->vid_input, route->input);
+}
- case VIDIOC_INT_G_AUDIO_ROUTING:
- if (state->is_cx25836)
- return -EINVAL;
- route->input = state->aud_input;
- route->output = 0;
- break;
+static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_AUDIO_ROUTING:
- if (state->is_cx25836)
- return -EINVAL;
- return set_input(client, state->vid_input, route->input);
+ if (!state->is_cx25836)
+ input_change(client);
+ return 0;
+}
- case VIDIOC_S_FREQUENCY:
- if (!state->is_cx25836) {
- input_change(client);
- }
- break;
+static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 vpres = cx25840_read(client, 0x40e) & 0x20;
+ u8 mode;
+ int val = 0;
- case VIDIOC_G_TUNER:
- {
- u8 vpres = cx25840_read(client, 0x40e) & 0x20;
- u8 mode;
- int val = 0;
+ if (state->radio)
+ return 0;
- if (state->radio)
- break;
+ vt->signal = vpres ? 0xffff : 0x0;
+ if (state->is_cx25836)
+ return 0;
- vt->signal = vpres ? 0xffff : 0x0;
- if (state->is_cx25836)
- break;
+ vt->capability |=
+ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
- vt->capability |=
- V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
- V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+ mode = cx25840_read(client, 0x804);
- mode = cx25840_read(client, 0x804);
+ /* get rxsubchans and audmode */
+ if ((mode & 0xf) == 1)
+ val |= V4L2_TUNER_SUB_STEREO;
+ else
+ val |= V4L2_TUNER_SUB_MONO;
- /* 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 == 2 || mode == 4)
- val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+ if (mode & 0x10)
+ val |= V4L2_TUNER_SUB_SAP;
- if (mode & 0x10)
- val |= V4L2_TUNER_SUB_SAP;
+ vt->rxsubchans = val;
+ vt->audmode = state->audmode;
+ return 0;
+}
- vt->rxsubchans = val;
- vt->audmode = state->audmode;
- break;
- }
+static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_S_TUNER:
- if (state->radio || state->is_cx25836)
- break;
+ if (state->radio || state->is_cx25836)
+ return 0;
- switch (vt->audmode) {
+ switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
/* mono -> mono
stereo -> mono
@@ -1345,41 +1362,100 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
break;
default:
return -EINVAL;
- }
- state->audmode = vt->audmode;
- break;
+ }
+ state->audmode = vt->audmode;
+ return 0;
+}
- case VIDIOC_G_FMT:
- return get_v4lfmt(client, (struct v4l2_format *)arg);
+static int cx25840_reset(struct v4l2_subdev *sd, u32 val)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_S_FMT:
- return set_v4lfmt(client, (struct v4l2_format *)arg);
+ if (state->is_cx25836)
+ cx25836_initialize(client);
+ else if (state->is_cx23885)
+ cx23885_initialize(client);
+ else
+ cx25840_initialize(client);
+ return 0;
+}
- case VIDIOC_INT_RESET:
- if (state->is_cx25836)
- cx25836_initialize(client);
- else if (state->is_cx23885)
- cx23885_initialize(client);
- else
- cx25840_initialize(client);
- break;
+static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev);
+ return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev);
+}
- default:
- return -EINVAL;
- }
+static int cx25840_log_status(struct v4l2_subdev *sd)
+{
+ struct cx25840_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ log_video_status(client);
+ if (!state->is_cx25836)
+ log_audio_status(client);
return 0;
}
+static int cx25840_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 cx25840_core_ops = {
+ .log_status = cx25840_log_status,
+ .g_chip_ident = cx25840_g_chip_ident,
+ .g_ctrl = cx25840_g_ctrl,
+ .s_ctrl = cx25840_s_ctrl,
+ .queryctrl = cx25840_queryctrl,
+ .reset = cx25840_reset,
+ .init = cx25840_init,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = cx25840_g_register,
+ .s_register = cx25840_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = {
+ .s_frequency = cx25840_s_frequency,
+ .s_std = cx25840_s_std,
+ .s_radio = cx25840_s_radio,
+ .g_tuner = cx25840_g_tuner,
+ .s_tuner = cx25840_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops cx25840_audio_ops = {
+ .s_clock_freq = cx25840_s_clock_freq,
+ .s_routing = cx25840_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops cx25840_video_ops = {
+ .s_routing = cx25840_s_video_routing,
+ .g_fmt = cx25840_g_fmt,
+ .s_fmt = cx25840_s_fmt,
+ .decode_vbi_line = cx25840_decode_vbi_line,
+ .s_stream = cx25840_s_stream,
+};
+
+static const struct v4l2_subdev_ops cx25840_ops = {
+ .core = &cx25840_core_ops,
+ .tuner = &cx25840_tuner_ops,
+ .audio = &cx25840_audio_ops,
+ .video = &cx25840_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
static int cx25840_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct cx25840_state *state;
+ struct v4l2_subdev *sd;
u32 id;
u16 device_id;
@@ -1411,10 +1487,11 @@ static int cx25840_probe(struct i2c_client *client,
}
state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL);
- if (state == NULL) {
+ if (state == NULL)
return -ENOMEM;
- }
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &cx25840_ops);
/* Note: revision '(device_id & 0x0f) == 2' was never built. The
marking skips from 0x1 == 22 to 0x3 == 23. */
v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n",
@@ -1422,7 +1499,6 @@ static int cx25840_probe(struct i2c_client *client,
(device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f),
client->addr << 1, client->adapter->name);
- i2c_set_clientdata(client, state);
state->c = client;
state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
@@ -1449,7 +1525,10 @@ static int cx25840_probe(struct i2c_client *client,
static int cx25840_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;
}
diff --git a/linux/drivers/media/video/cx25840/cx25840-core.h b/linux/drivers/media/video/cx25840/cx25840-core.h
index cd549b119..0a99c19df 100644
--- a/linux/drivers/media/video/cx25840/cx25840-core.h
+++ b/linux/drivers/media/video/cx25840/cx25840-core.h
@@ -23,6 +23,7 @@
#include "compat.h"
#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
#include <linux/i2c.h>
/* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is
@@ -35,6 +36,7 @@
struct cx25840_state {
struct i2c_client *c;
+ struct v4l2_subdev sd;
int pvr150_workaround;
int radio;
v4l2_std_id std;
@@ -54,6 +56,11 @@ struct cx25840_state {
struct work_struct fw_work; /* work entry for fw load */
};
+static inline struct cx25840_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct cx25840_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
/* cx25850-core.c */
int cx25840_write(struct i2c_client *client, u16 addr, u8 value);
diff --git a/linux/drivers/media/video/cx25840/cx25840-firmware.c b/linux/drivers/media/video/cx25840/cx25840-firmware.c
index 04a5dbb65..01fbe174e 100644
--- a/linux/drivers/media/video/cx25840/cx25840-firmware.c
+++ b/linux/drivers/media/video/cx25840/cx25840-firmware.c
@@ -92,7 +92,7 @@ static int fw_write(struct i2c_client *client, const u8 *data, int size)
int cx25840_loadfw(struct i2c_client *client)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
const struct firmware *fw = NULL;
u8 buffer[FWSEND];
const u8 *ptr;
diff --git a/linux/drivers/media/video/cx25840/cx25840-vbi.c b/linux/drivers/media/video/cx25840/cx25840-vbi.c
index e45c39625..7790afd5b 100644
--- a/linux/drivers/media/video/cx25840/cx25840-vbi.c
+++ b/linux/drivers/media/video/cx25840/cx25840-vbi.c
@@ -85,7 +85,7 @@ static int decode_vps(u8 * dst, u8 * p)
int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg)
{
- struct cx25840_state *state = i2c_get_clientdata(client);
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
struct v4l2_format *fmt;
struct v4l2_sliced_vbi_format *svbi;
diff --git a/linux/drivers/media/video/cx88/Kconfig b/linux/drivers/media/video/cx88/Kconfig
index 0b9e5fac6..b0f837588 100644
--- a/linux/drivers/media/video/cx88/Kconfig
+++ b/linux/drivers/media/video/cx88/Kconfig
@@ -56,12 +56,12 @@ config VIDEO_CX88_DVB
select DVB_NXT200X if !DVB_FE_CUSTOMISE
select DVB_CX24123 if !DVB_FE_CUSTOMISE
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
select DVB_S5H1411 if !DVB_FE_CUSTOMISE
select DVB_CX24116 if !DVB_FE_CUSTOMISE
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
---help---
This adds support for DVB/ATSC cards based on the
Conexant 2388x chip.
diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c
index 3dd2f6629..f7171586d 100644
--- a/linux/drivers/media/video/cx88/cx88-alsa.c
+++ b/linux/drivers/media/video/cx88/cx88-alsa.c
@@ -795,7 +795,6 @@ static int __devinit snd_cx88_create(struct snd_card *card,
core = cx88_core_get(pci);
if (NULL == core) {
err = -EINVAL;
- kfree (chip);
return err;
}
@@ -872,7 +871,7 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci,
err = snd_cx88_create(card, pci, &chip);
if (err < 0)
- return (err);
+ goto error;
err = snd_cx88_pcm(chip, 0, "CX88 Digital");
if (err < 0)
diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c
index b090c6620..561dc7f0b 100644
--- a/linux/drivers/media/video/cx88/cx88-blackbird.c
+++ b/linux/drivers/media/video/cx88/cx88-blackbird.c
@@ -1263,8 +1263,16 @@ static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv)
* We're being given access to re-arrange the GPIOs.
* Take the bus off the cx22702 and put the cx23416 on it.
*/
- cx_clear(MO_GP0_IO, 0x00000080); /* cx22702 in reset */
- cx_set(MO_GP0_IO, 0x00000004); /* Disable the cx22702 */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ /* tri-state the cx22702 pins */
+ cx_set(MO_GP0_IO, 0x00000004);
+ udelay(1000);
break;
default:
err = -ENODEV;
diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c
index 5a9fafd25..fc3a080ac 100644
--- a/linux/drivers/media/video/cx88/cx88-dvb.c
+++ b/linux/drivers/media/video/cx88/cx88-dvb.c
@@ -1135,40 +1135,44 @@ static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv)
* on the bus. Take the bus from the cx23416 and enable the
* cx22702 demod
*/
- cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset and enable */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ /* enable the cx22702 pins */
cx_clear(MO_GP0_IO, 0x00000004);
udelay(1000);
break;
case CX88_BOARD_HAUPPAUGE_HVR3000:
case CX88_BOARD_HAUPPAUGE_HVR4000:
- if(core->dvbdev->frontends.active_fe_id == 1) {
- /* DVB-S/S2 Enabled */
-
- /* Toggle reset on cx22702 leaving i2c active */
- cx_write(MO_GP0_IO, (core->board.input[0].gpio0 & 0x0000ff00) | 0x00000080);
- udelay(1000);
- cx_clear(MO_GP0_IO, 0x00000080);
- udelay(50);
- cx_set(MO_GP0_IO, 0x00000080); /* cx22702 out of reset */
- cx_set(MO_GP0_IO, 0x00000004); /* tri-state the cx22702 pins */
- udelay(1000);
-
- cx_write(MO_SRST_IO, 1); /* Take the cx24116/cx24123 out of reset */
+ /* Toggle reset on cx22702 leaving i2c active */
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080);
+ udelay(1000);
+ switch (core->dvbdev->frontends.active_fe_id) {
+ case 1: /* DVB-S/S2 Enabled */
+ /* tri-state the cx22702 pins */
+ cx_set(MO_GP0_IO, 0x00000004);
+ /* Take the cx24116/cx24123 out of reset */
+ cx_write(MO_SRST_IO, 1);
core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */
- } else
- if (core->dvbdev->frontends.active_fe_id == 2) {
- /* DVB-T Enabled */
-
+ break;
+ case 2: /* DVB-T Enabled */
/* Put the cx24116/cx24123 into reset */
cx_write(MO_SRST_IO, 0);
-
- /* cx22702 out of reset and enable it */
- cx_set(MO_GP0_IO, 0x00000080);
+ /* enable the cx22702 pins */
cx_clear(MO_GP0_IO, 0x00000004);
core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */
- udelay(1000);
+ break;
}
+ udelay(1000);
break;
default:
diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c
index 7a2553c4d..c14fca841 100644
--- a/linux/drivers/media/video/em28xx/em28xx-cards.c
+++ b/linux/drivers/media/video/em28xx/em28xx-cards.c
@@ -1238,19 +1238,17 @@ struct em28xx_board em28xx_boards[] = {
},
[EM2883_BOARD_KWORLD_HYBRID_A316] = {
.name = "Kworld PlusTV HD Hybrid 330",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_XC2028,
.tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
-#if 0 /* FIXME: add an entry at em28xx-dvb */
+ .mts_firmware = 1,
.has_dvb = 1,
- .dvb_gpio = hauppauge_wintv_hvr_900_digital,
-#endif
+ .dvb_gpio = default_digital,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_VIDEO,
- .gpio = hauppauge_wintv_hvr_900_analog,
+ .gpio = default_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c
index ddc4f6a99..8d3ed7dc8 100644
--- a/linux/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c
@@ -413,6 +413,7 @@ static int dvb_init(struct em28xx *dev)
case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850:
case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950:
case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
+ case EM2883_BOARD_KWORLD_HYBRID_A316:
case EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600:
dvb->frontend = dvb_attach(lgdt330x_attach,
&em2880_lgdt3303_dev,
diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c
index f3c9e2331..78eb335a5 100644
--- a/linux/drivers/media/video/em28xx/em28xx-video.c
+++ b/linux/drivers/media/video/em28xx/em28xx-video.c
@@ -605,12 +605,10 @@ static int res_get(struct em28xx_fh *fh)
return rc;
if (dev->stream_on)
- return -EINVAL;
+ return -EBUSY;
- mutex_lock(&dev->lock);
dev->stream_on = 1;
fh->stream_on = 1;
- mutex_unlock(&dev->lock);
return rc;
}
@@ -623,10 +621,8 @@ static void res_free(struct em28xx_fh *fh)
{
struct em28xx *dev = fh->dev;
- mutex_lock(&dev->lock);
fh->stream_on = 0;
dev->stream_on = 0;
- mutex_unlock(&dev->lock);
}
/*
@@ -740,19 +736,17 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
/* width must even because of the YUYV format
height must be even because of interlacing */
height &= 0xfffe;
- width &= 0xfffe;
+ width &= 0xfffe;
- if (height < 32)
+ if (unlikely(height < 32))
height = 32;
- if (height > maxh)
+ if (unlikely(height > maxh))
height = maxh;
- if (width < 48)
+ if (unlikely(width < 48))
width = 48;
- if (width > maxw)
+ if (unlikely(width > maxw))
width = maxw;
- mutex_lock(&dev->lock);
-
if (dev->board.is_em2800) {
/* the em2800 can only scale down to 50% */
if (height % (maxh / 2))
@@ -782,7 +776,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
f->fmt.pix.field = V4L2_FIELD_INTERLACED;
- mutex_unlock(&dev->lock);
return 0;
}
@@ -797,10 +790,10 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
if (rc < 0)
return rc;
- vidioc_try_fmt_vid_cap(file, priv, f);
-
mutex_lock(&dev->lock);
+ vidioc_try_fmt_vid_cap(file, priv, f);
+
if (videobuf_queue_is_busy(&fh->vb_vidq)) {
em28xx_errdev("%s queue busy\n", __func__);
rc = -EBUSY;
@@ -841,15 +834,12 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
mutex_lock(&dev->lock);
dev->norm = *norm;
- mutex_unlock(&dev->lock);
/* Adjusts width/height, if needed */
f.fmt.pix.width = dev->width;
f.fmt.pix.height = dev->height;
vidioc_try_fmt_vid_cap(file, priv, &f);
- mutex_lock(&dev->lock);
-
/* set new image size */
dev->width = f.fmt.pix.width;
dev->height = f.fmt.pix.height;
@@ -984,11 +974,15 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
if (a->index != dev->ctl_ainput)
return -EINVAL;
#else
+ mutex_lock(&dev->lock);
+
dev->ctl_ainput = INPUT(a->index)->amux;
dev->ctl_aoutput = INPUT(a->index)->aout;
if (!dev->ctl_aoutput)
dev->ctl_aoutput = EM28XX_AOUT_MASTER;
+
+ mutex_unlock(&dev->lock);
#endif
return 0;
}
@@ -1038,6 +1032,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
rc = check_dev(dev);
if (rc < 0)
return rc;
+
mutex_lock(&dev->lock);
if (!dev->board.has_msp34xx)
@@ -1148,8 +1143,10 @@ static int vidioc_g_frequency(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
+ mutex_lock(&dev->lock);
f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
f->frequency = dev->ctl_freq;
+ mutex_unlock(&dev->lock);
return 0;
}
@@ -1179,6 +1176,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1206,15 +1204,20 @@ static int vidioc_g_register(struct file *file, void *priv,
return -EINVAL;
if (em28xx_reg_len(reg->reg) == 1) {
+ mutex_lock(&dev->lock);
ret = em28xx_read_reg(dev, reg->reg);
+ mutex_unlock(&dev->lock);
+
if (ret < 0)
return ret;
reg->val = ret;
} else {
__le64 val = 0;
+ mutex_lock(&dev->lock);
ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS,
reg->reg, (char *)&val, 2);
+ mutex_unlock(&dev->lock);
if (ret < 0)
return ret;
@@ -1230,11 +1233,16 @@ static int vidioc_s_register(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
__le64 buf;
+ int rc;
buf = cpu_to_le64(reg->val);
- return em28xx_write_regs(dev, reg->reg, (char *)&buf,
- em28xx_reg_len(reg->reg));
+ mutex_lock(&dev->lock);
+ rc = em28xx_write_regs(dev, reg->reg, (char *)&buf,
+ em28xx_reg_len(reg->reg));
+ mutex_unlock(&dev->lock);
+
+ return rc;
}
#endif
@@ -1271,10 +1279,17 @@ static int vidioc_streamon(struct file *file, void *priv,
return rc;
- if (unlikely(res_get(fh) < 0))
- return -EBUSY;
+ mutex_lock(&dev->lock);
+ rc = res_get(fh);
+
+ if (unlikely(rc < 0))
+ return rc;
+
+ rc = videobuf_streamon(&fh->vb_vidq);
- return (videobuf_streamon(&fh->vb_vidq));
+ mutex_unlock(&dev->lock);
+
+ return rc;
}
static int vidioc_streamoff(struct file *file, void *priv,
@@ -1293,9 +1308,13 @@ static int vidioc_streamoff(struct file *file, void *priv,
if (type != fh->type)
return -EINVAL;
+ mutex_lock(&dev->lock);
+
videobuf_streamoff(&fh->vb_vidq);
res_free(fh);
+ mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1515,7 +1534,10 @@ static int radio_g_tuner(struct file *file, void *priv,
strcpy(t->name, "Radio");
t->type = V4L2_TUNER_RADIO;
+ mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
+ mutex_unlock(&dev->lock);
+
return 0;
}
@@ -1547,7 +1569,9 @@ static int radio_s_tuner(struct file *file, void *priv,
if (0 != t->index)
return -EINVAL;
+ mutex_lock(&dev->lock);
em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
+ mutex_unlock(&dev->lock);
return 0;
}
@@ -1611,6 +1635,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
}
}
mutex_unlock(&em28xx_devlist_mutex);
+
if (NULL == dev)
return -ENODEV;
@@ -1741,11 +1766,10 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
em28xx_videodbg("users=%d\n", dev->users);
+ mutex_lock(&dev->lock);
if (res_check(fh))
res_free(fh);
- mutex_lock(&dev->lock);
-
if (dev->users == 1) {
videobuf_stop(&fh->vb_vidq);
videobuf_mmap_free(&fh->vb_vidq);
@@ -1803,8 +1827,12 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
*/
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- if (unlikely(res_get(fh)))
- return -EBUSY;
+ 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);
@@ -1826,7 +1854,11 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table * wait)
if (rc < 0)
return rc;
- if (unlikely(res_get(fh) < 0))
+ 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)
@@ -1844,13 +1876,17 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
struct em28xx *dev = fh->dev;
int rc;
- if (unlikely(res_get(fh) < 0))
- return -EBUSY;
-
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);
em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
@@ -2095,7 +2131,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
unsigned int maxh, maxw;
dev->udev = udev;
- mutex_init(&dev->lock);
mutex_init(&dev->ctrl_urb_lock);
spin_lock_init(&dev->slock);
init_waitqueue_head(&dev->open);
@@ -2213,7 +2248,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
return 0;
fail_reg_devices:
- mutex_unlock(&dev->lock);
return retval;
}
@@ -2416,6 +2450,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
dev->model = card[nr];
/* allocate device struct */
+ mutex_init(&dev->lock);
+ mutex_lock(&dev->lock);
retval = em28xx_init_dev(&dev, udev, nr);
if (retval) {
em28xx_devused &= ~(1<<dev->devno);
@@ -2429,6 +2465,11 @@ static int em28xx_usb_probe(struct usb_interface *interface,
request_modules(dev);
+ /* Should be the last thing to do, to avoid newer udev's to
+ open the device before fully initializing it
+ */
+ mutex_unlock(&dev->lock);
+
return 0;
}
diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c
index d725663fc..71e7afe1b 100644
--- a/linux/drivers/media/video/gspca/gspca.c
+++ b/linux/drivers/media/video/gspca/gspca.c
@@ -649,8 +649,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
"usb_submit_urb [%d] err %d", n, ret);
gspca_dev->streaming = 0;
destroy_urbs(gspca_dev);
- if (ret == -ENOSPC)
+ if (ret == -ENOSPC) {
+ msleep(20); /* wait for kill
+ * complete */
break; /* try the previous alt */
+ }
goto out;
}
}
@@ -668,7 +671,7 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
if (ret < 0)
- PDEBUG(D_ERR|D_STREAM, "set interface 0 err %d", ret);
+ PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret);
return ret;
}
@@ -1078,6 +1081,35 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
return -EINVAL;
}
+/*fixme: have an audio flag in gspca_dev?*/
+static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index != 0)
+ return -EINVAL;
+ return 0;
+}
+
+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;
+}
+
+static int vidioc_enumaudio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+{
+ if (audio->index != 0)
+ return -EINVAL;
+
+ strcpy(audio->name, "Microphone");
+ audio->capability = 0;
+ audio->mode = 0;
+ return 0;
+}
+
static int vidioc_querymenu(struct file *file, void *priv,
struct v4l2_querymenu *qmenu)
{
@@ -1288,10 +1320,10 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv,
struct gspca_dev *gspca_dev = priv;
int ret;
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
if (!gspca_dev->sd_desc->set_jcomp)
return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
mutex_unlock(&gspca_dev->usb_lock);
return ret;
@@ -1305,6 +1337,17 @@ static int vidioc_g_parm(struct file *filp, void *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) {
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ ret = gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+
return 0;
}
@@ -1319,6 +1362,17 @@ static int vidioc_s_parm(struct file *filp, void *priv,
parm->parm.capture.readbuffers = gspca_dev->nbufread;
else
gspca_dev->nbufread = n;
+
+ if (gspca_dev->sd_desc->set_streamparm) {
+ int ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+ ret = gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+
return 0;
}
@@ -1794,6 +1848,9 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = {
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_audio = vidioc_g_audio,
+ .vidioc_s_audio = vidioc_s_audio,
+ .vidioc_enumaudio = vidioc_enumaudio,
.vidioc_querymenu = vidioc_querymenu,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h
index 15db13296..79cef31a5 100644
--- a/linux/drivers/media/video/gspca/gspca.h
+++ b/linux/drivers/media/video/gspca/gspca.h
@@ -74,6 +74,8 @@ typedef void (*cam_v_op) (struct gspca_dev *);
typedef int (*cam_cf_op) (struct gspca_dev *, const struct usb_device_id *);
typedef int (*cam_jpg_op) (struct gspca_dev *,
struct v4l2_jpegcompression *);
+typedef int (*cam_streamparm_op) (struct gspca_dev *,
+ struct v4l2_streamparm *);
typedef int (*cam_qmnu_op) (struct gspca_dev *,
struct v4l2_querymenu *);
typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
@@ -106,6 +108,8 @@ struct sd_desc {
cam_jpg_op get_jcomp;
cam_jpg_op set_jcomp;
cam_qmnu_op querymenu;
+ cam_streamparm_op get_streamparm;
+ cam_streamparm_op set_streamparm;
};
/* packet types when moving from iso buf to frame buf */
diff --git a/linux/drivers/media/video/gspca/ov519.c b/linux/drivers/media/video/gspca/ov519.c
index b18d98191..5fb519da2 100644
--- a/linux/drivers/media/video/gspca/ov519.c
+++ b/linux/drivers/media/video/gspca/ov519.c
@@ -3,7 +3,18 @@
*
* Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr)
*
- * (This module is adapted from the ov51x-jpeg package)
+ * This module is adapted from the ov51x-jpeg package, which itself
+ * was adapted from the ov511 driver.
+ *
+ * Original copyright for the ov511 driver is:
+ *
+ * Copyright (c) 1999-2004 Mark W. McClelland
+ * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach
+ *
+ * ov51x-jpeg original copyright is:
+ *
+ * Copyright (c) 2004-2007 Romain Beauxis <toots@rastageeks.org>
+ * Support for OV7670 sensors was contributed by Sam Skipsey <aoanla@yahoo.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
diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c
index a574be09b..7332947cb 100644
--- a/linux/drivers/media/video/gspca/ov534.c
+++ b/linux/drivers/media/video/gspca/ov534.c
@@ -1,6 +1,7 @@
/*
* ov534/ov772x gspca driver
* Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
+ * Copyright (C) 2008 Jim Paris <jim@jtan.com>
*
* Based on a prototype written by Mark Ferrell <majortrips@gmail.com>
* USB protocol reverse engineered by Jim Paris <jim@jtan.com>
@@ -42,13 +43,12 @@ MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
MODULE_DESCRIPTION("GSPCA/OV534 USB Camera Driver");
MODULE_LICENSE("GPL");
-/* global parameters */
-static int frame_rate;
-
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- __u8 frame_rate;
+ __u32 last_fid;
+ __u32 last_pts;
+ int frame_rate;
};
/* V4L2 controls supported by the driver */
@@ -63,12 +63,12 @@ static struct v4l2_pix_format vga_mode[] = {
.priv = 0},
};
-static void ov534_reg_write(struct usb_device *udev, u16 reg, u16 val)
+static void ov534_reg_write(struct usb_device *udev, u16 reg, u8 val)
{
- u16 data = val;
+ u8 data = val;
int ret;
- PDEBUG(D_USBO, "reg=0x%04x, val=0%04x", reg, val);
+ PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val);
ret = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
0x1,
@@ -78,9 +78,9 @@ static void ov534_reg_write(struct usb_device *udev, u16 reg, u16 val)
PDEBUG(D_ERR, "write failed");
}
-static u16 ov534_reg_read(struct usb_device *udev, u16 reg)
+static u8 ov534_reg_read(struct usb_device *udev, u16 reg)
{
- u16 data;
+ u8 data;
int ret;
ret = usb_control_msg(udev,
@@ -88,30 +88,17 @@ static u16 ov534_reg_read(struct usb_device *udev, u16 reg)
0x1,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x0, reg, &data, 1, CTRL_TIMEOUT);
- PDEBUG(D_USBI, "reg=0x%04x, data=0x%04x", reg, data);
+ PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, data);
if (ret < 0)
PDEBUG(D_ERR, "read failed");
return data;
}
-static void ov534_reg_verify_write(struct usb_device *udev, u16 reg, u16 val)
-{
- u16 data;
-
- ov534_reg_write(udev, reg, val);
- data = ov534_reg_read(udev, reg);
- if (data != val) {
- PDEBUG(D_ERR | D_USBO,
- "unexpected result from read: 0x%04x != 0x%04x", val,
- data);
- }
-}
-
/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
* (direction and output)? */
static void ov534_set_led(struct usb_device *udev, int status)
{
- u16 data;
+ u8 data;
PDEBUG(D_CONF, "led status: %d", status);
@@ -130,13 +117,13 @@ static void ov534_set_led(struct usb_device *udev, int status)
static int sccb_check_status(struct usb_device *udev)
{
- u16 data;
+ u8 data;
int i;
for (i = 0; i < 5; i++) {
data = ov534_reg_read(udev, OV534_REG_STATUS);
- switch (data & 0xFF) {
+ switch (data) {
case 0x00:
return 1;
case 0x04:
@@ -151,9 +138,9 @@ static int sccb_check_status(struct usb_device *udev)
return 0;
}
-static void sccb_reg_write(struct usb_device *udev, u16 reg, u16 val)
+static void sccb_reg_write(struct usb_device *udev, u16 reg, u8 val)
{
- PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%04x", reg, val);
+ PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%02x", reg, val);
ov534_reg_write(udev, OV534_REG_SUBADDR, reg);
ov534_reg_write(udev, OV534_REG_WRITE, val);
ov534_reg_write(udev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
@@ -162,174 +149,221 @@ static void sccb_reg_write(struct usb_device *udev, u16 reg, u16 val)
PDEBUG(D_ERR, "sccb_reg_write failed");
}
-/* setup method */
-static void ov534_setup(struct usb_device *udev)
+#ifdef GSPCA_DEBUG
+static u8 sccb_reg_read(struct usb_device *udev, u16 reg)
{
- ov534_reg_verify_write(udev, 0xe7, 0x3a);
-
- ov534_reg_write(udev, OV534_REG_ADDRESS, 0x60);
- ov534_reg_write(udev, OV534_REG_ADDRESS, 0x60);
- ov534_reg_write(udev, OV534_REG_ADDRESS, 0x60);
- ov534_reg_write(udev, OV534_REG_ADDRESS, 0x42);
-
- ov534_reg_verify_write(udev, 0xc2, 0x0c);
- ov534_reg_verify_write(udev, 0x88, 0xf8);
- ov534_reg_verify_write(udev, 0xc3, 0x69);
- ov534_reg_verify_write(udev, 0x89, 0xff);
- ov534_reg_verify_write(udev, 0x76, 0x03);
- ov534_reg_verify_write(udev, 0x92, 0x01);
- ov534_reg_verify_write(udev, 0x93, 0x18);
- ov534_reg_verify_write(udev, 0x94, 0x10);
- ov534_reg_verify_write(udev, 0x95, 0x10);
- ov534_reg_verify_write(udev, 0xe2, 0x00);
- ov534_reg_verify_write(udev, 0xe7, 0x3e);
-
- ov534_reg_write(udev, 0x1c, 0x0a);
- ov534_reg_write(udev, 0x1d, 0x22);
- ov534_reg_write(udev, 0x1d, 0x06);
-
- ov534_reg_verify_write(udev, 0x96, 0x00);
-
- ov534_reg_write(udev, 0x97, 0x20);
- ov534_reg_write(udev, 0x97, 0x20);
- ov534_reg_write(udev, 0x97, 0x20);
- ov534_reg_write(udev, 0x97, 0x0a);
- ov534_reg_write(udev, 0x97, 0x3f);
- ov534_reg_write(udev, 0x97, 0x4a);
- ov534_reg_write(udev, 0x97, 0x20);
- ov534_reg_write(udev, 0x97, 0x15);
- ov534_reg_write(udev, 0x97, 0x0b);
-
- ov534_reg_verify_write(udev, 0x8e, 0x40);
- ov534_reg_verify_write(udev, 0x1f, 0x81);
- ov534_reg_verify_write(udev, 0x34, 0x05);
- ov534_reg_verify_write(udev, 0xe3, 0x04);
- ov534_reg_verify_write(udev, 0x88, 0x00);
- ov534_reg_verify_write(udev, 0x89, 0x00);
- ov534_reg_verify_write(udev, 0x76, 0x00);
- ov534_reg_verify_write(udev, 0xe7, 0x2e);
- ov534_reg_verify_write(udev, 0x31, 0xf9);
- ov534_reg_verify_write(udev, 0x25, 0x42);
- ov534_reg_verify_write(udev, 0x21, 0xf0);
-
- ov534_reg_write(udev, 0x1c, 0x00);
- ov534_reg_write(udev, 0x1d, 0x40);
- ov534_reg_write(udev, 0x1d, 0x02);
- ov534_reg_write(udev, 0x1d, 0x00);
- ov534_reg_write(udev, 0x1d, 0x02);
- ov534_reg_write(udev, 0x1d, 0x57);
- ov534_reg_write(udev, 0x1d, 0xff);
-
- ov534_reg_verify_write(udev, 0x8d, 0x1c);
- ov534_reg_verify_write(udev, 0x8e, 0x80);
- ov534_reg_verify_write(udev, 0xe5, 0x04);
+ ov534_reg_write(udev, OV534_REG_SUBADDR, reg);
+ ov534_reg_write(udev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
+ if (!sccb_check_status(udev))
+ PDEBUG(D_ERR, "sccb_reg_read failed 1");
- ov534_set_led(udev, 1);
+ ov534_reg_write(udev, OV534_REG_OPERATION, OV534_OP_READ_2);
+ if (!sccb_check_status(udev))
+ PDEBUG(D_ERR, "sccb_reg_read failed 2");
- sccb_reg_write(udev, 0x12, 0x80);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x11, 0x01);
+ return ov534_reg_read(udev, OV534_REG_READ);
+}
+#endif
- ov534_set_led(udev, 0);
+static const __u8 ov534_reg_initdata[][2] = {
+ { 0xe7, 0x3a },
+
+ { OV534_REG_ADDRESS, 0x42 }, /* select OV772x sensor */
+
+ { 0xc2, 0x0c },
+ { 0x88, 0xf8 },
+ { 0xc3, 0x69 },
+ { 0x89, 0xff },
+ { 0x76, 0x03 },
+ { 0x92, 0x01 },
+ { 0x93, 0x18 },
+ { 0x94, 0x10 },
+ { 0x95, 0x10 },
+ { 0xe2, 0x00 },
+ { 0xe7, 0x3e },
+
+ { 0x96, 0x00 },
+
+ { 0x97, 0x20 },
+ { 0x97, 0x20 },
+ { 0x97, 0x20 },
+ { 0x97, 0x0a },
+ { 0x97, 0x3f },
+ { 0x97, 0x4a },
+ { 0x97, 0x20 },
+ { 0x97, 0x15 },
+ { 0x97, 0x0b },
+
+ { 0x8e, 0x40 },
+ { 0x1f, 0x81 },
+ { 0x34, 0x05 },
+ { 0xe3, 0x04 },
+ { 0x88, 0x00 },
+ { 0x89, 0x00 },
+ { 0x76, 0x00 },
+ { 0xe7, 0x2e },
+ { 0x31, 0xf9 },
+ { 0x25, 0x42 },
+ { 0x21, 0xf0 },
+
+ { 0x1c, 0x00 },
+ { 0x1d, 0x40 },
+ { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */
+ { 0x1d, 0x00 }, /* payload size */
+ { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */
+ { 0x1d, 0x58 }, /* frame size */
+ { 0x1d, 0x00 }, /* frame size */
+
+ { 0x1c, 0x0a },
+ { 0x1d, 0x08 }, /* turn on UVC header */
+ { 0x1d, 0x0e }, /* .. */
+
+ { 0x8d, 0x1c },
+ { 0x8e, 0x80 },
+ { 0xe5, 0x04 },
+
+ { 0xc0, 0x50 },
+ { 0xc1, 0x3c },
+ { 0xc2, 0x0c },
+};
- sccb_reg_write(udev, 0x3d, 0x03);
- sccb_reg_write(udev, 0x17, 0x26);
- sccb_reg_write(udev, 0x18, 0xa0);
- sccb_reg_write(udev, 0x19, 0x07);
- sccb_reg_write(udev, 0x1a, 0xf0);
- sccb_reg_write(udev, 0x32, 0x00);
- sccb_reg_write(udev, 0x29, 0xa0);
- sccb_reg_write(udev, 0x2c, 0xf0);
- sccb_reg_write(udev, 0x65, 0x20);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x42, 0x7f);
- sccb_reg_write(udev, 0x63, 0xe0);
- sccb_reg_write(udev, 0x64, 0xff);
- sccb_reg_write(udev, 0x66, 0x00);
- sccb_reg_write(udev, 0x13, 0xf0);
- sccb_reg_write(udev, 0x0d, 0x41);
- sccb_reg_write(udev, 0x0f, 0xc5);
- sccb_reg_write(udev, 0x14, 0x11);
+static const __u8 ov772x_reg_initdata[][2] = {
+ { 0x12, 0x80 },
+ { 0x11, 0x01 },
+
+ { 0x3d, 0x03 },
+ { 0x17, 0x26 },
+ { 0x18, 0xa0 },
+ { 0x19, 0x07 },
+ { 0x1a, 0xf0 },
+ { 0x32, 0x00 },
+ { 0x29, 0xa0 },
+ { 0x2c, 0xf0 },
+ { 0x65, 0x20 },
+ { 0x11, 0x01 },
+ { 0x42, 0x7f },
+ { 0x63, 0xe0 },
+ { 0x64, 0xff },
+ { 0x66, 0x00 },
+ { 0x13, 0xf0 },
+ { 0x0d, 0x41 },
+ { 0x0f, 0xc5 },
+ { 0x14, 0x11 },
+
+ { 0x22, 0x7f },
+ { 0x23, 0x03 },
+ { 0x24, 0x40 },
+ { 0x25, 0x30 },
+ { 0x26, 0xa1 },
+ { 0x2a, 0x00 },
+ { 0x2b, 0x00 },
+ { 0x6b, 0xaa },
+ { 0x13, 0xff },
+
+ { 0x90, 0x05 },
+ { 0x91, 0x01 },
+ { 0x92, 0x03 },
+ { 0x93, 0x00 },
+ { 0x94, 0x60 },
+ { 0x95, 0x3c },
+ { 0x96, 0x24 },
+ { 0x97, 0x1e },
+ { 0x98, 0x62 },
+ { 0x99, 0x80 },
+ { 0x9a, 0x1e },
+ { 0x9b, 0x08 },
+ { 0x9c, 0x20 },
+ { 0x9e, 0x81 },
+
+ { 0xa6, 0x04 },
+ { 0x7e, 0x0c },
+ { 0x7f, 0x16 },
+ { 0x80, 0x2a },
+ { 0x81, 0x4e },
+ { 0x82, 0x61 },
+ { 0x83, 0x6f },
+ { 0x84, 0x7b },
+ { 0x85, 0x86 },
+ { 0x86, 0x8e },
+ { 0x87, 0x97 },
+ { 0x88, 0xa4 },
+ { 0x89, 0xaf },
+ { 0x8a, 0xc5 },
+ { 0x8b, 0xd7 },
+ { 0x8c, 0xe8 },
+ { 0x8d, 0x20 },
+
+ { 0x0c, 0x90 },
+
+ { 0x2b, 0x00 },
+ { 0x22, 0x7f },
+ { 0x23, 0x03 },
+ { 0x11, 0x01 },
+ { 0x0c, 0xd0 },
+ { 0x64, 0xff },
+ { 0x0d, 0x41 },
+
+ { 0x14, 0x41 },
+ { 0x0e, 0xcd },
+ { 0xac, 0xbf },
+ { 0x8e, 0x00 },
+ { 0x0c, 0xd0 }
+};
- ov534_set_led(udev, 1);
+/* set framerate */
+static void ov534_set_frame_rate(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int fr = sd->frame_rate;
- sccb_reg_write(udev, 0x22, 0x7f);
- sccb_reg_write(udev, 0x23, 0x03);
- sccb_reg_write(udev, 0x24, 0x40);
- sccb_reg_write(udev, 0x25, 0x30);
- sccb_reg_write(udev, 0x26, 0xa1);
- sccb_reg_write(udev, 0x2a, 0x00);
- sccb_reg_write(udev, 0x2b, 0x00);
- sccb_reg_write(udev, 0x6b, 0xaa);
- sccb_reg_write(udev, 0x13, 0xff);
+ switch (fr) {
+ case 50:
+ sccb_reg_write(gspca_dev->dev, 0x11, 0x01);
+ sccb_reg_write(gspca_dev->dev, 0x0d, 0x41);
+ ov534_reg_write(gspca_dev->dev, 0xe5, 0x02);
+ break;
+ case 40:
+ sccb_reg_write(gspca_dev->dev, 0x11, 0x02);
+ sccb_reg_write(gspca_dev->dev, 0x0d, 0xc1);
+ ov534_reg_write(gspca_dev->dev, 0xe5, 0x04);
+ break;
+/* case 30: */
+ default:
+ fr = 30;
+ sccb_reg_write(gspca_dev->dev, 0x11, 0x04);
+ sccb_reg_write(gspca_dev->dev, 0x0d, 0x81);
+ ov534_reg_write(gspca_dev->dev, 0xe5, 0x02);
+ break;
+ case 15:
+ sccb_reg_write(gspca_dev->dev, 0x11, 0x03);
+ sccb_reg_write(gspca_dev->dev, 0x0d, 0x41);
+ ov534_reg_write(gspca_dev->dev, 0xe5, 0x04);
+ break;
+ }
- ov534_set_led(udev, 0);
+ sd->frame_rate = fr;
+ PDEBUG(D_PROBE, "frame_rate: %d", fr);
+}
- sccb_reg_write(udev, 0x90, 0x05);
- sccb_reg_write(udev, 0x91, 0x01);
- sccb_reg_write(udev, 0x92, 0x03);
- sccb_reg_write(udev, 0x93, 0x00);
- sccb_reg_write(udev, 0x94, 0x60);
- sccb_reg_write(udev, 0x95, 0x3c);
- sccb_reg_write(udev, 0x96, 0x24);
- sccb_reg_write(udev, 0x97, 0x1e);
- sccb_reg_write(udev, 0x98, 0x62);
- sccb_reg_write(udev, 0x99, 0x80);
- sccb_reg_write(udev, 0x9a, 0x1e);
- sccb_reg_write(udev, 0x9b, 0x08);
- sccb_reg_write(udev, 0x9c, 0x20);
- sccb_reg_write(udev, 0x9e, 0x81);
+/* setup method */
+static void ov534_setup(struct usb_device *udev)
+{
+ int i;
- ov534_set_led(udev, 1);
+ /* Initialize bridge chip */
+ for (i = 0; i < ARRAY_SIZE(ov534_reg_initdata); i++)
+ ov534_reg_write(udev, ov534_reg_initdata[i][0],
+ ov534_reg_initdata[i][1]);
- sccb_reg_write(udev, 0xa6, 0x04);
- sccb_reg_write(udev, 0x7e, 0x0c);
- sccb_reg_write(udev, 0x7f, 0x16);
- sccb_reg_write(udev, 0x80, 0x2a);
- sccb_reg_write(udev, 0x81, 0x4e);
- sccb_reg_write(udev, 0x82, 0x61);
- sccb_reg_write(udev, 0x83, 0x6f);
- sccb_reg_write(udev, 0x84, 0x7b);
- sccb_reg_write(udev, 0x85, 0x86);
- sccb_reg_write(udev, 0x86, 0x8e);
- sccb_reg_write(udev, 0x87, 0x97);
- sccb_reg_write(udev, 0x88, 0xa4);
- sccb_reg_write(udev, 0x89, 0xaf);
- sccb_reg_write(udev, 0x8a, 0xc5);
- sccb_reg_write(udev, 0x8b, 0xd7);
- sccb_reg_write(udev, 0x8c, 0xe8);
- sccb_reg_write(udev, 0x8d, 0x20);
-
- sccb_reg_write(udev, 0x0c, 0x90);
-
- ov534_reg_verify_write(udev, 0xc0, 0x50);
- ov534_reg_verify_write(udev, 0xc1, 0x3c);
- ov534_reg_verify_write(udev, 0xc2, 0x0c);
+ PDEBUG(D_PROBE, "sensor is ov%02x%02x",
+ sccb_reg_read(udev, 0x0a), sccb_reg_read(udev, 0x0b));
ov534_set_led(udev, 1);
- sccb_reg_write(udev, 0x2b, 0x00);
- sccb_reg_write(udev, 0x22, 0x7f);
- sccb_reg_write(udev, 0x23, 0x03);
- sccb_reg_write(udev, 0x11, 0x01);
- sccb_reg_write(udev, 0x0c, 0xd0);
- sccb_reg_write(udev, 0x64, 0xff);
- sccb_reg_write(udev, 0x0d, 0x41);
-
- sccb_reg_write(udev, 0x14, 0x41);
- sccb_reg_write(udev, 0x0e, 0xcd);
- sccb_reg_write(udev, 0xac, 0xbf);
- sccb_reg_write(udev, 0x8e, 0x00);
- sccb_reg_write(udev, 0x0c, 0xd0);
+ /* Initialize sensor */
+ for (i = 0; i < ARRAY_SIZE(ov772x_reg_initdata); i++)
+ sccb_reg_write(udev, ov772x_reg_initdata[i][0],
+ ov772x_reg_initdata[i][1]);
ov534_reg_write(udev, 0xe0, 0x09);
ov534_set_led(udev, 0);
@@ -347,67 +381,23 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
- cam->bulk_size = vga_mode[0].sizeimage;
+ cam->bulk_size = 16384;
cam->bulk_nurbs = 2;
- PDEBUG(D_PROBE, "bulk_size = %d", cam->bulk_size);
-
return 0;
}
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *)gspca_dev;
ov534_setup(gspca_dev->dev);
-
- if (frame_rate > 0)
- sd->frame_rate = frame_rate;
-
- PDEBUG(D_PROBE, "frame_rate = %d", sd->frame_rate);
-
- switch (sd->frame_rate) {
- case 50:
- sccb_reg_write(gspca_dev->dev, 0x11, 0x01);
- sccb_check_status(gspca_dev->dev);
- sccb_reg_write(gspca_dev->dev, 0x0d, 0x41);
- sccb_check_status(gspca_dev->dev);
- ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x02);
- break;
- case 40:
- sccb_reg_write(gspca_dev->dev, 0x11, 0x02);
- sccb_check_status(gspca_dev->dev);
- sccb_reg_write(gspca_dev->dev, 0x0d, 0xc1);
- sccb_check_status(gspca_dev->dev);
- ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x04);
- break;
- case 30:
- default:
- sccb_reg_write(gspca_dev->dev, 0x11, 0x04);
- sccb_check_status(gspca_dev->dev);
- sccb_reg_write(gspca_dev->dev, 0x0d, 0x81);
- sccb_check_status(gspca_dev->dev);
- ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x02);
- break;
- case 15:
- sccb_reg_write(gspca_dev->dev, 0x11, 0x03);
- sccb_check_status(gspca_dev->dev);
- sccb_reg_write(gspca_dev->dev, 0x0d, 0x41);
- sccb_check_status(gspca_dev->dev);
- ov534_reg_verify_write(gspca_dev->dev, 0xe5, 0x04);
- break;
- };
+ ov534_set_frame_rate(gspca_dev);
return 0;
}
static int sd_start(struct gspca_dev *gspca_dev)
{
- PDEBUG(D_PROBE, "width = %d, height = %d",
- gspca_dev->width, gspca_dev->height);
-
- gspca_dev->cam.bulk_size = gspca_dev->width * gspca_dev->height * 2;
-
/* start streaming data */
ov534_set_led(gspca_dev->dev, 1);
ov534_reg_write(gspca_dev->dev, 0xe0, 0x00);
@@ -422,25 +412,130 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
ov534_set_led(gspca_dev->dev, 0);
}
+/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+#define UVC_STREAM_EOH (1 << 7)
+#define UVC_STREAM_ERR (1 << 6)
+#define UVC_STREAM_STI (1 << 5)
+#define UVC_STREAM_RES (1 << 4)
+#define UVC_STREAM_SCR (1 << 3)
+#define UVC_STREAM_PTS (1 << 2)
+#define UVC_STREAM_EOF (1 << 1)
+#define UVC_STREAM_FID (1 << 0)
+
static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
__u8 *data, int len)
{
- /*
- * The current camera setup doesn't stream the last pixel, so we set it
- * to a dummy value
- */
- __u8 last_pixel[4] = { 0, 0, 0, 0 };
- int framesize = gspca_dev->cam.bulk_size;
-
- if (len == framesize - 4) {
- frame =
- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
- frame =
- gspca_frame_add(gspca_dev, LAST_PACKET, frame, last_pixel,
- 4);
- } else
- PDEBUG(D_PACK, "packet len = %d, framesize = %d", len,
- framesize);
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u32 this_pts;
+ int 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;
+ }
+
+ /* 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;
+ }
+
+ /* Add the data from this payload */
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+ data + 12, len - 12);
+
+ /* 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;
+ }
+
+ 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;
+}
+
+/* get stream parameters (framerate) */
+int sd_get_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ cp->capability |= V4L2_CAP_TIMEPERFRAME;
+ tpf->numerator = 1;
+ tpf->denominator = sd->frame_rate;
+
+ return 0;
+}
+
+/* set stream parameters (framerate) */
+int sd_set_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+{
+ struct v4l2_captureparm *cp = &parm->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* Set requested framerate */
+ sd->frame_rate = tpf->denominator / tpf->numerator;
+ ov534_set_frame_rate(gspca_dev);
+
+ /* Return the actual framerate */
+ tpf->numerator = 1;
+ tpf->denominator = sd->frame_rate;
+
+ return 0;
}
/* sub-driver description */
@@ -453,6 +548,8 @@ static const struct sd_desc sd_desc = {
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
+ .get_streamparm = sd_get_streamparm,
+ .set_streamparm = sd_set_streamparm,
};
/* -- module initialisation -- */
@@ -500,6 +597,3 @@ static void __exit sd_mod_exit(void)
module_init(sd_mod_init);
module_exit(sd_mod_exit);
-
-module_param(frame_rate, int, 0644);
-MODULE_PARM_DESC(frame_rate, "Frame rate (15, 30, 40, 50)");
diff --git a/linux/drivers/media/video/gspca/pac7311.c b/linux/drivers/media/video/gspca/pac7311.c
index f443df77e..b0a9d0687 100644
--- a/linux/drivers/media/video/gspca/pac7311.c
+++ b/linux/drivers/media/video/gspca/pac7311.c
@@ -1079,6 +1079,7 @@ static __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311},
{USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311},
{USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302},
+ {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302},
{USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302},
diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c
index 182b84f1c..ff94cd639 100644
--- a/linux/drivers/media/video/gspca/sonixj.c
+++ b/linux/drivers/media/video/gspca/sonixj.c
@@ -37,24 +37,26 @@ struct sd {
atomic_t avg_lum;
unsigned int exposure;
- unsigned short brightness;
- unsigned char contrast;
- unsigned char colors;
- unsigned char autogain;
+ __u16 brightness;
+ __u8 contrast;
+ __u8 colors;
+ __u8 autogain;
+ __u8 blue;
+ __u8 red;
__u8 vflip; /* ov7630 only */
__u8 infrared; /* mi0360 only */
- signed char ag_cnt;
+ __s8 ag_cnt;
#define AG_CNT_START 13
- char qindex;
- unsigned char bridge;
+ __u8 qindex;
+ __u8 bridge;
#define BRIDGE_SN9C102P 0
#define BRIDGE_SN9C105 1
#define BRIDGE_SN9C110 2
#define BRIDGE_SN9C120 3
#define BRIDGE_SN9C325 4
- char 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
@@ -62,7 +64,7 @@ struct sd {
#define SENSOR_OV7630 4
#define SENSOR_OV7648 5
#define SENSOR_OV7660 6
- unsigned char i2c_base;
+ __u8 i2c_base;
};
/* V4L2 controls supported by the driver */
@@ -72,6 +74,10 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getcontrast(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_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_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);
@@ -89,7 +95,7 @@ static struct ctrl sd_ctrls[] = {
#define BRIGHTNESS_MAX 0xffff
.maximum = BRIGHTNESS_MAX,
.step = 1,
-#define BRIGHTNESS_DEF 0x7fff
+#define BRIGHTNESS_DEF 0x8000
.default_value = BRIGHTNESS_DEF,
},
.set = sd_setbrightness,
@@ -116,7 +122,7 @@ static struct ctrl sd_ctrls[] = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Color",
.minimum = 0,
- .maximum = 64,
+ .maximum = 40,
.step = 1,
#define COLOR_DEF 32
.default_value = COLOR_DEF,
@@ -124,7 +130,35 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setcolors,
.get = sd_getcolors,
},
-#define AUTOGAIN_IDX 3
+ {
+ {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = 24,
+ .maximum = 40,
+ .step = 1,
+#define BLUE_BALANCE_DEF 32
+ .default_value = BLUE_BALANCE_DEF,
+ },
+ .set = sd_setblue_balance,
+ .get = sd_getblue_balance,
+ },
+ {
+ {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = 24,
+ .maximum = 40,
+ .step = 1,
+#define RED_BALANCE_DEF 32
+ .default_value = RED_BALANCE_DEF,
+ },
+ .set = sd_setred_balance,
+ .get = sd_getred_balance,
+ },
+#define AUTOGAIN_IDX 5
{
{
.id = V4L2_CID_AUTOGAIN,
@@ -140,7 +174,7 @@ static struct ctrl sd_ctrls[] = {
.get = sd_getautogain,
},
/* ov7630 only */
-#define VFLIP_IDX 4
+#define VFLIP_IDX 6
{
{
.id = V4L2_CID_VFLIP,
@@ -156,7 +190,7 @@ static struct ctrl sd_ctrls[] = {
.get = sd_getvflip,
},
/* mi0360 only */
-#define INFRARED_IDX 5
+#define INFRARED_IDX 7
{
{
.id = V4L2_CID_INFRARED,
@@ -173,6 +207,24 @@ static struct ctrl sd_ctrls[] = {
},
};
+/* table of the disabled controls */
+static __u32 ctrl_dis[] = {
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_HV7131R 0 */
+ (1 << VFLIP_IDX),
+ /* SENSOR_MI0360 1 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_MO4000 2 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_OM6802 3 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX),
+ /* SENSOR_OV7630 4 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_OV7648 5 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_OV7660 6 */
+};
+
static struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 160,
@@ -767,8 +819,6 @@ static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg)
static int probesensor(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */
msleep(10);
reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */
@@ -780,8 +830,7 @@ static int probesensor(struct gspca_dev *gspca_dev)
&& gspca_dev->usb_buf[3] == 0x00
&& gspca_dev->usb_buf[4] == 0x00) {
PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R");
- sd->sensor = SENSOR_HV7131R;
- return SENSOR_HV7131R;
+ return 0;
}
PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x",
gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
@@ -989,22 +1038,14 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->brightness = BRIGHTNESS_DEF;
sd->contrast = CONTRAST_DEF;
sd->colors = COLOR_DEF;
+ sd->blue = BLUE_BALANCE_DEF;
+ sd->red = RED_BALANCE_DEF;
sd->autogain = AUTOGAIN_DEF;
sd->ag_cnt = -1;
sd->vflip = VFLIP_DEF;
sd->infrared = INFRARED_DEF;
- switch (sd->sensor) {
- case SENSOR_OV7630:
- case SENSOR_OV7648:
- case SENSOR_OV7660:
- gspca_dev->ctrl_dis = (1 << AUTOGAIN_IDX);
- break;
- }
- if (sd->sensor != SENSOR_OV7630)
- gspca_dev->ctrl_dis |= (1 << VFLIP_IDX);
- if (sd->sensor != SENSOR_MI0360)
- gspca_dev->ctrl_dis |= (1 << INFRARED_IDX);
+ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
return 0;
}
@@ -1012,7 +1053,6 @@ 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;
-/* const __u8 *sn9c1xx; */
__u8 regGpio[] = { 0x29, 0x74 };
__u8 regF1;
@@ -1051,12 +1091,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0xf1, 0x01);
-#if 1 /*jfm: from win trace*/
return 0;
-#else
- sn9c1xx = sn_tb[(int) sd->sensor];
- return configure_gpio(gspca_dev, sn9c1xx);
-#endif
}
static unsigned int setexposure(struct gspca_dev *gspca_dev,
@@ -1136,32 +1171,13 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev,
return expo;
}
-/* this function is used for sensors o76xx only */
-static void setbrightcont(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int val;
- __u8 reg84_full[0x15];
-
- memcpy(reg84_full, reg84, sizeof reg84_full);
- val = sd->contrast * 0x30 / CONTRAST_MAX + 0x10; /* 10..40 */
- reg84_full[0] = (val + 1) / 2; /* red */
- reg84_full[2] = val; /* green */
- reg84_full[4] = (val + 1) / 5; /* blue */
- val = (sd->brightness - BRIGHTNESS_DEF) * 0x10
- / BRIGHTNESS_MAX;
- reg84_full[0x12] = val & 0x1f; /* 5:0 signed value */
- reg_w(gspca_dev, 0x84, reg84_full, sizeof reg84_full);
-}
-
-/* sensor != ov76xx */
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
unsigned int expo;
__u8 k2;
- k2 = sd->brightness >> 10;
+ k2 = ((int) sd->brightness - 0x8000) >> 10;
switch (sd->sensor) {
case SENSOR_HV7131R:
expo = sd->brightness << 4;
@@ -1183,38 +1199,49 @@ static void setbrightness(struct gspca_dev *gspca_dev)
break;
}
- reg_w1(gspca_dev, 0x96, k2);
+ reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
}
-/* sensor != ov76xx */
static void setcontrast(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
__u8 k2;
- __u8 contrast[] = { 0x00, 0x00, 0x28, 0x00, 0x07, 0x00 };
-
- k2 = sd->contrast;
- contrast[2] = k2;
- contrast[0] = (k2 + 1) >> 1;
- contrast[4] = (k2 + 1) / 5;
- reg_w(gspca_dev, 0x84, contrast, 6);
+ __u8 contrast[6];
+
+ k2 = sd->contrast * 0x30 / (CONTRAST_MAX + 1) + 0x10; /* 10..40 */
+ contrast[0] = (k2 + 1) / 2; /* red */
+ contrast[1] = 0;
+ contrast[2] = k2; /* green */
+ contrast[3] = 0;
+ contrast[4] = (k2 + 1) / 5; /* blue */
+ contrast[5] = 0;
+ reg_w(gspca_dev, 0x84, contrast, sizeof contrast);
}
static void setcolors(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- __u8 blue, red;
-
- if (sd->colors >= 32) {
- red = 32 + (sd->colors - 32) / 2;
- blue = 64 - sd->colors;
- } else {
- red = sd->colors;
- blue = 32 + (32 - sd->colors) / 2;
+ int i, v;
+ __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 */
+ };
+ for (i = 0; i < 6; i++) {
+ v = uv[i] * sd->colors / COLOR_DEF;
+ reg8a[i * 2] = v;
+ reg8a[i * 2 + 1] = (v >> 8) & 0x0f;
}
- reg_w1(gspca_dev, 0x05, red);
+ reg_w(gspca_dev, 0x8a, reg8a, sizeof reg8a);
+}
+
+static void setredblue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ reg_w1(gspca_dev, 0x05, sd->red);
/* reg_w1(gspca_dev, 0x07, 32); */
- reg_w1(gspca_dev, 0x06, blue);
+ reg_w1(gspca_dev, 0x06, sd->blue);
}
static void setautogain(struct gspca_dev *gspca_dev)
@@ -1300,9 +1327,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
}
reg_w1(gspca_dev, 0x17, reg17);
/* set reg1 was here */
- reg_w1(gspca_dev, 0x05, sn9c1xx[5]);
- reg_w1(gspca_dev, 0x07, sn9c1xx[7]);
- reg_w1(gspca_dev, 0x06, sn9c1xx[6]);
+ reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */
+ 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);
for (i = 0; i < 8; i++)
@@ -1361,27 +1388,19 @@ static int sd_start(struct gspca_dev *gspca_dev)
ov7648_InitSensor(gspca_dev);
reg17 = 0x21;
/* reg1 = 0x42; * 42 - 46? */
-/* if (mode)
- ; * 320x2...
- else
- ; * 640x... */
break;
default:
/* case SENSOR_OV7660: */
ov7660_InitSensor(gspca_dev);
- if (mode) {
-/* reg17 = 0x21; * 320 */
-/* reg1 = 0x44; */
-/* reg1 = 0x46; (done) */
- } else { /* 640 */
- if (sd->bridge == BRIDGE_SN9C120) {
+ if (sd->bridge == BRIDGE_SN9C120) {
+ if (mode) { /* 320x240 - 160x120 */
reg17 = 0xa2;
reg1 = 0x44; /* 48 Mhz, video trf eneble */
- } else {
- reg17 = 0x22;
- reg1 = 0x06; /* 24 Mhz, video trf eneble
- * inverse power down */
}
+ } else {
+ reg17 = 0x22;
+ reg1 = 0x06; /* 24 Mhz, video trf eneble
+ * inverse power down */
}
break;
}
@@ -1413,20 +1432,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_MI0360:
setinfrared(sd);
- /* fall thru */
- case SENSOR_HV7131R:
- case SENSOR_MO4000:
- case SENSOR_OM6802:
- setbrightness(gspca_dev);
- setcontrast(gspca_dev);
break;
case SENSOR_OV7630:
setvflip(sd);
- /* fall thru */
- default: /* OV76xx */
- setbrightcont(gspca_dev);
break;
}
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
setautogain(gspca_dev);
return 0;
}
@@ -1510,7 +1522,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
expotimes = 0;
sd->exposure = setexposure(gspca_dev,
(unsigned int) expotimes);
- setcolors(gspca_dev);
+ setredblue(gspca_dev);
break;
}
}
@@ -1564,19 +1576,8 @@ 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) {
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- case SENSOR_MI0360:
- case SENSOR_MO4000:
- case SENSOR_OM6802:
- setbrightness(gspca_dev);
- break;
- default: /* OV76xx */
- setbrightcont(gspca_dev);
- break;
- }
- }
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
return 0;
}
@@ -1593,19 +1594,8 @@ 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) {
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- case SENSOR_MI0360:
- case SENSOR_MO4000:
- case SENSOR_OM6802:
- setcontrast(gspca_dev);
- break;
- default: /* OV76xx */
- setbrightcont(gspca_dev);
- break;
- }
- }
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
return 0;
}
@@ -1635,6 +1625,42 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
return 0;
}
+static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->blue = val;
+ if (gspca_dev->streaming)
+ setredblue(gspca_dev);
+ return 0;
+}
+
+static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->blue;
+ return 0;
+}
+
+static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->red = val;
+ if (gspca_dev->streaming)
+ setredblue(gspca_dev);
+ return 0;
+}
+
+static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->red;
+ return 0;
+}
+
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1719,6 +1745,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
#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)},
{USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)},
/* bw600.inf:
{USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */
diff --git a/linux/drivers/media/video/gspca/stk014.c b/linux/drivers/media/video/gspca/stk014.c
index d9d64911f..74f57db53 100644
--- a/linux/drivers/media/video/gspca/stk014.c
+++ b/linux/drivers/media/video/gspca/stk014.c
@@ -424,10 +424,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* beginning of the frame */
#define STKHDRSZ 12
- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
- data + STKHDRSZ, len - STKHDRSZ);
-#undef STKHDRSZ
- return;
+ data += STKHDRSZ;
+ len -= STKHDRSZ;
}
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c
index f6fae60ae..4cc1f69f1 100644
--- a/linux/drivers/media/video/gspca/vc032x.c
+++ b/linux/drivers/media/video/gspca/vc032x.c
@@ -32,44 +32,68 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- unsigned char autogain;
- unsigned char lightfreq;
+ __u8 hflip;
+ __u8 vflip;
+ __u8 lightfreq;
+ __u8 sharpness;
- char qindex;
char bridge;
#define BRIDGE_VC0321 0
#define BRIDGE_VC0323 1
char sensor;
#define SENSOR_HV7131R 0
-#define SENSOR_MI1320 1
-#define SENSOR_MI1310_SOC 2
-#define SENSOR_OV7660 3
-#define SENSOR_OV7670 4
-#define SENSOR_PO3130NC 5
+#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
};
/* V4L2 controls supported by the driver */
-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_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
static int sd_getfreq(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[] = {
+/* next 2 controls work with ov7660 and ov7670 only */
+#define HFLIP_IDX 0
{
{
- .id = V4L2_CID_AUTOGAIN,
+ .id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
+ .name = "Mirror",
.minimum = 0,
.maximum = 1,
.step = 1,
-#define AUTOGAIN_DEF 1
- .default_value = AUTOGAIN_DEF,
+#define HFLIP_DEF 0
+ .default_value = HFLIP_DEF,
},
- .set = sd_setautogain,
- .get = sd_getautogain,
+ .set = sd_sethflip,
+ .get = sd_gethflip,
},
-#define LIGHTFREQ_IDX 1
+#define VFLIP_IDX 1
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Vflip",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+#define VFLIP_DEF 0
+ .default_value = VFLIP_DEF,
+ },
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
+#define LIGHTFREQ_IDX 2
{
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
@@ -84,6 +108,22 @@ static struct ctrl sd_ctrls[] = {
.set = sd_setfreq,
.get = sd_getfreq,
},
+/* po1200 only */
+#define SHARPNESS_IDX 3
+ {
+ {
+ .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 struct v4l2_pix_format vc0321_mode[] = {
@@ -111,15 +151,252 @@ static struct v4l2_pix_format vc0323_mode[] = {
.priv = 0},
};
-#if 0
-static const __u8 mi1310soc_gamma[17] = {
- 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
- 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+static struct v4l2_pix_format svga_mode[] = {
+ {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600 * 1 / 4 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
};
-static const __u8 mi1310soc_matrix[9] = {
- 0x56, 0xf0, 0xf6, 0xf3, 0x57, 0xf6, 0xf3, 0xef, 0x58
+
+/* OV7660/7670 registers */
+#define OV7660_REG_MVFP 0x1e
+#define OV7660_MVFP_MIRROR 0x20
+#define OV7660_MVFP_VFLIP 0x10
+
+static const __u8 mi0360_matrix[9] = {
+ 0x50, 0xf8, 0xf8, 0xf5, 0x50, 0xfb, 0xff, 0xf1, 0x50
};
-#endif
+
+static const __u8 mi0360_initVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xb8, 0x00, 0x13, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x14, 0x18, 0xcc},
+ {0xb8, 0xb2, 0x0a, 0xcc},
+ {0xb8, 0xb4, 0x0a, 0xcc},
+ {0xb8, 0xb5, 0x0a, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x31, 0x00, 0x00, 0xbb},
+ {0x09, 0x01, 0xc7, 0xbb},
+ {0x34, 0x01, 0x00, 0xbb},
+ {0x2b, 0x00, 0x28, 0xbb},
+ {0x2c, 0x00, 0x30, 0xbb},
+ {0x2d, 0x00, 0x30, 0xbb},
+ {0x2e, 0x00, 0x28, 0xbb},
+ {0x62, 0x04, 0x11, 0xbb},
+ {0x03, 0x01, 0xe0, 0xbb},
+ {0x2c, 0x00, 0x2c, 0xbb},
+ {0x20, 0xd0, 0x00, 0xbb},
+ {0x01, 0x00, 0x08, 0xbb},
+ {0x06, 0x00, 0x10, 0xbb},
+ {0x05, 0x00, 0x20, 0xbb},
+ {0x20, 0x00, 0x00, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x02, 0xcc},
+ {0xb6, 0x02, 0x80, 0xcc},
+ {0xb6, 0x05, 0x01, 0xcc},
+ {0xb6, 0x04, 0xe0, 0xcc},
+ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x02, 0xcc},
+ {0xb6, 0x17, 0x58, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x10, 0xcc},
+ {0xb9, 0x12, 0x00, 0xcc},
+ {0xb9, 0x13, 0x0a, 0xcc},
+ {0xb9, 0x14, 0x0a, 0xcc},
+ {0xb9, 0x15, 0x0a, 0xcc},
+ {0xb9, 0x16, 0x0a, 0xcc},
+ {0xb9, 0x18, 0x00, 0xcc},
+ {0xb9, 0x19, 0x0f, 0xcc},
+ {0xb9, 0x1a, 0x0f, 0xcc},
+ {0xb9, 0x1b, 0x0f, 0xcc},
+ {0xb9, 0x1c, 0x0f, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xb6, 0x13, 0x13, 0xcc},
+ {0x35, 0x00, 0x60, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+static const __u8 mi0360_initQVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb3, 0x00, 0x24, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x03, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x01, 0xcc},
+ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xbc, 0x00, 0xd1, 0xcc},
+ {0xb8, 0x00, 0x13, 0xcc},
+ {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x2c, 0x50, 0xcc},
+ {0xb8, 0x2d, 0xf8, 0xcc},
+ {0xb8, 0x2e, 0xf8, 0xcc},
+ {0xb8, 0x2f, 0xf8, 0xcc},
+ {0xb8, 0x30, 0x50, 0xcc},
+ {0xb8, 0x31, 0xf8, 0xcc},
+ {0xb8, 0x32, 0xf8, 0xcc},
+ {0xb8, 0x33, 0xf8, 0xcc},
+ {0xb8, 0x34, 0x50, 0xcc},
+ {0xb8, 0x35, 0x00, 0xcc},
+ {0xb8, 0x36, 0x00, 0xcc},
+ {0xb8, 0x37, 0x00, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x08, 0xe0, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc},
+ {0xb8, 0x14, 0x18, 0xcc},
+ {0xb8, 0xb2, 0x0a, 0xcc},
+ {0xb8, 0xb4, 0x0a, 0xcc},
+ {0xb8, 0xb5, 0x0a, 0xcc},
+ {0xb8, 0xfe, 0x00, 0xcc},
+ {0xb8, 0xff, 0x28, 0xcc},
+ {0xb9, 0x00, 0x28, 0xcc},
+ {0xb9, 0x01, 0x28, 0xcc},
+ {0xb9, 0x02, 0x28, 0xcc},
+ {0xb9, 0x03, 0x00, 0xcc},
+ {0xb9, 0x04, 0x00, 0xcc},
+ {0xb9, 0x05, 0x3c, 0xcc},
+ {0xb9, 0x06, 0x3c, 0xcc},
+ {0xb9, 0x07, 0x3c, 0xcc},
+ {0xb9, 0x08, 0x3c, 0xcc},
+ {0xb8, 0x8e, 0x00, 0xcc},
+ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x31, 0x00, 0x00, 0xbb},
+ {0x09, 0x01, 0xc7, 0xbb},
+ {0x34, 0x01, 0x00, 0xbb},
+ {0x2b, 0x00, 0x28, 0xbb},
+ {0x2c, 0x00, 0x30, 0xbb},
+ {0x2d, 0x00, 0x30, 0xbb},
+ {0x2e, 0x00, 0x28, 0xbb},
+ {0x62, 0x04, 0x11, 0xbb},
+ {0x03, 0x01, 0xe0, 0xbb},
+ {0x2c, 0x00, 0x2c, 0xbb},
+ {0x20, 0xd0, 0x00, 0xbb},
+ {0x01, 0x00, 0x08, 0xbb},
+ {0x06, 0x00, 0x10, 0xbb},
+ {0x05, 0x00, 0x20, 0xbb},
+ {0x20, 0x00, 0x00, 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},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x13, 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},
+ {0xb8, 0x0c, 0x20, 0xcc},
+ {0xb8, 0x0d, 0x70, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+ {0x35, 0x00, 0xef, 0xbb},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {}
+};
+
static const __u8 mi1310_socinitVGA_JPG[][4] = {
{0xb0, 0x03, 0x19, 0xcc},
{0xb0, 0x04, 0x02, 0xcc},
@@ -832,7 +1109,7 @@ static const __u8 ov7660_initVGA_data[][4] = {
{0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
{0x00, 0x12, 0x80, 0xaa},
{0x00, 0x12, 0x05, 0xaa},
- {0x00, 0x1e, 0x01, 0xaa},
+ {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
{0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
{0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
{0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
@@ -886,7 +1163,7 @@ static const __u8 ov7660_initQVGA_data[][4] = {
{0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x8f, 0x50, 0xcc},
{0x00, 0x01, 0x80, 0xaa}, {0x00, 0x02, 0x80, 0xaa},
{0x00, 0x12, 0x80, 0xaa}, {0x00, 0x12, 0x05, 0xaa},
- {0x00, 0x1e, 0x01, 0xaa},
+ {0x00, 0x1e, 0x01, 0xaa}, /* MVFP */
{0x00, 0x3d, 0x40, 0xaa}, /* 0x3d <-40 gamma 01 */
{0x00, 0x41, 0x00, 0xaa}, /* edge 00 */
{0x00, 0x0d, 0x48, 0xaa}, {0x00, 0x0e, 0x04, 0xaa},
@@ -992,7 +1269,8 @@ static const __u8 ov7670_initVGA_JPG[][4] = {
{0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa},
{0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa},
- {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa},
+ {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
+ {0x00, 0x21, 0x02, 0xaa},
{0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa},
{0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa},
{0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa},
@@ -1057,7 +1335,8 @@ static const __u8 ov7670_initVGA_JPG[][4] = {
{0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa},
{0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa},
{0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa},
- {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
+ {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa},
{0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa},
{0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa},
@@ -1119,7 +1398,8 @@ static const __u8 ov7670_initQVGA_JPG[][4] = {
{0x00, 0xa9, 0x90, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x13, 0xe5, 0xaa}, {0x00, 0x0e, 0x61, 0xaa},
{0x00, 0x0f, 0x4b, 0xaa}, {0x00, 0x16, 0x02, 0xaa},
- {0x00, 0x1e, 0x07, 0xaa}, {0x00, 0x21, 0x02, 0xaa},
+ {0x00, 0x1e, 0x07, 0xaa}, /* MVFP */
+ {0x00, 0x21, 0x02, 0xaa},
{0x00, 0x22, 0x91, 0xaa}, {0x00, 0x29, 0x07, 0xaa},
{0x00, 0x33, 0x0b, 0xaa}, {0x00, 0x35, 0x0b, 0xaa},
{0x00, 0x37, 0x1d, 0xaa}, {0x00, 0x38, 0x71, 0xaa},
@@ -1184,7 +1464,8 @@ static const __u8 ov7670_initQVGA_JPG[][4] = {
{0x00, 0x71, 0x35, 0xaa}, {0x00, 0x72, 0x11, 0xaa},
{0x00, 0x73, 0xf0, 0xaa}, {0x00, 0xa2, 0x02, 0xaa},
{0x00, 0xb1, 0x00, 0xaa}, {0x00, 0xb1, 0x0c, 0xaa},
- {0x00, 0x1e, 0x37, 0xaa}, {0x00, 0xaa, 0x14, 0xaa},
+ {0x00, 0x1e, 0x37, 0xaa}, /* MVFP */
+ {0x00, 0xaa, 0x14, 0xaa},
{0x00, 0x24, 0x80, 0xaa}, {0x00, 0x25, 0x74, 0xaa},
{0x00, 0x26, 0xd3, 0xaa}, {0x00, 0x0d, 0x00, 0xaa},
{0x00, 0x14, 0x18, 0xaa}, {0x00, 0x9d, 0x99, 0xaa},
@@ -1212,177 +1493,301 @@ static const __u8 ov7670_initQVGA_JPG[][4] = {
{0x00, 0x77, 0x05, 0xaa },
{},
};
+
+/* PO1200 - values from usbvm326.inf and ms-win trace */
+static const __u8 po1200_gamma[17] = {
+#if 1
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+#else
+/*ms-win trace*/
+ 0x01, 0x0b, 0x1e, 0x38, 0x51, 0x6b, 0x83, 0x9a, 0xaf,
+ 0xc1, 0xd0, 0xdd, 0xe8, 0xf2, 0xf9, 0xff, 0xff
+#endif
+};
+static const __u8 po1200_matrix[9] = {
+ 0x60, 0xf9, 0xe5, 0xe7, 0x50, 0x05, 0xf3, 0xe6, 0x5e
+};
+static const __u8 po1200_initVGA_data[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc}, /* reset? */
#if 0
-static const __u8 ov7670_initQVGA_JPG[][4] = {
- {0xb3, 0x00, 0x00, 0xcc},
- {0xb0, 0x16, 0x0d, 0xcc},
- {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x16, 0x00, 0xcc},
- {0x00, 0x00, 0x50, 0xdd}, {0xb0, 0x03, 0x19, 0xcc},
- {0x00, 0x00, 0x50, 0xdd}, {0xb3, 0x49, 0x11, 0xcc},
- {0xb3, 0x49, 0x00, 0xcc}, {0xb0, 0x16, 0x00, 0xcc},
-
- {0xb3, 0x01, 0x05, 0xcc}, {0x00, 0x00, 0x50, 0xdd},
- {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc},
- {0x00, 0x00, 0x50, 0xdd}, {0xb3, 0x00, 0x66, 0xcc},
- {0xb3, 0x00, 0x67, 0xcc}, {0xb3, 0x35, 0xa1, 0xcc},
- {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x05, 0x01, 0xcc},
- {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
- {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x02, 0x02, 0xcc},
- {0xb3, 0x03, 0x1f, 0xcc}, {0xb3, 0x14, 0x00, 0xcc},
- {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x02, 0xcc},
- {0xb3, 0x17, 0x7f, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
- {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc},
- {0xb3, 0x22, 0x01, 0xcc}, {0xb3, 0x23, 0xe0, 0xcc},
- {0xbc, 0x00, 0xd1, 0xcc}, /* set QVGA */
- {0xbc, 0x01, 0x01, 0xcc},/* */
- {0x00, 0x12, 0x80, 0xaa},/* OV sensor reset */
- {0x00, 0x00, 0x50, 0xdd},/* wait sometimes */
- {0, 0x12, 0x00, 0xaa},
- {0, 0x11, 0x40, 0xaa}, {0, 0x6b, 0x0a, 0xaa},
- {0, 0x3a, 0x04, 0xaa}, {0, 0x40, 0xc0, 0xaa},
- {0, 0x8c, 0x00, 0xaa}, {0, 0x7a, 0x29, 0xaa},
- {0, 0x7b, 0x0e, 0xaa}, {0, 0x7c, 0x1a, 0xaa},
- {0, 0x7d, 0x31, 0xaa}, {0, 0x7e, 0x53, 0xaa},
- {0, 0x7f, 0x60, 0xaa}, {0, 0x80, 0x6b, 0xaa},
- {0, 0x81, 0x73, 0xaa}, {0, 0x82, 0x7b, 0xaa},
- {0, 0x83, 0x82, 0xaa}, {0, 0x84, 0x89, 0xaa},
- {0, 0x85, 0x96, 0xaa}, {0, 0x86, 0xa1, 0xaa},
- {0, 0x87, 0xb7, 0xaa}, {0, 0x88, 0xcc, 0xaa},
- {0, 0x89, 0xe1, 0xaa}, {0, 0x13, 0xe0, 0xaa},
- {0, 0x00, 0x00, 0xaa}, {0, 0x10, 0x00, 0xaa},
- {0, 0x0d, 0x40, 0xaa}, {0, 0x14, 0x28, 0xaa},
- {0, 0xa5, 0x05, 0xaa}, {0, 0xab, 0x07, 0xaa},
- {0, 0x24, 0x95, 0xaa}, {0, 0x25, 0x33, 0xaa},
- {0, 0x26, 0xe3, 0xaa}, {0, 0x9f, 0x88, 0xaa},
- {0, 0xa0, 0x78, 0xaa}, {0, 0x55, 0x90, 0xaa},
- {0, 0xa1, 0x03, 0xaa}, {0, 0xa6, 0xe0, 0xaa},
- {0, 0xa7, 0xd8, 0xaa}, {0, 0xa8, 0xf0, 0xaa},
- {0, 0xa9, 0x90, 0xaa}, {0, 0xaa, 0x14, 0xaa},
- {0, 0x13, 0xe5, 0xaa}, {0, 0x0e, 0x61, 0xaa},
- {0, 0x0f, 0x4b, 0xaa}, {0, 0x16, 0x02, 0xaa},
- {0, 0x1e, 0x07, 0xaa}, {0, 0x21, 0x02, 0xaa},
- {0, 0x22, 0x91, 0xaa}, {0, 0x29, 0x07, 0xaa},
- {0, 0x33, 0x0b, 0xaa}, {0, 0x35, 0x0b, 0xaa},
- {0, 0x37, 0x1d, 0xaa}, {0, 0x38, 0x71, 0xaa},
- {0, 0x39, 0x2a, 0xaa}, {0, 0x3c, 0x78, 0xaa},
- {0, 0x4d, 0x40, 0xaa}, {0, 0x4e, 0x20, 0xaa},
- {0, 0x74, 0x19, 0xaa}, {0, 0x8d, 0x4f, 0xaa},
- {0, 0x8e, 0x00, 0xaa}, {0, 0x8f, 0x00, 0xaa},
- {0, 0x90, 0x00, 0xaa}, {0, 0x91, 0x00, 0xaa},
- {0, 0x96, 0x00, 0xaa}, {0, 0x9a, 0x80, 0xaa},
- {0, 0xb0, 0x84, 0xaa}, {0, 0xb1, 0x0c, 0xaa},
- {0, 0xb2, 0x0e, 0xaa}, {0, 0xb3, 0x82, 0xaa},
- {0, 0xb8, 0x0a, 0xaa}, {0, 0x43, 0x14, 0xaa},
- {0, 0x44, 0xf0, 0xaa}, {0, 0x45, 0x45, 0xaa},
- {0, 0x46, 0x63, 0xaa}, {0, 0x47, 0x2d, 0xaa},
- {0, 0x48, 0x46, 0xaa}, {0, 0x59, 0x88, 0xaa},
- {0, 0x5a, 0xa0, 0xaa}, {0, 0x5b, 0xc6, 0xaa},
- {0, 0x5c, 0x7d, 0xaa}, {0, 0x5d, 0x5f, 0xaa},
- {0, 0x5e, 0x19, 0xaa}, {0, 0x6c, 0x0a, 0xaa},
- {0, 0x6d, 0x55, 0xaa}, {0, 0x6e, 0x11, 0xaa},
- {0, 0x6f, 0x9e, 0xaa}, {0, 0x69, 0x00, 0xaa},
- {0, 0x6a, 0x40, 0xaa}, {0, 0x01, 0x40, 0xaa},
- {0, 0x02, 0x40, 0xaa}, {0, 0x13, 0xe7, 0xaa},
- {0, 0x5f, 0xf0, 0xaa}, {0, 0x60, 0xf0, 0xaa},
- {0, 0x61, 0xf0, 0xaa}, {0, 0x27, 0xa0, 0xaa},
- {0, 0x28, 0x80, 0xaa}, {0, 0x2c, 0x90, 0xaa},
- {0, 0x4f, 0x66, 0xaa}, {0, 0x50, 0x66, 0xaa},
- {0, 0x51, 0x00, 0xaa}, {0, 0x52, 0x22, 0xaa},
- {0, 0x53, 0x5e, 0xaa}, {0, 0x54, 0x80, 0xaa},
- {0, 0x58, 0x9e, 0xaa}, {0, 0x41, 0x08, 0xaa},
- {0, 0x3f, 0x00, 0xaa}, {0, 0x75, 0x85, 0xaa},
- {0, 0x76, 0xe1, 0xaa}, {0, 0x4c, 0x00, 0xaa},
- {0, 0x77, 0x0a, 0xaa}, {0, 0x3d, 0x88, 0xaa},
- {0, 0x4b, 0x09, 0xaa}, {0, 0xc9, 0x60, 0xaa},
- {0, 0x41, 0x38, 0xaa}, {0, 0x62, 0x30, 0xaa},
- {0, 0x63, 0x30, 0xaa}, {0, 0x64, 0x08, 0xaa},
- {0, 0x94, 0x07, 0xaa}, {0, 0x95, 0x0b, 0xaa},
- {0, 0x65, 0x00, 0xaa}, {0, 0x66, 0x05, 0xaa},
- {0, 0x56, 0x50, 0xaa}, {0, 0x34, 0x11, 0xaa},
- {0, 0xa4, 0x88, 0xaa}, {0, 0x96, 0x00, 0xaa},
- {0, 0x97, 0x30, 0xaa}, {0, 0x98, 0x20, 0xaa},
- {0, 0x99, 0x30, 0xaa}, {0, 0x9a, 0x84, 0xaa},
- {0, 0x9b, 0x29, 0xaa}, {0, 0x9c, 0x03, 0xaa},
- {0, 0x78, 0x04, 0xaa}, {0, 0x79, 0x01, 0xaa},
- {0, 0xc8, 0xf0, 0xaa}, {0, 0x79, 0x0f, 0xaa},
- {0, 0xc8, 0x00, 0xaa}, {0, 0x79, 0x10, 0xaa},
- {0, 0xc8, 0x7e, 0xaa}, {0, 0x79, 0x0a, 0xaa},
- {0, 0xc8, 0x80, 0xaa}, {0, 0x79, 0x0b, 0xaa},
- {0, 0xc8, 0x01, 0xaa}, {0, 0x79, 0x0c, 0xaa},
- {0, 0xc8, 0x0f, 0xaa}, {0, 0x79, 0x0d, 0xaa},
- {0, 0xc8, 0x20, 0xaa}, {0, 0x79, 0x09, 0xaa},
- {0, 0xc8, 0x80, 0xaa}, {0, 0x79, 0x02, 0xaa},
- {0, 0xc8, 0xc0, 0xaa}, {0, 0x79, 0x03, 0xaa},
- {0, 0xc8, 0x40, 0xaa}, {0, 0x79, 0x05, 0xaa},
- {0, 0xc8, 0x30, 0xaa}, {0, 0x79, 0x26, 0xaa},
- {0, 0x11, 0x40, 0xaa}, {0, 0x3a, 0x04, 0xaa},
- {0, 0x12, 0x00, 0xaa}, {0, 0x40, 0xc0, 0xaa},
- {0, 0x8c, 0x00, 0xaa}, {0, 0x17, 0x14, 0xaa},
- {0, 0x18, 0x02, 0xaa}, {0, 0x32, 0x92, 0xaa},
- {0, 0x19, 0x02, 0xaa}, {0, 0x1a, 0x7a, 0xaa},
- {0, 0x03, 0x0a, 0xaa}, {0, 0x0c, 0x00, 0xaa},
- {0, 0x3e, 0x00, 0xaa}, {0, 0x70, 0x3a, 0xaa},
- {0, 0x71, 0x35, 0xaa}, {0, 0x72, 0x11, 0xaa},
- {0, 0x73, 0xf0, 0xaa}, {0, 0xa2, 0x02, 0xaa},
- {0, 0xb1, 0x00, 0xaa}, {0, 0xb1, 0x0c, 0xaa},
- {0, 0x1e, 0x37, 0xaa}, {0, 0xaa, 0x14, 0xaa},
- {0, 0x24, 0x80, 0xaa}, {0, 0x25, 0x74, 0xaa},
- {0, 0x26, 0xd3, 0xaa}, {0, 0x0d, 0x00, 0xaa},
- {0, 0x14, 0x18, 0xaa}, {0, 0x9d, 0x99, 0xaa},
- {0, 0x9e, 0x7f, 0xaa}, {0, 0x64, 0x08, 0xaa},
- {0, 0x94, 0x07, 0xaa}, {0, 0x95, 0x06, 0xaa},
- {0, 0x66, 0x05, 0xaa}, {0, 0x41, 0x08, 0xaa},
- {0, 0x3f, 0x00, 0xaa}, {0, 0x75, 0x07, 0xaa},
- {0, 0x76, 0xe1, 0xaa}, {0, 0x4c, 0x00, 0xaa},
- {0, 0x77, 0x00, 0xaa}, {0, 0x3d, 0xc2, 0xaa},
- {0, 0x4b, 0x09, 0xaa}, {0, 0xc9, 0x60, 0xaa},
- {0, 0x41, 0x38, 0xaa},
- {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, 0x21, 0xcc},
- {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc},
- {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc},
+ {0x00, 0x00, 0x64, 0xdd},
+ {0xb3, 0x49, 0x11, 0xcc},
+ {0x00, 0x00, 0x33, 0xdd},
+/*read b349*/
+#endif
+ {0xb0, 0x03, 0x19, 0xcc},
+/* {0x00, 0x00, 0x33, 0xdd}, */
+ {0xb0, 0x04, 0x02, 0xcc},
+ {0xb0, 0x02, 0x02, 0xcc},
+ {0xb3, 0x5d, 0x00, 0xcc},
+ {0xb3, 0x01, 0x01, 0xcc},
+ {0xb3, 0x00, 0x64, 0xcc},
+ {0xb3, 0x00, 0x65, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc},
+ {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x02, 0xb2, 0xcc},
+ {0xb3, 0x03, 0x18, 0xcc},
+ {0xb3, 0x04, 0x15, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x02, 0xcc},
+ {0xb3, 0x23, 0x58, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x03, 0xcc},
+ {0xb3, 0x17, 0x1f, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+ {0xbc, 0x01, 0x01, 0xcc},
+ {0xb0, 0x54, 0x13, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x35, 0xdc, 0xcc},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x12, 0x05, 0xaa},
+ {0x00, 0x13, 0x02, 0xaa},
+ {0x00, 0x1e, 0xc6, 0xaa}, /* h/v flip */
+ {0x00, 0x21, 0x00, 0xaa},
+ {0x00, 0x25, 0x02, 0xaa},
+ {0x00, 0x3c, 0x4f, 0xaa},
+ {0x00, 0x3f, 0xe0, 0xaa},
+ {0x00, 0x42, 0xff, 0xaa},
+ {0x00, 0x45, 0x34, 0xaa},
+ {0x00, 0x55, 0xfe, 0xaa},
+ {0x00, 0x59, 0xd3, 0xaa},
+ {0x00, 0x5e, 0x04, 0xaa},
+ {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */
+ {0x00, 0x62, 0x02, 0xaa},
+ {0x00, 0xa7, 0x31, 0xaa},
+ {0x00, 0xa9, 0x66, 0xaa},
+ {0x00, 0xb0, 0x00, 0xaa},
+ {0x00, 0xb1, 0x00, 0xaa},
+ {0x00, 0xb3, 0x11, 0xaa},
+ {0x00, 0xb6, 0x26, 0xaa},
+ {0x00, 0xb7, 0x20, 0xaa},
+ {0x00, 0xba, 0x04, 0xaa},
+ {0x00, 0x88, 0x42, 0xaa},
+ {0x00, 0x89, 0x9a, 0xaa},
+ {0x00, 0x8a, 0x88, 0xaa},
+ {0x00, 0x8b, 0x8e, 0xaa},
+ {0x00, 0x8c, 0x3e, 0xaa},
+ {0x00, 0x8d, 0x90, 0xaa},
+ {0x00, 0x8e, 0x87, 0xaa},
+ {0x00, 0x8f, 0x96, 0xaa},
+ {0x00, 0x90, 0x3d, 0xaa},
+ {0x00, 0x64, 0x00, 0xaa},
+ {0x00, 0x65, 0x10, 0xaa},
+ {0x00, 0x66, 0x20, 0xaa},
+ {0x00, 0x67, 0x2b, 0xaa},
+ {0x00, 0x68, 0x36, 0xaa},
+ {0x00, 0x69, 0x49, 0xaa},
+ {0x00, 0x6a, 0x5a, 0xaa},
+ {0x00, 0x6b, 0x7f, 0xaa},
+ {0x00, 0x6c, 0x9b, 0xaa},
+ {0x00, 0x6d, 0xba, 0xaa},
+ {0x00, 0x6e, 0xd4, 0xaa},
+ {0x00, 0x6f, 0xea, 0xaa},
+ {0x00, 0x70, 0x00, 0xaa},
+ {0x00, 0x71, 0x10, 0xaa},
+ {0x00, 0x72, 0x20, 0xaa},
+ {0x00, 0x73, 0x2b, 0xaa},
+ {0x00, 0x74, 0x36, 0xaa},
+ {0x00, 0x75, 0x49, 0xaa},
+ {0x00, 0x76, 0x5a, 0xaa},
+ {0x00, 0x77, 0x7f, 0xaa},
+ {0x00, 0x78, 0x9b, 0xaa},
+ {0x00, 0x79, 0xba, 0xaa},
+ {0x00, 0x7a, 0xd4, 0xaa},
+ {0x00, 0x7b, 0xea, 0xaa},
+ {0x00, 0x7c, 0x00, 0xaa},
+ {0x00, 0x7d, 0x10, 0xaa},
+ {0x00, 0x7e, 0x20, 0xaa},
+ {0x00, 0x7f, 0x2b, 0xaa},
+ {0x00, 0x80, 0x36, 0xaa},
+ {0x00, 0x81, 0x49, 0xaa},
+ {0x00, 0x82, 0x5a, 0xaa},
+ {0x00, 0x83, 0x7f, 0xaa},
+ {0x00, 0x84, 0x9b, 0xaa},
+ {0x00, 0x85, 0xba, 0xaa},
+ {0x00, 0x86, 0xd4, 0xaa},
+ {0x00, 0x87, 0xea, 0xaa},
+ {0x00, 0x57, 0x2a, 0xaa},
+ {0x00, 0x03, 0x01, 0xaa},
+ {0x00, 0x04, 0x10, 0xaa},
+ {0x00, 0x05, 0x10, 0xaa},
+ {0x00, 0x06, 0x10, 0xaa},
+ {0x00, 0x07, 0x10, 0xaa},
+ {0x00, 0x08, 0x13, 0xaa},
+ {0x00, 0x0a, 0x00, 0xaa},
+ {0x00, 0x0b, 0x10, 0xaa},
+ {0x00, 0x0c, 0x20, 0xaa},
+ {0x00, 0x0d, 0x18, 0xaa},
+ {0x00, 0x22, 0x01, 0xaa},
+ {0x00, 0x23, 0x60, 0xaa},
+ {0x00, 0x25, 0x08, 0xaa},
+ {0x00, 0x26, 0x82, 0xaa},
+ {0x00, 0x2e, 0x0f, 0xaa},
+ {0x00, 0x2f, 0x1e, 0xaa},
+ {0x00, 0x30, 0x2d, 0xaa},
+ {0x00, 0x31, 0x3c, 0xaa},
+ {0x00, 0x32, 0x4b, 0xaa},
+ {0x00, 0x33, 0x5a, 0xaa},
+ {0x00, 0x34, 0x69, 0xaa},
+ {0x00, 0x35, 0x78, 0xaa},
+ {0x00, 0x36, 0x87, 0xaa},
+ {0x00, 0x37, 0x96, 0xaa},
+ {0x00, 0x38, 0xa5, 0xaa},
+ {0x00, 0x39, 0xb4, 0xaa},
+ {0x00, 0x3a, 0xc3, 0xaa},
+ {0x00, 0x3b, 0xd2, 0xaa},
+ {0x00, 0x3c, 0xe1, 0xaa},
+ {0x00, 0x3e, 0xff, 0xaa},
+ {0x00, 0x3f, 0xff, 0xaa},
+ {0x00, 0x40, 0xff, 0xaa},
+ {0x00, 0x41, 0xff, 0xaa},
+ {0x00, 0x42, 0xff, 0xaa},
+ {0x00, 0x43, 0xff, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x20, 0xc4, 0xaa},
+ {0x00, 0x13, 0x03, 0xaa},
+ {0x00, 0x3c, 0x50, 0xaa},
+ {0x00, 0x61, 0x6a, 0xaa}, /* sharpness? */
+ {0x00, 0x51, 0x5b, 0xaa},
+ {0x00, 0x52, 0x91, 0xaa},
+ {0x00, 0x53, 0x4c, 0xaa},
+ {0x00, 0x54, 0x50, 0xaa},
+ {0x00, 0x56, 0x02, 0xaa},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x03, 0xcc},
+ {0xb6, 0x02, 0x20, 0xcc},
+ {0xb6, 0x05, 0x02, 0xcc},
+ {0xb6, 0x04, 0x58, 0xcc},
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x21, 0xcc},
+ {0xb6, 0x18, 0x03, 0xcc},
+ {0xb6, 0x17, 0xa9, 0xcc},
+ {0xb6, 0x16, 0x80, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
{0xb6, 0x23, 0x0b, 0xcc},
- {0xbf, 0xc0, 0x39, 0xcc},/* set jpeg */
- {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, 0x5c, 0x01, 0xcc}, {0xb3, 0x01, 0x45, 0xcc},
- {0, 0x77, 0x05, 0xaa}, {0xb6, 0x12, 0xf8, 0xcc},
- {0xb6, 0x13, 0x20, 0xcc},
- {0, 0x24, 0x6c, 0xaa},
- {0, 0x25, 0x5c, 0xaa}, {0, 0x56, 0x50, 0xaa},
- {0, 0x4f, 0xa8, 0xaa}, {0, 0x50, 0xa5, 0xaa},
- {0, 0x51, 0x02, 0xaa}, {0, 0x52, 0x22, 0xaa},
- {0, 0x53, 0x86, 0xaa}, {0, 0x54, 0xaa, 0xaa},
- {0, 0x7a, 0x19, 0xaa}, {0, 0x7b, 0x0c, 0xaa},
- {0, 0x7c, 0x18, 0xaa}, {0, 0x7d, 0x2f, 0xaa},
- {0, 0x7e, 0x54, 0xaa}, {0, 0x7f, 0x64, 0xaa},
- {0, 0x80, 0x71, 0xaa}, {0, 0x81, 0x7d, 0xaa},
- {0, 0x82, 0x88, 0xaa}, {0, 0x83, 0x91, 0xaa},
- {0, 0x84, 0x98, 0xaa}, {0, 0x85, 0xa7, 0xaa},
- {0, 0x86, 0xb4, 0xaa}, {0, 0x87, 0xcb, 0xaa},
- {0, 0x88, 0xde, 0xaa}, {0, 0x89, 0xed, 0xaa},
- {0, 0x75, 0x86, 0xaa}, {0, 0x92, 0x00, 0xaa},
- {0, 0x3b, 0, 0xbb},
- {0, 0x3b, 0x00, 0xaa},
- {0, 0x13, 0, 0xbb}, {0, 0x13, 0xe7, 0xaa},
- {0, 0x2d, 0x00, 0xaa}, {0, 0x2e, 0x00, 0xaa},
- {0, 0x04, 0x00, 0xaa}, {0, 0x10, 0x00, 0xaa},
- {0, 0x3b, 0, 0xbb}, {0, 0x3b, 0x80, 0xaa},
- {0, 0x13, 0, 0xbb},
- {0, 0x13, 0xe7, 0xaa},
- {0, 0x1e, 0x27, 0xaa},
- {0, 0x1e, 0x27, 0xaa},
- {0, 0x3b, 0xc8, 0xaa},
- {}
-};
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+ {0xbf, 0xcc, 0x00, 0xcc},
+ {0xb8, 0x06, 0x20, 0xcc},
+ {0xb8, 0x07, 0x03, 0xcc},
+ {0xb8, 0x08, 0x58, 0xcc},
+ {0xb8, 0x09, 0x02, 0xcc},
+ {0xb3, 0x01, 0x41, 0xcc},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xd9, 0x0f, 0xaa},
+ {0x00, 0xda, 0xaa, 0xaa},
+ {0x00, 0xd9, 0x10, 0xaa},
+ {0x00, 0xda, 0xaa, 0xaa},
+ {0x00, 0xd9, 0x11, 0xaa},
+ {0x00, 0xda, 0x00, 0xaa},
+ {0x00, 0xd9, 0x12, 0xaa},
+ {0x00, 0xda, 0xff, 0xaa},
+ {0x00, 0xd9, 0x13, 0xaa},
+ {0x00, 0xda, 0xff, 0xaa},
+ {0x00, 0xe8, 0x11, 0xaa},
+ {0x00, 0xe9, 0x12, 0xaa},
+ {0x00, 0xea, 0x5c, 0xaa},
+ {0x00, 0xeb, 0xff, 0xaa},
+ {0x00, 0xd8, 0x80, 0xaa},
+ {0x00, 0xe6, 0x02, 0xaa},
+ {0x00, 0xd6, 0x40, 0xaa},
+ {0x00, 0xe3, 0x05, 0xaa},
+ {0x00, 0xe0, 0x40, 0xaa},
+ {0x00, 0xde, 0x03, 0xaa},
+ {0x00, 0xdf, 0x03, 0xaa},
+ {0x00, 0xdb, 0x02, 0xaa},
+ {0x00, 0xdc, 0x00, 0xaa},
+ {0x00, 0xdd, 0x03, 0xaa},
+ {0x00, 0xe1, 0x08, 0xaa},
+ {0x00, 0xe2, 0x01, 0xaa},
+ {0x00, 0xd6, 0x40, 0xaa},
+ {0x00, 0xe4, 0x40, 0xaa},
+#if 1
+ {0x00, 0xa8, 0x8f, 0xaa},
+#else
+/*modified later*/
+ {0x00, 0xa8, 0x9f, 0xaa},
+#endif
+ {0x00, 0xb4, 0x16, 0xaa},
+ {0xb0, 0x02, 0x06, 0xcc},
+ {0xb0, 0x18, 0x06, 0xcc},
+ {0xb0, 0x19, 0x06, 0xcc},
+ {0xb3, 0x5d, 0x18, 0xcc},
+ {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc},
+ {0x00, 0xb4, 0x0e, 0xaa},
+ {0x00, 0xb5, 0x49, 0xaa},
+ {0x00, 0xb6, 0x1c, 0xaa},
+ {0x00, 0xb7, 0x96, 0xaa},
+/* end of usbvm326.inf - start of ms-win trace */
+ {0xb6, 0x12, 0xf8, 0xcc},
+ {0xb6, 0x13, 0x3d, 0xcc},
+/*read b306*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x1a, 0x09, 0xaa},
+ {0x00, 0x1b, 0x8a, 0xaa},
+/*read b827*/
+ {0xb8, 0x27, 0x00, 0xcc},
+ {0xb8, 0x26, 0x60, 0xcc},
+ {0xb8, 0x26, 0x60, 0xcc},
+/*gamma - to do?*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xae, 0x84, 0xaa},
+/*gamma again*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x96, 0xa0, 0xaa},
+/*matrix*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x91, 0x35, 0xaa},
+ {0x00, 0x92, 0x22, 0xaa},
+/*gamma*/
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x95, 0x85, 0xaa},
+/*matrix*/
+#if 0
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x61, 0xb8, 0xaa}, /* sharpness */
+#endif
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x4d, 0x20, 0xaa},
+ {0xb8, 0x22, 0x40, 0xcc},
+ {0xb8, 0x23, 0x40, 0xcc},
+ {0xb8, 0x24, 0x40, 0xcc},
+ {0xb8, 0x81, 0x09, 0xcc},
+ {0x00, 0x00, 0x64, 0xdd},
+ {0x00, 0x03, 0x01, 0xaa},
+/*read 46*/
+ {0x00, 0x46, 0x3c, 0xaa},
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x16, 0x40, 0xaa},
+ {0x00, 0x17, 0x40, 0xaa},
+ {0x00, 0x18, 0x40, 0xaa},
+ {0x00, 0x19, 0x41, 0xaa},
+ {0x00, 0x03, 0x01, 0xaa},
+ {0x00, 0x46, 0x3c, 0xaa},
+ {0x00, 0x00, 0x18, 0xdd},
+/*read bfff*/
+#if 0
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x1e, 0x46, 0xaa}, /* h/v flip */
+ {0x00, 0xa8, 0x8f, 0xaa},
#endif
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0xb4, 0x1c, 0xaa},
+ {0x00, 0xb5, 0x92, 0xaa},
+ {0x00, 0xb6, 0x39, 0xaa},
+ {0x00, 0xb7, 0x24, 0xaa},
+/*write 89 0400 1415*/
+};
struct sensor_info {
int sensorId;
@@ -1402,6 +1807,9 @@ static const struct sensor_info sensor_info_data[] = {
{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_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
};
/* read 'len' bytes in gspca_dev->usb_buf */
@@ -1458,18 +1866,18 @@ static void read_sensor_register(struct gspca_dev *gspca_dev,
msleep(1);
}
reg_r(gspca_dev, 0xa1, 0xb33e, 1);
- hdata = gspca_dev->usb_buf[0];
+ 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);
- ldata = gspca_dev->usb_buf[0];
- PDEBUG(D_PROBE, "Read Sensor h (0x%02X) m (0x%02X) l (0x%02X)",
+ hdata = gspca_dev->usb_buf[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 = (ldata << 8) + mdata;
+ *value = (hdata << 8) + mdata;
else
- *value = ldata;
+ *value = hdata;
}
static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
@@ -1480,7 +1888,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
const struct sensor_info *ptsensor_info;
reg_r(gspca_dev, 0xa1, 0xbfcf, 1);
- PDEBUG(D_PROBE, "check sensor header %d", gspca_dev->usb_buf[0]);
+ PDEBUG(D_PROBE, "check sensor header %02x", gspca_dev->usb_buf[0]);
for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) {
ptsensor_info = &sensor_info_data[i];
reg_w(dev, 0xa0, 0x02, 0xb334);
@@ -1489,16 +1897,15 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
reg_w(dev, 0xa0, 0x01, 0xb308);
reg_w(dev, 0xa0, 0x0c, 0xb309);
reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335);
-/* PDEBUG(D_PROBE,
- "check sensor VC032X -> %d Add -> ox%02X!",
- i, ptsensor_info->I2cAdd); */
reg_w(dev, 0xa0, ptsensor_info->op, 0xb301);
read_sensor_register(gspca_dev, ptsensor_info->IdAdd, &value);
- if (value == ptsensor_info->VpId) {
-/* PDEBUG(D_PROBE, "find sensor VC032X -> ox%04X!",
- ptsensor_info->VpId); */
+ 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;
}
return -1;
}
@@ -1600,13 +2007,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
cam->epaddr = 0x02;
sd->bridge = id->driver_info;
- if (sd->bridge == BRIDGE_VC0321) {
- cam->cam_mode = vc0321_mode;
- cam->nmodes = ARRAY_SIZE(vc0321_mode);
- } else {
- cam->cam_mode = vc0323_mode;
- cam->nmodes = ARRAY_SIZE(vc0323_mode);
- }
vc0321_reset(gspca_dev);
sensor = vc032x_probe_sensor(gspca_dev);
@@ -1616,35 +2016,66 @@ static int sd_config(struct gspca_dev *gspca_dev,
return -EINVAL;
case SENSOR_HV7131R:
PDEBUG(D_PROBE, "Find Sensor HV7131R");
- sd->sensor = SENSOR_HV7131R;
+ break;
+ case SENSOR_MI0360:
+ PDEBUG(D_PROBE, "Find Sensor MI0360");
+ sd->bridge = BRIDGE_VC0323;
break;
case SENSOR_MI1310_SOC:
PDEBUG(D_PROBE, "Find Sensor MI1310_SOC");
- sd->sensor = SENSOR_MI1310_SOC;
break;
case SENSOR_MI1320:
PDEBUG(D_PROBE, "Find Sensor MI1320");
- sd->sensor = SENSOR_MI1320;
break;
case SENSOR_OV7660:
PDEBUG(D_PROBE, "Find Sensor OV7660");
- sd->sensor = SENSOR_OV7660;
break;
case SENSOR_OV7670:
PDEBUG(D_PROBE, "Find Sensor OV7670");
- sd->sensor = SENSOR_OV7670;
+ break;
+ case SENSOR_PO1200:
+ PDEBUG(D_PROBE, "Find Sensor PO1200");
break;
case SENSOR_PO3130NC:
PDEBUG(D_PROBE, "Find Sensor PO3130NC");
- sd->sensor = SENSOR_PO3130NC;
break;
}
+ sd->sensor = sensor;
- sd->qindex = 7;
- sd->autogain = AUTOGAIN_DEF;
+ if (sd->bridge == BRIDGE_VC0321) {
+ 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 {
+ cam->cam_mode = svga_mode;
+ cam->nmodes = ARRAY_SIZE(svga_mode);
+ }
+ }
+
+ sd->hflip = HFLIP_DEF;
+ sd->vflip = VFLIP_DEF;
+ if (sd->sensor == SENSOR_OV7670) {
+ sd->hflip = 1;
+ sd->vflip = 1;
+ }
sd->lightfreq = FREQ_DEF;
if (sd->sensor != SENSOR_OV7670)
gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX);
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ case SENSOR_OV7670:
+ case SENSOR_PO1200:
+ break;
+ default:
+ gspca_dev->ctrl_dis = (1 << HFLIP_IDX)
+ | (1 << VFLIP_IDX);
+ break;
+ }
+
+ sd->sharpness = SHARPNESS_DEF;
if (sd->bridge == BRIDGE_VC0321) {
reg_r(gspca_dev, 0x8a, 0, 3);
@@ -1662,29 +2093,33 @@ static int sd_init(struct gspca_dev *gspca_dev)
return 0;
}
-static void setquality(struct gspca_dev *gspca_dev)
-{
-#if 0
- struct sd *sd = (struct sd *) gspca_dev;
- __u8 quality = 0;
-
- quality = sd->qindex & 0xff;
- reg_w(gspca_dev->dev, 0xa0, quality, 0x0008);
-#endif
-}
-
-static void setautogain(struct gspca_dev *gspca_dev)
+/* for OV7660 and OV7670 only */
+static void sethvflip(struct gspca_dev *gspca_dev)
{
-#if 0
struct sd *sd = (struct sd *) gspca_dev;
- __u8 autoval;
+ __u8 data;
- if (sd->autogain)
- autoval = 0x42;
- else
- autoval = 0x02;
- reg_w(gspca_dev->dev, 0xa0, autoval, 0x0180);
-#endif
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
+ data = 1;
+ break;
+ case SENSOR_OV7670:
+ data = 7;
+ break;
+ case SENSOR_PO1200:
+ data = 0;
+ i2c_write(gspca_dev, 0x03, &data, 1);
+ data = 0x80 * sd->hflip
+ | 0x40 * sd->vflip
+ | 0x06;
+ i2c_write(gspca_dev, 0x1e, &data, 1);
+ return;
+ default:
+ return;
+ }
+ data |= OV7660_MVFP_MIRROR * sd->hflip
+ | OV7660_MVFP_VFLIP * sd->vflip;
+ i2c_write(gspca_dev, OV7660_REG_MVFP, &data, 1);
}
static void setlightfreq(struct gspca_dev *gspca_dev)
@@ -1698,6 +2133,20 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]);
}
+/* po1200 only */
+static void setsharpness(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 data;
+
+ if (sd->sensor != SENSOR_PO1200)
+ return;
+ data = 0;
+ i2c_write(gspca_dev, 0x03, &data, 1);
+ data = 0xb5 + sd->sharpness * 3;
+ i2c_write(gspca_dev, 0x61, &data, 1);
+}
+
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1748,6 +2197,17 @@ static int sd_start(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, ov7670_initVGA_JPG);
}
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);
+ }
+ break;
case SENSOR_MI1310_SOC:
if (mode) {
/* 320x240 */
@@ -1780,6 +2240,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
}
usb_exchange(gspca_dev, po3130_rundata);
break;
+ case SENSOR_PO1200:
+ GammaT = po1200_gamma;
+ MatrixT = po1200_matrix;
+ usb_exchange(gspca_dev, po1200_initVGA_data);
+ break;
default:
PDEBUG(D_PROBE, "Damned !! no sensor found Bye");
return -EMEDIUMTYPE;
@@ -1812,11 +2277,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev->dev, 0xa0, 0x23, 0xb800); * ISP CTRL_BAS
*/
/* set the led on 0x0892 0x0896 */
- reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
- msleep(100);
- setquality(gspca_dev);
- setautogain(gspca_dev);
- setlightfreq(gspca_dev);
+ if (sd->sensor != SENSOR_PO1200) {
+ reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
+ msleep(100);
+ sethvflip(gspca_dev);
+ setlightfreq(gspca_dev);
+ } else {
+ setsharpness(gspca_dev);
+ sethvflip(gspca_dev);
+ reg_w(gspca_dev->dev, 0x89, 0x0400, 0x1415);
+ }
}
return 0;
}
@@ -1862,24 +2332,48 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
data, len);
return;
}
+
+ /* The vc0321 sends some additional data after sending the complete
+ * frame, we ignore this. */
+ if (sd->bridge == BRIDGE_VC0321
+ && len > frame->v4l2_buf.length - (frame->data_end - frame->data))
+ len = frame->v4l2_buf.length - (frame->data_end - frame->data);
gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
}
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- sd->autogain = val;
+ sd->hflip = val;
if (gspca_dev->streaming)
- setautogain(gspca_dev);
+ sethvflip(gspca_dev);
return 0;
}
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->autogain;
+ *val = sd->hflip;
+ return 0;
+}
+
+static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->vflip = val;
+ if (gspca_dev->streaming)
+ sethvflip(gspca_dev);
+ return 0;
+}
+
+static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ *val = sd->vflip;
return 0;
}
@@ -1901,6 +2395,24 @@ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
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)
+ setsharpness(gspca_dev);
+ 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_querymenu(struct gspca_dev *gspca_dev,
struct v4l2_querymenu *menu)
{
@@ -1945,6 +2457,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, 0x6002), .driver_info = BRIDGE_VC0323},
{USB_DEVICE(0x17ef, 0x4802), .driver_info = BRIDGE_VC0323},
{}
};
diff --git a/linux/drivers/media/video/gspca/zc3xx-reg.h b/linux/drivers/media/video/gspca/zc3xx-reg.h
index f52e09c2c..bfb559c3b 100644
--- a/linux/drivers/media/video/gspca/zc3xx-reg.h
+++ b/linux/drivers/media/video/gspca/zc3xx-reg.h
@@ -244,14 +244,6 @@
#define ZC3XX_R1CA_SHARPNESS04 0x01ca
#define ZC3XX_R1CB_SHARPNESS05 0x01cb
-/* Synchronization */
-#define ZC3XX_R190_SYNC00LOW 0x0190
-#define ZC3XX_R191_SYNC00MID 0x0191
-#define ZC3XX_R192_SYNC00HIGH 0x0192
-#define ZC3XX_R195_SYNC01LOW 0x0195
-#define ZC3XX_R196_SYNC01MID 0x0196
-#define ZC3XX_R197_SYNC01HIGH 0x0197
-
/* Dead pixels */
#define ZC3XX_R250_DEADPIXELSMODE 0x0250
diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c
index adbca6a9d..678070b93 100644
--- a/linux/drivers/media/video/gspca/zc3xx.c
+++ b/linux/drivers/media/video/gspca/zc3xx.c
@@ -51,16 +51,16 @@ struct sd {
#define SENSOR_CS2102 0
#define SENSOR_CS2102K 1
#define SENSOR_GC0305 2
-#define SENSOR_HDCS2020 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_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
@@ -1653,295 +1653,6 @@ static const struct usb_action gc0305_NoFliker[] = {
{}
};
-/* play poker with registers at your own risk !! */
-static const struct usb_action hdcs2020xx_Initial[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
- {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW},
- /* D0 ?? E0 did not start */
- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
- {0xa0, 0x08, ZC3XX_R08D_COMPABILITYMODE},
- {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
- {0xaa, 0x02, 0x0002},
- {0xaa, 0x07, 0x0006},
- {0xaa, 0x08, 0x0002},
- {0xaa, 0x09, 0x0006},
- {0xaa, 0x0a, 0x0001},
- {0xaa, 0x0b, 0x0001},
- {0xaa, 0x0c, 0x0008},
- {0xaa, 0x0d, 0x0000},
- {0xaa, 0x10, 0x0000},
- {0xaa, 0x12, 0x0005},
- {0xaa, 0x13, 0x0063},
- {0xaa, 0x15, 0x0070},
- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x70, ZC3XX_R18D_YTARGET},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4 */
- {0xa0, 0x37, ZC3XX_R121_GAMMA01},
- {0xa0, 0x58, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x91, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa6, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb8, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc7, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd3, ZC3XX_R128_GAMMA08},
- {0xa0, 0xde, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xed, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x23, 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, 0x4c, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xf5, ZC3XX_R10B_RGB01},
- {0xa0, 0xff, ZC3XX_R10C_RGB02},
- {0xa0, 0xf9, ZC3XX_R10D_RGB10},
- {0xa0, 0x51, ZC3XX_R10E_RGB11},
- {0xa0, 0xf5, ZC3XX_R10F_RGB12},
- {0xa0, 0xfb, ZC3XX_R110_RGB20},
- {0xa0, 0xed, ZC3XX_R111_RGB21},
- {0xa0, 0x5f, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
- {0xaa, 0x20, 0x0004},
- {0xaa, 0x21, 0x003d},
- {0xaa, 0x03, 0x0041},
- {0xaa, 0x04, 0x0010},
- {0xaa, 0x05, 0x003d},
- {0xaa, 0x0e, 0x0001},
- {0xaa, 0x0f, 0x0000},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x41, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xad, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0195},
- {0xa1, 0x01, 0x0196},
- {0xa1, 0x01, 0x0197},
- {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x85, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0116},
- {0xa1, 0x01, 0x0118},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x85, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0116},
- {0xa1, 0x01, 0x0118},
-/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
- {0xa0, 0x00, 0x0007},
- {}
-};
-
-static const struct usb_action hdcs2020xx_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
- {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT},
- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
- {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, 0x08, ZC3XX_R08D_COMPABILITYMODE},
- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
- {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW},
- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
- {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW},
- {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH},
- {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW},
- {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH},
- {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW},
- {0xaa, 0x02, 0x0002},
- {0xaa, 0x07, 0x0006},
- {0xaa, 0x08, 0x0002},
- {0xaa, 0x09, 0x0006},
- {0xaa, 0x0a, 0x0001},
- {0xaa, 0x0b, 0x0001},
- {0xaa, 0x0c, 0x0008},
- {0xaa, 0x0d, 0x0000},
- {0xaa, 0x10, 0x0000},
- {0xaa, 0x12, 0x0005},
- {0xaa, 0x13, 0x0063},
- {0xaa, 0x15, 0x0070},
- {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION},
- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
- {0xa0, 0x00, 0x01ad},
- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
- {0xa0, 0x70, ZC3XX_R18D_YTARGET},
- {0xa1, 0x01, 0x0002},
- {0xa1, 0x01, 0x0008},
- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
- {0xa0, 0x04, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
- {0xa1, 0x01, 0x01c8},
- {0xa1, 0x01, 0x01c9},
- {0xa1, 0x01, 0x01ca},
- {0xa0, 0x07, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
- {0xa0, 0x11, ZC3XX_R120_GAMMA00}, /* gamma ~4*/
- {0xa0, 0x37, ZC3XX_R121_GAMMA01},
- {0xa0, 0x58, ZC3XX_R122_GAMMA02},
- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
- {0xa0, 0x91, ZC3XX_R124_GAMMA04},
- {0xa0, 0xa6, ZC3XX_R125_GAMMA05},
- {0xa0, 0xb8, ZC3XX_R126_GAMMA06},
- {0xa0, 0xc7, ZC3XX_R127_GAMMA07},
- {0xa0, 0xd3, ZC3XX_R128_GAMMA08},
- {0xa0, 0xde, ZC3XX_R129_GAMMA09},
- {0xa0, 0xe6, ZC3XX_R12A_GAMMA0A},
- {0xa0, 0xed, ZC3XX_R12B_GAMMA0B},
- {0xa0, 0xf3, ZC3XX_R12C_GAMMA0C},
- {0xa0, 0xf8, ZC3XX_R12D_GAMMA0D},
- {0xa0, 0xfb, ZC3XX_R12E_GAMMA0E},
- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
- {0xa0, 0x23, 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, 0x60, ZC3XX_R10A_RGB00}, /* matrix */
- {0xa0, 0xff, ZC3XX_R10B_RGB01},
- {0xa0, 0xff, ZC3XX_R10C_RGB02},
- {0xa0, 0xff, ZC3XX_R10D_RGB10},
- {0xa0, 0x60, ZC3XX_R10E_RGB11},
- {0xa0, 0xff, ZC3XX_R10F_RGB12},
- {0xa0, 0xff, ZC3XX_R110_RGB20},
- {0xa0, 0xff, ZC3XX_R111_RGB21},
- {0xa0, 0x60, ZC3XX_R112_RGB22},
-
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
- {0xaa, 0x20, 0x0002},
- {0xaa, 0x21, 0x001b},
- {0xaa, 0x03, 0x0044},
- {0xaa, 0x04, 0x0008},
- {0xaa, 0x05, 0x001b},
- {0xaa, 0x0e, 0x0001},
- {0xaa, 0x0f, 0x0000},
- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
- {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW},
- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
- {0xa0, 0x44, ZC3XX_R01D_HSYNC_0},
- {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1},
- {0xa0, 0xad, ZC3XX_R01F_HSYNC_2},
- {0xa0, 0xeb, ZC3XX_R020_HSYNC_3},
- {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID},
- {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW},
- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa1, 0x01, 0x0195},
- {0xa1, 0x01, 0x0196},
- {0xa1, 0x01, 0x0197},
- {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x99, ZC3XX_R118_BGAIN},
- {0xa1, 0x01, 0x0116},
- {0xa1, 0x01, 0x0118},
- {0xa1, 0x01, 0x0180},
- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
- {0xa0, 0x1d, ZC3XX_R116_RGAIN},
- {0xa0, 0x40, ZC3XX_R117_GGAIN},
- {0xa0, 0x99, ZC3XX_R118_BGAIN},
-/* {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, */
- {0xa0, 0x00, 0x0007},
-/* {0xa0, 0x18, 0x00fe}, */
- {}
-};
static const struct usb_action hdcs2020xb_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
@@ -2368,12 +2079,12 @@ static const struct usb_action hv7131b_50HZ[] = { /* 640x480*/
{0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
{0xaa, 0x22, 0x001b}, /* 00,22,1b,aa */
{0xaa, 0x23, 0x00fc}, /* 00,23,fc,aa */
- {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */
- {0xa0, 0x9b, ZC3XX_R191_SYNC00MID}, /* 01,91,9b,cc */
- {0xa0, 0x80, ZC3XX_R192_SYNC00HIGH}, /* 01,92,80,cc */
- {0xa0, 0x00, ZC3XX_R195_SYNC01LOW}, /* 01,95,00,cc */
- {0xa0, 0xea, ZC3XX_R196_SYNC01MID}, /* 01,96,ea,cc */
- {0xa0, 0x60, ZC3XX_R197_SYNC01HIGH}, /* 01,97,60,cc */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0xea, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,ea,cc */
+ {0xa0, 0x60, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,60,cc */
{0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
{0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
@@ -2393,12 +2104,12 @@ static const struct usb_action hv7131b_50HZScale[] = { /* 320x240 */
{0xaa, 0x21, 0x0050}, /* 00,21,50,aa */
{0xaa, 0x22, 0x0012}, /* 00,22,12,aa */
{0xaa, 0x23, 0x0080}, /* 00,23,80,aa */
- {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */
- {0xa0, 0x9b, ZC3XX_R191_SYNC00MID}, /* 01,91,9b,cc */
- {0xa0, 0x80, ZC3XX_R192_SYNC00HIGH}, /* 01,92,80,cc */
- {0xa0, 0x01, ZC3XX_R195_SYNC01LOW}, /* 01,95,01,cc */
- {0xa0, 0xd4, ZC3XX_R196_SYNC01MID}, /* 01,96,d4,cc */
- {0xa0, 0xc0, ZC3XX_R197_SYNC01HIGH}, /* 01,97,c0,cc */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x9b, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,9b,cc */
+ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,80,cc */
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
+ {0xa0, 0xd4, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,d4,cc */
+ {0xa0, 0xc0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,c0,cc */
{0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
{0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
{0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
@@ -2418,12 +2129,12 @@ static const struct usb_action hv7131b_60HZ[] = { /* 640x480*/
{0xaa, 0x21, 0x0040}, /* 00,21,40,aa */
{0xaa, 0x22, 0x0013}, /* 00,22,13,aa */
{0xaa, 0x23, 0x004c}, /* 00,23,4c,aa */
- {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */
- {0xa0, 0x4d, ZC3XX_R191_SYNC00MID}, /* 01,91,4d,cc */
- {0xa0, 0x60, ZC3XX_R192_SYNC00HIGH}, /* 01,92,60,cc */
- {0xa0, 0x00, ZC3XX_R195_SYNC01LOW}, /* 01,95,00,cc */
- {0xa0, 0xc3, ZC3XX_R196_SYNC01MID}, /* 01,96,c3,cc */
- {0xa0, 0x50, ZC3XX_R197_SYNC01HIGH}, /* 01,97,50,cc */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
+ {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0xc3, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,c3,cc */
+ {0xa0, 0x50, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,50,cc */
{0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, /* 01,8c,0c,cc */
{0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,18,cc */
{0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
@@ -2443,12 +2154,12 @@ static const struct usb_action hv7131b_60HZScale[] = { /* 320x240 */
{0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
{0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
{0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
- {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */
- {0xa0, 0x4d, ZC3XX_R191_SYNC00MID}, /* 01,91,4d,cc */
- {0xa0, 0x60, ZC3XX_R192_SYNC00HIGH}, /* 01,92,60,cc */
- {0xa0, 0x01, ZC3XX_R195_SYNC01LOW}, /* 01,95,01,cc */
- {0xa0, 0x86, ZC3XX_R196_SYNC01MID}, /* 01,96,86,cc */
- {0xa0, 0xa0, ZC3XX_R197_SYNC01HIGH}, /* 01,97,a0,cc */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0x4d, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,4d,cc */
+ {0xa0, 0x60, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,60,cc */
+ {0xa0, 0x01, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,01,cc */
+ {0xa0, 0x86, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,86,cc */
+ {0xa0, 0xa0, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,a0,cc */
{0xa0, 0x07, ZC3XX_R18C_AEFREEZE}, /* 01,8c,07,cc */
{0xa0, 0x0f, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,0f,cc */
{0xa0, 0x18, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,18,cc */
@@ -2468,12 +2179,12 @@ static const struct usb_action hv7131b_NoFliker[] = { /* 640x480*/
{0xaa, 0x21, 0x0010}, /* 00,21,10,aa */
{0xaa, 0x22, 0x0000}, /* 00,22,00,aa */
{0xaa, 0x23, 0x0003}, /* 00,23,03,aa */
- {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */
- {0xa0, 0xf8, ZC3XX_R191_SYNC00MID}, /* 01,91,f8,cc */
- {0xa0, 0x00, ZC3XX_R192_SYNC00HIGH}, /* 01,92,00,cc */
- {0xa0, 0x00, ZC3XX_R195_SYNC01LOW}, /* 01,95,00,cc */
- {0xa0, 0x02, ZC3XX_R196_SYNC01MID}, /* 01,96,02,cc */
- {0xa0, 0x00, ZC3XX_R197_SYNC01HIGH}, /* 01,97,00,cc */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
+ {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
{0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
@@ -2493,12 +2204,12 @@ static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
{0xaa, 0x21, 0x00a0}, /* 00,21,a0,aa */
{0xaa, 0x22, 0x0016}, /* 00,22,16,aa */
{0xaa, 0x23, 0x0040}, /* 00,23,40,aa */
- {0xa0, 0x2f, ZC3XX_R190_SYNC00LOW}, /* 01,90,2f,cc */
- {0xa0, 0xf8, ZC3XX_R191_SYNC00MID}, /* 01,91,f8,cc */
- {0xa0, 0x00, ZC3XX_R192_SYNC00HIGH}, /* 01,92,00,cc */
- {0xa0, 0x00, ZC3XX_R195_SYNC01LOW}, /* 01,95,00,cc */
- {0xa0, 0x02, ZC3XX_R196_SYNC01MID}, /* 01,96,02,cc */
- {0xa0, 0x00, ZC3XX_R197_SYNC01HIGH}, /* 01,97,00,cc */
+ {0xa0, 0x2f, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,2f,cc */
+ {0xa0, 0xf8, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,f8,cc */
+ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,00,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x02, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,02,cc */
+ {0xa0, 0x00, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,00,cc */
{0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
{0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
{0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
@@ -4424,6 +4135,270 @@ static const struct usb_action pas106b_NoFliker[] = {
{}
};
+/* from usbvm31b.inf */
+static const struct usb_action pas202b_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,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, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,e0,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, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
+ {0xa0, 0x03, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,03,cc */
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
+ {0xa0, 0x03, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,03,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xe6, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,e6,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
+ {0xaa, 0x02, 0x0002}, /* 00,02,04,aa --> 02 */
+ {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
+ {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+ {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+ {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
+ {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
+ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,b7,cc */
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
+ {0xa0, 0x00, 0x01ad}, /* 01,ad,00,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, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
+ {}
+};
+static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,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, 0x08, ZC3XX_R08D_COMPABILITYMODE}, /* 00,8d,08,cc */
+ {0xa0, 0x08, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,08,cc */
+ {0xa0, 0x02, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,02,cc */
+ {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */
+ {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+ {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */
+ {0xaa, 0x07, 0x0006}, /* 00,07,06,aa */
+ {0xaa, 0x08, 0x0002}, /* 00,08,02,aa */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+ {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+ {0xaa, 0x13, 0x0063}, /* 00,13,63,aa */
+ {0xaa, 0x15, 0x0070}, /* 00,15,70,aa */
+ {0xa0, 0x37, 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, 0x00, 0x01ad}, /* 01,ad,00,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, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
+ {}
+};
+static const struct usb_action pas202b_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0068}, /* 00,21,68,aa */
+ {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */
+ {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */
+ {0xaa, 0x05, 0x0028}, /* 00,05,28,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,d2,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+ {0xa0, 0xeb, ZC3XX_R020_HSYNC_3}, /* 00,20,eb,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x006c}, /* 00,21,6c,aa */
+ {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */
+ {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */
+ {0xaa, 0x05, 0x002c}, /* 00,05,2c,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+ {0xa0, 0xbe, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,be,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0000}, /* 00,21,00,aa */
+ {0xaa, 0x03, 0x0045}, /* 00,03,45,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+ {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,c0,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */
+ {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */
+ {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */
+ {0xa0, 0xf5, ZC3XX_R020_HSYNC_3}, /* 00,20,f5,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0004}, /* 00,21,04,aa */
+ {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0004}, /* 00,05,04,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+ {0xa0, 0x9f, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,9f,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0020}, /* 00,21,20,aa */
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0020}, /* 00,05,20,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+static const struct usb_action pas202b_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+ {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+ {0xaa, 0x05, 0x0010}, /* 00,05,10,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+ {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x0f, ZC3XX_R087_EXPTIMEMID}, /* 00,87,0f,cc */
+ {0xa0, 0x0e, ZC3XX_R088_EXPTIMELOW}, /* 00,88,0e,cc */
+ {}
+};
+
static const struct usb_action pb03303x_Initial[] = {
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
@@ -5760,7 +5735,7 @@ static const struct usb_action tas5130cxx_Initial[] = {
{}
};
static const struct usb_action tas5130cxx_InitialScale[] = {
- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+/*?? {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, */
{0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
{0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
@@ -6084,7 +6059,7 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = {
{0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa, */
{0xaa, 0x13, 0x0002}, /* 00,13,02,aa, */
{0xaa, 0x15, 0x0004}, /* 00,15,04,aa */
- {0xaa, 0x01, 0x0000},
+/*?? {0xaa, 0x01, 0x0000}, */
{0xaa, 0x01, 0x0000},
{0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa, */
{0xaa, 0x1c, 0x0017}, /* 00,1c,17,aa, */
@@ -6100,8 +6075,8 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = {
{0xaa, 0x0f, 0x00a0}, /* 00,0f,a0,aa, */
{0xaa, 0x10, 0x0000}, /* 00,10,00,aa, */
{0xaa, 0x11, 0x00a0}, /* 00,11,a0,aa, */
- {0xa0, 0x00, 0x0039},
- {0xa1, 0x01, 0x0037},
+/*?? {0xa0, 0x00, 0x0039},
+ {0xa1, 0x01, 0x0037}, */
{0xaa, 0x16, 0x0001}, /* 00,16,01,aa, */
{0xaa, 0x17, 0x00e8}, /* 00,17,e6,aa (e6 -> e8) */
{0xaa, 0x18, 0x0002}, /* 00,18,02,aa, */
@@ -6338,7 +6313,7 @@ static __u8 i2c_write(struct gspca_dev *gspca_dev,
reg_w_i(gspca_dev->dev, valL, 0x93);
reg_w_i(gspca_dev->dev, valH, 0x94);
reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */
- msleep(5);
+ msleep(15);
retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)",
reg, valH, valL, retbyte);
@@ -6381,31 +6356,35 @@ static void setmatrix(struct gspca_dev *gspca_dev)
{0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50};
static const __u8 ov7620_matrix[9] =
{0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58};
+ static const __u8 pas202b_matrix[9] =
+ {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f};
static const __u8 po2030_matrix[9] =
{0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60};
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 */
+ };
- switch (sd->sensor) {
- case SENSOR_GC0305:
- case SENSOR_HV7131B:
- matrix = gc0305_matrix;
- break;
- case SENSOR_MC501CB:
- return; /* no matrix? */
- case SENSOR_OV7620:
-/* case SENSOR_OV7648: */
- matrix = ov7620_matrix;
- break;
- case SENSOR_PO2030:
- matrix = po2030_matrix;
- break;
- case SENSOR_TAS5130C_VF0250:
- matrix = vf0250_matrix;
- break;
- default: /* matrix already loaded */
- return;
- }
+ matrix = matrix_tb[sd->sensor];
+ if (matrix == NULL)
+ return; /* matrix already loaded */
for (i = 0; i < ARRAY_SIZE(ov7620_matrix); i++)
reg_w(gspca_dev->dev, matrix[i], 0x010a + i);
}
@@ -6621,42 +6600,42 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
{gc0305_NoFliker, gc0305_NoFliker,
gc0305_50HZ, gc0305_50HZ,
gc0305_60HZ, gc0305_60HZ},
-/* SENSOR_HDCS2020 3 */
- {NULL, NULL,
- NULL, NULL,
- NULL, NULL},
-/* SENSOR_HDCS2020b 4 */
+/* SENSOR_HDCS2020b 3 */
{hdcs2020b_NoFliker, hdcs2020b_NoFliker,
hdcs2020b_50HZ, hdcs2020b_50HZ,
hdcs2020b_60HZ, hdcs2020b_60HZ},
-/* SENSOR_HV7131B 5 */
+/* SENSOR_HV7131B 4 */
{hv7131b_NoFlikerScale, hv7131b_NoFliker,
hv7131b_50HZScale, hv7131b_50HZ,
hv7131b_60HZScale, hv7131b_60HZ},
-/* SENSOR_HV7131C 6 */
+/* SENSOR_HV7131C 5 */
{NULL, NULL,
NULL, NULL,
NULL, NULL},
-/* SENSOR_ICM105A 7 */
+/* SENSOR_ICM105A 6 */
{icm105a_NoFliker, icm105a_NoFlikerScale,
icm105a_50HZ, icm105a_50HZScale,
icm105a_60HZ, icm105a_60HZScale},
-/* SENSOR_MC501CB 8 */
+/* SENSOR_MC501CB 7 */
{MC501CB_NoFliker, MC501CB_NoFlikerScale,
MC501CB_50HZ, MC501CB_50HZScale,
MC501CB_60HZ, MC501CB_60HZScale},
-/* SENSOR_OV7620 9 */
+/* SENSOR_OV7620 8 */
{OV7620_NoFliker, OV7620_NoFliker,
OV7620_50HZ, OV7620_50HZ,
OV7620_60HZ, OV7620_60HZ},
-/* SENSOR_OV7630C 10 */
+/* SENSOR_OV7630C 9 */
{NULL, NULL,
NULL, NULL,
NULL, NULL},
-/* SENSOR_PAS106 11 */
+/* SENSOR_PAS106 10 */
{pas106b_NoFliker, pas106b_NoFliker,
pas106b_50HZ, pas106b_50HZ,
pas106b_60HZ, pas106b_60HZ},
+/* SENSOR_PAS202B 11 */
+ {pas202b_NoFlikerScale, pas202b_NoFliker,
+ pas202b_50HZScale, pas202b_50HZ,
+ pas202b_60HZScale, pas202b_60HZ},
/* SENSOR_PB0330 12 */
{pb0330_NoFliker, pb0330_NoFlikerScale,
pb0330_50HZ, pb0330_50HZScale,
@@ -7038,15 +7017,15 @@ static int sd_config(struct gspca_dev *gspca_dev,
5, /* SENSOR_CS2102 0 */
5, /* SENSOR_CS2102K 1 */
4, /* SENSOR_GC0305 2 */
- 4, /* SENSOR_HDCS2020 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_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 */
@@ -7102,8 +7081,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor = SENSOR_ICM105A;
break;
case 0x0e:
- PDEBUG(D_PROBE, "Find Sensor HDCS2020");
- sd->sensor = SENSOR_HDCS2020;
+ PDEBUG(D_PROBE, "Find Sensor PAS202B");
+ sd->sensor = SENSOR_PAS202B;
sd->sharpness = 1;
break;
case 0x0f:
@@ -7196,7 +7175,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
case SENSOR_PO2030:
gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX);
break;
- case SENSOR_HDCS2020:
case SENSOR_HV7131B:
case SENSOR_HV7131C:
case SENSOR_OV7630C:
@@ -7226,15 +7204,15 @@ static int sd_start(struct gspca_dev *gspca_dev)
{cs2102_InitialScale, cs2102_Initial}, /* 0 */
{cs2102K_InitialScale, cs2102K_Initial}, /* 1 */
{gc0305_Initial, gc0305_InitialScale}, /* 2 */
- {hdcs2020xx_InitialScale, hdcs2020xx_Initial}, /* 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}, /* 9 */
- {OV7620_mode0, OV7620_mode1}, /* 9 */
- {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */
- {pas106b_InitialScale, pas106b_Initial}, /* 11 */
+ {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 */
/* or {pb03303x_InitialScale, pb03303x_Initial}, */
{PO2030_mode0, PO2030_mode1}, /* 13 */
@@ -7291,6 +7269,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_r(gspca_dev, 0x0008);
reg_w(dev, 0x00, 0x0008);
break;
+ case SENSOR_PAS202B:
+#if 0/*fixme*/
+ reg_r(gspca_dev, ZC3XX_R002_CLOCKSELECT);
+ /* fall thru */
+#endif
case SENSOR_GC0305:
reg_r(gspca_dev, 0x0008);
/* fall thru */
@@ -7304,7 +7287,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_CS2102: /* gamma set in xxx_Initial */
case SENSOR_CS2102K:
- case SENSOR_HDCS2020:
case SENSOR_HDCS2020b:
case SENSOR_PB0330: /* pb with chip_revision - see above */
case SENSOR_OV7630C:
@@ -7317,6 +7299,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
setmatrix(gspca_dev); /* one more time? */
switch (sd->sensor) {
case SENSOR_OV7620:
+ case SENSOR_PAS202B:
reg_r(gspca_dev, 0x0180); /* from win */
reg_w(dev, 0x00, 0x0180);
break;
@@ -7328,37 +7311,29 @@ static int sd_start(struct gspca_dev *gspca_dev)
switch (sd->sensor) {
case SENSOR_GC0305:
- case SENSOR_OV7620:
reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
reg_w(dev, 0x15, 0x01ae);
- sd->autogain = 0;
- break;
+ /* fall thru */
+ case SENSOR_PAS202B:
case SENSOR_PO2030:
- reg_w(dev, 0x40, 0x0117); /* (from win traces) */
+/* reg_w(dev, 0x40, ZC3XX_R117_GGAIN); * (from win traces) */
reg_r(gspca_dev, 0x0180);
break;
- }
-
- setautogain(gspca_dev);
- switch (sd->sensor) {
- case SENSOR_GC0305:
-/* setlightfreq(gspca_dev); ?? (end: 80 -> [18d]) */
- reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
- reg_w(dev, 0x15, 0x01ae);
- reg_w(dev, 0x40, 0x0180);
- reg_w(dev, 0x40, 0x0117);
- reg_r(gspca_dev, 0x0180);
- sd->autogain = 1;
- setautogain(gspca_dev);
- break;
case SENSOR_OV7620:
+ reg_w(dev, 0x09, 0x01ad);
+ reg_w(dev, 0x15, 0x01ae);
i2c_read(gspca_dev, 0x13); /*fixme: returns 0xa3 */
i2c_write(gspca_dev, 0x13, 0xa3, 0x00);
/*fixme: returned value to send? */
- reg_w(dev, 0x40, 0x0117); /* (from win traces) */
+ reg_w(dev, 0x40, 0x0117);
reg_r(gspca_dev, 0x0180);
- setautogain(gspca_dev);
- msleep(500);
+ break;
+ }
+
+ setautogain(gspca_dev);
+ switch (sd->sensor) {
+ case SENSOR_PAS202B:
+ reg_w(dev, 0x00, 0x0007); /* (from win traces) */
break;
case SENSOR_PO2030:
msleep(500);
@@ -7368,6 +7343,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(dev, 0x02, 0x0008);
break;
}
+ if (sd->sensor == SENSOR_PAS202B)
+ reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
return 0;
}
diff --git a/linux/drivers/media/video/ivtv/ivtv-controls.c b/linux/drivers/media/video/ivtv/ivtv-controls.c
index 48e103be7..62aa06f5d 100644
--- a/linux/drivers/media/video/ivtv/ivtv-controls.c
+++ b/linux/drivers/media/video/ivtv/ivtv-controls.c
@@ -63,7 +63,7 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl))
+ if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
@@ -73,7 +73,7 @@ int ivtv_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 (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
+ if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl))
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
return 0;
@@ -122,7 +122,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
@@ -130,7 +130,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
- return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl);
default:
IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
@@ -147,7 +147,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_HUE:
case V4L2_CID_SATURATION:
case V4L2_CID_CONTRAST:
- return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl);
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
@@ -155,7 +155,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
case V4L2_CID_AUDIO_BASS:
case V4L2_CID_AUDIO_TREBLE:
case V4L2_CID_AUDIO_LOUDNESS:
- return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
+ return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl);
default:
IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
return -EINVAL;
@@ -268,7 +268,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1);
fmt.fmt.pix.height = itv->params.height;
- itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, &fmt);
}
err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt)
@@ -279,7 +279,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
/* The audio clock of the digitizer must match the codec sample
rate otherwise you get some very strange effects. */
if (idx < sizeof(freqs))
- ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[idx]);
+ ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
return err;
}
return -EINVAL;
diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c
index db866d912..b29625ca2 100644
--- a/linux/drivers/media/video/ivtv/ivtv-driver.c
+++ b/linux/drivers/media/video/ivtv/ivtv-driver.c
@@ -60,9 +60,6 @@
#include <media/v4l2-chip-ident.h>
#include "tuner-xc2028.h"
-/* var to keep track of the number of array elements in use */
-int ivtv_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 PVR-350 with. Normally this would give a
@@ -70,12 +67,6 @@ int ivtv_cards_active;
setting this to 1 you ensure that radio0 is now also radio1. */
int ivtv_first_minor;
-/* Master variable for all ivtv info */
-struct ivtv *ivtv_cards[IVTV_MAX_CARDS];
-
-/* Protects ivtv_cards_active */
-DEFINE_SPINLOCK(ivtv_cards_lock);
-
/* add your revision and whatnot here */
static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
{PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15,
@@ -87,6 +78,9 @@ static struct pci_device_id ivtv_pci_tbl[] __devinitdata = {
MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl);
+/* ivtv instance counter */
+static atomic_t ivtv_instance = ATOMIC_INIT(0);
+
/* Parameter declarations */
static int cardtype[IVTV_MAX_CARDS];
static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
@@ -599,9 +593,9 @@ static void ivtv_process_options(struct ivtv *itv)
itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers * 1024;
itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers * 1024;
itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers;
- itv->options.cardtype = cardtype[itv->num];
- itv->options.tuner = tuner[itv->num];
- itv->options.radio = radio[itv->num];
+ itv->options.cardtype = cardtype[itv->instance];
+ itv->options.tuner = tuner[itv->instance];
+ itv->options.radio = radio[itv->instance];
itv->options.newi2c = newi2c;
if (tunertype < -1 || tunertype > 1) {
IVTV_WARN("Invalid tunertype argument, will autodetect instead\n");
@@ -688,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->name);
+ itv->irq_work_queues = create_singlethread_workqueue(itv->device.name);
if (itv->irq_work_queues == NULL) {
IVTV_ERR("Could not create ivtv workqueue\n");
return -1;
@@ -774,12 +768,6 @@ static void __devinit ivtv_init_struct2(struct ivtv *itv)
i = 0;
itv->active_input = i;
itv->audio_input = itv->card->video_inputs[i].audio_index;
- if (itv->card->hw_all & IVTV_HW_CX25840)
- itv->video_dec_func = ivtv_cx25840;
- else if (itv->card->hw_all & IVTV_HW_SAA717X)
- itv->video_dec_func = ivtv_saa717x;
- else
- itv->video_dec_func = ivtv_saa7115;
}
static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
@@ -792,21 +780,21 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
IVTV_DEBUG_INFO("Enabling pci device\n");
if (pci_enable_device(dev)) {
- IVTV_ERR("Can't enable device %d!\n", itv->num);
+ IVTV_ERR("Can't enable device!\n");
return -EIO;
}
if (pci_set_dma_mask(dev, 0xffffffff)) {
- IVTV_ERR("No suitable DMA available on card %d.\n", itv->num);
+ IVTV_ERR("No suitable DMA available.\n");
return -EIO;
}
if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) {
- IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num);
+ IVTV_ERR("Cannot request encoder memory region.\n");
return -EIO;
}
if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET,
IVTV_REG_SIZE, "ivtv registers")) {
- IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num);
+ IVTV_ERR("Cannot request register memory region.\n");
release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
return -EIO;
}
@@ -814,7 +802,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
if (itv->has_cx23415 &&
!request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET,
IVTV_DECODER_SIZE, "ivtv decoder")) {
- IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num);
+ IVTV_ERR("Cannot request decoder memory region.\n");
release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE);
return -EIO;
@@ -857,69 +845,11 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
return 0;
}
-#ifdef MODULE
-static u32 ivtv_request_module(struct ivtv *itv, u32 hw,
- const char *name, u32 id)
-{
- if ((hw & id) == 0)
- return hw;
- if (request_module(name) != 0) {
- IVTV_ERR("Failed to load module %s\n", name);
- return hw & ~id;
- }
- IVTV_DEBUG_INFO("Loaded module %s\n", name);
- return hw;
-}
-#endif
-
static void ivtv_load_and_init_modules(struct ivtv *itv)
{
u32 hw = itv->card->hw_all;
unsigned i;
-#ifdef MODULE
- /* load modules */
-#ifdef CONFIG_MEDIA_TUNER_MODULE
- hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER);
-#endif
-#ifdef CONFIG_VIDEO_CX25840_MODULE
- hw = ivtv_request_module(itv, hw, "cx25840", IVTV_HW_CX25840);
-#endif
-#ifdef CONFIG_VIDEO_SAA711X_MODULE
- hw = ivtv_request_module(itv, hw, "saa7115", IVTV_HW_SAA711X);
-#endif
-#ifdef CONFIG_VIDEO_SAA7127_MODULE
- hw = ivtv_request_module(itv, hw, "saa7127", IVTV_HW_SAA7127);
-#endif
-#ifdef CONFIG_VIDEO_SAA717X_MODULE
- hw = ivtv_request_module(itv, hw, "saa717x", IVTV_HW_SAA717X);
-#endif
-#ifdef CONFIG_VIDEO_UPD64031A_MODULE
- hw = ivtv_request_module(itv, hw, "upd64031a", IVTV_HW_UPD64031A);
-#endif
-#ifdef CONFIG_VIDEO_UPD64083_MODULE
- hw = ivtv_request_module(itv, hw, "upd64083", IVTV_HW_UPD6408X);
-#endif
-#ifdef CONFIG_VIDEO_MSP3400_MODULE
- hw = ivtv_request_module(itv, hw, "msp3400", IVTV_HW_MSP34XX);
-#endif
-#ifdef CONFIG_VIDEO_VP27SMPX_MODULE
- hw = ivtv_request_module(itv, hw, "vp27smpx", IVTV_HW_VP27SMPX);
-#endif
-#ifdef CONFIG_VIDEO_WM8775_MODULE
- hw = ivtv_request_module(itv, hw, "wm8775", IVTV_HW_WM8775);
-#endif
-#ifdef CONFIG_VIDEO_WM8739_MODULE
- hw = ivtv_request_module(itv, hw, "wm8739", IVTV_HW_WM8739);
-#endif
-#ifdef CONFIG_VIDEO_CS53L32A_MODULE
- hw = ivtv_request_module(itv, hw, "cs53l32a", IVTV_HW_CS53L32A);
-#endif
-#ifdef CONFIG_VIDEO_M52790_MODULE
- hw = ivtv_request_module(itv, hw, "m52790", IVTV_HW_M52790);
-#endif
-#endif
-
/* check which i2c devices are actually found */
for (i = 0; i < 32; i++) {
u32 device = 1 << i;
@@ -931,11 +861,21 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
itv->hw_flags |= device;
continue;
}
- ivtv_i2c_register(itv, i);
- if (ivtv_i2c_hw_addr(itv, device) > 0)
+ if (ivtv_i2c_register(itv, i) == 0)
itv->hw_flags |= device;
}
+ if (itv->card->hw_all & IVTV_HW_CX25840)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840);
+ else if (itv->card->hw_all & IVTV_HW_SAA717X)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA717X);
+ else if (itv->card->hw_all & IVTV_HW_SAA7114)
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7114);
+ else
+ itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7115);
+ itv->sd_audio = ivtv_find_hw(itv, itv->card->hw_audio_ctrl);
+ itv->sd_muxer = ivtv_find_hw(itv, itv->card->hw_muxer);
+
hw = itv->hw_flags;
if (itv->card->type == IVTV_CARD_CX23416GYC) {
@@ -953,7 +893,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
/* The crystal frequency of GVMVPRX is 24.576MHz */
crystal_freq.freq = SAA7115_FREQ_24_576_MHZ;
crystal_freq.flags = SAA7115_FREQ_FL_UCGC;
- itv->video_dec_func(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq);
+ v4l2_subdev_call(itv->sd_video, video, s_crystal_freq, &crystal_freq);
}
if (hw & IVTV_HW_CX25840) {
@@ -971,7 +911,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
/* determine the exact saa711x model */
itv->hw_flags &= ~IVTV_HW_SAA711X;
- ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v);
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v);
if (v.ident == V4L2_IDENT_SAA7114) {
itv->hw_flags |= IVTV_HW_SAA7114;
/* VBI is not yet supported by the saa7114 driver. */
@@ -1005,28 +945,20 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
int vbi_buf_size;
struct ivtv *itv;
- spin_lock(&ivtv_cards_lock);
-
- /* Make sure we've got a place for this card */
- if (ivtv_cards_active == IVTV_MAX_CARDS) {
- printk(KERN_ERR "ivtv: Maximum number of cards detected (%d)\n",
- ivtv_cards_active);
- spin_unlock(&ivtv_cards_lock);
- return -ENOMEM;
- }
-
itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
- if (itv == NULL) {
- spin_unlock(&ivtv_cards_lock);
+ if (itv == NULL)
return -ENOMEM;
- }
- ivtv_cards[ivtv_cards_active] = itv;
itv->dev = dev;
- itv->num = ivtv_cards_active++;
- snprintf(itv->name, sizeof(itv->name), "ivtv%d", itv->num);
- IVTV_INFO("Initializing card #%d\n", itv->num);
+ itv->instance = atomic_inc_return(&ivtv_instance) - 1;
- spin_unlock(&ivtv_cards_lock);
+ retval = v4l2_device_register(&dev->dev, &itv->device);
+ if (retval)
+ return retval;
+ /* "ivtv + PCI ID" is a bit of a mouthful, so use
+ "ivtv + instance" instead. */
+ snprintf(itv->device.name, sizeof(itv->device.name),
+ "ivtv%d", itv->instance);
+ IVTV_INFO("Initializing card %d\n", itv->instance);
ivtv_process_options(itv);
if (itv->options.cardtype == -1) {
@@ -1047,8 +979,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
else if (retval == -ENXIO)
goto free_mem;
}
- /* save itv in the pci struct for later use */
- pci_set_drvdata(dev, itv);
/* map io memory */
IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
@@ -1090,7 +1020,9 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
goto free_io;
}
- ivtv_gpio_init(itv);
+ retval = ivtv_gpio_init(itv);
+ if (retval)
+ goto free_io;
/* active i2c */
IVTV_DEBUG_INFO("activating i2c...\n");
@@ -1099,8 +1031,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
goto free_io;
}
- IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active);
-
if (itv->card->hw_all & IVTV_HW_TVEEPROM) {
/* Based on the model number the cardtype may be changed.
The PCI IDs are not always reliable. */
@@ -1195,7 +1125,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
ivtv_reset_tuner_gpio : NULL;
- ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup);
+ ivtv_call_all(itv, tuner, s_type_addr, &setup);
if (setup.type == TUNER_XC2028) {
static struct xc2028_ctrl ctrl = {
.fname = XC2028_DEFAULT_FIRMWARE,
@@ -1205,7 +1135,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
.tuner = itv->options.tuner,
.priv = &ctrl,
};
- ivtv_call_i2c_clients(itv, TUNER_SET_CONFIG, &cfg);
+ ivtv_call_all(itv, tuner, s_config, &cfg);
}
}
@@ -1214,11 +1144,11 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
itv->tuner_std = itv->std;
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
- ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std);
+ ivtv_call_all(itv, video, s_std_output, itv->std);
/* Turn off the output signal. The mpeg decoder is not yet
active so without this you would get a green image until the
mpeg decoder becomes active. */
- ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
}
/* clear interrupt mask, effectively disabling interrupts */
@@ -1226,7 +1156,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
/* Register IRQ */
retval = request_irq(itv->dev->irq, ivtv_irq_handler,
- IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv);
+ IRQF_SHARED | IRQF_DISABLED, itv->device.name, (void *)itv);
if (retval) {
IVTV_ERR("Failed to register irq %d\n", retval);
goto free_i2c;
@@ -1242,7 +1172,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
IVTV_ERR("Error %d registering devices\n", retval);
goto free_streams;
}
- IVTV_INFO("Initialized card #%d: %s\n", itv->num, itv->card_name);
+ IVTV_INFO("Initialized card: %s\n", itv->card_name);
return 0;
free_streams:
@@ -1265,10 +1195,8 @@ err:
retval = -ENODEV;
IVTV_ERR("Error %d on initialization\n", retval);
- spin_lock(&ivtv_cards_lock);
- kfree(ivtv_cards[ivtv_cards_active]);
- ivtv_cards[ivtv_cards_active] = NULL;
- spin_unlock(&ivtv_cards_lock);
+ v4l2_device_unregister(&itv->device);
+ kfree(itv);
return retval;
}
@@ -1308,10 +1236,11 @@ int ivtv_init_on_first_open(struct ivtv *itv)
if (itv->card->hw_all & IVTV_HW_CX25840) {
struct v4l2_control ctrl;
+ v4l2_subdev_call(itv->sd_video, core, init, 0);
/* CX25840_CID_ENABLE_PVR150_WORKAROUND */
ctrl.id = V4L2_CID_PRIVATE_BASE;
ctrl.value = itv->pvr150_workaround;
- itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl);
+ v4l2_subdev_call(itv->sd_video, core, s_ctrl, &ctrl);
}
vf.tuner = 0;
@@ -1341,7 +1270,7 @@ int ivtv_init_on_first_open(struct ivtv *itv)
/* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes
the mpeg decoder so now the saa7127 receives a proper
signal. */
- ivtv_saa7127(itv, VIDIOC_STREAMON, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
ivtv_init_mpeg_decoder(itv);
}
ivtv_s_std(NULL, &fh, &itv->tuner_std);
@@ -1366,9 +1295,11 @@ int ivtv_init_on_first_open(struct ivtv *itv)
static void ivtv_remove(struct pci_dev *pci_dev)
{
- struct ivtv *itv = pci_get_drvdata(pci_dev);
+ struct v4l2_device *dev = dev_get_drvdata(&pci_dev->dev);
+ struct ivtv *itv = to_ivtv(dev);
+ int i;
- IVTV_DEBUG_INFO("Removing Card #%d\n", itv->num);
+ IVTV_DEBUG_INFO("Removing card\n");
if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) {
/* Stop all captures */
@@ -1381,7 +1312,7 @@ static void ivtv_remove(struct pci_dev *pci_dev)
/* Turn off the TV-out */
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)
- ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
if (atomic_read(&itv->decoding) > 0) {
int type;
@@ -1406,6 +1337,8 @@ 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);
@@ -1417,8 +1350,11 @@ static void ivtv_remove(struct pci_dev *pci_dev)
release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
pci_disable_device(itv->dev);
+ for (i = 0; i < IVTV_VBI_FRAMES; i++)
+ kfree(itv->vbi.sliced_mpeg_data[i]);
- IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num);
+ printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name);
+ kfree(itv);
}
/* define a pci_driver for card detection */
@@ -1431,54 +1367,36 @@ static struct pci_driver ivtv_pci_driver = {
static int module_start(void)
{
- printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
-
- memset(ivtv_cards, 0, sizeof(ivtv_cards));
+ printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION);
/* Validate parameters */
if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) {
- printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n",
+ printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n",
IVTV_MAX_CARDS - 1);
return -1;
}
if (ivtv_debug < 0 || ivtv_debug > 2047) {
ivtv_debug = 0;
- printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n");
+ printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n");
}
if (pci_register_driver(&ivtv_pci_driver)) {
- printk(KERN_ERR "ivtv: Error detecting PCI card\n");
+ printk(KERN_ERR "ivtv: Error detecting PCI card\n");
return -ENODEV;
}
- printk(KERN_INFO "ivtv: End initialization\n");
+ printk(KERN_INFO "ivtv: End initialization\n");
return 0;
}
static void module_cleanup(void)
{
- int i, j;
-
pci_unregister_driver(&ivtv_pci_driver);
-
- spin_lock(&ivtv_cards_lock);
- for (i = 0; i < ivtv_cards_active; i++) {
- if (ivtv_cards[i] == NULL)
- continue;
- for (j = 0; j < IVTV_VBI_FRAMES; j++) {
- kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]);
- }
- kfree(ivtv_cards[i]);
- }
- spin_unlock(&ivtv_cards_lock);
}
/* Note: These symbols are exported because they are used by the ivtvfb
framebuffer module and an infrared module for the IR-blaster. */
EXPORT_SYMBOL(ivtv_set_irq_mask);
-EXPORT_SYMBOL(ivtv_cards_active);
-EXPORT_SYMBOL(ivtv_cards);
-EXPORT_SYMBOL(ivtv_cards_lock);
EXPORT_SYMBOL(ivtv_api);
EXPORT_SYMBOL(ivtv_vapi);
EXPORT_SYMBOL(ivtv_vapi_result);
diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.h b/linux/drivers/media/video/ivtv/ivtv-driver.h
index 9128df0f3..51f6e54ba 100644
--- a/linux/drivers/media/video/ivtv/ivtv-driver.h
+++ b/linux/drivers/media/video/ivtv/ivtv-driver.h
@@ -62,6 +62,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 <media/cx2341x.h>
@@ -114,9 +115,6 @@
#define IVTV_REG_VPU (0x9058)
#define IVTV_REG_APU (0xA064)
-/* i2c stuff */
-#define I2C_CLIENTS_MAX 16
-
/* debugging */
extern int ivtv_debug;
@@ -133,12 +131,10 @@ extern int ivtv_debug;
/* Flag to turn on high volume debugging */
#define IVTV_DBGFLG_HIGHVOL (1 << 10)
-/* NOTE: extra space before comma in 'itv->num , ## args' is required for
- gcc-2.95, otherwise it won't compile. */
#define IVTV_DEBUG(x, type, fmt, args...) \
do { \
if ((x) & ivtv_debug) \
- printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \
+ v4l2_info(&itv->device, " " 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)
@@ -153,8 +149,8 @@ extern int ivtv_debug;
#define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \
do { \
- if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \
- printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \
+ if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \
+ v4l2_info(&itv->device, " " 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)
@@ -168,9 +164,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...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args)
-#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args)
-#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args)
+#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)
/* output modes (cx23415 only) */
#define OUT_NONE 0
@@ -597,8 +593,6 @@ struct ivtv_card;
/* Struct to hold info about ivtv cards */
struct ivtv {
/* General fixed card data */
- int num; /* board number, -1 during init! */
- char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */
struct pci_dev *dev; /* PCI device */
const struct ivtv_card *card; /* card information */
const char *card_name; /* full name of the card */
@@ -610,14 +604,18 @@ struct ivtv {
u32 v4l2_cap; /* V4L2 capabilities of card */
u32 hw_flags; /* hardware description of the board */
v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */
- /* controlling video decoder function */
- int (*video_dec_func)(struct ivtv *, unsigned int, void *);
+ struct v4l2_subdev *sd_video; /* controlling video decoder subdev */
+ struct v4l2_subdev *sd_audio; /* controlling audio subdev */
+ struct v4l2_subdev *sd_muxer; /* controlling audio muxer subdev */
u32 base_addr; /* PCI resource base address */
volatile void __iomem *enc_mem; /* pointer to mapped encoder memory */
volatile void __iomem *dec_mem; /* pointer to mapped decoder memory */
volatile void __iomem *reg_mem; /* pointer to mapped registers */
struct ivtv_options options; /* user options */
+ struct v4l2_device device;
+ struct v4l2_subdev sd_gpio; /* GPIO sub-device */
+ u16 instance;
/* High-level state info */
unsigned long i_flags; /* global ivtv flags */
@@ -677,7 +675,6 @@ struct ivtv {
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
- struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];/* pointers to all I2C clients */
int i2c_state; /* i2c bit state */
struct mutex i2c_bus_lock; /* lock i2c bus */
@@ -723,11 +720,13 @@ struct ivtv {
struct osd_info *osd_info; /* ivtvfb private OSD info */
};
+static inline struct ivtv *to_ivtv(struct v4l2_device *dev)
+{
+ return container_of(dev, struct ivtv, device);
+}
+
/* Globals */
-extern struct ivtv *ivtv_cards[];
-extern int ivtv_cards_active;
extern int ivtv_first_minor;
-extern spinlock_t ivtv_cards_lock;
/*==============Prototypes==================*/
@@ -787,4 +786,19 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv)
#define write_dec_sync(val, addr) \
do { write_dec(val, addr); read_dec(addr); } while (0)
+/* 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)
+
+#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args)
+
+/* Call the specified callback for all subdevs matching 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 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)
+
+#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args)
+
#endif
diff --git a/linux/drivers/media/video/ivtv/ivtv-fileops.c b/linux/drivers/media/video/ivtv/ivtv-fileops.c
index 1c404e454..5eb587592 100644
--- a/linux/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/linux/drivers/media/video/ivtv/ivtv-fileops.c
@@ -155,7 +155,7 @@ static void ivtv_dualwatch(struct ivtv *itv)
new_stereo_mode = itv->params.audio_properties & stereo_mask;
memset(&vt, 0, sizeof(vt));
- ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt);
+ ivtv_call_all(itv, tuner, g_tuner, &vt);
if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
new_stereo_mode = dual;
@@ -857,7 +857,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp)
/* Mark that the radio is no longer in use */
clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags);
/* Switch tuner to TV */
- ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+ ivtv_call_all(itv, tuner, s_std, itv->std);
/* Select correct audio input (i.e. TV tuner or Line in) */
ivtv_audio_set_io(itv);
if (itv->hw_flags & IVTV_HW_SAA711X)
@@ -865,7 +865,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp)
struct v4l2_crystal_freq crystal_freq;
crystal_freq.freq = SAA7115_FREQ_32_11_MHZ;
crystal_freq.flags = 0;
- ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq);
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq);
}
if (atomic_read(&itv->capturing) > 0) {
/* Undo video mute */
@@ -952,15 +952,14 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
/* We have the radio */
ivtv_mute(itv);
/* Switch tuner to radio */
- ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL);
+ ivtv_call_all(itv, tuner, s_radio);
/* Select the correct audio input (i.e. radio tuner) */
ivtv_audio_set_io(itv);
- if (itv->hw_flags & IVTV_HW_SAA711X)
- {
+ if (itv->hw_flags & IVTV_HW_SAA711X) {
struct v4l2_crystal_freq crystal_freq;
crystal_freq.freq = SAA7115_FREQ_32_11_MHZ;
crystal_freq.flags = SAA7115_FREQ_FL_APLL;
- ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq);
+ ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq);
}
/* Done! Unmute and continue. */
ivtv_unmute(itv);
@@ -981,37 +980,18 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp)
int ivtv_v4l2_open(struct inode *inode, struct file *filp)
{
- int res, x, y = 0;
+ int res;
struct ivtv *itv = NULL;
struct ivtv_stream *s = NULL;
- int minor = iminor(inode);
-
- /* Find which card this open was on */
- spin_lock(&ivtv_cards_lock);
- for (x = 0; itv == NULL && x < ivtv_cards_active; x++) {
- if (ivtv_cards[x] == NULL)
- continue;
- /* find out which stream this open was on */
- for (y = 0; y < IVTV_MAX_STREAMS; y++) {
- s = &ivtv_cards[x]->streams[y];
- if (s->v4l2dev && s->v4l2dev->minor == minor) {
- itv = ivtv_cards[x];
- break;
- }
- }
- }
- spin_unlock(&ivtv_cards_lock);
+ struct video_device *vdev = video_devdata(filp);
- if (itv == NULL) {
- /* Couldn't find a device registered
- on that minor, shouldn't happen! */
- printk(KERN_WARNING "No ivtv device found on minor %d\n", minor);
- return -ENXIO;
- }
+ s = video_get_drvdata(vdev);
+ itv = s->itv;
mutex_lock(&itv->serialize_lock);
if (ivtv_init_on_first_open(itv)) {
- IVTV_ERR("Failed to initialize on minor %d\n", minor);
+ IVTV_ERR("Failed to initialize on minor %d\n",
+ s->v4l2dev->minor);
mutex_unlock(&itv->serialize_lock);
return -ENXIO;
}
diff --git a/linux/drivers/media/video/ivtv/ivtv-gpio.c b/linux/drivers/media/video/ivtv/ivtv-gpio.c
index 74a44844c..dc2850e87 100644
--- a/linux/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/linux/drivers/media/video/ivtv/ivtv-gpio.c
@@ -144,22 +144,9 @@ int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value)
return 0;
}
-void ivtv_gpio_init(struct ivtv *itv)
+static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd)
{
- u16 pin = 0;
-
- if (itv->card->xceive_pin)
- pin = 1 << itv->card->xceive_pin;
-
- if ((itv->card->gpio_init.direction | pin) == 0)
- return;
-
- IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
- read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
-
- /* init output data then direction */
- 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);
+ return container_of(sd, struct ivtv, sd_gpio);
}
static struct v4l2_queryctrl gpio_ctrl_mute = {
@@ -173,134 +160,231 @@ static struct v4l2_queryctrl gpio_ctrl_mute = {
.flags = 0,
};
-int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg)
+static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
- struct v4l2_tuner *tuner = arg;
- struct v4l2_control *ctrl = arg;
- struct v4l2_routing *route = arg;
+ struct ivtv *itv = sd_to_ivtv(sd);
u16 mask, data;
- switch (command) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- mask = itv->card->gpio_audio_freq.mask;
- switch (*(u32 *)arg) {
- case 32000:
- data = itv->card->gpio_audio_freq.f32000;
- break;
- case 44100:
- data = itv->card->gpio_audio_freq.f44100;
- break;
- case 48000:
- default:
- data = itv->card->gpio_audio_freq.f48000;
- break;
- }
+ mask = itv->card->gpio_audio_freq.mask;
+ switch (freq) {
+ case 32000:
+ data = itv->card->gpio_audio_freq.f32000;
+ break;
+ case 44100:
+ data = itv->card->gpio_audio_freq.f44100;
+ break;
+ case 48000:
+ default:
+ data = itv->card->gpio_audio_freq.f48000;
break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
- case VIDIOC_G_TUNER:
- mask = itv->card->gpio_audio_detect.mask;
- if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
- tuner->rxsubchans = V4L2_TUNER_MODE_STEREO |
- V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
- else
- tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
- return 0;
+static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask;
+
+ mask = itv->card->gpio_audio_detect.mask;
+ if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask))
+ vt->rxsubchans = V4L2_TUNER_MODE_STEREO |
+ V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2;
+ else
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ return 0;
+}
- case VIDIOC_S_TUNER:
- mask = itv->card->gpio_audio_mode.mask;
- switch (tuner->audmode) {
- case V4L2_TUNER_MODE_LANG1:
- data = itv->card->gpio_audio_mode.lang1;
- break;
- case V4L2_TUNER_MODE_LANG2:
- data = itv->card->gpio_audio_mode.lang2;
- break;
- case V4L2_TUNER_MODE_MONO:
- data = itv->card->gpio_audio_mode.mono;
- break;
- case V4L2_TUNER_MODE_STEREO:
- case V4L2_TUNER_MODE_LANG1_LANG2:
- default:
- data = itv->card->gpio_audio_mode.stereo;
- break;
- }
- break;
+static int subdev_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- case AUDC_SET_RADIO:
- mask = itv->card->gpio_audio_input.mask;
- data = itv->card->gpio_audio_input.radio;
+ mask = itv->card->gpio_audio_mode.mask;
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_LANG1:
+ data = itv->card->gpio_audio_mode.lang1;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ data = itv->card->gpio_audio_mode.lang2;
+ break;
+ case V4L2_TUNER_MODE_MONO:
+ data = itv->card->gpio_audio_mode.mono;
break;
+ case V4L2_TUNER_MODE_STEREO:
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ default:
+ data = itv->card->gpio_audio_mode.stereo;
+ break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_s_radio(struct v4l2_subdev *sd)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ mask = itv->card->gpio_audio_input.mask;
+ data = itv->card->gpio_audio_input.radio;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
- case VIDIOC_S_STD:
- mask = itv->card->gpio_audio_input.mask;
+static int subdev_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ mask = itv->card->gpio_audio_input.mask;
+ data = itv->card->gpio_audio_input.tuner;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
+
+ if (route->input > 2)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_input.mask;
+ switch (route->input) {
+ case 0:
data = itv->card->gpio_audio_input.tuner;
break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- if (route->input > 2)
- return -EINVAL;
- mask = itv->card->gpio_audio_input.mask;
- switch (route->input) {
- case 0:
- data = itv->card->gpio_audio_input.tuner;
- break;
- case 1:
- data = itv->card->gpio_audio_input.linein;
- break;
- case 2:
- default:
- data = itv->card->gpio_audio_input.radio;
- break;
- }
+ case 1:
+ data = itv->card->gpio_audio_input.linein;
+ break;
+ case 2:
+ default:
+ data = itv->card->gpio_audio_input.radio;
break;
+ }
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
- case VIDIOC_G_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = itv->card->gpio_audio_mute.mute;
- ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
- return 0;
+static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- case VIDIOC_S_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- mask = itv->card->gpio_audio_mute.mask;
- data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
- break;
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_mute.mask;
+ data = itv->card->gpio_audio_mute.mute;
+ ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data;
+ return 0;
+}
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- if (qc->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- *qc = gpio_ctrl_mute;
- return 0;
- }
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ mask = itv->card->gpio_audio_mute.mask;
+ data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0;
+ if (mask)
+ write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
+ return 0;
+}
+
+static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ if (qc->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ *qc = gpio_ctrl_mute;
+ return 0;
+}
+
+static int subdev_log_status(struct v4l2_subdev *sd)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
- case VIDIOC_LOG_STATUS:
- IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
+ IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n",
read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT),
read_reg(IVTV_REG_GPIO_IN));
- return 0;
+ return 0;
+}
- case VIDIOC_INT_S_VIDEO_ROUTING:
- if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */
- return -EINVAL;
- mask = itv->card->gpio_video_input.mask;
- if (route->input == 0)
- data = itv->card->gpio_video_input.tuner;
- else if (route->input == 1)
- data = itv->card->gpio_video_input.composite;
- else
- data = itv->card->gpio_video_input.svideo;
- break;
+static int subdev_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct ivtv *itv = sd_to_ivtv(sd);
+ u16 mask, data;
- default:
+ if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */
return -EINVAL;
- }
+ mask = itv->card->gpio_video_input.mask;
+ if (route->input == 0)
+ data = itv->card->gpio_video_input.tuner;
+ else if (route->input == 1)
+ data = itv->card->gpio_video_input.composite;
+ else
+ data = itv->card->gpio_video_input.svideo;
if (mask)
write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT);
return 0;
}
+
+static const struct v4l2_subdev_core_ops subdev_core_ops = {
+ .log_status = subdev_log_status,
+ .g_ctrl = subdev_g_ctrl,
+ .s_ctrl = subdev_s_ctrl,
+ .queryctrl = subdev_queryctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = {
+ .s_std = subdev_s_std,
+ .s_radio = subdev_s_radio,
+ .g_tuner = subdev_g_tuner,
+ .s_tuner = subdev_s_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops subdev_audio_ops = {
+ .s_clock_freq = subdev_s_clock_freq,
+ .s_routing = subdev_s_audio_routing,
+};
+
+static const struct v4l2_subdev_video_ops subdev_video_ops = {
+ .s_routing = subdev_s_video_routing,
+};
+
+static const struct v4l2_subdev_ops subdev_ops = {
+ .core = &subdev_core_ops,
+ .tuner = &subdev_tuner_ops,
+ .audio = &subdev_audio_ops,
+ .video = &subdev_video_ops,
+};
+
+int ivtv_gpio_init(struct ivtv *itv)
+{
+ u16 pin = 0;
+
+ if (itv->card->xceive_pin)
+ pin = 1 << itv->card->xceive_pin;
+
+ if ((itv->card->gpio_init.direction | pin) == 0)
+ return 0;
+
+ IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
+ read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
+
+ /* init output data then direction */
+ 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);
+ itv->sd_gpio.grp_id = IVTV_HW_GPIO;
+ return v4l2_device_register_subdev(&itv->device, &itv->sd_gpio);
+}
diff --git a/linux/drivers/media/video/ivtv/ivtv-gpio.h b/linux/drivers/media/video/ivtv/ivtv-gpio.h
index 48b629161..0b5d19c8e 100644
--- a/linux/drivers/media/video/ivtv/ivtv-gpio.h
+++ b/linux/drivers/media/video/ivtv/ivtv-gpio.h
@@ -22,9 +22,8 @@
#define IVTV_GPIO_H
/* GPIO stuff */
-void ivtv_gpio_init(struct ivtv *itv);
+int ivtv_gpio_init(struct ivtv *itv);
void ivtv_reset_ir_gpio(struct ivtv *itv);
int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value);
-int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg);
#endif
diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.c b/linux/drivers/media/video/ivtv/ivtv-i2c.c
index fa18eae17..41452e111 100644
--- a/linux/drivers/media/video/ivtv/ivtv-i2c.c
+++ b/linux/drivers/media/video/ivtv/ivtv-i2c.c
@@ -90,26 +90,6 @@
#define IVTV_M52790_I2C_ADDR 0x48
/* This array should match the IVTV_HW_ defines */
-static const u8 hw_driverids[] = {
- I2C_DRIVERID_CX25840,
- I2C_DRIVERID_SAA711X,
- I2C_DRIVERID_SAA7127,
- I2C_DRIVERID_MSP3400,
- I2C_DRIVERID_TUNER,
- I2C_DRIVERID_WM8775,
- I2C_DRIVERID_CS53L32A,
- I2C_DRIVERID_TVEEPROM,
- I2C_DRIVERID_SAA711X,
- I2C_DRIVERID_UPD64031A,
- I2C_DRIVERID_UPD64083,
- I2C_DRIVERID_SAA717X,
- I2C_DRIVERID_WM8739,
- I2C_DRIVERID_VP27SMPX,
- I2C_DRIVERID_M52790,
- 0 /* IVTV_HW_GPIO dummy driver ID */
-};
-
-/* This array should match the IVTV_HW_ defines */
static const u8 hw_addrs[] = {
IVTV_CX25840_I2C_ADDR,
IVTV_SAA7115_I2C_ADDR,
@@ -130,6 +110,26 @@ static const u8 hw_addrs[] = {
};
/* This array should match the IVTV_HW_ defines */
+static const char *hw_modules[] = {
+ "cx25840",
+ "saa7115",
+ "saa7127",
+ "msp3400",
+ "tuner",
+ "wm8775",
+ "cs53l32a",
+ NULL,
+ "saa7115",
+ "upd64031a",
+ "upd64083",
+ "saa717x",
+ "wm8739",
+ "vp27smpx",
+ "m52790",
+ NULL
+};
+
+/* This array should match the IVTV_HW_ defines */
static const char * const hw_devicenames[] = {
"cx25840",
"saa7115",
@@ -155,104 +155,58 @@ static const char * const hw_devicenames[] = {
int ivtv_i2c_register(struct ivtv *itv, unsigned idx)
{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
- struct i2c_board_info info;
- struct i2c_client *c;
- u8 id;
- int i;
+ struct v4l2_subdev *sd;
+ struct i2c_adapter *adap = &itv->i2c_adap;
+ const char *mod = hw_modules[idx];
+ const char *type = hw_devicenames[idx];
+ u32 hw = 1 << idx;
- IVTV_DEBUG_I2C("i2c client register\n");
- if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
+ if (idx >= ARRAY_SIZE(hw_addrs))
return -1;
- id = hw_driverids[idx];
- memset(&info, 0, sizeof(info));
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
- strlcpy(info.driver_name, hw_devicenames[idx],
- sizeof(info.driver_name));
-#else
- strlcpy(info.type, hw_devicenames[idx], sizeof(info.type));
-#endif
- info.addr = hw_addrs[idx];
- for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {}
-
- if (i == I2C_CLIENTS_MAX) {
- IVTV_ERR("insufficient room for new I2C client!\n");
- return -ENOMEM;
+ if (hw == IVTV_HW_TUNER) {
+ /* special tuner handling */
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ itv->card_i2c->radio);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ itv->card_i2c->demod);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
+ itv->card_i2c->tv);
+ if (sd)
+ sd->grp_id = 1 << idx;
+ return sd ? 0 : -1;
}
+ if (!hw_addrs[idx])
+ return -1;
+ if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) {
+ unsigned short addrs[2] = { hw_addrs[idx], I2C_CLIENT_END };
- if (id != I2C_DRIVERID_TUNER) {
- if (id == I2C_DRIVERID_UPD64031A ||
- id == I2C_DRIVERID_UPD64083) {
- unsigned short addrs[2] = { info.addr, I2C_CLIENT_END };
-
- c = i2c_new_probed_device(&itv->i2c_adap, &info, addrs);
- } else
- c = i2c_new_device(&itv->i2c_adap, &info);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i] = c;
- return itv->i2c_clients[i] ? 0 : -ENODEV;
- }
-
- /* special tuner handling */
- c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->radio);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i++] = c;
- c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->demod);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i++] = c;
- c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->tv);
- if (c && c->driver == NULL)
- i2c_unregister_device(c);
- else if (c)
- itv->i2c_clients[i++] = c;
- return 0;
-#else
- return 0;
-#endif
-}
-
-static int attach_inform(struct i2c_client *client)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
- struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
- int i;
-
- IVTV_DEBUG_I2C("i2c client attach\n");
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- if (itv->i2c_clients[i] == NULL) {
- itv->i2c_clients[i] = client;
- break;
- }
- }
- if (i == I2C_CLIENTS_MAX) {
- IVTV_ERR("Insufficient room for new I2C client\n");
+ sd = v4l2_i2c_new_probed_subdev(adap, mod, type, addrs);
+ } else {
+ sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]);
}
-#endif
- return 0;
+ if (sd)
+ sd->grp_id = 1 << idx;
+ return sd ? 0 : -1;
}
-static int detach_inform(struct i2c_client *client)
+struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw)
{
- int i;
- struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter);
+ struct v4l2_subdev *result = NULL;
+ struct v4l2_subdev *sd;
- IVTV_DEBUG_I2C("i2c client detach\n");
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- if (itv->i2c_clients[i] == client) {
- itv->i2c_clients[i] = NULL;
+ spin_lock(&itv->device.lock);
+ v4l2_device_for_each_subdev(sd, &itv->device) {
+ if (sd->grp_id == hw) {
+ result = sd;
break;
}
}
- IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n",
- client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
-
- return 0;
+ spin_unlock(&itv->device.lock);
+ return result;
}
/* Set the serial clock line to the desired state */
@@ -522,7 +476,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 ivtv *itv = i2c_get_adapdata(i2c_adap);
+ struct v4l2_device *drv = i2c_get_adapdata(i2c_adap);
+ struct ivtv *itv = to_ivtv(drv);
int retval;
int i;
@@ -561,8 +516,6 @@ static struct i2c_adapter ivtv_i2c_adap_hw_template = {
.id = I2C_HW_B_CX2341X,
.algo = &ivtv_algo,
.algo_data = NULL, /* filled from template */
- .client_register = attach_inform,
- .client_unregister = detach_inform,
.owner = THIS_MODULE,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
.class = I2C_CLASS_TV_ANALOG,
@@ -617,8 +570,6 @@ static struct i2c_adapter ivtv_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,
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
.class = I2C_CLASS_TV_ANALOG,
@@ -638,160 +589,6 @@ static struct i2c_client ivtv_i2c_client_template = {
.name = "ivtv internal",
};
-int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg)
-{
- struct i2c_client *client;
- int retval;
- int i;
-
- IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- client = itv->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_G_CHIP_IDENT)
- IVTV_ERR("i2c addr 0x%02x not found for command 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 ivtv_i2c_id_addr(struct ivtv *itv, u32 id)
-{
- struct i2c_client *client;
- int retval = -ENODEV;
- int i;
-
- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
- client = itv->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 DRIVERID */
-static const char *ivtv_i2c_id_name(u32 id)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
- if (hw_driverids[i] == id)
- return hw_devicenames[i];
- return "unknown device";
-}
-
-/* Find the i2c device name matching the IVTV_HW_ flag */
-static const char *ivtv_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 IVTV_HW_ flag and return
- its i2c address or -ENODEV if no matching device was found. */
-int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
- if (1 << i == hw)
- return ivtv_i2c_id_addr(itv, hw_driverids[i]);
- return -ENODEV;
-}
-
-/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing.
- If hw == IVTV_HW_GPIO then call the gpio handler. */
-int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg)
-{
- int addr;
-
- if (hw == IVTV_HW_GPIO)
- return ivtv_gpio(itv, cmd, arg);
- if (hw == 0)
- return 0;
-
- addr = ivtv_i2c_hw_addr(itv, hw);
- if (addr < 0) {
- IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x\n",
- hw, ivtv_i2c_hw_name(hw), cmd);
- return addr;
- }
- return ivtv_call_i2c_client(itv, addr, cmd, arg);
-}
-
-/* Calls i2c device based on I2C driver ID. */
-int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg)
-{
- int addr;
-
- addr = ivtv_i2c_id_addr(itv, id);
- if (addr < 0) {
- if (cmd != VIDIOC_G_CHIP_IDENT)
- IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x\n",
- id, ivtv_i2c_id_name(id), cmd);
- return addr;
- }
- return ivtv_call_i2c_client(itv, addr, cmd, arg);
-}
-
-int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg);
-}
-EXPORT_SYMBOL(ivtv_saa7127);
-
-int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg);
-}
-
-int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg);
-}
-
-/* broadcast cmd for all I2C clients and for the gpio subsystem */
-void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg)
-{
- if (itv->i2c_adap.algo == NULL) {
- IVTV_ERR("Adapter is not set");
- return;
- }
- i2c_clients_command(&itv->i2c_adap, cmd, arg);
- if (itv->hw_flags & IVTV_HW_GPIO)
- ivtv_gpio(itv, cmd, arg);
-}
-
/* init + register i2c algo-bit adapter */
int init_ivtv_i2c(struct ivtv *itv)
{
@@ -800,10 +597,9 @@ int init_ivtv_i2c(struct ivtv *itv)
/* Sanity checks for the I2C hardware arrays. They must be the
* same size and GPIO must be the last entry.
*/
- if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) ||
- ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
- IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1)) ||
- hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) {
+ if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
+ ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules) ||
+ IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1))) {
IVTV_ERR("Mismatched I2C hardware arrays\n");
return -ENODEV;
}
@@ -820,8 +616,8 @@ int init_ivtv_i2c(struct ivtv *itv)
itv->i2c_adap.algo_data = &itv->i2c_algo;
sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d",
- itv->num);
- i2c_set_adapdata(&itv->i2c_adap, itv);
+ itv->instance);
+ i2c_set_adapdata(&itv->i2c_adap, &itv->device);
memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
sizeof(struct i2c_client));
diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.h b/linux/drivers/media/video/ivtv/ivtv-i2c.h
index 022978cf5..396928a06 100644
--- a/linux/drivers/media/video/ivtv/ivtv-i2c.h
+++ b/linux/drivers/media/video/ivtv/ivtv-i2c.h
@@ -21,19 +21,8 @@
#ifndef IVTV_I2C_H
#define IVTV_I2C_H
-int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg);
-int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg);
-
-int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw);
-int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg);
-int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg);
-int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg);
-void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg);
int ivtv_i2c_register(struct ivtv *itv, unsigned idx);
+struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw);
/* init + register i2c algo-bit adapter */
int init_ivtv_i2c(struct ivtv *itv);
diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.c b/linux/drivers/media/video/ivtv/ivtv-ioctl.c
index 4bae38d21..cd990a4b8 100644
--- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -393,7 +393,7 @@ static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo
return 0;
}
- itv->video_dec_func(itv, VIDIOC_G_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
vbifmt->service_set = ivtv_get_service_set(vbifmt);
return 0;
}
@@ -581,7 +581,7 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
p->height = h;
if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1)
fmt->fmt.pix.width /= 2;
- itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
return ivtv_g_fmt_vid_cap(file, fh, fmt);
}
@@ -593,7 +593,7 @@ static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f
return -EBUSY;
itv->vbi.sliced_in->service_set = 0;
itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
- itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
return ivtv_g_fmt_vbi_cap(file, fh, fmt);
}
@@ -611,7 +611,7 @@ static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo
if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0)
return -EBUSY;
itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
- itv->video_dec_func(itv, VIDIOC_S_FMT, fmt);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt);
memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in));
return 0;
}
@@ -685,18 +685,17 @@ static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_chip_ident
chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416;
return 0;
}
- if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return ivtv_i2c_id(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip);
- if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
- return ivtv_call_i2c_client(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip);
- return -EINVAL;
+ if (chip->match_type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match_type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+ /* TODO: is this correct? */
+ return ivtv_call_all_err(itv, core, g_chip_ident, chip);
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
{
struct v4l2_register *regs = arg;
- unsigned long flags;
volatile u8 __iomem *reg_start;
if (!capable(CAP_SYS_ADMIN))
@@ -711,12 +710,10 @@ static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg)
else
return -EINVAL;
- spin_lock_irqsave(&ivtv_cards_lock, flags);
if (cmd == VIDIOC_DBG_G_REGISTER)
regs->val = readl(regs->reg + reg_start);
else
writel(regs->val, regs->reg + reg_start);
- spin_unlock_irqrestore(&ivtv_cards_lock, flags);
return 0;
}
@@ -726,9 +723,10 @@ static int ivtv_g_register(struct file *file, void *fh, struct v4l2_register *re
if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg);
- if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg);
- return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg);
+ /* TODO: subdev errors should not be ignored, this should become a
+ subdev helper function. */
+ ivtv_call_all(itv, core, g_register, reg);
+ return 0;
}
static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *reg)
@@ -737,9 +735,10 @@ static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *re
if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg);
- if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
- return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg);
- return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg);
+ /* TODO: subdev errors should not be ignored, this should become a
+ subdev helper function. */
+ ivtv_call_all(itv, core, s_register, reg);
+ return 0;
}
#endif
@@ -884,12 +883,6 @@ static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
streamtype = id->type;
- if (ivtv_debug & IVTV_DBGFLG_IOCTL) {
- printk(KERN_INFO "ivtv%d ioctl: ", itv->num);
- /* Should be replaced */
- /* v4l_printk_ioctl(VIDIOC_S_CROP); */
- }
-
if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) {
@@ -1050,7 +1043,7 @@ static int ivtv_s_output(struct file *file, void *fh, unsigned int outp)
itv->active_output = outp;
route.input = SAA7127_INPUT_TYPE_NORMAL;
route.output = itv->card->video_outputs[outp].video_output;
- ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, &route);
return 0;
}
@@ -1062,7 +1055,7 @@ static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *
if (vf->tuner != 0)
return -EINVAL;
- ivtv_call_i2c_clients(itv, VIDIOC_G_FREQUENCY, vf);
+ ivtv_call_all(itv, tuner, g_frequency, vf);
return 0;
}
@@ -1075,7 +1068,7 @@ int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
ivtv_mute(itv);
IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
- ivtv_call_i2c_clients(itv, VIDIOC_S_FREQUENCY, vf);
+ ivtv_call_all(itv, tuner, s_frequency, vf);
ivtv_unmute(itv);
return 0;
}
@@ -1123,14 +1116,14 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std)
IVTV_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)itv->std);
/* Tuner */
- ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std);
+ ivtv_call_all(itv, tuner, s_std, itv->std);
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
/* set display standard */
itv->std_out = *std;
itv->is_out_60hz = itv->is_60hz;
itv->is_out_50hz = itv->is_50hz;
- ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out);
+ ivtv_call_all(itv, video, s_std_output, itv->std_out);
ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz);
itv->main_rect.left = itv->main_rect.top = 0;
itv->main_rect.width = 720;
@@ -1154,7 +1147,7 @@ static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0)
return -EINVAL;
- ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt);
+ ivtv_call_all(itv, tuner, s_tuner, vt);
return 0;
}
@@ -1166,7 +1159,7 @@ static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
if (vt->index != 0)
return -EINVAL;
- ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt);
+ ivtv_call_all(itv, tuner, g_tuner, vt);
if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) {
strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name));
@@ -1444,14 +1437,15 @@ static int ivtv_log_status(struct file *file, void *fh)
struct v4l2_audio audin;
int i;
- IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num);
+ IVTV_INFO("================= START STATUS CARD #%d =================\n",
+ itv->instance);
IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name);
if (itv->hw_flags & IVTV_HW_TVEEPROM) {
struct tveeprom tv;
ivtv_read_eeprom(itv, &tv);
}
- ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL);
+ ivtv_call_all(itv, core, log_status);
ivtv_get_input(itv, itv->active_input, &vidin);
ivtv_get_audio_input(itv, itv->audio_input, &audin);
IVTV_INFO("Video Input: %s\n", vidin.name);
@@ -1518,7 +1512,7 @@ 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->name);
+ cx2341x_log_status(&itv->params, itv->device.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];
@@ -1530,8 +1524,11 @@ static int ivtv_log_status(struct file *file, void *fh)
(s->buffers * s->buf_size) / 1024, s->buffers);
}
- IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted);
- IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num);
+ IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n",
+ (long long)itv->mpg_data_received,
+ (long long)itv->vbi_data_inserted);
+ IVTV_INFO("================== END STATUS CARD #%d ==================\n",
+ itv->instance);
return 0;
}
@@ -1736,7 +1733,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg)
case VIDIOC_INT_S_AUDIO_ROUTING: {
struct v4l2_routing *route = arg;
- ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route);
+ ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, route);
break;
}
@@ -1746,7 +1743,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg)
if ((val == 0 && itv->options.newi2c) || (val & 0x01))
ivtv_reset_ir_gpio(itv);
if (val & 0x02)
- itv->video_dec_func(itv, cmd, NULL);
+ v4l2_subdev_call(itv->sd_video, core, reset, 0);
break;
}
diff --git a/linux/drivers/media/video/ivtv/ivtv-routing.c b/linux/drivers/media/video/ivtv/ivtv-routing.c
index 05564919b..3fd302294 100644
--- a/linux/drivers/media/video/ivtv/ivtv-routing.c
+++ b/linux/drivers/media/video/ivtv/ivtv-routing.c
@@ -47,13 +47,13 @@ void ivtv_audio_set_io(struct ivtv *itv)
route.output = 0;
if (itv->card->hw_muxer & IVTV_HW_M52790)
route.output = M52790_OUT_STEREO;
- ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+ v4l2_subdev_call(itv->sd_muxer, audio, s_routing, &route);
route.input = in->audio_input;
route.output = 0;
if (itv->card->hw_audio & IVTV_HW_MSP34XX)
route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
- ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route);
+ ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, &route);
}
/* Selects the video input and output according to the current
@@ -66,7 +66,7 @@ void ivtv_video_set_io(struct ivtv *itv)
route.input = itv->card->video_inputs[inp].video_input;
route.output = 0;
- itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ v4l2_subdev_call(itv->sd_video, video, s_routing, &route);
type = itv->card->video_inputs[inp].video_type;
@@ -79,7 +79,7 @@ void ivtv_video_set_io(struct ivtv *itv)
}
if (itv->card->hw_video & IVTV_HW_GPIO)
- ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_GPIO, video, s_routing, &route);
if (itv->card->hw_video & IVTV_HW_UPD64031A) {
if (type == IVTV_CARD_INPUT_VID_TUNER ||
@@ -92,7 +92,7 @@ void ivtv_video_set_io(struct ivtv *itv)
}
route.input |= itv->card->gr_config;
- ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_UPD64031A, video, s_routing, &route);
}
if (itv->card->hw_video & IVTV_HW_UPD6408X) {
@@ -110,6 +110,6 @@ void ivtv_video_set_io(struct ivtv *itv)
route.input |= UPD64083_EXT_Y_ADC;
}
}
- ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route);
+ ivtv_call_hw(itv, IVTV_HW_UPD6408X, video, s_routing, &route);
}
}
diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c
index 9b7aa79eb..76279ed30 100644
--- a/linux/drivers/media/video/ivtv/ivtv-streams.c
+++ b/linux/drivers/media/video/ivtv/ivtv-streams.c
@@ -172,7 +172,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
{
struct ivtv_stream *s = &itv->streams[type];
int num_offset = ivtv_stream_info[type].num_offset;
- int num = itv->num + ivtv_first_minor + num_offset;
+ int num = itv->instance + ivtv_first_minor + num_offset;
/* These four fields are always initialized. If v4l2dev == NULL, then
this stream is not in use. In that case no other fields but these
@@ -205,8 +205,8 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
return -ENOMEM;
}
- snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s",
- itv->num, s->name);
+ snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "%s %s",
+ itv->device.name, s->name);
s->v4l2dev->num = num;
s->v4l2dev->parent = &itv->dev->dev;
@@ -260,6 +260,7 @@ static int ivtv_reg_dev(struct ivtv *itv, int type)
if (s_mpg->v4l2dev)
num = s_mpg->v4l2dev->num + ivtv_stream_info[type].num_offset;
}
+ video_set_drvdata(s->v4l2dev, s);
/* Register device. First try the desired minor, then any free one. */
if (video_register_device(s->v4l2dev, vfl_type, num)) {
@@ -343,7 +344,7 @@ static void ivtv_vbi_setup(struct ivtv *itv)
ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0);
/* setup VBI registers */
- itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in);
+ v4l2_subdev_call(itv->sd_video, video, s_fmt, &itv->vbi.in);
/* determine number of lines and total number of VBI bytes.
A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
@@ -577,10 +578,10 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
clear_bit(IVTV_F_I_EOS, &itv->i_flags);
/* Initialize Digitizer for Capture */
- itv->video_dec_func(itv, VIDIOC_STREAMOFF, NULL);
+ v4l2_subdev_call(itv->sd_video, video, s_stream, 0);
ivtv_msleep_timeout(300, 1);
ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0);
- itv->video_dec_func(itv, VIDIOC_STREAMON, NULL);
+ v4l2_subdev_call(itv->sd_video, video, s_stream, 1);
}
/* begin_capture */
diff --git a/linux/drivers/media/video/ivtv/ivtv-vbi.c b/linux/drivers/media/video/ivtv/ivtv-vbi.c
index 4a37a7d2e..5c5d1c462 100644
--- a/linux/drivers/media/video/ivtv/ivtv-vbi.c
+++ b/linux/drivers/media/video/ivtv/ivtv-vbi.c
@@ -21,6 +21,7 @@
#include "ivtv-i2c.h"
#include "ivtv-ioctl.h"
#include "ivtv-queue.h"
+#include "ivtv-cards.h"
#include "ivtv-vbi.h"
static void ivtv_set_vps(struct ivtv *itv, int enabled)
@@ -37,7 +38,7 @@ static void ivtv_set_vps(struct ivtv *itv, int enabled)
data.data[9] = itv->vbi.vps_payload.data[2];
data.data[10] = itv->vbi.vps_payload.data[3];
data.data[11] = itv->vbi.vps_payload.data[4];
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
}
static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc)
@@ -51,12 +52,12 @@ static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc)
data.line = (mode & 1) ? 21 : 0;
data.data[0] = cc->odd[0];
data.data[1] = cc->odd[1];
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
data.field = 1;
data.line = (mode & 2) ? 21 : 0;
data.data[0] = cc->even[0];
data.data[1] = cc->even[1];
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
}
static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
@@ -79,7 +80,7 @@ static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode)
data.line = enabled ? 23 : 0;
data.data[0] = mode & 0xff;
data.data[1] = (mode >> 8) & 0xff;
- ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data);
}
static int odd_parity(u8 c)
@@ -313,7 +314,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8
continue;
}
vbi.p = p + 4;
- itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
+ v4l2_subdev_call(itv->sd_video, video, decode_vbi_line, &vbi);
if (vbi.type && !(lines & (1 << vbi.line))) {
lines |= 1 << vbi.line;
itv->vbi.sliced_data[line].id = vbi.type;
@@ -437,7 +438,7 @@ void ivtv_vbi_work_handler(struct ivtv *itv)
data.id = V4L2_SLICED_WSS_625;
data.field = 0;
- if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) {
ivtv_set_wss(itv, 1, data.data[0] & 0xf);
vi->wss_missing_cnt = 0;
} else if (vi->wss_missing_cnt == 4) {
@@ -451,13 +452,13 @@ void ivtv_vbi_work_handler(struct ivtv *itv)
data.id = V4L2_SLICED_CAPTION_525;
data.field = 0;
- if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) {
mode |= 1;
cc.odd[0] = data.data[0];
cc.odd[1] = data.data[1];
}
data.field = 1;
- if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) {
+ if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) {
mode |= 2;
cc.even[0] = data.data[0];
cc.even[1] = data.data[1];
diff --git a/linux/drivers/media/video/ivtv/ivtvfb.c b/linux/drivers/media/video/ivtv/ivtvfb.c
index 5e3613d86..26b91d2c8 100644
--- a/linux/drivers/media/video/ivtv/ivtvfb.c
+++ b/linux/drivers/media/video/ivtv/ivtvfb.c
@@ -48,6 +48,7 @@
#endif
#include "ivtv-driver.h"
+#include "ivtv-cards.h"
#include "ivtv-i2c.h"
#include "ivtv-udma.h"
#include "ivtv-mailbox.h"
@@ -121,15 +122,15 @@ MODULE_LICENSE("GPL");
#define IVTVFB_DEBUG(x, type, fmt, args...) \
do { \
if ((x) & ivtvfb_debug) \
- printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->num , ## args); \
+ printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \
} while (0)
#define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args)
#define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args)
/* Standard kernel messages */
-#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->num , ## args)
-#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->num , ## args)
-#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->num , ## args)
+#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args)
+#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args)
+#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args)
/* --------------------------------------------------------------------- */
@@ -904,16 +905,16 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info)
switch (blank_mode) {
case FB_BLANK_UNBLANK:
ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1);
- ivtv_saa7127(itv, VIDIOC_STREAMON, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
break;
case FB_BLANK_NORMAL:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_VSYNC_SUSPEND:
ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
- ivtv_saa7127(itv, VIDIOC_STREAMON, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1);
break;
case FB_BLANK_POWERDOWN:
- ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL);
+ ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0);
ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0);
break;
}
@@ -1197,10 +1198,45 @@ 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);
+
+ 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);
+ (*(int *)p)++;
+ }
+ }
+ return 0;
+}
+
+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);
+
+ if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+ if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
+ IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n",
+ itv->instance);
+ return 0;
+ }
+ IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance);
+ ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
+ ivtvfb_release_buffers(itv);
+ itv->osd_video_pbase = 0;
+ }
+ return 0;
+}
+
static int __init ivtvfb_init(void)
{
- struct ivtv *itv;
- int i, registered = 0;
+ struct device_driver *drv;
+ int registered = 0;
+ int err;
if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) {
printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n",
@@ -1208,20 +1244,11 @@ static int __init ivtvfb_init(void)
return -EINVAL;
}
- /* Locate & initialise all cards supporting an OSD. */
- for (i = 0; i < ivtv_cards_active; i++) {
- if (ivtvfb_card_id != -1 && i != ivtvfb_card_id)
- continue;
- itv = ivtv_cards[i];
- if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
- if (ivtvfb_init_card(itv) == 0) {
- IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i);
- registered++;
- }
- }
- }
+ drv = driver_find("ivtv", &pci_bus_type);
+ err = driver_for_each_device(drv, NULL, &registered, ivtvfb_callback_init);
+ put_driver(drv);
if (!registered) {
- printk(KERN_ERR "ivtvfb: no cards found");
+ printk(KERN_ERR "ivtvfb: no cards found\n");
return -ENODEV;
}
return 0;
@@ -1229,24 +1256,14 @@ static int __init ivtvfb_init(void)
static void ivtvfb_cleanup(void)
{
- struct ivtv *itv;
- int i;
+ struct device_driver *drv;
+ int err;
printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n");
- for (i = 0; i < ivtv_cards_active; i++) {
- itv = ivtv_cards[i];
- if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) {
- if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
- IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i);
- return;
- }
- IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i);
- ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info);
- ivtvfb_release_buffers(itv);
- itv->osd_video_pbase = 0;
- }
- }
+ drv = driver_find("ivtv", &pci_bus_type);
+ err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup);
+ put_driver(drv);
}
module_init(ivtvfb_init);
diff --git a/linux/drivers/media/video/m52790.c b/linux/drivers/media/video/m52790.c
index f39da4a36..1976451e0 100644
--- a/linux/drivers/media/video/m52790.c
+++ b/linux/drivers/media/video/m52790.c
@@ -28,7 +28,7 @@
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
#include <media/m52790.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include "compat.h"
@@ -44,89 +44,130 @@ I2C_CLIENT_INSMOD;
#endif
struct m52790_state {
+ struct v4l2_subdev sd;
u16 input;
u16 output;
};
+static inline struct m52790_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct m52790_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
-static int m52790_write(struct i2c_client *client)
+static int m52790_write(struct v4l2_subdev *sd)
{
- struct m52790_state *state = i2c_get_clientdata(client);
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
u8 sw1 = (state->input | state->output) & 0xff;
u8 sw2 = (state->input | state->output) >> 8;
return i2c_smbus_write_byte_data(client, sw1, sw2);
}
-static int m52790_command(struct i2c_client *client, unsigned int cmd,
- void *arg)
+/* Note: audio and video are linked and cannot be switched separately.
+ So audio and video routing commands are identical for this chip.
+ In theory the video amplifier and audio modes could be handled
+ separately for the output, but that seems to be overkill right now.
+ The same holds for implementing an audio mute control, this is now
+ part of the audio output routing. The normal case is that another
+ chip takes care of the actual muting so making it part of the
+ output routing seems to be the right thing to do for now. */
+static int m52790_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct m52790_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
-
- /* Note: audio and video are linked and cannot be switched separately.
- So audio and video routing commands are identical for this chip.
- In theory the video amplifier and audio modes could be handled
- separately for the output, but that seems to be overkill right now.
- The same holds for implementing an audio mute control, this is now
- part of the audio output routing. The normal case is that another
- chip takes care of the actual muting so making it part of the
- output routing seems to be the right thing to do for now. */
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = state->input;
- route->output = state->output;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- case VIDIOC_INT_S_VIDEO_ROUTING:
- state->input = route->input;
- state->output = route->output;
- m52790_write(client);
- break;
+ struct m52790_state *state = to_state(sd);
+
+ state->input = route->input;
+ state->output = route->output;
+ m52790_write(sd);
+ return 0;
+}
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (reg->reg != 0)
- return -EINVAL;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = state->input | state->output;
- else {
- state->input = reg->val & 0x0303;
- state->output = reg->val & ~0x0303;
- m52790_write(client);
- }
- break;
- }
-#endif
+static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_M52790, 0);
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (reg->reg != 0)
+ return -EINVAL;
+ reg->val = state->input | state->output;
+ return 0;
+}
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Switch 1: %02x\n",
- (state->input | state->output) & 0xff);
- v4l_info(client, "Switch 2: %02x\n",
- (state->input | state->output) >> 8);
- break;
+static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct m52790_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- default:
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
return -EINVAL;
- }
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (reg->reg != 0)
+ return -EINVAL;
+ state->input = reg->val & 0x0303;
+ state->output = reg->val & ~0x0303;
+ m52790_write(sd);
return 0;
}
+#endif
+
+static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0);
+}
+
+static int m52790_log_status(struct v4l2_subdev *sd)
+{
+ struct m52790_state *state = to_state(sd);
+
+ v4l2_info(sd, "Switch 1: %02x\n",
+ (state->input | state->output) & 0xff);
+ v4l2_info(sd, "Switch 2: %02x\n",
+ (state->input | state->output) >> 8);
+ 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 = {
+ .log_status = m52790_log_status,
+ .g_chip_ident = m52790_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = m52790_g_register,
+ .s_register = m52790_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_audio_ops m52790_audio_ops = {
+ .s_routing = m52790_s_routing,
+};
+
+static const struct v4l2_subdev_video_ops m52790_video_ops = {
+ .s_routing = m52790_s_routing,
+};
+
+static const struct v4l2_subdev_ops m52790_ops = {
+ .core = &m52790_core_ops,
+ .audio = &m52790_audio_ops,
+ .video = &m52790_video_ops,
+};
/* ----------------------------------------------------------------------- */
@@ -136,6 +177,7 @@ static int m52790_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct m52790_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -152,16 +194,20 @@ static int m52790_probe(struct i2c_client *client,
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &m52790_ops);
state->input = M52790_IN_TUNER;
state->output = M52790_OUT_STEREO;
- i2c_set_clientdata(client, state);
- m52790_write(client);
+ m52790_write(sd);
return 0;
}
static int m52790_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;
}
diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c
index c6c0a584c..def15e381 100644
--- a/linux/drivers/media/video/msp3400-driver.c
+++ b/linux/drivers/media/video/msp3400-driver.c
@@ -51,18 +51,18 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-i2c-drv-legacy.h>
-#include <media/tvaudio.h>
-#include <media/msp3400.h>
#include <linux/kthread.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
#include <linux/suspend.h>
#else
#include <linux/freezer.h>
#endif
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-i2c-drv-legacy.h>
+#include <media/msp3400.h>
+#include <media/tvaudio.h>
#include "compat.h"
#include "msp3400-driver.h"
@@ -270,7 +270,7 @@ static char *scart_names[] = {
void msp_set_scart(struct i2c_client *client, int in, int out)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
state->in_scart = in;
@@ -294,7 +294,7 @@ void msp_set_scart(struct i2c_client *client, int in, int out)
void msp_set_audio(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int bal = 0, bass, treble, loudness;
int val = 0;
int reallymuted = state->muted | state->scan_in_progress;
@@ -341,7 +341,7 @@ void msp_set_audio(struct i2c_client *client)
static void msp_wake_thread(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (NULL == state->kthread)
return;
@@ -395,9 +395,9 @@ static int msp_mode_v4l1_to_v4l2(int mode)
}
#endif
-static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
@@ -438,9 +438,10 @@ static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
@@ -486,40 +487,16 @@ static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
+#ifdef CONFIG_VIDEO_ALLOW_V4L1
+static int msp_ioctl(struct v4l2_subdev *sd, int cmd, void *arg)
{
- struct msp_state *state = i2c_get_clientdata(client);
-
- if (msp_debug >= 2)
- v4l_i2c_print_ioctl(client, cmd);
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
switch (cmd) {
- case AUDC_SET_RADIO:
- if (state->radio)
- return 0;
- state->radio = 1;
- v4l_dbg(1, msp_debug, client, "switching to radio mode\n");
- state->watch_stereo = 0;
- switch (state->opmode) {
- case OPMODE_MANUAL:
- /* set msp3400 to FM radio mode */
- msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
- msp3400c_set_carrier(client, MSP_CARRIER(10.7),
- MSP_CARRIER(10.7));
- msp_set_audio(client);
- break;
- case OPMODE_AUTODETECT:
- case OPMODE_AUTOSELECT:
- /* the thread will do for us */
- msp_wake_thread(client);
- break;
- }
- break;
-
/* --- v4l ioctls --- */
/* take care: bttv does userspace copying, we'll get a
kernel pointer here... */
-#ifdef CONFIG_VIDEO_ALLOW_V4L1
case VIDIOCGAUDIO:
{
struct video_audio *va = arg;
@@ -593,105 +570,137 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
msp_wake_thread(client);
break;
}
-#endif
- case VIDIOC_S_FREQUENCY:
- {
- /* new channel -- kick audio carrier scan */
- msp_wake_thread(client);
- break;
+ default:
+ return -ENOIOCTLCMD;
}
+ return 0;
+}
+#endif
- /* --- v4l2 ioctls --- */
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- int update = state->radio || state->v4l2_std != *id;
+/* --- v4l2 ioctls --- */
+static int msp_s_radio(struct v4l2_subdev *sd)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- state->v4l2_std = *id;
- state->radio = 0;
- if (update)
- msp_wake_thread(client);
+ if (state->radio)
return 0;
+ state->radio = 1;
+ v4l_dbg(1, msp_debug, client, "switching to radio mode\n");
+ state->watch_stereo = 0;
+ switch (state->opmode) {
+ case OPMODE_MANUAL:
+ /* set msp3400 to FM radio mode */
+ msp3400c_set_mode(client, MSP_MODE_FM_RADIO);
+ msp3400c_set_carrier(client, MSP_CARRIER(10.7),
+ MSP_CARRIER(10.7));
+ msp_set_audio(client);
+ break;
+ case OPMODE_AUTODETECT:
+ case OPMODE_AUTOSELECT:
+ /* the thread will do for us */
+ msp_wake_thread(client);
+ break;
}
+ return 0;
+}
- case VIDIOC_INT_G_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
+static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- *rt = state->routing;
- break;
- }
+ /* new channel -- kick audio carrier scan */
+ msp_wake_thread(client);
+ return 0;
+}
- case VIDIOC_INT_S_AUDIO_ROUTING:
- {
- struct v4l2_routing *rt = arg;
- int tuner = (rt->input >> 3) & 1;
- int sc_in = rt->input & 0x7;
- int sc1_out = rt->output & 0xf;
- int sc2_out = (rt->output >> 4) & 0xf;
- u16 val, reg;
- int i;
- int extern_input = 1;
-
- if (state->routing.input == rt->input &&
- state->routing.output == rt->output)
- break;
- state->routing = *rt;
- /* check if the tuner input is used */
- for (i = 0; i < 5; i++) {
- if (((rt->input >> (4 + i * 4)) & 0xf) == 0)
- extern_input = 0;
- }
- state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT;
- state->rxsubchans = V4L2_TUNER_SUB_STEREO;
- msp_set_scart(client, sc_in, 0);
- msp_set_scart(client, sc1_out, 1);
- msp_set_scart(client, sc2_out, 2);
- msp_set_audmode(client);
- reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
- val = msp_read_dem(client, reg);
- msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
- /* wake thread when a new input is chosen */
+static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int update = state->radio || state->v4l2_std != id;
+
+ state->v4l2_std = id;
+ state->radio = 0;
+ if (update)
msp_wake_thread(client);
- break;
+ return 0;
+}
+
+static int msp_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int tuner = (rt->input >> 3) & 1;
+ int sc_in = rt->input & 0x7;
+ int sc1_out = rt->output & 0xf;
+ int sc2_out = (rt->output >> 4) & 0xf;
+ u16 val, reg;
+ int i;
+ int extern_input = 1;
+
+ if (state->routing.input == rt->input &&
+ state->routing.output == rt->output)
+ return 0;
+ state->routing = *rt;
+ /* check if the tuner input is used */
+ for (i = 0; i < 5; i++) {
+ if (((rt->input >> (4 + i * 4)) & 0xf) == 0)
+ extern_input = 0;
}
+ state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT;
+ state->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ msp_set_scart(client, sc_in, 0);
+ msp_set_scart(client, sc1_out, 1);
+ msp_set_scart(client, sc2_out, 2);
+ msp_set_audmode(client);
+ reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb;
+ val = msp_read_dem(client, reg);
+ msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8));
+ /* wake thread when a new input is chosen */
+ msp_wake_thread(client);
+ return 0;
+}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
+static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (state->radio)
- break;
- if (state->opmode == OPMODE_AUTOSELECT)
- msp_detect_stereo(client);
- vt->audmode = state->audmode;
- vt->rxsubchans = state->rxsubchans;
- vt->capability |= V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
- break;
- }
+ if (state->radio)
+ return 0;
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_detect_stereo(client);
+ vt->audmode = state->audmode;
+ vt->rxsubchans = state->rxsubchans;
+ vt->capability |= V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ return 0;
+}
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
+static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- if (state->radio) /* TODO: add mono/stereo support for radio */
- break;
- if (state->audmode == vt->audmode)
- break;
- state->audmode = vt->audmode;
- /* only set audmode */
- msp_set_audmode(client);
- break;
- }
+ if (state->radio) /* TODO: add mono/stereo support for radio */
+ return 0;
+ if (state->audmode == vt->audmode)
+ return 0;
+ state->audmode = vt->audmode;
+ /* only set audmode */
+ msp_set_audmode(client);
+ return 0;
+}
- case VIDIOC_INT_I2S_CLOCK_FREQ:
- {
- u32 *a = (u32 *)arg;
+static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a);
+ v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq);
- switch (*a) {
+ switch (freq) {
case 1024000:
state->i2s_mode = 0;
break;
@@ -700,24 +709,24 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
break;
default:
return -EINVAL;
- }
- break;
}
+ return 0;
+}
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ struct msp_state *state = to_state(sd);
- switch (qc->id) {
+ switch (qc->id) {
case V4L2_CID_AUDIO_VOLUME:
case V4L2_CID_AUDIO_MUTE:
return v4l2_ctrl_query_fill_std(qc);
default:
break;
- }
- if (!state->has_sound_processing)
- return -EINVAL;
- switch (qc->id) {
+ }
+ 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:
@@ -725,32 +734,38 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
return v4l2_ctrl_query_fill_std(qc);
default:
return -EINVAL;
- }
}
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return msp_get_ctrl(client, arg);
-
- case VIDIOC_S_CTRL:
- return msp_set_ctrl(client, arg);
+static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_LOG_STATUS:
- {
- const char *p;
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident,
+ (state->rev1 << 16) | state->rev2);
+}
- if (state->opmode == OPMODE_AUTOSELECT)
- msp_detect_stereo(client);
- v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
- client->name, state->rev1, state->rev2);
- v4l_info(client, "Audio: volume %d%s\n",
- state->volume, state->muted ? " (muted)" : "");
- if (state->has_sound_processing) {
- v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
- state->balance, state->bass,
- state->treble,
- state->loudness ? "on" : "off");
- }
- switch (state->mode) {
+static int msp_log_status(struct v4l2_subdev *sd)
+{
+ struct msp_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ const char *p;
+
+ if (state->opmode == OPMODE_AUTOSELECT)
+ msp_detect_stereo(client);
+ v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n",
+ client->name, state->rev1, state->rev2);
+ v4l_info(client, "Audio: volume %d%s\n",
+ state->volume, state->muted ? " (muted)" : "");
+ if (state->has_sound_processing) {
+ v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n",
+ state->balance, state->bass,
+ state->treble,
+ state->loudness ? "on" : "off");
+ }
+ switch (state->mode) {
case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break;
case MSP_MODE_FM_RADIO: p = "FM Radio"; break;
case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono/stereo"; break;
@@ -761,36 +776,25 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg)
case MSP_MODE_BTSC: p = "BTSC"; break;
case MSP_MODE_EXTERN: p = "External input"; break;
default: p = "unknown"; break;
- }
- if (state->mode == MSP_MODE_EXTERN) {
- v4l_info(client, "Mode: %s\n", p);
- } else if (state->opmode == OPMODE_MANUAL) {
- v4l_info(client, "Mode: %s (%s%s)\n", p,
+ }
+ if (state->mode == MSP_MODE_EXTERN) {
+ v4l_info(client, "Mode: %s\n", p);
+ } else if (state->opmode == OPMODE_MANUAL) {
+ v4l_info(client, "Mode: %s (%s%s)\n", p,
(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
- } else {
- if (state->opmode == OPMODE_AUTODETECT)
- v4l_info(client, "Mode: %s\n", p);
- v4l_info(client, "Standard: %s (%s%s)\n",
+ } else {
+ if (state->opmode == OPMODE_AUTODETECT)
+ v4l_info(client, "Mode: %s\n", p);
+ v4l_info(client, "Standard: %s (%s%s)\n",
msp_standard_std_name(state->std),
(state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono",
(state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : "");
- }
- v4l_info(client, "Audmode: 0x%04x\n", state->audmode);
- v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n",
- state->routing.input, state->routing.output);
- v4l_info(client, "ACB: 0x%04x\n", state->acb);
- break;
- }
-
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->ident,
- (state->rev1 << 16) | state->rev2);
-
- default:
- /* unknown */
- return -EINVAL;
}
+ v4l_info(client, "Audmode: 0x%04x\n", state->audmode);
+ v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n",
+ state->routing.input, state->routing.output);
+ v4l_info(client, "ACB: 0x%04x\n", state->acb);
return 0;
}
@@ -808,11 +812,49 @@ static int msp_resume(struct i2c_client *client)
return 0;
}
+static int msp_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 msp_core_ops = {
+ .log_status = msp_log_status,
+ .g_chip_ident = msp_g_chip_ident,
+ .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 = {
+ .s_frequency = msp_s_frequency,
+ .g_tuner = msp_g_tuner,
+ .s_tuner = msp_s_tuner,
+ .s_radio = msp_s_radio,
+ .s_std = msp_s_std,
+};
+
+static const struct v4l2_subdev_audio_ops msp_audio_ops = {
+ .s_routing = msp_s_routing,
+ .s_i2s_clock_freq = msp_s_i2s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops msp_ops = {
+ .core = &msp_core_ops,
+ .tuner = &msp_tuner_ops,
+ .audio = &msp_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct msp_state *state;
+ struct v4l2_subdev *sd;
int (*thread_func)(void *data) = NULL;
int msp_hard;
int msp_family;
@@ -836,7 +878,8 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (!state)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &msp_ops);
state->v4l2_std = V4L2_STD_NTSC;
state->audmode = V4L2_TUNER_MODE_STEREO;
@@ -990,8 +1033,9 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id)
static int msp_remove(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
+ v4l2_device_unregister_subdev(&state->sd);
/* shutdown control thread */
if (state->kthread) {
state->restart = 1;
diff --git a/linux/drivers/media/video/msp3400-driver.h b/linux/drivers/media/video/msp3400-driver.h
index ab69a290e..3fe1c1b10 100644
--- a/linux/drivers/media/video/msp3400-driver.h
+++ b/linux/drivers/media/video/msp3400-driver.h
@@ -5,6 +5,7 @@
#define MSP3400_DRIVER_H
#include <media/msp3400.h>
+#include <media/v4l2-device.h>
/* ---------------------------------------------------------------------- */
@@ -49,6 +50,7 @@ extern int msp_dolby;
extern int msp_stereo_thresh;
struct msp_state {
+ struct v4l2_subdev sd;
int rev1, rev2;
int ident;
u8 has_nicam;
@@ -96,6 +98,11 @@ struct msp_state {
unsigned int watch_stereo:1;
};
+static inline struct msp_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct msp_state, sd);
+}
+
/* msp3400-driver.c */
int msp_write_dem(struct i2c_client *client, int addr, int val);
int msp_write_dsp(struct i2c_client *client, int addr, int val);
diff --git a/linux/drivers/media/video/msp3400-kthreads.c b/linux/drivers/media/video/msp3400-kthreads.c
index 29c0d5cf6..ccba71d9f 100644
--- a/linux/drivers/media/video/msp3400-kthreads.c
+++ b/linux/drivers/media/video/msp3400-kthreads.c
@@ -162,7 +162,7 @@ const char *msp_standard_std_name(int std)
static void msp_set_source(struct i2c_client *client, u16 src)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (msp_dolby) {
msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */
@@ -189,7 +189,7 @@ void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2)
void msp3400c_set_mode(struct i2c_client *client, int mode)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode];
int tuner = (state->routing.input >> 3) & 1;
int i;
@@ -230,7 +230,7 @@ static void msp3400c_set_audmode(struct i2c_client *client)
static char *strmode[] = {
"mono", "stereo", "lang2", "lang1", "lang1+lang2"
};
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
char *modestr = (state->audmode >= 0 && state->audmode < 5) ?
strmode[state->audmode] : "unknown";
int src = 0; /* channel source: FM/AM, nicam or SCART */
@@ -359,7 +359,7 @@ static void msp3400c_set_audmode(struct i2c_client *client)
static void msp3400c_print_mode(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (state->main == state->second)
v4l_dbg(1, msp_debug, client,
@@ -388,7 +388,7 @@ static void msp3400c_print_mode(struct i2c_client *client)
static int msp3400c_detect_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int val;
int rxsubchans = state->rxsubchans;
int newnicam = state->nicam_on;
@@ -466,7 +466,7 @@ static int msp3400c_detect_stereo(struct i2c_client *client)
/* stereo/multilang monitoring */
static void watch_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (msp_detect_stereo(client))
msp_set_audmode(client);
@@ -478,7 +478,7 @@ static void watch_stereo(struct i2c_client *client)
int msp3400c_thread(void *data)
{
struct i2c_client *client = data;
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
struct msp3400c_carrier_detect *cd;
int count, max1, max2, val1, val2, val, i;
@@ -662,7 +662,7 @@ no_second:
int msp3410d_thread(void *data)
{
struct i2c_client *client = data;
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int val, i, std, count;
v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n");
@@ -828,7 +828,7 @@ restart:
static int msp34xxg_modus(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (state->radio) {
v4l_dbg(1, msp_debug, client, "selected radio modus\n");
@@ -855,7 +855,7 @@ static int msp34xxg_modus(struct i2c_client *client)
static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int source, matrix;
switch (state->audmode) {
@@ -898,7 +898,7 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in)
static void msp34xxg_set_sources(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
u32 in = state->routing.input;
msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf);
@@ -914,7 +914,7 @@ static void msp34xxg_set_sources(struct i2c_client *client)
/* (re-)initialize the msp34xxg */
static void msp34xxg_reset(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int tuner = (state->routing.input >> 3) & 1;
int modus;
@@ -957,7 +957,7 @@ static void msp34xxg_reset(struct i2c_client *client)
int msp34xxg_thread(void *data)
{
struct i2c_client *client = data;
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int val, i;
v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n");
@@ -1052,7 +1052,7 @@ unmute:
static int msp34xxg_detect_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
int status = msp_read_dem(client, 0x0200);
int is_bilingual = status & 0x100;
int is_stereo = status & 0x40;
@@ -1081,7 +1081,7 @@ static int msp34xxg_detect_stereo(struct i2c_client *client)
static void msp34xxg_set_audmode(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
if (state->std == 0x20) {
if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) &&
@@ -1098,7 +1098,7 @@ static void msp34xxg_set_audmode(struct i2c_client *client)
void msp_set_audmode(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
switch (state->opmode) {
case OPMODE_MANUAL:
@@ -1113,7 +1113,7 @@ void msp_set_audmode(struct i2c_client *client)
int msp_detect_stereo(struct i2c_client *client)
{
- struct msp_state *state = i2c_get_clientdata(client);
+ struct msp_state *state = to_state(i2c_get_clientdata(client));
switch (state->opmode) {
case OPMODE_MANUAL:
diff --git a/linux/drivers/media/video/omap24xxcam-dma.c b/linux/drivers/media/video/omap24xxcam-dma.c
new file mode 100644
index 000000000..1d54b86c9
--- /dev/null
+++ b/linux/drivers/media/video/omap24xxcam-dma.c
@@ -0,0 +1,601 @@
+/*
+ * drivers/media/video/omap24xxcam-dma.c
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com> and
+ * David Cohen <david.cohen@indt.org.br>.
+ *
+ * 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.
+ *
+ * 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/scatterlist.h>
+
+#include "omap24xxcam.h"
+
+/*
+ *
+ * DMA hardware.
+ *
+ */
+
+/* Ack all interrupt on CSR and IRQSTATUS_L0 */
+static void omap24xxcam_dmahw_ack_all(unsigned long base)
+{
+ u32 csr;
+ int i;
+
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; ++i) {
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(i));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(i), csr);
+ }
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, 0xf);
+}
+
+/* Ack dmach on CSR and IRQSTATUS_L0 */
+static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach)
+{
+ u32 csr;
+
+ csr = omap24xxcam_reg_in(base, CAMDMA_CSR(dmach));
+ /* ack interrupt in CSR */
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach), csr);
+ /* ack interrupt in IRQSTATUS */
+ omap24xxcam_reg_out(base, CAMDMA_IRQSTATUS_L0, (1 << dmach));
+
+ return csr;
+}
+
+static int omap24xxcam_dmahw_running(unsigned long base, int dmach)
+{
+ return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE;
+}
+
+static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach,
+ dma_addr_t start, u32 len)
+{
+ omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_FS
+ | CAMDMA_CCR_WR_ACTIVE
+ | CAMDMA_CCR_RD_ACTIVE
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CEN(dmach), len);
+ omap24xxcam_reg_out(base, CAMDMA_CFN(dmach), 1);
+ omap24xxcam_reg_out(base, CAMDMA_CSDP(dmach),
+ CAMDMA_CSDP_WRITE_MODE_POSTED
+ | CAMDMA_CSDP_DST_BURST_EN_32
+ | CAMDMA_CSDP_DST_PACKED
+ | CAMDMA_CSDP_SRC_BURST_EN_32
+ | CAMDMA_CSDP_SRC_PACKED
+ | CAMDMA_CSDP_DATA_TYPE_8BITS);
+ omap24xxcam_reg_out(base, CAMDMA_CSSA(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDSA(dmach), start);
+ omap24xxcam_reg_out(base, CAMDMA_CSEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSFI(dmach), DMA_THRESHOLD);
+ omap24xxcam_reg_out(base, CAMDMA_CDEI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CDFI(dmach), 0);
+ omap24xxcam_reg_out(base, CAMDMA_CSR(dmach),
+ CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR
+ | CAMDMA_CSR_BLOCK
+ | CAMDMA_CSR_DROP);
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach),
+ CAMDMA_CICR_MISALIGNED_ERR_IE
+ | CAMDMA_CICR_SECURE_ERR_IE
+ | CAMDMA_CICR_TRANS_ERR_IE
+ | CAMDMA_CICR_BLOCK_IE
+ | CAMDMA_CICR_DROP_IE);
+}
+
+static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach)
+{
+ omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
+ CAMDMA_CCR_SEL_SRC_DST_SYNC
+ | CAMDMA_CCR_BS
+ | CAMDMA_CCR_DST_AMODE_POST_INC
+ | CAMDMA_CCR_SRC_AMODE_POST_INC
+ | CAMDMA_CCR_ENABLE
+ | CAMDMA_CCR_FS
+ | CAMDMA_CCR_SYNCHRO_CAMERA);
+}
+
+static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach,
+ int free_dmach)
+{
+ int prev_dmach, ch;
+
+ if (dmach == 0)
+ prev_dmach = NUM_CAMDMA_CHANNELS - 1;
+ else
+ prev_dmach = dmach - 1;
+ omap24xxcam_reg_out(base, CAMDMA_CLNK_CTRL(prev_dmach),
+ CAMDMA_CLNK_CTRL_ENABLE_LNK | dmach);
+ /* Did we chain the DMA transfer before the previous one
+ * finished?
+ */
+ ch = (dmach + free_dmach) % NUM_CAMDMA_CHANNELS;
+ while (!(omap24xxcam_reg_in(base, CAMDMA_CCR(ch))
+ & CAMDMA_CCR_ENABLE)) {
+ if (ch == dmach) {
+ /* The previous transfer has ended and this one
+ * hasn't started, so we must not have chained
+ * to the previous one in time. We'll have to
+ * start it now.
+ */
+ omap24xxcam_dmahw_transfer_start(base, dmach);
+ break;
+ } else
+ ch = (ch + 1) % NUM_CAMDMA_CHANNELS;
+ }
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. The DMA
+ * controller may not be idle after this routine completes, because
+ * the completion routines might start new transfers.
+ */
+static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach)
+{
+ /* mask all interrupts from this channel */
+ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0);
+ /* unlink this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CLNK_CTRL(dmach), 0,
+ CAMDMA_CLNK_CTRL_ENABLE_LNK);
+ /* disable this channel */
+ omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE);
+}
+
+static void omap24xxcam_dmahw_init(unsigned long base)
+{
+ omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG,
+ CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY
+ | CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE
+ | CAMDMA_OCP_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_reg_merge(base, CAMDMA_GCR, 0x10,
+ CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH);
+
+ omap24xxcam_reg_out(base, CAMDMA_IRQENABLE_L0, 0xf);
+}
+
+/*
+ *
+ * Individual DMA channel handling.
+ *
+ */
+
+/* Start a DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully started, or non-zero if all
+ * DMA channels are already in use or starting is currently inhibited.
+ */
+static int omap24xxcam_dma_start(struct omap24xxcam_dma *dma, dma_addr_t start,
+ u32 len, dma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ int dmach;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ if (!dma->free_dmach || atomic_read(&dma->dma_stop)) {
+ spin_unlock_irqrestore(&dma->lock, flags);
+ return -EBUSY;
+ }
+
+ dmach = dma->next_dmach;
+
+ dma->ch_state[dmach].callback = callback;
+ dma->ch_state[dmach].arg = arg;
+
+ omap24xxcam_dmahw_transfer_setup(dma->base, dmach, start, len);
+
+ /* We're ready to start the DMA transfer. */
+
+ if (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ /* A transfer is already in progress, so try to chain to it. */
+ omap24xxcam_dmahw_transfer_chain(dma->base, dmach,
+ dma->free_dmach);
+ } else {
+ /* No transfer is in progress, so we'll just start this one
+ * now.
+ */
+ omap24xxcam_dmahw_transfer_start(dma->base, dmach);
+ }
+
+ dma->next_dmach = (dma->next_dmach + 1) % NUM_CAMDMA_CHANNELS;
+ dma->free_dmach--;
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+
+ return 0;
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. The DMA
+ * controller may not be idle after this routine completes, because
+ * the completion routines might start new transfers.
+ */
+static void omap24xxcam_dma_abort(struct omap24xxcam_dma *dma, u32 csr)
+{
+ unsigned long flags;
+ int dmach, i, free_dmach;
+ dma_callback_t callback;
+ void *arg;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ /* stop any DMA transfers in progress */
+ dmach = (dma->next_dmach + dma->free_dmach) % NUM_CAMDMA_CHANNELS;
+ for (i = 0; i < NUM_CAMDMA_CHANNELS; i++) {
+ omap24xxcam_dmahw_abort_ch(dma->base, dmach);
+ dmach = (dmach + 1) % NUM_CAMDMA_CHANNELS;
+ }
+
+ /* We have to be careful here because the callback routine
+ * might start a new DMA transfer, and we only want to abort
+ * transfers that were started before this routine was called.
+ */
+ free_dmach = dma->free_dmach;
+ while ((dma->free_dmach < NUM_CAMDMA_CHANNELS) &&
+ (free_dmach < NUM_CAMDMA_CHANNELS)) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ free_dmach++;
+ if (callback) {
+ /* leave interrupts disabled during callback */
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+/* Abort all chained DMA transfers. After all transfers have been
+ * aborted and the DMA controller is idle, the completion routines for
+ * any aborted transfers will be called in sequence. If the completion
+ * routines attempt to start a new DMA transfer it will fail, so the
+ * DMA controller will be idle after this routine completes.
+ */
+static void omap24xxcam_dma_stop(struct omap24xxcam_dma *dma, u32 csr)
+{
+ atomic_inc(&dma->dma_stop);
+ omap24xxcam_dma_abort(dma, csr);
+ atomic_dec(&dma->dma_stop);
+}
+
+/* Camera DMA interrupt service routine. */
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma)
+{
+ int dmach;
+ dma_callback_t callback;
+ void *arg;
+ u32 csr;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&dma->lock);
+
+ if (dma->free_dmach == NUM_CAMDMA_CHANNELS) {
+ /* A camera DMA interrupt occurred while all channels
+ * are idle, so we'll acknowledge the interrupt in the
+ * IRQSTATUS register and exit.
+ */
+ omap24xxcam_dmahw_ack_all(dma->base);
+ spin_unlock(&dma->lock);
+ return;
+ }
+
+ while (dma->free_dmach < NUM_CAMDMA_CHANNELS) {
+ dmach = (dma->next_dmach + dma->free_dmach)
+ % NUM_CAMDMA_CHANNELS;
+ if (omap24xxcam_dmahw_running(dma->base, dmach)) {
+ /* This buffer hasn't finished yet, so we're done. */
+ break;
+ }
+ csr = omap24xxcam_dmahw_ack_ch(dma->base, dmach);
+ if (csr & csr_error) {
+ /* A DMA error occurred, so stop all DMA
+ * transfers in progress.
+ */
+ spin_unlock(&dma->lock);
+ omap24xxcam_dma_stop(dma, csr);
+ return;
+ } else {
+ callback = dma->ch_state[dmach].callback;
+ arg = dma->ch_state[dmach].arg;
+ dma->free_dmach++;
+ if (callback) {
+ spin_unlock(&dma->lock);
+ (*callback) (dma, csr, arg);
+ spin_lock(&dma->lock);
+ }
+ }
+ }
+
+ spin_unlock(&dma->lock);
+
+ omap24xxcam_sgdma_process(
+ container_of(dma, struct omap24xxcam_sgdma, dma));
+}
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ omap24xxcam_dmahw_init(dma->base);
+
+ spin_unlock_irqrestore(&dma->lock, flags);
+}
+
+static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma,
+ unsigned long base)
+{
+ int ch;
+
+ /* group all channels on DMA IRQ0 and unmask irq */
+ spin_lock_init(&dma->lock);
+ dma->base = base;
+ dma->free_dmach = NUM_CAMDMA_CHANNELS;
+ dma->next_dmach = 0;
+ for (ch = 0; ch < NUM_CAMDMA_CHANNELS; ch++) {
+ dma->ch_state[ch].callback = NULL;
+ dma->ch_state[ch].arg = NULL;
+ }
+}
+
+/*
+ *
+ * Scatter-gather DMA.
+ *
+ * High-level DMA construct for transferring whole picture frames to
+ * memory that is discontinuous.
+ *
+ */
+
+/* DMA completion routine for the scatter-gather DMA fragments. */
+static void omap24xxcam_sgdma_callback(struct omap24xxcam_dma *dma, u32 csr,
+ void *arg)
+{
+ struct omap24xxcam_sgdma *sgdma =
+ container_of(dma, struct omap24xxcam_sgdma, dma);
+ int sgslot = (int)arg;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock(&sgdma->lock);
+
+ /* We got an interrupt, we can remove the timer */
+ del_timer(&sgdma->reset_timer);
+
+ sg_state = sgdma->sg_state + sgslot;
+ if (!sg_state->queued_sglist) {
+ spin_unlock(&sgdma->lock);
+ printk(KERN_ERR "%s: sgdma completed when none queued!\n",
+ __func__);
+ return;
+ }
+
+ sg_state->csr |= csr;
+ if (!--sg_state->queued_sglist) {
+ /* Queue for this sglist is empty, so check to see if we're
+ * done.
+ */
+ if ((sg_state->next_sglist == sg_state->sglen)
+ || (sg_state->csr & csr_error)) {
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ u32 sg_csr = sg_state->csr;
+ /* All done with this sglist */
+ sgdma->free_sgdma++;
+ if (callback) {
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, sg_csr, arg);
+ return;
+ }
+ }
+ }
+
+ spin_unlock(&sgdma->lock);
+}
+
+/* Start queued scatter-gather DMA transfers. */
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int queued_sgdma, sgslot;
+ struct sgdma_state *sg_state;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ queued_sgdma = NUM_SG_DMA - sgdma->free_sgdma;
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ while (queued_sgdma > 0) {
+ sg_state = sgdma->sg_state + sgslot;
+ while ((sg_state->next_sglist < sg_state->sglen) &&
+ !(sg_state->csr & csr_error)) {
+ const struct scatterlist *sglist;
+ unsigned int len;
+
+ sglist = sg_state->sglist + sg_state->next_sglist;
+ /* try to start the next DMA transfer */
+ if (sg_state->next_sglist + 1 == sg_state->sglen) {
+ /*
+ * On the last sg, we handle the case where
+ * cam->img.pix.sizeimage % PAGE_ALIGN != 0
+ */
+ len = sg_state->len - sg_state->bytes_read;
+ } else {
+ len = sg_dma_len(sglist);
+ }
+
+ if (omap24xxcam_dma_start(&sgdma->dma,
+ sg_dma_address(sglist),
+ len,
+ omap24xxcam_sgdma_callback,
+ (void *)sgslot)) {
+ /* DMA start failed */
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return;
+ } else {
+ unsigned long expires;
+ /* DMA start was successful */
+ sg_state->next_sglist++;
+ sg_state->bytes_read += len;
+ sg_state->queued_sglist++;
+
+ /* We start the reset timer */
+ expires = jiffies + HZ;
+ mod_timer(&sgdma->reset_timer, expires);
+ }
+ }
+ queued_sgdma--;
+ sgslot = (sgslot + 1) % NUM_SG_DMA;
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+/*
+ * Queue a scatter-gather DMA transfer from the camera to memory.
+ * Returns zero if the transfer was successfully queued, or non-zero
+ * if all of the scatter-gather slots are already in use.
+ */
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg)
+{
+ unsigned long flags;
+ struct sgdma_state *sg_state;
+
+ if ((sglen < 0) || ((sglen > 0) & !sglist))
+ return -EINVAL;
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (!sgdma->free_sgdma) {
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+ return -EBUSY;
+ }
+
+ sg_state = sgdma->sg_state + sgdma->next_sgdma;
+
+ sg_state->sglist = sglist;
+ sg_state->sglen = sglen;
+ sg_state->next_sglist = 0;
+ sg_state->bytes_read = 0;
+ sg_state->len = len;
+ sg_state->queued_sglist = 0;
+ sg_state->csr = 0;
+ sg_state->callback = callback;
+ sg_state->arg = arg;
+
+ sgdma->next_sgdma = (sgdma->next_sgdma + 1) % NUM_SG_DMA;
+ sgdma->free_sgdma--;
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+
+ omap24xxcam_sgdma_process(sgdma);
+
+ return 0;
+}
+
+/* Sync scatter-gather DMA by aborting any DMA transfers currently in progress.
+ * Any queued scatter-gather DMA transactions that have not yet been started
+ * will remain queued. The DMA controller will be idle after this routine
+ * completes. When the scatter-gather queue is restarted, the next
+ * scatter-gather DMA transfer will begin at the start of a new transaction.
+ */
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma)
+{
+ unsigned long flags;
+ int sgslot;
+ struct sgdma_state *sg_state;
+ u32 csr = CAMDMA_CSR_TRANS_ERR;
+
+ /* stop any DMA transfers in progress */
+ omap24xxcam_dma_stop(&sgdma->dma, csr);
+
+ spin_lock_irqsave(&sgdma->lock, flags);
+
+ if (sgdma->free_sgdma < NUM_SG_DMA) {
+ sgslot = (sgdma->next_sgdma + sgdma->free_sgdma) % NUM_SG_DMA;
+ sg_state = sgdma->sg_state + sgslot;
+ if (sg_state->next_sglist != 0) {
+ /* This DMA transfer was in progress, so abort it. */
+ sgdma_callback_t callback = sg_state->callback;
+ void *arg = sg_state->arg;
+ sgdma->free_sgdma++;
+ if (callback) {
+ /* leave interrupts masked */
+ spin_unlock(&sgdma->lock);
+ (*callback) (sgdma, csr, arg);
+ spin_lock(&sgdma->lock);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&sgdma->lock, flags);
+}
+
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ unsigned long base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data)
+{
+ int sg;
+
+ spin_lock_init(&sgdma->lock);
+ sgdma->free_sgdma = NUM_SG_DMA;
+ sgdma->next_sgdma = 0;
+ for (sg = 0; sg < NUM_SG_DMA; sg++) {
+ sgdma->sg_state[sg].sglen = 0;
+ sgdma->sg_state[sg].next_sglist = 0;
+ sgdma->sg_state[sg].bytes_read = 0;
+ sgdma->sg_state[sg].queued_sglist = 0;
+ sgdma->sg_state[sg].csr = 0;
+ sgdma->sg_state[sg].callback = NULL;
+ sgdma->sg_state[sg].arg = NULL;
+ }
+
+ omap24xxcam_dma_init(&sgdma->dma, base);
+ setup_timer(&sgdma->reset_timer, reset_callback, reset_callback_data);
+}
diff --git a/linux/drivers/media/video/omap24xxcam.c b/linux/drivers/media/video/omap24xxcam.c
new file mode 100644
index 000000000..85c3c7c92
--- /dev/null
+++ b/linux/drivers/media/video/omap24xxcam.c
@@ -0,0 +1,1908 @@
+/*
+ * drivers/media/video/omap24xxcam.c
+ *
+ * OMAP 2 camera block driver.
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007-2008 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com>
+ *
+ * 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.
+ *
+ * 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/videodev2.h>
+#include <linux/pci.h> /* needed for videobufs */
+#include <linux/version.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+
+#include "omap24xxcam.h"
+
+#define OMAP24XXCAM_VERSION KERNEL_VERSION(0, 0, 0)
+
+#define RESET_TIMEOUT_NS 10000
+
+static void omap24xxcam_reset(struct omap24xxcam_device *cam);
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam);
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s);
+static int omap24xxcam_remove(struct platform_device *pdev);
+
+/* module parameters */
+static int video_nr = -1; /* video device minor (-1 ==> auto assign) */
+/*
+ * Maximum amount of memory to use for capture buffers.
+ * Default is 4800KB, enough to double-buffer SXGA.
+ */
+static int capture_mem = 1280 * 960 * 2 * 2;
+
+static struct v4l2_int_device omap24xxcam;
+
+/*
+ *
+ * Clocks.
+ *
+ */
+
+static void omap24xxcam_clock_put(struct omap24xxcam_device *cam)
+{
+ if (cam->ick != NULL && !IS_ERR(cam->ick))
+ clk_put(cam->ick);
+ if (cam->fck != NULL && !IS_ERR(cam->fck))
+ clk_put(cam->fck);
+
+ cam->ick = cam->fck = NULL;
+}
+
+static int omap24xxcam_clock_get(struct omap24xxcam_device *cam)
+{
+ int rval = 0;
+
+ cam->fck = clk_get(cam->dev, "cam_fck");
+ if (IS_ERR(cam->fck)) {
+ dev_err(cam->dev, "can't get cam_fck");
+ rval = PTR_ERR(cam->fck);
+ omap24xxcam_clock_put(cam);
+ return rval;
+ }
+
+ cam->ick = clk_get(cam->dev, "cam_ick");
+ if (IS_ERR(cam->ick)) {
+ dev_err(cam->dev, "can't get cam_ick");
+ rval = PTR_ERR(cam->ick);
+ omap24xxcam_clock_put(cam);
+ }
+
+ return rval;
+}
+
+static void omap24xxcam_clock_on(struct omap24xxcam_device *cam)
+{
+ clk_enable(cam->fck);
+ clk_enable(cam->ick);
+}
+
+static void omap24xxcam_clock_off(struct omap24xxcam_device *cam)
+{
+ clk_disable(cam->fck);
+ clk_disable(cam->ick);
+}
+
+/*
+ *
+ * Camera core
+ *
+ */
+
+/*
+ * Set xclk.
+ *
+ * To disable xclk, use value zero.
+ */
+static void omap24xxcam_core_xclk_set(const struct omap24xxcam_device *cam,
+ u32 xclk)
+{
+ if (xclk) {
+ u32 divisor = CAM_MCLK / xclk;
+
+ if (divisor == 1)
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK,
+ CC_CTRL_XCLK_DIV_BYPASS);
+ else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, divisor);
+ } else
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET,
+ CC_CTRL_XCLK, CC_CTRL_XCLK_DIV_STABLE_LOW);
+}
+
+static void omap24xxcam_core_hwinit(const struct omap24xxcam_device *cam)
+{
+ /*
+ * Setting the camera core AUTOIDLE bit causes problems with frame
+ * synchronization, so we will clear the AUTOIDLE bit instead.
+ */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_SYSCONFIG,
+ CC_SYSCONFIG_AUTOIDLE);
+
+ /* program the camera interface DMA packet size */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL_DMA,
+ CC_CTRL_DMA_EN | (DMA_THRESHOLD / 4 - 1));
+
+ /* enable camera core error interrupts */
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQENABLE,
+ CC_IRQENABLE_FW_ERR_IRQ
+ | CC_IRQENABLE_FSC_ERR_IRQ
+ | CC_IRQENABLE_SSC_ERR_IRQ
+ | CC_IRQENABLE_FIFO_OF_IRQ);
+}
+
+/*
+ * Enable the camera core.
+ *
+ * Data transfer to the camera DMA starts from next starting frame.
+ */
+static void omap24xxcam_core_enable(const struct omap24xxcam_device *cam)
+{
+
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ cam->cc_ctrl);
+}
+
+/*
+ * Disable camera core.
+ *
+ * The data transfer will be stopped immediately (CC_CTRL_CC_RST). The
+ * core internal state machines will be reset. Use
+ * CC_CTRL_CC_FRAME_TRIG instead if you want to transfer the current
+ * frame completely.
+ */
+static void omap24xxcam_core_disable(const struct omap24xxcam_device *cam)
+{
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_CTRL,
+ CC_CTRL_CC_RST);
+}
+
+/* Interrupt service routine for camera core interrupts. */
+static void omap24xxcam_core_isr(struct omap24xxcam_device *cam)
+{
+ u32 cc_irqstatus;
+ const u32 cc_irqstatus_err =
+ CC_IRQSTATUS_FW_ERR_IRQ
+ | CC_IRQSTATUS_FSC_ERR_IRQ
+ | CC_IRQSTATUS_SSC_ERR_IRQ
+ | CC_IRQSTATUS_FIFO_UF_IRQ
+ | CC_IRQSTATUS_FIFO_OF_IRQ;
+
+ cc_irqstatus = omap24xxcam_reg_in(cam->mmio_base + CC_REG_OFFSET,
+ CC_IRQSTATUS);
+ omap24xxcam_reg_out(cam->mmio_base + CC_REG_OFFSET, CC_IRQSTATUS,
+ cc_irqstatus);
+
+ if (cc_irqstatus & cc_irqstatus_err
+ && !atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, cc_irqstatus 0x%x\n",
+ cc_irqstatus);
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ *
+ * videobuf_buffer handling.
+ *
+ * Memory for mmapped videobuf_buffers is not allocated
+ * conventionally, but by several kmalloc allocations and then
+ * creating the scatterlist on our own. User-space buffers are handled
+ * normally.
+ *
+ */
+
+/*
+ * Free the memory-mapped buffer memory allocated for a
+ * videobuf_buffer and the associated scatterlist.
+ */
+static void omap24xxcam_vbq_free_mmap_buffer(struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+ size_t alloc_size;
+ struct page *page;
+ int i;
+
+ if (dma->sglist == NULL)
+ return;
+
+ i = dma->sglen;
+ while (i) {
+ i--;
+ alloc_size = sg_dma_len(&dma->sglist[i]);
+ page = sg_page(&dma->sglist[i]);
+ do {
+ ClearPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ __free_pages(sg_page(&dma->sglist[i]),
+ get_order(sg_dma_len(&dma->sglist[i])));
+ }
+
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+}
+
+/* Release all memory related to the videobuf_queue. */
+static void omap24xxcam_vbq_free_mmap_buffers(struct videobuf_queue *vbq)
+{
+ int i;
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+ if (NULL == vbq->bufs[i])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[i]->memory)
+ continue;
+ vbq->ops->buf_release(vbq, vbq->bufs[i]);
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ kfree(vbq->bufs[i]);
+ vbq->bufs[i] = NULL;
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ videobuf_mmap_free(vbq);
+}
+
+/*
+ * Allocate physically as contiguous as possible buffer for video
+ * frame and allocate and build DMA scatter-gather list for it.
+ */
+static int omap24xxcam_vbq_alloc_mmap_buffer(struct videobuf_buffer *vb)
+{
+ unsigned int order;
+ size_t alloc_size, size = vb->bsize; /* vb->bsize is page aligned */
+ struct page *page;
+ int max_pages, err = 0, i = 0;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /*
+ * allocate maximum size scatter-gather list. Note this is
+ * overhead. We may not use as many entries as we allocate
+ */
+ max_pages = vb->bsize >> PAGE_SHIFT;
+ dma->sglist = kcalloc(max_pages, sizeof(*dma->sglist), GFP_KERNEL);
+ if (dma->sglist == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ while (size) {
+ order = get_order(size);
+ /*
+ * do not over-allocate even if we would get larger
+ * contiguous chunk that way
+ */
+ if ((PAGE_SIZE << order) > size)
+ order--;
+
+ /* try to allocate as many contiguous pages as possible */
+ page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+ /* if allocation fails, try to allocate smaller amount */
+ while (page == NULL) {
+ order--;
+ page = alloc_pages(GFP_KERNEL | GFP_DMA, order);
+ if (page == NULL && !order) {
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+ size -= (PAGE_SIZE << order);
+
+ /* append allocated chunk of pages into scatter-gather list */
+ sg_set_page(&dma->sglist[i], page, PAGE_SIZE << order, 0);
+ dma->sglen++;
+ i++;
+
+ alloc_size = (PAGE_SIZE << order);
+
+ /* clear pages before giving them to user space */
+ memset(page_address(page), 0, alloc_size);
+
+ /* mark allocated pages reserved */
+ do {
+ SetPageReserved(page++);
+ } while (alloc_size -= PAGE_SIZE);
+ }
+ /*
+ * REVISIT: not fully correct to assign nr_pages == sglen but
+ * video-buf is passing nr_pages for e.g. unmap_sg calls
+ */
+ dma->nr_pages = dma->sglen;
+ dma->direction = PCI_DMA_FROMDEVICE;
+
+ return 0;
+
+out:
+ omap24xxcam_vbq_free_mmap_buffer(vb);
+ return err;
+}
+
+static int omap24xxcam_vbq_alloc_mmap_buffers(struct videobuf_queue *vbq,
+ unsigned int count)
+{
+ int i, err = 0;
+ struct omap24xxcam_fh *fh =
+ container_of(vbq, struct omap24xxcam_fh, vbq);
+
+ mutex_lock(&vbq->vb_lock);
+
+ for (i = 0; i < count; i++) {
+ err = omap24xxcam_vbq_alloc_mmap_buffer(vbq->bufs[i]);
+ if (err)
+ goto out;
+ dev_dbg(fh->cam->dev, "sglen is %d for buffer %d\n",
+ videobuf_to_dma(vbq->bufs[i])->sglen, i);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return 0;
+out:
+ while (i) {
+ i--;
+ omap24xxcam_vbq_free_mmap_buffer(vbq->bufs[i]);
+ }
+
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+/*
+ * This routine is called from interrupt context when a scatter-gather DMA
+ * transfer of a videobuf_buffer completes.
+ */
+static void omap24xxcam_vbq_complete(struct omap24xxcam_sgdma *sgdma,
+ u32 csr, void *arg)
+{
+ struct omap24xxcam_device *cam =
+ container_of(sgdma, struct omap24xxcam_device, sgdma);
+ struct omap24xxcam_fh *fh = cam->streaming->private_data;
+ struct videobuf_buffer *vb = (struct videobuf_buffer *)arg;
+ const u32 csr_error = CAMDMA_CSR_MISALIGNED_ERR
+ | CAMDMA_CSR_SUPERVISOR_ERR | CAMDMA_CSR_SECURE_ERR
+ | CAMDMA_CSR_TRANS_ERR | CAMDMA_CSR_DROP;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (--cam->sgdma_in_queue == 0)
+ omap24xxcam_core_disable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ do_gettimeofday(&vb->ts);
+ vb->field_count = atomic_add_return(2, &fh->field_count);
+ if (csr & csr_error) {
+ vb->state = VIDEOBUF_ERROR;
+ if (!atomic_read(&fh->cam->in_reset)) {
+ dev_dbg(cam->dev, "resetting camera, csr 0x%x\n", csr);
+ omap24xxcam_reset(cam);
+ }
+ } else
+ vb->state = VIDEOBUF_DONE;
+ wake_up(&vb->done);
+}
+
+static void omap24xxcam_vbq_release(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
+
+ /* wait for buffer, especially to get out of the sgdma queue */
+ videobuf_waiton(vb, 0, 0);
+ if (vb->memory == V4L2_MEMORY_MMAP) {
+ dma_unmap_sg(vbq->dev, dma->sglist, dma->sglen,
+ dma->direction);
+ dma->direction = DMA_NONE;
+ } else {
+ videobuf_dma_unmap(vbq, videobuf_to_dma(vb));
+ videobuf_dma_free(videobuf_to_dma(vb));
+ }
+
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+/*
+ * Limit the number of available kernel image capture buffers based on the
+ * number requested, the currently selected image size, and the maximum
+ * amount of memory permitted for kernel capture buffers.
+ */
+static int omap24xxcam_vbq_setup(struct videobuf_queue *vbq, unsigned int *cnt,
+ unsigned int *size)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+
+ if (*cnt <= 0)
+ *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */
+
+ if (*cnt > VIDEO_MAX_FRAME)
+ *cnt = VIDEO_MAX_FRAME;
+
+ *size = fh->pix.sizeimage;
+
+ /* accessing fh->cam->capture_mem is ok, it's constant */
+ while (*size * *cnt > fh->cam->capture_mem)
+ (*cnt)--;
+
+ return 0;
+}
+
+static int omap24xxcam_dma_iolock(struct videobuf_queue *vbq,
+ struct videobuf_dmabuf *dma)
+{
+ int err = 0;
+
+ dma->direction = PCI_DMA_FROMDEVICE;
+ if (!dma_map_sg(vbq->dev, dma->sglist, dma->sglen, dma->direction)) {
+ kfree(dma->sglist);
+ dma->sglist = NULL;
+ dma->sglen = 0;
+ err = -EIO;
+ }
+
+ return err;
+}
+
+static int omap24xxcam_vbq_prepare(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ int err = 0;
+
+ /*
+ * Accessing pix here is okay since it's constant while
+ * streaming is on (and we only get called then).
+ */
+ if (vb->baddr) {
+ /* This is a userspace buffer. */
+ if (fh->pix.sizeimage > vb->bsize) {
+ /* The buffer isn't big enough. */
+ err = -EINVAL;
+ } else
+ vb->size = fh->pix.sizeimage;
+ } else {
+ if (vb->state != VIDEOBUF_NEEDS_INIT) {
+ /*
+ * We have a kernel bounce buffer that has
+ * already been allocated.
+ */
+ if (fh->pix.sizeimage > vb->size) {
+ /*
+ * The image size has been changed to
+ * a larger size since this buffer was
+ * allocated, so we need to free and
+ * reallocate it.
+ */
+ omap24xxcam_vbq_release(vbq, vb);
+ vb->size = fh->pix.sizeimage;
+ }
+ } else {
+ /* We need to allocate a new kernel bounce buffer. */
+ vb->size = fh->pix.sizeimage;
+ }
+ }
+
+ if (err)
+ return err;
+
+ vb->width = fh->pix.width;
+ vb->height = fh->pix.height;
+ vb->field = field;
+
+ if (vb->state == VIDEOBUF_NEEDS_INIT) {
+ if (vb->memory == V4L2_MEMORY_MMAP)
+ /*
+ * we have built the scatter-gather list by ourself so
+ * do the scatter-gather mapping as well
+ */
+ err = omap24xxcam_dma_iolock(vbq, videobuf_to_dma(vb));
+ else
+ err = videobuf_iolock(vbq, vb, NULL);
+ }
+
+ if (!err)
+ vb->state = VIDEOBUF_PREPARED;
+ else
+ omap24xxcam_vbq_release(vbq, vb);
+
+ return err;
+}
+
+static void omap24xxcam_vbq_queue(struct videobuf_queue *vbq,
+ struct videobuf_buffer *vb)
+{
+ struct omap24xxcam_fh *fh = vbq->priv_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ enum videobuf_state state = vb->state;
+ unsigned long flags;
+ int err;
+
+ /*
+ * FIXME: We're marking the buffer active since we have no
+ * pretty way of marking it active exactly when the
+ * scatter-gather transfer starts.
+ */
+ vb->state = VIDEOBUF_ACTIVE;
+
+ err = omap24xxcam_sgdma_queue(&fh->cam->sgdma,
+ videobuf_to_dma(vb)->sglist,
+ videobuf_to_dma(vb)->sglen, vb->size,
+ omap24xxcam_vbq_complete, vb);
+
+ if (!err) {
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+ if (++cam->sgdma_in_queue == 1
+ && !atomic_read(&cam->in_reset))
+ omap24xxcam_core_enable(cam);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ } else {
+ /*
+ * Oops. We're not supposed to get any errors here.
+ * The only way we could get an error is if we ran out
+ * of scatter-gather DMA slots, but we are supposed to
+ * have at least as many scatter-gather DMA slots as
+ * video buffers so that can't happen.
+ */
+ dev_err(cam->dev, "failed to queue a video buffer for dma!\n");
+ dev_err(cam->dev, "likely a bug in the driver!\n");
+ vb->state = state;
+ }
+}
+
+static struct videobuf_queue_ops omap24xxcam_vbq_ops = {
+ .buf_setup = omap24xxcam_vbq_setup,
+ .buf_prepare = omap24xxcam_vbq_prepare,
+ .buf_queue = omap24xxcam_vbq_queue,
+ .buf_release = omap24xxcam_vbq_release,
+};
+
+/*
+ *
+ * OMAP main camera system
+ *
+ */
+
+/*
+ * Reset camera block to power-on state.
+ */
+static void omap24xxcam_poweron_reset(struct omap24xxcam_device *cam)
+{
+ int max_loop = RESET_TIMEOUT_NS;
+
+ /* Reset whole camera subsystem */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAM_SYSCONFIG,
+ CAM_SYSCONFIG_SOFTRESET);
+
+ /* Wait till it's finished */
+ while (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE)
+ && --max_loop) {
+ ndelay(1);
+ }
+
+ if (!(omap24xxcam_reg_in(cam->mmio_base, CAM_SYSSTATUS)
+ & CAM_SYSSTATUS_RESETDONE))
+ dev_err(cam->dev, "camera soft reset timeout\n");
+}
+
+/*
+ * (Re)initialise the camera block.
+ */
+static void omap24xxcam_hwinit(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_poweron_reset(cam);
+
+ /* set the camera subsystem autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base, CAM_SYSCONFIG,
+ CAM_SYSCONFIG_AUTOIDLE);
+
+ /* set the camera MMU autoidle bit */
+ omap24xxcam_reg_out(cam->mmio_base,
+ CAMMMU_REG_OFFSET + CAMMMU_SYSCONFIG,
+ CAMMMU_SYSCONFIG_AUTOIDLE);
+
+ omap24xxcam_core_hwinit(cam);
+
+ omap24xxcam_dma_hwinit(&cam->sgdma.dma);
+}
+
+/*
+ * Callback for dma transfer stalling.
+ */
+static void omap24xxcam_stalled_dma_reset(unsigned long data)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)data;
+
+ if (!atomic_read(&cam->in_reset)) {
+ dev_dbg(cam->dev, "dma stalled, resetting camera\n");
+ omap24xxcam_reset(cam);
+ }
+}
+
+/*
+ * Stop capture. Mark we're doing a reset, stop DMA transfers and
+ * core. (No new scatter-gather transfers will be queued whilst
+ * in_reset is non-zero.)
+ *
+ * If omap24xxcam_capture_stop is called from several places at
+ * once, only the first call will have an effect. Similarly, the last
+ * call omap24xxcam_streaming_cont will have effect.
+ *
+ * Serialisation is ensured by using cam->core_enable_disable_lock.
+ */
+static void omap24xxcam_capture_stop(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_inc_return(&cam->in_reset) != 1) {
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+ return;
+ }
+
+ omap24xxcam_core_disable(cam);
+
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+
+ omap24xxcam_sgdma_sync(&cam->sgdma);
+}
+
+/*
+ * Reset and continue streaming.
+ *
+ * Note: Resetting the camera FIFO via the CC_RST bit in the CC_CTRL
+ * register is supposed to be sufficient to recover from a camera
+ * interface error, but it doesn't seem to be enough. If we only do
+ * that then subsequent image captures are out of sync by either one
+ * or two times DMA_THRESHOLD bytes. Resetting and re-initializing the
+ * entire camera subsystem prevents the problem with frame
+ * synchronization.
+ */
+static void omap24xxcam_capture_cont(struct omap24xxcam_device *cam)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cam->core_enable_disable_lock, flags);
+
+ if (atomic_read(&cam->in_reset) != 1)
+ goto out;
+
+ omap24xxcam_hwinit(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ omap24xxcam_sgdma_process(&cam->sgdma);
+
+ if (cam->sgdma_in_queue)
+ omap24xxcam_core_enable(cam);
+
+out:
+ atomic_dec(&cam->in_reset);
+ spin_unlock_irqrestore(&cam->core_enable_disable_lock, flags);
+}
+
+static ssize_t
+omap24xxcam_streaming_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct omap24xxcam_device *cam = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", cam->streaming ? "active" : "inactive");
+}
+static DEVICE_ATTR(streaming, S_IRUGO, omap24xxcam_streaming_show, NULL);
+
+/*
+ * Stop capture and restart it. I.e. reset the camera during use.
+ */
+static void omap24xxcam_reset(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_capture_stop(cam);
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ * The main interrupt handler.
+ */
+static irqreturn_t omap24xxcam_isr(int irq, void *arg)
+{
+ struct omap24xxcam_device *cam = (struct omap24xxcam_device *)arg;
+ u32 irqstatus;
+ unsigned int irqhandled = 0;
+
+ irqstatus = omap24xxcam_reg_in(cam->mmio_base, CAM_IRQSTATUS);
+
+ if (irqstatus &
+ (CAM_IRQSTATUS_DMA_IRQ2 | CAM_IRQSTATUS_DMA_IRQ1
+ | CAM_IRQSTATUS_DMA_IRQ0)) {
+ omap24xxcam_dma_isr(&cam->sgdma.dma);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_CC_IRQ) {
+ omap24xxcam_core_isr(cam);
+ irqhandled = 1;
+ }
+ if (irqstatus & CAM_IRQSTATUS_MMU_IRQ)
+ dev_err(cam->dev, "unhandled camera MMU interrupt!\n");
+
+ return IRQ_RETVAL(irqhandled);
+}
+
+/*
+ *
+ * Sensor handling.
+ *
+ */
+
+/*
+ * Enable the external sensor interface. Try to negotiate interface
+ * parameters with the sensor and start using the new ones. The calls
+ * to sensor_if_enable and sensor_if_disable need not to be balanced.
+ */
+static int omap24xxcam_sensor_if_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+ struct v4l2_ifparm p;
+
+ rval = vidioc_int_g_ifparm(cam->sdev, &p);
+ if (rval) {
+ dev_err(cam->dev, "vidioc_int_g_ifparm failed with %d\n", rval);
+ return rval;
+ }
+
+ cam->if_type = p.if_type;
+
+ cam->cc_ctrl = CC_CTRL_CC_EN;
+
+ switch (p.if_type) {
+ case V4L2_IF_TYPE_BT656:
+ if (p.u.bt656.frame_start_on_rising_vs)
+ cam->cc_ctrl |= CC_CTRL_NOBT_SYNCHRO;
+ if (p.u.bt656.bt_sync_correct)
+ cam->cc_ctrl |= CC_CTRL_BT_CORRECT;
+ if (p.u.bt656.swap)
+ cam->cc_ctrl |= CC_CTRL_PAR_ORDERCAM;
+ if (p.u.bt656.latch_clk_inv)
+ cam->cc_ctrl |= CC_CTRL_PAR_CLK_POL;
+ if (p.u.bt656.nobt_hs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_HS_POL;
+ if (p.u.bt656.nobt_vs_inv)
+ cam->cc_ctrl |= CC_CTRL_NOBT_VS_POL;
+
+ switch (p.u.bt656.mode) {
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT10;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_NOBT_12BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_NOBT12;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_8BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT8;
+ break;
+ case V4L2_IF_TYPE_BT656_MODE_BT_10BIT:
+ cam->cc_ctrl |= CC_CTRL_PAR_MODE_BT10;
+ break;
+ default:
+ dev_err(cam->dev,
+ "bt656 interface mode %d not supported\n",
+ p.u.bt656.mode);
+ return -EINVAL;
+ }
+ /*
+ * The clock rate that the sensor wants has changed.
+ * We have to adjust the xclk from OMAP 2 side to
+ * match the sensor's wish as closely as possible.
+ */
+ if (p.u.bt656.clock_curr != cam->if_u.bt656.xclk) {
+ u32 xclk = p.u.bt656.clock_curr;
+ u32 divisor;
+
+ if (xclk == 0)
+ return -EINVAL;
+
+ if (xclk > CAM_MCLK)
+ xclk = CAM_MCLK;
+
+ divisor = CAM_MCLK / xclk;
+ if (divisor * xclk < CAM_MCLK)
+ divisor++;
+ if (CAM_MCLK / divisor < p.u.bt656.clock_min
+ && divisor > 1)
+ divisor--;
+ if (divisor > 30)
+ divisor = 30;
+
+ xclk = CAM_MCLK / divisor;
+
+ if (xclk < p.u.bt656.clock_min
+ || xclk > p.u.bt656.clock_max)
+ return -EINVAL;
+
+ cam->if_u.bt656.xclk = xclk;
+ }
+ omap24xxcam_core_xclk_set(cam, cam->if_u.bt656.xclk);
+ break;
+ default:
+ /* FIXME: how about other interfaces? */
+ dev_err(cam->dev, "interface type %d not supported\n",
+ p.if_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void omap24xxcam_sensor_if_disable(const struct omap24xxcam_device *cam)
+{
+ switch (cam->if_type) {
+ case V4L2_IF_TYPE_BT656:
+ omap24xxcam_core_xclk_set(cam, 0);
+ break;
+ }
+}
+
+/*
+ * Initialise the sensor hardware.
+ */
+static int omap24xxcam_sensor_init(struct omap24xxcam_device *cam)
+{
+ int err = 0;
+ struct v4l2_int_device *sdev = cam->sdev;
+
+ omap24xxcam_clock_on(cam);
+ err = omap24xxcam_sensor_if_enable(cam);
+ if (err) {
+ dev_err(cam->dev, "sensor interface could not be enabled at "
+ "initialisation, %d\n", err);
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ /* power up sensor during sensor initialization */
+ vidioc_int_s_power(sdev, 1);
+
+ err = vidioc_int_dev_init(sdev);
+ if (err) {
+ dev_err(cam->dev, "cannot initialize sensor, error %d\n", err);
+ /* Sensor init failed --- it's nonexistent to us! */
+ cam->sdev = NULL;
+ goto out;
+ }
+
+ dev_info(cam->dev, "sensor is %s\n", sdev->name);
+
+out:
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+
+ vidioc_int_s_power(sdev, 0);
+
+ return err;
+}
+
+static void omap24xxcam_sensor_exit(struct omap24xxcam_device *cam)
+{
+ if (cam->sdev)
+ vidioc_int_dev_exit(cam->sdev);
+}
+
+static void omap24xxcam_sensor_disable(struct omap24xxcam_device *cam)
+{
+ omap24xxcam_sensor_if_disable(cam);
+ omap24xxcam_clock_off(cam);
+ vidioc_int_s_power(cam->sdev, 0);
+}
+
+/*
+ * Power-up and configure camera sensor. It's ready for capturing now.
+ */
+static int omap24xxcam_sensor_enable(struct omap24xxcam_device *cam)
+{
+ int rval;
+
+ omap24xxcam_clock_on(cam);
+
+ omap24xxcam_sensor_if_enable(cam);
+
+ rval = vidioc_int_s_power(cam->sdev, 1);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_init(cam->sdev);
+ if (rval)
+ goto out;
+
+ return 0;
+
+out:
+ omap24xxcam_sensor_disable(cam);
+
+ return rval;
+}
+
+static void omap24xxcam_sensor_reset_work(struct work_struct *work)
+{
+ struct omap24xxcam_device *cam =
+ container_of(work, struct omap24xxcam_device,
+ sensor_reset_work);
+
+ if (atomic_read(&cam->reset_disable))
+ return;
+
+ omap24xxcam_capture_stop(cam);
+
+ if (vidioc_int_reset(cam->sdev) == 0) {
+ vidioc_int_init(cam->sdev);
+ } else {
+ /* Can't reset it by vidioc_int_reset. */
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_sensor_enable(cam);
+ }
+
+ omap24xxcam_capture_cont(cam);
+}
+
+/*
+ *
+ * IOCTL interface.
+ *
+ */
+
+static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+
+ strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver));
+ strlcpy(cap->card, cam->vfd->name, sizeof(cap->card));
+ cap->version = OMAP24XXCAM_VERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_enum_fmt_cap(cam->sdev, f);
+
+ return rval;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = vidioc_int_s_fmt_cap(cam->sdev, f);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ if (!rval) {
+ mutex_lock(&ofh->vbq.vb_lock);
+ ofh->pix = f->fmt.pix;
+ mutex_unlock(&ofh->vbq.vb_lock);
+ }
+
+ memset(f, 0, sizeof(*f));
+ vidioc_g_fmt_vid_cap(file, fh, f);
+
+ return rval;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_try_fmt_cap(cam->sdev, f);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+ mutex_unlock(&cam->mutex);
+
+ rval = videobuf_reqbufs(&ofh->vbq, b);
+
+ /*
+ * Either videobuf_reqbufs failed or the buffers are not
+ * memory-mapped (which would need special attention).
+ */
+ if (rval < 0 || b->memory != V4L2_MEMORY_MMAP)
+ goto out;
+
+ rval = omap24xxcam_vbq_alloc_mmap_buffers(&ofh->vbq, rval);
+ if (rval)
+ omap24xxcam_vbq_free_mmap_buffers(&ofh->vbq);
+
+out:
+ return rval;
+}
+
+static int vidioc_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_querybuf(&ofh->vbq, b);
+}
+
+static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+
+ return videobuf_qbuf(&ofh->vbq, b);
+}
+
+static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_buffer *vb;
+ int rval;
+
+videobuf_dqbuf_again:
+ rval = videobuf_dqbuf(&ofh->vbq, b, file->f_flags & O_NONBLOCK);
+ if (rval)
+ goto out;
+
+ vb = ofh->vbq.bufs[b->index];
+
+ mutex_lock(&cam->mutex);
+ /* _needs_reset returns -EIO if reset is required. */
+ rval = vidioc_int_g_needs_reset(cam->sdev, (void *)vb->baddr);
+ mutex_unlock(&cam->mutex);
+ if (rval == -EIO)
+ schedule_work(&cam->sensor_reset_work);
+ else
+ rval = 0;
+
+out:
+ /*
+ * This is a hack. We don't want to show -EIO to the user
+ * space. Requeue the buffer and try again if we're not doing
+ * this in non-blocking mode.
+ */
+ if (rval == -EIO) {
+ videobuf_qbuf(&ofh->vbq, b);
+ if (!(file->f_flags & O_NONBLOCK))
+ goto videobuf_dqbuf_again;
+ /*
+ * We don't have a videobuf_buffer now --- maybe next
+ * time...
+ */
+ rval = -EAGAIN;
+ }
+
+ return rval;
+}
+
+static int vidioc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+ goto out;
+ }
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ if (rval) {
+ dev_dbg(cam->dev, "vidioc_int_g_ifparm failed\n");
+ goto out;
+ }
+
+ rval = videobuf_streamon(&ofh->vbq);
+ if (!rval) {
+ cam->streaming = file;
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ struct videobuf_queue *q = &ofh->vbq;
+ int rval;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_scheduled_work();
+
+ rval = videobuf_streamoff(q);
+ if (!rval) {
+ mutex_lock(&cam->mutex);
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ return rval;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh,
+ struct v4l2_input *inp)
+{
+ if (inp->index > 0)
+ return -EINVAL;
+
+ strlcpy(inp->name, "camera", sizeof(inp->name));
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+ return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh,
+ struct v4l2_queryctrl *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ rval = vidioc_int_queryctrl(cam->sdev, a);
+
+ return rval;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh,
+ struct v4l2_control *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_s_ctrl(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a) {
+ struct omap24xxcam_fh *ofh = 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);
+
+ return rval;
+}
+
+static int vidioc_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct omap24xxcam_fh *ofh = fh;
+ struct omap24xxcam_device *cam = ofh->cam;
+ 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;
+ goto out;
+ }
+
+ old_streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ rval = vidioc_int_g_parm(cam->sdev, &old_streamparm);
+ if (rval)
+ goto out;
+
+ rval = vidioc_int_s_parm(cam->sdev, a);
+ if (rval)
+ goto out;
+
+ rval = omap24xxcam_sensor_if_enable(cam);
+ /*
+ * Revert to old streaming parameters if enabling sensor
+ * interface with the new ones failed.
+ */
+ if (rval)
+ vidioc_int_s_parm(cam->sdev, &old_streamparm);
+
+out:
+ mutex_unlock(&cam->mutex);
+
+ return rval;
+}
+
+/*
+ *
+ * File operations.
+ *
+ */
+
+static unsigned int omap24xxcam_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_buffer *vb;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming != file) {
+ mutex_unlock(&cam->mutex);
+ return POLLERR;
+ }
+ mutex_unlock(&cam->mutex);
+
+ mutex_lock(&fh->vbq.vb_lock);
+ if (list_empty(&fh->vbq.stream)) {
+ mutex_unlock(&fh->vbq.vb_lock);
+ return POLLERR;
+ }
+ vb = list_entry(fh->vbq.stream.next, struct videobuf_buffer, stream);
+ mutex_unlock(&fh->vbq.vb_lock);
+
+ poll_wait(file, &vb->done, wait);
+
+ if (vb->state == VIDEOBUF_DONE || vb->state == VIDEOBUF_ERROR)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int omap24xxcam_mmap_buffers(struct file *file,
+ struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+ struct videobuf_queue *vbq = &fh->vbq;
+ unsigned int first, last, size, i, j;
+ int err = 0;
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ mutex_unlock(&cam->mutex);
+ return -EBUSY;
+ }
+ mutex_unlock(&cam->mutex);
+ mutex_lock(&vbq->vb_lock);
+
+ /* look for first buffer to map */
+ for (first = 0; first < VIDEO_MAX_FRAME; first++) {
+ if (NULL == vbq->bufs[first])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[first]->memory)
+ continue;
+ if (vbq->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
+ break;
+ }
+
+ /* look for last buffer to map */
+ for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
+ if (NULL == vbq->bufs[last])
+ continue;
+ if (V4L2_MEMORY_MMAP != vbq->bufs[last]->memory)
+ continue;
+ size += vbq->bufs[last]->bsize;
+ if (size == (vma->vm_end - vma->vm_start))
+ break;
+ }
+
+ size = 0;
+ for (i = first; i <= last; i++) {
+ struct videobuf_dmabuf *dma = videobuf_to_dma(vbq->bufs[i]);
+
+ for (j = 0; j < dma->sglen; j++) {
+ err = remap_pfn_range(
+ vma, vma->vm_start + size,
+ page_to_pfn(sg_page(&dma->sglist[j])),
+ sg_dma_len(&dma->sglist[j]), vma->vm_page_prot);
+ if (err)
+ goto out;
+ size += sg_dma_len(&dma->sglist[j]);
+ }
+ }
+
+out:
+ mutex_unlock(&vbq->vb_lock);
+
+ return err;
+}
+
+static int omap24xxcam_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ int rval;
+
+ /* let the video-buf mapper check arguments and set-up structures */
+ rval = videobuf_mmap_mapper(&fh->vbq, vma);
+ if (rval)
+ return rval;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ /* do mapping to our allocated buffers */
+ rval = omap24xxcam_mmap_buffers(file, vma);
+ /*
+ * In case of error, free vma->vm_private_data allocated by
+ * videobuf_mmap_mapper.
+ */
+ if (rval)
+ kfree(vma->vm_private_data);
+
+ return rval;
+}
+
+static int omap24xxcam_open(struct inode *inode, struct file *file)
+{
+ int minor = iminor(inode);
+ struct omap24xxcam_device *cam = omap24xxcam.priv;
+ struct omap24xxcam_fh *fh;
+ struct v4l2_format format;
+
+ if (!cam || !cam->vfd || (cam->vfd->minor != minor))
+ return -ENODEV;
+
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (fh == NULL)
+ return -ENOMEM;
+
+ mutex_lock(&cam->mutex);
+ if (cam->sdev == NULL || !try_module_get(cam->sdev->module)) {
+ mutex_unlock(&cam->mutex);
+ goto out_try_module_get;
+ }
+
+ if (atomic_inc_return(&cam->users) == 1) {
+ omap24xxcam_hwinit(cam);
+ if (omap24xxcam_sensor_enable(cam)) {
+ mutex_unlock(&cam->mutex);
+ goto out_omap24xxcam_sensor_enable;
+ }
+ }
+ mutex_unlock(&cam->mutex);
+
+ fh->cam = cam;
+ mutex_lock(&cam->mutex);
+ vidioc_int_g_fmt_cap(cam->sdev, &format);
+ mutex_unlock(&cam->mutex);
+ /* FIXME: how about fh->pix when there are more users? */
+ fh->pix = format.fmt.pix;
+
+ file->private_data = fh;
+
+ spin_lock_init(&fh->vbq_lock);
+
+ videobuf_queue_sg_init(&fh->vbq, &omap24xxcam_vbq_ops, NULL,
+ &fh->vbq_lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_NONE,
+ sizeof(struct videobuf_buffer), fh);
+
+ return 0;
+
+out_omap24xxcam_sensor_enable:
+ omap24xxcam_poweron_reset(cam);
+ module_put(cam->sdev->module);
+
+out_try_module_get:
+ kfree(fh);
+
+ return -ENODEV;
+}
+
+static int omap24xxcam_release(struct inode *inode, struct file *file)
+{
+ struct omap24xxcam_fh *fh = file->private_data;
+ struct omap24xxcam_device *cam = fh->cam;
+
+ atomic_inc(&cam->reset_disable);
+
+ flush_scheduled_work();
+
+ /* stop streaming capture */
+ videobuf_streamoff(&fh->vbq);
+
+ mutex_lock(&cam->mutex);
+ if (cam->streaming == file) {
+ cam->streaming = NULL;
+ mutex_unlock(&cam->mutex);
+ sysfs_notify(&cam->dev->kobj, NULL, "streaming");
+ } else {
+ mutex_unlock(&cam->mutex);
+ }
+
+ atomic_dec(&cam->reset_disable);
+
+ omap24xxcam_vbq_free_mmap_buffers(&fh->vbq);
+
+ /*
+ * Make sure the reset work we might have scheduled is not
+ * pending! It may be run *only* if we have users. (And it may
+ * not be scheduled anymore since streaming is already
+ * disabled.)
+ */
+ flush_scheduled_work();
+
+ mutex_lock(&cam->mutex);
+ if (atomic_dec_return(&cam->users) == 0) {
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+ }
+ mutex_unlock(&cam->mutex);
+
+ file->private_data = NULL;
+
+ module_put(cam->sdev->module);
+ kfree(fh);
+
+ return 0;
+}
+
+static struct file_operations omap24xxcam_fops = {
+ .llseek = no_llseek,
+ .ioctl = video_ioctl2,
+ .poll = omap24xxcam_poll,
+ .mmap = omap24xxcam_mmap,
+ .open = omap24xxcam_open,
+ .release = omap24xxcam_release,
+};
+
+/*
+ *
+ * Power management.
+ *
+ */
+
+#ifdef CONFIG_PM
+static int omap24xxcam_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_stop(cam);
+
+ omap24xxcam_sensor_disable(cam);
+ omap24xxcam_poweron_reset(cam);
+
+ return 0;
+}
+
+static int omap24xxcam_resume(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (atomic_read(&cam->users) == 0)
+ return 0;
+
+ omap24xxcam_hwinit(cam);
+ omap24xxcam_sensor_enable(cam);
+
+ if (!atomic_read(&cam->reset_disable))
+ omap24xxcam_capture_cont(cam);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+static const struct v4l2_ioctl_ops omap24xxcam_ioctl_fops = {
+ .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_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_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_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+};
+
+/*
+ *
+ * Camera device (i.e. /dev/video).
+ *
+ */
+
+static int omap24xxcam_device_register(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+ struct video_device *vfd;
+ int rval;
+
+ /* We already have a slave. */
+ if (cam->sdev)
+ return -EBUSY;
+
+ cam->sdev = s;
+
+ if (device_create_file(cam->dev, &dev_attr_streaming) != 0) {
+ dev_err(cam->dev, "could not register sysfs entry\n");
+ rval = -EBUSY;
+ goto err;
+ }
+
+ /* initialize the video_device struct */
+ vfd = cam->vfd = video_device_alloc();
+ if (!vfd) {
+ dev_err(cam->dev, "could not allocate video device struct\n");
+ rval = -ENOMEM;
+ goto err;
+ }
+ vfd->release = video_device_release;
+
+ 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;
+
+ omap24xxcam_hwinit(cam);
+
+ rval = omap24xxcam_sensor_init(cam);
+ if (rval)
+ goto err;
+
+ if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) {
+ dev_err(cam->dev, "could not register V4L device\n");
+ vfd->minor = -1;
+ rval = -EBUSY;
+ goto err;
+ }
+
+ omap24xxcam_poweron_reset(cam);
+
+ dev_info(cam->dev, "registered device video%d\n", vfd->minor);
+
+ return 0;
+
+err:
+ omap24xxcam_device_unregister(s);
+
+ return rval;
+}
+
+static void omap24xxcam_device_unregister(struct v4l2_int_device *s)
+{
+ struct omap24xxcam_device *cam = s->u.slave->master->priv;
+
+ omap24xxcam_sensor_exit(cam);
+
+ if (cam->vfd) {
+ if (cam->vfd->minor == -1) {
+ /*
+ * The device was never registered, so release the
+ * video_device struct directly.
+ */
+ video_device_release(cam->vfd);
+ } else {
+ /*
+ * The unregister function will release the
+ * video_device struct as well as
+ * unregistering it.
+ */
+ video_unregister_device(cam->vfd);
+ }
+ cam->vfd = NULL;
+ }
+
+ device_remove_file(cam->dev, &dev_attr_streaming);
+
+ cam->sdev = NULL;
+}
+
+static struct v4l2_int_master omap24xxcam_master = {
+ .attach = omap24xxcam_device_register,
+ .detach = omap24xxcam_device_unregister,
+};
+
+static struct v4l2_int_device omap24xxcam = {
+ .module = THIS_MODULE,
+ .name = CAM_NAME,
+ .type = v4l2_int_type_master,
+ .u = {
+ .master = &omap24xxcam_master
+ },
+};
+
+/*
+ *
+ * Driver initialisation and deinitialisation.
+ *
+ */
+
+static int __init omap24xxcam_probe(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam;
+ struct resource *mem;
+ int irq;
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam) {
+ dev_err(&pdev->dev, "could not allocate memory\n");
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, cam);
+
+ cam->dev = &pdev->dev;
+
+ /*
+ * Impose a lower limit on the amount of memory allocated for
+ * capture. We require at least enough memory to double-buffer
+ * QVGA (300KB).
+ */
+ if (capture_mem < 320 * 240 * 2 * 2)
+ capture_mem = 320 * 240 * 2 * 2;
+ cam->capture_mem = capture_mem;
+
+ /* request the mem region for the camera registers */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(cam->dev, "no mem resource?\n");
+ goto err;
+ }
+ if (!request_mem_region(mem->start, (mem->end - mem->start) + 1,
+ pdev->name)) {
+ dev_err(cam->dev,
+ "cannot reserve camera register I/O region\n");
+ goto err;
+ }
+ cam->mmio_base_phys = mem->start;
+ cam->mmio_size = (mem->end - mem->start) + 1;
+
+ /* map the region */
+ cam->mmio_base = (unsigned long)
+ ioremap_nocache(cam->mmio_base_phys, cam->mmio_size);
+ if (!cam->mmio_base) {
+ dev_err(cam->dev, "cannot map camera register I/O region\n");
+ goto err;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(cam->dev, "no irq for camera?\n");
+ goto err;
+ }
+
+ /* install the interrupt service routine */
+ if (request_irq(irq, omap24xxcam_isr, 0, CAM_NAME, cam)) {
+ dev_err(cam->dev,
+ "could not install interrupt service routine\n");
+ goto err;
+ }
+ cam->irq = irq;
+
+ if (omap24xxcam_clock_get(cam))
+ goto err;
+
+ INIT_WORK(&cam->sensor_reset_work, omap24xxcam_sensor_reset_work);
+
+ mutex_init(&cam->mutex);
+ spin_lock_init(&cam->core_enable_disable_lock);
+
+ omap24xxcam_sgdma_init(&cam->sgdma,
+ cam->mmio_base + CAMDMA_REG_OFFSET,
+ omap24xxcam_stalled_dma_reset,
+ (unsigned long)cam);
+
+ omap24xxcam.priv = cam;
+
+ if (v4l2_int_device_register(&omap24xxcam))
+ goto err;
+
+ return 0;
+
+err:
+ omap24xxcam_remove(pdev);
+ return -ENODEV;
+}
+
+static int omap24xxcam_remove(struct platform_device *pdev)
+{
+ struct omap24xxcam_device *cam = platform_get_drvdata(pdev);
+
+ if (!cam)
+ return 0;
+
+ if (omap24xxcam.priv != NULL)
+ v4l2_int_device_unregister(&omap24xxcam);
+ omap24xxcam.priv = NULL;
+
+ omap24xxcam_clock_put(cam);
+
+ if (cam->irq) {
+ free_irq(cam->irq, cam);
+ cam->irq = 0;
+ }
+
+ if (cam->mmio_base) {
+ iounmap((void *)cam->mmio_base);
+ cam->mmio_base = 0;
+ }
+
+ if (cam->mmio_base_phys) {
+ release_mem_region(cam->mmio_base_phys, cam->mmio_size);
+ cam->mmio_base_phys = 0;
+ }
+
+ kfree(cam);
+
+ return 0;
+}
+
+static struct platform_driver omap24xxcam_driver = {
+ .probe = omap24xxcam_probe,
+ .remove = omap24xxcam_remove,
+#ifdef CONFIG_PM
+ .suspend = omap24xxcam_suspend,
+ .resume = omap24xxcam_resume,
+#endif
+ .driver = {
+ .name = CAM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*
+ *
+ * Module initialisation and deinitialisation
+ *
+ */
+
+static int __init omap24xxcam_init(void)
+{
+ return platform_driver_register(&omap24xxcam_driver);
+}
+
+static void __exit omap24xxcam_cleanup(void)
+{
+ platform_driver_unregister(&omap24xxcam_driver);
+}
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
+MODULE_DESCRIPTION("OMAP24xx Video for Linux camera driver");
+MODULE_LICENSE("GPL");
+module_param(video_nr, int, 0);
+MODULE_PARM_DESC(video_nr,
+ "Minor number for video device (-1 ==> auto assign)");
+module_param(capture_mem, int, 0);
+MODULE_PARM_DESC(capture_mem, "Maximum amount of memory for capture "
+ "buffers (default 4800kiB)");
+
+module_init(omap24xxcam_init);
+module_exit(omap24xxcam_cleanup);
diff --git a/linux/drivers/media/video/omap24xxcam.h b/linux/drivers/media/video/omap24xxcam.h
new file mode 100644
index 000000000..2ce67f5a4
--- /dev/null
+++ b/linux/drivers/media/video/omap24xxcam.h
@@ -0,0 +1,593 @@
+/*
+ * drivers/media/video/omap24xxcam.h
+ *
+ * Copyright (C) 2004 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ * Copyright (C) 2007 Nokia Corporation.
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * Based on code from Andy Lowe <source@mvista.com>.
+ *
+ * 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.
+ *
+ * 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 St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef OMAP24XXCAM_H
+#define OMAP24XXCAM_H
+
+#include <media/videobuf-dma-sg.h>
+#include <media/v4l2-int-device.h>
+
+/*
+ *
+ * General driver related definitions.
+ *
+ */
+
+#define CAM_NAME "omap24xxcam"
+
+#define CAM_MCLK 96000000
+
+/* number of bytes transferred per DMA request */
+#define DMA_THRESHOLD 32
+
+/*
+ * NUM_CAMDMA_CHANNELS is the number of logical channels provided by
+ * the camera DMA controller.
+ */
+#define NUM_CAMDMA_CHANNELS 4
+
+/*
+ * NUM_SG_DMA is the number of scatter-gather DMA transfers that can
+ * be queued. (We don't have any overlay sglists now.)
+ */
+#define NUM_SG_DMA (VIDEO_MAX_FRAME)
+
+/*
+ *
+ * Register definitions.
+ *
+ */
+
+/* subsystem register block offsets */
+#define CC_REG_OFFSET 0x00000400
+#define CAMDMA_REG_OFFSET 0x00000800
+#define CAMMMU_REG_OFFSET 0x00000C00
+
+/* define camera subsystem register offsets */
+#define CAM_REVISION 0x000
+#define CAM_SYSCONFIG 0x010
+#define CAM_SYSSTATUS 0x014
+#define CAM_IRQSTATUS 0x018
+#define CAM_GPO 0x040
+#define CAM_GPI 0x050
+
+/* define camera core register offsets */
+#define CC_REVISION 0x000
+#define CC_SYSCONFIG 0x010
+#define CC_SYSSTATUS 0x014
+#define CC_IRQSTATUS 0x018
+#define CC_IRQENABLE 0x01C
+#define CC_CTRL 0x040
+#define CC_CTRL_DMA 0x044
+#define CC_CTRL_XCLK 0x048
+#define CC_FIFODATA 0x04C
+#define CC_TEST 0x050
+#define CC_GENPAR 0x054
+#define CC_CCPFSCR 0x058
+#define CC_CCPFECR 0x05C
+#define CC_CCPLSCR 0x060
+#define CC_CCPLECR 0x064
+#define CC_CCPDFR 0x068
+
+/* define camera dma register offsets */
+#define CAMDMA_REVISION 0x000
+#define CAMDMA_IRQSTATUS_L0 0x008
+#define CAMDMA_IRQSTATUS_L1 0x00C
+#define CAMDMA_IRQSTATUS_L2 0x010
+#define CAMDMA_IRQSTATUS_L3 0x014
+#define CAMDMA_IRQENABLE_L0 0x018
+#define CAMDMA_IRQENABLE_L1 0x01C
+#define CAMDMA_IRQENABLE_L2 0x020
+#define CAMDMA_IRQENABLE_L3 0x024
+#define CAMDMA_SYSSTATUS 0x028
+#define CAMDMA_OCP_SYSCONFIG 0x02C
+#define CAMDMA_CAPS_0 0x064
+#define CAMDMA_CAPS_2 0x06C
+#define CAMDMA_CAPS_3 0x070
+#define CAMDMA_CAPS_4 0x074
+#define CAMDMA_GCR 0x078
+#define CAMDMA_CCR(n) (0x080 + (n)*0x60)
+#define CAMDMA_CLNK_CTRL(n) (0x084 + (n)*0x60)
+#define CAMDMA_CICR(n) (0x088 + (n)*0x60)
+#define CAMDMA_CSR(n) (0x08C + (n)*0x60)
+#define CAMDMA_CSDP(n) (0x090 + (n)*0x60)
+#define CAMDMA_CEN(n) (0x094 + (n)*0x60)
+#define CAMDMA_CFN(n) (0x098 + (n)*0x60)
+#define CAMDMA_CSSA(n) (0x09C + (n)*0x60)
+#define CAMDMA_CDSA(n) (0x0A0 + (n)*0x60)
+#define CAMDMA_CSEI(n) (0x0A4 + (n)*0x60)
+#define CAMDMA_CSFI(n) (0x0A8 + (n)*0x60)
+#define CAMDMA_CDEI(n) (0x0AC + (n)*0x60)
+#define CAMDMA_CDFI(n) (0x0B0 + (n)*0x60)
+#define CAMDMA_CSAC(n) (0x0B4 + (n)*0x60)
+#define CAMDMA_CDAC(n) (0x0B8 + (n)*0x60)
+#define CAMDMA_CCEN(n) (0x0BC + (n)*0x60)
+#define CAMDMA_CCFN(n) (0x0C0 + (n)*0x60)
+#define CAMDMA_COLOR(n) (0x0C4 + (n)*0x60)
+
+/* define camera mmu register offsets */
+#define CAMMMU_REVISION 0x000
+#define CAMMMU_SYSCONFIG 0x010
+#define CAMMMU_SYSSTATUS 0x014
+#define CAMMMU_IRQSTATUS 0x018
+#define CAMMMU_IRQENABLE 0x01C
+#define CAMMMU_WALKING_ST 0x040
+#define CAMMMU_CNTL 0x044
+#define CAMMMU_FAULT_AD 0x048
+#define CAMMMU_TTB 0x04C
+#define CAMMMU_LOCK 0x050
+#define CAMMMU_LD_TLB 0x054
+#define CAMMMU_CAM 0x058
+#define CAMMMU_RAM 0x05C
+#define CAMMMU_GFLUSH 0x060
+#define CAMMMU_FLUSH_ENTRY 0x064
+#define CAMMMU_READ_CAM 0x068
+#define CAMMMU_READ_RAM 0x06C
+#define CAMMMU_EMU_FAULT_AD 0x070
+
+/* Define bit fields within selected registers */
+#define CAM_REVISION_MAJOR (15 << 4)
+#define CAM_REVISION_MAJOR_SHIFT 4
+#define CAM_REVISION_MINOR (15 << 0)
+#define CAM_REVISION_MINOR_SHIFT 0
+
+#define CAM_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAM_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAM_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAM_IRQSTATUS_CC_IRQ (1 << 4)
+#define CAM_IRQSTATUS_MMU_IRQ (1 << 3)
+#define CAM_IRQSTATUS_DMA_IRQ2 (1 << 2)
+#define CAM_IRQSTATUS_DMA_IRQ1 (1 << 1)
+#define CAM_IRQSTATUS_DMA_IRQ0 (1 << 0)
+
+#define CAM_GPO_CAM_S_P_EN (1 << 1)
+#define CAM_GPO_CAM_CCP_MODE (1 << 0)
+
+#define CAM_GPI_CC_DMA_REQ1 (1 << 24)
+#define CAP_GPI_CC_DMA_REQ0 (1 << 23)
+#define CAP_GPI_CAM_MSTANDBY (1 << 21)
+#define CAP_GPI_CAM_WAIT (1 << 20)
+#define CAP_GPI_CAM_S_DATA (1 << 17)
+#define CAP_GPI_CAM_S_CLK (1 << 16)
+#define CAP_GPI_CAM_P_DATA (0xFFF << 3)
+#define CAP_GPI_CAM_P_DATA_SHIFT 3
+#define CAP_GPI_CAM_P_VS (1 << 2)
+#define CAP_GPI_CAM_P_HS (1 << 1)
+#define CAP_GPI_CAM_P_CLK (1 << 0)
+
+#define CC_REVISION_MAJOR (15 << 4)
+#define CC_REVISION_MAJOR_SHIFT 4
+#define CC_REVISION_MINOR (15 << 0)
+#define CC_REVISION_MINOR_SHIFT 0
+
+#define CC_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CC_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CC_SYSCONFIG_SOFTRESET (1 << 1)
+#define CC_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CC_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CC_IRQSTATUS_FS_IRQ (1 << 19)
+#define CC_IRQSTATUS_LE_IRQ (1 << 18)
+#define CC_IRQSTATUS_LS_IRQ (1 << 17)
+#define CC_IRQSTATUS_FE_IRQ (1 << 16)
+#define CC_IRQSTATUS_FW_ERR_IRQ (1 << 10)
+#define CC_IRQSTATUS_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQSTATUS_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQSTATUS_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQSTATUS_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQSTATUS_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQSTATUS_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQSTATUS_FIFO_UF_IRQ (1 << 0)
+
+#define CC_IRQENABLE_FS_IRQ (1 << 19)
+#define CC_IRQENABLE_LE_IRQ (1 << 18)
+#define CC_IRQENABLE_LS_IRQ (1 << 17)
+#define CC_IRQENABLE_FE_IRQ (1 << 16)
+#define CC_IRQENABLE_FW_ERR_IRQ (1 << 10)
+#define CC_IRQENABLE_FSC_ERR_IRQ (1 << 9)
+#define CC_IRQENABLE_SSC_ERR_IRQ (1 << 8)
+#define CC_IRQENABLE_FIFO_NOEMPTY_IRQ (1 << 4)
+#define CC_IRQENABLE_FIFO_FULL_IRQ (1 << 3)
+#define CC_IRQENABLE_FIFO_THR_IRQ (1 << 2)
+#define CC_IRQENABLE_FIFO_OF_IRQ (1 << 1)
+#define CC_IRQENABLE_FIFO_UF_IRQ (1 << 0)
+
+#define CC_CTRL_CC_ONE_SHOT (1 << 20)
+#define CC_CTRL_CC_IF_SYNCHRO (1 << 19)
+#define CC_CTRL_CC_RST (1 << 18)
+#define CC_CTRL_CC_FRAME_TRIG (1 << 17)
+#define CC_CTRL_CC_EN (1 << 16)
+#define CC_CTRL_NOBT_SYNCHRO (1 << 13)
+#define CC_CTRL_BT_CORRECT (1 << 12)
+#define CC_CTRL_PAR_ORDERCAM (1 << 11)
+#define CC_CTRL_PAR_CLK_POL (1 << 10)
+#define CC_CTRL_NOBT_HS_POL (1 << 9)
+#define CC_CTRL_NOBT_VS_POL (1 << 8)
+#define CC_CTRL_PAR_MODE (7 << 1)
+#define CC_CTRL_PAR_MODE_SHIFT 1
+#define CC_CTRL_PAR_MODE_NOBT8 (0 << 1)
+#define CC_CTRL_PAR_MODE_NOBT10 (1 << 1)
+#define CC_CTRL_PAR_MODE_NOBT12 (2 << 1)
+#define CC_CTRL_PAR_MODE_BT8 (4 << 1)
+#define CC_CTRL_PAR_MODE_BT10 (5 << 1)
+#define CC_CTRL_PAR_MODE_FIFOTEST (7 << 1)
+#define CC_CTRL_CCP_MODE (1 << 0)
+
+#define CC_CTRL_DMA_EN (1 << 8)
+#define CC_CTRL_DMA_FIFO_THRESHOLD (0x7F << 0)
+#define CC_CTRL_DMA_FIFO_THRESHOLD_SHIFT 0
+
+#define CC_CTRL_XCLK_DIV (0x1F << 0)
+#define CC_CTRL_XCLK_DIV_SHIFT 0
+#define CC_CTRL_XCLK_DIV_STABLE_LOW (0 << 0)
+#define CC_CTRL_XCLK_DIV_STABLE_HIGH (1 << 0)
+#define CC_CTRL_XCLK_DIV_BYPASS (31 << 0)
+
+#define CC_TEST_FIFO_RD_POINTER (0xFF << 24)
+#define CC_TEST_FIFO_RD_POINTER_SHIFT 24
+#define CC_TEST_FIFO_WR_POINTER (0xFF << 16)
+#define CC_TEST_FIFO_WR_POINTER_SHIFT 16
+#define CC_TEST_FIFO_LEVEL (0xFF << 8)
+#define CC_TEST_FIFO_LEVEL_SHIFT 8
+#define CC_TEST_FIFO_LEVEL_PEAK (0xFF << 0)
+#define CC_TEST_FIFO_LEVEL_PEAK_SHIFT 0
+
+#define CC_GENPAR_FIFO_DEPTH (7 << 0)
+#define CC_GENPAR_FIFO_DEPTH_SHIFT 0
+
+#define CC_CCPDFR_ALPHA (0xFF << 8)
+#define CC_CCPDFR_ALPHA_SHIFT 8
+#define CC_CCPDFR_DATAFORMAT (15 << 0)
+#define CC_CCPDFR_DATAFORMAT_SHIFT 0
+#define CC_CCPDFR_DATAFORMAT_YUV422BE (0 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV422 (1 << 0)
+#define CC_CCPDFR_DATAFORMAT_YUV420 (2 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB444 (4 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB565 (5 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888NDE (6 << 0)
+#define CC_CCPDFR_DATAFORMAT_RGB888 (7 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8NDE (8 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW8 (9 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10NDE (10 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW10 (11 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12NDE (12 << 0)
+#define CC_CCPDFR_DATAFORMAT_RAW12 (13 << 0)
+#define CC_CCPDFR_DATAFORMAT_JPEG8 (15 << 0)
+
+#define CAMDMA_REVISION_MAJOR (15 << 4)
+#define CAMDMA_REVISION_MAJOR_SHIFT 4
+#define CAMDMA_REVISION_MINOR (15 << 0)
+#define CAMDMA_REVISION_MINOR_SHIFT 0
+
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE (3 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY (0 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_NSTANDBY (1 << 12)
+#define CAMDMA_OCP_SYSCONFIG_MIDLEMODE_SSTANDBY (2 << 12)
+#define CAMDMA_OCP_SYSCONFIG_FUNC_CLOCK (1 << 9)
+#define CAMDMA_OCP_SYSCONFIG_OCP_CLOCK (1 << 8)
+#define CAMDMA_OCP_SYSCONFIG_EMUFREE (1 << 5)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE (3 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_FIDLE (0 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_NIDLE (1 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SIDLEMODE_SIDLE (2 << 3)
+#define CAMDMA_OCP_SYSCONFIG_SOFTRESET (1 << 1)
+#define CAMDMA_OCP_SYSCONFIG_AUTOIDLE (1 << 0)
+
+#define CAMDMA_SYSSTATUS_RESETDONE (1 << 0)
+
+#define CAMDMA_GCR_ARBITRATION_RATE (0xFF << 16)
+#define CAMDMA_GCR_ARBITRATION_RATE_SHIFT 16
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH (0xFF << 0)
+#define CAMDMA_GCR_MAX_CHANNEL_FIFO_DEPTH_SHIFT 0
+
+#define CAMDMA_CCR_SEL_SRC_DST_SYNC (1 << 24)
+#define CAMDMA_CCR_PREFETCH (1 << 23)
+#define CAMDMA_CCR_SUPERVISOR (1 << 22)
+#define CAMDMA_CCR_SECURE (1 << 21)
+#define CAMDMA_CCR_BS (1 << 18)
+#define CAMDMA_CCR_TRANSPARENT_COPY_ENABLE (1 << 17)
+#define CAMDMA_CCR_CONSTANT_FILL_ENABLE (1 << 16)
+#define CAMDMA_CCR_DST_AMODE (3 << 14)
+#define CAMDMA_CCR_DST_AMODE_CONST_ADDR (0 << 14)
+#define CAMDMA_CCR_DST_AMODE_POST_INC (1 << 14)
+#define CAMDMA_CCR_DST_AMODE_SGL_IDX (2 << 14)
+#define CAMDMA_CCR_DST_AMODE_DBL_IDX (3 << 14)
+#define CAMDMA_CCR_SRC_AMODE (3 << 12)
+#define CAMDMA_CCR_SRC_AMODE_CONST_ADDR (0 << 12)
+#define CAMDMA_CCR_SRC_AMODE_POST_INC (1 << 12)
+#define CAMDMA_CCR_SRC_AMODE_SGL_IDX (2 << 12)
+#define CAMDMA_CCR_SRC_AMODE_DBL_IDX (3 << 12)
+#define CAMDMA_CCR_WR_ACTIVE (1 << 10)
+#define CAMDMA_CCR_RD_ACTIVE (1 << 9)
+#define CAMDMA_CCR_SUSPEND_SENSITIVE (1 << 8)
+#define CAMDMA_CCR_ENABLE (1 << 7)
+#define CAMDMA_CCR_PRIO (1 << 6)
+#define CAMDMA_CCR_FS (1 << 5)
+#define CAMDMA_CCR_SYNCHRO ((3 << 19) | (31 << 0))
+#define CAMDMA_CCR_SYNCHRO_CAMERA 0x01
+
+#define CAMDMA_CLNK_CTRL_ENABLE_LNK (1 << 15)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID (0x1F << 0)
+#define CAMDMA_CLNK_CTRL_NEXTLCH_ID_SHIFT 0
+
+#define CAMDMA_CICR_MISALIGNED_ERR_IE (1 << 11)
+#define CAMDMA_CICR_SUPERVISOR_ERR_IE (1 << 10)
+#define CAMDMA_CICR_SECURE_ERR_IE (1 << 9)
+#define CAMDMA_CICR_TRANS_ERR_IE (1 << 8)
+#define CAMDMA_CICR_PACKET_IE (1 << 7)
+#define CAMDMA_CICR_BLOCK_IE (1 << 5)
+#define CAMDMA_CICR_LAST_IE (1 << 4)
+#define CAMDMA_CICR_FRAME_IE (1 << 3)
+#define CAMDMA_CICR_HALF_IE (1 << 2)
+#define CAMDMA_CICR_DROP_IE (1 << 1)
+
+#define CAMDMA_CSR_MISALIGNED_ERR (1 << 11)
+#define CAMDMA_CSR_SUPERVISOR_ERR (1 << 10)
+#define CAMDMA_CSR_SECURE_ERR (1 << 9)
+#define CAMDMA_CSR_TRANS_ERR (1 << 8)
+#define CAMDMA_CSR_PACKET (1 << 7)
+#define CAMDMA_CSR_SYNC (1 << 6)
+#define CAMDMA_CSR_BLOCK (1 << 5)
+#define CAMDMA_CSR_LAST (1 << 4)
+#define CAMDMA_CSR_FRAME (1 << 3)
+#define CAMDMA_CSR_HALF (1 << 2)
+#define CAMDMA_CSR_DROP (1 << 1)
+
+#define CAMDMA_CSDP_SRC_ENDIANNESS (1 << 21)
+#define CAMDMA_CSDP_SRC_ENDIANNESS_LOCK (1 << 20)
+#define CAMDMA_CSDP_DST_ENDIANNESS (1 << 19)
+#define CAMDMA_CSDP_DST_ENDIANNESS_LOCK (1 << 18)
+#define CAMDMA_CSDP_WRITE_MODE (3 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_WRNP (0 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED (1 << 16)
+#define CAMDMA_CSDP_WRITE_MODE_POSTED_LAST_WRNP (2 << 16)
+#define CAMDMA_CSDP_DST_BURST_EN (3 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_1 (0 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_16 (1 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_32 (2 << 14)
+#define CAMDMA_CSDP_DST_BURST_EN_64 (3 << 14)
+#define CAMDMA_CSDP_DST_PACKED (1 << 13)
+#define CAMDMA_CSDP_WR_ADD_TRSLT (15 << 9)
+#define CAMDMA_CSDP_WR_ADD_TRSLT_ENABLE_MREQADD (3 << 9)
+#define CAMDMA_CSDP_SRC_BURST_EN (3 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_1 (0 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_16 (1 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_32 (2 << 7)
+#define CAMDMA_CSDP_SRC_BURST_EN_64 (3 << 7)
+#define CAMDMA_CSDP_SRC_PACKED (1 << 6)
+#define CAMDMA_CSDP_RD_ADD_TRSLT (15 << 2)
+#define CAMDMA_CSDP_RD_ADD_TRSLT_ENABLE_MREQADD (3 << 2)
+#define CAMDMA_CSDP_DATA_TYPE (3 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_8BITS (0 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_16BITS (1 << 0)
+#define CAMDMA_CSDP_DATA_TYPE_32BITS (2 << 0)
+
+#define CAMMMU_SYSCONFIG_AUTOIDLE (1 << 0)
+
+/*
+ *
+ * Declarations.
+ *
+ */
+
+/* forward declarations */
+struct omap24xxcam_sgdma;
+struct omap24xxcam_dma;
+
+typedef void (*sgdma_callback_t)(struct omap24xxcam_sgdma *cam,
+ u32 status, void *arg);
+typedef void (*dma_callback_t)(struct omap24xxcam_dma *cam,
+ u32 status, void *arg);
+
+struct channel_state {
+ dma_callback_t callback;
+ void *arg;
+};
+
+/* sgdma state for each of the possible videobuf_buffers + 2 overlays */
+struct sgdma_state {
+ const struct scatterlist *sglist;
+ int sglen; /* number of sglist entries */
+ int next_sglist; /* index of next sglist entry to process */
+ unsigned int bytes_read; /* number of bytes read */
+ unsigned int len; /* total length of sglist (excluding
+ * bytes due to page alignment) */
+ int queued_sglist; /* number of sglist entries queued for DMA */
+ u32 csr; /* DMA return code */
+ sgdma_callback_t callback;
+ void *arg;
+};
+
+/* physical DMA channel management */
+struct omap24xxcam_dma {
+ spinlock_t lock; /* Lock for the whole structure. */
+
+ unsigned long base; /* base address for dma controller */
+
+ /* While dma_stop!=0, an attempt to start a new DMA transfer will
+ * fail.
+ */
+ atomic_t dma_stop;
+ int free_dmach; /* number of dma channels free */
+ int next_dmach; /* index of next dma channel to use */
+ struct channel_state ch_state[NUM_CAMDMA_CHANNELS];
+};
+
+/* scatter-gather DMA (scatterlist stuff) management */
+struct omap24xxcam_sgdma {
+ struct omap24xxcam_dma dma;
+
+ spinlock_t lock; /* Lock for the fields below. */
+ int free_sgdma; /* number of free sg dma slots */
+ int next_sgdma; /* index of next sg dma slot to use */
+ struct sgdma_state sg_state[NUM_SG_DMA];
+
+ /* Reset timer data */
+ struct timer_list reset_timer;
+};
+
+/* per-device data structure */
+struct omap24xxcam_device {
+ /*** mutex ***/
+ /*
+ * mutex serialises access to this structure. Also camera
+ * opening and releasing is synchronised by this.
+ */
+ struct mutex mutex;
+
+ /*** general driver state information ***/
+ atomic_t users;
+ /*
+ * Lock to serialise core enabling and disabling and access to
+ * sgdma_in_queue.
+ */
+ spinlock_t core_enable_disable_lock;
+ /*
+ * Number or sgdma requests in scatter-gather queue, protected
+ * by the lock above.
+ */
+ int sgdma_in_queue;
+ /*
+ * Sensor interface parameters: interface type, CC_CTRL
+ * register value and interface specific data.
+ */
+ int if_type;
+ union {
+ struct parallel {
+ u32 xclk;
+ } bt656;
+ } if_u;
+ u32 cc_ctrl;
+
+ /*** subsystem structures ***/
+ struct omap24xxcam_sgdma sgdma;
+
+ /*** hardware resources ***/
+ unsigned int irq;
+ unsigned long mmio_base;
+ unsigned long mmio_base_phys;
+ unsigned long mmio_size;
+
+ /*** interfaces and device ***/
+ struct v4l2_int_device *sdev;
+ struct device *dev;
+ struct video_device *vfd;
+
+ /*** camera and sensor reset related stuff ***/
+ struct work_struct sensor_reset_work;
+ /*
+ * We're in the middle of a reset. Don't enable core if this
+ * is non-zero! This exists to help decisionmaking in a case
+ * where videobuf_qbuf is called while we are in the middle of
+ * a reset.
+ */
+ atomic_t in_reset;
+ /*
+ * Non-zero if we don't want any resets for now. Used to
+ * prevent reset work to run when we're about to stop
+ * streaming.
+ */
+ atomic_t reset_disable;
+
+ /*** video device parameters ***/
+ int capture_mem;
+
+ /*** camera module clocks ***/
+ struct clk *fck;
+ struct clk *ick;
+
+ /*** capture data ***/
+ /* file handle, if streaming is on */
+ struct file *streaming;
+};
+
+/* Per-file handle data. */
+struct omap24xxcam_fh {
+ spinlock_t vbq_lock; /* spinlock for the videobuf queue */
+ struct videobuf_queue vbq;
+ struct v4l2_pix_format pix; /* serialise pix by vbq->lock */
+ atomic_t field_count; /* field counter for videobuf_buffer */
+ /* accessing cam here doesn't need serialisation: it's constant */
+ struct omap24xxcam_device *cam;
+};
+
+/*
+ *
+ * Register I/O functions.
+ *
+ */
+
+static inline u32 omap24xxcam_reg_in(unsigned long base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline u32 omap24xxcam_reg_out(unsigned long base, u32 offset,
+ u32 val)
+{
+ writel(val, base + offset);
+ return val;
+}
+
+static inline u32 omap24xxcam_reg_merge(unsigned long base, u32 offset,
+ u32 val, u32 mask)
+{
+ u32 addr = base + offset;
+ u32 new_val = (readl(addr) & ~mask) | (val & mask);
+
+ writel(new_val, addr);
+ return new_val;
+}
+
+/*
+ *
+ * Function prototypes.
+ *
+ */
+
+/* dma prototypes */
+
+void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma);
+void omap24xxcam_dma_isr(struct omap24xxcam_dma *dma);
+
+/* sgdma prototypes */
+
+void omap24xxcam_sgdma_process(struct omap24xxcam_sgdma *sgdma);
+int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
+ const struct scatterlist *sglist, int sglen,
+ int len, sgdma_callback_t callback, void *arg);
+void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma);
+void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
+ unsigned long base,
+ void (*reset_callback)(unsigned long data),
+ unsigned long reset_callback_data);
+void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma);
+
+#endif
diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig
index 19eb274c9..854c2a885 100644
--- a/linux/drivers/media/video/pvrusb2/Kconfig
+++ b/linux/drivers/media/video/pvrusb2/Kconfig
@@ -42,7 +42,7 @@ config VIDEO_PVRUSB2_DVB
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 !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
---help---
diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c
index 02386e403..e9edd4d37 100644
--- a/linux/drivers/media/video/saa7115.c
+++ b/linux/drivers/media/video/saa7115.c
@@ -44,7 +44,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include <media/saa7115.h>
@@ -71,6 +71,7 @@ static unsigned short normal_i2c[] = {
I2C_CLIENT_INSMOD;
struct saa711x_state {
+ struct v4l2_subdev sd;
v4l2_std_id std;
int input;
int output;
@@ -90,10 +91,17 @@ struct saa711x_state {
u8 apll;
};
+static inline struct saa711x_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa711x_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
-static inline int saa711x_write(struct i2c_client *client, u8 reg, u8 value)
+static inline int saa711x_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);
}
@@ -129,9 +137,9 @@ static int saa711x_has_reg(const int id, const u8 reg)
return 1;
}
-static int saa711x_writeregs(struct i2c_client *client, const unsigned char *regs)
+static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
unsigned char reg, data;
while (*regs != 0x00) {
@@ -140,18 +148,20 @@ static int saa711x_writeregs(struct i2c_client *client, const unsigned char *reg
/* According with datasheets, reserved regs should be
filled with 0 - seems better not to touch on they */
- if (saa711x_has_reg(state->ident,reg)) {
- if (saa711x_write(client, reg, data) < 0)
+ if (saa711x_has_reg(state->ident, reg)) {
+ if (saa711x_write(sd, reg, data) < 0)
return -1;
} else {
- v4l_dbg(1, debug, client, "tried to access reserved reg 0x%02x\n", reg);
+ v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg);
}
}
return 0;
}
-static inline int saa711x_read(struct i2c_client *client, u8 reg)
+static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
return i2c_smbus_read_byte_data(client, reg);
}
@@ -602,7 +612,7 @@ static int saa711x_odd_parity(u8 c)
return c & 1;
}
-static int saa711x_decode_vps(u8 * dst, u8 * p)
+static int saa711x_decode_vps(u8 *dst, u8 *p)
{
static const u8 biphase_tbl[] = {
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
@@ -649,7 +659,7 @@ static int saa711x_decode_vps(u8 * dst, u8 * p)
return err & 0xf0;
}
-static int saa711x_decode_wss(u8 * p)
+static int saa711x_decode_wss(u8 *p)
{
static const int wss_bits[8] = {
0, 0, 0, 1, 0, 1, 1, 1
@@ -676,9 +686,9 @@ static int saa711x_decode_wss(u8 * p)
return wss;
}
-static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
+static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
u32 acpf;
u32 acni;
u32 hz;
@@ -686,10 +696,10 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
u8 acc = 0; /* reg 0x3a, audio clock control */
/* Checks for chips that don't have audio clock (saa7111, saa7113) */
- if (!saa711x_has_reg(state->ident,R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
+ if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
return 0;
- v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq);
+ v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq);
/* sanity check */
if (freq < 32000 || freq > 48000)
@@ -716,66 +726,66 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
if (state->apll)
acc |= 0x08;
- saa711x_write(client, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
- saa711x_write(client, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
- saa711x_write(client, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
+ saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
+ saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
+ saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
- saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
- saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
(acpf >> 8) & 0xff);
- saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
+ saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
(acpf >> 16) & 0x03);
- saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
- saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
- saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
+ saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
state->audclk_freq = freq;
return 0;
}
-static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value < 0 || ctrl->value > 255) {
- v4l_err(client, "invalid brightness setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
return -ERANGE;
}
state->bright = ctrl->value;
- saa711x_write(client, R_0A_LUMA_BRIGHT_CNTL, state->bright);
+ saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid contrast setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
return -ERANGE;
}
state->contrast = ctrl->value;
- saa711x_write(client, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
+ saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
break;
case V4L2_CID_SATURATION:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid saturation setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
return -ERANGE;
}
state->sat = ctrl->value;
- saa711x_write(client, R_0C_CHROMA_SAT_CNTL, state->sat);
+ saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat);
break;
case V4L2_CID_HUE:
if (ctrl->value < -127 || ctrl->value > 127) {
- v4l_err(client, "invalid hue setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
return -ERANGE;
}
state->hue = ctrl->value;
- saa711x_write(client, R_0D_CHROMA_HUE_CNTL, state->hue);
+ saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue);
break;
default:
@@ -785,9 +795,9 @@ static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
return 0;
}
-static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
@@ -809,16 +819,16 @@ static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *c
return 0;
}
-static int saa711x_set_size(struct i2c_client *client, int width, int height)
+static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
int HPSC, HFSC;
int VSCY;
int res;
int is_50hz = state->std & V4L2_STD_625_50;
int Vsrc = is_50hz ? 576 : 480;
- v4l_dbg(1, debug, client, "decoder set size to %ix%i\n",width,height);
+ v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height);
/* FIXME need better bounds checking here */
if ((width < 1) || (width > 1440))
@@ -826,7 +836,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
if ((height < 1) || (height > Vsrc))
return -EINVAL;
- if (!saa711x_has_reg(state->ident,R_D0_B_HORIZ_PRESCALING)) {
+ if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) {
/* Decoder only supports 720 columns and 480 or 576 lines */
if (width != 720)
return -EINVAL;
@@ -844,22 +854,22 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
/* Set output width/height */
/* width */
- saa711x_write(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
+ saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
(u8) (width & 0xff));
- saa711x_write(client, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
+ saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
(u8) ((width >> 8) & 0xff));
/* Vertical Scaling uses height/2 */
- res=height/2;
+ res = height / 2;
/* On 60Hz, it is using a higher Vertical Output Size */
if (!is_50hz)
res += (VRES_60HZ - 480) >> 1;
/* height */
- saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
+ saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
(u8) (res & 0xff));
- saa711x_write(client, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
+ saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
(u8) ((res >> 8) & 0xff));
/* Scaling settings */
@@ -870,54 +880,54 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height)
HFSC = (int)((1024 * 720) / (HPSC * width));
/* FIXME hardcodes to "Task B"
* write H prescaler integer */
- saa711x_write(client, R_D0_B_HORIZ_PRESCALING,
+ saa711x_write(sd, R_D0_B_HORIZ_PRESCALING,
(u8) (HPSC & 0x3f));
- v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
+ v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
/* write H fine-scaling (luminance) */
- saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC,
+ saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC,
(u8) (HFSC & 0xff));
- saa711x_write(client, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
+ saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
(u8) ((HFSC >> 8) & 0xff));
/* write H fine-scaling (chrominance)
* must be lum/2, so i'll just bitshift :) */
- saa711x_write(client, R_DC_B_HORIZ_CHROMA_SCALING,
+ saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING,
(u8) ((HFSC >> 1) & 0xff));
- saa711x_write(client, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
+ saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
(u8) ((HFSC >> 9) & 0xff));
VSCY = (int)((1024 * Vsrc) / height);
- v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
+ v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
/* Correct Contrast and Luminance */
- saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL,
+ saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL,
(u8) (64 * 1024 / VSCY));
- saa711x_write(client, R_D6_B_CHROMA_SATURATION_CNTL,
+ saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL,
(u8) (64 * 1024 / VSCY));
/* write V fine-scaling (luminance) */
- saa711x_write(client, R_E0_B_VERT_LUMA_SCALING_INC,
+ saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC,
(u8) (VSCY & 0xff));
- saa711x_write(client, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
+ saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
(u8) ((VSCY >> 8) & 0xff));
/* write V fine-scaling (chrominance) */
- saa711x_write(client, R_E2_B_VERT_CHROMA_SCALING_INC,
+ saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC,
(u8) (VSCY & 0xff));
- saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
+ saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
(u8) ((VSCY >> 8) & 0xff));
- saa711x_writeregs(client, saa7115_cfg_reset_scaler);
+ saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
/* Activates task "B" */
- saa711x_write(client, R_80_GLOBAL_CNTL_1,
- saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20);
+ saa711x_write(sd, R_80_GLOBAL_CNTL_1,
+ saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20);
return 0;
}
-static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
+static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
/* Prevent unnecessary standard changes. During a standard
change the I-Port is temporarily disabled. Any devices
@@ -933,13 +943,13 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
// This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
if (std & V4L2_STD_525_60) {
- v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n");
- saa711x_writeregs(client, saa7115_cfg_60hz_video);
- saa711x_set_size(client, 720, 480);
+ v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n");
+ saa711x_writeregs(sd, saa7115_cfg_60hz_video);
+ saa711x_set_size(sd, 720, 480);
} else {
- v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n");
- saa711x_writeregs(client, saa7115_cfg_50hz_video);
- saa711x_set_size(client, 720, 576);
+ v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n");
+ saa711x_writeregs(sd, saa7115_cfg_50hz_video);
+ saa711x_set_size(sd, 720, 576);
}
/* Register 0E - Bits D6-D4 on NO-AUTO mode
@@ -953,7 +963,7 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
*/
if (state->ident == V4L2_IDENT_SAA7111 ||
state->ident == V4L2_IDENT_SAA7113) {
- u8 reg = saa711x_read(client, R_0E_CHROMA_CNTL_1) & 0x8f;
+ u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
if (std == V4L2_STD_PAL_M) {
reg |= 0x30;
@@ -966,87 +976,31 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
} else if (std & V4L2_STD_SECAM) {
reg |= 0x50;
}
- saa711x_write(client, R_0E_CHROMA_CNTL_1, reg);
+ saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg);
} else {
/* restart task B if needed */
- int taskb = saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10;
+ int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10;
if (taskb && state->ident == V4L2_IDENT_SAA7114) {
- saa711x_writeregs(client, saa7115_cfg_vbi_on);
+ saa711x_writeregs(sd, saa7115_cfg_vbi_on);
}
/* switch audio mode too! */
- saa711x_set_audio_clock_freq(client, state->audclk_freq);
- }
-}
-
-static v4l2_std_id saa711x_get_v4lstd(struct i2c_client *client)
-{
- struct saa711x_state *state = i2c_get_clientdata(client);
-
- return state->std;
-}
-
-static void saa711x_log_status(struct i2c_client *client)
-{
- struct saa711x_state *state = i2c_get_clientdata(client);
- int reg1e, reg1f;
- int signalOk;
- int vcr;
-
- v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq);
- if (state->ident != V4L2_IDENT_SAA7115) {
- /* status for the saa7114 */
- reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
- signalOk = (reg1f & 0xc1) == 0x81;
- v4l_info(client, "Video signal: %s\n", signalOk ? "ok" : "bad");
- v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
- return;
- }
-
- /* status for the saa7115 */
- reg1e = saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC);
- reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
-
- signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
- vcr = !(reg1f & 0x10);
-
- if (state->input >= 6) {
- v4l_info(client, "Input: S-Video %d\n", state->input - 6);
- } else {
- v4l_info(client, "Input: Composite %d\n", state->input);
+ saa711x_s_clock_freq(sd, state->audclk_freq);
}
- v4l_info(client, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
- v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
-
- switch (reg1e & 0x03) {
- case 1:
- v4l_info(client, "Detected format: NTSC\n");
- break;
- case 2:
- v4l_info(client, "Detected format: PAL\n");
- break;
- case 3:
- v4l_info(client, "Detected format: SECAM\n");
- break;
- default:
- v4l_info(client, "Detected format: BW/No color\n");
- break;
- }
- v4l_info(client, "Width, Height: %d, %d\n", state->width, state->height);
}
/* setup the sliced VBI lcr registers according to the sliced VBI format */
-static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt)
+static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
int is_50hz = (state->std & V4L2_STD_625_50);
u8 lcr[24];
int i, x;
#if 1 /* keep */
/* saa7113/7114/7118 VBI support are experimental */
- if (!saa711x_has_reg(state->ident,R_41_LCR_BASE))
+ if (!saa711x_has_reg(state->ident, R_41_LCR_BASE))
return;
#else
@@ -1110,16 +1064,16 @@ static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo
/* write the lcr registers */
for (i = 2; i <= 23; i++) {
- saa711x_write(client, i - 2 + R_41_LCR_BASE, lcr[i]);
+ saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]);
}
/* enable/disable raw VBI capturing */
- saa711x_writeregs(client, fmt == NULL ?
+ saa711x_writeregs(sd, fmt == NULL ?
saa7115_cfg_vbi_on :
saa7115_cfg_vbi_off);
}
-static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
static u16 lcr2vbi[] = {
0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
@@ -1135,10 +1089,10 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
return -EINVAL;
memset(sliced, 0, sizeof(*sliced));
/* done if using raw VBI */
- if (saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10)
+ if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10)
return 0;
for (i = 2; i <= 23; i++) {
- u8 v = saa711x_read(client, i - 2 + R_41_LCR_BASE);
+ u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE);
sliced->service_lines[0][i] = lcr2vbi[v >> 4];
sliced->service_lines[1][i] = lcr2vbi[v & 0xf];
@@ -1148,20 +1102,20 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
return 0;
}
-static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int saa711x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
{
if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
- saa711x_set_lcr(client, &fmt->fmt.sliced);
+ saa711x_set_lcr(sd, &fmt->fmt.sliced);
return 0;
}
if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
- saa711x_set_lcr(client, NULL);
+ saa711x_set_lcr(sd, NULL);
return 0;
}
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- return saa711x_set_size(client,fmt->fmt.pix.width,fmt->fmt.pix.height);
+ return saa711x_set_size(sd, fmt->fmt.pix.width, fmt->fmt.pix.height);
}
/* Decode the sliced VBI data stream as created by the saa7115.
@@ -1170,13 +1124,12 @@ static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
The current implementation uses SAV/EAV codes and not the ancillary data
headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV
code. */
-static void saa711x_decode_vbi_line(struct i2c_client *client,
- struct v4l2_decode_vbi_line *vbi)
+static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
{
+ struct saa711x_state *state = to_state(sd);
static const char vbi_no_data_pattern[] = {
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0
};
- struct saa711x_state *state = i2c_get_clientdata(client);
u8 *p = vbi->p;
u32 wss;
int id1, id2; /* the ID1 and ID2 bytes from the internal header */
@@ -1203,7 +1156,7 @@ static void saa711x_decode_vbi_line(struct i2c_client *client,
/* If the VBI slicer does not detect any signal it will fill up
the payload buffer with 0xa0 bytes. */
if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern)))
- return;
+ return 0;
/* decode payloads */
switch (id2) {
@@ -1212,275 +1165,352 @@ static void saa711x_decode_vbi_line(struct i2c_client *client,
break;
case 4:
if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1]))
- return;
+ return 0;
vbi->type = V4L2_SLICED_CAPTION_525;
break;
case 5:
wss = saa711x_decode_wss(p);
if (wss == -1)
- return;
+ return 0;
p[0] = wss & 0xff;
p[1] = wss >> 8;
vbi->type = V4L2_SLICED_WSS_625;
break;
case 7:
if (saa711x_decode_vps(p, p) != 0)
- return;
+ return 0;
vbi->type = V4L2_SLICED_VPS;
break;
default:
- return;
+ break;
}
+ return 0;
}
/* ============ SAA7115 AUDIO settings (end) ============= */
-static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
{
- struct saa711x_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = to_state(sd);
+ int status;
- /* ioctls to allow direct access to the saa7115 registers for testing */
- switch (cmd) {
- case VIDIOC_S_FMT:
- return saa711x_set_v4lfmt(client, (struct v4l2_format *)arg);
+ if (state->radio)
+ return 0;
+ status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
- case VIDIOC_G_FMT:
- return saa711x_get_v4lfmt(client, (struct v4l2_format *)arg);
+ v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status);
+ vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
+ return 0;
+}
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- return saa711x_set_audio_clock_freq(client, *(u32 *)arg);
+static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ 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:
+ return -EINVAL;
+ }
+}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = arg;
- int status;
+static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa711x_state *state = to_state(sd);
- if (state->radio)
- break;
- status = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
+ state->radio = 0;
+ saa711x_set_v4lstd(sd, std);
+ return 0;
+}
- v4l_dbg(1, debug, client, "status: 0x%02x\n", status);
- vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
- break;
- }
+static int saa711x_s_radio(struct v4l2_subdev *sd)
+{
+ struct saa711x_state *state = to_state(sd);
- case VIDIOC_LOG_STATUS:
- saa711x_log_status(client);
- break;
+ state->radio = 1;
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return saa711x_get_v4lctrl(client, (struct v4l2_control *)arg);
+static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct saa711x_state *state = to_state(sd);
+ u32 input = route->input;
+ u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0;
+
+ v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", route->input, route->output);
+ /* saa7111/3 does not have these inputs */
+ if ((state->ident == V4L2_IDENT_SAA7113 ||
+ state->ident == V4L2_IDENT_SAA7111) &&
+ (route->input == SAA7115_COMPOSITE4 ||
+ route->input == SAA7115_COMPOSITE5)) {
+ return -EINVAL;
+ }
+ if (route->input > SAA7115_SVIDEO3)
+ return -EINVAL;
+ if (route->output > SAA7115_IPORT_ON)
+ return -EINVAL;
+ if (state->input == route->input && state->output == route->output)
+ return 0;
+ v4l2_dbg(1, debug, sd, "now setting %s input %s output\n",
+ (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite",
+ (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
+ state->input = route->input;
+
+ /* saa7111 has slightly different input numbering */
+ if (state->ident == V4L2_IDENT_SAA7111) {
+ if (input >= SAA7115_COMPOSITE4)
+ input -= 2;
+ /* saa7111 specific */
+ saa711x_write(sd, R_10_CHROMA_CNTL_2,
+ (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) |
+ ((route->output & 0xc0) ^ 0x40));
+ saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL,
+ (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
+ ((route->output & 2) ? 0x0a : 0));
+ }
- case VIDIOC_S_CTRL:
- return saa711x_set_v4lctrl(client, (struct v4l2_control *)arg);
+ /* select mode */
+ saa711x_write(sd, R_02_INPUT_CNTL_1,
+ (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) |
+ input);
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
+ /* bypass chrominance trap for S-Video modes */
+ saa711x_write(sd, R_09_LUMA_CNTL,
+ (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) |
+ (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
- 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:
- return -EINVAL;
- }
+ state->output = route->output;
+ if (state->ident == V4L2_IDENT_SAA7114 ||
+ state->ident == V4L2_IDENT_SAA7115) {
+ saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
+ (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
+ (state->output & 0x01));
}
+ return 0;
+}
- case VIDIOC_G_STD:
- *(v4l2_std_id *)arg = saa711x_get_v4lstd(client);
- break;
+static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val)
+{
+ struct saa711x_state *state = to_state(sd);
- case VIDIOC_S_STD:
- state->radio = 0;
- saa711x_set_v4lstd(client, *(v4l2_std_id *)arg);
- break;
+ if (state->ident != V4L2_IDENT_SAA7111)
+ return -EINVAL;
+ saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
+ (val ? 0x80 : 0));
+ return 0;
+}
- case AUDC_SET_RADIO:
- state->radio = 1;
- break;
+static int saa711x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa711x_state *state = to_state(sd);
- case VIDIOC_INT_G_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
+ v4l2_dbg(1, debug, sd, "%s output\n",
+ enable ? "enable" : "disable");
- route->input = state->input;
- route->output = state->output;
- break;
+ if (state->enable != enable) {
+ state->enable = enable;
+ saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED,
+ state->enable);
}
+ return 0;
+}
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- struct v4l2_routing *route = arg;
- u32 input = route->input;
- u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0;
-
- v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output);
- /* saa7111/3 does not have these inputs */
- if ((state->ident == V4L2_IDENT_SAA7113 ||
- state->ident == V4L2_IDENT_SAA7111) &&
- (route->input == SAA7115_COMPOSITE4 ||
- route->input == SAA7115_COMPOSITE5)) {
- return -EINVAL;
- }
- if (route->input > SAA7115_SVIDEO3)
- return -EINVAL;
- if (route->output > SAA7115_IPORT_ON)
- return -EINVAL;
- if (state->input == route->input && state->output == route->output)
- break;
- v4l_dbg(1, debug, client, "now setting %s input %s output\n",
- (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off");
- state->input = route->input;
-
- /* saa7111 has slightly different input numbering */
- if (state->ident == V4L2_IDENT_SAA7111) {
- if (input >= SAA7115_COMPOSITE4)
- input -= 2;
- /* saa7111 specific */
- saa711x_write(client, R_10_CHROMA_CNTL_2,
- (saa711x_read(client, R_10_CHROMA_CNTL_2) & 0x3f) |
- ((route->output & 0xc0) ^ 0x40));
- saa711x_write(client, R_13_RT_X_PORT_OUT_CNTL,
- (saa711x_read(client, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) |
- ((route->output & 2) ? 0x0a : 0));
- }
+static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq)
+{
+ struct saa711x_state *state = to_state(sd);
- /* select mode */
- saa711x_write(client, R_02_INPUT_CNTL_1,
- (saa711x_read(client, R_02_INPUT_CNTL_1) & mask) |
- input);
+ if (freq->freq != SAA7115_FREQ_32_11_MHZ &&
+ freq->freq != SAA7115_FREQ_24_576_MHZ)
+ return -EINVAL;
+ state->crystal_freq = freq->freq;
+ state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
+ state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
+ state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
+ saa711x_s_clock_freq(sd, state->audclk_freq);
+ return 0;
+}
- /* bypass chrominance trap for S-Video modes */
- saa711x_write(client, R_09_LUMA_CNTL,
- (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) |
- (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
+static int saa711x_reset(struct v4l2_subdev *sd, u32 val)
+{
+ v4l2_dbg(1, debug, sd, "decoder RESET\n");
+ saa711x_writeregs(sd, saa7115_cfg_reset_scaler);
+ return 0;
+}
- state->output = route->output;
- if (state->ident == V4L2_IDENT_SAA7114 ||
- state->ident == V4L2_IDENT_SAA7115) {
- saa711x_write(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
- (saa711x_read(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
- (state->output & 0x01));
+static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data)
+{
+ /* Note: the internal field ID is inverted for NTSC,
+ so data->field 0 maps to the saa7115 even field,
+ whereas for PAL it maps to the saa7115 odd field. */
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ if (saa711x_read(sd, 0x6b) & 0xc0)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x6c);
+ data->data[1] = saa711x_read(sd, 0x6d);
+ return 0;
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0) {
+ /* CC */
+ if (saa711x_read(sd, 0x66) & 0x30)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x69);
+ data->data[1] = saa711x_read(sd, 0x6a);
+ return 0;
}
- break;
+ /* XDS */
+ if (saa711x_read(sd, 0x66) & 0xc0)
+ return -EIO;
+ data->data[0] = saa711x_read(sd, 0x67);
+ data->data[1] = saa711x_read(sd, 0x68);
+ return 0;
+ default:
+ return -EINVAL;
}
+}
- case VIDIOC_STREAMON:
- case VIDIOC_STREAMOFF:
- v4l_dbg(1, debug, client, "%s output\n",
- (cmd == VIDIOC_STREAMON) ? "enable" : "disable");
-
- if (state->enable != (cmd == VIDIOC_STREAMON)) {
- state->enable = (cmd == VIDIOC_STREAMON);
- saa711x_write(client,
- R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED,
- state->enable);
- }
- break;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_CRYSTAL_FREQ:
- {
- struct v4l2_crystal_freq *freq = arg;
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa711x_read(sd, reg->reg & 0xff);
+ return 0;
+}
- if (freq->freq != SAA7115_FREQ_32_11_MHZ &&
- freq->freq != SAA7115_FREQ_24_576_MHZ)
- return -EINVAL;
- state->crystal_freq = freq->freq;
- state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
- state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
- state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
- saa711x_set_audio_clock_freq(client, state->audclk_freq);
- break;
- }
+static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_DECODE_VBI_LINE:
- saa711x_decode_vbi_line(client, arg);
- break;
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
- case VIDIOC_INT_RESET:
- v4l_dbg(1, debug, client, "decoder RESET\n");
- saa711x_writeregs(client, saa7115_cfg_reset_scaler);
- break;
+static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct saa711x_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_INT_S_GPIO:
- if (state->ident != V4L2_IDENT_SAA7111)
- return -EINVAL;
- saa711x_write(client, 0x11, (saa711x_read(client, 0x11) & 0x7f) |
- (*(u32 *)arg ? 0x80 : 0));
- break;
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
+}
- case VIDIOC_INT_G_VBI_DATA:
- {
- struct v4l2_sliced_vbi_data *data = arg;
+static int saa711x_log_status(struct v4l2_subdev *sd)
+{
+ struct saa711x_state *state = to_state(sd);
+ int reg1e, reg1f;
+ int signalOk;
+ int vcr;
- /* Note: the internal field ID is inverted for NTSC,
- so data->field 0 maps to the saa7115 even field,
- whereas for PAL it maps to the saa7115 odd field. */
- switch (data->id) {
- case V4L2_SLICED_WSS_625:
- if (saa711x_read(client, 0x6b) & 0xc0)
- return -EIO;
- data->data[0] = saa711x_read(client, 0x6c);
- data->data[1] = saa711x_read(client, 0x6d);
- return 0;
- case V4L2_SLICED_CAPTION_525:
- if (data->field == 0) {
- /* CC */
- if (saa711x_read(client, 0x66) & 0x30)
- return -EIO;
- data->data[0] = saa711x_read(client, 0x69);
- data->data[1] = saa711x_read(client, 0x6a);
- return 0;
- }
- /* XDS */
- if (saa711x_read(client, 0x66) & 0xc0)
- return -EIO;
- data->data[0] = saa711x_read(client, 0x67);
- data->data[1] = saa711x_read(client, 0x68);
- return 0;
- default:
- return -EINVAL;
- }
- break;
+ v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq);
+ if (state->ident != V4L2_IDENT_SAA7115) {
+ /* status for the saa7114 */
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
+ signalOk = (reg1f & 0xc1) == 0x81;
+ v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad");
+ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+ return 0;
}
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
+ /* status for the saa7115 */
+ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
+ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = saa711x_read(client, reg->reg & 0xff);
- else
- saa711x_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
-#endif
+ signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
+ vcr = !(reg1f & 0x10);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0);
+ if (state->input >= 6)
+ v4l2_info(sd, "Input: S-Video %d\n", state->input - 6);
+ else
+ v4l2_info(sd, "Input: Composite %d\n", state->input);
+ v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad");
+ v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
+ switch (reg1e & 0x03) {
+ case 1:
+ v4l2_info(sd, "Detected format: NTSC\n");
+ break;
+ case 2:
+ v4l2_info(sd, "Detected format: PAL\n");
+ break;
+ case 3:
+ v4l2_info(sd, "Detected format: SECAM\n");
+ break;
default:
- return -EINVAL;
+ v4l2_info(sd, "Detected format: BW/No color\n");
+ break;
}
-
+ v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height);
return 0;
}
+static int saa711x_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 saa711x_core_ops = {
+ .log_status = saa711x_log_status,
+ .g_chip_ident = saa711x_g_chip_ident,
+ .g_ctrl = saa711x_g_ctrl,
+ .s_ctrl = saa711x_s_ctrl,
+ .queryctrl = saa711x_queryctrl,
+ .reset = saa711x_reset,
+ .s_gpio = saa711x_s_gpio,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa711x_g_register,
+ .s_register = saa711x_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = {
+ .s_std = saa711x_s_std,
+ .s_radio = saa711x_s_radio,
+ .g_tuner = saa711x_g_tuner,
+};
+
+static const struct v4l2_subdev_audio_ops saa711x_audio_ops = {
+ .s_clock_freq = saa711x_s_clock_freq,
+};
+
+static const struct v4l2_subdev_video_ops saa711x_video_ops = {
+ .s_routing = saa711x_s_routing,
+ .s_crystal_freq = saa711x_s_crystal_freq,
+ .g_fmt = saa711x_g_fmt,
+ .s_fmt = saa711x_s_fmt,
+ .g_vbi_data = saa711x_g_vbi_data,
+ .decode_vbi_line = saa711x_decode_vbi_line,
+ .s_stream = saa711x_s_stream,
+};
+
+static const struct v4l2_subdev_ops saa711x_ops = {
+ .core = &saa711x_core_ops,
+ .tuner = &saa711x_tuner_ops,
+ .audio = &saa711x_audio_ops,
+ .video = &saa711x_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
-static int saa7115_probe(struct i2c_client *client,
+static int saa711x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct saa711x_state *state;
+ struct v4l2_subdev *sd;
int i;
char name[17];
char chip_id;
@@ -1497,8 +1527,8 @@ static int saa7115_probe(struct i2c_client *client,
#endif
for (i = 0; i < 0x0f; i++) {
- saa711x_write(client, 0, i);
- name[i] = (saa711x_read(client, 0) & 0x0f) + '0';
+ i2c_smbus_write_byte_data(client, 0, i);
+ name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0';
if (name[i] > '9')
name[i] += 'a' - '9' - 1;
}
@@ -1527,7 +1557,8 @@ static int saa7115_probe(struct i2c_client *client,
state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
state->input = -1;
state->output = SAA7115_IPORT_ON;
state->enable = 1;
@@ -1554,41 +1585,45 @@ static int saa7115_probe(struct i2c_client *client,
break;
default:
state->ident = V4L2_IDENT_SAA7111;
- v4l_info(client, "WARNING: Chip is not known - Falling back to saa7111\n");
+ v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
}
state->audclk_freq = 48000;
- v4l_dbg(1, debug, client, "writing init values\n");
+ v4l2_dbg(1, debug, sd, "writing init values\n");
/* init to 60hz/48khz */
state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
switch (state->ident) {
case V4L2_IDENT_SAA7111:
- saa711x_writeregs(client, saa7111_init);
+ saa711x_writeregs(sd, saa7111_init);
break;
case V4L2_IDENT_SAA7113:
- saa711x_writeregs(client, saa7113_init);
+ saa711x_writeregs(sd, saa7113_init);
break;
default:
state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
- saa711x_writeregs(client, saa7115_init_auto_input);
+ saa711x_writeregs(sd, saa7115_init_auto_input);
}
if (state->ident != V4L2_IDENT_SAA7111)
- saa711x_writeregs(client, saa7115_init_misc);
- saa711x_set_v4lstd(client, V4L2_STD_NTSC);
+ saa711x_writeregs(sd, saa7115_init_misc);
+ saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
- v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n",
- saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC), saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC));
+ v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n",
+ saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC),
+ saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC));
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7115_remove(struct i2c_client *client)
+static int saa711x_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;
}
@@ -1608,9 +1643,9 @@ MODULE_DEVICE_TABLE(i2c, saa7115_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "saa7115",
.driverid = I2C_DRIVERID_SAA711X,
- .command = saa7115_command,
- .probe = saa7115_probe,
- .remove = saa7115_remove,
+ .command = saa711x_command,
+ .probe = saa711x_probe,
+ .remove = saa711x_remove,
.legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
.id_table = saa7115_id,
diff --git a/linux/drivers/media/video/saa7127.c b/linux/drivers/media/video/saa7127.c
index fd7d15305..5414b027f 100644
--- a/linux/drivers/media/video/saa7127.c
+++ b/linux/drivers/media/video/saa7127.c
@@ -53,7 +53,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/saa7127.h>
@@ -237,6 +237,7 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = {
*/
struct saa7127_state {
+ struct v4l2_subdev sd;
v4l2_std_id std;
u32 ident;
enum saa7127_input_type input_type;
@@ -256,6 +257,11 @@ struct saa7127_state {
u8 reg_61;
};
+static inline struct saa7127_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa7127_state, sd);
+}
+
static const char * const output_strs[] =
{
"S-Video + Composite",
@@ -287,32 +293,35 @@ static const char * const wss_strs[] = {
/* ----------------------------------------------------------------------- */
-static int saa7127_read(struct i2c_client *client, u8 reg)
+static int saa7127_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 saa7127_write(struct i2c_client *client, u8 reg, u8 val)
+static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
for (i = 0; i < 3; i++) {
if (i2c_smbus_write_byte_data(client, reg, val) == 0)
return 0;
}
- v4l_err(client, "I2C Write Problem\n");
+ v4l2_err(sd, "I2C Write Problem\n");
return -1;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_write_inittab(struct i2c_client *client,
+static int saa7127_write_inittab(struct v4l2_subdev *sd,
const struct i2c_reg_value *regs)
{
while (regs->reg != 0) {
- saa7127_write(client, regs->reg, regs->value);
+ saa7127_write(sd, regs->reg, regs->value);
regs++;
}
return 0;
@@ -320,16 +329,16 @@ static int saa7127_write_inittab(struct i2c_client *client,
/* ----------------------------------------------------------------------- */
-static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
int enable = (data->line != 0);
if (enable && (data->field != 0 || data->line != 16))
return -EINVAL;
if (state->vps_enable != enable) {
- v4l_dbg(1, debug, client, "Turn VPS Signal %s\n", enable ? "on" : "off");
- saa7127_write(client, 0x54, enable << 7);
+ v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off");
+ saa7127_write(sd, 0x54, enable << 7);
state->vps_enable = enable;
}
if (!enable)
@@ -340,91 +349,91 @@ static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_dat
state->vps_data[2] = data->data[9];
state->vps_data[3] = data->data[10];
state->vps_data[4] = data->data[11];
- v4l_dbg(1, debug, client, "Set VPS data %02x %02x %02x %02x %02x\n",
+ v4l2_dbg(1, debug, sd, "Set VPS data %02x %02x %02x %02x %02x\n",
state->vps_data[0], state->vps_data[1],
state->vps_data[2], state->vps_data[3],
state->vps_data[4]);
- saa7127_write(client, 0x55, state->vps_data[0]);
- saa7127_write(client, 0x56, state->vps_data[1]);
- saa7127_write(client, 0x57, state->vps_data[2]);
- saa7127_write(client, 0x58, state->vps_data[3]);
- saa7127_write(client, 0x59, state->vps_data[4]);
+ saa7127_write(sd, 0x55, state->vps_data[0]);
+ saa7127_write(sd, 0x56, state->vps_data[1]);
+ saa7127_write(sd, 0x57, state->vps_data[2]);
+ saa7127_write(sd, 0x58, state->vps_data[3]);
+ saa7127_write(sd, 0x59, state->vps_data[4]);
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_cc(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
u16 cc = data->data[1] << 8 | data->data[0];
int enable = (data->line != 0);
if (enable && (data->field != 0 || data->line != 21))
return -EINVAL;
if (state->cc_enable != enable) {
- v4l_dbg(1, debug, client,
+ v4l2_dbg(1, debug, sd,
"Turn CC %s\n", enable ? "on" : "off");
- saa7127_write(client, SAA7127_REG_CLOSED_CAPTION,
+ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
(state->xds_enable << 7) | (enable << 6) | 0x11);
state->cc_enable = enable;
}
if (!enable)
return 0;
- v4l_dbg(2, debug, client, "CC data: %04x\n", cc);
- saa7127_write(client, SAA7127_REG_LINE_21_ODD_0, cc & 0xff);
- saa7127_write(client, SAA7127_REG_LINE_21_ODD_1, cc >> 8);
+ v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc);
+ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff);
+ saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8);
state->cc_data = cc;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_xds(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
u16 xds = data->data[1] << 8 | data->data[0];
int enable = (data->line != 0);
if (enable && (data->field != 1 || data->line != 21))
return -EINVAL;
if (state->xds_enable != enable) {
- v4l_dbg(1, debug, client, "Turn XDS %s\n", enable ? "on" : "off");
- saa7127_write(client, SAA7127_REG_CLOSED_CAPTION,
+ v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off");
+ saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION,
(enable << 7) | (state->cc_enable << 6) | 0x11);
state->xds_enable = enable;
}
if (!enable)
return 0;
- v4l_dbg(2, debug, client, "XDS data: %04x\n", xds);
- saa7127_write(client, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff);
- saa7127_write(client, SAA7127_REG_LINE_21_EVEN_1, xds >> 8);
+ v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds);
+ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff);
+ saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8);
state->xds_data = xds;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_data *data)
+static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
int enable = (data->line != 0);
if (enable && (data->field != 0 || data->line != 23))
return -EINVAL;
if (state->wss_enable != enable) {
- v4l_dbg(1, debug, client, "Turn WSS %s\n", enable ? "on" : "off");
- saa7127_write(client, 0x27, enable << 7);
+ v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off");
+ saa7127_write(sd, 0x27, enable << 7);
state->wss_enable = enable;
}
if (!enable)
return 0;
- saa7127_write(client, 0x26, data->data[0]);
- saa7127_write(client, 0x27, 0x80 | (data->data[1] & 0x3f));
- v4l_dbg(1, debug, client,
+ saa7127_write(sd, 0x26, data->data[0]);
+ saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f));
+ v4l2_dbg(1, debug, sd,
"WSS mode: %s\n", wss_strs[data->data[0] & 0xf]);
state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0];
return 0;
@@ -432,18 +441,18 @@ static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_dat
/* ----------------------------------------------------------------------- */
-static int saa7127_set_video_enable(struct i2c_client *client, int enable)
+static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
if (enable) {
- v4l_dbg(1, debug, client, "Enable Video Output\n");
- saa7127_write(client, 0x2d, state->reg_2d);
- saa7127_write(client, 0x61, state->reg_61);
+ v4l2_dbg(1, debug, sd, "Enable Video Output\n");
+ saa7127_write(sd, 0x2d, state->reg_2d);
+ saa7127_write(sd, 0x61, state->reg_61);
} else {
- v4l_dbg(1, debug, client, "Disable Video Output\n");
- saa7127_write(client, 0x2d, (state->reg_2d & 0xf0));
- saa7127_write(client, 0x61, (state->reg_61 | 0xc0));
+ v4l2_dbg(1, debug, sd, "Disable Video Output\n");
+ saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0));
+ saa7127_write(sd, 0x61, (state->reg_61 | 0xc0));
}
state->video_enable = enable;
return 0;
@@ -451,32 +460,32 @@ static int saa7127_set_video_enable(struct i2c_client *client, int enable)
/* ----------------------------------------------------------------------- */
-static int saa7127_set_std(struct i2c_client *client, v4l2_std_id std)
+static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
const struct i2c_reg_value *inittab;
if (std & V4L2_STD_525_60) {
- v4l_dbg(1, debug, client, "Selecting 60 Hz video Standard\n");
+ v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n");
inittab = saa7127_init_config_60hz;
state->reg_61 = SAA7127_60HZ_DAC_CONTROL;
} else {
- v4l_dbg(1, debug, client, "Selecting 50 Hz video Standard\n");
+ v4l2_dbg(1, debug, sd, "Selecting 50 Hz video Standard\n");
inittab = saa7127_init_config_50hz;
state->reg_61 = SAA7127_50HZ_DAC_CONTROL;
}
/* Write Table */
- saa7127_write_inittab(client, inittab);
+ saa7127_write_inittab(sd, inittab);
state->std = std;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_output_type(struct i2c_client *client, int output)
+static int saa7127_set_output_type(struct v4l2_subdev *sd, int output)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
switch (output) {
case SAA7127_OUTPUT_TYPE_RGB:
@@ -512,165 +521,195 @@ static int saa7127_set_output_type(struct i2c_client *client, int output)
default:
return -EINVAL;
}
- v4l_dbg(1, debug, client,
+ v4l2_dbg(1, debug, sd,
"Selecting %s output type\n", output_strs[output]);
/* Configure Encoder */
- saa7127_write(client, 0x2d, state->reg_2d);
- saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb);
+ saa7127_write(sd, 0x2d, state->reg_2d);
+ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
state->output_type = output;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_set_input_type(struct i2c_client *client, int input)
+static int saa7127_set_input_type(struct v4l2_subdev *sd, int input)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
+ struct saa7127_state *state = to_state(sd);
switch (input) {
case SAA7127_INPUT_TYPE_NORMAL: /* avia */
- v4l_dbg(1, debug, client, "Selecting Normal Encoder Input\n");
+ v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n");
state->reg_3a_cb = 0;
break;
case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */
- v4l_dbg(1, debug, client, "Selecting Color Bar generator\n");
+ v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n");
state->reg_3a_cb = 0x80;
break;
default:
return -EINVAL;
}
- saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb);
+ saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb);
state->input_type = input;
return 0;
}
/* ----------------------------------------------------------------------- */
-static int saa7127_command(struct i2c_client *client,
- unsigned int cmd, void *arg)
+static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
{
- struct saa7127_state *state = i2c_get_clientdata(client);
- struct v4l2_format *fmt = arg;
- struct v4l2_routing *route = arg;
-
- switch (cmd) {
- case VIDIOC_INT_S_STD_OUTPUT:
- if (state->std == *(v4l2_std_id *)arg)
- break;
- return saa7127_set_std(client, *(v4l2_std_id *)arg);
-
- case VIDIOC_INT_G_STD_OUTPUT:
- *(v4l2_std_id *)arg = state->std;
- break;
+ struct saa7127_state *state = to_state(sd);
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = state->input_type;
- route->output = state->output_type;
- break;
+ if (state->std == std)
+ return 0;
+ return saa7127_set_std(sd, std);
+}
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- int rc = 0;
+static int saa7127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct saa7127_state *state = to_state(sd);
+ int rc = 0;
+
+ if (state->input_type != route->input)
+ rc = saa7127_set_input_type(sd, route->input);
+ if (rc == 0 && state->output_type != route->output)
+ rc = saa7127_set_output_type(sd, route->output);
+ return rc;
+}
- if (state->input_type != route->input)
- rc = saa7127_set_input_type(client, route->input);
- if (rc == 0 && state->output_type != route->output)
- rc = saa7127_set_output_type(client, route->output);
- return rc;
- }
+static int saa7127_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa7127_state *state = to_state(sd);
- case VIDIOC_STREAMON:
- case VIDIOC_STREAMOFF:
- if (state->video_enable == (cmd == VIDIOC_STREAMON))
- break;
- return saa7127_set_video_enable(client, cmd == VIDIOC_STREAMON);
-
- case VIDIOC_G_FMT:
- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
- return -EINVAL;
-
- memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced));
- if (state->vps_enable)
- fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS;
- if (state->wss_enable)
- fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
- if (state->cc_enable) {
- fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
- fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
- }
- fmt->fmt.sliced.service_set =
- (state->vps_enable ? V4L2_SLICED_VPS : 0) |
- (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) |
- (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0);
- break;
+ if (state->video_enable == enable)
+ return 0;
+ return saa7127_set_video_enable(sd, enable);
+}
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz");
- v4l_info(client, "Input: %s\n", state->input_type ? "color bars" : "normal");
- v4l_info(client, "Output: %s\n", state->video_enable ?
- output_strs[state->output_type] : "disabled");
- v4l_info(client, "WSS: %s\n", state->wss_enable ?
- wss_strs[state->wss_mode] : "disabled");
- v4l_info(client, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled");
- v4l_info(client, "CC: %s\n", state->cc_enable ? "enabled" : "disabled");
- break;
+static int saa7127_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct saa7127_state *state = to_state(sd);
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER)
- reg->val = saa7127_read(client, reg->reg & 0xff);
- else
- saa7127_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
-#endif
+ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+ return -EINVAL;
- case VIDIOC_INT_S_VBI_DATA:
- {
- struct v4l2_sliced_vbi_data *data = arg;
-
- switch (data->id) {
- case V4L2_SLICED_WSS_625:
- return saa7127_set_wss(client, data);
- case V4L2_SLICED_VPS:
- return saa7127_set_vps(client, data);
- case V4L2_SLICED_CAPTION_525:
- if (data->field == 0)
- return saa7127_set_cc(client, data);
- return saa7127_set_xds(client, data);
- default:
- return -EINVAL;
- }
- break;
+ memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced));
+ if (state->vps_enable)
+ fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS;
+ if (state->wss_enable)
+ fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+ if (state->cc_enable) {
+ fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
+ fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
}
+ fmt->fmt.sliced.service_set =
+ (state->vps_enable ? V4L2_SLICED_VPS : 0) |
+ (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) |
+ (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0);
+ return 0;
+}
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0);
-
+static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data)
+{
+ switch (data->id) {
+ case V4L2_SLICED_WSS_625:
+ return saa7127_set_wss(sd, data);
+ case V4L2_SLICED_VPS:
+ return saa7127_set_vps(sd, data);
+ case V4L2_SLICED_CAPTION_525:
+ if (data->field == 0)
+ return saa7127_set_cc(sd, data);
+ return saa7127_set_xds(sd, data);
default:
return -EINVAL;
}
return 0;
}
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa7127_read(sd, reg->reg & 0xff);
+ return 0;
+}
+
+static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct saa7127_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
+}
+
+static int saa7127_log_status(struct v4l2_subdev *sd)
+{
+ struct saa7127_state *state = to_state(sd);
+
+ v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz");
+ v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal");
+ v4l2_info(sd, "Output: %s\n", state->video_enable ?
+ output_strs[state->output_type] : "disabled");
+ v4l2_info(sd, "WSS: %s\n", state->wss_enable ?
+ wss_strs[state->wss_mode] : "disabled");
+ v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled");
+ v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled");
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops saa7127_core_ops = {
+ .log_status = saa7127_log_status,
+ .g_chip_ident = saa7127_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa7127_g_register,
+ .s_register = saa7127_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops saa7127_video_ops = {
+ .s_vbi_data = saa7127_s_vbi_data,
+ .g_fmt = saa7127_g_fmt,
+ .s_std_output = saa7127_s_std_output,
+ .s_routing = saa7127_s_routing,
+ .s_stream = saa7127_s_stream,
+};
+
+static const struct v4l2_subdev_ops saa7127_ops = {
+ .core = &saa7127_core_ops,
+ .video = &saa7127_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
static int saa7127_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct saa7127_state *state;
+ struct v4l2_subdev *sd;
struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */
/* Check if the adapter supports the needed features */
@@ -684,40 +723,42 @@ static int saa7127_probe(struct i2c_client *client,
v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n",
client->addr << 1);
+ state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL);
+ if (state == NULL)
+ return -ENOMEM;
+
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa7127_ops);
+
/* First test register 0: Bits 5-7 are a version ID (should be 0),
and bit 2 should also be 0.
This is rather general, so the second test is more specific and
looks at the 'ending point of burst in clock cycles' which is
0x1d after a reset and not expected to ever change. */
- if ((saa7127_read(client, 0) & 0xe4) != 0 ||
- (saa7127_read(client, 0x29) & 0x3f) != 0x1d) {
- v4l_dbg(1, debug, client, "saa7127 not found\n");
+ if ((saa7127_read(sd, 0) & 0xe4) != 0 ||
+ (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) {
+ v4l2_dbg(1, debug, sd, "saa7127 not found\n");
+ kfree(state);
return -ENODEV;
}
- state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL);
-
- if (state == NULL)
- return -ENOMEM;
-
- i2c_set_clientdata(client, state);
/* Configure Encoder */
- v4l_dbg(1, debug, client, "Configuring encoder\n");
- saa7127_write_inittab(client, saa7127_init_config_common);
- saa7127_set_std(client, V4L2_STD_NTSC);
- saa7127_set_output_type(client, SAA7127_OUTPUT_TYPE_BOTH);
- saa7127_set_vps(client, &vbi);
- saa7127_set_wss(client, &vbi);
- saa7127_set_cc(client, &vbi);
- saa7127_set_xds(client, &vbi);
+ v4l2_dbg(1, debug, sd, "Configuring encoder\n");
+ saa7127_write_inittab(sd, saa7127_init_config_common);
+ saa7127_set_std(sd, V4L2_STD_NTSC);
+ saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH);
+ saa7127_set_vps(sd, &vbi);
+ saa7127_set_wss(sd, &vbi);
+ saa7127_set_cc(sd, &vbi);
+ saa7127_set_xds(sd, &vbi);
if (test_image == 1)
/* The Encoder has an internal Colorbar generator */
/* This can be used for debugging */
- saa7127_set_input_type(client, SAA7127_INPUT_TYPE_TEST_IMAGE);
+ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE);
else
- saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL);
- saa7127_set_video_enable(client, 1);
+ saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL);
+ saa7127_set_video_enable(sd, 1);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
if (id->driver_data) { /* Chip type is already known */
@@ -729,10 +770,10 @@ static int saa7127_probe(struct i2c_client *client,
int read_result;
/* Detect if it's an saa7129 */
- read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2);
- saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa);
- if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
- saa7127_write(client, SAA7129_REG_FADE_KEY_COL2,
+ read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2);
+ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa);
+ if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) {
+ saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2,
read_result);
state->ident = V4L2_IDENT_SAA7129;
strlcpy(client->name, "saa7129", I2C_NAME_SIZE);
@@ -742,10 +783,10 @@ static int saa7127_probe(struct i2c_client *client,
}
}
- v4l_info(client, "%s found @ 0x%x (%s)\n", client->name,
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
client->addr << 1, client->adapter->name);
if (state->ident == V4L2_IDENT_SAA7129)
- saa7127_write_inittab(client, saa7129_init_config_extra);
+ saa7127_write_inittab(sd, saa7129_init_config_extra);
return 0;
}
@@ -753,9 +794,12 @@ static int saa7127_probe(struct i2c_client *client,
static int saa7127_remove(struct i2c_client *client)
{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+ v4l2_device_unregister_subdev(sd);
/* Turn off TV output */
- saa7127_set_video_enable(client, 0);
- kfree(i2c_get_clientdata(client));
+ saa7127_set_video_enable(sd, 0);
+ kfree(to_state(sd));
return 0;
}
@@ -776,7 +820,6 @@ MODULE_DEVICE_TABLE(i2c, saa7127_id);
static struct v4l2_i2c_driver_data v4l2_i2c_data = {
.name = "saa7127",
.driverid = I2C_DRIVERID_SAA7127,
- .command = saa7127_command,
.probe = saa7127_probe,
.remove = saa7127_remove,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
diff --git a/linux/drivers/media/video/saa7134/Kconfig b/linux/drivers/media/video/saa7134/Kconfig
index 7021bbf58..fc2164e28 100644
--- a/linux/drivers/media/video/saa7134/Kconfig
+++ b/linux/drivers/media/video/saa7134/Kconfig
@@ -34,9 +34,9 @@ config VIDEO_SAA7134_DVB
select DVB_NXT200X if !DVB_FE_CUSTOMISE
select DVB_TDA10086 if !DVB_FE_CUSTOMISE
select DVB_TDA826X if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_TDA827X if !DVB_FE_CUSTOMISE
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
- select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE
+ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
---help---
This adds support for DVB cards based on the
Philips saa7134 chip.
diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c
index e2dc0822b..54be8314f 100644
--- a/linux/drivers/media/video/saa7134/saa7134-cards.c
+++ b/linux/drivers/media/video/saa7134/saa7134-cards.c
@@ -4812,6 +4812,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.driver_data = SAA7134_BOARD_MD7134,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x16be, /* CTX946 analog TV, HW mpeg, DVB-T */
+ .subdevice = 0x5000, /* only analog TV and DVB-T for now */
+ .driver_data = SAA7134_BOARD_MD7134,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7130,
.subvendor = 0x1048,
.subdevice = 0x226b,
diff --git a/linux/drivers/media/video/saa717x.c b/linux/drivers/media/video/saa717x.c
index b0751c195..2d667e66a 100644
--- a/linux/drivers/media/video/saa717x.c
+++ b/linux/drivers/media/video/saa717x.c
@@ -37,7 +37,7 @@
#include <linux/videodev2.h>
#include <linux/i2c.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-i2c-drv.h>
#include "compat.h"
@@ -60,6 +60,7 @@ I2C_CLIENT_INSMOD;
#endif
struct saa717x_state {
+ struct v4l2_subdev sd;
v4l2_std_id std;
int input;
int enable;
@@ -81,6 +82,11 @@ struct saa717x_state {
int audio_input;
};
+static inline struct saa717x_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct saa717x_state, sd);
+}
+
/* ----------------------------------------------------------------------- */
/* for audio mode */
@@ -94,8 +100,9 @@ struct saa717x_state {
/* ----------------------------------------------------------------------- */
-static int saa717x_write(struct i2c_client *client, u32 reg, u32 value)
+static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
struct i2c_adapter *adap = client->adapter;
int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488;
unsigned char mm1[6];
@@ -115,20 +122,21 @@ static int saa717x_write(struct i2c_client *client, u32 reg, u32 value)
}
msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */
msg.buf = mm1;
- v4l_dbg(2, debug, client, "wrote: reg 0x%03x=%08x\n", reg, value);
+ v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value);
return i2c_transfer(adap, &msg, 1) == 1;
}
-static void saa717x_write_regs(struct i2c_client *client, u32 *data)
+static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data)
{
while (data[0] || data[1]) {
- saa717x_write(client, data[0], data[1]);
+ saa717x_write(sd, data[0], data[1]);
data += 2;
}
}
-static u32 saa717x_read(struct i2c_client *client, u32 reg)
+static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
struct i2c_adapter *adap = client->adapter;
int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528;
unsigned char mm1[2];
@@ -152,7 +160,7 @@ static u32 saa717x_read(struct i2c_client *client, u32 reg)
else
value = mm2[0] & 0xff;
- v4l_dbg(2, debug, client, "read: reg 0x%03x=0x%08x\n", reg, value);
+ v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value);
return value;
}
@@ -686,7 +694,7 @@ static u32 reg_set_audio_template[4][2] =
/* Get detected audio flags (from saa7134 driver) */
-static void get_inf_dev_status(struct i2c_client *client,
+static void get_inf_dev_status(struct v4l2_subdev *sd,
int *dual_flag, int *stereo_flag)
{
u32 reg_data3;
@@ -725,13 +733,13 @@ static void get_inf_dev_status(struct i2c_client *client,
/* (demdec status: 0x528) */
/* read current status */
- reg_data3 = saa717x_read(client, 0x0528);
+ reg_data3 = saa717x_read(sd, 0x0528);
- v4l_dbg(1, debug, client, "tvaudio thread status: 0x%x [%s%s%s]\n",
+ v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n",
reg_data3, stdres[reg_data3 & 0x1f],
(reg_data3 & 0x000020) ? ",stereo" : "",
(reg_data3 & 0x000040) ? ",dual" : "");
- v4l_dbg(1, debug, client, "detailed status: "
+ v4l2_dbg(1, debug, sd, "detailed status: "
"%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n",
(reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "",
(reg_data3 & 0x000100) ? " A2/EIAJ dual " : "",
@@ -752,51 +760,51 @@ static void get_inf_dev_status(struct i2c_client *client,
(reg_data3 & 0x100000) ? " init done " : "");
if (reg_data3 & 0x000220) {
- v4l_dbg(1, debug, client, "ST!!!\n");
+ v4l2_dbg(1, debug, sd, "ST!!!\n");
*stereo_flag = 1;
}
if (reg_data3 & 0x000140) {
- v4l_dbg(1, debug, client, "DUAL!!!\n");
+ v4l2_dbg(1, debug, sd, "DUAL!!!\n");
*dual_flag = 1;
}
}
/* regs write to set audio mode */
-static void set_audio_mode(struct i2c_client *client, int audio_mode)
+static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode)
{
- v4l_dbg(1, debug, client, "writing registers to set audio mode by set %d\n",
+ v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n",
audio_mode);
- saa717x_write(client, 0x46c, reg_set_audio_template[audio_mode][0]);
- saa717x_write(client, 0x470, reg_set_audio_template[audio_mode][1]);
+ saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]);
+ saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]);
}
/* write regs to video output level (bright,contrast,hue,sat) */
-static void set_video_output_level_regs(struct i2c_client *client,
+static void set_video_output_level_regs(struct v4l2_subdev *sd,
struct saa717x_state *decoder)
{
/* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */
- saa717x_write(client, 0x10a, decoder->bright);
+ saa717x_write(sd, 0x10a, decoder->bright);
/* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) -
0h (luminance off) 40: i2c dump
c0h (-1.0 inverse chrominance)
80h (-2.0 inverse chrominance) */
- saa717x_write(client, 0x10b, decoder->contrast);
+ saa717x_write(sd, 0x10b, decoder->contrast);
/* saturation? 7fh(max)-40h(ITU)-0h(color off)
c0h (-1.0 inverse chrominance)
80h (-2.0 inverse chrominance) */
- saa717x_write(client, 0x10c, decoder->sat);
+ saa717x_write(sd, 0x10c, decoder->sat);
/* color hue (phase) control
7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */
- saa717x_write(client, 0x10d, decoder->hue);
+ saa717x_write(sd, 0x10d, decoder->hue);
}
/* write regs to set audio volume, bass and treble */
-static int set_audio_regs(struct i2c_client *client,
+static int set_audio_regs(struct v4l2_subdev *sd,
struct saa717x_state *decoder)
{
u8 mute = 0xac; /* -84 dB */
@@ -804,8 +812,8 @@ static int set_audio_regs(struct i2c_client *client,
unsigned int work_l, work_r;
/* set SIF analog I/O select */
- saa717x_write(client, 0x0594, decoder->audio_input);
- v4l_dbg(1, debug, client, "set audio input %d\n",
+ saa717x_write(sd, 0x0594, decoder->audio_input);
+ v4l2_dbg(1, debug, sd, "set audio input %d\n",
decoder->audio_input);
/* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */
@@ -825,17 +833,17 @@ static int set_audio_regs(struct i2c_client *client,
((u8)decoder->audio_main_vol_r << 8);
}
- saa717x_write(client, 0x480, val);
+ saa717x_write(sd, 0x480, val);
/* bass and treble; go to another function */
/* set bass and treble */
val = decoder->audio_main_bass | (decoder->audio_main_treble << 8);
- saa717x_write(client, 0x488, val);
+ saa717x_write(sd, 0x488, val);
return 0;
}
/********** scaling staff ***********/
-static void set_h_prescale(struct i2c_client *client,
+static void set_h_prescale(struct v4l2_subdev *sd,
int task, int prescale)
{
static const struct {
@@ -868,107 +876,101 @@ static void set_h_prescale(struct i2c_client *client,
return;
/* horizonal prescaling */
- saa717x_write(client, 0x60 + task_shift, vals[i].xpsc);
+ saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc);
/* accumulation length */
- saa717x_write(client, 0x61 + task_shift, vals[i].xacl);
+ saa717x_write(sd, 0x61 + task_shift, vals[i].xacl);
/* level control */
- saa717x_write(client, 0x62 + task_shift,
+ saa717x_write(sd, 0x62 + task_shift,
(vals[i].xc2_1 << 3) | vals[i].xdcg);
/*FIR prefilter control */
- saa717x_write(client, 0x63 + task_shift,
+ saa717x_write(sd, 0x63 + task_shift,
(vals[i].vpfy << 2) | vals[i].vpfy);
}
/********** scaling staff ***********/
-static void set_v_scale(struct i2c_client *client, int task, int yscale)
+static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale)
{
int task_shift;
task_shift = task * 0x40;
/* Vertical scaling ratio (LOW) */
- saa717x_write(client, 0x70 + task_shift, yscale & 0xff);
+ saa717x_write(sd, 0x70 + task_shift, yscale & 0xff);
/* Vertical scaling ratio (HI) */
- saa717x_write(client, 0x71 + task_shift, yscale >> 8);
-}
-
-static int saa717x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
-{
- /* not yet implament, so saa717x_cfg_??hz_??_audio is not defined. */
- return 0;
+ saa717x_write(sd, 0x71 + task_shift, yscale >> 8);
}
-static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa717x_state *state = i2c_get_clientdata(client);
+ struct saa717x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
if (ctrl->value < 0 || ctrl->value > 255) {
- v4l_err(client, "invalid brightness setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value);
return -ERANGE;
}
state->bright = ctrl->value;
- v4l_dbg(1, debug, client, "bright:%d\n", state->bright);
- saa717x_write(client, 0x10a, state->bright);
+ v4l2_dbg(1, debug, sd, "bright:%d\n", state->bright);
+ saa717x_write(sd, 0x10a, state->bright);
break;
case V4L2_CID_CONTRAST:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid contrast setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value);
return -ERANGE;
}
state->contrast = ctrl->value;
- v4l_dbg(1, debug, client, "contrast:%d\n", state->contrast);
- saa717x_write(client, 0x10b, state->contrast);
+ v4l2_dbg(1, debug, sd, "contrast:%d\n", state->contrast);
+ saa717x_write(sd, 0x10b, state->contrast);
break;
case V4L2_CID_SATURATION:
if (ctrl->value < 0 || ctrl->value > 127) {
- v4l_err(client, "invalid saturation setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value);
return -ERANGE;
}
state->sat = ctrl->value;
- v4l_dbg(1, debug, client, "sat:%d\n", state->sat);
- saa717x_write(client, 0x10c, state->sat);
+ v4l2_dbg(1, debug, sd, "sat:%d\n", state->sat);
+ saa717x_write(sd, 0x10c, state->sat);
break;
case V4L2_CID_HUE:
if (ctrl->value < -127 || ctrl->value > 127) {
- v4l_err(client, "invalid hue setting %d\n", ctrl->value);
+ v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
return -ERANGE;
}
state->hue = ctrl->value;
- v4l_dbg(1, debug, client, "hue:%d\n", state->hue);
- saa717x_write(client, 0x10d, state->hue);
+ v4l2_dbg(1, debug, sd, "hue:%d\n", state->hue);
+ saa717x_write(sd, 0x10d, state->hue);
break;
case V4L2_CID_AUDIO_MUTE:
state->audio_main_mute = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_VOLUME:
state->audio_main_volume = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_BALANCE:
state->audio_main_balance = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_TREBLE:
state->audio_main_treble = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
case V4L2_CID_AUDIO_BASS:
state->audio_main_bass = ctrl->value;
- set_audio_regs(client, state);
+ set_audio_regs(sd, state);
break;
default:
@@ -978,9 +980,9 @@ static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
return 0;
}
-static int saa717x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa717x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct saa717x_state *state = i2c_get_clientdata(client);
+ struct saa717x_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
@@ -1109,13 +1111,15 @@ static struct v4l2_queryctrl saa717x_qctrl[] = {
},
};
-static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_state *decoder, int inp)
+static int saa717x_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
+ struct saa717x_state *decoder = to_state(sd);
+ int inp = route->input;
int is_tuner = inp & 0x80; /* tuner input flag */
inp &= 0x7f;
- v4l_dbg(1, debug, client, "decoder set input (%d)\n", inp);
+ v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", inp);
/* inputs from 0-9 are available*/
/* saa717x have mode0-mode9 but mode5 is reserved. */
if (inp < 0 || inp > 9 || inp == 5)
@@ -1125,222 +1129,197 @@ static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_sta
int input_line = inp;
decoder->input = input_line;
- v4l_dbg(1, debug, client, "now setting %s input %d\n",
+ v4l2_dbg(1, debug, sd, "now setting %s input %d\n",
input_line >= 6 ? "S-Video" : "Composite",
input_line);
/* select mode */
- saa717x_write(client, 0x102,
- (saa717x_read(client, 0x102) & 0xf0) |
+ saa717x_write(sd, 0x102,
+ (saa717x_read(sd, 0x102) & 0xf0) |
input_line);
/* bypass chrominance trap for modes 6..9 */
- saa717x_write(client, 0x109,
- (saa717x_read(client, 0x109) & 0x7f) |
+ saa717x_write(sd, 0x109,
+ (saa717x_read(sd, 0x109) & 0x7f) |
(input_line < 6 ? 0x0 : 0x80));
/* change audio_mode */
if (is_tuner) {
/* tuner */
- set_audio_mode(client, decoder->tuner_audio_mode);
+ set_audio_mode(sd, decoder->tuner_audio_mode);
} else {
/* Force to STEREO mode if Composite or
* S-Video were chosen */
- set_audio_mode(client, TUNER_AUDIO_STEREO);
+ set_audio_mode(sd, TUNER_AUDIO_STEREO);
}
/* change initialize procedure (Composite/S-Video) */
if (is_tuner)
- saa717x_write_regs(client, reg_init_tuner_input);
+ saa717x_write_regs(sd, reg_init_tuner_input);
else if (input_line >= 6)
- saa717x_write_regs(client, reg_init_svideo_input);
+ saa717x_write_regs(sd, reg_init_svideo_input);
else
- saa717x_write_regs(client, reg_init_composite_input);
+ saa717x_write_regs(sd, reg_init_composite_input);
}
return 0;
}
-static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
{
- struct saa717x_state *decoder = i2c_get_clientdata(client);
-
- v4l_dbg(1, debug, client, "IOCTL: %08x\n", cmd);
+ int i;
- switch (cmd) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- return saa717x_set_audio_clock_freq(client, *(u32 *)arg);
+ for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++)
+ if (qc->id && qc->id == saa717x_qctrl[i].id) {
+ memcpy(qc, &saa717x_qctrl[i], sizeof(*qc));
+ return 0;
+ }
+ return -EINVAL;
+}
- case VIDIOC_G_CTRL:
- return saa717x_get_v4lctrl(client, (struct v4l2_control *)arg);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_S_CTRL:
- return saa717x_set_v4lctrl(client, (struct v4l2_control *)arg);
+ if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = saa717x_read(sd, reg->reg);
+ return 0;
+}
- case VIDIOC_QUERYCTRL: {
- struct v4l2_queryctrl *qc = arg;
- int i;
+static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u16 addr = reg->reg & 0xffff;
+ u8 val = reg->val & 0xff;
- for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++)
- if (qc->id && qc->id == saa717x_qctrl[i].id) {
- memcpy(qc, &saa717x_qctrl[i], sizeof(*qc));
- return 0;
- }
+ if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
return -EINVAL;
- }
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER: {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- reg->val = saa717x_read(client, reg->reg);
- break;
- }
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ saa717x_write(sd, addr, val);
+ return 0;
+}
+#endif
- case VIDIOC_DBG_S_REGISTER: {
- struct v4l2_register *reg = arg;
- u16 addr = reg->reg & 0xffff;
- u8 val = reg->val & 0xff;
+static int saa717x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct v4l2_pix_format *pix;
+ int prescale, h_scale, v_scale;
- if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- saa717x_write(client, addr, val);
- break;
- }
-#endif
+ pix = &fmt->fmt.pix;
+ v4l2_dbg(1, debug, sd, "decoder set size\n");
- case VIDIOC_S_FMT: {
- struct v4l2_format *fmt = (struct v4l2_format *)arg;
- struct v4l2_pix_format *pix;
- int prescale, h_scale, v_scale;
-
- pix = &fmt->fmt.pix;
- v4l_dbg(1, debug, client, "decoder set size\n");
-
- /* FIXME need better bounds checking here */
- if (pix->width < 1 || pix->width > 1440)
- return -EINVAL;
- if (pix->height < 1 || pix->height > 960)
- return -EINVAL;
-
- /* scaling setting */
- /* NTSC and interlace only */
- prescale = SAA717X_NTSC_WIDTH / pix->width;
- if (prescale == 0)
- prescale = 1;
- h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width;
- /* interlace */
- v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height;
-
- /* Horizontal prescaling etc */
- set_h_prescale(client, 0, prescale);
- set_h_prescale(client, 1, prescale);
-
- /* Horizontal scaling increment */
- /* TASK A */
- saa717x_write(client, 0x6C, (u8)(h_scale & 0xFF));
- saa717x_write(client, 0x6D, (u8)((h_scale >> 8) & 0xFF));
- /* TASK B */
- saa717x_write(client, 0xAC, (u8)(h_scale & 0xFF));
- saa717x_write(client, 0xAD, (u8)((h_scale >> 8) & 0xFF));
-
- /* Vertical prescaling etc */
- set_v_scale(client, 0, v_scale);
- set_v_scale(client, 1, v_scale);
-
- /* set video output size */
- /* video number of pixels at output */
- /* TASK A */
- saa717x_write(client, 0x5C, (u8)(pix->width & 0xFF));
- saa717x_write(client, 0x5D, (u8)((pix->width >> 8) & 0xFF));
- /* TASK B */
- saa717x_write(client, 0x9C, (u8)(pix->width & 0xFF));
- saa717x_write(client, 0x9D, (u8)((pix->width >> 8) & 0xFF));
-
- /* video number of lines at output */
- /* TASK A */
- saa717x_write(client, 0x5E, (u8)(pix->height & 0xFF));
- saa717x_write(client, 0x5F, (u8)((pix->height >> 8) & 0xFF));
- /* TASK B */
- saa717x_write(client, 0x9E, (u8)(pix->height & 0xFF));
- saa717x_write(client, 0x9F, (u8)((pix->height >> 8) & 0xFF));
- break;
- }
+ /* FIXME need better bounds checking here */
+ if (pix->width < 1 || pix->width > 1440)
+ return -EINVAL;
+ if (pix->height < 1 || pix->height > 960)
+ return -EINVAL;
- case AUDC_SET_RADIO:
- decoder->radio = 1;
- break;
+ /* scaling setting */
+ /* NTSC and interlace only */
+ prescale = SAA717X_NTSC_WIDTH / pix->width;
+ if (prescale == 0)
+ prescale = 1;
+ h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width;
+ /* interlace */
+ v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height;
+
+ /* Horizontal prescaling etc */
+ set_h_prescale(sd, 0, prescale);
+ set_h_prescale(sd, 1, prescale);
+
+ /* Horizontal scaling increment */
+ /* TASK A */
+ saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF));
+ saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF));
+ saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF));
+
+ /* Vertical prescaling etc */
+ set_v_scale(sd, 0, v_scale);
+ set_v_scale(sd, 1, v_scale);
+
+ /* set video output size */
+ /* video number of pixels at output */
+ /* TASK A */
+ saa717x_write(sd, 0x5C, (u8)(pix->width & 0xFF));
+ saa717x_write(sd, 0x5D, (u8)((pix->width >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0x9C, (u8)(pix->width & 0xFF));
+ saa717x_write(sd, 0x9D, (u8)((pix->width >> 8) & 0xFF));
+
+ /* video number of lines at output */
+ /* TASK A */
+ saa717x_write(sd, 0x5E, (u8)(pix->height & 0xFF));
+ saa717x_write(sd, 0x5F, (u8)((pix->height >> 8) & 0xFF));
+ /* TASK B */
+ saa717x_write(sd, 0x9E, (u8)(pix->height & 0xFF));
+ saa717x_write(sd, 0x9F, (u8)((pix->height >> 8) & 0xFF));
+ return 0;
+}
- case VIDIOC_S_STD: {
- v4l2_std_id std = *(v4l2_std_id *) arg;
+static int saa717x_s_radio(struct v4l2_subdev *sd)
+{
+ struct saa717x_state *decoder = to_state(sd);
- v4l_dbg(1, debug, client, "decoder set norm ");
- v4l_dbg(1, debug, client, "(not yet implementd)\n");
+ decoder->radio = 1;
+ return 0;
+}
- decoder->radio = 0;
- decoder->std = std;
- break;
- }
+static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct saa717x_state *decoder = to_state(sd);
- case VIDIOC_INT_G_AUDIO_ROUTING: {
- struct v4l2_routing *route = arg;
+ v4l2_dbg(1, debug, sd, "decoder set norm ");
+ v4l2_dbg(1, debug, sd, "(not yet implementd)\n");
- route->input = decoder->audio_input;
- route->output = 0;
- break;
- }
+ decoder->radio = 0;
+ decoder->std = std;
+ return 0;
+}
- case VIDIOC_INT_S_AUDIO_ROUTING: {
- struct v4l2_routing *route = arg;
+static int saa717x_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct saa717x_state *decoder = to_state(sd);
- if (route->input < 3) { /* FIXME! --tadachi */
- decoder->audio_input = route->input;
- v4l_dbg(1, debug, client,
+ if (route->input < 3) { /* FIXME! --tadachi */
+ decoder->audio_input = route->input;
+ v4l2_dbg(1, debug, sd,
"set decoder audio input to %d\n",
decoder->audio_input);
- set_audio_regs(client, decoder);
- break;
- }
- return -ERANGE;
- }
-
- case VIDIOC_INT_S_VIDEO_ROUTING: {
- struct v4l2_routing *route = arg;
- int inp = route->input;
-
- return saa717x_set_video_input(client, decoder, inp);
+ set_audio_regs(sd, decoder);
+ return 0;
}
+ return -ERANGE;
+}
- case VIDIOC_STREAMON: {
- v4l_dbg(1, debug, client, "decoder enable output\n");
- decoder->enable = 1;
- saa717x_write(client, 0x193, 0xa6);
- break;
- }
+static int saa717x_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct saa717x_state *decoder = to_state(sd);
- case VIDIOC_STREAMOFF: {
- v4l_dbg(1, debug, client, "decoder disable output\n");
- decoder->enable = 0;
- saa717x_write(client, 0x193, 0x26); /* right? FIXME!--tadachi */
- break;
- }
+ v4l2_dbg(1, debug, sd, "decoder %s output\n",
+ enable ? "enable" : "disable");
+ decoder->enable = enable;
+ saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26);
+ return 0;
+}
- /* change audio mode */
- case VIDIOC_S_TUNER: {
- struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
- int audio_mode;
- char *mes[4] = {
- "MONO", "STEREO", "LANG1", "LANG2/SAP"
- };
+/* change audio mode */
+static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa717x_state *decoder = to_state(sd);
+ int audio_mode;
+ char *mes[4] = {
+ "MONO", "STEREO", "LANG1", "LANG2/SAP"
+ };
- audio_mode = V4L2_TUNER_MODE_STEREO;
+ audio_mode = V4L2_TUNER_MODE_STEREO;
- switch (vt->audmode) {
+ switch (vt->audmode) {
case V4L2_TUNER_MODE_MONO:
audio_mode = TUNER_AUDIO_MONO;
break;
@@ -1353,70 +1332,101 @@ static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg)
case V4L2_TUNER_MODE_LANG1:
audio_mode = TUNER_AUDIO_LANG1;
break;
- }
-
- v4l_dbg(1, debug, client, "change audio mode to %s\n",
- mes[audio_mode]);
- decoder->tuner_audio_mode = audio_mode;
- /* The registers are not changed here. */
- /* See DECODER_ENABLE_OUTPUT section. */
- set_audio_mode(client, decoder->tuner_audio_mode);
- break;
}
- case VIDIOC_G_TUNER: {
- struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
- int dual_f, stereo_f;
+ v4l2_dbg(1, debug, sd, "change audio mode to %s\n",
+ mes[audio_mode]);
+ decoder->tuner_audio_mode = audio_mode;
+ /* The registers are not changed here. */
+ /* See DECODER_ENABLE_OUTPUT section. */
+ set_audio_mode(sd, decoder->tuner_audio_mode);
+ return 0;
+}
- if (decoder->radio)
- break;
- get_inf_dev_status(client, &dual_f, &stereo_f);
+static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct saa717x_state *decoder = to_state(sd);
+ int dual_f, stereo_f;
- v4l_dbg(1, debug, client, "DETECT==st:%d dual:%d\n",
- stereo_f, dual_f);
+ if (decoder->radio)
+ return 0;
+ get_inf_dev_status(sd, &dual_f, &stereo_f);
- /* mono */
- if ((dual_f == 0) && (stereo_f == 0)) {
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==MONO\n");
- }
+ v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n",
+ stereo_f, dual_f);
- /* stereo */
- if (stereo_f == 1) {
- if (vt->audmode == V4L2_TUNER_MODE_STEREO ||
- vt->audmode == V4L2_TUNER_MODE_LANG1) {
- vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
- v4l_dbg(1, debug, client, "DETECT==ST(ST)\n");
- } else {
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==ST(MONO)\n");
- }
- }
+ /* mono */
+ if ((dual_f == 0) && (stereo_f == 0)) {
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==MONO\n");
+ }
- /* dual */
- if (dual_f == 1) {
- if (vt->audmode == V4L2_TUNER_MODE_LANG2) {
- vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==DUAL1\n");
- } else {
- vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO;
- v4l_dbg(1, debug, client, "DETECT==DUAL2\n");
- }
+ /* stereo */
+ if (stereo_f == 1) {
+ if (vt->audmode == V4L2_TUNER_MODE_STEREO ||
+ vt->audmode == V4L2_TUNER_MODE_LANG1) {
+ vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n");
+ } else {
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n");
}
- break;
}
- case VIDIOC_LOG_STATUS:
- /* not yet implemented */
- break;
-
- default:
- return -EINVAL;
+ /* dual */
+ if (dual_f == 1) {
+ if (vt->audmode == V4L2_TUNER_MODE_LANG2) {
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n");
+ } else {
+ vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO;
+ v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n");
+ }
}
-
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 = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = saa717x_g_register,
+ .s_register = saa717x_s_register,
+#endif
+ .queryctrl = saa717x_queryctrl,
+ .g_ctrl = saa717x_g_ctrl,
+ .s_ctrl = saa717x_s_ctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = {
+ .g_tuner = saa717x_g_tuner,
+ .s_tuner = saa717x_s_tuner,
+ .s_std = saa717x_s_std,
+ .s_radio = saa717x_s_radio,
+};
+
+static const struct v4l2_subdev_video_ops saa717x_video_ops = {
+ .s_routing = saa717x_s_video_routing,
+ .s_fmt = saa717x_s_fmt,
+ .s_stream = saa717x_s_stream,
+};
+
+static const struct v4l2_subdev_audio_ops saa717x_audio_ops = {
+ .s_routing = saa717x_s_audio_routing,
+};
+
+static const struct v4l2_subdev_ops saa717x_ops = {
+ .core = &saa717x_core_ops,
+ .tuner = &saa717x_tuner_ops,
+ .audio = &saa717x_audio_ops,
+ .video = &saa717x_video_ops,
+};
+
/* ----------------------------------------------------------------------- */
@@ -1427,6 +1437,7 @@ static int saa717x_probe(struct i2c_client *client,
const struct i2c_device_id *did)
{
struct saa717x_state *decoder;
+ struct v4l2_subdev *sd;
u8 id = 0;
char *p = "";
@@ -1434,16 +1445,24 @@ static int saa717x_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
+ decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL);
+ if (decoder == NULL)
+ return -ENOMEM;
+
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &saa717x_ops);
+
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
snprintf(client->name, sizeof(client->name) - 1, "saa717x");
#endif
- if (saa717x_write(client, 0x5a4, 0xfe) &&
- saa717x_write(client, 0x5a5, 0x0f) &&
- saa717x_write(client, 0x5a6, 0x00) &&
- saa717x_write(client, 0x5a7, 0x01))
- id = saa717x_read(client, 0x5a0);
+ if (saa717x_write(sd, 0x5a4, 0xfe) &&
+ saa717x_write(sd, 0x5a5, 0x0f) &&
+ saa717x_write(sd, 0x5a6, 0x00) &&
+ saa717x_write(sd, 0x5a7, 0x01))
+ id = saa717x_read(sd, 0x5a0);
if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) {
- v4l_dbg(1, debug, client, "saa717x not found (id=%02x)\n", id);
+ v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id);
+ kfree(decoder);
return -ENODEV;
}
if (id == 0xc2)
@@ -1454,14 +1473,8 @@ static int saa717x_probe(struct i2c_client *client,
p = "saa7174HL";
else
p = "saa7171";
- v4l_info(client, "%s found @ 0x%x (%s)\n", p,
+ v4l2_info(sd, "%s found @ 0x%x (%s)\n", p,
client->addr << 1, client->adapter->name);
-
- decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL);
- i2c_set_clientdata(client, decoder);
-
- if (decoder == NULL)
- return -ENOMEM;
decoder->std = V4L2_STD_NTSC;
decoder->input = -1;
decoder->enable = 1;
@@ -1490,15 +1503,15 @@ static int saa717x_probe(struct i2c_client *client,
decoder->audio_main_volume =
(decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40));
- v4l_dbg(1, debug, client, "writing init values\n");
+ v4l2_dbg(1, debug, sd, "writing init values\n");
/* FIXME!! */
- saa717x_write_regs(client, reg_init_initialize);
- set_video_output_level_regs(client, decoder);
+ saa717x_write_regs(sd, reg_init_initialize);
+ set_video_output_level_regs(sd, decoder);
/* set bass,treble to 0db 20041101 K.Ohta */
decoder->audio_main_bass = 0;
decoder->audio_main_treble = 0;
- set_audio_regs(client, decoder);
+ set_audio_regs(sd, decoder);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(2*HZ);
@@ -1507,7 +1520,10 @@ static int saa717x_probe(struct i2c_client *client,
static int saa717x_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;
}
diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c
index e2e816213..bd2a1754b 100644
--- a/linux/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c
@@ -448,7 +448,7 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
__u32 pixfmt, struct v4l2_rect *rect)
{
- const struct soc_camera_data_format *cam_fmt;
+ const struct soc_camera_data_format *cam_fmt = NULL;
int ret;
/*
diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c
index 5dd4bd660..e649f2eb7 100644
--- a/linux/drivers/media/video/tuner-core.c
+++ b/linux/drivers/media/video/tuner-core.c
@@ -19,7 +19,7 @@
#include <linux/videodev.h>
#include <media/tuner.h>
#include <media/tuner-types.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "mt20xx.h"
@@ -79,6 +79,7 @@ struct tuner {
/* device */
struct dvb_frontend fe;
struct i2c_client *i2c;
+ struct v4l2_subdev sd;
struct list_head list;
unsigned int using_v4l2:1;
@@ -96,6 +97,11 @@ struct tuner {
const char *name;
};
+static inline struct tuner *to_tuner(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct tuner, sd);
+}
+
/* standard i2c insmod options */
static unsigned short normal_i2c[] = {
#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE))
@@ -214,7 +220,7 @@ static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg)
static void tuner_status(struct dvb_frontend *fe);
-static struct analog_demod_ops tuner_core_ops = {
+static struct analog_demod_ops tuner_analog_ops = {
.set_params = fe_set_params,
.standby = fe_standby,
.has_signal = fe_has_signal,
@@ -225,7 +231,7 @@ static struct analog_demod_ops tuner_core_ops = {
/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */
static void set_tv_freq(struct i2c_client *c, unsigned int freq)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
struct analog_parameters params = {
@@ -260,7 +266,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq)
static void set_radio_freq(struct i2c_client *c, unsigned int freq)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
struct analog_parameters params = {
@@ -295,7 +301,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq)
static void set_freq(struct i2c_client *c, unsigned long freq)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
switch (t->mode) {
case V4L2_TUNER_RADIO:
@@ -348,7 +354,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
unsigned int new_mode_mask, unsigned int new_config,
int (*tuner_callback) (void *dev, int component, int cmd, int arg))
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
unsigned char buffer[4];
@@ -471,7 +477,7 @@ static void set_type(struct i2c_client *c, unsigned int type,
t->name = fe_tuner_ops->info.name;
t->fe.analog_demod_priv = t;
- memcpy(analog_ops, &tuner_core_ops,
+ memcpy(analog_ops, &tuner_analog_ops,
sizeof(struct analog_demod_ops));
} else {
@@ -516,7 +522,7 @@ attach_failed:
static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) &&
(t->mode_mask & tun_setup->mode_mask))) ||
@@ -758,43 +764,56 @@ static inline int check_v4l2(struct tuner *t)
return 0;
}
-static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type)
{
- struct tuner *t = i2c_get_clientdata(client);
- struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
+ type->type,
+ type->addr,
+ type->mode_mask,
+ type->config);
+
+ set_addr(client, type);
+ return 0;
+}
+
+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)
+ return 0;
+ if (t->radio_freq)
+ set_freq(client, t->radio_freq);
+ return 0;
+}
+
+static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby)
+{
+ struct tuner *t = to_tuner(sd);
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
- if (tuner_debug > 1) {
- v4l_i2c_print_ioctl(client,cmd);
- printk("\n");
- }
+ if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL)
+ return 0;
+ t->mode = T_STANDBY;
+ if (analog_ops->standby)
+ analog_ops->standby(&t->fe);
+ return 0;
+}
- switch (cmd) {
- /* --- configuration --- */
- case TUNER_SET_TYPE_ADDR:
- tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n",
- ((struct tuner_setup *)arg)->type,
- ((struct tuner_setup *)arg)->addr,
- ((struct tuner_setup *)arg)->mode_mask,
- ((struct tuner_setup *)arg)->config);
-
- set_addr(client, (struct tuner_setup *)arg);
- break;
- case AUDC_SET_RADIO:
- if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
- == -EINVAL)
- return 0;
- if (t->radio_freq)
- set_freq(client, t->radio_freq);
- break;
- case TUNER_SET_STANDBY:
- if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL)
- return 0;
- t->mode = T_STANDBY;
- if (analog_ops->standby)
- analog_ops->standby(&t->fe);
- break;
#ifdef CONFIG_VIDEO_ALLOW_V4L1
+static int tuner_ioctl(struct v4l2_subdev *sd, 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;
@@ -907,149 +926,172 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
}
return 0;
}
+ }
+ return -ENOIOCTLCMD;
+}
#endif
- case TUNER_SET_CONFIG:
- {
- struct v4l2_priv_tun_config *cfg = arg;
- if (t->type != cfg->tuner)
- break;
+static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
- if (analog_ops->set_config) {
- analog_ops->set_config(&t->fe, cfg->priv);
- break;
- }
+ if (t->type != cfg->tuner)
+ return 0;
- tuner_dbg("Tuner frontend module has no way to set config\n");
- break;
+ if (analog_ops->set_config) {
+ analog_ops->set_config(&t->fe, cfg->priv);
+ return 0;
}
- /* --- v4l ioctls --- */
- /* take care: bttv does userspace copying, we'll get a
- kernel pointer here... */
- case VIDIOC_S_STD:
- {
- v4l2_std_id *id = arg;
- if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
- == -EINVAL)
- return 0;
+ tuner_dbg("Tuner frontend module has no way to set config\n");
+ return 0;
+}
- switch_v4l2();
+/* --- v4l ioctls --- */
+/* take care: bttv does userspace copying, we'll get a
+ kernel pointer here... */
+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);
- t->std = *id;
- tuner_fixup_std(t);
- if (t->tv_freq)
- set_freq(client, t->tv_freq);
- break;
- }
- case VIDIOC_S_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
+ == -EINVAL)
+ return 0;
- if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
- == -EINVAL)
- return 0;
- switch_v4l2();
- set_freq(client,f->frequency);
+ switch_v4l2();
- break;
- }
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *f = arg;
+ t->std = std;
+ tuner_fixup_std(t);
+ if (t->tv_freq)
+ set_freq(client, t->tv_freq);
+ return 0;
+}
- if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL)
- return 0;
- switch_v4l2();
- f->type = t->mode;
- if (fe_tuner_ops->get_frequency) {
- u32 abs_freq;
-
- fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
- f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
- (abs_freq * 2 + 125/2) / 125 :
- (abs_freq + 62500/2) / 62500;
- break;
- }
- f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
- t->radio_freq : t->tv_freq;
- break;
- }
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *tuner = arg;
+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 (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL)
- return 0;
- switch_v4l2();
-
- tuner->type = t->mode;
- if (analog_ops->get_afc)
- tuner->afc = analog_ops->get_afc(&t->fe);
- if (t->mode == V4L2_TUNER_ANALOG_TV)
- tuner->capability |= V4L2_TUNER_CAP_NORM;
- if (t->mode != V4L2_TUNER_RADIO) {
- tuner->rangelow = tv_range[0] * 16;
- tuner->rangehigh = tv_range[1] * 16;
- break;
- }
+ if (set_mode(client, t, f->type, "VIDIOC_S_FREQUENCY")
+ == -EINVAL)
+ return 0;
+ switch_v4l2();
+ set_freq(client, f->frequency);
- /* radio mode */
- tuner->rxsubchans =
- V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- if (fe_tuner_ops->get_status) {
- u32 tuner_status;
-
- fe_tuner_ops->get_status(&t->fe, &tuner_status);
- tuner->rxsubchans =
- (tuner_status & TUNER_STATUS_STEREO) ?
- V4L2_TUNER_SUB_STEREO :
- V4L2_TUNER_SUB_MONO;
- } else {
- if (analog_ops->is_stereo) {
- tuner->rxsubchans =
- analog_ops->is_stereo(&t->fe) ?
- V4L2_TUNER_SUB_STEREO :
- V4L2_TUNER_SUB_MONO;
- }
- }
- if (analog_ops->has_signal)
- tuner->signal = analog_ops->has_signal(&t->fe);
- tuner->capability |=
- V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
- tuner->audmode = t->audmode;
- tuner->rangelow = radio_range[0] * 16000;
- tuner->rangehigh = radio_range[1] * 16000;
- break;
- }
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *tuner = arg;
+ return 0;
+}
- if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL)
- return 0;
+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;
- switch_v4l2();
+ if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL)
+ return 0;
+ switch_v4l2();
+ f->type = t->mode;
+ if (fe_tuner_ops->get_frequency) {
+ u32 abs_freq;
+
+ fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
+ f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
+ (abs_freq * 2 + 125/2) / 125 :
+ (abs_freq + 62500/2) / 62500;
+ return 0;
+ }
+ f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
+ t->radio_freq : t->tv_freq;
+ return 0;
+}
- /* do nothing unless we're a radio tuner */
- if (t->mode != V4L2_TUNER_RADIO)
- break;
- t->audmode = tuner->audmode;
- set_radio_freq(client, t->radio_freq);
- break;
+static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct tuner *t = to_tuner(sd);
+ 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)
+ return 0;
+ switch_v4l2();
+
+ vt->type = t->mode;
+ if (analog_ops->get_afc)
+ vt->afc = analog_ops->get_afc(&t->fe);
+ if (t->mode == V4L2_TUNER_ANALOG_TV)
+ vt->capability |= V4L2_TUNER_CAP_NORM;
+ if (t->mode != V4L2_TUNER_RADIO) {
+ vt->rangelow = tv_range[0] * 16;
+ vt->rangehigh = tv_range[1] * 16;
+ return 0;
+ }
+
+ /* radio mode */
+ vt->rxsubchans =
+ V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ if (fe_tuner_ops->get_status) {
+ u32 tuner_status;
+
+ fe_tuner_ops->get_status(&t->fe, &tuner_status);
+ vt->rxsubchans =
+ (tuner_status & TUNER_STATUS_STEREO) ?
+ V4L2_TUNER_SUB_STEREO :
+ V4L2_TUNER_SUB_MONO;
+ } else {
+ if (analog_ops->is_stereo) {
+ vt->rxsubchans =
+ analog_ops->is_stereo(&t->fe) ?
+ V4L2_TUNER_SUB_STEREO :
+ V4L2_TUNER_SUB_MONO;
}
- case VIDIOC_LOG_STATUS:
- if (analog_ops->tuner_status)
- analog_ops->tuner_status(&t->fe);
- break;
}
+ if (analog_ops->has_signal)
+ vt->signal = analog_ops->has_signal(&t->fe);
+ vt->capability |=
+ V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+ vt->audmode = t->audmode;
+ vt->rangelow = radio_range[0] * 16000;
+ vt->rangehigh = radio_range[1] * 16000;
+ return 0;
+}
+
+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)
+ return 0;
+
+ switch_v4l2();
+
+ /* do nothing unless we're a radio tuner */
+ if (t->mode != V4L2_TUNER_RADIO)
+ return 0;
+ t->audmode = vt->audmode;
+ set_radio_freq(client, t->radio_freq);
+ return 0;
+}
+
+static int tuner_log_status(struct v4l2_subdev *sd)
+{
+ struct tuner *t = to_tuner(sd);
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+ if (analog_ops->tuner_status)
+ analog_ops->tuner_status(&t->fe);
return 0;
}
+static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
+{
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
static int tuner_suspend(struct i2c_client *c, pm_message_t state)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
tuner_dbg("suspend\n");
/* FIXME: power down ??? */
@@ -1058,7 +1100,7 @@ static int tuner_suspend(struct i2c_client *c, pm_message_t state)
static int tuner_resume(struct i2c_client *c)
{
- struct tuner *t = i2c_get_clientdata(c);
+ struct tuner *t = to_tuner(i2c_get_clientdata(c));
tuner_dbg("resume\n");
if (V4L2_TUNER_RADIO == t->mode) {
@@ -1071,6 +1113,30 @@ static int tuner_resume(struct i2c_client *c)
return 0;
}
+/* ----------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops tuner_core_ops = {
+ .log_status = tuner_log_status,
+ .s_standby = tuner_s_standby,
+ .ioctl = tuner_ioctl,
+};
+
+static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
+ .s_std = tuner_s_std,
+ .s_radio = tuner_s_radio,
+ .g_tuner = tuner_g_tuner,
+ .s_tuner = tuner_s_tuner,
+ .s_frequency = tuner_s_frequency,
+ .g_frequency = tuner_g_frequency,
+ .s_type_addr = tuner_s_type_addr,
+ .s_config = tuner_s_config,
+};
+
+static const struct v4l2_subdev_ops tuner_ops = {
+ .core = &tuner_core_ops,
+ .tuner = &tuner_tuner_ops,
+};
+
/* ---------------------------------------------------------------------- */
static LIST_HEAD(tuner_list);
@@ -1119,9 +1185,9 @@ static int tuner_probe(struct i2c_client *client,
t = kzalloc(sizeof(struct tuner), GFP_KERNEL);
if (NULL == t)
return -ENOMEM;
+ v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops);
t->i2c = client;
t->name = "(tuner unset)";
- i2c_set_clientdata(client, t);
t->type = UNSET;
t->audmode = V4L2_TUNER_MODE_STEREO;
t->mode_mask = T_UNINITIALIZED;
@@ -1271,8 +1337,9 @@ static int tuner_legacy_probe(struct i2c_adapter *adap)
static int tuner_remove(struct i2c_client *client)
{
- struct tuner *t = i2c_get_clientdata(client);
+ struct tuner *t = to_tuner(i2c_get_clientdata(client));
+ v4l2_device_unregister_subdev(&t->sd);
tuner_detach(&t->fe);
t->fe.analog_demod_priv = NULL;
diff --git a/linux/drivers/media/video/tvp514x.c b/linux/drivers/media/video/tvp514x.c
new file mode 100644
index 000000000..ac9aa40d0
--- /dev/null
+++ b/linux/drivers/media/video/tvp514x.c
@@ -0,0 +1,1569 @@
+/*
+ * drivers/media/video/tvp514x.c
+ *
+ * TI TVP5146/47 decoder driver
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.com>
+ *
+ * This package 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.
+ *
+ * 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/i2c.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-int-device.h>
+#include <media/tvp514x.h>
+
+#include "tvp514x_regs.h"
+
+/* Module Name */
+#define TVP514X_MODULE_NAME "tvp514x"
+
+/* Private macros for TVP */
+#define I2C_RETRY_COUNT (5)
+#define LOCK_RETRY_COUNT (5)
+#define LOCK_RETRY_DELAY (200)
+
+/* Debug functions */
+static int debug;
+module_param(debug, bool, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+#define dump_reg(client, reg, val) \
+ do { \
+ val = tvp514x_read_reg(client, reg); \
+ v4l_info(client, "Reg(0x%.2X): 0x%.2X\n", reg, val); \
+ } while (0)
+
+/**
+ * enum tvp514x_std - enum for supported standards
+ */
+enum tvp514x_std {
+ STD_NTSC_MJ = 0,
+ STD_PAL_BDGHIN,
+ STD_INVALID
+};
+
+/**
+ * enum tvp514x_state - enum for different decoder states
+ */
+enum tvp514x_state {
+ STATE_NOT_DETECTED,
+ STATE_DETECTED
+};
+
+/**
+ * struct tvp514x_std_info - Structure to store standard informations
+ * @width: Line width in pixels
+ * @height:Number of active lines
+ * @video_std: Value to write in REG_VIDEO_STD register
+ * @standard: v4l2 standard structure information
+ */
+struct tvp514x_std_info {
+ unsigned long width;
+ unsigned long height;
+ u8 video_std;
+ struct v4l2_standard standard;
+};
+
+/**
+ * struct tvp514x_decoded - TVP5146/47 decoder object
+ * @v4l2_int_device: Slave handle
+ * @pdata: Board specific
+ * @client: I2C client data
+ * @id: Entry from I2C table
+ * @ver: Chip version
+ * @state: TVP5146/47 decoder state - detected or not-detected
+ * @pix: Current pixel format
+ * @num_fmts: Number of formats
+ * @fmt_list: Format list
+ * @current_std: Current standard
+ * @num_stds: Number of standards
+ * @std_list: Standards list
+ * @route: input and output routing at chip level
+ */
+struct tvp514x_decoder {
+ struct v4l2_int_device *v4l2_int_device;
+ const struct tvp514x_platform_data *pdata;
+ struct i2c_client *client;
+
+ struct i2c_device_id *id;
+
+ int ver;
+ enum tvp514x_state state;
+
+ struct v4l2_pix_format pix;
+ int num_fmts;
+ const struct v4l2_fmtdesc *fmt_list;
+
+ enum tvp514x_std current_std;
+ int num_stds;
+ struct tvp514x_std_info *std_list;
+
+ struct v4l2_routing route;
+};
+
+/* TVP514x default register values */
+static struct tvp514x_reg tvp514x_reg_list[] = {
+ {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */
+ {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F},
+ {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+ {TOK_SKIP, REG_AUTOSWITCH_MASK, 0x3F},
+ {TOK_WRITE, REG_COLOR_KILLER, 0x10},
+ {TOK_WRITE, REG_LUMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL2, 0x00},
+ {TOK_WRITE, REG_LUMA_CONTROL3, 0x02},
+ {TOK_WRITE, REG_BRIGHTNESS, 0x80},
+ {TOK_WRITE, REG_CONTRAST, 0x80},
+ {TOK_WRITE, REG_SATURATION, 0x80},
+ {TOK_WRITE, REG_HUE, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL1, 0x00},
+ {TOK_WRITE, REG_CHROMA_CONTROL2, 0x0E},
+ {TOK_SKIP, 0x0F, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_COMP_PR_SATURATION, 0x80},
+ {TOK_WRITE, REG_COMP_Y_CONTRAST, 0x80},
+ {TOK_WRITE, REG_COMP_PB_SATURATION, 0x80},
+ {TOK_SKIP, 0x13, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_COMP_Y_BRIGHTNESS, 0x80},
+ {TOK_SKIP, 0x15, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_AVID_START_PIXEL_LSB, 0x55}, /* NTSC timing */
+ {TOK_SKIP, REG_AVID_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_LSB, 0x25},
+ {TOK_SKIP, REG_AVID_STOP_PIXEL_MSB, 0x03},
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_LSB, 0x00}, /* NTSC timing */
+ {TOK_SKIP, REG_HSYNC_START_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_LSB, 0x40},
+ {TOK_SKIP, REG_HSYNC_STOP_PIXEL_MSB, 0x00},
+ {TOK_SKIP, REG_VSYNC_START_LINE_LSB, 0x04}, /* NTSC timing */
+ {TOK_SKIP, REG_VSYNC_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_LSB, 0x07},
+ {TOK_SKIP, REG_VSYNC_STOP_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VBLK_START_LINE_LSB, 0x01}, /* NTSC timing */
+ {TOK_SKIP, REG_VBLK_START_LINE_MSB, 0x00},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_LSB, 0x15},
+ {TOK_SKIP, REG_VBLK_STOP_LINE_MSB, 0x00},
+ {TOK_SKIP, 0x26, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x27, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_FAST_SWTICH_CONTROL, 0xCC},
+ {TOK_SKIP, 0x29, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_FAST_SWTICH_SCART_DELAY, 0x00},
+ {TOK_SKIP, 0x2B, 0x00}, /* Reserved */
+ {TOK_SKIP, REG_SCART_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_DELAY, 0x00},
+ {TOK_SKIP, REG_CTI_CONTROL, 0x00},
+ {TOK_SKIP, 0x2F, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x30, 0x00}, /* Reserved */
+ {TOK_SKIP, 0x31, 0x00}, /* Reserved */
+ {TOK_WRITE, REG_SYNC_CONTROL, 0x00}, /* HS, VS active high */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER1, 0x00}, /* 10-bit BT.656 */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER2, 0x11}, /* Enable clk & data */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER3, 0xEE}, /* Enable AVID & FLD */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER4, 0xAF}, /* Enable VS & HS */
+ {TOK_WRITE, REG_OUTPUT_FORMATTER5, 0xFF},
+ {TOK_WRITE, REG_OUTPUT_FORMATTER6, 0xFF},
+ {TOK_WRITE, REG_CLEAR_LOST_LOCK, 0x01}, /* Clear status */
+ {TOK_TERM, 0, 0},
+};
+
+/* List of image formats supported by TVP5146/47 decoder
+ * Currently we are using 8 bit mode only, but can be
+ * extended to 10/20 bit mode.
+ */
+static const struct v4l2_fmtdesc tvp514x_fmt_list[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = 0,
+ .description = "8-bit UYVY 4:2:2 Format",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ },
+};
+
+/*
+ * Supported standards -
+ *
+ * Currently supports two standards only, need to add support for rest of the
+ * modes, like SECAM, etc...
+ */
+static struct tvp514x_std_info tvp514x_std_list[] = {
+ /* Standard: STD_NTSC_MJ */
+ [STD_NTSC_MJ] = {
+ .width = NTSC_NUM_ACTIVE_PIXELS,
+ .height = NTSC_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_NTSC_MJ_BIT,
+ .standard = {
+ .index = 0,
+ .id = V4L2_STD_NTSC,
+ .name = "NTSC",
+ .frameperiod = {1001, 30000},
+ .framelines = 525
+ },
+ /* Standard: STD_PAL_BDGHIN */
+ },
+ [STD_PAL_BDGHIN] = {
+ .width = PAL_NUM_ACTIVE_PIXELS,
+ .height = PAL_NUM_ACTIVE_LINES,
+ .video_std = VIDEO_STD_PAL_BDGHIN_BIT,
+ .standard = {
+ .index = 1,
+ .id = V4L2_STD_PAL,
+ .name = "PAL",
+ .frameperiod = {1, 25},
+ .framelines = 625
+ },
+ },
+ /* Standard: need to add for additional standard */
+};
+/*
+ * Control structure for Auto Gain
+ * This is temporary data, will get replaced once
+ * v4l2_ctrl_query_fill supports it.
+ */
+static const struct v4l2_queryctrl tvp514x_autogain_ctrl = {
+ .id = V4L2_CID_AUTOGAIN,
+ .name = "Gain, Automatic",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 1,
+};
+
+/*
+ * Read a value from a register in an TVP5146/47 decoder device.
+ * Returns value read if successful, or non-zero (-1) otherwise.
+ */
+static int tvp514x_read_reg(struct i2c_client *client, u8 reg)
+{
+ int err;
+ int retry = 0;
+read_again:
+
+ err = i2c_smbus_read_byte_data(client, reg);
+ if (err == -1) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l_warn(client, "Read: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto read_again;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * Write a value to a register in an TVP5146/47 decoder device.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ int err;
+ int retry = 0;
+write_again:
+
+ err = i2c_smbus_write_byte_data(client, reg, val);
+ if (err) {
+ if (retry <= I2C_RETRY_COUNT) {
+ v4l_warn(client, "Write: retry ... %d\n", retry);
+ retry++;
+ msleep_interruptible(10);
+ goto write_again;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * tvp514x_write_regs : Initializes a list of TVP5146/47 registers
+ * if token is TOK_TERM, then entire write operation terminates
+ * if token is TOK_DELAY, then a delay of 'val' msec is introduced
+ * if token is TOK_SKIP, then the register write is skipped
+ * if token is TOK_WRITE, then the register write is performed
+ *
+ * reglist - list of registers to be written
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_write_regs(struct i2c_client *client,
+ const struct tvp514x_reg reglist[])
+{
+ int err;
+ const struct tvp514x_reg *next = reglist;
+
+ for (; next->token != TOK_TERM; next++) {
+ if (next->token == TOK_DELAY) {
+ msleep(next->val);
+ continue;
+ }
+
+ if (next->token == TOK_SKIP)
+ continue;
+
+ err = tvp514x_write_reg(client, next->reg, (u8) next->val);
+ if (err) {
+ v4l_err(client, "Write failed. Err[%d]\n", err);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/*
+ * tvp514x_get_current_std:
+ * Returns the current standard detected by TVP5146/47
+ */
+static enum tvp514x_std tvp514x_get_current_std(struct tvp514x_decoder
+ *decoder)
+{
+ u8 std, std_status;
+
+ std = tvp514x_read_reg(decoder->client, REG_VIDEO_STD);
+ if ((std & VIDEO_STD_MASK) == VIDEO_STD_AUTO_SWITCH_BIT) {
+ /* use the standard status register */
+ std_status = tvp514x_read_reg(decoder->client,
+ REG_VIDEO_STD_STATUS);
+ } else
+ std_status = std; /* use the standard register itself */
+
+ switch (std_status & VIDEO_STD_MASK) {
+ case VIDEO_STD_NTSC_MJ_BIT:
+ return STD_NTSC_MJ;
+
+ case VIDEO_STD_PAL_BDGHIN_BIT:
+ return STD_PAL_BDGHIN;
+
+ default:
+ return STD_INVALID;
+ }
+
+ return STD_INVALID;
+}
+
+/*
+ * TVP5146/47 register dump function
+ */
+static void tvp514x_reg_dump(struct tvp514x_decoder *decoder)
+{
+ u8 value;
+
+ dump_reg(decoder->client, REG_INPUT_SEL, value);
+ dump_reg(decoder->client, REG_AFE_GAIN_CTRL, value);
+ dump_reg(decoder->client, REG_VIDEO_STD, value);
+ dump_reg(decoder->client, REG_OPERATION_MODE, value);
+ dump_reg(decoder->client, REG_COLOR_KILLER, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL1, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL2, value);
+ dump_reg(decoder->client, REG_LUMA_CONTROL3, value);
+ dump_reg(decoder->client, REG_BRIGHTNESS, value);
+ dump_reg(decoder->client, REG_CONTRAST, value);
+ dump_reg(decoder->client, REG_SATURATION, value);
+ dump_reg(decoder->client, REG_HUE, value);
+ dump_reg(decoder->client, REG_CHROMA_CONTROL1, value);
+ dump_reg(decoder->client, REG_CHROMA_CONTROL2, value);
+ dump_reg(decoder->client, REG_COMP_PR_SATURATION, value);
+ dump_reg(decoder->client, REG_COMP_Y_CONTRAST, value);
+ dump_reg(decoder->client, REG_COMP_PB_SATURATION, value);
+ dump_reg(decoder->client, REG_COMP_Y_BRIGHTNESS, value);
+ dump_reg(decoder->client, REG_AVID_START_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_AVID_START_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_AVID_STOP_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_AVID_STOP_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_HSYNC_START_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_HSYNC_START_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_LSB, value);
+ dump_reg(decoder->client, REG_HSYNC_STOP_PIXEL_MSB, value);
+ dump_reg(decoder->client, REG_VSYNC_START_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VSYNC_START_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VSYNC_STOP_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VSYNC_STOP_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VBLK_START_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VBLK_START_LINE_MSB, value);
+ dump_reg(decoder->client, REG_VBLK_STOP_LINE_LSB, value);
+ dump_reg(decoder->client, REG_VBLK_STOP_LINE_MSB, value);
+ dump_reg(decoder->client, REG_SYNC_CONTROL, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER1, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER2, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER3, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER4, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER5, value);
+ dump_reg(decoder->client, REG_OUTPUT_FORMATTER6, value);
+ dump_reg(decoder->client, REG_CLEAR_LOST_LOCK, value);
+}
+
+/*
+ * Configure the TVP5146/47 with the current register settings
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int tvp514x_configure(struct tvp514x_decoder *decoder)
+{
+ int err;
+
+ /* common register initialization */
+ err =
+ tvp514x_write_regs(decoder->client, tvp514x_reg_list);
+ if (err)
+ return err;
+
+ if (debug)
+ tvp514x_reg_dump(decoder);
+
+ return 0;
+}
+
+/*
+ * Detect if an tvp514x is present, and if so which revision.
+ * A device is considered to be detected if the chip ID (LSB and MSB)
+ * registers match the expected values.
+ * Any value of the rom version register is accepted.
+ * Returns ENODEV error number if no device is detected, or zero
+ * if a device is detected.
+ */
+static int tvp514x_detect(struct tvp514x_decoder *decoder)
+{
+ u8 chip_id_msb, chip_id_lsb, rom_ver;
+
+ chip_id_msb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_MSB);
+ chip_id_lsb = tvp514x_read_reg(decoder->client, REG_CHIP_ID_LSB);
+ rom_ver = tvp514x_read_reg(decoder->client, REG_ROM_VERSION);
+
+ v4l_dbg(1, debug, decoder->client,
+ "chip id detected msb:0x%x lsb:0x%x rom version:0x%x\n",
+ chip_id_msb, chip_id_lsb, rom_ver);
+ if ((chip_id_msb != TVP514X_CHIP_ID_MSB)
+ || ((chip_id_lsb != TVP5146_CHIP_ID_LSB)
+ && (chip_id_lsb != TVP5147_CHIP_ID_LSB))) {
+ /* We didn't read the values we expected, so this must not be
+ * an TVP5146/47.
+ */
+ v4l_err(decoder->client,
+ "chip id mismatch msb:0x%x lsb:0x%x\n",
+ chip_id_msb, chip_id_lsb);
+ return -ENODEV;
+ }
+
+ decoder->ver = rom_ver;
+ decoder->state = STATE_DETECTED;
+
+ v4l_info(decoder->client,
+ "%s found at 0x%x (%s)\n", decoder->client->name,
+ decoder->client->addr << 1,
+ decoder->client->adapter->name);
+ return 0;
+}
+
+/*
+ * Following are decoder interface functions implemented by
+ * TVP5146/47 decoder driver.
+ */
+
+/**
+ * ioctl_querystd - V4L2 decoder interface handler for VIDIOC_QUERYSTD ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @std_id: standard V4L2 std_id ioctl enum
+ *
+ * Returns the current standard detected by TVP5146/47. If no active input is
+ * detected, returns -EINVAL
+ */
+static int ioctl_querystd(struct v4l2_int_device *s, v4l2_std_id *std_id)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ enum tvp514x_std current_std;
+ enum tvp514x_input input_sel;
+ u8 sync_lock_status, lock_mask;
+
+ if (std_id == NULL)
+ return -EINVAL;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ input_sel = decoder->route.input;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /*Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+ /* check whether signal is locked */
+ sync_lock_status = tvp514x_read_reg(decoder->client, REG_STATUS1);
+ if (lock_mask != (sync_lock_status & lock_mask))
+ return -EINVAL; /* No input detected */
+
+ decoder->current_std = current_std;
+ *std_id = decoder->std_list[current_std].standard.id;
+
+ v4l_dbg(1, debug, decoder->client, "Current STD: %s",
+ decoder->std_list[current_std].standard.name);
+ return 0;
+}
+
+/**
+ * ioctl_s_std - V4L2 decoder interface handler for VIDIOC_S_STD ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @std_id: standard V4L2 v4l2_std_id ioctl enum
+ *
+ * If std_id is supported, sets the requested standard. Otherwise, returns
+ * -EINVAL
+ */
+static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err, i;
+
+ if (std_id == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < decoder->num_stds; i++)
+ if (*std_id & decoder->std_list[i].standard.id)
+ break;
+
+ if ((i == decoder->num_stds) || (i == STD_INVALID))
+ return -EINVAL;
+
+ err = tvp514x_write_reg(decoder->client, REG_VIDEO_STD,
+ decoder->std_list[i].video_std);
+ if (err)
+ return err;
+
+ decoder->current_std = i;
+ tvp514x_reg_list[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);
+ return 0;
+}
+
+/**
+ * ioctl_s_routing - V4L2 decoder interface handler for VIDIOC_S_INPUT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @index: number of the input
+ *
+ * If index is valid, selects the requested input. Otherwise, returns -EINVAL if
+ * the input is not supported or there is no active signal present in the
+ * selected input.
+ */
+static int ioctl_s_routing(struct v4l2_int_device *s,
+ struct v4l2_routing *route)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err;
+ enum tvp514x_input input_sel;
+ enum tvp514x_output output_sel;
+ enum tvp514x_std current_std = STD_INVALID;
+ u8 sync_lock_status, lock_mask;
+ int try_count = LOCK_RETRY_COUNT;
+
+ if ((!route) || (route->input >= INPUT_INVALID) ||
+ (route->output >= OUTPUT_INVALID))
+ return -EINVAL; /* Index out of bound */
+
+ input_sel = route->input;
+ output_sel = route->output;
+
+ err = tvp514x_write_reg(decoder->client, REG_INPUT_SEL, input_sel);
+ if (err)
+ return err;
+
+ output_sel |= tvp514x_read_reg(decoder->client,
+ REG_OUTPUT_FORMATTER1) & 0x7;
+ err = tvp514x_write_reg(decoder->client, REG_OUTPUT_FORMATTER1,
+ output_sel);
+ if (err)
+ return err;
+
+ tvp514x_reg_list[REG_INPUT_SEL].val = input_sel;
+ tvp514x_reg_list[REG_OUTPUT_FORMATTER1].val = output_sel;
+
+ /* Clear status */
+ msleep(LOCK_RETRY_DELAY);
+ err =
+ tvp514x_write_reg(decoder->client, REG_CLEAR_LOST_LOCK, 0x01);
+ if (err)
+ return err;
+
+ switch (input_sel) {
+ case INPUT_CVBS_VI1A:
+ case INPUT_CVBS_VI1B:
+ case INPUT_CVBS_VI1C:
+ case INPUT_CVBS_VI2A:
+ case INPUT_CVBS_VI2B:
+ case INPUT_CVBS_VI2C:
+ case INPUT_CVBS_VI3A:
+ case INPUT_CVBS_VI3B:
+ case INPUT_CVBS_VI3C:
+ case INPUT_CVBS_VI4A:
+ lock_mask = STATUS_CLR_SUBCAR_LOCK_BIT |
+ STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+
+ case INPUT_SVIDEO_VI2A_VI1A:
+ case INPUT_SVIDEO_VI2B_VI1B:
+ case INPUT_SVIDEO_VI2C_VI1C:
+ case INPUT_SVIDEO_VI2A_VI3A:
+ case INPUT_SVIDEO_VI2B_VI3B:
+ case INPUT_SVIDEO_VI2C_VI3C:
+ case INPUT_SVIDEO_VI4A_VI1A:
+ case INPUT_SVIDEO_VI4A_VI1B:
+ case INPUT_SVIDEO_VI4A_VI1C:
+ case INPUT_SVIDEO_VI4A_VI3A:
+ case INPUT_SVIDEO_VI4A_VI3B:
+ case INPUT_SVIDEO_VI4A_VI3C:
+ lock_mask = STATUS_HORZ_SYNC_LOCK_BIT |
+ STATUS_VIRT_SYNC_LOCK_BIT;
+ break;
+ /*Need to add other interfaces*/
+ default:
+ return -EINVAL;
+ }
+
+ while (try_count-- > 0) {
+ /* Allow decoder to sync up with new input */
+ msleep(LOCK_RETRY_DELAY);
+
+ /* get the current standard for future reference */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ continue;
+
+ sync_lock_status = tvp514x_read_reg(decoder->client,
+ REG_STATUS1);
+ if (lock_mask == (sync_lock_status & lock_mask))
+ break; /* Input detected */
+ }
+
+ if ((current_std == STD_INVALID) || (try_count < 0))
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+ decoder->route.input = route->input;
+ decoder->route.output = route->output;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Input set to: %d, std : %d",
+ input_sel, current_std);
+
+ return 0;
+}
+
+/**
+ * ioctl_queryctrl - V4L2 decoder interface handler for VIDIOC_QUERYCTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @qctrl: standard V4L2 v4l2_queryctrl structure
+ *
+ * If the requested control is supported, returns the control information.
+ * Otherwise, returns -EINVAL if the control is not supported.
+ */
+static int
+ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = -EINVAL;
+
+ if (qctrl == NULL)
+ return err;
+
+ switch (qctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ /* Brightness supported is same as standard one (0-255),
+ * so make use of standard API provided.
+ */
+ err = v4l2_ctrl_query_fill_std(qctrl);
+ break;
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ /* Saturation and Contrast supported is -
+ * Contrast: 0 - 255 (Default - 128)
+ * Saturation: 0 - 255 (Default - 128)
+ */
+ err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128);
+ break;
+ case V4L2_CID_HUE:
+ /* Hue Supported is -
+ * Hue - -180 - +180 (Default - 0, Step - +180)
+ */
+ err = v4l2_ctrl_query_fill(qctrl, -180, 180, 180, 0);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ /* Autogain is either 0 or 1*/
+ memcpy(qctrl, &tvp514x_autogain_ctrl,
+ sizeof(struct v4l2_queryctrl));
+ err = 0;
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", qctrl->id);
+ return err;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Query Control: %s : Min - %d, Max - %d, Def - %d",
+ qctrl->name,
+ qctrl->minimum,
+ qctrl->maximum,
+ qctrl->default_value);
+
+ return err;
+}
+
+/**
+ * ioctl_g_ctrl - V4L2 decoder interface handler for VIDIOC_G_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @ctrl: pointer to v4l2_control structure
+ *
+ * If the requested control is supported, returns the control's current
+ * value from the decoder. Otherwise, returns -EINVAL if the control is not
+ * supported.
+ */
+static int
+ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (ctrl == NULL)
+ return -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = tvp514x_reg_list[REG_BRIGHTNESS].val;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = tvp514x_reg_list[REG_CONTRAST].val;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = tvp514x_reg_list[REG_SATURATION].val;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = tvp514x_reg_list[REG_HUE].val;
+ if (ctrl->value == 0x7F)
+ ctrl->value = 180;
+ else if (ctrl->value == 0x80)
+ ctrl->value = -180;
+ else
+ ctrl->value = 0;
+
+ break;
+ case V4L2_CID_AUTOGAIN:
+ ctrl->value = tvp514x_reg_list[REG_AFE_GAIN_CTRL].val;
+ if ((ctrl->value & 0x3) == 3)
+ ctrl->value = 1;
+ else
+ ctrl->value = 0;
+
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", ctrl->id);
+ return -EINVAL;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Get Control: ID - %d - %d",
+ ctrl->id, ctrl->value);
+ return 0;
+}
+
+/**
+ * ioctl_s_ctrl - V4L2 decoder interface handler for VIDIOC_S_CTRL ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @ctrl: pointer to v4l2_control structure
+ *
+ * If the requested control is supported, sets the control's current
+ * value in HW. Otherwise, returns -EINVAL if the control is not supported.
+ */
+static int
+ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = -EINVAL, value;
+
+ if (ctrl == NULL)
+ return err;
+
+ value = (__s32) ctrl->value;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid brightness setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_BRIGHTNESS,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_BRIGHTNESS].val = value;
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid contrast setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_CONTRAST,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_CONTRAST].val = value;
+ break;
+ case V4L2_CID_SATURATION:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+ v4l_err(decoder->client,
+ "invalid saturation setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_SATURATION,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_SATURATION].val = value;
+ break;
+ case V4L2_CID_HUE:
+ if (value == 180)
+ value = 0x7F;
+ else if (value == -180)
+ value = 0x80;
+ else if (value == 0)
+ value = 0;
+ else {
+ v4l_err(decoder->client,
+ "invalid hue setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_HUE,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_HUE].val = value;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (value == 1)
+ value = 0x0F;
+ else if (value == 0)
+ value = 0x0C;
+ else {
+ v4l_err(decoder->client,
+ "invalid auto gain setting %d\n",
+ ctrl->value);
+ return -ERANGE;
+ }
+ err = tvp514x_write_reg(decoder->client, REG_AFE_GAIN_CTRL,
+ value);
+ if (err)
+ return err;
+ tvp514x_reg_list[REG_AFE_GAIN_CTRL].val = value;
+ break;
+ default:
+ v4l_err(decoder->client,
+ "invalid control id %d\n", ctrl->id);
+ return err;
+ }
+
+ v4l_dbg(1, debug, decoder->client,
+ "Set Control: ID - %d - %d",
+ ctrl->id, ctrl->value);
+
+ return err;
+}
+
+/**
+ * ioctl_enum_fmt_cap - Implement the CAPTURE buffer VIDIOC_ENUM_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @fmt: standard V4L2 VIDIOC_ENUM_FMT ioctl structure
+ *
+ * Implement the VIDIOC_ENUM_FMT ioctl to enumerate supported formats
+ */
+static int
+ioctl_enum_fmt_cap(struct v4l2_int_device *s, struct v4l2_fmtdesc *fmt)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int index;
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ index = fmt->index;
+ if ((index >= decoder->num_fmts) || (index < 0))
+ return -EINVAL; /* Index out of bound */
+
+ if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ memcpy(fmt, &decoder->fmt_list[index],
+ sizeof(struct v4l2_fmtdesc));
+
+ v4l_dbg(1, debug, decoder->client,
+ "Current FMT: index - %d (%s)",
+ decoder->fmt_list[index].index,
+ decoder->fmt_list[index].description);
+ return 0;
+}
+
+/**
+ * ioctl_try_fmt_cap - Implement the CAPTURE buffer VIDIOC_TRY_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure
+ *
+ * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This
+ * ioctl is used to negotiate the image capture size and pixel format
+ * without actually making it take effect.
+ */
+static int
+ioctl_try_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int ifmt;
+ struct v4l2_pix_format *pix;
+ enum tvp514x_std current_std;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ pix = &f->fmt.pix;
+
+ /* Calculate height and width based on current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+ pix->width = decoder->std_list[current_std].width;
+ pix->height = decoder->std_list[current_std].height;
+
+ for (ifmt = 0; ifmt < decoder->num_fmts; ifmt++) {
+ if (pix->pixelformat ==
+ decoder->fmt_list[ifmt].pixelformat)
+ break;
+ }
+ if (ifmt == decoder->num_fmts)
+ ifmt = 0; /* None of the format matched, select default */
+ pix->pixelformat = decoder->fmt_list[ifmt].pixelformat;
+
+ pix->field = V4L2_FIELD_INTERLACED;
+ pix->bytesperline = pix->width * 2;
+ pix->sizeimage = pix->bytesperline * pix->height;
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pix->priv = 0;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Try FMT: pixelformat - %s, bytesperline - %d"
+ "Width - %d, Height - %d",
+ decoder->fmt_list[ifmt].description, pix->bytesperline,
+ pix->width, pix->height);
+ return 0;
+}
+
+/**
+ * ioctl_s_fmt_cap - V4L2 decoder interface handler for VIDIOC_S_FMT ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure
+ *
+ * If the requested format is supported, configures the HW to use that
+ * format, returns error code if format not supported or HW can't be
+ * correctly configured.
+ */
+static int
+ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_pix_format *pix;
+ int rval;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ pix = &f->fmt.pix;
+ rval = ioctl_try_fmt_cap(s, f);
+ if (rval)
+ return rval;
+
+ decoder->pix = *pix;
+
+ return rval;
+}
+
+/**
+ * ioctl_g_fmt_cap - V4L2 decoder interface handler for ioctl_g_fmt_cap
+ * @s: pointer to standard V4L2 device structure
+ * @f: pointer to standard V4L2 v4l2_format structure
+ *
+ * Returns the decoder's current pixel format in the v4l2_format
+ * parameter.
+ */
+static int
+ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (f == NULL)
+ return -EINVAL;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ f->fmt.pix = decoder->pix;
+
+ v4l_dbg(1, debug, decoder->client,
+ "Current FMT: bytesperline - %d"
+ "Width - %d, Height - %d",
+ decoder->pix.bytesperline,
+ decoder->pix.width, decoder->pix.height);
+ return 0;
+}
+
+/**
+ * ioctl_g_parm - V4L2 decoder interface handler for VIDIOC_G_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_G_PARM ioctl structure
+ *
+ * Returns the decoder's video CAPTURE parameters.
+ */
+static int
+ioctl_g_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_captureparm *cparm;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ memset(a, 0, sizeof(*a));
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+
+ cparm = &a->parm.capture;
+ cparm->capability = V4L2_CAP_TIMEPERFRAME;
+ cparm->timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * ioctl_s_parm - V4L2 decoder interface handler for VIDIOC_S_PARM ioctl
+ * @s: pointer to standard V4L2 device structure
+ * @a: pointer to standard V4L2 VIDIOC_S_PARM ioctl structure
+ *
+ * Configures the decoder to use the input parameters, if possible. If
+ * not possible, returns the appropriate error code.
+ */
+static int
+ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ struct v4l2_fract *timeperframe;
+ enum tvp514x_std current_std;
+
+ if (a == NULL)
+ return -EINVAL;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL; /* only capture is supported */
+
+ timeperframe = &a->parm.capture.timeperframe;
+
+ /* get the current standard */
+ current_std = tvp514x_get_current_std(decoder);
+ if (current_std == STD_INVALID)
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+
+ *timeperframe =
+ decoder->std_list[current_std].standard.frameperiod;
+
+ return 0;
+}
+
+/**
+ * ioctl_g_ifparm - V4L2 decoder interface handler for vidioc_int_g_ifparm_num
+ * @s: pointer to standard V4L2 device structure
+ * @p: pointer to standard V4L2 vidioc_int_g_ifparm_num ioctl structure
+ *
+ * Gets slave interface parameters.
+ * Calculates the required xclk value to support the requested
+ * clock parameters in p. This value is returned in the p
+ * parameter.
+ */
+static int ioctl_g_ifparm(struct v4l2_int_device *s, struct v4l2_ifparm *p)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int rval;
+
+ if (p == NULL)
+ return -EINVAL;
+
+ if (NULL == decoder->pdata->ifparm)
+ return -EINVAL;
+
+ rval = decoder->pdata->ifparm(p);
+ if (rval) {
+ v4l_err(decoder->client, "g_ifparm.Err[%d]\n", rval);
+ return rval;
+ }
+
+ p->u.bt656.clock_curr = TVP514X_XCLK_BT656;
+
+ return 0;
+}
+
+/**
+ * ioctl_g_priv - V4L2 decoder interface handler for vidioc_int_g_priv_num
+ * @s: pointer to standard V4L2 device structure
+ * @p: void pointer to hold decoder's private data address
+ *
+ * Returns device's (decoder's) private data area address in p parameter
+ */
+static int ioctl_g_priv(struct v4l2_int_device *s, void *p)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ if (NULL == decoder->pdata->priv_data_set)
+ return -EINVAL;
+
+ return decoder->pdata->priv_data_set(p);
+}
+
+/**
+ * ioctl_s_power - V4L2 decoder interface handler for vidioc_int_s_power_num
+ * @s: pointer to standard V4L2 device structure
+ * @on: power state to which device is to be set
+ *
+ * Sets devices power state to requrested state, if possible.
+ */
+static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power on)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err = 0;
+
+ switch (on) {
+ case V4L2_POWER_OFF:
+ /* Power Down Sequence */
+ err =
+ tvp514x_write_reg(decoder->client, REG_OPERATION_MODE,
+ 0x01);
+ /* Disable mux for TVP5146/47 decoder data path */
+ if (decoder->pdata->power_set)
+ err |= decoder->pdata->power_set(on);
+ decoder->state = STATE_NOT_DETECTED;
+ break;
+
+ case V4L2_POWER_STANDBY:
+ if (decoder->pdata->power_set)
+ err = decoder->pdata->power_set(on);
+ break;
+
+ case V4L2_POWER_ON:
+ /* Enable mux for TVP5146/47 decoder data path */
+ if ((decoder->pdata->power_set) &&
+ (decoder->state == STATE_NOT_DETECTED)) {
+ int i;
+ struct tvp514x_init_seq *int_seq =
+ (struct tvp514x_init_seq *)
+ decoder->id->driver_data;
+
+ err = decoder->pdata->power_set(on);
+
+ /* Power Up Sequence */
+ for (i = 0; i < int_seq->no_regs; i++) {
+ err |= tvp514x_write_reg(decoder->client,
+ int_seq->init_reg_seq[i].reg,
+ int_seq->init_reg_seq[i].val);
+ }
+ /* Detect the sensor is not already detected */
+ err |= tvp514x_detect(decoder);
+ if (err) {
+ v4l_err(decoder->client,
+ "Unable to detect decoder\n");
+ return err;
+ }
+ }
+ err |= tvp514x_configure(decoder);
+ break;
+
+ default:
+ err = -ENODEV;
+ break;
+ }
+
+ return err;
+}
+
+/**
+ * ioctl_init - V4L2 decoder interface handler for VIDIOC_INT_INIT
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialize the decoder device (calls tvp514x_configure())
+ */
+static int ioctl_init(struct v4l2_int_device *s)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+
+ /* Set default standard to auto */
+ tvp514x_reg_list[REG_VIDEO_STD].val =
+ VIDEO_STD_AUTO_SWITCH_BIT;
+
+ return tvp514x_configure(decoder);
+}
+
+/**
+ * ioctl_dev_exit - V4L2 decoder interface handler for vidioc_int_dev_exit_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Delinitialise the dev. at slave detach. The complement of ioctl_dev_init.
+ */
+static int ioctl_dev_exit(struct v4l2_int_device *s)
+{
+ return 0;
+}
+
+/**
+ * ioctl_dev_init - V4L2 decoder interface handler for vidioc_int_dev_init_num
+ * @s: pointer to standard V4L2 device structure
+ *
+ * Initialise the device when slave attaches to the master. Returns 0 if
+ * TVP5146/47 device could be found, otherwise returns appropriate error.
+ */
+static int ioctl_dev_init(struct v4l2_int_device *s)
+{
+ struct tvp514x_decoder *decoder = s->priv;
+ int err;
+
+ err = tvp514x_detect(decoder);
+ if (err < 0) {
+ v4l_err(decoder->client,
+ "Unable to detect decoder\n");
+ return err;
+ }
+
+ v4l_info(decoder->client,
+ "chip version 0x%.2x detected\n", decoder->ver);
+
+ return 0;
+}
+
+static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = {
+ {vidioc_int_dev_init_num, (v4l2_int_ioctl_func*) ioctl_dev_init},
+ {vidioc_int_dev_exit_num, (v4l2_int_ioctl_func*) ioctl_dev_exit},
+ {vidioc_int_s_power_num, (v4l2_int_ioctl_func*) ioctl_s_power},
+ {vidioc_int_g_priv_num, (v4l2_int_ioctl_func*) ioctl_g_priv},
+ {vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func*) ioctl_g_ifparm},
+ {vidioc_int_init_num, (v4l2_int_ioctl_func*) ioctl_init},
+ {vidioc_int_enum_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_enum_fmt_cap},
+ {vidioc_int_try_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_try_fmt_cap},
+ {vidioc_int_g_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_g_fmt_cap},
+ {vidioc_int_s_fmt_cap_num,
+ (v4l2_int_ioctl_func *) ioctl_s_fmt_cap},
+ {vidioc_int_g_parm_num, (v4l2_int_ioctl_func *) ioctl_g_parm},
+ {vidioc_int_s_parm_num, (v4l2_int_ioctl_func *) ioctl_s_parm},
+ {vidioc_int_queryctrl_num,
+ (v4l2_int_ioctl_func *) ioctl_queryctrl},
+ {vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *) ioctl_g_ctrl},
+ {vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *) ioctl_s_ctrl},
+ {vidioc_int_querystd_num, (v4l2_int_ioctl_func *) ioctl_querystd},
+ {vidioc_int_s_std_num, (v4l2_int_ioctl_func *) ioctl_s_std},
+ {vidioc_int_s_video_routing_num,
+ (v4l2_int_ioctl_func *) ioctl_s_routing},
+};
+
+static 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,
+
+ .fmt_list = tvp514x_fmt_list,
+ .num_fmts = ARRAY_SIZE(tvp514x_fmt_list),
+
+ .pix = { /* Default to NTSC 8-bit YUV 422 */
+ .width = NTSC_NUM_ACTIVE_PIXELS,
+ .height = NTSC_NUM_ACTIVE_LINES,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = NTSC_NUM_ACTIVE_PIXELS * 2,
+ .sizeimage =
+ NTSC_NUM_ACTIVE_PIXELS * 2 * NTSC_NUM_ACTIVE_LINES,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ },
+
+ .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,
+ },
+};
+
+/**
+ * tvp514x_probe - decoder driver i2c probe handler
+ * @client: i2c driver client device structure
+ *
+ * Register decoder as an i2c client device and V4L2
+ * device.
+ */
+static int
+tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct tvp514x_decoder *decoder = &tvp514x_dev;
+ 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) {
+ v4l_err(client, "No platform data\n!!");
+ return -ENODEV;
+ }
+ /*
+ * 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->pdata->clk_polarity << 1);
+ tvp514x_reg_list[REG_SYNC_CONTROL].val |=
+ ((decoder->pdata->hs_polarity << 2) |
+ (decoder->pdata->vs_polarity << 3));
+ /*
+ * Save the id data, required for power up sequence
+ */
+ decoder->id = (struct i2c_device_id *)id;
+ /* Attach to Master */
+ strcpy(tvp514x_int_device.u.slave->attach_to, decoder->pdata->master);
+ decoder->v4l2_int_device = &tvp514x_int_device;
+ decoder->client = client;
+ i2c_set_clientdata(client, decoder);
+
+ /* Register with V4L2 layer as slave device */
+ err = v4l2_int_device_register(decoder->v4l2_int_device);
+ if (err) {
+ i2c_set_clientdata(client, NULL);
+ v4l_err(client,
+ "Unable to register to v4l2. Err[%d]\n", err);
+
+ } else
+ v4l_info(client, "Registered to v4l2 master %s!!\n",
+ decoder->pdata->master);
+
+ return 0;
+}
+
+/**
+ * tvp514x_remove - decoder driver i2c remove handler
+ * @client: i2c driver client device structure
+ *
+ * Unregister decoder as an i2c client device and V4L2
+ * device. Complement of tvp514x_probe().
+ */
+static int __exit tvp514x_remove(struct i2c_client *client)
+{
+ struct tvp514x_decoder *decoder = i2c_get_clientdata(client);
+
+ if (!client->adapter)
+ return -ENODEV; /* our client isn't attached */
+
+ v4l2_int_device_unregister(decoder->v4l2_int_device);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+/*
+ * TVP5146 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp5146_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp5146_init = {
+ .no_regs = ARRAY_SIZE(tvp5146_init_reg_seq),
+ .init_reg_seq = tvp5146_init_reg_seq,
+};
+/*
+ * TVP5147 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp5147_init_reg_seq[] = {
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x02},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0x80},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x01},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xA0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x16},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS1, 0x60},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS2, 0x00},
+ {TOK_WRITE, REG_VBUS_ADDRESS_ACCESS3, 0xB0},
+ {TOK_WRITE, REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR, 0x00},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp5147_init = {
+ .no_regs = ARRAY_SIZE(tvp5147_init_reg_seq),
+ .init_reg_seq = tvp5147_init_reg_seq,
+};
+/*
+ * TVP5146M2/TVP5147M1 Init/Power on Sequence
+ */
+static const struct tvp514x_reg tvp514xm_init_reg_seq[] = {
+ {TOK_WRITE, REG_OPERATION_MODE, 0x01},
+ {TOK_WRITE, REG_OPERATION_MODE, 0x00},
+};
+static const struct tvp514x_init_seq tvp514xm_init = {
+ .no_regs = ARRAY_SIZE(tvp514xm_init_reg_seq),
+ .init_reg_seq = tvp514xm_init_reg_seq,
+};
+/*
+ * I2C Device Table -
+ *
+ * name - Name of the actual device/chip.
+ * driver_data - Driver data
+ */
+static const struct i2c_device_id tvp514x_id[] = {
+ {"tvp5146", (unsigned long)&tvp5146_init},
+ {"tvp5146m2", (unsigned long)&tvp514xm_init},
+ {"tvp5147", (unsigned long)&tvp5147_init},
+ {"tvp5147m1", (unsigned long)&tvp514xm_init},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tvp514x_id);
+
+static struct i2c_driver tvp514x_i2c_driver = {
+ .driver = {
+ .name = TVP514X_MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = tvp514x_probe,
+ .remove = __exit_p(tvp514x_remove),
+ .id_table = tvp514x_id,
+};
+
+/**
+ * tvp514x_init
+ *
+ * Module init function
+ */
+static int __init tvp514x_init(void)
+{
+ return i2c_add_driver(&tvp514x_i2c_driver);
+}
+
+/**
+ * tvp514x_cleanup
+ *
+ * Module exit function
+ */
+static void __exit tvp514x_cleanup(void)
+{
+ i2c_del_driver(&tvp514x_i2c_driver);
+}
+
+module_init(tvp514x_init);
+module_exit(tvp514x_cleanup);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TVP514X linux decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/video/tvp514x_regs.h b/linux/drivers/media/video/tvp514x_regs.h
new file mode 100644
index 000000000..351620aee
--- /dev/null
+++ b/linux/drivers/media/video/tvp514x_regs.h
@@ -0,0 +1,297 @@
+/*
+ * drivers/media/video/tvp514x_regs.h
+ *
+ * Copyright (C) 2008 Texas Instruments Inc
+ * Author: Vaibhav Hiremath <hvaibhav@ti.com>
+ *
+ * Contributors:
+ * Sivaraj R <sivaraj@ti.com>
+ * Brijesh R Jadav <brijesh.j@ti.com>
+ * Hardik Shah <hardik.shah@ti.com>
+ * Manjunath Hadli <mrh@ti.com>
+ * Karicheri Muralidharan <m-karicheri2@ti.com>
+ *
+ * This package 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.
+ *
+ * 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 _TVP514X_REGS_H
+#define _TVP514X_REGS_H
+
+/*
+ * TVP5146/47 registers
+ */
+#define REG_INPUT_SEL (0x00)
+#define REG_AFE_GAIN_CTRL (0x01)
+#define REG_VIDEO_STD (0x02)
+#define REG_OPERATION_MODE (0x03)
+#define REG_AUTOSWITCH_MASK (0x04)
+
+#define REG_COLOR_KILLER (0x05)
+#define REG_LUMA_CONTROL1 (0x06)
+#define REG_LUMA_CONTROL2 (0x07)
+#define REG_LUMA_CONTROL3 (0x08)
+
+#define REG_BRIGHTNESS (0x09)
+#define REG_CONTRAST (0x0A)
+#define REG_SATURATION (0x0B)
+#define REG_HUE (0x0C)
+
+#define REG_CHROMA_CONTROL1 (0x0D)
+#define REG_CHROMA_CONTROL2 (0x0E)
+
+/* 0x0F Reserved */
+
+#define REG_COMP_PR_SATURATION (0x10)
+#define REG_COMP_Y_CONTRAST (0x11)
+#define REG_COMP_PB_SATURATION (0x12)
+
+/* 0x13 Reserved */
+
+#define REG_COMP_Y_BRIGHTNESS (0x14)
+
+/* 0x15 Reserved */
+
+#define REG_AVID_START_PIXEL_LSB (0x16)
+#define REG_AVID_START_PIXEL_MSB (0x17)
+#define REG_AVID_STOP_PIXEL_LSB (0x18)
+#define REG_AVID_STOP_PIXEL_MSB (0x19)
+
+#define REG_HSYNC_START_PIXEL_LSB (0x1A)
+#define REG_HSYNC_START_PIXEL_MSB (0x1B)
+#define REG_HSYNC_STOP_PIXEL_LSB (0x1C)
+#define REG_HSYNC_STOP_PIXEL_MSB (0x1D)
+
+#define REG_VSYNC_START_LINE_LSB (0x1E)
+#define REG_VSYNC_START_LINE_MSB (0x1F)
+#define REG_VSYNC_STOP_LINE_LSB (0x20)
+#define REG_VSYNC_STOP_LINE_MSB (0x21)
+
+#define REG_VBLK_START_LINE_LSB (0x22)
+#define REG_VBLK_START_LINE_MSB (0x23)
+#define REG_VBLK_STOP_LINE_LSB (0x24)
+#define REG_VBLK_STOP_LINE_MSB (0x25)
+
+/* 0x26 - 0x27 Reserved */
+
+#define REG_FAST_SWTICH_CONTROL (0x28)
+
+/* 0x29 Reserved */
+
+#define REG_FAST_SWTICH_SCART_DELAY (0x2A)
+
+/* 0x2B Reserved */
+
+#define REG_SCART_DELAY (0x2C)
+#define REG_CTI_DELAY (0x2D)
+#define REG_CTI_CONTROL (0x2E)
+
+/* 0x2F - 0x31 Reserved */
+
+#define REG_SYNC_CONTROL (0x32)
+#define REG_OUTPUT_FORMATTER1 (0x33)
+#define REG_OUTPUT_FORMATTER2 (0x34)
+#define REG_OUTPUT_FORMATTER3 (0x35)
+#define REG_OUTPUT_FORMATTER4 (0x36)
+#define REG_OUTPUT_FORMATTER5 (0x37)
+#define REG_OUTPUT_FORMATTER6 (0x38)
+#define REG_CLEAR_LOST_LOCK (0x39)
+
+#define REG_STATUS1 (0x3A)
+#define REG_STATUS2 (0x3B)
+
+#define REG_AGC_GAIN_STATUS_LSB (0x3C)
+#define REG_AGC_GAIN_STATUS_MSB (0x3D)
+
+/* 0x3E Reserved */
+
+#define REG_VIDEO_STD_STATUS (0x3F)
+#define REG_GPIO_INPUT1 (0x40)
+#define REG_GPIO_INPUT2 (0x41)
+
+/* 0x42 - 0x45 Reserved */
+
+#define REG_AFE_COARSE_GAIN_CH1 (0x46)
+#define REG_AFE_COARSE_GAIN_CH2 (0x47)
+#define REG_AFE_COARSE_GAIN_CH3 (0x48)
+#define REG_AFE_COARSE_GAIN_CH4 (0x49)
+
+#define REG_AFE_FINE_GAIN_PB_B_LSB (0x4A)
+#define REG_AFE_FINE_GAIN_PB_B_MSB (0x4B)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_LSB (0x4C)
+#define REG_AFE_FINE_GAIN_Y_G_CHROMA_MSB (0x4D)
+#define REG_AFE_FINE_GAIN_PR_R_LSB (0x4E)
+#define REG_AFE_FINE_GAIN_PR_R_MSB (0x4F)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_LSB (0x50)
+#define REG_AFE_FINE_GAIN_CVBS_LUMA_MSB (0x51)
+
+/* 0x52 - 0x68 Reserved */
+
+#define REG_FBIT_VBIT_CONTROL1 (0x69)
+
+/* 0x6A - 0x6B Reserved */
+
+#define REG_BACKEND_AGC_CONTROL (0x6C)
+
+/* 0x6D - 0x6E Reserved */
+
+#define REG_AGC_DECREMENT_SPEED_CONTROL (0x6F)
+#define REG_ROM_VERSION (0x70)
+
+/* 0x71 - 0x73 Reserved */
+
+#define REG_AGC_WHITE_PEAK_PROCESSING (0x74)
+#define REG_FBIT_VBIT_CONTROL2 (0x75)
+#define REG_VCR_TRICK_MODE_CONTROL (0x76)
+#define REG_HORIZONTAL_SHAKE_INCREMENT (0x77)
+#define REG_AGC_INCREMENT_SPEED (0x78)
+#define REG_AGC_INCREMENT_DELAY (0x79)
+
+/* 0x7A - 0x7F Reserved */
+
+#define REG_CHIP_ID_MSB (0x80)
+#define REG_CHIP_ID_LSB (0x81)
+
+/* 0x82 Reserved */
+
+#define REG_CPLL_SPEED_CONTROL (0x83)
+
+/* 0x84 - 0x96 Reserved */
+
+#define REG_STATUS_REQUEST (0x97)
+
+/* 0x98 - 0x99 Reserved */
+
+#define REG_VERTICAL_LINE_COUNT_LSB (0x9A)
+#define REG_VERTICAL_LINE_COUNT_MSB (0x9B)
+
+/* 0x9C - 0x9D Reserved */
+
+#define REG_AGC_DECREMENT_DELAY (0x9E)
+
+/* 0x9F - 0xB0 Reserved */
+
+#define REG_VDP_TTX_FILTER_1_MASK1 (0xB1)
+#define REG_VDP_TTX_FILTER_1_MASK2 (0xB2)
+#define REG_VDP_TTX_FILTER_1_MASK3 (0xB3)
+#define REG_VDP_TTX_FILTER_1_MASK4 (0xB4)
+#define REG_VDP_TTX_FILTER_1_MASK5 (0xB5)
+#define REG_VDP_TTX_FILTER_2_MASK1 (0xB6)
+#define REG_VDP_TTX_FILTER_2_MASK2 (0xB7)
+#define REG_VDP_TTX_FILTER_2_MASK3 (0xB8)
+#define REG_VDP_TTX_FILTER_2_MASK4 (0xB9)
+#define REG_VDP_TTX_FILTER_2_MASK5 (0xBA)
+#define REG_VDP_TTX_FILTER_CONTROL (0xBB)
+#define REG_VDP_FIFO_WORD_COUNT (0xBC)
+#define REG_VDP_FIFO_INTERRUPT_THRLD (0xBD)
+
+/* 0xBE Reserved */
+
+#define REG_VDP_FIFO_RESET (0xBF)
+#define REG_VDP_FIFO_OUTPUT_CONTROL (0xC0)
+#define REG_VDP_LINE_NUMBER_INTERRUPT (0xC1)
+#define REG_VDP_PIXEL_ALIGNMENT_LSB (0xC2)
+#define REG_VDP_PIXEL_ALIGNMENT_MSB (0xC3)
+
+/* 0xC4 - 0xD5 Reserved */
+
+#define REG_VDP_LINE_START (0xD6)
+#define REG_VDP_LINE_STOP (0xD7)
+#define REG_VDP_GLOBAL_LINE_MODE (0xD8)
+#define REG_VDP_FULL_FIELD_ENABLE (0xD9)
+#define REG_VDP_FULL_FIELD_MODE (0xDA)
+
+/* 0xDB - 0xDF Reserved */
+
+#define REG_VBUS_DATA_ACCESS_NO_VBUS_ADDR_INCR (0xE0)
+#define REG_VBUS_DATA_ACCESS_VBUS_ADDR_INCR (0xE1)
+#define REG_FIFO_READ_DATA (0xE2)
+
+/* 0xE3 - 0xE7 Reserved */
+
+#define REG_VBUS_ADDRESS_ACCESS1 (0xE8)
+#define REG_VBUS_ADDRESS_ACCESS2 (0xE9)
+#define REG_VBUS_ADDRESS_ACCESS3 (0xEA)
+
+/* 0xEB - 0xEF Reserved */
+
+#define REG_INTERRUPT_RAW_STATUS0 (0xF0)
+#define REG_INTERRUPT_RAW_STATUS1 (0xF1)
+#define REG_INTERRUPT_STATUS0 (0xF2)
+#define REG_INTERRUPT_STATUS1 (0xF3)
+#define REG_INTERRUPT_MASK0 (0xF4)
+#define REG_INTERRUPT_MASK1 (0xF5)
+#define REG_INTERRUPT_CLEAR0 (0xF6)
+#define REG_INTERRUPT_CLEAR1 (0xF7)
+
+/* 0xF8 - 0xFF Reserved */
+
+/*
+ * Mask and bit definitions of TVP5146/47 registers
+ */
+/* The ID values we are looking for */
+#define TVP514X_CHIP_ID_MSB (0x51)
+#define TVP5146_CHIP_ID_LSB (0x46)
+#define TVP5147_CHIP_ID_LSB (0x47)
+
+#define VIDEO_STD_MASK (0x07)
+#define VIDEO_STD_AUTO_SWITCH_BIT (0x00)
+#define VIDEO_STD_NTSC_MJ_BIT (0x01)
+#define VIDEO_STD_PAL_BDGHIN_BIT (0x02)
+#define VIDEO_STD_PAL_M_BIT (0x03)
+#define VIDEO_STD_PAL_COMBINATION_N_BIT (0x04)
+#define VIDEO_STD_NTSC_4_43_BIT (0x05)
+#define VIDEO_STD_SECAM_BIT (0x06)
+#define VIDEO_STD_PAL_60_BIT (0x07)
+
+/*
+ * Status bit
+ */
+#define STATUS_TV_VCR_BIT (1<<0)
+#define STATUS_HORZ_SYNC_LOCK_BIT (1<<1)
+#define STATUS_VIRT_SYNC_LOCK_BIT (1<<2)
+#define STATUS_CLR_SUBCAR_LOCK_BIT (1<<3)
+#define STATUS_LOST_LOCK_DETECT_BIT (1<<4)
+#define STATUS_FEILD_RATE_BIT (1<<5)
+#define STATUS_LINE_ALTERNATING_BIT (1<<6)
+#define STATUS_PEAK_WHITE_DETECT_BIT (1<<7)
+
+/* Tokens for register write */
+#define TOK_WRITE (0) /* token for write operation */
+#define TOK_TERM (1) /* terminating token */
+#define TOK_DELAY (2) /* delay token for reg list */
+#define TOK_SKIP (3) /* token to skip a register */
+/**
+ * struct tvp514x_reg - Structure for TVP5146/47 register initialization values
+ * @token - Token: TOK_WRITE, TOK_TERM etc..
+ * @reg - Register offset
+ * @val - Register Value for TOK_WRITE or delay in ms for TOK_DELAY
+ */
+struct tvp514x_reg {
+ u8 token;
+ u8 reg;
+ u32 val;
+};
+
+/**
+ * struct tvp514x_init_seq - Structure for TVP5146/47/46M2/47M1 power up
+ * Sequence.
+ * @ no_regs - Number of registers to write for power up sequence.
+ * @ init_reg_seq - Array of registers and respective value to write.
+ */
+struct tvp514x_init_seq {
+ unsigned int no_regs;
+ const struct tvp514x_reg *init_reg_seq;
+};
+#endif /* ifndef _TVP514X_REGS_H */
diff --git a/linux/drivers/media/video/upd64031a.c b/linux/drivers/media/video/upd64031a.c
index dfca5ed6e..df5f4b0c8 100644
--- a/linux/drivers/media/video/upd64031a.c
+++ b/linux/drivers/media/video/upd64031a.c
@@ -26,7 +26,7 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/upd64031a.h>
@@ -68,6 +68,7 @@ enum {
};
struct upd64031a_state {
+ struct v4l2_subdev sd;
u8 regs[TOT_REGS];
u8 gr_mode;
u8 direct_3dycs_connect;
@@ -75,6 +76,11 @@ struct upd64031a_state {
u8 ext_vert_sync;
};
+static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct upd64031a_state, sd);
+}
+
static u8 upd64031a_init[] = {
0x00, 0xb8, 0x48, 0xd2, 0xe6,
0x03, 0x10, 0x0b, 0xaf, 0x7f,
@@ -84,8 +90,9 @@ static u8 upd64031a_init[] = {
/* ------------------------------------------------------------------------ */
-static u8 upd64031a_read(struct i2c_client *client, u8 reg)
+static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[2];
if (reg >= sizeof(buf))
@@ -96,106 +103,127 @@ static u8 upd64031a_read(struct i2c_client *client, u8 reg)
/* ------------------------------------------------------------------------ */
-static void upd64031a_write(struct i2c_client *client, u8 reg, u8 val)
+static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[2];
buf[0] = reg;
buf[1] = val;
- v4l_dbg(1, debug, client, "write reg: %02X val: %02X\n", reg, val);
+ v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val);
if (i2c_master_send(client, buf, 2) != 2)
- v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val);
+ v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val);
}
/* ------------------------------------------------------------------------ */
/* The input changed due to new input or channel changed */
-static void upd64031a_change(struct i2c_client *client)
+static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
{
- struct upd64031a_state *state = i2c_get_clientdata(client);
+ struct upd64031a_state *state = to_state(sd);
u8 reg = state->regs[R00];
- v4l_dbg(1, debug, client, "changed input or channel\n");
- upd64031a_write(client, R00, reg | 0x10);
- upd64031a_write(client, R00, reg & ~0x10);
+ v4l2_dbg(1, debug, sd, "changed input or channel\n");
+ upd64031a_write(sd, R00, reg | 0x10);
+ upd64031a_write(sd, R00, reg & ~0x10);
+ return 0;
}
/* ------------------------------------------------------------------------ */
+static int upd64031a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+{
+ struct upd64031a_state *state = to_state(sd);
+ u8 r00, r05, r08;
+
+ state->gr_mode = (route->input & 3) << 6;
+ state->direct_3dycs_connect = (route->input & 0xc) << 4;
+ state->ext_comp_sync =
+ (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1;
+ state->ext_vert_sync =
+ (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2;
+ r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode;
+ r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) |
+ state->ext_comp_sync | state->ext_vert_sync;
+ r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) |
+ state->direct_3dycs_connect;
+ upd64031a_write(sd, R00, r00);
+ upd64031a_write(sd, R05, r05);
+ upd64031a_write(sd, R08, r08);
+ return upd64031a_s_frequency(sd, NULL);
+}
+
+static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0);
+}
+
+static int upd64031a_log_status(struct v4l2_subdev *sd)
+{
+ v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n",
+ upd64031a_read(sd, 0), upd64031a_read(sd, 1));
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = upd64031a_read(sd, reg->reg & 0xff);
+ return 0;
+}
+
+static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
static int upd64031a_command(struct i2c_client *client, unsigned cmd, void *arg)
{
- struct upd64031a_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
-
- switch (cmd) {
- case VIDIOC_S_FREQUENCY:
- upd64031a_change(client);
- break;
-
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = (state->gr_mode >> 6) |
- (state->direct_3dycs_connect >> 4) |
- (state->ext_comp_sync >> 1) |
- (state->ext_vert_sync >> 2);
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- u8 r00, r05, r08;
-
- state->gr_mode = (route->input & 3) << 6;
- state->direct_3dycs_connect = (route->input & 0xc) << 4;
- state->ext_comp_sync =
- (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1;
- state->ext_vert_sync =
- (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2;
- r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode;
- r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) |
- state->ext_comp_sync | state->ext_vert_sync;
- r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) |
- state->direct_3dycs_connect;
- upd64031a_write(client, R00, r00);
- upd64031a_write(client, R05, r05);
- upd64031a_write(client, R08, r08);
- upd64031a_change(client);
- break;
- }
-
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Status: SA00=0x%02x SA01=0x%02x\n",
- upd64031a_read(client, 0), upd64031a_read(client, 1));
- break;
+ return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+}
+
+/* ----------------------------------------------------------------------- */
+static const struct v4l2_subdev_core_ops upd64031a_core_ops = {
+ .log_status = upd64031a_log_status,
+ .g_chip_ident = upd64031a_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
-
- if (!v4l2_chip_match_i2c_client(client,
- reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER) {
- reg->val = upd64031a_read(client, reg->reg & 0xff);
- break;
- }
- upd64031a_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
+ .g_register = upd64031a_g_register,
+ .s_register = upd64031a_s_register,
#endif
+};
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_UPD64031A, 0);
+static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = {
+ .s_frequency = upd64031a_s_frequency,
+};
- default:
- break;
- }
- return 0;
-}
+static const struct v4l2_subdev_video_ops upd64031a_video_ops = {
+ .s_routing = upd64031a_s_routing,
+};
+
+static const struct v4l2_subdev_ops upd64031a_ops = {
+ .core = &upd64031a_core_ops,
+ .tuner = &upd64031a_tuner_ops,
+ .video = &upd64031a_video_ops,
+};
/* ------------------------------------------------------------------------ */
@@ -205,6 +233,7 @@ static int upd64031a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct upd64031a_state *state;
+ struct v4l2_subdev *sd;
int i;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -216,19 +245,23 @@ static int upd64031a_probe(struct i2c_client *client,
state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &upd64031a_ops);
memcpy(state->regs, upd64031a_init, sizeof(state->regs));
state->gr_mode = UPD64031A_GR_ON << 6;
state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4;
state->ext_comp_sync = state->ext_vert_sync = 0;
for (i = 0; i < TOT_REGS; i++)
- upd64031a_write(client, i, state->regs[i]);
+ upd64031a_write(sd, i, state->regs[i]);
return 0;
}
static int upd64031a_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;
}
diff --git a/linux/drivers/media/video/upd64083.c b/linux/drivers/media/video/upd64083.c
index 5f0c6919d..fe71b038b 100644
--- a/linux/drivers/media/video/upd64083.c
+++ b/linux/drivers/media/video/upd64083.c
@@ -26,7 +26,7 @@
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include <media/upd64083.h>
@@ -57,11 +57,17 @@ enum {
};
struct upd64083_state {
+ struct v4l2_subdev sd;
u8 mode;
u8 ext_y_adc;
u8 regs[TOT_REGS];
};
+static inline struct upd64083_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct upd64083_state, sd);
+}
+
/* Initial values when used in combination with the
NEC upd64031a ghost reduction chip. */
static u8 upd64083_init[] = {
@@ -74,34 +80,24 @@ static u8 upd64083_init[] = {
/* ------------------------------------------------------------------------ */
-static void upd64083_log_status(struct i2c_client *client)
-{
- u8 buf[7];
-
- i2c_master_recv(client, buf, 7);
- v4l_info(client, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x "
- "SA04=%02x SA05=%02x SA06=%02x\n",
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
-}
-
-/* ------------------------------------------------------------------------ */
-
-static void upd64083_write(struct i2c_client *client, u8 reg, u8 val)
+static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[2];
buf[0] = reg;
buf[1] = val;
- v4l_dbg(1, debug, client, "write reg: %02x val: %02x\n", reg, val);
+ v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val);
if (i2c_master_send(client, buf, 2) != 2)
- v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val);
+ v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val);
}
/* ------------------------------------------------------------------------ */
#ifdef CONFIG_VIDEO_ADV_DEBUG
-static u8 upd64083_read(struct i2c_client *client, u8 reg)
+static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 buf[7];
if (reg >= sizeof(buf))
@@ -113,67 +109,94 @@ static u8 upd64083_read(struct i2c_client *client, u8 reg)
/* ------------------------------------------------------------------------ */
-static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int upd64083_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct upd64083_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_VIDEO_ROUTING:
- route->input = (state->mode >> 6) | (state->ext_y_adc >> 3);
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_VIDEO_ROUTING:
- {
- u8 r00, r02;
-
- if (route->input > 7 || (route->input & 6) == 6)
- return -EINVAL;
- state->mode = (route->input & 3) << 6;
- state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3;
- r00 = (state->regs[R00] & ~(3 << 6)) | state->mode;
- r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc;
- upd64083_write(client, R00, r00);
- upd64083_write(client, R02, r02);
- break;
- }
-
- case VIDIOC_LOG_STATUS:
- upd64083_log_status(client);
- break;
+ struct upd64083_state *state = to_state(sd);
+ u8 r00, r02;
+
+ if (route->input > 7 || (route->input & 6) == 6)
+ return -EINVAL;
+ state->mode = (route->input & 3) << 6;
+ state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3;
+ r00 = (state->regs[R00] & ~(3 << 6)) | state->mode;
+ r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc;
+ upd64083_write(sd, R00, r00);
+ upd64083_write(sd, R02, r02);
+ return 0;
+}
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_G_REGISTER:
- case VIDIOC_DBG_S_REGISTER:
- {
- struct v4l2_register *reg = arg;
+static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
+ reg->match_type, reg->match_chip))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = upd64083_read(sd, reg->reg & 0xff);
+ return 0;
+}
- if (!v4l2_chip_match_i2c_client(client,
+static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client,
reg->match_type, reg->match_chip))
- return -EINVAL;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (cmd == VIDIOC_DBG_G_REGISTER) {
- reg->val = upd64083_read(client, reg->reg & 0xff);
- break;
- }
- upd64083_write(client, reg->reg & 0xff, reg->val & 0xff);
- break;
- }
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
#endif
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_UPD64083, 0);
+static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0);
+}
- default:
- break;
- }
+static int upd64083_log_status(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[7];
+ i2c_master_recv(client, buf, 7);
+ v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x "
+ "SA04=%02x SA05=%02x SA06=%02x\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
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 = {
+ .log_status = upd64083_log_status,
+ .g_chip_ident = upd64083_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = upd64083_g_register,
+ .s_register = upd64083_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops upd64083_video_ops = {
+ .s_routing = upd64083_s_routing,
+};
+
+static const struct v4l2_subdev_ops upd64083_ops = {
+ .core = &upd64083_core_ops,
+ .video = &upd64083_video_ops,
+};
+
/* ------------------------------------------------------------------------ */
/* i2c implementation */
@@ -182,6 +205,7 @@ static int upd64083_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct upd64083_state *state;
+ struct v4l2_subdev *sd;
int i;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -193,19 +217,23 @@ static int upd64083_probe(struct i2c_client *client,
state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
- i2c_set_clientdata(client, state);
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &upd64083_ops);
/* Initially assume that a ghost reduction chip is present */
state->mode = 0; /* YCS mode */
state->ext_y_adc = (1 << 5);
memcpy(state->regs, upd64083_init, TOT_REGS);
for (i = 0; i < TOT_REGS; i++)
- upd64083_write(client, i, state->regs[i]);
+ upd64083_write(sd, i, state->regs[i]);
return 0;
}
static int upd64083_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;
}
diff --git a/linux/drivers/media/video/uvc/uvc_ctrl.c b/linux/drivers/media/video/uvc/uvc_ctrl.c
index 9de8a1db5..e9a75b064 100644
--- a/linux/drivers/media/video/uvc/uvc_ctrl.c
+++ b/linux/drivers/media/video/uvc/uvc_ctrl.c
@@ -329,6 +329,31 @@ static struct uvc_menu_info exposure_auto_controls[] = {
{ 8, "Aperture Priority Mode" },
};
+static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping,
+ __u8 query, const __u8 *data)
+{
+ __s8 zoom = (__s8)data[0];
+
+ switch (query) {
+ case GET_CUR:
+ return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]);
+
+ case GET_MIN:
+ case GET_MAX:
+ case GET_RES:
+ case GET_DEF:
+ default:
+ return data[2];
+ }
+}
+
+static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping,
+ __s32 value, __u8 *data)
+{
+ data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff;
+ data[2] = min(abs(value), 0xff);
+}
+
static struct uvc_control_mapping uvc_ctrl_mappings[] = {
{
.id = V4L2_CID_BRIGHTNESS,
@@ -534,6 +559,38 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
},
+ {
+ .id = V4L2_CID_ZOOM_ABSOLUTE,
+ .name = "Zoom, Absolute",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ZOOM_ABSOLUTE_CONTROL,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_ZOOM_CONTINUOUS,
+ .name = "Zoom, Continuous",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_ZOOM_RELATIVE_CONTROL,
+ .size = 0,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ .get = uvc_ctrl_get_zoom,
+ .set = uvc_ctrl_set_zoom,
+ },
+ {
+ .id = V4L2_CID_PRIVACY,
+ .name = "Privacy",
+ .entity = UVC_GUID_UVC_CAMERA,
+ .selector = CT_PRIVACY_CONTROL,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
+ .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ },
};
/* ------------------------------------------------------------------------
@@ -545,18 +602,23 @@ static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
return ctrl->data + id * ctrl->info->size;
}
-static inline int uvc_get_bit(const __u8 *data, int bit)
+static inline int uvc_test_bit(const __u8 *data, int bit)
{
return (data[bit >> 3] >> (bit & 7)) & 1;
}
+static inline void uvc_clear_bit(__u8 *data, int bit)
+{
+ data[bit >> 3] &= ~(1 << (bit & 7));
+}
+
/* Extract the bit string specified by mapping->offset and mapping->size
* from the little-endian data stored at 'data' and return the result as
* a signed 32bit integer. Sign extension will be performed if the mapping
* references a signed data type.
*/
-static __s32 uvc_get_le_value(const __u8 *data,
- struct uvc_control_mapping *mapping)
+static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping,
+ __u8 query, const __u8 *data)
{
int bits = mapping->size;
int offset = mapping->offset;
@@ -585,8 +647,8 @@ static __s32 uvc_get_le_value(const __u8 *data,
/* Set the bit string specified by mapping->offset and mapping->size
* in the little-endian data stored at 'data' to the value 'value'.
*/
-static void uvc_set_le_value(__s32 value, __u8 *data,
- struct uvc_control_mapping *mapping)
+static void uvc_set_le_value(struct uvc_control_mapping *mapping,
+ __s32 value, __u8 *data)
{
int bits = mapping->size;
int offset = mapping->offset;
@@ -738,7 +800,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->default_value = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->default_value = mapping->get(mapping, GET_DEF, data);
}
switch (mapping->v4l2_type) {
@@ -774,21 +836,21 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->minimum = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->minimum = mapping->get(mapping, GET_MIN, data);
}
if (ctrl->info->flags & UVC_CONTROL_GET_MAX) {
if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->maximum = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->maximum = mapping->get(mapping, GET_MAX, data);
}
if (ctrl->info->flags & UVC_CONTROL_GET_RES) {
if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id,
video->dev->intfnum, ctrl->info->selector,
data, ctrl->info->size)) < 0)
goto out;
- v4l2_ctrl->step = uvc_get_le_value(data, mapping);
+ v4l2_ctrl->step = mapping->get(mapping, GET_RES, data);
}
ret = 0;
@@ -925,8 +987,8 @@ int uvc_ctrl_get(struct uvc_video_device *video,
ctrl->loaded = 1;
}
- xctrl->value = uvc_get_le_value(
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping);
+ xctrl->value = mapping->get(mapping, GET_CUR,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
menu = mapping->menu_info;
@@ -982,8 +1044,8 @@ int uvc_ctrl_set(struct uvc_video_device *video,
ctrl->info->size);
}
- uvc_set_le_value(value,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping);
+ mapping->set(mapping, value,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
ctrl->dirty = 1;
ctrl->modified = 1;
@@ -1259,6 +1321,11 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping)
struct uvc_control_mapping *map;
int ret = -EINVAL;
+ if (mapping->get == NULL)
+ mapping->get = uvc_get_le_value;
+ if (mapping->set == NULL)
+ mapping->set = uvc_set_le_value;
+
if (mapping->id & ~V4L2_CTRL_ID_MASK) {
uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with "
"invalid control id 0x%08x\n", mapping->name,
@@ -1308,6 +1375,51 @@ end:
}
/*
+ * Prune an entity of its bogus controls. This currently includes processing
+ * unit auto controls for which no corresponding manual control is available.
+ * Such auto controls make little sense if any, and are known to crash at
+ * least the SiGma Micro webcam.
+ */
+static void
+uvc_ctrl_prune_entity(struct uvc_entity *entity)
+{
+ static const struct {
+ u8 idx_manual;
+ u8 idx_auto;
+ } blacklist[] = {
+ { 2, 11 }, /* Hue */
+ { 6, 12 }, /* White Balance Temperature */
+ { 7, 13 }, /* White Balance Component */
+ };
+
+ u8 *controls;
+ unsigned int size;
+ unsigned int i;
+
+ if (UVC_ENTITY_TYPE(entity) != VC_PROCESSING_UNIT)
+ return;
+
+ controls = entity->processing.bmControls;
+ size = entity->processing.bControlSize;
+
+ for (i = 0; i < ARRAY_SIZE(blacklist); ++i) {
+ if (blacklist[i].idx_auto >= 8 * size ||
+ blacklist[i].idx_manual >= 8 * size)
+ continue;
+
+ if (!uvc_test_bit(controls, blacklist[i].idx_auto) ||
+ uvc_test_bit(controls, blacklist[i].idx_manual))
+ continue;
+
+ uvc_trace(UVC_TRACE_CONTROL, "Auto control %u/%u has no "
+ "matching manual control, removing it.\n", entity->id,
+ blacklist[i].idx_auto);
+
+ uvc_clear_bit(controls, blacklist[i].idx_auto);
+ }
+}
+
+/*
* Initialize device controls.
*/
int uvc_ctrl_init_device(struct uvc_device *dev)
@@ -1333,6 +1445,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
bControlSize = entity->camera.bControlSize;
}
+ if (dev->quirks & UVC_QUIRK_PRUNE_CONTROLS)
+ uvc_ctrl_prune_entity(entity);
+
for (i = 0; i < bControlSize; ++i)
ncontrols += hweight8(bmControls[i]);
@@ -1347,7 +1462,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
ctrl = entity->controls;
for (i = 0; i < bControlSize * 8; ++i) {
- if (uvc_get_bit(bmControls, i) == 0)
+ if (uvc_test_bit(bmControls, i) == 0)
continue;
ctrl->entity = entity;
diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c
index 0a0ef6611..343792f0f 100644
--- a/linux/drivers/media/video/uvc/uvc_driver.c
+++ b/linux/drivers/media/video/uvc/uvc_driver.c
@@ -44,6 +44,7 @@
#define DRIVER_VERSION "v0.1.0"
#endif
+unsigned int uvc_no_drop_param;
static unsigned int uvc_quirks_param;
unsigned int uvc_trace_param;
@@ -1895,7 +1896,8 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX
- | UVC_QUIRK_IGNORE_SELECTOR_UNIT},
+ | UVC_QUIRK_IGNORE_SELECTOR_UNIT
+ | UVC_QUIRK_PRUNE_CONTROLS },
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
{}
@@ -1945,6 +1947,8 @@ static void __exit uvc_cleanup(void)
module_init(uvc_init);
module_exit(uvc_cleanup);
+module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames");
module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(quirks, "Forced device quirks");
module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR);
diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c
index 95eb6f3ad..b6ee9aac6 100644
--- a/linux/drivers/media/video/uvc/uvc_v4l2.c
+++ b/linux/drivers/media/video/uvc/uvc_v4l2.c
@@ -920,7 +920,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- info = kmalloc(sizeof *info, GFP_KERNEL);
+ info = kzalloc(sizeof *info, GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
@@ -947,7 +947,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- map = kmalloc(sizeof *map, GFP_KERNEL);
+ map = kzalloc(sizeof *map, GFP_KERNEL);
if (map == NULL)
return -ENOMEM;
diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c
index 3be776a47..b19e91dd5 100644
--- a/linux/drivers/media/video/uvc/uvc_video.c
+++ b/linux/drivers/media/video/uvc/uvc_video.c
@@ -1007,7 +1007,8 @@ int uvc_video_enable(struct uvc_video_device *video, int enable)
return 0;
}
- if (video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED)
+ if ((video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) ||
+ uvc_no_drop_param)
video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE;
else
video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE;
diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h
index f95040380..549d09fd3 100644
--- a/linux/drivers/media/video/uvc/uvcvideo.h
+++ b/linux/drivers/media/video/uvc/uvcvideo.h
@@ -4,8 +4,6 @@
#include <linux/kernel.h>
#include <linux/videodev2.h>
-#include "compat.h"
-
/*
* Dynamic controls
*/
@@ -69,6 +67,7 @@ struct uvc_xu_control {
#ifdef __KERNEL__
#include <linux/poll.h>
+#include "compat.h"
/* --------------------------------------------------------------------------
* UVC constants
@@ -317,6 +316,7 @@ struct uvc_xu_control {
#define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008
#define UVC_QUIRK_STREAM_NO_FID 0x00000010
#define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020
+#define UVC_QUIRK_PRUNE_CONTROLS 0x00000040
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
@@ -384,6 +384,11 @@ struct uvc_control_mapping {
struct uvc_menu_info *menu_info;
__u32 menu_count;
+
+ __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query,
+ const __u8 *data);
+ void (*set) (struct uvc_control_mapping *mapping, __s32 value,
+ __u8 *data);
};
struct uvc_control {
@@ -684,6 +689,7 @@ struct uvc_driver {
#define UVC_WARN_MINMAX 0
#define UVC_WARN_PROBE_DEF 1
+extern unsigned int uvc_no_drop_param;
extern unsigned int uvc_trace_param;
#define uvc_trace(flag, msg...) \
diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c
index e87a04a60..02eb97fa4 100644
--- a/linux/drivers/media/video/v4l2-common.c
+++ b/linux/drivers/media/video/v4l2-common.c
@@ -58,6 +58,7 @@
#include <asm/div64.h>
#define __OLD_VIDIOC_ /* To allow fixing old calls*/
#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <linux/videodev2.h>
@@ -321,6 +322,19 @@ const char **v4l2_ctrl_get_menu(u32 id)
"Private packet, IVTV format",
NULL
};
+ static const char *camera_power_line_frequency[] = {
+ "Disabled",
+ "50 Hz",
+ "60 Hz",
+ NULL
+ };
+ static const char *camera_exposure_auto[] = {
+ "Auto Mode",
+ "Manual Mode",
+ "Shutter Priority Mode",
+ "Aperture Priority Mode",
+ NULL
+ };
switch (id) {
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
@@ -353,6 +367,10 @@ const char **v4l2_ctrl_get_menu(u32 id)
return mpeg_stream_type;
case V4L2_CID_MPEG_STREAM_VBI_FMT:
return mpeg_stream_vbi_fmt;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ return camera_power_line_frequency;
+ case V4L2_CID_EXPOSURE_AUTO:
+ return camera_exposure_auto;
default:
return NULL;
}
@@ -364,17 +382,37 @@ const char *v4l2_ctrl_get_name(u32 id)
{
switch (id) {
/* USER controls */
- case V4L2_CID_USER_CLASS: return "User Controls";
- 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_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_USER_CLASS: return "User Controls";
+ 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_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";
+ case V4L2_CID_RED_BALANCE: return "Red Balance";
+ case V4L2_CID_BLUE_BALANCE: return "Blue Balance";
+ case V4L2_CID_GAMMA: return "Gamma";
+ case V4L2_CID_EXPOSURE: return "Exposure";
+ case V4L2_CID_AUTOGAIN: return "Gain, Automatic";
+ case V4L2_CID_GAIN: return "Gain";
+ case V4L2_CID_HFLIP: return "Horizontal Flip";
+ case V4L2_CID_VFLIP: return "Vertical Flip";
+ case V4L2_CID_HCENTER: return "Horizontal Center";
+ case V4L2_CID_VCENTER: return "Vertical Center";
+ case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency";
+ case V4L2_CID_HUE_AUTO: return "Hue, Automatic";
+ case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature";
+ case V4L2_CID_SHARPNESS: return "Sharpness";
+ case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
+ case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
+ case V4L2_CID_COLOR_KILLER: return "Color Killer";
/* MPEG controls */
case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
@@ -411,6 +449,25 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID";
case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format";
+ /* CAMERA controls */
+ case V4L2_CID_CAMERA_CLASS: return "Camera Controls";
+ case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure";
+ case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute";
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate";
+ case V4L2_CID_PAN_RELATIVE: return "Pan, Relative";
+ case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative";
+ case V4L2_CID_PAN_RESET: return "Pan, Reset";
+ case V4L2_CID_TILT_RESET: return "Tilt, Reset";
+ case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute";
+ case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute";
+ case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute";
+ case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative";
+ case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic";
+ case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute";
+ case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative";
+ case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous";
+ case V4L2_CID_PRIVACY: return "Privacy";
+
default:
return NULL;
}
@@ -429,14 +486,22 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
switch (qctrl->id) {
case V4L2_CID_AUDIO_MUTE:
case V4L2_CID_AUDIO_LOUDNESS:
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ case V4L2_CID_AUTOGAIN:
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HUE_AUTO:
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_PRIVACY:
qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
min = 0;
max = step = 1;
break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
case V4L2_CID_MPEG_AUDIO_ENCODING:
case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
@@ -452,10 +517,12 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
case V4L2_CID_MPEG_STREAM_TYPE:
case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ case V4L2_CID_EXPOSURE_AUTO:
qctrl->type = V4L2_CTRL_TYPE_MENU;
step = 1;
break;
case V4L2_CID_USER_CLASS:
+ case V4L2_CID_CAMERA_CLASS:
case V4L2_CID_MPEG_CLASS:
qctrl->type = V4L2_CTRL_TYPE_CTRL_CLASS;
qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
@@ -802,4 +869,173 @@ int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver
return err != -ENOMEM ? 0 : err;
}
EXPORT_SYMBOL(v4l2_i2c_attach);
+
+void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
+ const struct v4l2_subdev_ops *ops)
+{
+ v4l2_subdev_init(sd, ops);
+ /* the owner is the same as the i2c_client's driver owner */
+ sd->owner = client->driver->driver.owner;
+ /* i2c_client and v4l2_subdev point to one another */
+ v4l2_set_subdevdata(sd, client);
+ i2c_set_clientdata(client, sd);
+ /* initialize name */
+ snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
+ client->driver->driver.name, i2c_adapter_id(client->adapter),
+ client->addr);
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+/* Supporting function to find a client on a specific address on the
+ given adapter. Used for legacy i2c drivers. */
+static struct i2c_client *v4l2_i2c_legacy_find_client(struct i2c_adapter *adap, u8 addr)
+{
+ struct i2c_client *result = NULL;
+ struct i2c_client *client;
+ struct list_head *item;
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16)
+ down(&adap->clist_lock);
+#else
+ mutex_lock(&adap->clist_lock);
+#endif
+ list_for_each(item, &adap->clients) {
+ client = list_entry(item, struct i2c_client, list);
+ if (client->addr == addr) {
+ result = client;
+ break;
+ }
+ }
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16)
+ up(&adap->clist_lock);
+#else
+ mutex_unlock(&adap->clist_lock);
+#endif
+ return result;
+}
+#endif
+
+
+/* Load an i2c sub-device. It assumes that i2c_get_adapdata(adapter)
+ returns the v4l2_device and that i2c_get_clientdata(client)
+ returns the v4l2_subdev. */
+struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter,
+ const char *module_name, const char *client_type, u8 addr)
+{
+ struct v4l2_device *dev = i2c_get_adapdata(adapter);
+ struct v4l2_subdev *sd = NULL;
+ struct i2c_client *client;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+ struct i2c_board_info info;
+#endif
+
+ BUG_ON(!dev);
+#ifdef MODULE
+ if (module_name)
+ request_module(module_name);
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+ /* Setup the i2c board info with the device type and
+ the device address. */
+ memset(&info, 0, sizeof(info));
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
+ strlcpy(info.driver_name, client_type, sizeof(info.driver_name));
+#else
+ strlcpy(info.type, client_type, sizeof(info.type));
+#endif
+ info.addr = addr;
+
+ /* Create the i2c client */
+ client = i2c_new_device(adapter, &info);
+#else
+ /* Legacy code: loading the module automatically
+ probes and creates the i2c_client on the adapter.
+ Try to find the client by walking the adapter's client list. */
+ client = v4l2_i2c_legacy_find_client(adapter, addr);
+#endif
+ /* Note: it is possible in the future that
+ c->driver is NULL if the driver is still being loaded.
+ 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;
+
+ /* Lock the module so we can safely get the v4l2_subdev pointer */
+ if (!try_module_get(client->driver->driver.owner))
+ return NULL;
+ sd = i2c_get_clientdata(client);
+
+ /* Register with the v4l2_device which increases the module's
+ use count as well. */
+ if (v4l2_device_register_subdev(dev, sd))
+ sd = NULL;
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(client->driver->driver.owner);
+ return sd;
+
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev);
+
+/* Probe and load an i2c sub-device. It assumes that i2c_get_adapdata(adapter)
+ returns the v4l2_device and that i2c_get_clientdata(client)
+ returns the v4l2_subdev. */
+struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter,
+ const char *module_name, const char *client_type,
+ const unsigned short *addrs)
+{
+ struct v4l2_device *dev = i2c_get_adapdata(adapter);
+ struct v4l2_subdev *sd = NULL;
+ struct i2c_client *client = NULL;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+ struct i2c_board_info info;
+#endif
+
+ BUG_ON(!dev);
+#ifdef MODULE
+ if (module_name)
+ request_module(module_name);
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+ /* Setup the i2c board info with the device type and
+ the device address. */
+ memset(&info, 0, sizeof(info));
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
+ strlcpy(info.driver_name, client_type, sizeof(info.driver_name));
+#else
+ strlcpy(info.type, client_type, sizeof(info.type));
+#endif
+
+ /* Probe and create the i2c client */
+ client = i2c_new_probed_device(adapter, &info, addrs);
+#else
+ /* Legacy code: loading the module should automatically
+ probe and create the i2c_client on the adapter.
+ Try to find the client by walking the adapter's client list
+ for each of the possible addresses. */
+ while (!client && *addrs != I2C_CLIENT_END)
+ client = v4l2_i2c_legacy_find_client(adapter, *addrs++);
+#endif
+ /* Note: it is possible in the future that
+ c->driver is NULL if the driver is still being loaded.
+ 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;
+
+ /* Lock the module so we can safely get the v4l2_subdev pointer */
+ if (!try_module_get(client->driver->driver.owner))
+ return NULL;
+ sd = i2c_get_clientdata(client);
+
+ /* Register with the v4l2_device which increases the module's
+ use count as well. */
+ if (v4l2_device_register_subdev(dev, sd))
+ sd = NULL;
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(client->driver->driver.owner);
+ return sd;
+}
+EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev);
+
#endif
diff --git a/linux/drivers/media/video/v4l2-compat-ioctl32.c b/linux/drivers/media/video/v4l2-compat-ioctl32.c
index 09d5c5693..171f1ccd1 100644
--- a/linux/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/linux/drivers/media/video/v4l2-compat-ioctl32.c
@@ -887,7 +887,7 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret = -ENOIOCTLCMD;
- if (!file->f_op->ioctl)
+ if (!file->f_op->ioctl && !file->f_op->unlocked_ioctl)
return ret;
switch (cmd) {
@@ -977,6 +977,8 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
#endif
default:
v4l_print_ioctl("compat_ioctl32", cmd);
+ printk(KERN_CONT "\n");
+ break;
}
return ret;
}
diff --git a/linux/drivers/media/video/v4l2-device.c b/linux/drivers/media/video/v4l2-device.c
new file mode 100644
index 000000000..9eefde031
--- /dev/null
+++ b/linux/drivers/media/video/v4l2-device.c
@@ -0,0 +1,86 @@
+/*
+ V4L2 device support.
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+
+int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
+{
+ if (dev == NULL || v4l2_dev == NULL)
+ return -EINVAL;
+ /* Warn if we apparently re-register a device */
+ WARN_ON(dev_get_drvdata(dev));
+ 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);
+ dev_set_drvdata(dev, v4l2_dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_device_register);
+
+void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_subdev *sd, *next;
+
+ if (v4l2_dev == NULL || v4l2_dev->dev == NULL)
+ return;
+ dev_set_drvdata(v4l2_dev->dev, NULL);
+ /* 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)
+{
+ /* Check for valid input */
+ if (dev == NULL || sd == NULL || !sd->name[0])
+ return -EINVAL;
+ /* Warn if we apparently re-register a subdev */
+ WARN_ON(sd->dev);
+ 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);
+ return 0;
+}
+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)
+ return;
+ spin_lock(&sd->dev->lock);
+ list_del(&sd->list);
+ spin_unlock(&sd->dev->lock);
+ sd->dev = NULL;
+ module_put(sd->owner);
+}
+EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
diff --git a/linux/drivers/media/video/v4l2-subdev.c b/linux/drivers/media/video/v4l2-subdev.c
new file mode 100644
index 000000000..fe1f01c97
--- /dev/null
+++ b/linux/drivers/media/video/v4l2-subdev.c
@@ -0,0 +1,108 @@
+/*
+ V4L2 sub-device support.
+
+ Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-subdev.h>
+
+int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg)
+{
+ switch (cmd) {
+ case VIDIOC_QUERYCTRL:
+ return v4l2_subdev_call(sd, core, querymenu, arg);
+ case VIDIOC_G_CTRL:
+ return v4l2_subdev_call(sd, core, g_ctrl, arg);
+ case VIDIOC_S_CTRL:
+ return v4l2_subdev_call(sd, core, s_ctrl, arg);
+ case VIDIOC_QUERYMENU:
+ return v4l2_subdev_call(sd, core, queryctrl, arg);
+ case VIDIOC_LOG_STATUS:
+ return v4l2_subdev_call(sd, core, log_status);
+ case VIDIOC_G_CHIP_IDENT:
+ return v4l2_subdev_call(sd, core, g_chip_ident, arg);
+ case VIDIOC_INT_S_STANDBY:
+ return v4l2_subdev_call(sd, core, s_standby, *(u32 *)arg);
+ case VIDIOC_INT_RESET:
+ return v4l2_subdev_call(sd, core, reset, *(u32 *)arg);
+ case VIDIOC_INT_S_GPIO:
+ return v4l2_subdev_call(sd, core, s_gpio, *(u32 *)arg);
+ case VIDIOC_INT_INIT:
+ return v4l2_subdev_call(sd, core, init, *(u32 *)arg);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ case VIDIOC_DBG_G_REGISTER:
+ return v4l2_subdev_call(sd, core, g_register, arg);
+ case VIDIOC_DBG_S_REGISTER:
+ return v4l2_subdev_call(sd, core, s_register, arg);
+#endif
+
+ case VIDIOC_INT_S_TUNER_MODE:
+ return v4l2_subdev_call(sd, tuner, s_mode, *(enum v4l2_tuner_type *)arg);
+ case AUDC_SET_RADIO:
+ return v4l2_subdev_call(sd, tuner, s_radio);
+ case VIDIOC_S_TUNER:
+ return v4l2_subdev_call(sd, tuner, s_tuner, arg);
+ case VIDIOC_G_TUNER:
+ return v4l2_subdev_call(sd, tuner, g_tuner, arg);
+ case VIDIOC_S_STD:
+ return v4l2_subdev_call(sd, tuner, s_std, *(v4l2_std_id *)arg);
+ case VIDIOC_S_FREQUENCY:
+ return v4l2_subdev_call(sd, tuner, s_frequency, arg);
+ case VIDIOC_G_FREQUENCY:
+ return v4l2_subdev_call(sd, tuner, g_frequency, arg);
+ case TUNER_SET_TYPE_ADDR:
+ return v4l2_subdev_call(sd, tuner, s_type_addr, arg);
+ case TUNER_SET_CONFIG:
+ return v4l2_subdev_call(sd, tuner, s_config, arg);
+
+ case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+ return v4l2_subdev_call(sd, audio, s_clock_freq, *(u32 *)arg);
+ case VIDIOC_INT_S_AUDIO_ROUTING:
+ return v4l2_subdev_call(sd, audio, s_routing, arg);
+ case VIDIOC_INT_I2S_CLOCK_FREQ:
+ return v4l2_subdev_call(sd, audio, s_i2s_clock_freq, *(u32 *)arg);
+
+ case VIDIOC_INT_S_VIDEO_ROUTING:
+ return v4l2_subdev_call(sd, video, s_routing, arg);
+ case VIDIOC_INT_S_CRYSTAL_FREQ:
+ return v4l2_subdev_call(sd, video, s_crystal_freq, arg);
+ case VIDIOC_INT_DECODE_VBI_LINE:
+ return v4l2_subdev_call(sd, video, decode_vbi_line, arg);
+ case VIDIOC_INT_S_VBI_DATA:
+ return v4l2_subdev_call(sd, video, s_vbi_data, arg);
+ case VIDIOC_INT_G_VBI_DATA:
+ return v4l2_subdev_call(sd, video, g_vbi_data, 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_STREAMON:
+ return v4l2_subdev_call(sd, video, s_stream, 1);
+ case VIDIOC_STREAMOFF:
+ return v4l2_subdev_call(sd, video, s_stream, 0);
+
+ default:
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+ }
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_command);
diff --git a/linux/drivers/media/video/vp27smpx.c b/linux/drivers/media/video/vp27smpx.c
index 80de9e796..cd9df6a1c 100644
--- a/linux/drivers/media/video/vp27smpx.c
+++ b/linux/drivers/media/video/vp27smpx.c
@@ -28,7 +28,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include "compat.h"
@@ -46,13 +46,20 @@ I2C_CLIENT_INSMOD;
/* ----------------------------------------------------------------------- */
struct vp27smpx_state {
+ struct v4l2_subdev sd;
int radio;
u32 audmode;
};
-static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode)
+static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd)
{
- struct vp27smpx_state *state = i2c_get_clientdata(client);
+ return container_of(sd, struct vp27smpx_state, sd);
+}
+
+static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode)
+{
+ struct vp27smpx_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
u8 data[3] = { 0x00, 0x00, 0x04 };
switch (audmode) {
@@ -69,55 +76,89 @@ static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode)
}
if (i2c_master_send(client, data, sizeof(data)) != sizeof(data))
- v4l_err(client, "%s: I/O error setting audmode\n",
- client->name);
+ v4l2_err(sd, "I/O error setting audmode\n");
else
state->audmode = audmode;
}
-static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int vp27smpx_s_radio(struct v4l2_subdev *sd)
{
- struct vp27smpx_state *state = i2c_get_clientdata(client);
- struct v4l2_tuner *vt = arg;
+ struct vp27smpx_state *state = to_state(sd);
- switch (cmd) {
- case AUDC_SET_RADIO:
- state->radio = 1;
- break;
+ state->radio = 1;
+ return 0;
+}
- case VIDIOC_S_STD:
- state->radio = 0;
- break;
+static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+{
+ struct vp27smpx_state *state = to_state(sd);
- case VIDIOC_S_TUNER:
- if (!state->radio)
- vp27smpx_set_audmode(client, vt->audmode);
- break;
+ state->radio = 0;
+ return 0;
+}
- case VIDIOC_G_TUNER:
- if (state->radio)
- break;
- vt->audmode = state->audmode;
- vt->capability = V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
- vt->rxsubchans = V4L2_TUNER_SUB_MONO;
- break;
+static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct vp27smpx_state *state = to_state(sd);
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client, arg,
- V4L2_IDENT_VP27SMPX, 0);
+ if (!state->radio)
+ vp27smpx_set_audmode(sd, vt->audmode);
+ return 0;
+}
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Audio Mode: %u%s\n", state->audmode,
- state->radio ? " (Radio)" : "");
- break;
+static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+{
+ struct vp27smpx_state *state = to_state(sd);
+
+ if (state->radio)
+ return 0;
+ vt->audmode = state->audmode;
+ vt->capability = V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ return 0;
+}
- default:
- return -EINVAL;
- }
+static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0);
+}
+
+static int vp27smpx_log_status(struct v4l2_subdev *sd)
+{
+ struct vp27smpx_state *state = to_state(sd);
+
+ v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode,
+ state->radio ? " (Radio)" : "");
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 = {
+ .log_status = vp27smpx_log_status,
+ .g_chip_ident = vp27smpx_g_chip_ident,
+};
+
+static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = {
+ .s_radio = vp27smpx_s_radio,
+ .s_std = vp27smpx_s_std,
+ .s_tuner = vp27smpx_s_tuner,
+ .g_tuner = vp27smpx_g_tuner,
+};
+
+static const struct v4l2_subdev_ops vp27smpx_ops = {
+ .core = &vp27smpx_core_ops,
+ .tuner = &vp27smpx_tuner_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -131,6 +172,7 @@ static int vp27smpx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct vp27smpx_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -145,17 +187,21 @@ static int vp27smpx_probe(struct i2c_client *client,
state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops);
state->audmode = V4L2_TUNER_MODE_STEREO;
- i2c_set_clientdata(client, state);
/* initialize vp27smpx */
- vp27smpx_set_audmode(client, state->audmode);
+ vp27smpx_set_audmode(sd, state->audmode);
return 0;
}
static int vp27smpx_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;
}
diff --git a/linux/drivers/media/video/wm8739.c b/linux/drivers/media/video/wm8739.c
index f9d13b89a..ea311672c 100644
--- a/linux/drivers/media/video/wm8739.c
+++ b/linux/drivers/media/video/wm8739.c
@@ -28,7 +28,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv.h>
#include "compat.h"
@@ -58,6 +58,7 @@ enum {
};
struct wm8739_state {
+ struct v4l2_subdev sd;
u32 clock_freq;
u8 muted;
u16 volume;
@@ -66,43 +67,49 @@ struct wm8739_state {
u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */
};
+static inline struct wm8739_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct wm8739_state, sd);
+}
+
/* ------------------------------------------------------------------------ */
-static int wm8739_write(struct i2c_client *client, int reg, u16 val)
+static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val)
{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
if (reg < 0 || reg >= TOT_REGS) {
- v4l_err(client, "Invalid register R%d\n", reg);
+ v4l2_err(sd, "Invalid register R%d\n", reg);
return -1;
}
- v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val);
+ v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val);
for (i = 0; i < 3; i++)
if (i2c_smbus_write_byte_data(client,
(reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
/* write regs to set audio volume etc */
-static void wm8739_set_audio(struct i2c_client *client)
+static void wm8739_set_audio(struct v4l2_subdev *sd)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
u16 mute = state->muted ? 0x80 : 0;
/* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB
* Default setting: 0x17 = 0 dB
*/
- wm8739_write(client, R0, (state->vol_l & 0x1f) | mute);
- wm8739_write(client, R1, (state->vol_r & 0x1f) | mute);
+ wm8739_write(sd, R0, (state->vol_l & 0x1f) | mute);
+ wm8739_write(sd, R1, (state->vol_r & 0x1f) | mute);
}
-static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int wm8739_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -123,9 +130,9 @@ static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
return 0;
}
-static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
+ struct wm8739_state *state = to_state(sd);
unsigned int work_l, work_r;
switch (ctrl->id) {
@@ -153,7 +160,7 @@ static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
state->vol_r = (long)work_r * 31 / 65535;
/* set audio volume etc. */
- wm8739_set_audio(client);
+ wm8739_set_audio(sd);
return 0;
}
@@ -192,77 +199,89 @@ static struct v4l2_queryctrl wm8739_qctrl[] = {
/* ------------------------------------------------------------------------ */
-static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq)
{
- struct wm8739_state *state = i2c_get_clientdata(client);
-
- switch (cmd) {
- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- {
- u32 audiofreq = *(u32 *)arg;
-
- state->clock_freq = audiofreq;
- /* de-activate */
- wm8739_write(client, R9, 0x000);
- switch (audiofreq) {
- case 44100:
- /* 256fps, fs=44.1k */
- wm8739_write(client, R8, 0x020);
- break;
- case 48000:
- /* 256fps, fs=48k */
- wm8739_write(client, R8, 0x000);
- break;
- case 32000:
- /* 256fps, fs=32k */
- wm8739_write(client, R8, 0x018);
- break;
- default:
- break;
- }
- /* activate */
- wm8739_write(client, R9, 0x001);
+ struct wm8739_state *state = to_state(sd);
+
+ state->clock_freq = audiofreq;
+ /* de-activate */
+ wm8739_write(sd, R9, 0x000);
+ switch (audiofreq) {
+ case 44100:
+ /* 256fps, fs=44.1k */
+ wm8739_write(sd, R8, 0x020);
+ break;
+ case 48000:
+ /* 256fps, fs=48k */
+ wm8739_write(sd, R8, 0x000);
+ break;
+ case 32000:
+ /* 256fps, fs=32k */
+ wm8739_write(sd, R8, 0x018);
+ break;
+ default:
break;
}
+ /* activate */
+ wm8739_write(sd, R9, 0x001);
+ return 0;
+}
- case VIDIOC_G_CTRL:
- return wm8739_get_ctrl(client, arg);
-
- case VIDIOC_S_CTRL:
- return wm8739_set_ctrl(client, arg);
+static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ int i;
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *qc = arg;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++)
- if (qc->id && qc->id == wm8739_qctrl[i].id) {
- memcpy(qc, &wm8739_qctrl[i], sizeof(*qc));
- return 0;
- }
- return -EINVAL;
- }
+ for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++)
+ if (qc->id && qc->id == wm8739_qctrl[i].id) {
+ memcpy(qc, &wm8739_qctrl[i], sizeof(*qc));
+ return 0;
+ }
+ return -EINVAL;
+}
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_WM8739, 0);
+static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Frequency: %u Hz\n", state->clock_freq);
- v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f,
- state->muted ? " (muted)" : "");
- v4l_info(client, "Volume R: %02x%s\n", state->vol_r & 0x1f,
- state->muted ? " (muted)" : "");
- break;
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0);
+}
- default:
- return -EINVAL;
- }
+static int wm8739_log_status(struct v4l2_subdev *sd)
+{
+ struct wm8739_state *state = to_state(sd);
+ v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq);
+ v4l2_info(sd, "Volume L: %02x%s\n", state->vol_l & 0x1f,
+ state->muted ? " (muted)" : "");
+ v4l2_info(sd, "Volume R: %02x%s\n", state->vol_r & 0x1f,
+ state->muted ? " (muted)" : "");
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 = {
+ .log_status = wm8739_log_status,
+ .g_chip_ident = wm8739_g_chip_ident,
+ .queryctrl = wm8739_queryctrl,
+ .g_ctrl = wm8739_g_ctrl,
+ .s_ctrl = wm8739_s_ctrl,
+};
+
+static const struct v4l2_subdev_audio_ops wm8739_audio_ops = {
+ .s_clock_freq = wm8739_s_clock_freq,
+};
+
+static const struct v4l2_subdev_ops wm8739_ops = {
+ .core = &wm8739_core_ops,
+ .audio = &wm8739_audio_ops,
+};
+
/* ------------------------------------------------------------------------ */
/* i2c implementation */
@@ -271,6 +290,7 @@ static int wm8739_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct wm8739_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -282,6 +302,8 @@ static int wm8739_probe(struct i2c_client *client,
state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &wm8739_ops);
state->vol_l = 0x17; /* 0dB */
state->vol_r = 0x17; /* 0dB */
state->muted = 0;
@@ -289,31 +311,33 @@ static int wm8739_probe(struct i2c_client *client,
/* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */
state->volume = ((long)state->vol_l + 1) * 65535 / 31;
state->clock_freq = 48000;
- i2c_set_clientdata(client, state);
/* Initialize wm8739 */
/* reset */
- wm8739_write(client, R15, 0x00);
+ wm8739_write(sd, R15, 0x00);
/* filter setting, high path, offet clear */
- wm8739_write(client, R5, 0x000);
+ wm8739_write(sd, R5, 0x000);
/* ADC, OSC, Power Off mode Disable */
- wm8739_write(client, R6, 0x000);
+ wm8739_write(sd, R6, 0x000);
/* Digital Audio interface format:
Enable Master mode, 24 bit, MSB first/left justified */
- wm8739_write(client, R7, 0x049);
+ wm8739_write(sd, R7, 0x049);
/* sampling control: normal, 256fs, 48KHz sampling rate */
- wm8739_write(client, R8, 0x000);
+ wm8739_write(sd, R8, 0x000);
/* activate */
- wm8739_write(client, R9, 0x001);
+ wm8739_write(sd, R9, 0x001);
/* set volume/mute */
- wm8739_set_audio(client);
+ wm8739_set_audio(sd);
return 0;
}
static int wm8739_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;
}
diff --git a/linux/drivers/media/video/wm8775.c b/linux/drivers/media/video/wm8775.c
index ff7fa162b..526926635 100644
--- a/linux/drivers/media/video/wm8775.c
+++ b/linux/drivers/media/video/wm8775.c
@@ -32,7 +32,7 @@
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
#include <media/v4l2-i2c-drv-legacy.h>
#include "compat.h"
@@ -55,16 +55,23 @@ enum {
};
struct wm8775_state {
+ struct v4l2_subdev sd;
u8 input; /* Last selected input (0-0xf) */
u8 muted;
};
-static int wm8775_write(struct i2c_client *client, int reg, u16 val)
+static inline struct wm8775_state *to_state(struct v4l2_subdev *sd)
{
+ return container_of(sd, struct wm8775_state, sd);
+}
+
+static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
int i;
if (reg < 0 || reg >= TOT_REGS) {
- v4l_err(client, "Invalid register R%d\n", reg);
+ v4l2_err(sd, "Invalid register R%d\n", reg);
return -1;
}
@@ -72,84 +79,117 @@ static int wm8775_write(struct i2c_client *client, int reg, u16 val)
if (i2c_smbus_write_byte_data(client,
(reg << 1) | (val >> 8), val & 0xff) == 0)
return 0;
- v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg);
+ v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg);
return -1;
}
-static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg)
+static int wm8775_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
{
- struct wm8775_state *state = i2c_get_clientdata(client);
- struct v4l2_routing *route = arg;
- struct v4l2_control *ctrl = arg;
-
- switch (cmd) {
- case VIDIOC_INT_G_AUDIO_ROUTING:
- route->input = state->input;
- route->output = 0;
- break;
-
- case VIDIOC_INT_S_AUDIO_ROUTING:
- /* There are 4 inputs and one output. Zero or more inputs
- are multiplexed together to the output. Hence there are
- 16 combinations.
- If only one input is active (the normal case) then the
- input values 1, 2, 4 or 8 should be used. */
- if (route->input > 15) {
- v4l_err(client, "Invalid input %d.\n", route->input);
- return -EINVAL;
- }
- state->input = route->input;
- if (state->muted)
- break;
- wm8775_write(client, R21, 0x0c0);
- wm8775_write(client, R14, 0x1d4);
- wm8775_write(client, R15, 0x1d4);
- wm8775_write(client, R21, 0x100 + state->input);
- break;
-
- case VIDIOC_G_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- ctrl->value = state->muted;
- break;
-
- case VIDIOC_S_CTRL:
- if (ctrl->id != V4L2_CID_AUDIO_MUTE)
- return -EINVAL;
- state->muted = ctrl->value;
- wm8775_write(client, R21, 0x0c0);
- wm8775_write(client, R14, 0x1d4);
- wm8775_write(client, R15, 0x1d4);
- if (!state->muted)
- wm8775_write(client, R21, 0x100 + state->input);
- break;
-
- case VIDIOC_G_CHIP_IDENT:
- return v4l2_chip_ident_i2c_client(client,
- arg, V4L2_IDENT_WM8775, 0);
-
- case VIDIOC_LOG_STATUS:
- v4l_info(client, "Input: %d%s\n", state->input,
- state->muted ? " (muted)" : "");
- break;
-
- case VIDIOC_S_FREQUENCY:
- /* If I remove this, then it can happen that I have no
- sound the first time I tune from static to a valid channel.
- It's difficult to reproduce and is almost certainly related
- to the zero cross detect circuit. */
- wm8775_write(client, R21, 0x0c0);
- wm8775_write(client, R14, 0x1d4);
- wm8775_write(client, R15, 0x1d4);
- wm8775_write(client, R21, 0x100 + state->input);
- break;
-
- default:
+ struct wm8775_state *state = to_state(sd);
+
+ /* There are 4 inputs and one output. Zero or more inputs
+ are multiplexed together to the output. Hence there are
+ 16 combinations.
+ If only one input is active (the normal case) then the
+ input values 1, 2, 4 or 8 should be used. */
+ if (route->input > 15) {
+ v4l2_err(sd, "Invalid input %d.\n", route->input);
return -EINVAL;
}
+ state->input = route->input;
+ if (state->muted)
+ return 0;
+ wm8775_write(sd, R21, 0x0c0);
+ wm8775_write(sd, R14, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
+ wm8775_write(sd, R21, 0x100 + state->input);
+ return 0;
+}
+
+static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ ctrl->value = state->muted;
+ return 0;
+}
+
+static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ if (ctrl->id != V4L2_CID_AUDIO_MUTE)
+ return -EINVAL;
+ state->muted = ctrl->value;
+ wm8775_write(sd, R21, 0x0c0);
+ wm8775_write(sd, R14, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
+ if (!state->muted)
+ wm8775_write(sd, R21, 0x100 + state->input);
+ return 0;
+}
+
+static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0);
+}
+
+static int wm8775_log_status(struct v4l2_subdev *sd)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ v4l2_info(sd, "Input: %d%s\n", state->input,
+ state->muted ? " (muted)" : "");
return 0;
}
+static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq)
+{
+ struct wm8775_state *state = to_state(sd);
+
+ /* If I remove this, then it can happen that I have no
+ sound the first time I tune from static to a valid channel.
+ It's difficult to reproduce and is almost certainly related
+ to the zero cross detect circuit. */
+ wm8775_write(sd, R21, 0x0c0);
+ wm8775_write(sd, R14, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
+ wm8775_write(sd, R21, 0x100 + state->input);
+ 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 = {
+ .log_status = wm8775_log_status,
+ .g_chip_ident = wm8775_g_chip_ident,
+ .g_ctrl = wm8775_g_ctrl,
+ .s_ctrl = wm8775_s_ctrl,
+};
+
+static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = {
+ .s_frequency = wm8775_s_frequency,
+};
+
+static const struct v4l2_subdev_audio_ops wm8775_audio_ops = {
+ .s_routing = wm8775_s_routing,
+};
+
+static const struct v4l2_subdev_ops wm8775_ops = {
+ .core = &wm8775_core_ops,
+ .tuner = &wm8775_tuner_ops,
+ .audio = &wm8775_audio_ops,
+};
+
/* ----------------------------------------------------------------------- */
/* i2c implementation */
@@ -163,56 +203,61 @@ static int wm8775_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct wm8775_state *state;
+ struct v4l2_subdev *sd;
/* Check if the adapter supports the needed features */
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- v4l_info(client, "chip found @ 0x%x (%s)\n",
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
client->addr << 1, client->adapter->name);
state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL);
if (state == NULL)
return -ENOMEM;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &wm8775_ops);
state->input = 2;
state->muted = 0;
- i2c_set_clientdata(client, state);
/* Initialize wm8775 */
/* RESET */
- wm8775_write(client, R23, 0x000);
+ wm8775_write(sd, R23, 0x000);
/* Disable zero cross detect timeout */
- wm8775_write(client, R7, 0x000);
+ wm8775_write(sd, R7, 0x000);
/* Left justified, 24-bit mode */
- wm8775_write(client, R11, 0x021);
+ wm8775_write(sd, R11, 0x021);
/* Master mode, clock ratio 256fs */
- wm8775_write(client, R12, 0x102);
+ wm8775_write(sd, R12, 0x102);
/* Powered up */
- wm8775_write(client, R13, 0x000);
+ wm8775_write(sd, R13, 0x000);
/* ADC gain +2.5dB, enable zero cross */
- wm8775_write(client, R14, 0x1d4);
+ wm8775_write(sd, R14, 0x1d4);
/* ADC gain +2.5dB, enable zero cross */
- wm8775_write(client, R15, 0x1d4);
+ wm8775_write(sd, R15, 0x1d4);
/* ALC Stereo, ALC target level -1dB FS max gain +8dB */
- wm8775_write(client, R16, 0x1bf);
+ wm8775_write(sd, R16, 0x1bf);
/* Enable gain control, use zero cross detection,
ALC hold time 42.6 ms */
- wm8775_write(client, R17, 0x185);
+ wm8775_write(sd, R17, 0x185);
/* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */
- wm8775_write(client, R18, 0x0a2);
+ wm8775_write(sd, R18, 0x0a2);
/* Enable noise gate, threshold -72dBfs */
- wm8775_write(client, R19, 0x005);
+ wm8775_write(sd, R19, 0x005);
/* Transient window 4ms, lower PGA gain limit -1dB */
- wm8775_write(client, R20, 0x07a);
+ wm8775_write(sd, R20, 0x07a);
/* LRBOTH = 1, use input 2. */
- wm8775_write(client, R21, 0x102);
+ wm8775_write(sd, R21, 0x102);
return 0;
}
static int wm8775_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;
}