]> nv-tegra.nvidia Code Review - linux-4.9.git/commitdiff
usbtuner: add AVerMedia H837 support
authorTerry Heo <terryheo@google.com>
Thu, 7 May 2015 02:17:38 +0000 (11:17 +0900)
committermobile promotions <svcmobile_promotions@nvidia.com>
Wed, 31 Oct 2018 21:59:35 +0000 (14:59 -0700)
Add support for TDA18272 tuner module and
AVerMedia AVerTV Volar Hybrid Q (H837) USB TV tuner.

Resolved style issues from original commit and
DVB frontend API issues for TDA18272.

Bug: 20649732
Bug 1736911
Bug 2190055
Bug 2363907

Change-Id: Ib550504231ba2eaedebb864295db50069ebe769c
Signed-off-by: Terry Heo <terryheo@google.com>
Reviewed-on: http://git-master/r/1311288
(cherry picked from commit 8240d550519c4b97e8c97efcf646439fce40a9a0)
Signed-off-by: Magdalena Grodzinska <mgrodzinska@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1806528
(cherry picked from commit 19e5815c62e99b0dbcfa51989af2ee69c14311a4)
Reviewed-on: https://git-master.nvidia.com/r/1809387
(cherry picked from commit 3616efb8d342001e30c8bb337fc61b0128103165)
Reviewed-on: https://git-master.nvidia.com/r/1938553
Tested-by: Vladislav Zhurba <vzhurba@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
Reviewed-by: Vinayak Pane <vpane@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
drivers/media/tuners/tda18272.h [new file with mode: 0644]
drivers/media/usb/cx231xx/Kconfig
drivers/media/usb/cx231xx/cx231xx-avcore.c
drivers/media/usb/cx231xx/cx231xx-cards.c
drivers/media/usb/cx231xx/cx231xx-core.c
drivers/media/usb/cx231xx/cx231xx-dvb.c
drivers/media/usb/cx231xx/cx231xx-video.c
drivers/media/usb/cx231xx/cx231xx.h

diff --git a/drivers/media/tuners/tda18272.h b/drivers/media/tuners/tda18272.h
new file mode 100644 (file)
index 0000000..7737a49
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * TDA18272 Silicon tuner driver
+ * Copyright (C) Manu Abraham <abraham.manu@gmail.com>
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __TDA18272_H
+#define __TDA18272_H
+
+enum tda18272_mode {
+       TDA18272_SINGLE = 0,
+       TDA18272_MASTER,
+       TDA18272_SLAVE,
+};
+
+struct tda18272_config {
+       u8                      addr;
+       enum tda18272_mode      mode;
+};
+
+#if IS_ENABLED(CONFIG_MEDIA_TUNER_TDA18272)
+
+extern struct dvb_frontend *tda18272_attach(
+                                       struct dvb_frontend *fe,
+                                       struct i2c_adapter *i2c,
+                                       const struct tda18272_config *config);
+
+#else
+static inline struct dvb_frontend *tda18272_attach(
+                                       struct dvb_frontend *fe,
+                                       struct i2c_adapter *i2c,
+                                       const struct tda18272_config *config)
+{
+       printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+       return NULL;
+}
+
+#endif /* CONFIG_MEDIA_TUNER_TDA18272 */
+
+#endif /* __TDA18272_H */
index 0cced3e5b040bfddc8696d030aa3752003108e9f..f08f85a61e9b879e3394155f1d49d2468a4222ad 100644 (file)
@@ -45,6 +45,7 @@ config VIDEO_CX231XX_DVB
        select VIDEOBUF_DVB
        select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
        select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
+       select MEDIA_TUNER_TDA18272 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_MB86A20S if MEDIA_SUBDRV_AUTOSELECT
        select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT
        select DVB_LGDT3306A if MEDIA_SUBDRV_AUTOSELECT
index 2f52d66b4daec65cfb64a07244182d314ecb008e..b526277bfc4032d3cfe1b982dabfc0935a69d93e 100644 (file)
@@ -2293,6 +2293,8 @@ int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode)
        case POLARIS_AVMODE_ANALOGT_TV:
 
                tmp |= PWR_DEMOD_EN;
