| /* |
| * 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; |
| 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_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; |
| } |