提交 8871b201 编写于 作者: L Linus Torvalds

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
  ...
......@@ -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);
......
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;
return ret;
ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, 0x10);
ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, vinmode);
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 */
......
/*
* 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, &reg->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, &reg->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,
};
/*
* 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 @@ int soc_camera_video_start(struct soc_camera_device *icd)
}
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,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册