+               if (dev->model == CX231XX_BOARD_AVERMEDIA_H837B)
+                       tmp &= ~PWR_DEMOD_EN;
                value[0] = (u8) tmp;
                value[1] = (u8) (tmp >> 8);
                value[2] = (u8) (tmp >> 16);
@@ -2396,8 +2398,19 @@ int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode)
                status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
                                                PWR_CTL_EN, value, 4);
                msleep(PWR_SLEEP_INTERVAL);
-
-               if (!(tmp & PWR_DEMOD_EN)) {
+               if (is_model_avermedia_h837_series(dev->model)) {
+                       if (dev->model == CX231XX_BOARD_AVERMEDIA_H837B)
+                               tmp |= PWR_DEMOD_EN;
+                       else
+                               tmp &= ~PWR_DEMOD_EN;
+                       value[0] = (u8) tmp;
+                       value[1] = (u8) (tmp >> 8);
+                       value[2] = (u8) (tmp >> 16);
+                       value[3] = (u8) (tmp >> 24);
+                       status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+                                                       PWR_CTL_EN, value, 4);
+                       msleep(5 * PWR_SLEEP_INTERVAL);
+               } else if (!(tmp & PWR_DEMOD_EN)) {
                        tmp |= PWR_DEMOD_EN;
                        value[0] = (u8) tmp;
                        value[1] = (u8) (tmp >> 8);
@@ -2418,6 +2431,22 @@ int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode)
                }
                break;
 
+       case POLARIS_AVMODE_DEFAULT:
+               if (is_model_avermedia_h837_series(dev->model)) {
+                       tmp &= ~PWR_MODE_MASK;
+                       if (dev->model == CX231XX_BOARD_AVERMEDIA_H837A ||
+                           dev->model == CX231XX_BOARD_AVERMEDIA_H837M)
+                               tmp |= PWR_DEMOD_EN;
+                       value[0] = (u8) tmp;
+                       value[1] = (u8) (tmp >> 8);
+                       value[2] = (u8) (tmp >> 16);
+                       value[3] = (u8) (tmp >> 24);
+                       cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, value, 4);
+                       msleep(PWR_SLEEP_INTERVAL);
+                       return 0;
+               }
+               break;
+
        default:
                break;
        }
@@ -2592,8 +2621,13 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type)
                                dev_dbg(dev->dev, "%s: BDA\n", __func__);
                                status = cx231xx_mode_register(dev,
                                                         TS_MODE_REG, 0x101);
-                               status = cx231xx_mode_register(dev,
+                               if (is_model_avermedia_h837_series(dev->model)) {
+                                       status = cx231xx_mode_register(dev,
+                                                       TS1_CFG_REG, 0x408);
+                               } else {
+                                       status = cx231xx_mode_register(dev,
                                                        TS1_CFG_REG, 0x010);
+                               }
                        }
                        break;
 
index 69156affd0aef0ca07a30636fedb95f3f87f6060..d9093e1ce521d20d1c6618e063dd2da0c7160b57 100644 (file)
@@ -841,6 +841,105 @@ struct cx231xx_board cx231xx_boards[] = {
                        .gpio = NULL,
                } },
        },
