diff options
Diffstat (limited to 'drivers/media/video/em28xx')
-rw-r--r-- | drivers/media/video/em28xx/em28xx-cards.c | 134 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-core.c | 22 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-dvb.c | 62 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-video.c | 16 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx.h | 31 |
5 files changed, 192 insertions, 73 deletions
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index ebd24a25fb8..320f1f60276 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -58,8 +58,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; @@ -159,6 +157,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}, @@ -205,13 +217,15 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = { */ struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { - .name = "Unknown EM2750/EM2751 webcam grabber", + .name = "EM2710/EM2750/EM2751 webcam grabber", .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, .amux = EM28XX_AMUX_VIDEO, + .gpio = silvercrest_reg_seq, } }, }, [EM2800_BOARD_UNKNOWN] = { @@ -233,13 +247,15 @@ 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 */ .name = "Huaqi DLCW-130", .valid = EM28XX_BOARD_NOT_VALIDATED, .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -440,7 +456,8 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { .name = "Videology 20K14XUSB USB2.0", .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -450,8 +467,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_SILVERCREST_WEBCAM] = { .name = "Silvercrest Webcam 1.3mpix", .tuner_type = TUNER_ABSENT, - .is_27xx = 1, - .decoder = EM28XX_MT9V011, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -500,7 +516,8 @@ struct em28xx_board em28xx_boards[] = { /* Beijing Huaqi Information Digital Technology Co., Ltd */ .name = "NetGMBH Cam", .valid = EM28XX_BOARD_NOT_VALIDATED, - .tuner_type = TUNER_ABSENT, /* This is a webcam */ + .tuner_type = TUNER_ABSENT, + .is_webcam = 1, .input = { { .type = EM28XX_VMUX_COMPOSITE1, .vmux = 0, @@ -1250,25 +1267,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, + .has_dvb = 1, + .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] = { @@ -1638,6 +1656,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 */ @@ -1704,6 +1723,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, ®s[i][0], 3); + + return 0; +} + /* HINT method: webcam I2C chips * * This method work for webcams with Micron sensors @@ -1716,9 +1761,6 @@ static int em28xx_hint_sensor(struct em28xx *dev) __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); @@ -1729,16 +1771,38 @@ static int em28xx_hint_sensor(struct em28xx *dev) version = be16_to_cpu(version_be); switch (version) { - case MT9V011_VERSION: + case 0x8243: /* mt9v011 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; + 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 Sensor 0x%04x\n", be16_to_cpu(version)); + printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version)); return -EINVAL; } - em28xx_errdev("Sensor is %s, assuming that webcam is %s\n", + em28xx_errdev("Sensor is %s, using model %s entry.\n", sensor_name, em28xx_boards[dev->model].name); return 0; @@ -1772,10 +1836,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_27xx) - 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"); @@ -1929,6 +1990,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: @@ -2225,6 +2287,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: @@ -2262,9 +2325,14 @@ 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->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 (dev->board.adecoder == EM28XX_TVAUDIO) v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, @@ -2410,7 +2478,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/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 079ab4d563a..5b78e199abd 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/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_27xx) { - 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_27xx) { - /* 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/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 3da97c32b8f..cf0ac7f2a30 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -31,6 +31,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>"); @@ -243,7 +245,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, @@ -258,6 +260,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) @@ -440,7 +477,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, @@ -451,20 +487,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/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 14316c91217..ff37b4c15f4 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -657,8 +657,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) @@ -726,11 +726,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - if (dev->board.is_27xx) { - /* 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; @@ -767,12 +763,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_27xx) { - width = 640; - height = 480; - } - fmt = format_by_fourcc(fourcc); if (!fmt) return -EINVAL; diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index d90fef46376..45bd513f62d 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -358,10 +358,15 @@ struct em28xx_input { #define INPUT(nr) (&em28xx_boards[dev->model].input[nr]) enum em28xx_decoder { - EM28XX_NODECODER, + EM28XX_NODECODER = 0, EM28XX_TVP5150, EM28XX_SAA711X, +}; + +enum em28xx_sensor { + EM28XX_NOSENSOR = 0, EM28XX_MT9V011, + EM28XX_MT9M001, }; enum em28xx_adecoder { @@ -390,7 +395,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 is_webcam:1; unsigned int valid:1; unsigned char xclk, i2c_speed; @@ -474,6 +479,14 @@ 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; unsigned int has_alsa_audio:1; @@ -754,17 +767,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 |