18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * FireDTV driver (formerly known as FireSAT)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/string.h>
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "firedtv.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int fdtv_dvb_init(struct dvb_frontend *fe)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct firedtv *fdtv = fe->sec_priv;
228c2ecf20Sopenharmony_ci	int err;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	/* FIXME - allocate free channel at IRM */
258c2ecf20Sopenharmony_ci	fdtv->isochannel = fdtv->adapter.num;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	err = cmp_establish_pp_connection(fdtv, fdtv->subunit,
288c2ecf20Sopenharmony_ci					  fdtv->isochannel);
298c2ecf20Sopenharmony_ci	if (err) {
308c2ecf20Sopenharmony_ci		dev_err(fdtv->device,
318c2ecf20Sopenharmony_ci			"could not establish point to point connection\n");
328c2ecf20Sopenharmony_ci		return err;
338c2ecf20Sopenharmony_ci	}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return fdtv_start_iso(fdtv);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int fdtv_sleep(struct dvb_frontend *fe)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct firedtv *fdtv = fe->sec_priv;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	fdtv_stop_iso(fdtv);
438c2ecf20Sopenharmony_ci	cmp_break_pp_connection(fdtv, fdtv->subunit, fdtv->isochannel);
448c2ecf20Sopenharmony_ci	fdtv->isochannel = -1;
458c2ecf20Sopenharmony_ci	return 0;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define LNBCONTROL_DONTCARE 0xff
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int fdtv_diseqc_send_master_cmd(struct dvb_frontend *fe,
518c2ecf20Sopenharmony_ci				       struct dvb_diseqc_master_cmd *cmd)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct firedtv *fdtv = fe->sec_priv;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return avc_lnb_control(fdtv, LNBCONTROL_DONTCARE, LNBCONTROL_DONTCARE,
568c2ecf20Sopenharmony_ci			       LNBCONTROL_DONTCARE, 1, cmd);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int fdtv_diseqc_send_burst(struct dvb_frontend *fe,
608c2ecf20Sopenharmony_ci				  enum fe_sec_mini_cmd minicmd)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	return 0;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int fdtv_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct firedtv *fdtv = fe->sec_priv;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	fdtv->tone = tone;
708c2ecf20Sopenharmony_ci	return 0;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic int fdtv_set_voltage(struct dvb_frontend *fe,
748c2ecf20Sopenharmony_ci			    enum fe_sec_voltage voltage)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct firedtv *fdtv = fe->sec_priv;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	fdtv->voltage = voltage;
798c2ecf20Sopenharmony_ci	return 0;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int fdtv_read_status(struct dvb_frontend *fe, enum fe_status *status)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct firedtv *fdtv = fe->sec_priv;
858c2ecf20Sopenharmony_ci	struct firedtv_tuner_status stat;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (avc_tuner_status(fdtv, &stat))
888c2ecf20Sopenharmony_ci		return -EINVAL;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (stat.no_rf)
918c2ecf20Sopenharmony_ci		*status = 0;
928c2ecf20Sopenharmony_ci	else
938c2ecf20Sopenharmony_ci		*status = FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC |
948c2ecf20Sopenharmony_ci			  FE_HAS_CARRIER | FE_HAS_LOCK;
958c2ecf20Sopenharmony_ci	return 0;
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic int fdtv_read_ber(struct dvb_frontend *fe, u32 *ber)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct firedtv *fdtv = fe->sec_priv;
1018c2ecf20Sopenharmony_ci	struct firedtv_tuner_status stat;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (avc_tuner_status(fdtv, &stat))
1048c2ecf20Sopenharmony_ci		return -EINVAL;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	*ber = stat.ber;
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int fdtv_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct firedtv *fdtv = fe->sec_priv;
1138c2ecf20Sopenharmony_ci	struct firedtv_tuner_status stat;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (avc_tuner_status(fdtv, &stat))
1168c2ecf20Sopenharmony_ci		return -EINVAL;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	*strength = stat.signal_strength << 8;
1198c2ecf20Sopenharmony_ci	return 0;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic int fdtv_read_snr(struct dvb_frontend *fe, u16 *snr)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct firedtv *fdtv = fe->sec_priv;
1258c2ecf20Sopenharmony_ci	struct firedtv_tuner_status stat;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (avc_tuner_status(fdtv, &stat))
1288c2ecf20Sopenharmony_ci		return -EINVAL;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* C/N[dB] = -10 * log10(snr / 65535) */
1318c2ecf20Sopenharmony_ci	*snr = stat.carrier_noise_ratio * 257;
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int fdtv_read_uncorrected_blocks(struct dvb_frontend *fe, u32 *ucblocks)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int fdtv_set_frontend(struct dvb_frontend *fe)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *p = &fe->dtv_property_cache;
1438c2ecf20Sopenharmony_ci	struct firedtv *fdtv = fe->sec_priv;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return avc_tuner_dsd(fdtv, p);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_civoid fdtv_frontend_init(struct firedtv *fdtv, const char *name)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct dvb_frontend_ops *ops = &fdtv->fe.ops;
1518c2ecf20Sopenharmony_ci	struct dvb_frontend_internal_info *fi = &ops->info;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ops->init			= fdtv_dvb_init;
1548c2ecf20Sopenharmony_ci	ops->sleep			= fdtv_sleep;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ops->set_frontend		= fdtv_set_frontend;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	ops->read_status		= fdtv_read_status;
1598c2ecf20Sopenharmony_ci	ops->read_ber			= fdtv_read_ber;
1608c2ecf20Sopenharmony_ci	ops->read_signal_strength	= fdtv_read_signal_strength;
1618c2ecf20Sopenharmony_ci	ops->read_snr			= fdtv_read_snr;
1628c2ecf20Sopenharmony_ci	ops->read_ucblocks		= fdtv_read_uncorrected_blocks;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	ops->diseqc_send_master_cmd	= fdtv_diseqc_send_master_cmd;
1658c2ecf20Sopenharmony_ci	ops->diseqc_send_burst		= fdtv_diseqc_send_burst;
1668c2ecf20Sopenharmony_ci	ops->set_tone			= fdtv_set_tone;
1678c2ecf20Sopenharmony_ci	ops->set_voltage		= fdtv_set_voltage;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	switch (fdtv->type) {
1708c2ecf20Sopenharmony_ci	case FIREDTV_DVB_S:
1718c2ecf20Sopenharmony_ci		ops->delsys[0]		= SYS_DVBS;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		fi->frequency_min_hz	=   950 * MHz;
1748c2ecf20Sopenharmony_ci		fi->frequency_max_hz	=  2150 * MHz;
1758c2ecf20Sopenharmony_ci		fi->frequency_stepsize_hz = 125 * kHz;
1768c2ecf20Sopenharmony_ci		fi->symbol_rate_min	= 1000000;
1778c2ecf20Sopenharmony_ci		fi->symbol_rate_max	= 40000000;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		fi->caps		= FE_CAN_INVERSION_AUTO |
1808c2ecf20Sopenharmony_ci					  FE_CAN_FEC_1_2	|
1818c2ecf20Sopenharmony_ci					  FE_CAN_FEC_2_3	|
1828c2ecf20Sopenharmony_ci					  FE_CAN_FEC_3_4	|
1838c2ecf20Sopenharmony_ci					  FE_CAN_FEC_5_6	|
1848c2ecf20Sopenharmony_ci					  FE_CAN_FEC_7_8	|
1858c2ecf20Sopenharmony_ci					  FE_CAN_FEC_AUTO	|
1868c2ecf20Sopenharmony_ci					  FE_CAN_QPSK;
1878c2ecf20Sopenharmony_ci		break;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	case FIREDTV_DVB_S2:
1908c2ecf20Sopenharmony_ci		ops->delsys[0]		= SYS_DVBS;
1918c2ecf20Sopenharmony_ci		ops->delsys[1]		= SYS_DVBS2;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		fi->frequency_min_hz	=   950 * MHz;
1948c2ecf20Sopenharmony_ci		fi->frequency_max_hz	=  2150 * MHz;
1958c2ecf20Sopenharmony_ci		fi->frequency_stepsize_hz = 125 * kHz;
1968c2ecf20Sopenharmony_ci		fi->symbol_rate_min	= 1000000;
1978c2ecf20Sopenharmony_ci		fi->symbol_rate_max	= 40000000;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		fi->caps		= FE_CAN_INVERSION_AUTO |
2008c2ecf20Sopenharmony_ci					  FE_CAN_FEC_1_2        |
2018c2ecf20Sopenharmony_ci					  FE_CAN_FEC_2_3        |
2028c2ecf20Sopenharmony_ci					  FE_CAN_FEC_3_4        |
2038c2ecf20Sopenharmony_ci					  FE_CAN_FEC_5_6        |
2048c2ecf20Sopenharmony_ci					  FE_CAN_FEC_7_8        |
2058c2ecf20Sopenharmony_ci					  FE_CAN_FEC_AUTO       |
2068c2ecf20Sopenharmony_ci					  FE_CAN_QPSK           |
2078c2ecf20Sopenharmony_ci					  FE_CAN_2G_MODULATION;
2088c2ecf20Sopenharmony_ci		break;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	case FIREDTV_DVB_C:
2118c2ecf20Sopenharmony_ci		ops->delsys[0]		= SYS_DVBC_ANNEX_A;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci		fi->frequency_min_hz	=      47 * MHz;
2148c2ecf20Sopenharmony_ci		fi->frequency_max_hz	=     866 * MHz;
2158c2ecf20Sopenharmony_ci		fi->frequency_stepsize_hz = 62500;
2168c2ecf20Sopenharmony_ci		fi->symbol_rate_min	= 870000;
2178c2ecf20Sopenharmony_ci		fi->symbol_rate_max	= 6900000;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		fi->caps		= FE_CAN_INVERSION_AUTO |
2208c2ecf20Sopenharmony_ci					  FE_CAN_QAM_16		|
2218c2ecf20Sopenharmony_ci					  FE_CAN_QAM_32		|
2228c2ecf20Sopenharmony_ci					  FE_CAN_QAM_64		|
2238c2ecf20Sopenharmony_ci					  FE_CAN_QAM_128	|
2248c2ecf20Sopenharmony_ci					  FE_CAN_QAM_256	|
2258c2ecf20Sopenharmony_ci					  FE_CAN_QAM_AUTO;
2268c2ecf20Sopenharmony_ci		break;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	case FIREDTV_DVB_T:
2298c2ecf20Sopenharmony_ci		ops->delsys[0]		= SYS_DVBT;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		fi->frequency_min_hz	=  49 * MHz;
2328c2ecf20Sopenharmony_ci		fi->frequency_max_hz	= 861 * MHz;
2338c2ecf20Sopenharmony_ci		fi->frequency_stepsize_hz = 62500;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		fi->caps		= FE_CAN_INVERSION_AUTO		|
2368c2ecf20Sopenharmony_ci					  FE_CAN_FEC_2_3		|
2378c2ecf20Sopenharmony_ci					  FE_CAN_TRANSMISSION_MODE_AUTO |
2388c2ecf20Sopenharmony_ci					  FE_CAN_GUARD_INTERVAL_AUTO	|
2398c2ecf20Sopenharmony_ci					  FE_CAN_HIERARCHY_AUTO;
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	default:
2438c2ecf20Sopenharmony_ci		dev_err(fdtv->device, "no frontend for model type %d\n",
2448c2ecf20Sopenharmony_ci			fdtv->type);
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci	strscpy(fi->name, name, sizeof(fi->name));
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	fdtv->fe.dvb = &fdtv->adapter;
2498c2ecf20Sopenharmony_ci	fdtv->fe.sec_priv = fdtv;
2508c2ecf20Sopenharmony_ci}
251