| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver | |
| * | |
| * Copyright (c) STMicroelectronics 2015 | |
| * | |
| * Author Peter Griffin <peter.griffin@linaro.org> | |
| * | |
| */ | |
| #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; | |
| const 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_client_device(tsin->i2c_adapter, | |
| &tda18212_info); | |
| if (!i2c_client_has_driver(client)) { | |
| 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; | |
| } |