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