forked from Minki/linux
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 ...
This commit is contained in:
commit
8871b201da
@ -25,7 +25,7 @@ use IO::Handle;
|
||||
"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 @@ sub cx18 {
|
||||
$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/";
|
||||
|
||||
|
@ -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)
|
||||
|
@ -1096,8 +1096,19 @@ static int xc2028_set_params(struct dvb_frontend *fe,
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
@ -1,5 +1,6 @@
|
||||
config TTPCI_EEPROM
|
||||
tristate
|
||||
depends on I2C
|
||||
default n
|
||||
|
||||
config DVB_AV7110
|
||||
|
@ -1200,7 +1200,7 @@ static int si470x_fops_release(struct file *file)
|
||||
video_unregister_device(radio->videodev);
|
||||
kfree(radio->buffer);
|
||||
kfree(radio);
|
||||
goto done;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* stop rds reception */
|
||||
@ -1213,9 +1213,8 @@ static int si470x_fops_release(struct file *file)
|
||||
retval = si470x_stop(radio);
|
||||
usb_autopm_put_interface(radio->intf);
|
||||
}
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&radio->disconnect_lock);
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
@ -312,6 +312,14 @@ config VIDEO_OV7670
|
||||
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
|
||||
|
@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
|
||||
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
|
||||
|
@ -198,11 +198,14 @@ static const struct cx18_card_pci_info cx18_pci_mpc718[] = {
|
||||
|
||||
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 @@ static const struct cx18_card cx18_card_mpc718 = {
|
||||
{ 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,
|
||||
|
@ -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 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
||||
#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 @@ static struct s5h1409_config hauppauge_hvr1600_config = {
|
||||
.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 @@ static struct zl10353_config leadtek_dvr3100h_demod = {
|
||||
.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 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
|
||||
break;
|
||||
|
||||
case CX18_CARD_LEADTEK_DVR3100H:
|
||||
case CX18_CARD_YUAN_MPC718:
|
||||
default:
|
||||
/* Assumption - Parallel transport - Signalling
|
||||
* undefined or default.
|
||||
@ -326,6 +454,38 @@ static int dvb_register(struct cx18_stream *stream)
|
||||
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;
|
||||
|
@ -463,6 +463,30 @@ static struct xc5000_config mygica_x8506_xc5000_config = {
|
||||
.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 @@ static int dvb_register(struct cx23885_tsport *port)
|
||||
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];
|
||||
|
@ -288,6 +288,10 @@ struct cx23885_tsport {
|
||||
/* 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 {
|
||||
|
@ -8,6 +8,8 @@ config VIDEO_EM28XX
|
||||
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.
|
||||
|
||||
|
@ -58,6 +58,8 @@ static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET };
|
||||
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 @@ static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = {
|
||||
{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 @@ struct em28xx_board em28xx_boards[] = {
|
||||
.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 @@ struct em28xx_board em28xx_boards[] = {
|
||||
.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 @@ static unsigned short tvp5150_addrs[] = {
|
||||
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 @@ static inline void em28xx_set_model(struct em28xx *dev)
|
||||
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 @@ void em28xx_pre_card_setup(struct em28xx *dev)
|
||||
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 @@ void em28xx_card_setup(struct em28xx *dev)
|
||||
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 @@ void em28xx_card_setup(struct em28xx *dev)
|
||||
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 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
|
||||
return errCode;
|
||||
}
|
||||
|
||||
em28xx_hint_sensor(dev);
|
||||
|
||||
/* Do board specific init and eeprom reading */
|
||||
em28xx_card_setup(dev);
|
||||
|
||||
@ -2573,6 +2652,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
|
||||
retval = em28xx_init_dev(&dev, udev, interface, nr);
|
||||
if (retval) {
|
||||
em28xx_devused &= ~(1<<dev->devno);
|
||||
mutex_unlock(&dev->lock);
|
||||
kfree(dev);
|
||||
goto err;
|
||||
}
|
||||
|
@ -648,17 +648,28 @@ int em28xx_capture_start(struct em28xx *dev, int start)
|
||||
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 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
|
||||
{
|
||||
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 @@ int em28xx_resolution_set(struct em28xx *dev)
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -243,6 +243,14 @@ static struct s5h1409_config em28xx_s5h1409_with_xc3028 = {
|
||||
.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 @@ static int dvb_init(struct em28xx *dev)
|
||||
}
|
||||
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 @@ static int dvb_init(struct em28xx *dev)
|
||||
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,
|
||||
|
@ -483,7 +483,7 @@ static char *i2c_devs[128] = {
|
||||
[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)",
|
||||
|
@ -90,10 +90,35 @@ MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
|
||||
/* 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 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
|
||||
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 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
|
||||
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 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
|
||||
|
||||
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 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
|
||||
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 @@ static int em28xx_v4l2_open(struct file *filp)
|
||||
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 @@ int em28xx_register_analog_devices(struct em28xx *dev)
|
||||
|
||||
/* 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 */
|
||||
|
@ -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 @@ enum em28xx_decoder {
|
||||
EM28XX_NODECODER,
|
||||
EM28XX_TVP5150,
|
||||
EM28XX_SAA711X,
|
||||
EM28XX_MT9V011,
|
||||
};
|
||||
|
||||
enum em28xx_adecoder {
|
||||
@ -388,6 +390,7 @@ struct em28xx_board {
|
||||
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;
|
||||
|
@ -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 */
|
||||
|
431
drivers/media/video/mt9v011.c
Normal file
431
drivers/media/video/mt9v011.c
Normal file
@ -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,
|
||||
};
|
35
drivers/media/video/mt9v011.h
Normal file
35
drivers/media/video/mt9v011.h
Normal file
@ -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
|
@ -237,11 +237,11 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd)
|
||||
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 @@ static int soc_camera_probe(struct device *dev)
|
||||
(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 @@ static int soc_camera_remove(struct device *dev)
|
||||
{
|
||||
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 @@ evidallocd:
|
||||
}
|
||||
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 @@ void soc_camera_video_stop(struct soc_camera_device *icd)
|
||||
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);
|
||||
|
||||
|
@ -343,6 +343,53 @@ static struct bar_std bars[] = {
|
||||
#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 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
|
||||
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 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
|
||||
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 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
|
||||
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 @@ static int __init vivi_create_instance(int inst)
|
||||
goto unreg_dev;
|
||||
|
||||
*vfd = vivi_template;
|
||||
vfd->debug = debug;
|
||||
|
||||
ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
|
||||
if (ret < 0)
|
||||
|
@ -318,6 +318,8 @@ struct v4l2_pix_format {
|
||||
/* 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...
|
||||
|
@ -155,6 +155,9 @@ enum {
|
||||
/* 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,
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user