diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index 873630e7e53e..014d255231fc 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -66,3 +66,4 @@ 68 -> Terratec AV350 (em2860) [0ccd:0084] 69 -> KWorld ATSC 315U HDTV TV Box (em2882) [eb1a:a313] 70 -> Evga inDtube (em2882) + 71 -> Silvercrest Webcam 1.3mpix (em2820/em2840) diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index 16a5af30e9d1..6524b493e033 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -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. diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index c43fdb9bc888..bd9b637c7ea5 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -191,6 +191,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 +445,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, @@ -1639,6 +1658,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, @@ -1706,7 +1730,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 +2185,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 +2220,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); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index c8d7ce8fbd36..dda2721ee5b0 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -648,17 +648,29 @@ 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; + outfmt = 0x24; + } 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 +707,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 +738,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); } diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 813ce45c2f99..d90fef463764 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -107,6 +107,7 @@ #define EM2860_BOARD_TERRATEC_AV350 68 #define EM2882_BOARD_KWORLD_ATSC_315U 69 #define EM2882_BOARD_EVGA_INDTUBE 70 +#define EM2820_BOARD_SILVERCREST_WEBCAM 71 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -360,6 +361,7 @@ 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;