@@ -77,6 +77,16 @@
To compile this driver as a module, choose M here: the
module will be called wis-tw9903
+config VIDEO_GO7007_TW9906
+ tristate "TW9906 subdev support"
+ depends on VIDEO_GO7007
+ default N
+ ---help---
+ This is a video4linux driver for the TW9906 sub-device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wis-tw9906
+
config VIDEO_GO7007_UDA1342
tristate "UDA1342 subdev support"
depends on VIDEO_GO7007
@@ -170,6 +171,17 @@
/* Set GPIO pin 0 to be an output (audio clock control) */
go7007_write_addr(go, 0x3c82, 0x0001);
go7007_write_addr(go, 0x3c80, 0x00fe);
+ break;
+ case GO7007_BOARDID_ADS_USBAV_709:
+ /* GPIO pin 0: audio clock control */
+ /* pin 2: TW9906 reset */
+ /* pin 3: capture LED */
+ go7007_write_addr(go, 0x3c82, 0x000d);
+ go7007_write_addr(go, 0x3c80, 0x00f2);
+ break;
+ default:
+ /* No special setup */
+ break;
}
return 0;
}
@@ -212,6 +224,9 @@
case I2C_DRIVERID_WIS_TW9903:
modname = "wis-tw9903";
break;
+ case I2C_DRIVERID_WIS_TW9906:
+ modname = "wis-tw9906";
+ break;
case I2C_DRIVERID_WIS_TW2804:
modname = "wis-tw2804";
break;
@@ -269,6 +284,12 @@
go->i2c_adapter_online = 1;
}
if (go->i2c_adapter_online) {
+ if (go->board_id == GO7007_BOARDID_ADS_USBAV_709) {
+ /* Reset the TW9906 */
+ go7007_write_addr(go, 0x3c82, 0x0009);
+ msleep(50);
+ go7007_write_addr(go, 0x3c82, 0x000d);
+ }
for (i = 0; i < go->board_info->num_i2c_devs; ++i)
init_i2c_module(&go->i2c_adapter,
go->board_info->i2c_devs[i].type,
@@ -444,6 +444,44 @@
},
};
+static struct go7007_usb_board board_ads_usbav_709 = {
+ .flags = GO7007_USB_EZUSB,
+ .main_info = {
+ .firmware = "go7007tv.bin",
+ .flags = GO7007_BOARD_HAS_AUDIO |
+ GO7007_BOARD_USE_ONBOARD_I2C,
+ .audio_flags = GO7007_AUDIO_I2S_MODE_1 |
+ GO7007_AUDIO_I2S_MASTER |
+ GO7007_AUDIO_WORD_16,
+ .audio_rate = 48000,
+ .audio_bclk_div = 8,
+ .audio_main_div = 2,
+ .hpi_buffer_cap = 7,
+ .sensor_flags = GO7007_SENSOR_656 |
+ GO7007_SENSOR_TV |
+ GO7007_SENSOR_VBI,
+ .num_i2c_devs = 1,
+ .i2c_devs = {
+ {
+ .type = "wis_tw9906",
+ .id = I2C_DRIVERID_WIS_TW9906,
+ .addr = 0x44,
+ },
+ },
+ .num_inputs = 2,
+ .inputs = {
+ {
+ .video_input = 0,
+ .name = "Composite",
+ },
+ {
+ .video_input = 10,
+ .name = "S-Video",
+ },
+ },
+ },
+};
+
static struct usb_device_id go7007_usb_id_table[] = {
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
@@ -545,6 +583,14 @@
.bcdDevice_hi = 0x1,
.driver_info = (kernel_ulong_t)GO7007_BOARDID_SENSORAY_2250,
},
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+ .idVendor = 0x06e1, /* Vendor ID of ADS Technologies */
+ .idProduct = 0x0709, /* Product ID of DVD Xpress DX2 */
+ .bcdDevice_lo = 0x204,
+ .bcdDevice_hi = 0x204,
+ .driver_info = (kernel_ulong_t)GO7007_BOARDID_ADS_USBAV_709,
+ },
{ } /* Terminating entry */
};
@@ -1023,6 +1069,10 @@
name = "Sensoray 2250/2251";
board = &board_sensoray_2250;
break;
+ case GO7007_BOARDID_ADS_USBAV_709:
+ name = "ADS Tech DVD Xpress DX2";
+ board = &board_ads_usbav_709;
+ break;
default:
printk(KERN_ERR "go7007-usb: unknown board ID %d!\n",
(unsigned int)id->driver_info);
@@ -43,9 +43,39 @@
#define V4L2_MPEG_STREAM_TYPE_MPEG_ELEM 6 /* MPEG elementary stream */
#endif
#ifndef V4L2_MPEG_VIDEO_ENCODING_MPEG_4
-#define V4L2_MPEG_VIDEO_ENCODING_MPEG_4 3
+#define V4L2_MPEG_VIDEO_ENCODING_MPEG_4 2
+#endif
+#ifndef V4L2_MPEG_VIDEO_ENCODING_NONE
+#define V4L2_MPEG_VIDEO_ENCODING_NONE 3
#endif
+/* Must be sorted from low to high control ID! */
+static const u32 user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ 0
+};
+
+static const u32 mpeg_ctrls[] = {
+ V4L2_CID_MPEG_CLASS,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ 0
+};
+
+static const u32 *ctrl_classes[] = {
+ user_ctrls,
+ mpeg_ctrls,
+ NULL
+};
+
static void deactivate_buffer(struct go7007_buffer *gobuf)
{
int i;
@@ -387,23 +417,6 @@
static int mpeg_queryctrl(struct v4l2_queryctrl *ctrl)
{
- static const u32 mpeg_ctrls[] = {
- V4L2_CID_MPEG_CLASS,
- V4L2_CID_MPEG_STREAM_TYPE,
- V4L2_CID_MPEG_VIDEO_ENCODING,
- V4L2_CID_MPEG_VIDEO_ASPECT,
- V4L2_CID_MPEG_VIDEO_GOP_SIZE,
- V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
- V4L2_CID_MPEG_VIDEO_BITRATE,
- 0
- };
- static const u32 *ctrl_classes[] = {
- mpeg_ctrls,
- NULL
- };
-
- ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id);
-
switch (ctrl->id) {
case V4L2_CID_MPEG_CLASS:
return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0);
@@ -415,8 +428,8 @@
case V4L2_CID_MPEG_VIDEO_ENCODING:
return v4l2_ctrl_query_fill(ctrl,
V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_4, 1,
- V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+ V4L2_MPEG_VIDEO_ENCODING_NONE, 1,
+ V4L2_MPEG_VIDEO_ENCODING_NONE);
case V4L2_CID_MPEG_VIDEO_ASPECT:
return v4l2_ctrl_query_fill(ctrl,
V4L2_MPEG_VIDEO_ASPECT_1x1,
@@ -556,6 +570,9 @@
case GO7007_FORMAT_MPEG4:
ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4;
break;
+ case GO7007_FORMAT_MJPEG:
+ ctrl->value = V4L2_MPEG_VIDEO_ENCODING_NONE;
+ break;
default:
return -EINVAL;
}
@@ -858,7 +875,6 @@
return retval;
}
-
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
struct go7007_file *gofh = priv;
@@ -977,11 +993,61 @@
if (!go->i2c_adapter_online)
return -EIO;
+ query->id = v4l2_ctrl_next(ctrl_classes, query->id);
+
i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, query);
return (!query->name[0]) ? mpeg_queryctrl(query) : 0;
}
+const char **go7007_ctrl_get_menu(void *priv, u32 id)
+{
+ static const char *mpeg_stream_type[] = {
+ "",
+ "",
+ "",
+ "MPEG-2 DVD-compatible Stream",
+ "",
+ "",
+ "MPEG-2 Elementary Stream",
+ NULL
+ };
+ static const char *mpeg_video_encoding[] = {
+ "MPEG-1",
+ "MPEG-2",
+ "MPEG-4",
+ "None (Motion-JPEG)",
+ NULL
+ };
+ static const char *mpeg_video_aspect[] = {
+ "1x1",
+ "4x3",
+ "16x9",
+ NULL
+ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return mpeg_video_encoding;
+ case V4L2_CID_MPEG_VIDEO_ASPECT:
+ return mpeg_video_aspect;
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return mpeg_stream_type;
+ default:
+ return NULL;
+ }
+}
+
+int vidioc_querymenu(struct file *file, void *priv, struct v4l2_querymenu *qmenu)
+{
+ struct v4l2_queryctrl qctrl;
+
+ qctrl.id = qmenu->id;
+ vidioc_queryctrl(file, priv, &qctrl);
+
+ return v4l2_ctrl_query_menu(qmenu, &qctrl, go7007_ctrl_get_menu(priv, qmenu->id));
+}
+
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
@@ -1020,6 +1086,46 @@
return 0;
}
+int vidioc_g_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *c)
+{
+ struct v4l2_control ctrl;
+
+ int i;
+ int err = 0;
+
+ for (i = 0; i < c->count; i++) {
+ ctrl.id = c->controls[i].id;
+ ctrl.value = c->controls[i].value;
+ err = vidioc_g_ctrl(file, priv, &ctrl);
+ c->controls[i].value = ctrl.value;
+ if (err) {
+ c->error_idx = i;
+ break;
+ }
+ }
+ return err;
+}
+
+int vidioc_s_ext_ctrls(struct file *file, void *priv, struct v4l2_ext_controls *c)
+{
+ struct v4l2_control ctrl;
+
+ int i;
+ int err = 0;
+
+ for (i = 0; i < c->count; i++) {
+ ctrl.id = c->controls[i].id;
+ ctrl.value = c->controls[i].value;
+ err = vidioc_s_ctrl(file, priv, &ctrl);
+ c->controls[i].value = ctrl.value;
+ if (err) {
+ c->error_idx = i;
+ break;
+ }
+ }
+ return err;
+}
+
static int vidioc_g_parm(struct file *filp, void *priv,
struct v4l2_streamparm *parm)
{
@@ -1440,7 +1546,13 @@
and vidioc_s_ext_ctrls()
*/
-#if 0
+static long go7007_prop_ioctl(struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct go7007 *go = ((struct go7007_file *) file->private_data)->go;
+
+ switch (cmd) {
+#if 1
/* Temporary ioctls for controlling compression characteristics */
case GO7007IOC_S_BITRATE:
{
@@ -1578,10 +1690,9 @@
go->gop_header_enable =
mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER
? 0 : 1;
- if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER)
- go->repeat_seqhead = 1;
- else
- go->repeat_seqhead = 0;
+ go->repeat_seqhead =
+ mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER
+ ? 1 : 0;
go->dvd_mode = 0;
}
/* fall-through */
@@ -1661,6 +1772,25 @@
return clip_to_modet_map(go, region->region, region->clips);
}
#endif
+ }
+ return -EINVAL;
+}
+
+
+static long go7007_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct go7007 *go = ((struct go7007_file *) file->private_data)->go;
+ int retval;
+
+ if (go->status != STATUS_ONLINE)
+ return -EIO;
+
+ retval = video_ioctl2(file, cmd, arg);
+ if (retval < 0)
+ retval = video_usercopy(file, cmd, arg, (v4l2_kioctl)go7007_prop_ioctl);
+ return retval;
+}
static ssize_t go7007_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
Files linux-2.6.32-gentoo/drivers/staging/go7007/go7007-v4l2.o and linux-2.6.32-gentoo_DX2/drivers/staging/go7007/go7007-v4l2.o differ
Files linux-2.6.32-gentoo/drivers/staging/go7007/go7007.ko and linux-2.6.32-gentoo_DX2/drivers/staging/go7007/go7007.ko differ
Files linux-2.6.32-gentoo/drivers/staging/go7007/go7007.o and linux-2.6.32-gentoo_DX2/drivers/staging/go7007/go7007.o differ
@@ -357,32 +357,18 @@
case VIDIOC_QUERYCTRL:
{
struct v4l2_queryctrl *ctrl = arg;
- static const u32 user_ctrls[] = {
- V4L2_CID_BRIGHTNESS,
- V4L2_CID_CONTRAST,
- V4L2_CID_SATURATION,
- V4L2_CID_HUE,
- 0
- };
- static const u32 *ctrl_classes[] = {
- user_ctrls,
- NULL
- };
- ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id);
switch (ctrl->id) {
+ case V4L2_CID_USER_CLASS:
+ return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0);
case V4L2_CID_BRIGHTNESS:
- v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
case V4L2_CID_CONTRAST:
- v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
case V4L2_CID_SATURATION:
- v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50);
case V4L2_CID_HUE:
- v4l2_ctrl_query_fill(ctrl, -50, 50, 1, 0);
- break;
+ return v4l2_ctrl_query_fill(ctrl, -50, 50, 1, 0);
default:
ctrl->name[0] = '\0';
return -EINVAL;
@@ -24,6 +24,7 @@
#define I2C_DRIVERID_WIS_OV7640 0xf0f5
#define I2C_DRIVERID_WIS_TW2804 0xf0f6
#define I2C_DRIVERID_S2250 0xf0f7
+#define I2C_DRIVERID_WIS_TW9906 0xf0f9
/* Flag to indicate that the client needs to be accessed with SCCB semantics */
/* We re-use the I2C_M_TEN value so the flag passes through the masks in the
@@ -152,42 +152,19 @@
struct v4l2_queryctrl *ctrl = arg;
switch (ctrl->id) {
+ case V4L2_CID_USER_CLASS:
+ return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0);
case V4L2_CID_BRIGHTNESS:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 255, 1, 128);
case V4L2_CID_CONTRAST:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 71;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 127, 1, 71);
case V4L2_CID_SATURATION:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 64;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 127, 1, 64);
case V4L2_CID_HUE:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
- ctrl->minimum = -128;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 0;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, -128, 127, 1, 0);
+ default:
+ ctrl->name[0] = '\0';
+ return -EINVAL;
}
break;
}
@@ -285,42 +285,19 @@
struct v4l2_queryctrl *ctrl = arg;
switch (ctrl->id) {
+ case V4L2_CID_USER_CLASS:
+ return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0);
case V4L2_CID_BRIGHTNESS:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 255, 1, 128);
case V4L2_CID_CONTRAST:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 64;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 127, 1, 64);
case V4L2_CID_SATURATION:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 64;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 127, 1, 64);
case V4L2_CID_HUE:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
- ctrl->minimum = -128;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 0;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, -128, 127, 1, 0);
+ default:
+ ctrl->name[0] = '\0';
+ return -EINVAL;
}
break;
}
@@ -182,42 +182,19 @@
struct v4l2_queryctrl *ctrl = arg;
switch (ctrl->id) {
+ case V4L2_CID_USER_CLASS:
+ return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0);
case V4L2_CID_BRIGHTNESS:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 255, 1, 128);
case V4L2_CID_CONTRAST:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 255, 1, 128);
case V4L2_CID_SATURATION:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 255, 1, 128);
case V4L2_CID_HUE:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 128;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, 0, 255, 1, 128);
+ default:
+ ctrl->name[0] = '\0';
+ return -EINVAL;
}
break;
}
@@ -152,45 +152,23 @@
struct v4l2_queryctrl *ctrl = arg;
switch (ctrl->id) {
+ case V4L2_CID_USER_CLASS:
+ return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0);
case V4L2_CID_BRIGHTNESS:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
- ctrl->minimum = -128;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 0x00;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, -128, 127, 1, 0x0);
case V4L2_CID_CONTRAST:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 255;
- ctrl->step = 1;
- ctrl->default_value = 0x60;
- ctrl->flags = 0;
- break;
-#if 0
- /* I don't understand how the Chroma Gain registers work... */
+ return v4l2_ctrl_query_fill(ctrl, 0, 255, 1, 0x60);
case V4L2_CID_SATURATION:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
- ctrl->minimum = 0;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 64;
- ctrl->flags = 0;
- break;
-#endif
+ {
+ int ret = v4l2_ctrl_query_fill(ctrl, 0, 127, 1, 64);
+ ctrl->flags |= V4L2_CTRL_FLAG_DISABLED; // I don't understand how the Chroma Gain registers work...
+ return ret;
+ }
case V4L2_CID_HUE:
- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
- strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
- ctrl->minimum = -128;
- ctrl->maximum = 127;
- ctrl->step = 1;
- ctrl->default_value = 0;
- ctrl->flags = 0;
- break;
+ return v4l2_ctrl_query_fill(ctrl, -128, 127, 1, 0);
+ default:
+ ctrl->name[0] = '\0';
+ return -EINVAL;
}
break;
}
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+
+#include "wis-i2c.h"
+
+struct wis_tw9906 {
+ int norm;
+ int brightness;
+ int contrast;
+ int hue;
+};
+
+static u8 initial_registers[] =
+{
+ 0x02, 0x40, /* input 0, composite */
+ 0x03, 0xa2, /* correct digital format */
+ 0x05, 0x81, /* or 0x01 for PAL */
+ 0x07, 0x02, /* window */
+ 0x08, 0x14, /* window */
+ 0x09, 0xf0, /* window */
+ 0x0a, 0x10, /* window */
+ 0x0b, 0xd0, /* window */
+ 0x0d, 0x00, /* scaling */
+ 0x0e, 0x11, /* scaling */
+ 0x0f, 0x00, /* scaling */
+ 0x10, 0x00, /* brightness */
+ 0x11, 0x60, /* contrast */
+ 0x12, 0x11, /* sharpness */
+ 0x13, 0x7e, /* U gain */
+ 0x14, 0x7e, /* V gain */
+ 0x15, 0x00, /* hue */
+ 0x19, 0x57, /* vbi */
+ 0x1a, 0x0f,
+ 0x1b, 0x40,
+ 0x29, 0x03,
+ 0x55, 0x00,
+ 0x6b, 0x26,
+ 0x6c, 0x36,
+ 0x6d, 0xf0,
+ 0x6e, 0x41,
+ 0x6f, 0x13,
+ 0xad, 0x70,
+ 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
+};
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 value)
+{
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int write_regs(struct i2c_client *client, u8 *regs)
+{
+ int i;
+
+ for (i = 0; regs[i] != 0x00; i += 2)
+ if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
+ return -1;
+ return 0;
+}
+
+static int wis_tw9906_command(struct i2c_client *client,
+ unsigned int cmd, void *arg)
+{
+ struct wis_tw9906 *dec = i2c_get_clientdata(client);
+
+ switch (cmd) {
+ case VIDIOC_S_INPUT:
+ {
+ int *input = arg;
+
+ i2c_smbus_write_byte_data(client, 0x02, 0x40 | (*input << 1));
+ break;
+ }
+ case VIDIOC_S_STD:
+ {
+ v4l2_std_id *input = arg;
+ u8 regs[] = {
+ 0x05, *input & V4L2_STD_NTSC ? 0x81 : 0x01,
+ 0x07, *input & V4L2_STD_NTSC ? 0x02 : 0x12,
+ 0x08, *input & V4L2_STD_NTSC ? 0x14 : 0x18,
+ 0x09, *input & V4L2_STD_NTSC ? 0xf0 : 0x20,
+ 0, 0,
+ };
+ write_regs(client, regs);
+ dec->norm = *input;
+ break;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_USER_CLASS:
+ return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0);
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(ctrl, -128, 127, 1, 0x0);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(ctrl, 0, 255, 1, 0x60);
+ case V4L2_CID_SATURATION:
+ {
+ int ret = v4l2_ctrl_query_fill(ctrl, 0, 127, 1, 64);
+ ctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return ret;
+ }
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(ctrl, -128, 127, 1, 0);
+ default:
+ ctrl->name[0] = '\0';
+ return -EINVAL;
+ }
+ break;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value > 127)
+ dec->brightness = 127;
+ else if (ctrl->value < -128)
+ dec->brightness = -128;
+ else
+ dec->brightness = ctrl->value;
+ write_reg(client, 0x10, dec->brightness);
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value > 255)
+ dec->contrast = 255;
+ else if (ctrl->value < 0)
+ dec->contrast = 0;
+ else
+ dec->contrast = ctrl->value;
+ write_reg(client, 0x11, dec->contrast);
+ break;
+ case V4L2_CID_HUE:
+ if (ctrl->value > 127)
+ dec->hue = 127;
+ else if (ctrl->value < -128)
+ dec->hue = -128;
+ else
+ dec->hue = ctrl->value;
+ write_reg(client, 0x15, dec->hue);
+ break;
+ }
+ break;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl = arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = dec->brightness;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = dec->contrast;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = dec->hue;
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wis_tw9906_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct wis_tw9906 *dec;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ dec = kmalloc(sizeof(struct wis_tw9906), GFP_KERNEL);
+ if (dec == NULL)
+ return -ENOMEM;
+
+ dec->norm = V4L2_STD_NTSC;
+ dec->brightness = 0;
+ dec->contrast = 0x60;
+ dec->hue = 0;
+ i2c_set_clientdata(client, dec);
+
+ printk(KERN_DEBUG
+ "wis-tw9906: initializing TW9906 at address %d on %s\n",
+ client->addr, adapter->name);
+
+ if (write_regs(client, initial_registers) < 0) {
+ printk(KERN_ERR "wis-tw9906: error initializing TW9906\n");
+ kfree(dec);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int wis_tw9906_remove(struct i2c_client *client)
+{
+
+ struct wis_tw9906 *dec = i2c_get_clientdata(client);
+
+ i2c_set_clientdata(client, NULL);
+ kfree(dec);
+ return 0;
+}
+
+static struct i2c_device_id wis_tw9906_id[] = {
+ { "wis_tw9906", 0 },
+ { }
+};
+
+static struct i2c_driver wis_tw9906_driver = {
+ .driver = {
+ .name = "WIS TW9906 I2C driver",
+ },
+ .probe = wis_tw9906_probe,
+ .remove = wis_tw9906_remove,
+ .command = wis_tw9906_command,
+ .id_table = wis_tw9906_id,
+};
+
+static int __init wis_tw9906_init(void)
+{
+ return i2c_add_driver(&wis_tw9906_driver);
+}
+
+static void __exit wis_tw9906_cleanup(void)
+{
+ i2c_del_driver(&wis_tw9906_driver);
+}
+
+module_init(wis_tw9906_init);
+module_exit(wis_tw9906_cleanup);
+
+MODULE_LICENSE("GPL v2");