summaryrefslogtreecommitdiffstats
path: root/drivers/media/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/usb')
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c40
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-dvb.c1
-rw-r--r--drivers/media/usb/cx231xx/cx231xx.h1
-rw-r--r--drivers/media/usb/dvb-usb-v2/Kconfig2
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb.h5
-rw-r--r--drivers/media/usb/dvb-usb-v2/dvb_usb_core.c134
-rw-r--r--drivers/media/usb/dvb-usb-v2/lmedm04.c2
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_devices.c12
-rw-r--r--drivers/media/usb/dvb-usb/m920x.c2
-rw-r--r--drivers/media/usb/em28xx/em28xx-i2c.c2
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c1
-rw-r--r--drivers/media/usb/gspca/Kconfig9
-rw-r--r--drivers/media/usb/gspca/Makefile2
-rw-r--r--drivers/media/usb/gspca/gspca.c6
-rw-r--r--drivers/media/usb/gspca/ov519.c32
-rw-r--r--drivers/media/usb/gspca/ov534.c3
-rw-r--r--drivers/media/usb/gspca/stk1135.c685
-rw-r--r--drivers/media/usb/gspca/stk1135.h57
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-core.c11
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-video.c9
-rw-r--r--drivers/media/usb/s2255/s2255drv.c9
-rw-r--r--drivers/media/usb/stk1160/Kconfig16
-rw-r--r--drivers/media/usb/stk1160/stk1160-v4l.c6
-rw-r--r--drivers/media/usb/tlg2300/pd-main.c37
-rw-r--r--drivers/media/usb/usbtv/Kconfig2
-rw-r--r--drivers/media/usb/usbtv/usbtv.c151
26 files changed, 1061 insertions, 176 deletions
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index 27948e1..a384f80 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -443,6 +443,44 @@ struct cx231xx_board cx231xx_boards[] = {
.gpio = NULL,
} },
},
+ [CX231XX_BOARD_KWORLD_UB445_USB_HYBRID] = {
+ .name = "Kworld UB445 USB Hybrid",
+ .tuner_type = TUNER_NXP_TDA18271,
+ .tuner_addr = 0x60,
+ .decoder = CX231XX_AVDECODER,
+ .output_mode = OUT_MODE_VIP11,
+ .demod_xfer_mode = 0,
+ .ctl_pin_status_mask = 0xFFFFFFC4,
+ .agc_analog_digital_select_gpio = 0x11, /* According with PV cxPolaris.inf file */
+ .tuner_sif_gpio = -1,
+ .tuner_scl_gpio = -1,
+ .tuner_sda_gpio = -1,
+ .gpio_pin_status_mask = 0x4001000,
+ .tuner_i2c_master = 2,
+ .demod_i2c_master = 1,
+ .ir_i2c_master = 2,
+ .has_dvb = 1,
+ .demod_addr = 0x10,
+ .norm = V4L2_STD_NTSC_M,
+ .input = {{
+ .type = CX231XX_VMUX_TELEVISION,
+ .vmux = CX231XX_VIN_3_1,
+ .amux = CX231XX_AMUX_VIDEO,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_COMPOSITE1,
+ .vmux = CX231XX_VIN_2_1,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ }, {
+ .type = CX231XX_VMUX_SVIDEO,
+ .vmux = CX231XX_VIN_1_1 |
+ (CX231XX_VIN_1_2 << 8) |
+ CX25840_SVIDEO_ON,
+ .amux = CX231XX_AMUX_LINE_IN,
+ .gpio = NULL,
+ } },
+ },
[CX231XX_BOARD_PV_PLAYTV_USB_HYBRID] = {
.name = "Pixelview PlayTV USB Hybrid",
.tuner_type = TUNER_NXP_TDA18271,
@@ -703,6 +741,8 @@ struct usb_device_id cx231xx_id_table[] = {
.driver_info = CX231XX_BOARD_PV_XCAPTURE_USB},
{USB_DEVICE(0x1b80, 0xe424),
.driver_info = CX231XX_BOARD_KWORLD_UB430_USB_HYBRID},
+ {USB_DEVICE(0x1b80, 0xe421),
+ .driver_info = CX231XX_BOARD_KWORLD_UB445_USB_HYBRID},
{USB_DEVICE(0x1f4d, 0x0237),
.driver_info = CX231XX_BOARD_ICONBIT_U100},
{USB_DEVICE(0x0fd9, 0x0037),
diff --git a/drivers/media/usb/cx231xx/cx231xx-dvb.c b/drivers/media/usb/cx231xx/cx231xx-dvb.c
index 14e2610..4504bc6 100644
--- a/drivers/media/usb/cx231xx/cx231xx-dvb.c
+++ b/drivers/media/usb/cx231xx/cx231xx-dvb.c
@@ -657,6 +657,7 @@ static int dvb_init(struct cx231xx *dev)
}
break;
case CX231XX_BOARD_CNXT_RDU_253S:
+ case CX231XX_BOARD_KWORLD_UB445_USB_HYBRID:
dev->dvb->frontend = dvb_attach(s5h1411_attach,
&tda18271_s5h1411_config,
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index e812119..babca7f 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -72,6 +72,7 @@
#define CX231XX_BOARD_HAUPPAUGE_USB2_FM_NTSC 15
#define CX231XX_BOARD_ELGATO_VIDEO_CAPTURE_V2 16
#define CX231XX_BOARD_OTG102 17
+#define CX231XX_BOARD_KWORLD_UB445_USB_HYBRID 18
/* Limits minimum and default number of buffers */
#define CX231XX_MIN_BUF 4
diff --git a/drivers/media/usb/dvb-usb-v2/Kconfig b/drivers/media/usb/dvb-usb-v2/Kconfig
index a3c8ecf..2059d0c8 100644
--- a/drivers/media/usb/dvb-usb-v2/Kconfig
+++ b/drivers/media/usb/dvb-usb-v2/Kconfig
@@ -1,6 +1,6 @@
config DVB_USB_V2
tristate "Support for various USB DVB devices v2"
- depends on DVB_CORE && USB && I2C
+ depends on DVB_CORE && USB && I2C && (RC_CORE || RC_CORE=n)
help
By enabling this you will be able to choose the various supported
USB1.1 and USB2.0 DVB devices.
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
index 399916b..124b4ba 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h
@@ -352,9 +352,7 @@ struct dvb_usb_adapter {
* @rc_map: name of rc codes table
* @rc_polling_active: set when RC polling is active
* @udev: pointer to the device's struct usb_device
- * @intf: pointer to the device's usb interface
* @rc: remote controller configuration
- * @probe_work: work to defer .probe()
* @powered: indicated whether the device is power or not
* @usb_mutex: mutex for usb control messages
* @i2c_mutex: mutex for i2c-transfers
@@ -370,10 +368,7 @@ struct dvb_usb_device {
const char *rc_map;
bool rc_polling_active;
struct usb_device *udev;
- struct usb_interface *intf;
struct dvb_usb_rc rc;
- struct work_struct probe_work;
- pid_t work_pid;
int powered;
/* locking */
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
index 19f6737..8a054d6 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
@@ -833,20 +833,44 @@ err:
return ret;
}
-/*
- * udev, which is used for the firmware downloading, requires we cannot
- * block during module_init(). module_init() calls USB probe() which
- * is this routine. Due to that we delay actual operation using workqueue
- * and return always success here.
- */
-static void dvb_usbv2_init_work(struct work_struct *work)
+int dvb_usbv2_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
{
int ret;
- struct dvb_usb_device *d =
- container_of(work, struct dvb_usb_device, probe_work);
+ struct dvb_usb_device *d;
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct dvb_usb_driver_info *driver_info =
+ (struct dvb_usb_driver_info *) id->driver_info;
+
+ dev_dbg(&udev->dev, "%s: bInterfaceNumber=%d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ if (!id->driver_info) {
+ dev_err(&udev->dev, "%s: driver_info failed\n", KBUILD_MODNAME);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL);
+ if (!d) {
+ dev_err(&udev->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
+ ret = -ENOMEM;
+ goto err;
+ }
- d->work_pid = current->pid;
- dev_dbg(&d->udev->dev, "%s: work_pid=%d\n", __func__, d->work_pid);
+ d->name = driver_info->name;
+ d->rc_map = driver_info->rc_map;
+ d->udev = udev;
+ d->props = driver_info->props;
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber !=
+ d->props->bInterfaceNumber) {
+ ret = -ENODEV;
+ goto err_free_all;
+ }
+
+ mutex_init(&d->usb_mutex);
+ mutex_init(&d->i2c_mutex);
if (d->props->size_of_priv) {
d->priv = kzalloc(d->props->size_of_priv, GFP_KERNEL);
@@ -854,7 +878,7 @@ static void dvb_usbv2_init_work(struct work_struct *work)
dev_err(&d->udev->dev, "%s: kzalloc() failed\n",
KBUILD_MODNAME);
ret = -ENOMEM;
- goto err_usb_driver_release_interface;
+ goto err_free_all;
}
}
@@ -884,20 +908,12 @@ static void dvb_usbv2_init_work(struct work_struct *work)
* device. As 'new' device is warm we should
* never go here again.
*/
- return;
+ goto exit;
} else {
- /*
- * Unexpected error. We must unregister driver
- * manually from the device, because device is
- * already register by returning from probe()
- * with success. usb_driver_release_interface()
- * finally calls disconnect() in order to free
- * resources.
- */
- goto err_usb_driver_release_interface;
+ goto err_free_all;
}
} else {
- goto err_usb_driver_release_interface;
+ goto err_free_all;
}
}
@@ -906,73 +922,17 @@ static void dvb_usbv2_init_work(struct work_struct *work)
ret = dvb_usbv2_init(d);
if (ret < 0)
- goto err_usb_driver_release_interface;
+ goto err_free_all;
dev_info(&d->udev->dev,
"%s: '%s' successfully initialized and connected\n",
KBUILD_MODNAME, d->name);
-
- return;
-err_usb_driver_release_interface:
- dev_info(&d->udev->dev, "%s: '%s' error while loading driver (%d)\n",
- KBUILD_MODNAME, d->name, ret);
- usb_driver_release_interface(to_usb_driver(d->intf->dev.driver),
- d->intf);
- dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret);
- return;
-}
-
-int dvb_usbv2_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- int ret;
- struct dvb_usb_device *d;
- struct usb_device *udev = interface_to_usbdev(intf);
- struct dvb_usb_driver_info *driver_info =
- (struct dvb_usb_driver_info *) id->driver_info;
-
- dev_dbg(&udev->dev, "%s: bInterfaceNumber=%d\n", __func__,
- intf->cur_altsetting->desc.bInterfaceNumber);
-
- if (!id->driver_info) {
- dev_err(&udev->dev, "%s: driver_info failed\n", KBUILD_MODNAME);
- ret = -ENODEV;
- goto err;
- }
-
- d = kzalloc(sizeof(struct dvb_usb_device), GFP_KERNEL);
- if (!d) {
- dev_err(&udev->dev, "%s: kzalloc() failed\n", KBUILD_MODNAME);
- ret = -ENOMEM;
- goto err;
- }
-
- d->name = driver_info->name;
- d->rc_map = driver_info->rc_map;
- d->udev = udev;
- d->intf = intf;
- d->props = driver_info->props;
-
- if (d->intf->cur_altsetting->desc.bInterfaceNumber !=
- d->props->bInterfaceNumber) {
- ret = -ENODEV;
- goto err_kfree;
- }
-
- mutex_init(&d->usb_mutex);
- mutex_init(&d->i2c_mutex);
- INIT_WORK(&d->probe_work, dvb_usbv2_init_work);
+exit:
usb_set_intfdata(intf, d);
- ret = schedule_work(&d->probe_work);
- if (ret < 0) {
- dev_err(&d->udev->dev, "%s: schedule_work() failed\n",
- KBUILD_MODNAME);
- goto err_kfree;
- }
return 0;
-err_kfree:
- kfree(d);
+err_free_all:
+ dvb_usbv2_exit(d);
err:
dev_dbg(&udev->dev, "%s: failed=%d\n", __func__, ret);
return ret;
@@ -984,12 +944,8 @@ void dvb_usbv2_disconnect(struct usb_interface *intf)
struct dvb_usb_device *d = usb_get_intfdata(intf);
const char *name = d->name;
struct device dev = d->udev->dev;
- dev_dbg(&d->udev->dev, "%s: pid=%d work_pid=%d\n", __func__,
- current->pid, d->work_pid);
-
- /* ensure initialization work is finished until release resources */
- if (d->work_pid != current->pid)
- cancel_work_sync(&d->probe_work);
+ dev_dbg(&d->udev->dev, "%s: bInterfaceNumber=%d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
if (d->props->exit)
d->props->exit(d);
diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c
index b3fd0ff..f674dc0 100644
--- a/drivers/media/usb/dvb-usb-v2/lmedm04.c
+++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c
@@ -1225,7 +1225,7 @@ static int lme2510_identify_state(struct dvb_usb_device *d, const char **name)
usb_reset_configuration(d->udev);
usb_set_interface(d->udev,
- d->intf->cur_altsetting->desc.bInterfaceNumber, 1);
+ d->props->bInterfaceNumber, 1);
st->dvb_usb_lme2510_firmware = dvb_usb_lme2510_firmware;
diff --git a/drivers/media/usb/dvb-usb/dib0700_devices.c b/drivers/media/usb/dvb-usb/dib0700_devices.c
index f081360..829323e 100644
--- a/drivers/media/usb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/usb/dvb-usb/dib0700_devices.c
@@ -3589,6 +3589,8 @@ struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7790P) },
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE8096P) },
/* 80 */{ USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_2) },
+ { USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_2002E) },
+ { USB_DEVICE(USB_VID_PCTV, USB_PID_PCTV_2002E_SE) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -3993,12 +3995,20 @@ struct dvb_usb_device_properties dib0700_devices[] = {
}
},
- .num_device_descs = 1,
+ .num_device_descs = 3,
.devices = {
{ "Hauppauge Nova-TD Stick (52009)",
{ &dib0700_usb_id_table[35], NULL },
{ NULL },
},
+ { "PCTV 2002e",
+ { &dib0700_usb_id_table[81], NULL },
+ { NULL },
+ },
+ { "PCTV 2002e SE",
+ { &dib0700_usb_id_table[82], NULL },
+ { NULL },
+ },
},
.rc.core = {
diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c
index c2b635d..0306cb7 100644
--- a/drivers/media/usb/dvb-usb/m920x.c
+++ b/drivers/media/usb/dvb-usb/m920x.c
@@ -1212,7 +1212,7 @@ static struct dvb_usb_device_properties vp7049_properties = {
.rc_interval = 150,
.rc_codes = RC_MAP_TWINHAN_VP1027_DVBS,
.rc_query = m920x_rc_core_query,
- .allowed_protos = RC_TYPE_UNKNOWN,
+ .allowed_protos = RC_BIT_UNKNOWN,
},
.size_of_priv = sizeof(struct m920x_state),
diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c
index 4851cc2..c4ff973 100644
--- a/drivers/media/usb/em28xx/em28xx-i2c.c
+++ b/drivers/media/usb/em28xx/em28xx-i2c.c
@@ -726,7 +726,7 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned bus,
*eedata = data;
*eedata_len = len;
- dev_config = (void *)eedata;
+ dev_config = (void *)*eedata;
switch (le16_to_cpu(dev_config->chip_conf) >> 4 & 0x3) {
case 0:
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index 1a577ed..9d10334 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -1008,6 +1008,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
else
f->fmt.pix.field = dev->interlaced ?
V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP;
+ f->fmt.pix.priv = 0;
return 0;
}
diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig
index 6345f93..4f0c6d5 100644
--- a/drivers/media/usb/gspca/Kconfig
+++ b/drivers/media/usb/gspca/Kconfig
@@ -338,6 +338,15 @@ config USB_GSPCA_STK014
To compile this driver as a module, choose M here: the
module will be called gspca_stk014.
+config USB_GSPCA_STK1135
+ tristate "Syntek STK1135 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on the STK1135 chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stk1135.
+
config USB_GSPCA_STV0680
tristate "STV0680 USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
diff --git a/drivers/media/usb/gspca/Makefile b/drivers/media/usb/gspca/Makefile
index c901da0..5855131 100644
--- a/drivers/media/usb/gspca/Makefile
+++ b/drivers/media/usb/gspca/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o
obj-$(CONFIG_USB_GSPCA_SQ930X) += gspca_sq930x.o
obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o
obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o
+obj-$(CONFIG_USB_GSPCA_STK1135) += gspca_stk1135.o
obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o
obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o
@@ -78,6 +79,7 @@ gspca_sq905-objs := sq905.o
gspca_sq905c-objs := sq905c.o
gspca_sq930x-objs := sq930x.o
gspca_stk014-objs := stk014.o
+gspca_stk1135-objs := stk1135.o
gspca_stv0680-objs := stv0680.o
gspca_sunplus-objs := sunplus.o
gspca_t613-objs := t613.o
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
index b7ae872..048507b 100644
--- a/drivers/media/usb/gspca/gspca.c
+++ b/drivers/media/usb/gspca/gspca.c
@@ -1266,6 +1266,7 @@ static void gspca_release(struct v4l2_device *v4l2_device)
static int dev_open(struct file *file)
{
struct gspca_dev *gspca_dev = video_drvdata(file);
+ int ret;
PDEBUG(D_STREAM, "[%s] open", current->comm);
@@ -1273,7 +1274,10 @@ static int dev_open(struct file *file)
if (!try_module_get(gspca_dev->module))
return -ENODEV;
- return v4l2_fh_open(file);
+ ret = v4l2_fh_open(file);
+ if (ret)
+ module_put(gspca_dev->module);
+ return ret;
}
static int dev_close(struct file *file)
diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c
index a3958ee..8937d79 100644
--- a/drivers/media/usb/gspca/ov519.c
+++ b/drivers/media/usb/gspca/ov519.c
@@ -75,6 +75,8 @@ struct sd {
struct v4l2_ctrl *brightness;
};
+ u8 revision;
+
u8 packet_nr;
char bridge;
@@ -3080,8 +3082,8 @@ static void ov518_configure(struct gspca_dev *gspca_dev)
};
/* First 5 bits of custom ID reg are a revision ID on OV518 */
- PDEBUG(D_PROBE, "Device revision %d",
- 0x1f & reg_r(sd, R51x_SYS_CUST_ID));
+ sd->revision = reg_r(sd, R51x_SYS_CUST_ID) & 0x1f;
+ PDEBUG(D_PROBE, "Device revision %d", sd->revision);
write_regvals(sd, init_518, ARRAY_SIZE(init_518));
@@ -3657,7 +3659,11 @@ static void ov518_mode_init_regs(struct sd *sd)
reg_w(sd, 0x2f, 0x80);
/******** Set the framerate ********/
- sd->clockdiv = 1;
+ if (sd->bridge == BRIDGE_OV518PLUS && sd->revision == 0 &&
+ sd->sensor == SEN_OV7620AE)
+ sd->clockdiv = 0;
+ else
+ sd->clockdiv = 1;
/* Mode independent, but framerate dependent, regs */
/* 0x51: Clock divider; Only works on some cams which use 2 crystals */
@@ -3668,12 +3674,24 @@ static void ov518_mode_init_regs(struct sd *sd)
if (sd->bridge == BRIDGE_OV518PLUS) {
switch (sd->sensor) {
case SEN_OV7620AE:
- if (sd->gspca_dev.width == 320) {
- reg_w(sd, 0x20, 0x00);
- reg_w(sd, 0x21, 0x19);
- } else {
+ /*
+ * HdG: 640x480 needs special handling on device
+ * revision 2, we check for device revison > 0 to
+ * avoid regressions, as we don't know the correct
+ * thing todo for revision 1.
+ *
+ * Also this likely means we don't need to
+ * differentiate between the OV7620 and OV7620AE,
+ * earlier testing hitting this same problem likely
+ * happened to be with revision < 2 cams using an
+ * OV7620 and revision 2 cams using an OV7620AE.
+ */
+ if (sd->revision > 0 && sd->gspca_dev.width == 640) {
reg_w(sd, 0x20, 0x60);
reg_w(sd, 0x21, 0x1f);
+ } else {
+ reg_w(sd, 0x20, 0x00);
+ reg_w(sd, 0x21, 0x19);
}
break;
case SEN_OV7620:
diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c
index 2e28c81..03a33c4 100644
--- a/drivers/media/usb/gspca/ov534.c
+++ b/drivers/media/usb/gspca/ov534.c
@@ -1305,8 +1305,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
ov534_set_led(gspca_dev, 1);
sccb_w_array(gspca_dev, sensor_init[sd->sensor].val,
sensor_init[sd->sensor].len);
- if (sd->sensor == SENSOR_OV767x)
- sd_start(gspca_dev);
+
sd_stopN(gspca_dev);
/* set_frame_rate(gspca_dev); */
diff --git a/drivers/media/usb/gspca/stk1135.c b/drivers/media/usb/gspca/stk1135.c
new file mode 100644
index 0000000..5858688
--- /dev/null
+++ b/drivers/media/usb/gspca/stk1135.c
@@ -0,0 +1,685 @@
+/*
+ * Syntek STK1135 subdriver
+ *
+ * Copyright (c) 2013 Ondrej Zary
+ *
+ * Based on Syntekdriver (stk11xx) by Nicolas VIVIEN:
+ * http://syntekdriver.sourceforge.net
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define MODULE_NAME "stk1135"
+
+#include "gspca.h"
+#include "stk1135.h"
+
+MODULE_AUTHOR("Ondrej Zary");
+MODULE_DESCRIPTION("Syntek STK1135 USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+
+/* specific webcam descriptor */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ u8 pkt_seq;
+ u8 sensor_page;
+
+ bool flip_status;
+ u8 flip_debounce;
+
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+};
+
+static const struct v4l2_pix_format stk1135_modes[] = {
+ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+ .sizeimage = 160 * 120,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 352,
+ .sizeimage = 352 * 288,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 640,
+ .sizeimage = 640 * 480,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {720, 576, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 720,
+ .sizeimage = 720 * 576,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+ .sizeimage = 800 * 600,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1024,
+ .sizeimage = 1024 * 768,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+ {1280, 1024, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
+ .bytesperline = 1280,
+ .sizeimage = 1280 * 1024,
+ .colorspace = V4L2_COLORSPACE_SRGB},
+};
+
+/* -- read a register -- */
+static u8 reg_r(struct gspca_dev *gspca_dev, u16 index)
+{
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
+ if (gspca_dev->usb_err < 0)
+ return 0;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0x00,
+ index,
+ gspca_dev->usb_buf, 1,
+ 500);
+
+ PDEBUG(D_USBI, "reg_r 0x%x=0x%02x", index, gspca_dev->usb_buf[0]);
+ if (ret < 0) {
+ pr_err("reg_r 0x%x err %d\n", index, ret);
+ gspca_dev->usb_err = ret;
+ return 0;
+ }
+
+ return gspca_dev->usb_buf[0];
+}
+
+/* -- write a register -- */
+static void reg_w(struct gspca_dev *gspca_dev, u16 index, u8 val)
+{
+ int ret;
+ struct usb_device *dev = gspca_dev->dev;
+
+ if (gspca_dev->usb_err < 0)
+ return;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ val,
+ index,
+ NULL,
+ 0,
+ 500);
+ PDEBUG(D_USBO, "reg_w 0x%x:=0x%02x", index, val);
+ if (ret < 0) {
+ pr_err("reg_w 0x%x err %d\n", index, ret);
+ gspca_dev->usb_err = ret;
+ }
+}
+
+static void reg_w_mask(struct gspca_dev *gspca_dev, u16 index, u8 val, u8 mask)
+{
+ val = (reg_r(gspca_dev, index) & ~mask) | (val & mask);
+ reg_w(gspca_dev, index, val);
+}
+
+/* this function is called at probe time */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ gspca_dev->cam.cam_mode = stk1135_modes;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(stk1135_modes);
+ return 0;
+}
+
+static int stk1135_serial_wait_ready(struct gspca_dev *gspca_dev)
+{
+ int i = 0;
+ u8 val;
+
+ do {
+ val = reg_r(gspca_dev, STK1135_REG_SICTL + 1);
+ if (i++ > 500) { /* maximum retry count */
+ pr_err("serial bus timeout: status=0x%02x\n", val);
+ return -1;
+ }
+ /* repeat if BUSY or WRITE/READ not finished */
+ } while ((val & 0x10) || !(val & 0x05));
+
+ return 0;
+}
+
+static u8 sensor_read_8(struct gspca_dev *gspca_dev, u8 addr)
+{
+ reg_w(gspca_dev, STK1135_REG_SBUSR, addr);
+ /* begin read */
+ reg_w(gspca_dev, STK1135_REG_SICTL, 0x20);
+ /* wait until finished */
+ if (stk1135_serial_wait_ready(gspca_dev)) {
+ pr_err("Sensor read failed\n");
+ return 0;
+ }
+
+ return reg_r(gspca_dev, STK1135_REG_SBUSR + 1);
+}
+
+static u16 sensor_read_16(struct gspca_dev *gspca_dev, u8 addr)
+{
+ return (sensor_read_8(gspca_dev, addr) << 8) |
+ sensor_read_8(gspca_dev, 0xf1);
+}
+
+static void sensor_write_8(struct gspca_dev *gspca_dev, u8 addr, u8 data)
+{
+ /* load address and data registers */
+ reg_w(gspca_dev, STK1135_REG_SBUSW, addr);
+ reg_w(gspca_dev, STK1135_REG_SBUSW + 1, data);
+ /* begin write */
+ reg_w(gspca_dev, STK1135_REG_SICTL, 0x01);
+ /* wait until finished */
+ if (stk1135_serial_wait_ready(gspca_dev)) {
+ pr_err("Sensor write failed\n");
+ return;
+ }
+}
+
+static void sensor_write_16(struct gspca_dev *gspca_dev, u8 addr, u16 data)
+{
+ sensor_write_8(gspca_dev, addr, data >> 8);
+ sensor_write_8(gspca_dev, 0xf1, data & 0xff);
+}
+
+static void sensor_set_page(struct gspca_dev *gspca_dev, u8 page)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (page != sd->sensor_page) {
+ sensor_write_16(gspca_dev, 0xf0, page);
+ sd->sensor_page = page;
+ }
+}
+
+static u16 sensor_read(struct gspca_dev *gspca_dev, u16 reg)
+{
+ sensor_set_page(gspca_dev, reg >> 8);
+ return sensor_read_16(gspca_dev, reg & 0xff);
+}
+
+static void sensor_write(struct gspca_dev *gspca_dev, u16 reg, u16 val)
+{
+ sensor_set_page(gspca_dev, reg >> 8);
+ sensor_write_16(gspca_dev, reg & 0xff, val);
+}
+
+static void sensor_write_mask(struct gspca_dev *gspca_dev,
+ u16 reg, u16 val, u16 mask)
+{
+ val = (sensor_read(gspca_dev, reg) & ~mask) | (val & mask);
+ sensor_write(gspca_dev, reg, val);
+}
+
+struct sensor_val {
+ u16 reg;
+ u16 val;
+};
+
+/* configure MT9M112 sensor */
+static void stk1135_configure_mt9m112(struct gspca_dev *gspca_dev)
+{
+ static const struct sensor_val cfg[] = {
+ /* restart&reset, chip enable, reserved */
+ { 0x00d, 0x000b }, { 0x00d, 0x0008 }, { 0x035, 0x0022 },
+ /* mode ctl: AWB on, AE both, clip aper corr, defect corr, AE */
+ { 0x106, 0x700e },
+
+ { 0x2dd, 0x18e0 }, /* B-R thresholds, */
+
+ /* AWB */
+ { 0x21f, 0x0180 }, /* Cb and Cr limits */
+ { 0x220, 0xc814 }, { 0x221, 0x8080 }, /* lum limits, RGB gain */
+ { 0x222, 0xa078 }, { 0x223, 0xa078 }, /* R, B limit */
+ { 0x224, 0x5f20 }, { 0x228, 0xea02 }, /* mtx adj lim, adv ctl */
+ { 0x229, 0x867a }, /* wide gates */
+
+ /* Color correction */
+ /* imager gains base, delta, delta signs */
+ { 0x25e, 0x594c }, { 0x25f, 0x4d51 }, { 0x260, 0x0002 },
+ /* AWB adv ctl 2, gain offs */
+ { 0x2ef, 0x0008 }, { 0x2f2, 0x0000 },
+ /* base matrix signs, scale K1-5, K6-9 */
+ { 0x202, 0x00ee }, { 0x203, 0x3923 }, { 0x204, 0x0724 },
+ /* base matrix coef */
+ { 0x209, 0x00cd }, { 0x20a, 0x0093 }, { 0x20b, 0x0004 },/*K1-3*/
+ { 0x20c, 0x005c }, { 0x20d, 0x00d9 }, { 0x20e, 0x0053 },/*K4-6*/
+ { 0x20f, 0x0008 }, { 0x210, 0x0091 }, { 0x211, 0x00cf },/*K7-9*/
+ { 0x215, 0x0000 }, /* delta mtx signs */
+ /* delta matrix coef */
+ { 0x216, 0x0000 }, { 0x217, 0x0000 }, { 0x218, 0x0000 },/*D1-3*/
+ { 0x219, 0x0000 }, { 0x21a, 0x0000 }, { 0x21b, 0x0000 },/*D4-6*/
+ { 0x21c, 0x0000 }, { 0x21d, 0x0000 }, { 0x21e, 0x0000 },/*D7-9*/
+ /* enable & disable manual WB to apply color corr. settings */
+ { 0x106, 0xf00e }, { 0x106, 0x700e },
+
+ /* Lens shading correction */
+ { 0x180, 0x0007 }, /* control */
+ /* vertical knee 0, 2+1, 4+3 */
+ { 0x181, 0xde13 }, { 0x182, 0xebe2 }, { 0x183, 0x00f6 }, /* R */
+ { 0x184, 0xe114 }, { 0x185, 0xeadd }, { 0x186, 0xfdf6 }, /* G */
+ { 0x187, 0xe511 }, { 0x188, 0xede6 }, { 0x189, 0xfbf7 }, /* B */
+ /* horizontal knee 0, 2+1, 4+3, 5 */
+ { 0x18a, 0xd613 }, { 0x18b, 0xedec }, /* R .. */
+ { 0x18c, 0xf9f2 }, { 0x18d, 0x0000 }, /* .. R */
+ { 0x18e, 0xd815 }, { 0x18f, 0xe9ea }, /* G .. */
+ { 0x190, 0xf9f1 }, { 0x191, 0x0002 }, /* .. G */
+ { 0x192, 0xde10 }, { 0x193, 0xefef }, /* B .. */
+ { 0x194, 0xfbf4 }, { 0x195, 0x0002 }, /* .. B */
+ /* vertical knee 6+5, 8+7 */
+ { 0x1b6, 0x0e06 }, { 0x1b7, 0x2713 }, /* R */
+ { 0x1b8, 0x1106 }, { 0x1b9, 0x2713 }, /* G */
+ { 0x1ba, 0x0c03 }, { 0x1bb, 0x2a0f }, /* B */
+ /* horizontal knee 7+6, 9+8, 10 */
+ { 0x1bc, 0x1208 }, { 0x1bd, 0x1a16 }, { 0x1be, 0x0022 }, /* R */
+ { 0x1bf, 0x150a }, { 0x1c0, 0x1c1a }, { 0x1c1, 0x002d }, /* G */
+ { 0x1c2, 0x1109 }, { 0x1c3, 0x1414 }, { 0x1c4, 0x002a }, /* B */
+ { 0x106, 0x740e }, /* enable lens shading correction */
+
+ /* Gamma correction - context A */
+ { 0x153, 0x0b03 }, { 0x154, 0x4722 }, { 0x155, 0xac82 },
+ { 0x156, 0xdac7 }, { 0x157, 0xf5e9 }, { 0x158, 0xff00 },
+ /* Gamma correction - context B */
+ { 0x1dc, 0x0b03 }, { 0x1dd, 0x4722 }, { 0x1de, 0xac82 },
+ { 0x1df, 0xdac7 }, { 0x1e0, 0xf5e9 }, { 0x1e1, 0xff00 },
+
+ /* output format: RGB, invert output pixclock, output bayer */
+ { 0x13a, 0x4300 }, { 0x19b, 0x4300 }, /* for context A, B */
+ { 0x108, 0x0180 }, /* format control - enable bayer row flip */
+
+ { 0x22f, 0xd100 }, { 0x29c, 0xd100 }, /* AE A, B */
+
+ /* default prg conf, prg ctl - by 0x2d2, prg advance - PA1 */
+ { 0x2d2, 0x0000 }, { 0x2cc, 0x0004 }, { 0x2cb, 0x0001 },
+
+ { 0x22e, 0x0c3c }, { 0x267, 0x1010 }, /* AE tgt ctl, gain lim */
+
+ /* PLL */
+ { 0x065, 0xa000 }, /* clk ctl - enable PLL (clear bit 14) */
+ { 0x066, 0x2003 }, { 0x067, 0x0501 }, /* PLL M=128, N=3, P=1 */
+ { 0x065, 0x2000 }, /* disable PLL bypass (clear bit 15) */
+
+ { 0x005, 0x01b8 }, { 0x007, 0x00d8 }, /* horiz blanking B, A */
+
+ /* AE line size, shutter delay limit */
+ { 0x239, 0x06c0 }, { 0x23b, 0x040e }, /* for context A */
+ { 0x23a, 0x06c0 }, { 0x23c, 0x0564 }, /* for context B */
+ /* shutter width basis 60Hz, 50Hz */
+ { 0x257, 0x0208 }, { 0x258, 0x0271 }, /* for context A */
+ { 0x259, 0x0209 }, { 0x25a, 0x0271 }, /* for context B */
+
+ { 0x25c, 0x120d }, { 0x25d, 0x1712 }, /* flicker 60Hz, 50Hz */
+ { 0x264, 0x5e1c }, /* reserved */
+ /* flicker, AE gain limits, gain zone limits */
+ { 0x25b, 0x0003 }, { 0x236, 0x7810 }, { 0x237, 0x8304 },
+
+ { 0x008, 0x0021 }, /* vert blanking A */
+ };
+ int i;
+ u16 width, height;
+
+ for (i = 0; i < ARRAY_SIZE(cfg); i++)
+ sensor_write(gspca_dev, cfg[i].reg, cfg[i].val);
+
+ /* set output size */
+ width = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].width;
+ height = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].height;
+ if (width <= 640) { /* use context A (half readout speed by default) */
+ sensor_write(gspca_dev, 0x1a7, width);
+ sensor_write(gspca_dev, 0x1aa, height);
+ /* set read mode context A */
+ sensor_write(gspca_dev, 0x0c8, 0x0000);
+ /* set resize, read mode, vblank, hblank context A */
+ sensor_write(gspca_dev, 0x2c8, 0x0000);
+ } else { /* use context B (full readout speed by default) */
+ sensor_write(gspca_dev, 0x1a1, width);
+ sensor_write(gspca_dev, 0x1a4, height);
+ /* set read mode context B */
+ sensor_write(gspca_dev, 0x0c8, 0x0008);
+ /* set resize, read mode, vblank, hblank context B */
+ sensor_write(gspca_dev, 0x2c8, 0x040b);
+ }
+}
+
+static void stk1135_configure_clock(struct gspca_dev *gspca_dev)
+{
+ /* configure SCLKOUT */
+ reg_w(gspca_dev, STK1135_REG_TMGEN, 0x12);
+ /* set 1 clock per pixel */
+ /* and positive edge clocked pulse high when pixel counter = 0 */
+ reg_w(gspca_dev, STK1135_REG_TCP1 + 0, 0x41);
+ reg_w(gspca_dev, STK1135_REG_TCP1 + 1, 0x00);
+ reg_w(gspca_dev, STK1135_REG_TCP1 + 2, 0x00);
+ reg_w(gspca_dev, STK1135_REG_TCP1 + 3, 0x00);
+
+ /* enable CLKOUT for sensor */
+ reg_w(gspca_dev, STK1135_REG_SENSO + 0, 0x10);
+ /* disable STOP clock */
+ reg_w(gspca_dev, STK1135_REG_SENSO + 1, 0x00);
+ /* set lower 8 bits of PLL feedback divider */
+ reg_w(gspca_dev, STK1135_REG_SENSO + 3, 0x07);
+ /* set other PLL parameters */
+ reg_w(gspca_dev, STK1135_REG_PLLFD, 0x06);
+ /* enable timing generator */
+ reg_w(gspca_dev, STK1135_REG_TMGEN, 0x80);
+ /* enable PLL */
+ reg_w(gspca_dev, STK1135_REG_SENSO + 2, 0x04);
+
+ /* set serial interface clock divider (30MHz/0x1f*16+2) = 60240 kHz) */
+ reg_w(gspca_dev, STK1135_REG_SICTL + 2, 0x1f);
+}
+
+static void stk1135_camera_disable(struct gspca_dev *gspca_dev)
+{
+ /* set capture end Y position to 0 */
+ reg_w(gspca_dev, STK1135_REG_CIEPO + 2, 0x00);
+ reg_w(gspca_dev, STK1135_REG_CIEPO + 3, 0x00);
+ /* disable capture */
+ reg_w_mask(gspca_dev, STK1135_REG_SCTRL, 0x00, 0x80);
+
+ /* enable sensor standby and diasble chip enable */
+ sensor_write_mask(gspca_dev, 0x00d, 0x0004, 0x000c);
+
+ /* disable PLL */
+ reg_w_mask(gspca_dev, STK1135_REG_SENSO + 2, 0x00, 0x01);
+ /* disable timing generator */
+ reg_w(gspca_dev, STK1135_REG_TMGEN, 0x00);
+ /* enable STOP clock */
+ reg_w(gspca_dev, STK1135_REG_SENSO + 1, 0x20);
+ /* disable CLKOUT for sensor */
+ reg_w(gspca_dev, STK1135_REG_SENSO, 0x00);
+
+ /* disable sensor (GPIO5) and enable GPIO0,3,6 (?) - sensor standby? */
+ reg_w(gspca_dev, STK1135_REG_GCTRL, 0x49);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ u16 sensor_id;
+ char *sensor_name;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* set GPIO3,4,5,6 direction to output */
+ reg_w(gspca_dev, STK1135_REG_GCTRL + 2, 0x78);
+ /* enable sensor (GPIO5) */
+ reg_w(gspca_dev, STK1135_REG_GCTRL, (1 << 5));
+ /* disable ROM interface */
+ reg_w(gspca_dev, STK1135_REG_GCTRL + 3, 0x80);
+ /* enable interrupts from GPIO8 (flip sensor) and GPIO9 (???) */
+ reg_w(gspca_dev, STK1135_REG_ICTRL + 1, 0x00);
+ reg_w(gspca_dev, STK1135_REG_ICTRL + 3, 0x03);
+ /* enable remote wakeup from GPIO9 (???) */
+ reg_w(gspca_dev, STK1135_REG_RMCTL + 1, 0x00);
+ reg_w(gspca_dev, STK1135_REG_RMCTL + 3, 0x02);
+
+ /* reset serial interface */
+ reg_w(gspca_dev, STK1135_REG_SICTL, 0x80);
+ reg_w(gspca_dev, STK1135_REG_SICTL, 0x00);
+ /* set sensor address */
+ reg_w(gspca_dev, STK1135_REG_SICTL + 3, 0xba);
+ /* disable alt 2-wire serial interface */
+ reg_w(gspca_dev, STK1135_REG_ASIC + 3, 0x00);
+
+ stk1135_configure_clock(gspca_dev);
+
+ /* read sensor ID */
+ sd->sensor_page = 0xff;
+ sensor_id = sensor_read(gspca_dev, 0x000);
+
+ switch (sensor_id) {
+ case 0x148c:
+ sensor_name = "MT9M112";
+ break;
+ default:
+ sensor_name = "unknown";
+ }
+ pr_info("Detected sensor type %s (0x%x)\n", sensor_name, sensor_id);
+
+ stk1135_camera_disable(gspca_dev);
+
+ return gspca_dev->usb_err;
+}
+
+/* -- start the camera -- */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ u16 width, height;
+
+ /* enable sensor (GPIO5) */
+ reg_w(gspca_dev, STK1135_REG_GCTRL, (1 << 5));
+
+ stk1135_configure_clock(gspca_dev);
+
+ /* set capture start position X = 0, Y = 0 */
+ reg_w(gspca_dev, STK1135_REG_CISPO + 0, 0x00);
+ reg_w(gspca_dev, STK1135_REG_CISPO + 1, 0x00);
+ reg_w(gspca_dev, STK1135_REG_CISPO + 2, 0x00);
+ reg_w(gspca_dev, STK1135_REG_CISPO + 3, 0x00);
+
+ /* set capture end position */
+ width = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].width;
+ height = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].height;
+ reg_w(gspca_dev, STK1135_REG_CIEPO + 0, width & 0xff);
+ reg_w(gspca_dev, STK1135_REG_CIEPO + 1, width >> 8);
+ reg_w(gspca_dev, STK1135_REG_CIEPO + 2, height & 0xff);
+ reg_w(gspca_dev, STK1135_REG_CIEPO + 3, height >> 8);
+
+ /* set 8-bit mode */
+ reg_w(gspca_dev, STK1135_REG_SCTRL, 0x20);
+
+ stk1135_configure_mt9m112(gspca_dev);
+
+ /* enable capture */
+ reg_w_mask(gspca_dev, STK1135_REG_SCTRL, 0x80, 0x80);
+
+ if (gspca_dev->usb_err >= 0)
+ PDEBUG(D_STREAM, "camera started alt: 0x%02x",
+ gspca_dev->alt);
+
+ sd->pkt_seq = 0;
+
+ return gspca_dev->usb_err;
+}
+
+static void sd_stopN(struct gspca_dev *gspca_dev)
+{
+ struct usb_device *dev = gspca_dev->dev;
+
+ usb_set_interface(dev, gspca_dev->iface, 0);
+
+ stk1135_camera_disable(gspca_dev);
+
+ PDEBUG(D_STREAM, "camera stopped");
+}
+
+static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int skip = sizeof(struct stk1135_pkt_header);
+ bool flip;
+ enum gspca_packet_type pkt_type = INTER_PACKET;
+ struct stk1135_pkt_header *hdr = (void *)data;
+ u8 seq;
+
+ if (len < 4) {
+ PDEBUG(D_PACK, "received short packet (less than 4 bytes)");
+ return;
+ }
+
+ /* GPIO 8 is flip sensor (1 = normal position, 0 = flipped to back) */
+ flip = !(le16_to_cpu(hdr->gpio) & (1 << 8));
+ /* it's a switch, needs software debounce */
+ if (sd->flip_status != flip)
+ sd->flip_debounce++;
+ else
+ sd->flip_debounce = 0;
+
+ /* check sequence number (not present in new frame packets) */
+ if (!(hdr->flags & STK1135_HDR_FRAME_START)) {
+ seq = hdr->seq & STK1135_HDR_SEQ_MASK;
+ if (seq != sd->pkt_seq) {
+ PDEBUG(D_PACK, "received out-of-sequence packet");
+ /* resync sequence and discard packet */
+ sd->pkt_seq = seq;
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ return;
+ }
+ }
+ sd->pkt_seq++;
+ if (sd->pkt_seq > STK1135_HDR_SEQ_MASK)
+ sd->pkt_seq = 0;
+
+ if (len == sizeof(struct stk1135_pkt_header))
+ return;
+
+ if (hdr->flags & STK1135_HDR_FRAME_START) { /* new frame */
+ skip = 8; /* the header is longer */
+ gspca_frame_add(gspca_dev, LAST_PACKET, data, 0);
+ pkt_type = FIRST_PACKET;
+ }
+ gspca_frame_add(gspca_dev, pkt_type, data + skip, len - skip);
+}
+
+static void sethflip(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->flip_status)
+ val = !val;
+ sensor_write_mask(gspca_dev, 0x020, val ? 0x0002 : 0x0000 , 0x0002);
+}
+
+static void setvflip(struct gspca_dev *gspca_dev, s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->flip_status)
+ val = !val;
+ sensor_write_mask(gspca_dev, 0x020, val ? 0x0001 : 0x0000 , 0x0001);
+}
+
+static void stk1135_dq_callback(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->flip_debounce > 100) {
+ sd->flip_status = !sd->flip_status;
+ sethflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip));
+ setvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->vflip));
+ }
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ sethflip(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_VFLIP:
+ setvflip(gspca_dev, ctrl->val);
+ break;
+ }
+
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 2);
+ sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ return 0;
+}
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .init_controls = sd_init_controls,
+ .start = sd_start,
+ .stopN = sd_stopN,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = stk1135_dq_callback,
+};
+
+/* -- module initialisation -- */
+static const struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x174f, 0x6a31)}, /* ASUS laptop, MT9M112 sensor */
+ {}
+};
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+ .reset_resume = gspca_resume,
+#endif
+};
+
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/gspca/stk1135.h b/drivers/media/usb/gspca/stk1135.h
new file mode 100644
index 0000000..e1dd92a
--- /dev/null
+++ b/drivers/media/usb/gspca/stk1135.h
@@ -0,0 +1,57 @@
+/*
+ * STK1135 registers
+ *
+ * Copyright (c) 2013 Ondrej Zary
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define STK1135_REG_GCTRL 0x000 /* GPIO control */
+#define STK1135_REG_ICTRL 0x004 /* Interrupt control */
+#define STK1135_REG_IDATA 0x008 /* Interrupt data */
+#define STK1135_REG_RMCTL 0x00c /* Remote wakeup control */
+#define STK1135_REG_POSVA 0x010 /* Power-on strapping data */
+
+#define STK1135_REG_SENSO 0x018 /* Sensor select options */
+#define STK1135_REG_PLLFD 0x01c /* PLL frequency divider */
+
+#define STK1135_REG_SCTRL 0x100 /* Sensor control register */
+#define STK1135_REG_DCTRL 0x104 /* Decimation control register */
+#define STK1135_REG_CISPO 0x110 /* Capture image starting position */
+#define STK1135_REG_CIEPO 0x114 /* Capture image ending position */
+#define STK1135_REG_TCTRL 0x120 /* Test data control */
+
+#define STK1135_REG_SICTL 0x200 /* Serial interface control register */
+#define STK1135_REG_SBUSW 0x204 /* Serial bus write */
+#define STK1135_REG_SBUSR 0x208 /* Serial bus read */
+#define STK1135_REG_SCSI 0x20c /* Software control serial interface */
+#define STK1135_REG_GSBWP 0x210 /* General serial bus write port */
+#define STK1135_REG_GSBRP 0x214 /* General serial bus read port */
+#define STK1135_REG_ASIC 0x2fc /* Alternate serial interface control */
+
+#define STK1135_REG_TMGEN 0x300 /* Timing generator */
+#define STK1135_REG_TCP1 0x350 /* Timing control parameter 1 */
+
+struct stk1135_pkt_header {
+ u8 flags;
+ u8 seq;
+ __le16 gpio;
+} __packed;
+
+#define STK1135_HDR_FRAME_START (1 << 7)
+#define STK1135_HDR_ODD (1 << 6)
+#define STK1135_HDR_I2C_VBLANK (1 << 5)
+
+#define STK1135_HDR_SEQ_MASK 0x3f
diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c
index cb69405..6e50707 100644
--- a/drivers/media/usb/hdpvr/hdpvr-core.c
+++ b/drivers/media/usb/hdpvr/hdpvr-core.c
@@ -303,6 +303,11 @@ static int hdpvr_probe(struct usb_interface *interface,
dev->workqueue = 0;
+ /* init video transfer queues first of all */
+ /* to prevent oops in hdpvr_delete() on error paths */
+ INIT_LIST_HEAD(&dev->free_buff_list);
+ INIT_LIST_HEAD(&dev->rec_buff_list);
+
/* register v4l2_device early so it can be used for printks */
if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) {
dev_err(&interface->dev, "v4l2_device_register failed\n");
@@ -325,10 +330,6 @@ static int hdpvr_probe(struct usb_interface *interface,
if (!dev->workqueue)
goto error;
- /* init video transfer queues */
- INIT_LIST_HEAD(&dev->free_buff_list);
- INIT_LIST_HEAD(&dev->rec_buff_list);
-
dev->options = hdpvr_default_options;
if (default_video_input < HDPVR_VIDEO_INPUTS)
@@ -405,7 +406,7 @@ static int hdpvr_probe(struct usb_interface *interface,
video_nr[atomic_inc_return(&dev_nr)]);
if (retval < 0) {
v4l2_err(&dev->v4l2_dev, "registering videodev failed\n");
- goto error;
+ goto reg_fail;
}
/* let the user know what node this device is now attached to */
diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c
index 4f8567a..0500c417 100644
--- a/drivers/media/usb/hdpvr/hdpvr-video.c
+++ b/drivers/media/usb/hdpvr/hdpvr-video.c
@@ -24,6 +24,7 @@
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-dv-timings.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include "hdpvr.h"
@@ -641,7 +642,7 @@ static int vidioc_s_dv_timings(struct file *file, void *_fh,
if (dev->status != STATUS_IDLE)
return -EBUSY;
for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++)
- if (v4l_match_dv_timings(timings, hdpvr_dv_timings + i, 0))
+ if (v4l2_match_dv_timings(timings, hdpvr_dv_timings + i, 0))
break;
if (i == ARRAY_SIZE(hdpvr_dv_timings))
return -EINVAL;
@@ -689,10 +690,8 @@ static int vidioc_query_dv_timings(struct file *file, void *_fh,
unsigned vsize;
unsigned fps;
- hsize = bt->hfrontporch + bt->hsync + bt->hbackporch + bt->width;
- vsize = bt->vfrontporch + bt->vsync + bt->vbackporch +
- bt->il_vfrontporch + bt->il_vsync + bt->il_vbackporch +
- bt->height;
+ hsize = V4L2_DV_BT_FRAME_WIDTH(bt);
+ vsize = V4L2_DV_BT_FRAME_HEIGHT(bt);
fps = (unsigned)bt->pixelclock / (hsize * vsize);
if (bt->width != vid_info.width ||
bt->height != vid_info.height ||
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index ab97e7d..6bc9b8e 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -1,7 +1,7 @@
/*
* s2255drv.c - a driver for the Sensoray 2255 USB video capture device
*
- * Copyright (C) 2007-2010 by Sensoray Company Inc.
+ * Copyright (C) 2007-2013 by Sensoray Company Inc.
* Dean Anderson
*
* Some video buffer code based on vivi driver:
@@ -52,7 +52,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
-#define S2255_VERSION "1.22.1"
+#define S2255_VERSION "1.23.1"
#define FIRMWARE_FILE_NAME "f2255usb.bin"
/* default JPEG quality */
@@ -1303,11 +1303,6 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i)
int ret = 0;
mutex_lock(&q->vb_lock);
- if (videobuf_queue_is_busy(q)) {
- dprintk(1, "queue busy\n");
- ret = -EBUSY;
- goto out_s_std;
- }
if (res_locked(fh)) {
dprintk(1, "can't change standard after started\n");
ret = -EBUSY;
diff --git a/drivers/media/usb/stk1160/Kconfig b/drivers/media/usb/stk1160/Kconfig
index 1c3a1ec..95584c1 100644
--- a/drivers/media/usb/stk1160/Kconfig
+++ b/drivers/media/usb/stk1160/Kconfig
@@ -1,8 +1,6 @@
-config VIDEO_STK1160
+config VIDEO_STK1160_COMMON
tristate "STK1160 USB video capture support"
depends on VIDEO_DEV && I2C
- select VIDEOBUF2_VMALLOC
- select VIDEO_SAA711X
---help---
This is a video4linux driver for STK1160 based video capture devices.
@@ -12,9 +10,15 @@ config VIDEO_STK1160
config VIDEO_STK1160_AC97
bool "STK1160 AC97 codec support"
- depends on VIDEO_STK1160 && SND
- select SND_AC97_CODEC
+ depends on VIDEO_STK1160_COMMON && SND
---help---
Enables AC97 codec support for stk1160 driver.
-.
+
+config VIDEO_STK1160
+ tristate
+ depends on (!VIDEO_STK1160_AC97 || (SND='n') || SND) && VIDEO_STK1160_COMMON
+ default y
+ select VIDEOBUF2_VMALLOC
+ select VIDEO_SAA711X
+ select SND_AC97_CODEC if SND
diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c
index 876fc26..c45c988 100644
--- a/drivers/media/usb/stk1160/stk1160-v4l.c
+++ b/drivers/media/usb/stk1160/stk1160-v4l.c
@@ -379,6 +379,9 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
struct stk1160 *dev = video_drvdata(file);
struct vb2_queue *q = &dev->vb_vidq;
+ if (dev->norm == norm)
+ return 0;
+
if (vb2_is_busy(q))
return -EBUSY;
@@ -440,9 +443,6 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
struct stk1160 *dev = video_drvdata(file);
- if (vb2_is_busy(&dev->vb_vidq))
- return -EBUSY;
-
if (i > STK1160_MAX_INPUT)
return -EINVAL;
diff --git a/drivers/media/usb/tlg2300/pd-main.c b/drivers/media/usb/tlg2300/pd-main.c
index e07e4c6..95f94e5 100644
--- a/drivers/media/usb/tlg2300/pd-main.c
+++ b/drivers/media/usb/tlg2300/pd-main.c
@@ -375,7 +375,7 @@ static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev)
}
#endif
-static int check_firmware(struct usb_device *udev, int *down_firmware)
+static int check_firmware(struct usb_device *udev)
{
void *buf;
int ret;
@@ -395,10 +395,8 @@ static int check_firmware(struct usb_device *udev, int *down_firmware)
USB_CTRL_GET_TIMEOUT);
kfree(buf);
- if (ret < 0) {
- *down_firmware = 1;
+ if (ret < 0)
return firmware_download(udev);
- }
return 0;
}
@@ -411,9 +409,9 @@ static int poseidon_probe(struct usb_interface *interface,
int new_one = 0;
/* download firmware */
- check_firmware(udev, &ret);
+ ret = check_firmware(udev);
if (ret)
- return 0;
+ return ret;
/* Do I recovery from the hibernate ? */
pd = find_old_poseidon(udev);
@@ -436,12 +434,22 @@ static int poseidon_probe(struct usb_interface *interface,
/* register v4l2 device */
ret = v4l2_device_register(&interface->dev, &pd->v4l2_dev);
+ if (ret)
+ goto err_v4l2;
/* register devices in directory /dev */
ret = pd_video_init(pd);
- poseidon_audio_init(pd);
- poseidon_fm_init(pd);
- pd_dvb_usb_device_init(pd);
+ if (ret)
+ goto err_video;
+ ret = poseidon_audio_init(pd);
+ if (ret)
+ goto err_audio;
+ ret = poseidon_fm_init(pd);
+ if (ret)
+ goto err_fm;
+ ret = pd_dvb_usb_device_init(pd);
+ if (ret)
+ goto err_dvb;
INIT_LIST_HEAD(&pd->device_list);
list_add_tail(&pd->device_list, &pd_device_list);
@@ -459,6 +467,17 @@ static int poseidon_probe(struct usb_interface *interface,
}
#endif
return 0;
+err_dvb:
+ poseidon_fm_exit(pd);
+err_fm:
+ poseidon_audio_free(pd);
+err_audio:
+ pd_video_exit(pd);
+err_video:
+ v4l2_device_unregister(&pd->v4l2_dev);
+err_v4l2:
+ kfree(pd);
+ return ret;
}
static void poseidon_disconnect(struct usb_interface *interface)
diff --git a/drivers/media/usb/usbtv/Kconfig b/drivers/media/usb/usbtv/Kconfig
index 8864436..7c5b860 100644
--- a/drivers/media/usb/usbtv/Kconfig
+++ b/drivers/media/usb/usbtv/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_USBTV
tristate "USBTV007 video capture support"
- depends on VIDEO_DEV
+ depends on VIDEO_V4L2
select VIDEOBUF2_VMALLOC
---help---
diff --git a/drivers/media/usb/usbtv/usbtv.c b/drivers/media/usb/usbtv/usbtv.c
index bf43f87..8a505a9 100644
--- a/drivers/media/usb/usbtv/usbtv.c
+++ b/drivers/media/usb/usbtv/usbtv.c
@@ -33,7 +33,6 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
-#include <linux/version.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
@@ -57,7 +56,7 @@
#define USBTV_CHUNK_SIZE 256
#define USBTV_CHUNK 240
#define USBTV_CHUNKS (USBTV_WIDTH * USBTV_HEIGHT \
- / 2 / USBTV_CHUNK)
+ / 4 / USBTV_CHUNK)
/* Chunk header. */
#define USBTV_MAGIC_OK(chunk) ((be32_to_cpu(chunk[0]) & 0xff000000) \
@@ -89,18 +88,80 @@ struct usbtv {
/* Number of currently processed frame, useful find
* out when a new one begins. */
u32 frame_id;
+ int chunks_done;
+ enum {
+ USBTV_COMPOSITE_INPUT,
+ USBTV_SVIDEO_INPUT,
+ } input;
int iso_size;
unsigned int sequence;
struct urb *isoc_urbs[USBTV_ISOC_TRANSFERS];
};
-static int usbtv_setup_capture(struct usbtv *usbtv)
+static int usbtv_set_regs(struct usbtv *usbtv, const u16 regs[][2], int size)
{
int ret;
int pipe = usb_rcvctrlpipe(usbtv->udev, 0);
int i;
- static const u16 protoregs[][2] = {
+
+ for (i = 0; i < size; i++) {
+ u16 index = regs[i][0];
+ u16 value = regs[i][1];
+
+ ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int usbtv_select_input(struct usbtv *usbtv, int input)
+{
+ int ret;
+
+ static const u16 composite[][2] = {
+ { USBTV_BASE + 0x0105, 0x0060 },
+ { USBTV_BASE + 0x011f, 0x00f2 },
+ { USBTV_BASE + 0x0127, 0x0060 },
+ { USBTV_BASE + 0x00ae, 0x0010 },
+ { USBTV_BASE + 0x0284, 0x00aa },
+ { USBTV_BASE + 0x0239, 0x0060 },
+ };
+
+ static const u16 svideo[][2] = {
+ { USBTV_BASE + 0x0105, 0x0010 },
+ { USBTV_BASE + 0x011f, 0x00ff },
+ { USBTV_BASE + 0x0127, 0x0060 },
+ { USBTV_BASE + 0x00ae, 0x0030 },
+ { USBTV_BASE + 0x0284, 0x0088 },
+ { USBTV_BASE + 0x0239, 0x0060 },
+ };
+
+ switch (input) {
+ case USBTV_COMPOSITE_INPUT:
+ ret = usbtv_set_regs(usbtv, composite, ARRAY_SIZE(composite));
+ break;
+ case USBTV_SVIDEO_INPUT:
+ ret = usbtv_set_regs(usbtv, svideo, ARRAY_SIZE(svideo));
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ if (!ret)
+ usbtv->input = input;
+
+ return ret;
+}
+
+static int usbtv_setup_capture(struct usbtv *usbtv)
+{
+ int ret;
+ static const u16 setup[][2] = {
/* These seem to enable the device. */
{ USBTV_BASE + 0x0008, 0x0001 },
{ USBTV_BASE + 0x01d0, 0x00ff },
@@ -188,20 +249,37 @@ static int usbtv_setup_capture(struct usbtv *usbtv)
{ USBTV_BASE + 0x024f, 0x0002 },
};
- for (i = 0; i < ARRAY_SIZE(protoregs); i++) {
- u16 index = protoregs[i][0];
- u16 value = protoregs[i][1];
+ ret = usbtv_set_regs(usbtv, setup, ARRAY_SIZE(setup));
+ if (ret)
+ return ret;
- ret = usb_control_msg(usbtv->udev, pipe, USBTV_REQUEST_REG,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- value, index, NULL, 0, 0);
- if (ret < 0)
- return ret;
- }
+ ret = usbtv_select_input(usbtv, usbtv->input);
+ if (ret)
+ return ret;
return 0;
}
+/* Copy data from chunk into a frame buffer, deinterlacing the data
+ * into every second line. Unfortunately, they don't align nicely into
+ * 720 pixel lines, as the chunk is 240 words long, which is 480 pixels.
+ * Therefore, we break down the chunk into two halves before copyting,
+ * so that we can interleave a line if needed. */
+static void usbtv_chunk_to_vbuf(u32 *frame, u32 *src, int chunk_no, int odd)
+{
+ int half;
+
+ for (half = 0; half < 2; half++) {
+ int part_no = chunk_no * 2 + half;
+ int line = part_no / 3;
+ int part_index = (line * 2 + !odd) * 3 + (part_no % 3);
+
+ u32 *dst = &frame[part_index * USBTV_CHUNK/2];
+ memcpy(dst, src, USBTV_CHUNK/2 * sizeof(*src));
+ src += USBTV_CHUNK/2;
+ }
+}
+
/* Called for each 256-byte image chunk.
* First word identifies the chunk, followed by 240 words of image
* data and padding. */
@@ -218,17 +296,17 @@ static void usbtv_image_chunk(struct usbtv *usbtv, u32 *chunk)
frame_id = USBTV_FRAME_ID(chunk);
odd = USBTV_ODD(chunk);
chunk_no = USBTV_CHUNK_NO(chunk);
-
- /* Deinterlace. TODO: Use interlaced frame format. */
- chunk_no = (chunk_no - chunk_no % 3) * 2 + chunk_no % 3;
- chunk_no += !odd * 3;
-
if (chunk_no >= USBTV_CHUNKS)
return;
/* Beginning of a frame. */
- if (chunk_no == 0)
+ if (chunk_no == 0) {
usbtv->frame_id = frame_id;
+ usbtv->chunks_done = 0;
+ }
+
+ if (usbtv->frame_id != frame_id)
+ return;
spin_lock_irqsave(&usbtv->buflock, flags);
if (list_empty(&usbtv->bufs)) {
@@ -241,19 +319,23 @@ static void usbtv_image_chunk(struct usbtv *usbtv, u32 *chunk)
buf = list_first_entry(&usbtv->bufs, struct usbtv_buf, list);
frame = vb2_plane_vaddr(&buf->vb, 0);
- /* Copy the chunk. */
- memcpy(&frame[chunk_no * USBTV_CHUNK], &chunk[1],
- USBTV_CHUNK * sizeof(chunk[1]));
+ /* Copy the chunk data. */
+ usbtv_chunk_to_vbuf(frame, &chunk[1], chunk_no, odd);
+ usbtv->chunks_done++;
/* Last chunk in a frame, signalling an end */
- if (usbtv->frame_id && chunk_no == USBTV_CHUNKS-1) {
+ if (odd && chunk_no == USBTV_CHUNKS-1) {
int size = vb2_plane_size(&buf->vb, 0);
+ enum vb2_buffer_state state = usbtv->chunks_done ==
+ USBTV_CHUNKS ?
+ VB2_BUF_STATE_DONE :
+ VB2_BUF_STATE_ERROR;
buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
buf->vb.v4l2_buf.sequence = usbtv->sequence++;
v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp);
vb2_set_plane_payload(&buf->vb, 0, size);
- vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE);
+ vb2_buffer_done(&buf->vb, state);
list_del(&buf->list);
}
@@ -418,10 +500,17 @@ static int usbtv_querycap(struct file *file, void *priv,
static int usbtv_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
{
- if (i->index > 0)
+ switch (i->index) {
+ case USBTV_COMPOSITE_INPUT:
+ strlcpy(i->name, "Composite", sizeof(i->name));
+ break;
+ case USBTV_SVIDEO_INPUT:
+ strlcpy(i->name, "S-Video", sizeof(i->name));
+ break;
+ default:
return -EINVAL;
+ }
- strlcpy(i->name, "Composite", sizeof(i->name));
i->type = V4L2_INPUT_TYPE_CAMERA;
i->std = V4L2_STD_525_60;
return 0;
@@ -461,15 +550,15 @@ static int usbtv_g_std(struct file *file, void *priv, v4l2_std_id *norm)
static int usbtv_g_input(struct file *file, void *priv, unsigned int *i)
{
- *i = 0;
+ struct usbtv *usbtv = video_drvdata(file);
+ *i = usbtv->input;
return 0;
}
static int usbtv_s_input(struct file *file, void *priv, unsigned int i)
{
- if (i > 0)
- return -EINVAL;
- return 0;
+ struct usbtv *usbtv = video_drvdata(file);
+ return usbtv_select_input(usbtv, i);
}
static int usbtv_s_std(struct file *file, void *priv, v4l2_std_id norm)
@@ -518,7 +607,7 @@ static int usbtv_queue_setup(struct vb2_queue *vq,
if (*nbuffers < 2)
*nbuffers = 2;
*nplanes = 1;
- sizes[0] = USBTV_CHUNK * USBTV_CHUNKS * sizeof(u32);
+ sizes[0] = USBTV_WIDTH * USBTV_HEIGHT / 2 * sizeof(u32);
return 0;
}
OpenPOWER on IntegriCloud