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