summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/em28xx
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media/video/em28xx')
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-cards.c136
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-core.c22
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-dvb.c62
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-i2c.c2
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-video.c157
-rw-r--r--linux/drivers/media/video/em28xx/em28xx.h21
6 files changed, 277 insertions, 123 deletions
diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c
index 5f063e4a1..795fd16a5 100644
--- a/linux/drivers/media/video/em28xx/em28xx-cards.c
+++ b/linux/drivers/media/video/em28xx/em28xx-cards.c
@@ -59,8 +59,6 @@ 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;
@@ -172,6 +170,20 @@ static struct em28xx_reg_seq evga_indtube_digital[] = {
{ -1, -1, -1, -1},
};
+/* Pinnacle Hybrid Pro eb1a:2881 */
+static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
+ {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10},
+ { -1, -1, -1, -1},
+};
+
+static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
+ {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10},
+ {EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */
+ {EM2880_R04_GPO, 0x0c, 0xff, 1},
+ { -1, -1, -1, -1},
+};
+
+
/* Callback for the most boards */
static struct em28xx_reg_seq default_tuner_gpio[] = {
{EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
@@ -226,6 +238,7 @@ struct em28xx_board em28xx_boards[] = {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = 0,
.amux = EM28XX_AMUX_VIDEO,
+ .gpio = silvercrest_reg_seq,
} },
},
[EM2800_BOARD_UNKNOWN] = {
@@ -247,6 +260,7 @@ struct em28xx_board em28xx_boards[] = {
[EM2820_BOARD_UNKNOWN] = {
.name = "Unknown EM2750/28xx video grabber",
.tuner_type = TUNER_ABSENT,
+ .is_webcam = 1, /* To enable sensor probe */
},
[EM2750_BOARD_DLCW_130] = {
/* Beijing Huaqi Information Digital Technology Co., Ltd */
@@ -625,22 +639,27 @@ struct em28xx_board em28xx_boards[] = {
},
[EM2861_BOARD_PLEXTOR_PX_TV100U] = {
.name = "Plextor ConvertX PX-TV100U",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_TNF_5335MF,
+ .xclk = EM28XX_XCLK_I2S_MSB_TIMING |
+ EM28XX_XCLK_FREQUENCY_12MHZ,
.tda9887_conf = TDA9887_PRESENT,
.decoder = EM28XX_TVP5150,
+ .has_msp34xx = 1,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
.amux = EM28XX_AMUX_LINE_IN,
+ .gpio = pinnacle_hybrid_pro_analog,
} },
},
@@ -1306,29 +1325,26 @@ struct em28xx_board em28xx_boards[] = {
},
[EM2881_BOARD_PINNACLE_HYBRID_PRO] = {
.name = "Pinnacle Hybrid Pro",
- .valid = EM28XX_BOARD_NOT_VALIDATED,
.tuner_type = TUNER_XC2028,
.tuner_gpio = default_tuner_gpio,
.decoder = EM28XX_TVP5150,
-#if 0 /* FIXME: add an entry at em28xx-dvb */
.has_dvb = 1,
- .dvb_gpio = default_digital,
-#endif
+ .dvb_gpio = pinnacle_hybrid_pro_digital,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
.amux = EM28XX_AMUX_VIDEO,
- .gpio = default_analog,
+ .gpio = pinnacle_hybrid_pro_analog,
}, {
.type = EM28XX_VMUX_COMPOSITE1,
.vmux = TVP5150_COMPOSITE1,
.amux = EM28XX_AMUX_LINE_IN,
- .gpio = default_analog,
+ .gpio = pinnacle_hybrid_pro_analog,
}, {
.type = EM28XX_VMUX_SVIDEO,
.vmux = TVP5150_SVIDEO,
.amux = EM28XX_AMUX_LINE_IN,
- .gpio = default_analog,
+ .gpio = pinnacle_hybrid_pro_analog,
} },
},
[EM2882_BOARD_PINNACLE_HYBRID_PRO] = {
@@ -1712,6 +1728,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = {
{0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
{0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
{0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028},
+ {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
};
/* I2C devicelist hash table for devices with generic USB IDs */
@@ -1778,6 +1795,32 @@ static inline void em28xx_set_model(struct em28xx *dev)
EM28XX_I2C_FREQ_100_KHZ;
}
+/* FIXME: Should be replaced by a proper mt9m001 driver */
+static int em28xx_initialize_mt9m001(struct em28xx *dev)
+{
+ int i;
+ unsigned char regs[][3] = {
+ { 0x0d, 0x00, 0x01, },
+ { 0x0d, 0x00, 0x00, },
+ { 0x04, 0x05, 0x00, }, /* hres = 1280 */
+ { 0x03, 0x04, 0x00, }, /* vres = 1024 */
+ { 0x20, 0x11, 0x00, },
+ { 0x06, 0x00, 0x10, },
+ { 0x2b, 0x00, 0x24, },
+ { 0x2e, 0x00, 0x24, },
+ { 0x35, 0x00, 0x24, },
+ { 0x2d, 0x00, 0x20, },
+ { 0x2c, 0x00, 0x20, },
+ { 0x09, 0x0a, 0xd4, },
+ { 0x35, 0x00, 0x57, },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
+
+ return 0;
+}
+
/* HINT method: webcam I2C chips
*
* This method work for webcams with Micron sensors
@@ -1790,10 +1833,6 @@ static int em28xx_hint_sensor(struct em28xx *dev)
__be16 version_be;
u16 version;
- if (dev->model != EM2820_BOARD_UNKNOWN &&
- dev->model != EM2750_BOARD_UNKNOWN)
- return 0;
-
dev->i2c_client.addr = 0xba >> 1;
cmd = 0;
i2c_master_send(&dev->i2c_client, &cmd, 1);
@@ -1804,10 +1843,36 @@ static int em28xx_hint_sensor(struct em28xx *dev)
version = be16_to_cpu(version_be);
switch (version) {
- case MT9V011_VERSION:
+ case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */
+ case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */
dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
sensor_name = "mt9v011";
dev->em28xx_sensor = EM28XX_MT9V011;
+ dev->sensor_xres = 640;
+ dev->sensor_yres = 480;
+ dev->sensor_xtal = 6300000;
+
+ /* probably means GRGB 16 bit bayer */
+ dev->vinmode = 0x0d;
+ dev->vinctl = 0x00;
+
+ break;
+#if 0
+ case 0x8411:
+ case 0x8421:
+#endif
+ case 0x8431:
+ dev->model = EM2750_BOARD_UNKNOWN;
+ sensor_name = "mt9m001";
+ dev->em28xx_sensor = EM28XX_MT9M001;
+ em28xx_initialize_mt9m001(dev);
+ dev->sensor_xres = 1280;
+ dev->sensor_yres = 1024;
+
+ /* probably means BGGR 16 bit bayer */
+ dev->vinmode = 0x0c;
+ dev->vinctl = 0x00;
+
break;
default:
printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version));
@@ -1848,10 +1913,7 @@ void em28xx_pre_card_setup(struct em28xx *dev)
em28xx_info("chip ID is em2750\n");
break;
case CHIP_ID_EM2820:
- if (dev->board.is_webcam)
- em28xx_info("chip is em2710\n");
- else
- em28xx_info("chip ID is em2820\n");
+ em28xx_info("chip ID is em2710 or em2820\n");
break;
case CHIP_ID_EM2840:
em28xx_info("chip ID is em2840\n");
@@ -1891,9 +1953,8 @@ void em28xx_pre_card_setup(struct em28xx *dev)
/* request some modules */
switch (dev->model) {
case EM2861_BOARD_PLEXTOR_PX_TV100U:
- /* FIXME guess */
- /* Turn on analog audio output */
- em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ /* Sets the msp34xx I2S speed */
+ dev->i2s_speed = 2048000;
break;
case EM2861_BOARD_KWORLD_PVRTV_300U:
case EM2880_BOARD_KWORLD_DVB_305U:
@@ -2005,6 +2066,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
ctl->demod = XC3028_FE_ZARLINK456;
break;
case EM2880_BOARD_TERRATEC_HYBRID_XS:
+ case EM2881_BOARD_PINNACLE_HYBRID_PRO:
ctl->demod = XC3028_FE_ZARLINK456;
break;
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
@@ -2335,6 +2397,7 @@ void em28xx_card_setup(struct em28xx *dev)
em28xx_set_mode() in em28xx_pre_card_setup() was a no-op,
so make the call now so the analog GPIOs are set properly
before probing the i2c bus. */
+ em28xx_gpio_set(dev, dev->board.tuner_gpio);
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
break;
case EM2820_BOARD_SILVERCREST_WEBCAM:
@@ -2372,9 +2435,20 @@ void em28xx_card_setup(struct em28xx *dev)
v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tvp5150", "tvp5150", tvp5150_addrs);
- if (dev->em28xx_sensor == EM28XX_MT9V011)
+ if (dev->em28xx_sensor == EM28XX_MT9V011) {
+ struct v4l2_subdev *sd;
+
+ sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev,
+ &dev->i2c_adap, "mt9v011", "mt9v011", mt9v011_addrs);
+ v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal);
+ }
+
+#if 0
+ /* FIXME: use mt9m001 after their conversion to v4l dev/subdev */
+ if (dev->em28xx_sensor == EM28XX_MT9M001)
v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "mt9v011", "mt9v011", mt9v011_addrs);
+ "mt9m001", "mt9m001", mt9v011_addrs);
+#endif
if (dev->board.adecoder == EM28XX_TVAUDIO)
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
@@ -2531,7 +2605,19 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
return errCode;
}
- em28xx_hint_sensor(dev);
+ /*
+ * Default format, used for tvp5150 or saa711x output formats
+ */
+ dev->vinmode = 0x10;
+ dev->vinctl = 0x11;
+
+ /*
+ * If the device can be a webcam, seek for a sensor.
+ * If sensor is not found, then it isn't a webcam.
+ */
+ if (dev->board.is_webcam)
+ if (em28xx_hint_sensor(dev) < 0)
+ dev->board.is_webcam = 0;
/* Do board specific init and eeprom reading */
em28xx_card_setup(dev);
diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c
index 420e343f2..f24af40df 100644
--- a/linux/drivers/media/video/em28xx/em28xx-core.c
+++ b/linux/drivers/media/video/em28xx/em28xx-core.c
@@ -648,28 +648,17 @@ 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_webcam) {
- vinmode = 0x0d;
- vinctl = 0x00;
- } else {
- vinmode = 0x10;
- vinctl = 0x11;
- }
ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT,
- outfmt | 0x20, 0xff);
+ dev->format->reg | 0x20, 0xff);
if (ret < 0)
return ret;
- ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, vinmode);
+ ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, dev->vinmode);
if (ret < 0)
return ret;
- return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctl);
+ return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, dev->vinctl);
}
static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
@@ -707,10 +696,7 @@ 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_webcam) {
- /* FIXME: Don't use the scaler yet */
- mode = 0;
- } else if (dev->board.is_em2800) {
+ if (dev->board.is_em2800) {
mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00);
} else {
u8 buf[2];
diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c
index 86be58641..e32267383 100644
--- a/linux/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c
@@ -32,6 +32,8 @@
#include "lgdt330x.h"
#include "zl10353.h"
#include "s5h1409.h"
+#include "mt352.h"
+#include "mt352_priv.h" /* FIXME */
MODULE_DESCRIPTION("driver for em28xx based DVB cards");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
@@ -244,7 +246,7 @@ static struct s5h1409_config em28xx_s5h1409_with_xc3028 = {
.mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
};
-static struct zl10353_config em28xx_terratec_xs_zl10353_xc3028 = {
+static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = {
.demod_address = (0x1e >> 1),
.no_tuner = 1,
.disable_i2c_gate_ctrl = 1,
@@ -259,6 +261,41 @@ static struct drx397xD_config em28xx_drx397xD_with_xc3028 = {
};
#endif
+static int mt352_terratec_xs_init(struct dvb_frontend *fe)
+{
+ /* Values extracted from a USB trace of the Terratec Windows driver */
+ static u8 clock_config[] = { CLOCK_CTL, 0x38, 0x2c };
+ static u8 reset[] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg[] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg[] = { AGC_TARGET, 0x28, 0xa0 };
+ static u8 input_freq_cfg[] = { INPUT_FREQ_1, 0x31, 0xb8 };
+ static u8 rs_err_cfg[] = { RS_ERR_PER_1, 0x00, 0x4d };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+ static u8 trl_nom_cfg[] = { TRL_NOMINAL_RATE_1, 0x64, 0x00 };
+ static u8 tps_given_cfg[] = { TPS_GIVEN_1, 0x40, 0x80, 0x50 };
+ static u8 tuner_go[] = { TUNER_GO, 0x01};
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, input_freq_cfg, sizeof(input_freq_cfg));
+ mt352_write(fe, rs_err_cfg, sizeof(rs_err_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+ mt352_write(fe, trl_nom_cfg, sizeof(trl_nom_cfg));
+ mt352_write(fe, tps_given_cfg, sizeof(tps_given_cfg));
+ mt352_write(fe, tuner_go, sizeof(tuner_go));
+ return 0;
+}
+
+static struct mt352_config terratec_xs_mt352_cfg = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .if2 = 45600,
+ .demod_init = mt352_terratec_xs_init,
+};
+
/* ------------------------------------------------------------------ */
static int attach_xc3028(u8 addr, struct em28xx *dev)
@@ -441,7 +478,6 @@ static int dvb_init(struct em28xx *dev)
goto out_free;
}
break;
- case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
case EM2880_BOARD_KWORLD_DVB_310U:
case EM2880_BOARD_EMPIRE_DUAL_TV:
dvb->frontend = dvb_attach(zl10353_attach,
@@ -452,20 +488,28 @@ static int dvb_init(struct em28xx *dev)
goto out_free;
}
break;
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ dvb->frontend = dvb_attach(zl10353_attach,
+ &em28xx_zl10353_xc3028_no_i2c_gate,
+ &dev->i2c_adap);
+ if (attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
case EM2880_BOARD_TERRATEC_HYBRID_XS:
+ case EM2881_BOARD_PINNACLE_HYBRID_PRO:
dvb->frontend = dvb_attach(zl10353_attach,
- &em28xx_terratec_xs_zl10353_xc3028,
+ &em28xx_zl10353_xc3028_no_i2c_gate,
&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;
+ dvb->frontend = dvb_attach(mt352_attach,
+ &terratec_xs_mt352_cfg,
+ &dev->i2c_adap);
}
+
if (attach_xc3028(0x61, dev) < 0) {
result = -EINVAL;
goto out_free;
diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c
index 91cff2af3..5ed24c23a 100644
--- a/linux/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c
@@ -465,7 +465,9 @@ static struct i2c_adapter em28xx_adap_template = {
.class = I2C_CLASS_TV_ANALOG,
#endif
.name = "em28xx",
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
.id = I2C_HW_B_EM28XX,
+#endif
.algo = &em28xx_algo,
};
diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c
index e447c9f0a..fcb99fb71 100644
--- a/linux/drivers/media/video/em28xx/em28xx-video.c
+++ b/linux/drivers/media/video/em28xx/em28xx-video.c
@@ -142,7 +142,7 @@ static struct em28xx_fmt format[] = {
/* supported controls */
/* Common to all boards */
-static struct v4l2_queryctrl em28xx_qctrl[] = {
+static struct v4l2_queryctrl ac97_qctrl[] = {
{
.id = V4L2_CID_AUDIO_VOLUME,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -151,7 +151,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = {
.maximum = 0x1f,
.step = 0x1,
.default_value = 0x1f,
- .flags = 0,
+ .flags = V4L2_CTRL_FLAG_SLIDER,
}, {
.id = V4L2_CID_AUDIO_MUTE,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -622,10 +622,29 @@ static void res_free(struct em28xx_fh *fh)
}
/*
- * em28xx_get_ctrl()
- * return the current saturation, brightness or contrast, mute state
+ * ac97_queryctrl()
+ * return the ac97 supported controls
*/
-static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
+static int ac97_queryctrl(struct v4l2_queryctrl *qc)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+ if (qc->id && qc->id == ac97_qctrl[i].id) {
+ memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+
+ /* Control is not ac97 related */
+ return 1;
+}
+
+/*
+ * ac97_get_ctrl()
+ * return the current values for ac97 mute and volume
+ */
+static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
{
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
@@ -635,29 +654,41 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl)
ctrl->value = dev->volume;
return 0;
default:
- return -EINVAL;
+ /* Control is not ac97 related */
+ return 1;
}
}
/*
- * em28xx_set_ctrl()
- * mute or set new saturation, brightness or contrast
+ * ac97_set_ctrl()
+ * set values for ac97 mute and volume
*/
-static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
+static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl)
{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++)
+ if (ctrl->id == ac97_qctrl[i].id)
+ goto handle;
+
+ /* Announce that hasn't handle it */
+ return 1;
+
+handle:
+ if (ctrl->value < ac97_qctrl[i].minimum ||
+ ctrl->value > ac97_qctrl[i].maximum)
+ return -ERANGE;
+
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value != dev->mute) {
- dev->mute = ctrl->value;
- return em28xx_audio_analog_set(dev);
- }
- return 0;
+ dev->mute = ctrl->value;
+ break;
case V4L2_CID_AUDIO_VOLUME:
dev->volume = ctrl->value;
- return em28xx_audio_analog_set(dev);
- default:
- return -EINVAL;
+ break;
}
+
+ return em28xx_audio_analog_set(dev);
}
static int check_dev(struct em28xx *dev)
@@ -679,8 +710,8 @@ static void get_scale(struct em28xx *dev,
unsigned int width, unsigned int height,
unsigned int *hscale, unsigned int *vscale)
{
- unsigned int maxw = norm_maxw(dev);
- unsigned int maxh = norm_maxh(dev);
+ unsigned int maxw = norm_maxw(dev);
+ unsigned int maxh = norm_maxh(dev);
*hscale = (((unsigned long)maxw) << 12) / width - 4096L;
if (*hscale >= 0x4000)
@@ -748,11 +779,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
return -EINVAL;
}
- if (dev->board.is_webcam) {
- /* FIXME: This is the only supported fmt */
- width = 640;
- height = 480;
- } else if (dev->board.is_em2800) {
+ 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;
@@ -789,12 +816,6 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc,
{
struct em28xx_fmt *fmt;
- /* FIXME: This is the only supported fmt */
- if (dev->board.is_webcam) {
- width = 640;
- height = 480;
- }
-
fmt = format_by_fourcc(fourcc);
if (!fmt)
return -EINVAL;
@@ -1025,7 +1046,6 @@ static int vidioc_queryctrl(struct file *file, void *priv,
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
int id = qc->id;
- int i;
int rc;
rc = check_dev(dev);
@@ -1036,15 +1056,14 @@ static int vidioc_queryctrl(struct file *file, void *priv,
qc->id = id;
- if (!dev->board.has_msp34xx) {
- for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
- if (qc->id && qc->id == em28xx_qctrl[i].id) {
- memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
- return 0;
- }
- }
+ /* enumberate AC97 controls */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
+ rc = ac97_queryctrl(qc);
+ if (!rc)
+ return 0;
}
+ /* enumberate V4L2 device controls */
mutex_lock(&dev->lock);
v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
mutex_unlock(&dev->lock);
@@ -1069,14 +1088,16 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
mutex_lock(&dev->lock);
- if (dev->board.has_msp34xx)
+ /* Set an AC97 control */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+ rc = ac97_get_ctrl(dev, ctrl);
+ else
+ rc = 1;
+
+ /* It were not an AC97 control. Sends it to the v4l2 dev interface */
+ if (rc == 1) {
v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
- else {
- rc = em28xx_get_ctrl(dev, ctrl);
- if (rc < 0) {
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
- rc = 0;
- }
+ rc = 0;
}
mutex_unlock(&dev->lock);
@@ -1088,7 +1109,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
- u8 i;
int rc;
rc = check_dev(dev);
@@ -1097,28 +1117,31 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
mutex_lock(&dev->lock);
- if (dev->board.has_msp34xx)
- v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
- else {
+ /* Set an AC97 control */
+ if (dev->audio_mode.ac97 != EM28XX_NO_AC97)
+ rc = ac97_set_ctrl(dev, ctrl);
+ else
rc = 1;
- for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
- if (ctrl->id == em28xx_qctrl[i].id) {
- if (ctrl->value < em28xx_qctrl[i].minimum ||
- ctrl->value > em28xx_qctrl[i].maximum) {
- rc = -ERANGE;
- break;
- }
-
- rc = em28xx_set_ctrl(dev, ctrl);
- break;
- }
- }
- }
- /* Control not found - try to send it to the attached devices */
+ /* It isn't an AC97 control. Sends it to the v4l2 dev interface */
if (rc == 1) {
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
- rc = 0;
+
+ /*
+ * In the case of non-AC97 volume controls, we still need
+ * to do some setups at em28xx, in order to mute/unmute
+ * and to adjust audio volume. However, the value ranges
+ * should be checked by the corresponding V4L subdriver.
+ */
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ dev->mute = ctrl->value;
+ rc = em28xx_audio_analog_set(dev);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ dev->volume = ctrl->value;
+ rc = em28xx_audio_analog_set(dev);
+ }
}
mutex_unlock(&dev->lock);
@@ -1678,9 +1701,9 @@ static int radio_queryctrl(struct file *file, void *priv,
qc->id >= V4L2_CID_LASTP1)
return -EINVAL;
- for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) {
- if (qc->id && qc->id == em28xx_qctrl[i].id) {
- memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc));
+ for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) {
+ if (qc->id && qc->id == ac97_qctrl[i].id) {
+ memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc));
return 0;
}
}
diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h
index 559903b2a..df7fe9af7 100644
--- a/linux/drivers/media/video/em28xx/em28xx.h
+++ b/linux/drivers/media/video/em28xx/em28xx.h
@@ -367,6 +367,7 @@ enum em28xx_decoder {
enum em28xx_sensor {
EM28XX_NOSENSOR = 0,
EM28XX_MT9V011,
+ EM28XX_MT9M001,
};
enum em28xx_adecoder {
@@ -487,7 +488,13 @@ struct em28xx {
struct v4l2_device v4l2_dev;
struct em28xx_board board;
+ /* Webcam specific fields */
enum em28xx_sensor em28xx_sensor;
+ int sensor_xres, sensor_yres;
+ int sensor_xtal;
+
+ /* Vinmode/Vinctl used at the driver */
+ int vinmode, vinctl;
unsigned int stream_on:1; /* Locks streams */
unsigned int has_audio_class:1;
@@ -769,17 +776,23 @@ static inline int em28xx_gamma_set(struct em28xx *dev, s32 val)
/*FIXME: maxw should be dependent of alt mode */
static inline unsigned int norm_maxw(struct em28xx *dev)
{
+ if (dev->board.is_webcam)
+ return dev->sensor_xres;
+
if (dev->board.max_range_640_480)
return 640;
- else
- return 720;
+
+ return 720;
}
static inline unsigned int norm_maxh(struct em28xx *dev)
{
+ if (dev->board.is_webcam)
+ return dev->sensor_yres;
+
if (dev->board.max_range_640_480)
return 480;
- else
- return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
+
+ return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
}
#endif