|
@@ -0,0 +1,244 @@
|
|
|
|
+/*
|
|
|
|
+ * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver
|
|
|
|
+ *
|
|
|
|
+ * Copyright (c) STMicroelectronics 2015
|
|
|
|
+ *
|
|
|
|
+ * Author Peter Griffin <peter.griffin@linaro.org>
|
|
|
|
+ *
|
|
|
|
+ * 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.
|
|
|
|
+ */
|
|
|
|
+#include <linux/completion.h>
|
|
|
|
+#include <linux/delay.h>
|
|
|
|
+#include <linux/i2c.h>
|
|
|
|
+#include <linux/interrupt.h>
|
|
|
|
+#include <linux/version.h>
|
|
|
|
+
|
|
|
|
+#include <dt-bindings/media/c8sectpfe.h>
|
|
|
|
+
|
|
|
|
+#include "c8sectpfe-common.h"
|
|
|
|
+#include "c8sectpfe-core.h"
|
|
|
|
+#include "c8sectpfe-dvb.h"
|
|
|
|
+
|
|
|
|
+#include "dvb-pll.h"
|
|
|
|
+#include "lnbh24.h"
|
|
|
|
+#include "stv0367.h"
|
|
|
|
+#include "stv0367_priv.h"
|
|
|
|
+#include "stv6110x.h"
|
|
|
|
+#include "stv090x.h"
|
|
|
|
+#include "tda18212.h"
|
|
|
|
+
|
|
|
|
+static inline const char *dvb_card_str(unsigned int c)
|
|
|
|
+{
|
|
|
|
+ switch (c) {
|
|
|
|
+ case STV0367_TDA18212_NIMA_1: return "STV0367_TDA18212_NIMA_1";
|
|
|
|
+ case STV0367_TDA18212_NIMA_2: return "STV0367_TDA18212_NIMA_2";
|
|
|
|
+ case STV0367_TDA18212_NIMB_1: return "STV0367_TDA18212_NIMB_1";
|
|
|
|
+ case STV0367_TDA18212_NIMB_2: return "STV0367_TDA18212_NIMB_2";
|
|
|
|
+ case STV0903_6110_LNB24_NIMA: return "STV0903_6110_LNB24_NIMA";
|
|
|
|
+ case STV0903_6110_LNB24_NIMB: return "STV0903_6110_LNB24_NIMB";
|
|
|
|
+ default: return "unknown dvb frontend card";
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static struct stv090x_config stv090x_config = {
|
|
|
|
+ .device = STV0903,
|
|
|
|
+ .demod_mode = STV090x_SINGLE,
|
|
|
|
+ .clk_mode = STV090x_CLK_EXT,
|
|
|
|
+ .xtal = 16000000,
|
|
|
|
+ .address = 0x69,
|
|
|
|
+
|
|
|
|
+ .ts1_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
|
|
|
|
+ .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
|
|
|
|
+
|
|
|
|
+ .repeater_level = STV090x_RPTLEVEL_64,
|
|
|
|
+
|
|
|
|
+ .tuner_init = NULL,
|
|
|
|
+ .tuner_set_mode = NULL,
|
|
|
|
+ .tuner_set_frequency = NULL,
|
|
|
|
+ .tuner_get_frequency = NULL,
|
|
|
|
+ .tuner_set_bandwidth = NULL,
|
|
|
|
+ .tuner_get_bandwidth = NULL,
|
|
|
|
+ .tuner_set_bbgain = NULL,
|
|
|
|
+ .tuner_get_bbgain = NULL,
|
|
|
|
+ .tuner_set_refclk = NULL,
|
|
|
|
+ .tuner_get_status = NULL,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static struct stv6110x_config stv6110x_config = {
|
|
|
|
+ .addr = 0x60,
|
|
|
|
+ .refclk = 16000000,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#define NIMA 0
|
|
|
|
+#define NIMB 1
|
|
|
|
+
|
|
|
|
+static struct stv0367_config stv0367_tda18212_config[] = {
|
|
|
|
+ {
|
|
|
|
+ .demod_address = 0x1c,
|
|
|
|
+ .xtal = 16000000,
|
|
|
|
+ .if_khz = 4500,
|
|
|
|
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
|
|
|
|
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
|
|
|
|
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
|
|
|
|
+ }, {
|
|
|
|
+ .demod_address = 0x1d,
|
|
|
|
+ .xtal = 16000000,
|
|
|
|
+ .if_khz = 4500,
|
|
|
|
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
|
|
|
|
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
|
|
|
|
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
|
|
|
|
+ }, {
|
|
|
|
+ .demod_address = 0x1e,
|
|
|
|
+ .xtal = 16000000,
|
|
|
|
+ .if_khz = 4500,
|
|
|
|
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
|
|
|
|
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
|
|
|
|
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
|
|
|
|
+ },
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static struct tda18212_config tda18212_conf = {
|
|
|
|
+ .if_dvbt_6 = 4150,
|
|
|
|
+ .if_dvbt_7 = 4150,
|
|
|
|
+ .if_dvbt_8 = 4500,
|
|
|
|
+ .if_dvbc = 5000,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+int c8sectpfe_frontend_attach(struct dvb_frontend **fe,
|
|
|
|
+ struct c8sectpfe *c8sectpfe,
|
|
|
|
+ struct channel_info *tsin, int chan_num)
|
|
|
|
+{
|
|
|
|
+ struct tda18212_config *tda18212;
|
|
|
|
+ struct stv6110x_devctl *fe2;
|
|
|
|
+ struct i2c_client *client;
|
|
|
|
+ struct i2c_board_info tda18212_info = {
|
|
|
|
+ .type = "tda18212",
|
|
|
|
+ .addr = 0x60,
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ if (!tsin)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ switch (tsin->dvb_card) {
|
|
|
|
+
|
|
|
|
+ case STV0367_TDA18212_NIMA_1:
|
|
|
|
+ case STV0367_TDA18212_NIMA_2:
|
|
|
|
+ case STV0367_TDA18212_NIMB_1:
|
|
|
|
+ case STV0367_TDA18212_NIMB_2:
|
|
|
|
+ if (tsin->dvb_card == STV0367_TDA18212_NIMA_1)
|
|
|
|
+ *fe = dvb_attach(stv0367ter_attach,
|
|
|
|
+ &stv0367_tda18212_config[0],
|
|
|
|
+ tsin->i2c_adapter);
|
|
|
|
+ else if (tsin->dvb_card == STV0367_TDA18212_NIMB_1)
|
|
|
|
+ *fe = dvb_attach(stv0367ter_attach,
|
|
|
|
+ &stv0367_tda18212_config[1],
|
|
|
|
+ tsin->i2c_adapter);
|
|
|
|
+ else
|
|
|
|
+ *fe = dvb_attach(stv0367ter_attach,
|
|
|
|
+ &stv0367_tda18212_config[2],
|
|
|
|
+ tsin->i2c_adapter);
|
|
|
|
+
|
|
|
|
+ if (!*fe) {
|
|
|
|
+ dev_err(c8sectpfe->device,
|
|
|
|
+ "%s: stv0367ter_attach failed for NIM card %s\n"
|
|
|
|
+ , __func__, dvb_card_str(tsin->dvb_card));
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * init the demod so that i2c gate_ctrl
|
|
|
|
+ * to the tuner works correctly
|
|
|
|
+ */
|
|
|
|
+ (*fe)->ops.init(*fe);
|
|
|
|
+
|
|
|
|
+ /* Allocate the tda18212 structure */
|
|
|
|
+ tda18212 = devm_kzalloc(c8sectpfe->device,
|
|
|
|
+ sizeof(struct tda18212_config),
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
+ if (!tda18212) {
|
|
|
|
+ dev_err(c8sectpfe->device,
|
|
|
|
+ "%s: devm_kzalloc failed\n", __func__);
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memcpy(tda18212, &tda18212_conf,
|
|
|
|
+ sizeof(struct tda18212_config));
|
|
|
|
+
|
|
|
|
+ tda18212->fe = (*fe);
|
|
|
|
+
|
|
|
|
+ tda18212_info.platform_data = tda18212;
|
|
|
|
+
|
|
|
|
+ /* attach tuner */
|
|
|
|
+ request_module("tda18212");
|
|
|
|
+ client = i2c_new_device(tsin->i2c_adapter, &tda18212_info);
|
|
|
|
+ if (!client || !client->dev.driver) {
|
|
|
|
+ dvb_frontend_detach(*fe);
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!try_module_get(client->dev.driver->owner)) {
|
|
|
|
+ i2c_unregister_device(client);
|
|
|
|
+ dvb_frontend_detach(*fe);
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ tsin->i2c_client = client;
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case STV0903_6110_LNB24_NIMA:
|
|
|
|
+ *fe = dvb_attach(stv090x_attach, &stv090x_config,
|
|
|
|
+ tsin->i2c_adapter, STV090x_DEMODULATOR_0);
|
|
|
|
+ if (!*fe) {
|
|
|
|
+ dev_err(c8sectpfe->device, "%s: stv090x_attach failed\n"
|
|
|
|
+ "\tfor NIM card %s\n",
|
|
|
|
+ __func__, dvb_card_str(tsin->dvb_card));
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fe2 = dvb_attach(stv6110x_attach, *fe,
|
|
|
|
+ &stv6110x_config, tsin->i2c_adapter);
|
|
|
|
+ if (!fe2) {
|
|
|
|
+ dev_err(c8sectpfe->device,
|
|
|
|
+ "%s: stv6110x_attach failed for NIM card %s\n"
|
|
|
|
+ , __func__, dvb_card_str(tsin->dvb_card));
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ stv090x_config.tuner_init = fe2->tuner_init;
|
|
|
|
+ stv090x_config.tuner_set_mode = fe2->tuner_set_mode;
|
|
|
|
+ stv090x_config.tuner_set_frequency = fe2->tuner_set_frequency;
|
|
|
|
+ stv090x_config.tuner_get_frequency = fe2->tuner_get_frequency;
|
|
|
|
+ stv090x_config.tuner_set_bandwidth = fe2->tuner_set_bandwidth;
|
|
|
|
+ stv090x_config.tuner_get_bandwidth = fe2->tuner_get_bandwidth;
|
|
|
|
+ stv090x_config.tuner_set_bbgain = fe2->tuner_set_bbgain;
|
|
|
|
+ stv090x_config.tuner_get_bbgain = fe2->tuner_get_bbgain;
|
|
|
|
+ stv090x_config.tuner_set_refclk = fe2->tuner_set_refclk;
|
|
|
|
+ stv090x_config.tuner_get_status = fe2->tuner_get_status;
|
|
|
|
+
|
|
|
|
+ dvb_attach(lnbh24_attach, *fe, tsin->i2c_adapter, 0, 0, 0x9);
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ dev_err(c8sectpfe->device,
|
|
|
|
+ "%s: DVB frontend card %s not yet supported\n",
|
|
|
|
+ __func__, dvb_card_str(tsin->dvb_card));
|
|
|
|
+ return -ENODEV;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ (*fe)->id = chan_num;
|
|
|
|
+
|
|
|
|
+ dev_info(c8sectpfe->device,
|
|
|
|
+ "DVB frontend card %s successfully attached",
|
|
|
|
+ dvb_card_str(tsin->dvb_card));
|
|
|
|
+ return 0;
|
|
|
|
+}
|