1// SPDX-License-Identifier: GPL-2.0 2/* 3 * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver 4 * 5 * Copyright (c) STMicroelectronics 2015 6 * 7 * Author Peter Griffin <peter.griffin@linaro.org> 8 * 9 */ 10#include <linux/completion.h> 11#include <linux/delay.h> 12#include <linux/i2c.h> 13#include <linux/interrupt.h> 14#include <linux/version.h> 15 16#include <dt-bindings/media/c8sectpfe.h> 17 18#include "c8sectpfe-common.h" 19#include "c8sectpfe-core.h" 20#include "c8sectpfe-dvb.h" 21 22#include "dvb-pll.h" 23#include "lnbh24.h" 24#include "stv0367.h" 25#include "stv0367_priv.h" 26#include "stv6110x.h" 27#include "stv090x.h" 28#include "tda18212.h" 29 30static inline const char *dvb_card_str(unsigned int c) 31{ 32 switch (c) { 33 case STV0367_TDA18212_NIMA_1: return "STV0367_TDA18212_NIMA_1"; 34 case STV0367_TDA18212_NIMA_2: return "STV0367_TDA18212_NIMA_2"; 35 case STV0367_TDA18212_NIMB_1: return "STV0367_TDA18212_NIMB_1"; 36 case STV0367_TDA18212_NIMB_2: return "STV0367_TDA18212_NIMB_2"; 37 case STV0903_6110_LNB24_NIMA: return "STV0903_6110_LNB24_NIMA"; 38 case STV0903_6110_LNB24_NIMB: return "STV0903_6110_LNB24_NIMB"; 39 default: return "unknown dvb frontend card"; 40 } 41} 42 43static struct stv090x_config stv090x_config = { 44 .device = STV0903, 45 .demod_mode = STV090x_SINGLE, 46 .clk_mode = STV090x_CLK_EXT, 47 .xtal = 16000000, 48 .address = 0x69, 49 50 .ts1_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, 51 .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, 52 53 .repeater_level = STV090x_RPTLEVEL_64, 54 55 .tuner_init = NULL, 56 .tuner_set_mode = NULL, 57 .tuner_set_frequency = NULL, 58 .tuner_get_frequency = NULL, 59 .tuner_set_bandwidth = NULL, 60 .tuner_get_bandwidth = NULL, 61 .tuner_set_bbgain = NULL, 62 .tuner_get_bbgain = NULL, 63 .tuner_set_refclk = NULL, 64 .tuner_get_status = NULL, 65}; 66 67static struct stv6110x_config stv6110x_config = { 68 .addr = 0x60, 69 .refclk = 16000000, 70}; 71 72#define NIMA 0 73#define NIMB 1 74 75static struct stv0367_config stv0367_tda18212_config[] = { 76 { 77 .demod_address = 0x1c, 78 .xtal = 16000000, 79 .if_khz = 4500, 80 .if_iq_mode = FE_TER_NORMAL_IF_TUNER, 81 .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, 82 .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, 83 }, { 84 .demod_address = 0x1d, 85 .xtal = 16000000, 86 .if_khz = 4500, 87 .if_iq_mode = FE_TER_NORMAL_IF_TUNER, 88 .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, 89 .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, 90 }, { 91 .demod_address = 0x1e, 92 .xtal = 16000000, 93 .if_khz = 4500, 94 .if_iq_mode = FE_TER_NORMAL_IF_TUNER, 95 .ts_mode = STV0367_SERIAL_PUNCT_CLOCK, 96 .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT, 97 }, 98}; 99 100static struct tda18212_config tda18212_conf = { 101 .if_dvbt_6 = 4150, 102 .if_dvbt_7 = 4150, 103 .if_dvbt_8 = 4500, 104 .if_dvbc = 5000, 105}; 106 107int c8sectpfe_frontend_attach(struct dvb_frontend **fe, 108 struct c8sectpfe *c8sectpfe, 109 struct channel_info *tsin, int chan_num) 110{ 111 struct tda18212_config *tda18212; 112 const struct stv6110x_devctl *fe2; 113 struct i2c_client *client; 114 struct i2c_board_info tda18212_info = { 115 .type = "tda18212", 116 .addr = 0x60, 117 }; 118 119 if (!tsin) 120 return -EINVAL; 121 122 switch (tsin->dvb_card) { 123 124 case STV0367_TDA18212_NIMA_1: 125 case STV0367_TDA18212_NIMA_2: 126 case STV0367_TDA18212_NIMB_1: 127 case STV0367_TDA18212_NIMB_2: 128 if (tsin->dvb_card == STV0367_TDA18212_NIMA_1) 129 *fe = dvb_attach(stv0367ter_attach, 130 &stv0367_tda18212_config[0], 131 tsin->i2c_adapter); 132 else if (tsin->dvb_card == STV0367_TDA18212_NIMB_1) 133 *fe = dvb_attach(stv0367ter_attach, 134 &stv0367_tda18212_config[1], 135 tsin->i2c_adapter); 136 else 137 *fe = dvb_attach(stv0367ter_attach, 138 &stv0367_tda18212_config[2], 139 tsin->i2c_adapter); 140 141 if (!*fe) { 142 dev_err(c8sectpfe->device, 143 "%s: stv0367ter_attach failed for NIM card %s\n" 144 , __func__, dvb_card_str(tsin->dvb_card)); 145 return -ENODEV; 146 } 147 148 /* 149 * init the demod so that i2c gate_ctrl 150 * to the tuner works correctly 151 */ 152 (*fe)->ops.init(*fe); 153 154 /* Allocate the tda18212 structure */ 155 tda18212 = devm_kzalloc(c8sectpfe->device, 156 sizeof(struct tda18212_config), 157 GFP_KERNEL); 158 if (!tda18212) { 159 dev_err(c8sectpfe->device, 160 "%s: devm_kzalloc failed\n", __func__); 161 return -ENOMEM; 162 } 163 164 memcpy(tda18212, &tda18212_conf, 165 sizeof(struct tda18212_config)); 166 167 tda18212->fe = (*fe); 168 169 tda18212_info.platform_data = tda18212; 170 171 /* attach tuner */ 172 request_module("tda18212"); 173 client = i2c_new_client_device(tsin->i2c_adapter, 174 &tda18212_info); 175 if (!i2c_client_has_driver(client)) { 176 dvb_frontend_detach(*fe); 177 return -ENODEV; 178 } 179 180 if (!try_module_get(client->dev.driver->owner)) { 181 i2c_unregister_device(client); 182 dvb_frontend_detach(*fe); 183 return -ENODEV; 184 } 185 186 tsin->i2c_client = client; 187 188 break; 189 190 case STV0903_6110_LNB24_NIMA: 191 *fe = dvb_attach(stv090x_attach, &stv090x_config, 192 tsin->i2c_adapter, STV090x_DEMODULATOR_0); 193 if (!*fe) { 194 dev_err(c8sectpfe->device, "%s: stv090x_attach failed\n" 195 "\tfor NIM card %s\n", 196 __func__, dvb_card_str(tsin->dvb_card)); 197 return -ENODEV; 198 } 199 200 fe2 = dvb_attach(stv6110x_attach, *fe, 201 &stv6110x_config, tsin->i2c_adapter); 202 if (!fe2) { 203 dev_err(c8sectpfe->device, 204 "%s: stv6110x_attach failed for NIM card %s\n" 205 , __func__, dvb_card_str(tsin->dvb_card)); 206 return -ENODEV; 207 } 208 209 stv090x_config.tuner_init = fe2->tuner_init; 210 stv090x_config.tuner_set_mode = fe2->tuner_set_mode; 211 stv090x_config.tuner_set_frequency = fe2->tuner_set_frequency; 212 stv090x_config.tuner_get_frequency = fe2->tuner_get_frequency; 213 stv090x_config.tuner_set_bandwidth = fe2->tuner_set_bandwidth; 214 stv090x_config.tuner_get_bandwidth = fe2->tuner_get_bandwidth; 215 stv090x_config.tuner_set_bbgain = fe2->tuner_set_bbgain; 216 stv090x_config.tuner_get_bbgain = fe2->tuner_get_bbgain; 217 stv090x_config.tuner_set_refclk = fe2->tuner_set_refclk; 218 stv090x_config.tuner_get_status = fe2->tuner_get_status; 219 220 dvb_attach(lnbh24_attach, *fe, tsin->i2c_adapter, 0, 0, 0x9); 221 break; 222 223 default: 224 dev_err(c8sectpfe->device, 225 "%s: DVB frontend card %s not yet supported\n", 226 __func__, dvb_card_str(tsin->dvb_card)); 227 return -ENODEV; 228 } 229 230 (*fe)->id = chan_num; 231 232 dev_info(c8sectpfe->device, 233 "DVB frontend card %s successfully attached", 234 dvb_card_str(tsin->dvb_card)); 235 return 0; 236} 237