+       [CX231XX_BOARD_AVERMEDIA_H837A] = {
+               .name = "AVerMedia H837-A USB Hybrid ATSC/QAM",
+               .tuner_type = TUNER_ABSENT,
+               .tuner_addr = 0x60,
+               .tuner_sif_gpio = 0x05,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x1c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 2,
+               .demod_i2c_master = 1,
+               .has_dvb = 1,
+               .norm = V4L2_STD_NTSC,
+
+               .input = {{
+                       .type = CX231XX_VMUX_TELEVISION,
+                       .vmux = CX231XX_VIN_3_1,
+                       .amux = CX231XX_AMUX_VIDEO,
+                       .gpio = 0,
+               }, {
+                       .type = CX231XX_VMUX_COMPOSITE1,
+                       .vmux = CX231XX_VIN_2_1,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = 0,
+               }, {
+                       .type = CX231XX_VMUX_SVIDEO,
+                       .vmux = CX231XX_VIN_1_1 |
+                               (CX231XX_VIN_1_2 << 8) |
+                               CX25840_SVIDEO_ON,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = 0,
+               } },
+       },
+       [CX231XX_BOARD_AVERMEDIA_H837B] = {
+               .name = "AVerMedia H837-B USB Hybrid ATSC/QAM",
+               .tuner_type = TUNER_ABSENT,
+               .tuner_addr = 0x60,
+               .tuner_sif_gpio = 0x05,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x1c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 2,
+               .demod_i2c_master = 1,
+               .has_dvb = 1,
+               .norm = V4L2_STD_NTSC,
+
+               .input = {{
+                       .type = CX231XX_VMUX_TELEVISION,
+                       .vmux = CX231XX_VIN_3_1,
+                       .amux = CX231XX_AMUX_VIDEO,
+                       .gpio = 0,
+               }, {
+                       .type = CX231XX_VMUX_COMPOSITE1,
+                       .vmux = CX231XX_VIN_2_1,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = 0,
+               }, {
+                       .type = CX231XX_VMUX_SVIDEO,
+                       .vmux = CX231XX_VIN_1_1 |
+                               (CX231XX_VIN_1_2 << 8) |
+                               CX25840_SVIDEO_ON,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = 0,
+               } },
+       },
+       [CX231XX_BOARD_AVERMEDIA_H837M] = {
+               .name = "AVerMedia H837-M USB Hybrid ATSC/QAM",
+               .tuner_type = TUNER_ABSENT,
+               .tuner_addr = 0x60,
+               .tuner_sif_gpio = 0x05,
+               .demod_xfer_mode = 0,
+               .ctl_pin_status_mask = 0xFFFFFFC4,
+               .agc_analog_digital_select_gpio = 0x1c,
+               .gpio_pin_status_mask = 0x4001000,
+               .tuner_i2c_master = 2,
+               .demod_i2c_master = 1,
+               .has_dvb = 1,
+               .norm = V4L2_STD_NTSC,
+
+               .input = {{
+                       .type = CX231XX_VMUX_TELEVISION,
+                       .vmux = CX231XX_VIN_3_1,
+                       .amux = CX231XX_AMUX_VIDEO,
+                       .gpio = 0,
+               }, {
+                       .type = CX231XX_VMUX_COMPOSITE1,
+                       .vmux = CX231XX_VIN_2_1,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = 0,
+               }, {
+                       .type = CX231XX_VMUX_SVIDEO,
+                       .vmux = CX231XX_VIN_1_1 |
+                               (CX231XX_VIN_1_2 << 8) |
+                               CX25840_SVIDEO_ON,
+                       .amux = CX231XX_AMUX_LINE_IN,
+                       .gpio = 0,
+               } },
+       },
 };
 const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
 
@@ -911,6 +1010,12 @@ struct usb_device_id cx231xx_id_table[] = {
         .driver_info = CX231XX_BOARD_OTG102},
        {USB_DEVICE(USB_VID_TERRATEC, 0x00a6),
         .driver_info = CX231XX_BOARD_TERRATEC_GRABBY},
+       {USB_DEVICE(0x07ca, 0x0837),
+        .driver_info = CX231XX_BOARD_AVERMEDIA_H837A},
+       {USB_DEVICE(0x07ca, 0x0837),
+        .driver_info = CX231XX_BOARD_AVERMEDIA_H837B},
+       {USB_DEVICE(0x07ca, 0x1837),
+        .driver_info = CX231XX_BOARD_AVERMEDIA_H837M},
        {},
 };
 
@@ -1577,7 +1682,8 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
        dev->gpio_dir = 0;
        dev->gpio_val = 0;
        dev->xc_fw_load_done = 0;
-       dev->has_alsa_audio = 1;
+       if (!is_model_avermedia_h837_series(dev->model))
+               dev->has_alsa_audio = 1;
        dev->power_mode = -1;
        atomic_set(&dev->devlist_count, 0);
 
index 71b65ab573ac153f3dab3c33e603e9fc8a486e67..cb8ea5eb5d0a1fd2c0bbce35468f1ef6125177da 100644 (file)
@@ -713,7 +713,18 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
                case CX231XX_BOARD_CNXT_RDE_250:
                case CX231XX_BOARD_CNXT_SHELBY:
                case CX231XX_BOARD_CNXT_RDU_250:
