--- /dev/null
+/*
+ * 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 */
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
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);
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);
}
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;
}
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;
.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);
.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},
{},
};
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);
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:
} 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:
/*****************************************************************
* 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;
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 */
#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>");
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 = {
.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, },
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)
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;
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);
}
/* ------------------------------------------------------------------ */
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:
&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",
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;
#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
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