Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (25 commits)
V4L/DVB (12206): get_dvb_firmware: Correct errors in MPC718 firmware extraction logic
V4L/DVB (12203): radio-si470x: fix lock imbalance
V4L/DVB (12202): em28xx, fix lock imbalance
V4L/DVB (12172): em28xx: Add autodetection code for Silvercrest 1.3 mpix
V4L/DVB (12171): em28xx: fix webcam usage with different output formats
V4L/DVB (12169): em28xx-video: fix VIDIOC_G_FMT and VIDIOC_ENUMFMT with webcams
V4L/DVB (12156): em28xx: Fix tuning for Terratec Cinergy T XS USB (zl10353 version)
V4L/DVB (12139): em28xx: add other video formats
V4L/DVB (12138): em28xx: add support for Silvercrest Webcam
V4L/DVB (12174): mt9v011: let's stick with datasheet values where it works
V4L/DVB (12173): mt9v011: properly calculate image resolution registers
V4L/DVB (12137): mt9v011: CodingStyle fixes
V4L/DVB (12136): mt9v011: Some fixes at the register initialization table
V4L/DVB (12135): Add a driver for mt9v011 sensor
V4L/DVB (12166): cx23885: add FIXME comment above set_frontend override
V4L/DVB (12165): cx23885: override set_frontend to allow rf input path switching on the HVR1275
V4L/DVB (12148): move V4L2_PIX_FMT_SGRBG8 to the proper place
V4L/DVB (12182): cx18: Add DVB-T support for Yuan MPC-718 cards with an MT352 or ZL10353
V4L/DVB (12181): get_dvb_firmware: Add Yuan MPC718 MT352 DVB-T "firmware" extraction
V4L/DVB (12180): cx18: Update Yuan MPC-718 card entry with better information and guesses
...
diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware
index a52adfc..3d1b0ab 100644
--- a/Documentation/dvb/get_dvb_firmware
+++ b/Documentation/dvb/get_dvb_firmware
@@ -25,7 +25,7 @@
"tda10046lifeview", "av7110", "dec2000t", "dec2540t",
"dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004",
"or51211", "or51132_qam", "or51132_vsb", "bluebird",
- "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2" );
+ "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718" );
# Check args
syntax() if (scalar(@ARGV) != 1);
@@ -381,6 +381,57 @@
$allfiles;
}
+sub mpc718 {
+ my $archive = 'Yuan MPC718 TV Tuner Card 2.13.10.1016.zip';
+ my $url = "ftp://ftp.work.acer-euro.com/desktop/aspire_idea510/vista/Drivers/$archive";
+ my $fwfile = "dvb-cx18-mpc718-mt352.fw";
+ my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1);
+
+ checkstandard();
+ wgetfile($archive, $url);
+ unzip($archive, $tmpdir);
+
+ my $sourcefile = "$tmpdir/Yuan MPC718 TV Tuner Card 2.13.10.1016/mpc718_32bit/yuanrap.sys";
+ my $found = 0;
+
+ open IN, '<', $sourcefile or die "Couldn't open $sourcefile to extract $fwfile data\n";
+ binmode IN;
+ open OUT, '>', $fwfile;
+ binmode OUT;
+ {
+ # Block scope because we change the line terminator variable $/
+ my $prevlen = 0;
+ my $currlen;
+
+ # Buried in the data segment are 3 runs of almost identical
+ # register-value pairs that end in 0x5d 0x01 which is a "TUNER GO"
+ # command for the MT352.
+ # Pull out the middle run (because it's easy) of register-value
+ # pairs to make the "firmware" file.
+
+ local $/ = "\x5d\x01"; # MT352 "TUNER GO"
+
+ while (<IN>) {
+ $currlen = length($_);
+ if ($prevlen == $currlen && $currlen <= 64) {
+ chop; chop; # Get rid of "TUNER GO"
+ s/^\0\0//; # get rid of leading 00 00 if it's there
+ printf OUT "$_";
+ $found = 1;
+ last;
+ }
+ $prevlen = $currlen;
+ }
+ }
+ close OUT;
+ close IN;
+ if (!$found) {
+ unlink $fwfile;
+ die "Couldn't find valid register-value sequence in $sourcefile for $fwfile\n";
+ }
+ $fwfile;
+}
+
sub cx23885 {
my $url = "http://linuxtv.org/downloads/firmware/";
diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx
index 873630e..014d255 100644
--- a/Documentation/video4linux/CARDLIST.em28xx
+++ b/Documentation/video4linux/CARDLIST.em28xx
@@ -66,3 +66,4 @@
68 -> Terratec AV350 (em2860) [0ccd:0084]
69 -> KWorld ATSC 315U HDTV TV Box (em2882) [eb1a:a313]
70 -> Evga inDtube (em2882)
+ 71 -> Silvercrest Webcam 1.3mpix (em2820/em2840)
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index b6da9c3..aa20ce8 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -1096,8 +1096,19 @@
}
/* All S-code tables need a 200kHz shift */
- if (priv->ctrl.demod)
+ if (priv->ctrl.demod) {
demod = priv->ctrl.demod + 200;
+ /*
+ * The DTV7 S-code table needs a 700 kHz shift.
+ * Thanks to Terry Wu <terrywu2009@gmail.com> for reporting this
+ *
+ * DTV7 is only used in Australia. Germany or Italy may also
+ * use this firmware after initialization, but a tune to a UHF
+ * channel should then cause DTV78 to be used.
+ */
+ if (type & DTV7)
+ demod += 500;
+ }
return generic_set_freq(fe, p->frequency,
T_DIGITAL_TV, type, 0, demod);
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index 68eb449..d8d4214 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -1,5 +1,6 @@
config TTPCI_EEPROM
tristate
+ depends on I2C
default n
config DVB_AV7110
diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c
index 640421c..46d2163 100644
--- a/drivers/media/radio/radio-si470x.c
+++ b/drivers/media/radio/radio-si470x.c
@@ -1200,7 +1200,7 @@
video_unregister_device(radio->videodev);
kfree(radio->buffer);
kfree(radio);
- goto done;
+ goto unlock;
}
/* stop rds reception */
@@ -1213,9 +1213,8 @@
retval = si470x_stop(radio);
usb_autopm_put_interface(radio->intf);
}
-
+unlock:
mutex_unlock(&radio->disconnect_lock);
-
done:
return retval;
}
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 061e147..84b6fc1 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -312,6 +312,14 @@
OV7670 VGA camera. It currently only works with the M88ALP01
controller.
+config VIDEO_MT9V011
+ tristate "Micron mt9v011 sensor support"
+ depends on I2C && VIDEO_V4L2
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the Micron
+ mt0v011 1.3 Mpixel camera. It currently only works with the
+ em28xx driver.
+
config VIDEO_TCM825X
tristate "TCM825x camera sensor support"
depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 7fb3add..9f2e321 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -69,6 +69,7 @@
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
+obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
index c92a250..36f2d76 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -198,11 +198,14 @@
static const struct cx18_card cx18_card_mpc718 = {
.type = CX18_CARD_YUAN_MPC718,
- .name = "Yuan MPC718",
- .comment = "Analog video capture works; some audio line in may not.\n",
+ .name = "Yuan MPC718 MiniPCI DVB-T/Analog",
+ .comment = "Experimenters needed for device to work well.\n"
+ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
.v4l2_capabilities = CX18_CAP_ENCODER,
.hw_audio_ctrl = CX18_HW_418_AV,
- .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
+ .hw_muxer = CX18_HW_GPIO_MUX,
+ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER |
+ CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
.video_inputs = {
{ CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
@@ -211,27 +214,34 @@
{ CX18_CARD_INPUT_SVIDEO2, 2,
CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 },
{ CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 },
- { CX18_CARD_INPUT_COMPOSITE3, 2, CX18_AV_COMPOSITE3 },
},
.audio_inputs = {
{ CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 },
- { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 0 },
- { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL1, 0 },
+ { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 },
+ { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL2, 1 },
},
- .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL1, 0 },
.tuners = {
/* XC3028 tuner */
{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
},
+ /* FIXME - the FM radio is just a guess and driver doesn't use SIF */
+ .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 },
.ddr = {
- /* Probably Samsung K4D263238G-VC33 memory */
- .chip_config = 0x003,
- .refresh = 0x30c,
- .timing1 = 0x23230b73,
- .timing2 = 0x08,
+ /* Hynix HY5DU283222B DDR RAM */
+ .chip_config = 0x303,
+ .refresh = 0x3bd,
+ .timing1 = 0x36320966,
+ .timing2 = 0x1f,
.tune_lane = 0,
.initial_emrs = 2,
},
+ .gpio_init.initial_value = 0x1,
+ .gpio_init.direction = 0x3,
+ /* FIXME - these GPIO's are just guesses */
+ .gpio_audio_input = { .mask = 0x3,
+ .tuner = 0x1,
+ .linein = 0x3,
+ .radio = 0x1 },
.xceive_pin = 0,
.pci_list = cx18_pci_mpc718,
.i2c = &cx18_i2c_std,
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
index 6ea3fe6..51a0c33 100644
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -30,6 +30,10 @@
#include "s5h1409.h"
#include "mxl5005s.h"
#include "zl10353.h"
+
+#include <linux/firmware.h>
+#include "mt352.h"
+#include "mt352_priv.h"
#include "tuner-xc2028.h"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
@@ -38,6 +42,11 @@
#define CX18_CLOCK_ENABLE2 0xc71024
#define CX18_DMUX_CLK_MASK 0x0080
+/*
+ * CX18_CARD_HVR_1600_ESMT
+ * CX18_CARD_HVR_1600_SAMSUNG
+ */
+
static struct mxl5005s_config hauppauge_hvr1600_tuner = {
.i2c_address = 0xC6 >> 1,
.if_freq = IF_FREQ_5380000HZ,
@@ -65,6 +74,9 @@
.mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
};
+/*
+ * CX18_CARD_LEADTEK_DVR3100H
+ */
/* Information/confirmation of proper config values provided by Terry Wu */
static struct zl10353_config leadtek_dvr3100h_demod = {
.demod_address = 0x1e >> 1, /* Datasheet suggested straps */
@@ -74,6 +86,121 @@
.disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */
};
+/*
+ * CX18_CARD_YUAN_MPC718
+ */
+/*
+ * Due to
+ *
+ * 1. an absence of information on how to prgram the MT352
+ * 2. the Linux mt352 module pushing MT352 initialzation off onto us here
+ *
+ * We have to use an init sequence that *you* must extract from the Windows
+ * driver (yuanrap.sys) and which we load as a firmware.
+ *
+ * If someone can provide me with a Zarlink MT352 (Intel CE6352?) Design Manual
+ * with chip programming details, then I can remove this annoyance.
+ */
+static int yuan_mpc718_mt352_reqfw(struct cx18_stream *stream,
+ const struct firmware **fw)
+{
+ struct cx18 *cx = stream->cx;
+ const char *fn = "dvb-cx18-mpc718-mt352.fw";
+ int ret;
+
+ ret = request_firmware(fw, fn, &cx->pci_dev->dev);
+ if (ret)
+ CX18_ERR("Unable to open firmware file %s\n", fn);
+ else {
+ size_t sz = (*fw)->size;
+ if (sz < 2 || sz > 64 || (sz % 2) != 0) {
+ CX18_ERR("Firmware %s has a bad size: %lu bytes\n",
+ fn, (unsigned long) sz);
+ ret = -EILSEQ;
+ release_firmware(*fw);
+ *fw = NULL;
+ }
+ }
+
+ if (ret) {
+ CX18_ERR("The MPC718 board variant with the MT352 DVB-T"
+ "demodualtor will not work without it\n");
+ CX18_ERR("Run 'linux/Documentation/dvb/get_dvb_firmware "
+ "mpc718' if you need the firmware\n");
+ }
+ return ret;
+}
+
+static int yuan_mpc718_mt352_init(struct dvb_frontend *fe)
+{
+ struct cx18_dvb *dvb = container_of(fe->dvb,
+ struct cx18_dvb, dvb_adapter);
+ struct cx18_stream *stream = container_of(dvb, struct cx18_stream, dvb);
+ const struct firmware *fw = NULL;
+ int ret;
+ int i;
+ u8 buf[3];
+
+ ret = yuan_mpc718_mt352_reqfw(stream, &fw);
+ if (ret)
+ return ret;
+
+ /* Loop through all the register-value pairs in the firmware file */
+ for (i = 0; i < fw->size; i += 2) {
+ buf[0] = fw->data[i];
+ /* Intercept a few registers we want to set ourselves */
+ switch (buf[0]) {
+ case TRL_NOMINAL_RATE_0:
+ /* Set our custom OFDM bandwidth in the case below */
+ break;
+ case TRL_NOMINAL_RATE_1:
+ /* 6 MHz: 64/7 * 6/8 / 20.48 * 2^16 = 0x55b6.db6 */
+ /* 7 MHz: 64/7 * 7/8 / 20.48 * 2^16 = 0x6400 */
+ /* 8 MHz: 64/7 * 8/8 / 20.48 * 2^16 = 0x7249.249 */
+ buf[1] = 0x72;
+ buf[2] = 0x49;
+ mt352_write(fe, buf, 3);
+ break;
+ case INPUT_FREQ_0:
+ /* Set our custom IF in the case below */
+ break;
+ case INPUT_FREQ_1:
+ /* 4.56 MHz IF: (20.48 - 4.56)/20.48 * 2^14 = 0x31c0 */
+ buf[1] = 0x31;
+ buf[2] = 0xc0;
+ mt352_write(fe, buf, 3);
+ break;
+ default:
+ /* Pass through the register-value pair from the fw */
+ buf[1] = fw->data[i+1];
+ mt352_write(fe, buf, 2);
+ break;
+ }
+ }
+
+ buf[0] = (u8) TUNER_GO;
+ buf[1] = 0x01; /* Go */
+ mt352_write(fe, buf, 2);
+ release_firmware(fw);
+ return 0;
+}
+
+static struct mt352_config yuan_mpc718_mt352_demod = {
+ .demod_address = 0x1e >> 1,
+ .adc_clock = 20480, /* 20.480 MHz */
+ .if2 = 4560, /* 4.560 MHz */
+ .no_tuner = 1, /* XC3028 is not behind the gate */
+ .demod_init = yuan_mpc718_mt352_init,
+};
+
+static struct zl10353_config yuan_mpc718_zl10353_demod = {
+ .demod_address = 0x1e >> 1, /* Datasheet suggested straps */
+ .if2 = 45600, /* 4.560 MHz IF from the XC3028 */
+ .parallel_ts = 1, /* Not a serial TS */
+ .no_tuner = 1, /* XC3028 is not behind the gate */
+ .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */
+};
+
static int dvb_register(struct cx18_stream *stream);
/* Kernel DVB framework calls this when the feed needs to start.
@@ -113,6 +240,7 @@
break;
case CX18_CARD_LEADTEK_DVR3100H:
+ case CX18_CARD_YUAN_MPC718:
default:
/* Assumption - Parallel transport - Signalling
* undefined or default.
@@ -326,6 +454,38 @@
fe->ops.tuner_ops.set_config(fe, &ctrl);
}
break;
+ case CX18_CARD_YUAN_MPC718:
+ /*
+ * TODO
+ * Apparently, these cards also could instead have a
+ * DiBcom demod supported by one of the db7000 drivers
+ */
+ dvb->fe = dvb_attach(mt352_attach,
+ &yuan_mpc718_mt352_demod,
+ &cx->i2c_adap[1]);
+ if (dvb->fe == NULL)
+ dvb->fe = dvb_attach(zl10353_attach,
+ &yuan_mpc718_zl10353_demod,
+ &cx->i2c_adap[1]);
+ if (dvb->fe != NULL) {
+ struct dvb_frontend *fe;
+ struct xc2028_config cfg = {
+ .i2c_adap = &cx->i2c_adap[1],
+ .i2c_addr = 0xc2 >> 1,
+ .ctrl = NULL,
+ };
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+ .max_len = 64,
+ .demod = XC3028_FE_ZARLINK456,
+ .type = XC2028_AUTO,
+ };
+
+ fe = dvb_attach(xc2028_attach, dvb->fe, &cfg);
+ if (fe != NULL && fe->ops.tuner_ops.set_config != NULL)
+ fe->ops.tuner_ops.set_config(fe, &ctrl);
+ }
+ break;
default:
/* No Digital Tv Support */
break;
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index 48a9751..86ac529 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -463,6 +463,30 @@
.if_khz = 5380,
};
+static int cx23885_dvb_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+{
+ struct cx23885_tsport *port = fe->dvb->priv;
+ struct cx23885_dev *dev = port->dev;
+
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1275:
+ switch (param->u.vsb.modulation) {
+ case VSB_8:
+ cx23885_gpio_clear(dev, GPIO_5);
+ break;
+ case QAM_64:
+ case QAM_256:
+ default:
+ cx23885_gpio_set(dev, GPIO_5);
+ break;
+ }
+ break;
+ }
+ return (port->set_frontend_save) ?
+ port->set_frontend_save(fe, param) : -ENODEV;
+}
+
static int dvb_register(struct cx23885_tsport *port)
{
struct cx23885_dev *dev = port->dev;
@@ -502,6 +526,12 @@
0x60, &dev->i2c_bus[1].i2c_adap,
&hauppauge_hvr127x_config);
}
+
+ /* FIXME: temporary hack */
+ /* define bridge override to set_frontend */
+ port->set_frontend_save = fe0->dvb.frontend->ops.set_frontend;
+ fe0->dvb.frontend->ops.set_frontend = cx23885_dvb_set_frontend;
+
break;
case CX23885_BOARD_HAUPPAUGE_HVR1255:
i2c_bus = &dev->i2c_bus[0];
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index 1a2ac51..214a55e 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -288,6 +288,10 @@
/* Allow a single tsport to have multiple frontends */
u32 num_frontends;
void *port_priv;
+
+ /* FIXME: temporary hack */
+ int (*set_frontend_save) (struct dvb_frontend *,
+ struct dvb_frontend_parameters *);
};
struct cx23885_dev {
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
index 16a5af3..6524b49 100644
--- a/drivers/media/video/em28xx/Kconfig
+++ b/drivers/media/video/em28xx/Kconfig
@@ -8,6 +8,8 @@
select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_MT9V011 if VIDEO_HELPER_CHIPS_AUTO
+
---help---
This is a video4linux driver for Empia 28xx based TV cards.
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index c43fdb9..ebd24a2 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -58,6 +58,8 @@
module_param_array(card, int, NULL, 0444);
MODULE_PARM_DESC(card, "card type");
+#define MT9V011_VERSION 0x8243
+
/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
static unsigned long em28xx_devused;
@@ -191,6 +193,13 @@
{EM28XX_R08_GPIO, 0xff, 0xff, 10},
{ -1, -1, -1, -1},
};
+
+static struct em28xx_reg_seq silvercrest_reg_seq[] = {
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM28XX_R08_GPIO, 0x01, 0xf7, 10},
+ { -1, -1, -1, -1},
+};
+
/*
* Board definitions
*/
@@ -438,6 +447,18 @@
.amux = EM28XX_AMUX_VIDEO,
} },
},
+ [EM2820_BOARD_SILVERCREST_WEBCAM] = {
+ .name = "Silvercrest Webcam 1.3mpix",
+ .tuner_type = TUNER_ABSENT,
+ .is_27xx = 1,
+ .decoder = EM28XX_MT9V011,
+ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .amux = EM28XX_AMUX_VIDEO,
+ .gpio = silvercrest_reg_seq,
+ } },
+ },
[EM2821_BOARD_SUPERCOMP_USB_2] = {
.name = "Supercomp USB 2.0 TV",
.valid = EM28XX_BOARD_NOT_VALIDATED,
@@ -826,7 +847,7 @@
.tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
.has_dvb = 1,
- .dvb_gpio = default_analog,
+ .dvb_gpio = default_digital,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
@@ -1639,6 +1660,11 @@
I2C_CLIENT_END
};
+static unsigned short mt9v011_addrs[] = {
+ 0xba >> 1,
+ I2C_CLIENT_END
+};
+
static unsigned short msp3400_addrs[] = {
0x80 >> 1,
0x88 >> 1,
@@ -1678,6 +1704,46 @@
EM28XX_I2C_FREQ_100_KHZ;
}
+/* HINT method: webcam I2C chips
+ *
+ * This method work for webcams with Micron sensors
+ */
+static int em28xx_hint_sensor(struct em28xx *dev)
+{
+ int rc;
+ char *sensor_name;
+ unsigned char cmd;
+ __be16 version_be;
+ u16 version;
+
+ if (dev->model != EM2820_BOARD_UNKNOWN)
+ return 0;
+
+ dev->i2c_client.addr = 0xba >> 1;
+ cmd = 0;
+ i2c_master_send(&dev->i2c_client, &cmd, 1);
+ rc = i2c_master_recv(&dev->i2c_client, (char *)&version_be, 2);
+ if (rc != 2)
+ return -EINVAL;
+
+ version = be16_to_cpu(version_be);
+
+ switch (version) {
+ case MT9V011_VERSION:
+ dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
+ sensor_name = "mt9v011";
+ break;
+ default:
+ printk("Unknown Sensor 0x%04x\n", be16_to_cpu(version));
+ return -EINVAL;
+ }
+
+ em28xx_errdev("Sensor is %s, assuming that webcam is %s\n",
+ sensor_name, em28xx_boards[dev->model].name);
+
+ return 0;
+}
+
/* Since em28xx_pre_card_setup() requires a proper dev->model,
* this won't work for boards with generic PCI IDs
*/
@@ -1706,7 +1772,10 @@
em28xx_info("chip ID is em2750\n");
break;
case CHIP_ID_EM2820:
- em28xx_info("chip ID is em2820\n");
+ if (dev->board.is_27xx)
+ em28xx_info("chip is em2710\n");
+ else
+ em28xx_info("chip ID is em2820\n");
break;
case CHIP_ID_EM2840:
em28xx_info("chip ID is em2840\n");
@@ -2158,6 +2227,10 @@
before probing the i2c bus. */
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
break;
+ case EM2820_BOARD_SILVERCREST_WEBCAM:
+ /* FIXME: need to document the registers bellow */
+ em28xx_write_reg(dev, 0x0d, 0x42);
+ em28xx_write_reg(dev, 0x13, 0x08);
}
if (dev->board.has_snapshot_button)
@@ -2189,6 +2262,10 @@
v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tvp5150", "tvp5150", tvp5150_addrs);
+ if (dev->board.decoder == EM28XX_MT9V011)
+ v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "mt9v011", "mt9v011", mt9v011_addrs);
+
if (dev->board.adecoder == EM28XX_TVAUDIO)
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tvaudio", "tvaudio", dev->board.tvaudio_addr);
@@ -2333,6 +2410,8 @@
return errCode;
}
+ em28xx_hint_sensor(dev);
+
/* Do board specific init and eeprom reading */
em28xx_card_setup(dev);
@@ -2573,6 +2652,7 @@
retval = em28xx_init_dev(&dev, udev, interface, nr);
if (retval) {
em28xx_devused &= ~(1<<dev->devno);
+ mutex_unlock(&dev->lock);
kfree(dev);
goto err;
}
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index c8d7ce8..079ab4d 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -648,17 +648,28 @@
int em28xx_set_outfmt(struct em28xx *dev)
{
int ret;
+ int vinmode, vinctl, outfmt;
+
+ outfmt = dev->format->reg;
+
+ if (dev->board.is_27xx) {
+ vinmode = 0x0d;
+ vinctl = 0x00;
+ } else {
+ vinmode = 0x10;
+ vinctl = 0x11;
+ }
ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT,
- dev->format->reg | 0x20, 0x3f);
+ outfmt | 0x20, 0xff);
+ if (ret < 0)
+ return ret;
+
+ ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, vinmode);
if (ret < 0)
return ret;
- ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, 0x10);
- if (ret < 0)
- return ret;
-
- return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x11);
+ return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctl);
}
static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
@@ -695,13 +706,19 @@
{
u8 mode;
/* the em2800 scaler only supports scaling down to 50% */
- if (dev->board.is_em2800)
+
+ if (dev->board.is_27xx) {
+ /* FIXME: Don't use the scaler yet */
+ mode = 0;
+ } else if (dev->board.is_em2800) {
mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00);
- else {
+ } else {
u8 buf[2];
+
buf[0] = h;
buf[1] = h >> 8;
em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2);
+
buf[0] = v;
buf[1] = v >> 8;
em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2);
@@ -720,8 +737,11 @@
height = norm_maxh(dev) >> 1;
em28xx_set_outfmt(dev);
+
+
em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
+
return em28xx_scaler_set(dev, dev->hscale, dev->vscale);
}
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index e7b47c8..3da97c3 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -243,6 +243,14 @@
.mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
};
+static struct zl10353_config em28xx_terratec_xs_zl10353_xc3028 = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .disable_i2c_gate_ctrl = 1,
+ .parallel_ts = 1,
+ .if2 = 45600,
+};
+
#ifdef EM28XX_DRX397XD_SUPPORT
/* [TODO] djh - not sure yet what the device config needs to contain */
static struct drx397xD_config em28xx_drx397xD_with_xc3028 = {
@@ -433,7 +441,6 @@
}
break;
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
- case EM2880_BOARD_TERRATEC_HYBRID_XS:
case EM2880_BOARD_KWORLD_DVB_310U:
case EM2880_BOARD_EMPIRE_DUAL_TV:
dvb->frontend = dvb_attach(zl10353_attach,
@@ -444,6 +451,25 @@
goto out_free;
}
break;
+ case EM2880_BOARD_TERRATEC_HYBRID_XS:
+ dvb->frontend = dvb_attach(zl10353_attach,
+ &em28xx_terratec_xs_zl10353_xc3028,
+ &dev->i2c_adap);
+ if (dvb->frontend == NULL) {
+ /* This board could have either a zl10353 or a mt352.
+ If the chip id isn't for zl10353, try mt352 */
+
+ /* FIXME: make support for mt352 work */
+ printk(KERN_ERR "version of this board with mt352 not "
+ "currently supported\n");
+ result = -EINVAL;
+ goto out_free;
+ }
+ if (attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
case EM2883_BOARD_KWORLD_HYBRID_330U:
case EM2882_BOARD_EVGA_INDTUBE:
dvb->frontend = dvb_attach(s5h1409_attach,
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index 2c86fcf..27e33a2 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -483,7 +483,7 @@
[0xa0 >> 1] = "eeprom",
[0xb0 >> 1] = "tda9874",
[0xb8 >> 1] = "tvp5150a",
- [0xba >> 1] = "tvp5150a",
+ [0xba >> 1] = "webcam sensor or tvp5150a",
[0xc0 >> 1] = "tuner (analog)",
[0xc2 >> 1] = "tuner (analog)",
[0xc4 >> 1] = "tuner (analog)",
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 8fe1bee..14316c9 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -90,10 +90,35 @@
/* supported video standards */
static struct em28xx_fmt format[] = {
{
- .name = "16bpp YUY2, 4:2:2, packed",
+ .name = "16 bpp YUY2, 4:2:2, packed",
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16,
.reg = EM28XX_OUTFMT_YUV422_Y0UY1V,
+ }, {
+ .name = "16 bpp RGB 565, LE",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .depth = 16,
+ .reg = EM28XX_OUTFMT_RGB_16_656,
+ }, {
+ .name = "8 bpp Bayer BGBG..GRGR",
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .depth = 8,
+ .reg = EM28XX_OUTFMT_RGB_8_BGBG,
+ }, {
+ .name = "8 bpp Bayer GRGR..BGBG",
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .depth = 8,
+ .reg = EM28XX_OUTFMT_RGB_8_GRGR,
+ }, {
+ .name = "8 bpp Bayer GBGB..RGRG",
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .depth = 8,
+ .reg = EM28XX_OUTFMT_RGB_8_GBGB,
+ }, {
+ .name = "12 bpp YUV411",
+ .fourcc = V4L2_PIX_FMT_YUV411P,
+ .depth = 12,
+ .reg = EM28XX_OUTFMT_YUV411,
},
};
@@ -701,7 +726,11 @@
return -EINVAL;
}
- if (dev->board.is_em2800) {
+ if (dev->board.is_27xx) {
+ /* FIXME: This is the only supported fmt */
+ width = 640;
+ height = 480;
+ } else if (dev->board.is_em2800) {
/* the em2800 can only scale down to 50% */
height = height > (3 * maxh / 4) ? maxh : maxh / 2;
width = width > (3 * maxw / 4) ? maxw : maxw / 2;
@@ -733,13 +762,40 @@
return 0;
}
+static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc,
+ unsigned width, unsigned height)
+{
+ struct em28xx_fmt *fmt;
+
+ /* FIXME: This is the only supported fmt */
+ if (dev->board.is_27xx) {
+ width = 640;
+ height = 480;
+ }
+
+ fmt = format_by_fourcc(fourcc);
+ if (!fmt)
+ return -EINVAL;
+
+ dev->format = fmt;
+ dev->width = width;
+ dev->height = height;
+
+ /* set new image size */
+ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
+
+ em28xx_set_alternate(dev);
+ em28xx_resolution_set(dev);
+
+ return 0;
+}
+
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int rc;
- struct em28xx_fmt *fmt;
rc = check_dev(dev);
if (rc < 0)
@@ -749,12 +805,6 @@
vidioc_try_fmt_vid_cap(file, priv, f);
- fmt = format_by_fourcc(f->fmt.pix.pixelformat);
- if (!fmt) {
- rc = -EINVAL;
- goto out;
- }
-
if (videobuf_queue_is_busy(&fh->vb_vidq)) {
em28xx_errdev("%s queue busy\n", __func__);
rc = -EBUSY;
@@ -767,16 +817,8 @@
goto out;
}
- /* set new image size */
- dev->width = f->fmt.pix.width;
- dev->height = f->fmt.pix.height;
- dev->format = fmt;
- get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
-
- em28xx_set_alternate(dev);
- em28xx_resolution_set(dev);
-
- rc = 0;
+ rc = em28xx_set_video_format(dev, f->fmt.pix.pixelformat,
+ f->fmt.pix.width, f->fmt.pix.height);
out:
mutex_unlock(&dev->lock);
@@ -1616,11 +1658,6 @@
filp->private_data = fh;
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
- dev->width = norm_maxw(dev);
- dev->height = norm_maxh(dev);
- dev->hscale = 0;
- dev->vscale = 0;
-
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
em28xx_set_alternate(dev);
em28xx_resolution_set(dev);
@@ -1962,15 +1999,14 @@
/* set default norm */
dev->norm = em28xx_video_template.current_norm;
- dev->width = norm_maxw(dev);
- dev->height = norm_maxh(dev);
dev->interlaced = EM28XX_INTERLACED_DEFAULT;
- dev->hscale = 0;
- dev->vscale = 0;
dev->ctl_input = 0;
/* Analog specific initialization */
dev->format = &format[0];
+ em28xx_set_video_format(dev, format[0].fourcc,
+ norm_maxw(dev), norm_maxh(dev));
+
video_mux(dev, dev->ctl_input);
/* Audio defaults */
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 813ce45..d90fef4 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -107,6 +107,7 @@
#define EM2860_BOARD_TERRATEC_AV350 68
#define EM2882_BOARD_KWORLD_ATSC_315U 69
#define EM2882_BOARD_EVGA_INDTUBE 70
+#define EM2820_BOARD_SILVERCREST_WEBCAM 71
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -360,6 +361,7 @@
EM28XX_NODECODER,
EM28XX_TVP5150,
EM28XX_SAA711X,
+ EM28XX_MT9V011,
};
enum em28xx_adecoder {
@@ -388,6 +390,7 @@
unsigned int max_range_640_480:1;
unsigned int has_dvb:1;
unsigned int has_snapshot_button:1;
+ unsigned int is_27xx:1;
unsigned int valid:1;
unsigned char xclk, i2c_speed;
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h
index 9df7137..992ce53 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.h
@@ -36,10 +36,6 @@
#define STV_ISOC_ENDPOINT_ADDR 0x81
-#ifndef V4L2_PIX_FMT_SGRBG8
-#define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G')
-#endif
-
#define STV_REG23 0x0423
/* Control registers of the STV0600 ASIC */
diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c
new file mode 100644
index 0000000..1fe8fc9
--- /dev/null
+++ b/drivers/media/video/mt9v011.c
@@ -0,0 +1,431 @@
+/*
+ * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor
+ *
+ * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com)
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+#include <linux/i2c.h>
+#include <linux/videodev2.h>
+#include <linux/delay.h>
+#include <media/v4l2-device.h>
+#include "mt9v011.h"
+#include <media/v4l2-i2c-drv.h>
+#include <media/v4l2-chip-ident.h>
+
+MODULE_DESCRIPTION("Micron mt9v011 sensor driver");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_LICENSE("GPL");
+
+
+static int debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+/* supported controls */
+static struct v4l2_queryctrl mt9v011_qctrl[] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 0,
+ .maximum = (1 << 10) - 1,
+ .step = 1,
+ .default_value = 0x0020,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+ .minimum = -1 << 9,
+ .maximum = (1 << 9) - 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ }, {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+ .minimum = -1 << 9,
+ .maximum = (1 << 9) - 1,
+ .step = 1,
+ .default_value = 0,
+ .flags = 0,
+ },
+};
+
+struct mt9v011 {
+ struct v4l2_subdev sd;
+ unsigned width, height;
+
+ u16 global_gain, red_bal, blue_bal;
+};
+
+static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mt9v011, sd);
+}
+
+static int mt9v011_read(struct v4l2_subdev *sd, unsigned char addr)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ __be16 buffer;
+ int rc, val;
+
+ rc = i2c_master_send(c, &addr, 1);
+ if (rc != 1)
+ v4l2_dbg(0, debug, sd,
+ "i2c i/o error: rc == %d (should be 1)\n", rc);
+
+ msleep(10);
+
+ rc = i2c_master_recv(c, (char *)&buffer, 2);
+ if (rc != 2)
+ v4l2_dbg(0, debug, sd,
+ "i2c i/o error: rc == %d (should be 2)\n", rc);
+
+ val = be16_to_cpu(buffer);
+
+ v4l2_dbg(2, debug, sd, "mt9v011: read 0x%02x = 0x%04x\n", addr, val);
+
+ return val;
+}
+
+static void mt9v011_write(struct v4l2_subdev *sd, unsigned char addr,
+ u16 value)
+{
+ struct i2c_client *c = v4l2_get_subdevdata(sd);
+ unsigned char buffer[3];
+ int rc;
+
+ buffer[0] = addr;
+ buffer[1] = value >> 8;
+ buffer[2] = value & 0xff;
+
+ v4l2_dbg(2, debug, sd,
+ "mt9v011: writing 0x%02x 0x%04x\n", buffer[0], value);
+ rc = i2c_master_send(c, buffer, 3);
+ if (rc != 3)
+ v4l2_dbg(0, debug, sd,
+ "i2c i/o error: rc == %d (should be 3)\n", rc);
+}
+
+
+struct i2c_reg_value {
+ unsigned char reg;
+ u16 value;
+};
+
+/*
+ * Values used at the original driver
+ * Some values are marked as Reserved at the datasheet
+ */
+static const struct i2c_reg_value mt9v011_init_default[] = {
+ { R0D_MT9V011_RESET, 0x0001 },
+ { R0D_MT9V011_RESET, 0x0000 },
+
+ { R0C_MT9V011_SHUTTER_DELAY, 0x0000 },
+ { R09_MT9V011_SHUTTER_WIDTH, 0x1fc },
+
+ { R0A_MT9V011_CLK_SPEED, 0x0000 },
+ { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 },
+ { R20_MT9V011_READ_MODE, 0x1000 },
+
+ { R07_MT9V011_OUT_CTRL, 0x000a }, /* chip enable */
+};
+
+static void set_balance(struct v4l2_subdev *sd)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ u16 green1_gain, green2_gain, blue_gain, red_gain;
+
+ green1_gain = core->global_gain;
+ green2_gain = core->global_gain;
+
+ blue_gain = core->global_gain +
+ core->global_gain * core->blue_bal / (1 << 9);
+
+ red_gain = core->global_gain +
+ core->global_gain * core->blue_bal / (1 << 9);
+
+ mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green1_gain);
+ mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green1_gain);
+ mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain);
+ mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain);
+}
+
+static void set_res(struct v4l2_subdev *sd)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ unsigned vstart, hstart;
+
+ /*
+ * The mt9v011 doesn't have scaling. So, in order to select the desired
+ * resolution, we're cropping at the middle of the sensor.
+ * hblank and vblank should be adjusted, in order to warrant that
+ * we'll preserve the line timings for 30 fps, no matter what resolution
+ * is selected.
+ * NOTE: datasheet says that width (and height) should be filled with
+ * width-1. However, this doesn't work, since one pixel per line will
+ * be missing.
+ */
+
+ hstart = 14 + (640 - core->width) / 2;
+ mt9v011_write(sd, R02_MT9V011_COLSTART, hstart);
+ mt9v011_write(sd, R04_MT9V011_WIDTH, core->width);
+ mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width);
+
+ vstart = 8 + (640 - core->height) / 2;
+ mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart);
+ mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height);
+ mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height);
+};
+
+static int mt9v011_reset(struct v4l2_subdev *sd, u32 val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt9v011_init_default); i++)
+ mt9v011_write(sd, mt9v011_init_default[i].reg,
+ mt9v011_init_default[i].value);
+
+ set_balance(sd);
+ set_res(sd);
+
+ return 0;
+};
+
+static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+
+ v4l2_dbg(1, debug, sd, "g_ctrl called\n");
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ ctrl->value = core->global_gain;
+ return 0;
+ case V4L2_CID_RED_BALANCE:
+ ctrl->value = core->red_bal;
+ return 0;
+ case V4L2_CID_BLUE_BALANCE:
+ ctrl->value = core->blue_bal;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct mt9v011 *core = to_mt9v011(sd);
+ u8 i, n;
+ n = ARRAY_SIZE(mt9v011_qctrl);
+
+ for (i = 0; i < n; i++) {
+ if (ctrl->id != mt9v011_qctrl[i].id)
+ continue;
+ if (ctrl->value < mt9v011_qctrl[i].minimum ||
+ ctrl->value > mt9v011_qctrl[i].maximum)
+ return -ERANGE;
+ v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n",
+ ctrl->id, ctrl->value);
+ break;
+ }
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ core->global_gain = ctrl->value;
+ break;
+ case V4L2_CID_RED_BALANCE:
+ core->red_bal = ctrl->value;
+ break;
+ case V4L2_CID_BLUE_BALANCE:
+ core->blue_bal = ctrl->value;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ set_balance(sd);
+
+ return 0;
+}
+
+static int mt9v011_enum_fmt(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt)
+{
+ if (fmt->index > 0)
+ return -EINVAL;
+
+ fmt->flags = 0;
+ strcpy(fmt->description, "8 bpp Bayer GRGR..BGBG");
+ fmt->pixelformat = V4L2_PIX_FMT_SGRBG8;
+
+ return 0;
+}
+
+static int mt9v011_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+
+ if (pix->pixelformat != V4L2_PIX_FMT_SGRBG8)
+ return -EINVAL;
+
+ v4l_bound_align_image(&pix->width, 48, 639, 1,
+ &pix->height, 32, 480, 1, 0);
+
+ return 0;
+}
+
+static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+{
+ struct v4l2_pix_format *pix = &fmt->fmt.pix;
+ struct mt9v011 *core = to_mt9v011(sd);
+ int rc;
+
+ rc = mt9v011_try_fmt(sd, fmt);
+ if (rc < 0)
+ return -EINVAL;
+
+ core->width = pix->width;
+ core->height = pix->height;
+
+ set_res(sd);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9v011_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, ®->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ reg->val = mt9v011_read(sd, reg->reg & 0xff);
+ reg->size = 2;
+
+ return 0;
+}
+
+static int mt9v011_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, ®->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff);
+
+ return 0;
+}
+#endif
+
+static int mt9v011_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011,
+ MT9V011_VERSION);
+}
+
+static const struct v4l2_subdev_core_ops mt9v011_core_ops = {
+ .g_ctrl = mt9v011_g_ctrl,
+ .s_ctrl = mt9v011_s_ctrl,
+ .reset = mt9v011_reset,
+ .g_chip_ident = mt9v011_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9v011_g_register,
+ .s_register = mt9v011_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops mt9v011_video_ops = {
+ .enum_fmt = mt9v011_enum_fmt,
+ .try_fmt = mt9v011_try_fmt,
+ .s_fmt = mt9v011_s_fmt,
+};
+
+static const struct v4l2_subdev_ops mt9v011_ops = {
+ .core = &mt9v011_core_ops,
+ .video = &mt9v011_video_ops,
+};
+
+
+/****************************************************************************
+ I2C Client & Driver
+ ****************************************************************************/
+
+static int mt9v011_probe(struct i2c_client *c,
+ const struct i2c_device_id *id)
+{
+ u16 version;
+ struct mt9v011 *core;
+ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(c->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -EIO;
+
+ core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL);
+ if (!core)
+ return -ENOMEM;
+
+ sd = &core->sd;
+ v4l2_i2c_subdev_init(sd, c, &mt9v011_ops);
+
+ /* Check if the sensor is really a MT9V011 */
+ version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);
+ if (version != MT9V011_VERSION) {
+ v4l2_info(sd, "*** unknown micron chip detected (0x%04x.\n",
+ version);
+ kfree(core);
+ return -EINVAL;
+ }
+
+ core->global_gain = 0x0024;
+ core->width = 640;
+ core->height = 480;
+
+ v4l_info(c, "chip found @ 0x%02x (%s)\n",
+ c->addr << 1, c->adapter->name);
+
+ return 0;
+}
+
+static int mt9v011_remove(struct i2c_client *c)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(c);
+
+ v4l2_dbg(1, debug, sd,
+ "mt9v011.c: removing mt9v011 adapter on address 0x%x\n",
+ c->addr << 1);
+
+ v4l2_device_unregister_subdev(sd);
+ kfree(to_mt9v011(sd));
+ return 0;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static const struct i2c_device_id mt9v011_id[] = {
+ { "mt9v011", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mt9v011_id);
+
+static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "mt9v011",
+ .probe = mt9v011_probe,
+ .remove = mt9v011_remove,
+ .id_table = mt9v011_id,
+};
diff --git a/drivers/media/video/mt9v011.h b/drivers/media/video/mt9v011.h
new file mode 100644
index 0000000..9e443ee
--- /dev/null
+++ b/drivers/media/video/mt9v011.h
@@ -0,0 +1,35 @@
+/*
+ * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor
+ *
+ * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com)
+ * This code is placed under the terms of the GNU General Public License v2
+ */
+
+#ifndef MT9V011_H_
+#define MT9V011_H_
+
+#define R00_MT9V011_CHIP_VERSION 0x00
+#define R01_MT9V011_ROWSTART 0x01
+#define R02_MT9V011_COLSTART 0x02
+#define R03_MT9V011_HEIGHT 0x03
+#define R04_MT9V011_WIDTH 0x04
+#define R05_MT9V011_HBLANK 0x05
+#define R06_MT9V011_VBLANK 0x06
+#define R07_MT9V011_OUT_CTRL 0x07
+#define R09_MT9V011_SHUTTER_WIDTH 0x09
+#define R0A_MT9V011_CLK_SPEED 0x0a
+#define R0B_MT9V011_RESTART 0x0b
+#define R0C_MT9V011_SHUTTER_DELAY 0x0c
+#define R0D_MT9V011_RESET 0x0d
+#define R1E_MT9V011_DIGITAL_ZOOM 0x1e
+#define R20_MT9V011_READ_MODE 0x20
+#define R2B_MT9V011_GREEN_1_GAIN 0x2b
+#define R2C_MT9V011_BLUE_GAIN 0x2c
+#define R2D_MT9V011_RED_GAIN 0x2d
+#define R2E_MT9V011_GREEN_2_GAIN 0x2e
+#define R35_MT9V011_GLOBAL_GAIN 0x35
+#define RF1_MT9V011_CHIP_ENABLE 0xf1
+
+#define MT9V011_VERSION 0x8243
+
+#endif
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index 16f595d..9f5ae81 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -237,11 +237,11 @@
return -ENOMEM;
icd->num_user_formats = fmts;
- fmts = 0;
dev_dbg(&icd->dev, "Found %d supported formats.\n", fmts);
/* Second pass - actually fill data formats */
+ fmts = 0;
for (i = 0; i < icd->num_formats; i++)
if (!ici->ops->get_formats) {
icd->user_formats[i].host_fmt = icd->formats + i;
@@ -877,8 +877,11 @@
(unsigned short)~0;
ret = soc_camera_init_user_formats(icd);
- if (ret < 0)
+ if (ret < 0) {
+ if (icd->ops->remove)
+ icd->ops->remove(icd);
goto eiufmt;
+ }
icd->height = DEFAULT_HEIGHT;
icd->width = DEFAULT_WIDTH;
@@ -902,8 +905,10 @@
{
struct soc_camera_device *icd = to_soc_camera_dev(dev);
+ mutex_lock(&icd->video_lock);
if (icd->ops->remove)
icd->ops->remove(icd);
+ mutex_unlock(&icd->video_lock);
soc_camera_free_user_formats(icd);
@@ -1145,6 +1150,7 @@
}
EXPORT_SYMBOL(soc_camera_video_start);
+/* Called from client .remove() methods with .video_lock held */
void soc_camera_video_stop(struct soc_camera_device *icd)
{
struct video_device *vdev = icd->vdev;
@@ -1154,10 +1160,8 @@
if (!icd->dev.parent || !vdev)
return;
- mutex_lock(&icd->video_lock);
video_unregister_device(vdev);
icd->vdev = NULL;
- mutex_unlock(&icd->video_lock);
}
EXPORT_SYMBOL(soc_camera_video_stop);
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index cd72668..7705fc6 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -343,6 +343,53 @@
#define TO_U(r, g, b) \
(((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)
+/* precalculate color bar values to speed up rendering */
+static void precalculate_bars(struct vivi_fh *fh)
+{
+ struct vivi_dev *dev = fh->dev;
+ unsigned char r, g, b;
+ int k, is_yuv;
+
+ fh->input = dev->input;
+
+ for (k = 0; k < 8; k++) {
+ r = bars[fh->input].bar[k][0];
+ g = bars[fh->input].bar[k][1];
+ b = bars[fh->input].bar[k][2];
+ is_yuv = 0;
+
+ switch (fh->fmt->fourcc) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ is_yuv = 1;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ r >>= 3;
+ g >>= 2;
+ b >>= 3;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_RGB555X:
+ r >>= 3;
+ g >>= 3;
+ b >>= 3;
+ break;
+ }
+
+ if (is_yuv) {
+ fh->bars[k][0] = TO_Y(r, g, b); /* Luma */
+ fh->bars[k][1] = TO_U(r, g, b); /* Cb */
+ fh->bars[k][2] = TO_V(r, g, b); /* Cr */
+ } else {
+ fh->bars[k][0] = r;
+ fh->bars[k][1] = g;
+ fh->bars[k][2] = b;
+ }
+ }
+
+}
+
#define TSTAMP_MIN_Y 24
#define TSTAMP_MAX_Y (TSTAMP_MIN_Y + 15)
#define TSTAMP_INPUT_X 10
@@ -755,6 +802,8 @@
buf->vb.height = fh->height;
buf->vb.field = field;
+ precalculate_bars(fh);
+
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
rc = videobuf_iolock(vq, &buf->vb, NULL);
if (rc < 0)
@@ -893,53 +942,6 @@
return 0;
}
-/* precalculate color bar values to speed up rendering */
-static void precalculate_bars(struct vivi_fh *fh)
-{
- struct vivi_dev *dev = fh->dev;
- unsigned char r, g, b;
- int k, is_yuv;
-
- fh->input = dev->input;
-
- for (k = 0; k < 8; k++) {
- r = bars[fh->input].bar[k][0];
- g = bars[fh->input].bar[k][1];
- b = bars[fh->input].bar[k][2];
- is_yuv = 0;
-
- switch (fh->fmt->fourcc) {
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_UYVY:
- is_yuv = 1;
- break;
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_RGB565X:
- r >>= 3;
- g >>= 2;
- b >>= 3;
- break;
- case V4L2_PIX_FMT_RGB555:
- case V4L2_PIX_FMT_RGB555X:
- r >>= 3;
- g >>= 3;
- b >>= 3;
- break;
- }
-
- if (is_yuv) {
- fh->bars[k][0] = TO_Y(r, g, b); /* Luma */
- fh->bars[k][1] = TO_U(r, g, b); /* Cb */
- fh->bars[k][2] = TO_V(r, g, b); /* Cr */
- } else {
- fh->bars[k][0] = r;
- fh->bars[k][1] = g;
- fh->bars[k][2] = b;
- }
- }
-
-}
-
/*FIXME: This seems to be generic enough to be at videodev2 */
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
@@ -965,8 +967,6 @@
fh->vb_vidq.field = f->fmt.pix.field;
fh->type = f->type;
- precalculate_bars(fh);
-
ret = 0;
out:
mutex_unlock(&q->vb_lock);
@@ -1357,6 +1357,7 @@
goto unreg_dev;
*vfd = vivi_template;
+ vfd->debug = debug;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
if (ret < 0)
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 8a025d5..95846d9 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -318,6 +318,8 @@
/* see http://www.siliconimaging.com/RGB%20Bayer.htm */
#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */
#define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */
+#define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G') /* 8 GRGR.. BGBG.. */
+
/*
* 10bit raw bayer, expanded to 16 bits
* xxxxrrrrrrrrrrxxxxgggggggggg xxxxggggggggggxxxxbbbbbbbbbb...
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 4d7e227..11a4a2d 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -155,6 +155,9 @@
/* module cafe_ccic, just ident 8801 */
V4L2_IDENT_CAFE = 8801,
+ /* module mt9v011, just ident 8243 */
+ V4L2_IDENT_MT9V011 = 8243,
+
/* module tw9910: just ident 9910 */
V4L2_IDENT_TW9910 = 9910,