-               errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
+                       errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 0);
+                       break;
+               case CX231XX_BOARD_AVERMEDIA_H837M:
+               case CX231XX_BOARD_AVERMEDIA_H837B:
+               case CX231XX_BOARD_AVERMEDIA_H837A: {
+                       cx231xx_set_power_mode(dev, POLARIS_AVMODE_DEFAULT);
+                       msleep(20);
+                       cx231xx_set_agc_analog_digital_mux_select(dev, 0);
+                       cx231xx_set_power_mode(dev, POLARIS_AVMODE_DIGITAL);
+                       msleep(50);
+                       return 0;
+                       }
                        break;
                case CX231XX_BOARD_CNXT_RDE_253S:
                case CX231XX_BOARD_CNXT_RDU_253S:
@@ -731,6 +742,12 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
        } else/* Set Analog Power mode */ {
        /* set AGC mode to Analog */
                switch (dev->model) {
+               case CX231XX_BOARD_AVERMEDIA_H837A:
+               case CX231XX_BOARD_AVERMEDIA_H837B:
+               case CX231XX_BOARD_AVERMEDIA_H837M:
+                       cx231xx_set_agc_analog_digital_mux_select(dev, 1);
+                       cx231xx_set_power_mode(dev, POLARIS_AVMODE_DEFAULT);
+                       return 0;
                case CX231XX_BOARD_CNXT_CARRAERA:
                case CX231XX_BOARD_CNXT_RDE_250:
                case CX231XX_BOARD_CNXT_SHELBY:
@@ -1301,6 +1318,48 @@ void cx231xx_start_TS1(struct cx231xx *dev)
 /*****************************************************************
 *             Device Init/UnInit functions                       *
 ******************************************************************/
+static void cx231xx_check_model(struct cx231xx *dev)
+{
+       if (is_model_avermedia_h837_series(dev->model)) {
+               struct i2c_msg msg[2];
+               unsigned char offset = 255, value = 0;
+
+               dev->i2c_bus[0].i2c_period =
+               dev->i2c_bus[1].i2c_period =
+               dev->i2c_bus[2].i2c_period = I2C_SPEED_400K;
+               /* first a write message to write EE offset */
+               msg[0].addr = 0x50;
+               msg[0].flags = 0;
+               msg[0].len = 1;
+               msg[0].buf = &offset;
+
+               /* then a read message to read EE content */
+               /* maximum read length is 4 bytes */
+               msg[1].addr = 0x50;
+               msg[1].flags = I2C_M_RD;
+               msg[1].len = 1;
+               msg[1].buf = &value;
+
+               if (i2c_transfer(&dev->i2c_bus[1].i2c_adap, msg, 2) < 0) {
+                       dev_err(dev->dev, "Failed to check EEPROM");
+                       return;
+               }
+
+               if (value == 0x01) {
+                       if (dev->model == CX231XX_BOARD_AVERMEDIA_H837B)
+                               return;
+                       dev->model = CX231XX_BOARD_AVERMEDIA_H837B;
+               } else {
+                       if (dev->model == CX231XX_BOARD_AVERMEDIA_H837A ||
+                               dev->model == CX231XX_BOARD_AVERMEDIA_H837M)
+                               return;
+                       dev->model = CX231XX_BOARD_AVERMEDIA_H837A;
+               }
+               dev->board = cx231xx_boards[dev->model];
+               dev_info(dev->dev, "Correct device model as %s\n", dev->board.name);
+       }
+}
+
 int cx231xx_dev_init(struct cx231xx *dev)
 {
        int errCode = 0;
@@ -1359,6 +1418,9 @@ int cx231xx_dev_init(struct cx231xx *dev)
        cx231xx_do_i2c_scan(dev, I2C_2);
        cx231xx_do_i2c_scan(dev, I2C_1_MUX_3);
 
+       /* model check */
+       cx231xx_check_model(dev);
+
        /* init hardware */
        /* Note : with out calling set power mode function,
        afe can not be set up correctly */
index 1417515d30eb240641bf360fbcbeebe96ca3a6e0..e11a0ad5a2fa2996fb4cb9a18825c5b473808cb8 100644 (file)
@@ -36,6 +36,7 @@
 #include "mb86a20s.h"
 #include "si2157.h"
 #include "lgdt3306a.h"
+#include "tda18272.h"
 
 MODULE_DESCRIPTION("driver for cx231xx based DVB cards");
 MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
@@ -67,6 +68,12 @@ struct cx231xx_dvb {
        struct dvb_net net;
        struct i2c_client *i2c_client_demod;
        struct i2c_client *i2c_client_tuner;
+       int    power_on;
+};
+
+static struct tda18272_config h837_tda18272_config = {
+       0x60,                  /* dev->board.tuner_addr */
+       TDA18272_SINGLE,
 };
 
 static struct s5h1432_config dvico_s5h1432_config = {
@@ -128,6 +135,17 @@ static struct lgdt3305_config hcw_lgdt3305_config = {
        .vsb_if_khz         = 3250,
 };
 
+static struct lgdt3305_config h837_lgdt3305_config = {
+       .i2c_addr           = 0xB2 >> 1,
+       .mpeg_mode          = LGDT3305_MPEG_SERIAL,
+       .tpclk_edge         = LGDT3305_TPCLK_FALLING_EDGE,
+       .tpvalid_polarity   = LGDT3305_TP_VALID_HIGH,
+       .deny_i2c_rptr      = 1,
+       .spectral_inversion = 1,
+       .qam_if_khz         = 3600,
+       .vsb_if_khz         = 3250,
+};
+
 static struct tda18271_std_map hauppauge_tda18271_std_map = {
        .atsc_6   = { .if_freq = 3250, .agc_mode = 3, .std = 4,
                      .if_lvl = 1, .rfagc_top = 0x58, },
@@ -263,30 +281,31 @@ static int start_streaming(struct cx231xx_dvb *dvb)
        int rc;
        struct cx231xx *dev = dvb->adapter.priv;
 
+       dev_dbg(dev->dev, "DVB transfer mode is %s.\n",
+                       dev->USE_ISO ? "ISO" : "BULK" );
+       cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
+       rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+       if (rc < 0)
+               return rc;
+
+       dev->mode_tv = 1;
+
        if (dev->USE_ISO) {
-               dev_dbg(dev->dev, "DVB transfer mode is ISO.\n");
-               cx231xx_set_alt_setting(dev, INDEX_TS1, 4);
-               rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
-               if (rc < 0)
-                       return rc;
-               dev->mode_tv = 1;
-               return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
+               rc = cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
                                        CX231XX_DVB_NUM_BUFS,
                                        dev->ts1_mode.max_pkt_size,
                                        dvb_isoc_copy);
        } else {
-               dev_dbg(dev->dev, "DVB transfer mode is BULK.\n");
-               cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
-               rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
-               if (rc < 0)
-                       return rc;
-               dev->mode_tv = 1;
-               return cx231xx_init_bulk(dev, CX231XX_DVB_MAX_PACKETS,
+               rc = cx231xx_init_bulk(dev, CX231XX_DVB_MAX_PACKETS,
                                        CX231XX_DVB_NUM_BUFS,
                                        dev->ts1_mode.max_pkt_size,
                                        dvb_bulk_copy);
        }
 
+       if (rc >= 0 && is_model_avermedia_h837_series(dev->model))
+               ++dvb->power_on;
+
+       return rc;
 }
 
 static int stop_streaming(struct cx231xx_dvb *dvb)
@@ -298,6 +317,11 @@ static int stop_streaming(struct cx231xx_dvb *dvb)
        else
                cx231xx_uninit_bulk(dev);
 
+       if (is_model_avermedia_h837_series(dev->model)) {
+               --dvb->power_on;
+               if (dvb->power_on)
+                       return 0;
+       }
        cx231xx_set_mode(dev, CX231XX_SUSPEND);
 
        return 0;
@@ -346,11 +370,19 @@ static int stop_feed(struct dvb_demux_feed *feed)
 static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
 {
        struct cx231xx *dev = fe->dvb->priv;
+       struct cx231xx_dvb *dvb = dev->dvb;
 
-       if (acquire)
+       if (acquire) {
+               if (is_model_avermedia_h837_series(dev->model))
+                       ++dvb->power_on;
                return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
-       else
-               return cx231xx_set_mode(dev, CX231XX_SUSPEND);
+       }
+       if (is_model_avermedia_h837_series(dev->model)) {
+               --dvb->power_on;
+               if (dvb->power_on)
+                       return 0;
+       }
+       return cx231xx_set_mode(dev, CX231XX_SUSPEND);
 }
 
 /* ------------------------------------------------------------------ */
@@ -612,14 +644,20 @@ static int dvb_init(struct cx231xx *dev)
                return -ENOMEM;
        }
        dev->dvb = dvb;
+       dvb->power_on = 0;
        dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq;
        dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner;
 
        tuner_i2c = cx231xx_get_i2c_adap(dev, dev->board.tuner_i2c_master);
        demod_i2c = cx231xx_get_i2c_adap(dev, dev->board.demod_i2c_master);
        mutex_lock(&dev->lock);
-       cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
-       cx231xx_demod_reset(dev);
+       if (is_model_avermedia_h837_series(dev->model)) {
+               cx231xx_set_mode(dev, CX231XX_SUSPEND);
+               cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+       } else {
+               cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+               cx231xx_demod_reset(dev);
+       }
        /* init frontend */
        switch (dev->model) {
        case CX231XX_BOARD_CNXT_CARRAERA:
@@ -949,6 +987,29 @@ static int dvb_init(struct cx231xx *dev)
                           &pv_tda18271_config);
                break;
 
+       case CX231XX_BOARD_AVERMEDIA_H837A:
+       case CX231XX_BOARD_AVERMEDIA_H837B:
+       case CX231XX_BOARD_AVERMEDIA_H837M:
+               dev->dvb->frontend = dvb_attach(lgdt3305_attach,
+                                               &h837_lgdt3305_config,
+                                               &dev->i2c_bus[dev->board.demod_i2c_master].i2c_adap);
+
+               if (dev->dvb->frontend == NULL) {
+                       dev_err(dev->dev,
+                               "Failed to attach LG3305 front end\n");
+                       result = -EINVAL;
+                       goto out_free;
+               }
+
+               /* define general-purpose callback pointer */
+               dvb->frontend->callback = cx231xx_tuner_callback;
+               {
+                       dvb_attach(tda18272_attach, dev->dvb->frontend,
+                               &dev->i2c_bus[dev->board.tuner_i2c_master].i2c_adap,
+                               &h837_tda18272_config);
+               }
+               break;
+
        default:
                dev_err(dev->dev,
                        "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
index 6414188ffdfac89d807fa885e707688ec9532f07..a763d5ea43e0906a46fedad5704bbe5ab81ad838 100644 (file)
@@ -870,6 +870,8 @@ static struct videobuf_queue_ops cx231xx_video_qops = {
 
 void video_mux(struct cx231xx *dev, int index)
 {
+       if (is_model_avermedia_h837_series(dev->model))
+               return;
        dev->video_input = index;
        dev->ctl_ainput = INPUT(index)->amux;
 
index 90c867683076017f818a81d6db15dfb0c97adb95..a4383bca9b788b9822131833cc9a8c4982cfde95 100644 (file)
@@ -78,6 +78,9 @@
 #define CX231XX_BOARD_HAUPPAUGE_930C_HD_1114xx 20
 #define CX231XX_BOARD_HAUPPAUGE_955Q 21
 #define CX231XX_BOARD_TERRATEC_GRABBY 22
+#define CX231XX_BOARD_AVERMEDIA_H837A 23
+#define CX231XX_BOARD_AVERMEDIA_H837B 24
+#define CX231XX_BOARD_AVERMEDIA_H837M 25
 
 /* Limits minimum and default number of buffers */
 #define CX231XX_MIN_BUF                 4
@@ -1005,4 +1008,16 @@ static inline unsigned int norm_maxh(struct cx231xx *dev)
        else
                return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
 }
+
+static inline bool is_model_avermedia_h837_series(int model)
+{
+       switch (model) {
+       case CX231XX_BOARD_AVERMEDIA_H837A:
+       case CX231XX_BOARD_AVERMEDIA_H837B:
+       case CX231XX_BOARD_AVERMEDIA_H837M:
+               return true;
+       }
+       return false;
+}
+
 #endif