18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  c8sectpfe-dvb.c - C8SECTPFE STi DVB driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) STMicroelectronics 2015
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Author Peter Griffin <peter.griffin@linaro.org>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#include <linux/completion.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/i2c.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/version.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <dt-bindings/media/c8sectpfe.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "c8sectpfe-common.h"
198c2ecf20Sopenharmony_ci#include "c8sectpfe-core.h"
208c2ecf20Sopenharmony_ci#include "c8sectpfe-dvb.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "dvb-pll.h"
238c2ecf20Sopenharmony_ci#include "lnbh24.h"
248c2ecf20Sopenharmony_ci#include "stv0367.h"
258c2ecf20Sopenharmony_ci#include "stv0367_priv.h"
268c2ecf20Sopenharmony_ci#include "stv6110x.h"
278c2ecf20Sopenharmony_ci#include "stv090x.h"
288c2ecf20Sopenharmony_ci#include "tda18212.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic inline const char *dvb_card_str(unsigned int c)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	switch (c) {
338c2ecf20Sopenharmony_ci	case STV0367_TDA18212_NIMA_1:	return "STV0367_TDA18212_NIMA_1";
348c2ecf20Sopenharmony_ci	case STV0367_TDA18212_NIMA_2:	return "STV0367_TDA18212_NIMA_2";
358c2ecf20Sopenharmony_ci	case STV0367_TDA18212_NIMB_1:	return "STV0367_TDA18212_NIMB_1";
368c2ecf20Sopenharmony_ci	case STV0367_TDA18212_NIMB_2:	return "STV0367_TDA18212_NIMB_2";
378c2ecf20Sopenharmony_ci	case STV0903_6110_LNB24_NIMA:	return "STV0903_6110_LNB24_NIMA";
388c2ecf20Sopenharmony_ci	case STV0903_6110_LNB24_NIMB:	return "STV0903_6110_LNB24_NIMB";
398c2ecf20Sopenharmony_ci	default:			return "unknown dvb frontend card";
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic struct stv090x_config stv090x_config = {
448c2ecf20Sopenharmony_ci	.device                 = STV0903,
458c2ecf20Sopenharmony_ci	.demod_mode             = STV090x_SINGLE,
468c2ecf20Sopenharmony_ci	.clk_mode               = STV090x_CLK_EXT,
478c2ecf20Sopenharmony_ci	.xtal                   = 16000000,
488c2ecf20Sopenharmony_ci	.address                = 0x69,
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	.ts1_mode               = STV090x_TSMODE_SERIAL_CONTINUOUS,
518c2ecf20Sopenharmony_ci	.ts2_mode               = STV090x_TSMODE_SERIAL_CONTINUOUS,
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	.repeater_level         = STV090x_RPTLEVEL_64,
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	.tuner_init             = NULL,
568c2ecf20Sopenharmony_ci	.tuner_set_mode         = NULL,
578c2ecf20Sopenharmony_ci	.tuner_set_frequency    = NULL,
588c2ecf20Sopenharmony_ci	.tuner_get_frequency    = NULL,
598c2ecf20Sopenharmony_ci	.tuner_set_bandwidth    = NULL,
608c2ecf20Sopenharmony_ci	.tuner_get_bandwidth    = NULL,
618c2ecf20Sopenharmony_ci	.tuner_set_bbgain       = NULL,
628c2ecf20Sopenharmony_ci	.tuner_get_bbgain       = NULL,
638c2ecf20Sopenharmony_ci	.tuner_set_refclk       = NULL,
648c2ecf20Sopenharmony_ci	.tuner_get_status       = NULL,
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic struct stv6110x_config stv6110x_config = {
688c2ecf20Sopenharmony_ci	.addr                   = 0x60,
698c2ecf20Sopenharmony_ci	.refclk                 = 16000000,
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#define NIMA 0
738c2ecf20Sopenharmony_ci#define NIMB 1
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic struct stv0367_config stv0367_tda18212_config[] = {
768c2ecf20Sopenharmony_ci	{
778c2ecf20Sopenharmony_ci		.demod_address = 0x1c,
788c2ecf20Sopenharmony_ci		.xtal = 16000000,
798c2ecf20Sopenharmony_ci		.if_khz = 4500,
808c2ecf20Sopenharmony_ci		.if_iq_mode = FE_TER_NORMAL_IF_TUNER,
818c2ecf20Sopenharmony_ci		.ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
828c2ecf20Sopenharmony_ci		.clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
838c2ecf20Sopenharmony_ci	}, {
848c2ecf20Sopenharmony_ci		.demod_address = 0x1d,
858c2ecf20Sopenharmony_ci		.xtal = 16000000,
868c2ecf20Sopenharmony_ci		.if_khz = 4500,
878c2ecf20Sopenharmony_ci		.if_iq_mode = FE_TER_NORMAL_IF_TUNER,
888c2ecf20Sopenharmony_ci		.ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
898c2ecf20Sopenharmony_ci		.clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
908c2ecf20Sopenharmony_ci	}, {
918c2ecf20Sopenharmony_ci		.demod_address = 0x1e,
928c2ecf20Sopenharmony_ci		.xtal = 16000000,
938c2ecf20Sopenharmony_ci		.if_khz = 4500,
948c2ecf20Sopenharmony_ci		.if_iq_mode = FE_TER_NORMAL_IF_TUNER,
958c2ecf20Sopenharmony_ci		.ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
968c2ecf20Sopenharmony_ci		.clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
978c2ecf20Sopenharmony_ci	},
988c2ecf20Sopenharmony_ci};
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic struct tda18212_config tda18212_conf = {
1018c2ecf20Sopenharmony_ci	.if_dvbt_6 = 4150,
1028c2ecf20Sopenharmony_ci	.if_dvbt_7 = 4150,
1038c2ecf20Sopenharmony_ci	.if_dvbt_8 = 4500,
1048c2ecf20Sopenharmony_ci	.if_dvbc = 5000,
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ciint c8sectpfe_frontend_attach(struct dvb_frontend **fe,
1088c2ecf20Sopenharmony_ci		struct c8sectpfe *c8sectpfe,
1098c2ecf20Sopenharmony_ci		struct channel_info *tsin, int chan_num)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct tda18212_config *tda18212;
1128c2ecf20Sopenharmony_ci	const struct stv6110x_devctl *fe2;
1138c2ecf20Sopenharmony_ci	struct i2c_client *client;
1148c2ecf20Sopenharmony_ci	struct i2c_board_info tda18212_info = {
1158c2ecf20Sopenharmony_ci		.type = "tda18212",
1168c2ecf20Sopenharmony_ci		.addr = 0x60,
1178c2ecf20Sopenharmony_ci	};
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	if (!tsin)
1208c2ecf20Sopenharmony_ci		return -EINVAL;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	switch (tsin->dvb_card) {
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	case STV0367_TDA18212_NIMA_1:
1258c2ecf20Sopenharmony_ci	case STV0367_TDA18212_NIMA_2:
1268c2ecf20Sopenharmony_ci	case STV0367_TDA18212_NIMB_1:
1278c2ecf20Sopenharmony_ci	case STV0367_TDA18212_NIMB_2:
1288c2ecf20Sopenharmony_ci		if (tsin->dvb_card == STV0367_TDA18212_NIMA_1)
1298c2ecf20Sopenharmony_ci			*fe = dvb_attach(stv0367ter_attach,
1308c2ecf20Sopenharmony_ci				 &stv0367_tda18212_config[0],
1318c2ecf20Sopenharmony_ci					tsin->i2c_adapter);
1328c2ecf20Sopenharmony_ci		else if (tsin->dvb_card == STV0367_TDA18212_NIMB_1)
1338c2ecf20Sopenharmony_ci			*fe = dvb_attach(stv0367ter_attach,
1348c2ecf20Sopenharmony_ci				 &stv0367_tda18212_config[1],
1358c2ecf20Sopenharmony_ci					tsin->i2c_adapter);
1368c2ecf20Sopenharmony_ci		else
1378c2ecf20Sopenharmony_ci			*fe = dvb_attach(stv0367ter_attach,
1388c2ecf20Sopenharmony_ci				 &stv0367_tda18212_config[2],
1398c2ecf20Sopenharmony_ci					tsin->i2c_adapter);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		if (!*fe) {
1428c2ecf20Sopenharmony_ci			dev_err(c8sectpfe->device,
1438c2ecf20Sopenharmony_ci				"%s: stv0367ter_attach failed for NIM card %s\n"
1448c2ecf20Sopenharmony_ci				, __func__, dvb_card_str(tsin->dvb_card));
1458c2ecf20Sopenharmony_ci			return -ENODEV;
1468c2ecf20Sopenharmony_ci		}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		/*
1498c2ecf20Sopenharmony_ci		 * init the demod so that i2c gate_ctrl
1508c2ecf20Sopenharmony_ci		 * to the tuner works correctly
1518c2ecf20Sopenharmony_ci		 */
1528c2ecf20Sopenharmony_ci		(*fe)->ops.init(*fe);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		/* Allocate the tda18212 structure */
1558c2ecf20Sopenharmony_ci		tda18212 = devm_kzalloc(c8sectpfe->device,
1568c2ecf20Sopenharmony_ci					sizeof(struct tda18212_config),
1578c2ecf20Sopenharmony_ci					GFP_KERNEL);
1588c2ecf20Sopenharmony_ci		if (!tda18212) {
1598c2ecf20Sopenharmony_ci			dev_err(c8sectpfe->device,
1608c2ecf20Sopenharmony_ci				"%s: devm_kzalloc failed\n", __func__);
1618c2ecf20Sopenharmony_ci			return -ENOMEM;
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci		memcpy(tda18212, &tda18212_conf,
1658c2ecf20Sopenharmony_ci			sizeof(struct tda18212_config));
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		tda18212->fe = (*fe);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		tda18212_info.platform_data = tda18212;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci		/* attach tuner */
1728c2ecf20Sopenharmony_ci		request_module("tda18212");
1738c2ecf20Sopenharmony_ci		client = i2c_new_client_device(tsin->i2c_adapter,
1748c2ecf20Sopenharmony_ci					       &tda18212_info);
1758c2ecf20Sopenharmony_ci		if (!i2c_client_has_driver(client)) {
1768c2ecf20Sopenharmony_ci			dvb_frontend_detach(*fe);
1778c2ecf20Sopenharmony_ci			return -ENODEV;
1788c2ecf20Sopenharmony_ci		}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci		if (!try_module_get(client->dev.driver->owner)) {
1818c2ecf20Sopenharmony_ci			i2c_unregister_device(client);
1828c2ecf20Sopenharmony_ci			dvb_frontend_detach(*fe);
1838c2ecf20Sopenharmony_ci			return -ENODEV;
1848c2ecf20Sopenharmony_ci		}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		tsin->i2c_client = client;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci		break;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	case STV0903_6110_LNB24_NIMA:
1918c2ecf20Sopenharmony_ci		*fe = dvb_attach(stv090x_attach,	&stv090x_config,
1928c2ecf20Sopenharmony_ci				tsin->i2c_adapter, STV090x_DEMODULATOR_0);
1938c2ecf20Sopenharmony_ci		if (!*fe) {
1948c2ecf20Sopenharmony_ci			dev_err(c8sectpfe->device, "%s: stv090x_attach failed\n"
1958c2ecf20Sopenharmony_ci				"\tfor NIM card %s\n",
1968c2ecf20Sopenharmony_ci				__func__, dvb_card_str(tsin->dvb_card));
1978c2ecf20Sopenharmony_ci			return -ENODEV;
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		fe2 = dvb_attach(stv6110x_attach, *fe,
2018c2ecf20Sopenharmony_ci					&stv6110x_config, tsin->i2c_adapter);
2028c2ecf20Sopenharmony_ci		if (!fe2) {
2038c2ecf20Sopenharmony_ci			dev_err(c8sectpfe->device,
2048c2ecf20Sopenharmony_ci				"%s: stv6110x_attach failed for NIM card %s\n"
2058c2ecf20Sopenharmony_ci				, __func__, dvb_card_str(tsin->dvb_card));
2068c2ecf20Sopenharmony_ci			return -ENODEV;
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		stv090x_config.tuner_init = fe2->tuner_init;
2108c2ecf20Sopenharmony_ci		stv090x_config.tuner_set_mode = fe2->tuner_set_mode;
2118c2ecf20Sopenharmony_ci		stv090x_config.tuner_set_frequency = fe2->tuner_set_frequency;
2128c2ecf20Sopenharmony_ci		stv090x_config.tuner_get_frequency = fe2->tuner_get_frequency;
2138c2ecf20Sopenharmony_ci		stv090x_config.tuner_set_bandwidth = fe2->tuner_set_bandwidth;
2148c2ecf20Sopenharmony_ci		stv090x_config.tuner_get_bandwidth = fe2->tuner_get_bandwidth;
2158c2ecf20Sopenharmony_ci		stv090x_config.tuner_set_bbgain = fe2->tuner_set_bbgain;
2168c2ecf20Sopenharmony_ci		stv090x_config.tuner_get_bbgain = fe2->tuner_get_bbgain;
2178c2ecf20Sopenharmony_ci		stv090x_config.tuner_set_refclk = fe2->tuner_set_refclk;
2188c2ecf20Sopenharmony_ci		stv090x_config.tuner_get_status = fe2->tuner_get_status;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		dvb_attach(lnbh24_attach, *fe, tsin->i2c_adapter, 0, 0, 0x9);
2218c2ecf20Sopenharmony_ci		break;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	default:
2248c2ecf20Sopenharmony_ci		dev_err(c8sectpfe->device,
2258c2ecf20Sopenharmony_ci			"%s: DVB frontend card %s not yet supported\n",
2268c2ecf20Sopenharmony_ci			__func__, dvb_card_str(tsin->dvb_card));
2278c2ecf20Sopenharmony_ci		return -ENODEV;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	(*fe)->id = chan_num;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	dev_info(c8sectpfe->device,
2338c2ecf20Sopenharmony_ci			"DVB frontend card %s successfully attached",
2348c2ecf20Sopenharmony_ci			dvb_card_str(tsin->dvb_card));
2358c2ecf20Sopenharmony_ci	return 0;
2368c2ecf20Sopenharmony_ci}
237