18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Frontend driver for the GENPIX 8pks/qpsk/DCII USB2.0 DVB-S module
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2006,2007 Alan Nisota (alannisota@gmail.com)
68c2ecf20Sopenharmony_ci * Copyright (C) 2006,2007 Genpix Electronics (genpix@genpix-electronics.com)
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Thanks to GENPIX for the sample code used to implement this module.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This module is based off the vp7045 and vp702x modules
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "gp8psk-fe.h"
168c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic int debug;
198c2ecf20Sopenharmony_cimodule_param(debug, int, 0644);
208c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define dprintk(fmt, arg...) do {					\
238c2ecf20Sopenharmony_ci	if (debug)							\
248c2ecf20Sopenharmony_ci		printk(KERN_DEBUG pr_fmt("%s: " fmt),			\
258c2ecf20Sopenharmony_ci		       __func__, ##arg);				\
268c2ecf20Sopenharmony_ci} while (0)
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistruct gp8psk_fe_state {
298c2ecf20Sopenharmony_ci	struct dvb_frontend fe;
308c2ecf20Sopenharmony_ci	void *priv;
318c2ecf20Sopenharmony_ci	const struct gp8psk_fe_ops *ops;
328c2ecf20Sopenharmony_ci	bool is_rev1;
338c2ecf20Sopenharmony_ci	u8 lock;
348c2ecf20Sopenharmony_ci	u16 snr;
358c2ecf20Sopenharmony_ci	unsigned long next_status_check;
368c2ecf20Sopenharmony_ci	unsigned long status_check_interval;
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int gp8psk_tuned_to_DCII(struct dvb_frontend *fe)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
428c2ecf20Sopenharmony_ci	u8 status;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	st->ops->in(st->priv, GET_8PSK_CONFIG, 0, 0, &status, 1);
458c2ecf20Sopenharmony_ci	return status & bmDCtuned;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int gp8psk_set_tuner_mode(struct dvb_frontend *fe, int mode)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return st->ops->out(st->priv, SET_8PSK_CONFIG, mode, 0, NULL, 0);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int gp8psk_fe_update_status(struct gp8psk_fe_state *st)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	u8 buf[6];
588c2ecf20Sopenharmony_ci	if (time_after(jiffies,st->next_status_check)) {
598c2ecf20Sopenharmony_ci		st->ops->in(st->priv, GET_SIGNAL_LOCK, 0, 0, &st->lock, 1);
608c2ecf20Sopenharmony_ci		st->ops->in(st->priv, GET_SIGNAL_STRENGTH, 0, 0, buf, 6);
618c2ecf20Sopenharmony_ci		st->snr = (buf[1]) << 8 | buf[0];
628c2ecf20Sopenharmony_ci		st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000;
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci	return 0;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int gp8psk_fe_read_status(struct dvb_frontend *fe,
688c2ecf20Sopenharmony_ci				 enum fe_status *status)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
718c2ecf20Sopenharmony_ci	gp8psk_fe_update_status(st);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (st->lock)
748c2ecf20Sopenharmony_ci		*status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER;
758c2ecf20Sopenharmony_ci	else
768c2ecf20Sopenharmony_ci		*status = 0;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (*status & FE_HAS_LOCK)
798c2ecf20Sopenharmony_ci		st->status_check_interval = 1000;
808c2ecf20Sopenharmony_ci	else
818c2ecf20Sopenharmony_ci		st->status_check_interval = 100;
828c2ecf20Sopenharmony_ci	return 0;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* not supported by this Frontend */
868c2ecf20Sopenharmony_cistatic int gp8psk_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	(void) fe;
898c2ecf20Sopenharmony_ci	*ber = 0;
908c2ecf20Sopenharmony_ci	return 0;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* not supported by this Frontend */
948c2ecf20Sopenharmony_cistatic int gp8psk_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	(void) fe;
978c2ecf20Sopenharmony_ci	*unc = 0;
988c2ecf20Sopenharmony_ci	return 0;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int gp8psk_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
1048c2ecf20Sopenharmony_ci	gp8psk_fe_update_status(st);
1058c2ecf20Sopenharmony_ci	/* snr is reported in dBu*256 */
1068c2ecf20Sopenharmony_ci	*snr = st->snr;
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistatic int gp8psk_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
1138c2ecf20Sopenharmony_ci	gp8psk_fe_update_status(st);
1148c2ecf20Sopenharmony_ci	/* snr is reported in dBu*256 */
1158c2ecf20Sopenharmony_ci	/* snr / 38.4 ~= 100% strength */
1168c2ecf20Sopenharmony_ci	/* snr * 17 returns 100% strength as 65535 */
1178c2ecf20Sopenharmony_ci	if (st->snr > 0xf00)
1188c2ecf20Sopenharmony_ci		*strength = 0xffff;
1198c2ecf20Sopenharmony_ci	else
1208c2ecf20Sopenharmony_ci		*strength = (st->snr << 4) + st->snr; /* snr*17 */
1218c2ecf20Sopenharmony_ci	return 0;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int gp8psk_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	tune->min_delay_ms = 800;
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int gp8psk_fe_set_frontend(struct dvb_frontend *fe)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
1338c2ecf20Sopenharmony_ci	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
1348c2ecf20Sopenharmony_ci	u8 cmd[10];
1358c2ecf20Sopenharmony_ci	u32 freq = c->frequency * 1000;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	dprintk("%s()\n", __func__);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	cmd[4] = freq         & 0xff;
1408c2ecf20Sopenharmony_ci	cmd[5] = (freq >> 8)  & 0xff;
1418c2ecf20Sopenharmony_ci	cmd[6] = (freq >> 16) & 0xff;
1428c2ecf20Sopenharmony_ci	cmd[7] = (freq >> 24) & 0xff;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	/* backwards compatibility: DVB-S + 8-PSK were used for Turbo-FEC */
1458c2ecf20Sopenharmony_ci	if (c->delivery_system == SYS_DVBS && c->modulation == PSK_8)
1468c2ecf20Sopenharmony_ci		c->delivery_system = SYS_TURBO;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	switch (c->delivery_system) {
1498c2ecf20Sopenharmony_ci	case SYS_DVBS:
1508c2ecf20Sopenharmony_ci		if (c->modulation != QPSK) {
1518c2ecf20Sopenharmony_ci			dprintk("%s: unsupported modulation selected (%d)\n",
1528c2ecf20Sopenharmony_ci				__func__, c->modulation);
1538c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci		c->fec_inner = FEC_AUTO;
1568c2ecf20Sopenharmony_ci		break;
1578c2ecf20Sopenharmony_ci	case SYS_DVBS2: /* kept for backwards compatibility */
1588c2ecf20Sopenharmony_ci		dprintk("%s: DVB-S2 delivery system selected\n", __func__);
1598c2ecf20Sopenharmony_ci		break;
1608c2ecf20Sopenharmony_ci	case SYS_TURBO:
1618c2ecf20Sopenharmony_ci		dprintk("%s: Turbo-FEC delivery system selected\n", __func__);
1628c2ecf20Sopenharmony_ci		break;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	default:
1658c2ecf20Sopenharmony_ci		dprintk("%s: unsupported delivery system selected (%d)\n",
1668c2ecf20Sopenharmony_ci			__func__, c->delivery_system);
1678c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	cmd[0] =  c->symbol_rate        & 0xff;
1718c2ecf20Sopenharmony_ci	cmd[1] = (c->symbol_rate >>  8) & 0xff;
1728c2ecf20Sopenharmony_ci	cmd[2] = (c->symbol_rate >> 16) & 0xff;
1738c2ecf20Sopenharmony_ci	cmd[3] = (c->symbol_rate >> 24) & 0xff;
1748c2ecf20Sopenharmony_ci	switch (c->modulation) {
1758c2ecf20Sopenharmony_ci	case QPSK:
1768c2ecf20Sopenharmony_ci		if (st->is_rev1)
1778c2ecf20Sopenharmony_ci			if (gp8psk_tuned_to_DCII(fe))
1788c2ecf20Sopenharmony_ci				st->ops->reload(st->priv);
1798c2ecf20Sopenharmony_ci		switch (c->fec_inner) {
1808c2ecf20Sopenharmony_ci		case FEC_1_2:
1818c2ecf20Sopenharmony_ci			cmd[9] = 0; break;
1828c2ecf20Sopenharmony_ci		case FEC_2_3:
1838c2ecf20Sopenharmony_ci			cmd[9] = 1; break;
1848c2ecf20Sopenharmony_ci		case FEC_3_4:
1858c2ecf20Sopenharmony_ci			cmd[9] = 2; break;
1868c2ecf20Sopenharmony_ci		case FEC_5_6:
1878c2ecf20Sopenharmony_ci			cmd[9] = 3; break;
1888c2ecf20Sopenharmony_ci		case FEC_7_8:
1898c2ecf20Sopenharmony_ci			cmd[9] = 4; break;
1908c2ecf20Sopenharmony_ci		case FEC_AUTO:
1918c2ecf20Sopenharmony_ci			cmd[9] = 5; break;
1928c2ecf20Sopenharmony_ci		default:
1938c2ecf20Sopenharmony_ci			cmd[9] = 5; break;
1948c2ecf20Sopenharmony_ci		}
1958c2ecf20Sopenharmony_ci		if (c->delivery_system == SYS_TURBO)
1968c2ecf20Sopenharmony_ci			cmd[8] = ADV_MOD_TURBO_QPSK;
1978c2ecf20Sopenharmony_ci		else
1988c2ecf20Sopenharmony_ci			cmd[8] = ADV_MOD_DVB_QPSK;
1998c2ecf20Sopenharmony_ci		break;
2008c2ecf20Sopenharmony_ci	case PSK_8: /* PSK_8 is for compatibility with DN */
2018c2ecf20Sopenharmony_ci		cmd[8] = ADV_MOD_TURBO_8PSK;
2028c2ecf20Sopenharmony_ci		switch (c->fec_inner) {
2038c2ecf20Sopenharmony_ci		case FEC_2_3:
2048c2ecf20Sopenharmony_ci			cmd[9] = 0; break;
2058c2ecf20Sopenharmony_ci		case FEC_3_4:
2068c2ecf20Sopenharmony_ci			cmd[9] = 1; break;
2078c2ecf20Sopenharmony_ci		case FEC_3_5:
2088c2ecf20Sopenharmony_ci			cmd[9] = 2; break;
2098c2ecf20Sopenharmony_ci		case FEC_5_6:
2108c2ecf20Sopenharmony_ci			cmd[9] = 3; break;
2118c2ecf20Sopenharmony_ci		case FEC_8_9:
2128c2ecf20Sopenharmony_ci			cmd[9] = 4; break;
2138c2ecf20Sopenharmony_ci		default:
2148c2ecf20Sopenharmony_ci			cmd[9] = 0; break;
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci		break;
2178c2ecf20Sopenharmony_ci	case QAM_16: /* QAM_16 is for compatibility with DN */
2188c2ecf20Sopenharmony_ci		cmd[8] = ADV_MOD_TURBO_16QAM;
2198c2ecf20Sopenharmony_ci		cmd[9] = 0;
2208c2ecf20Sopenharmony_ci		break;
2218c2ecf20Sopenharmony_ci	default: /* Unknown modulation */
2228c2ecf20Sopenharmony_ci		dprintk("%s: unsupported modulation selected (%d)\n",
2238c2ecf20Sopenharmony_ci			__func__, c->modulation);
2248c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (st->is_rev1)
2288c2ecf20Sopenharmony_ci		gp8psk_set_tuner_mode(fe, 0);
2298c2ecf20Sopenharmony_ci	st->ops->out(st->priv, TUNE_8PSK, 0, 0, cmd, 10);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	st->lock = 0;
2328c2ecf20Sopenharmony_ci	st->next_status_check = jiffies;
2338c2ecf20Sopenharmony_ci	st->status_check_interval = 200;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return 0;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic int gp8psk_fe_send_diseqc_msg (struct dvb_frontend* fe,
2398c2ecf20Sopenharmony_ci				    struct dvb_diseqc_master_cmd *m)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, m->msg[0], 0,
2468c2ecf20Sopenharmony_ci			m->msg, m->msg_len)) {
2478c2ecf20Sopenharmony_ci		return -EINVAL;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	return 0;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic int gp8psk_fe_send_diseqc_burst(struct dvb_frontend *fe,
2538c2ecf20Sopenharmony_ci				       enum fe_sec_mini_cmd burst)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
2568c2ecf20Sopenharmony_ci	u8 cmd;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	dprintk("%s\n", __func__);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* These commands are certainly wrong */
2618c2ecf20Sopenharmony_ci	cmd = (burst == SEC_MINI_A) ? 0x00 : 0x01;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	if (st->ops->out(st->priv, SEND_DISEQC_COMMAND, cmd, 0,
2648c2ecf20Sopenharmony_ci			&cmd, 0)) {
2658c2ecf20Sopenharmony_ci		return -EINVAL;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci	return 0;
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic int gp8psk_fe_set_tone(struct dvb_frontend *fe,
2718c2ecf20Sopenharmony_ci			      enum fe_sec_tone_mode tone)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (st->ops->out(st->priv, SET_22KHZ_TONE,
2768c2ecf20Sopenharmony_ci			 (tone == SEC_TONE_ON), 0, NULL, 0)) {
2778c2ecf20Sopenharmony_ci		return -EINVAL;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci	return 0;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic int gp8psk_fe_set_voltage(struct dvb_frontend *fe,
2838c2ecf20Sopenharmony_ci				 enum fe_sec_voltage voltage)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (st->ops->out(st->priv, SET_LNB_VOLTAGE,
2888c2ecf20Sopenharmony_ci			 voltage == SEC_VOLTAGE_18, 0, NULL, 0)) {
2898c2ecf20Sopenharmony_ci		return -EINVAL;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci	return 0;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int gp8psk_fe_enable_high_lnb_voltage(struct dvb_frontend* fe, long onoff)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return st->ops->out(st->priv, USE_EXTRA_VOLT, onoff, 0, NULL, 0);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic int gp8psk_fe_send_legacy_dish_cmd (struct dvb_frontend* fe, unsigned long sw_cmd)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
3048c2ecf20Sopenharmony_ci	u8 cmd = sw_cmd & 0x7f;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (st->ops->out(st->priv, SET_DN_SWITCH, cmd, 0, NULL, 0))
3078c2ecf20Sopenharmony_ci		return -EINVAL;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (st->ops->out(st->priv, SET_LNB_VOLTAGE, !!(sw_cmd & 0x80),
3108c2ecf20Sopenharmony_ci			0, NULL, 0))
3118c2ecf20Sopenharmony_ci		return -EINVAL;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_cistatic void gp8psk_fe_release(struct dvb_frontend* fe)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st = fe->demodulator_priv;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	kfree(st);
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops gp8psk_fe_ops;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistruct dvb_frontend *gp8psk_fe_attach(const struct gp8psk_fe_ops *ops,
3268c2ecf20Sopenharmony_ci				      void *priv, bool is_rev1)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct gp8psk_fe_state *st;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (!ops || !ops->in || !ops->out || !ops->reload) {
3318c2ecf20Sopenharmony_ci		pr_err("Error! gp8psk-fe ops not defined.\n");
3328c2ecf20Sopenharmony_ci		return NULL;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	st = kzalloc(sizeof(struct gp8psk_fe_state), GFP_KERNEL);
3368c2ecf20Sopenharmony_ci	if (!st)
3378c2ecf20Sopenharmony_ci		return NULL;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	memcpy(&st->fe.ops, &gp8psk_fe_ops, sizeof(struct dvb_frontend_ops));
3408c2ecf20Sopenharmony_ci	st->fe.demodulator_priv = st;
3418c2ecf20Sopenharmony_ci	st->ops = ops;
3428c2ecf20Sopenharmony_ci	st->priv = priv;
3438c2ecf20Sopenharmony_ci	st->is_rev1 = is_rev1;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	pr_info("Frontend %sattached\n", is_rev1 ? "revision 1 " : "");
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return &st->fe;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(gp8psk_fe_attach);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops gp8psk_fe_ops = {
3528c2ecf20Sopenharmony_ci	.delsys = { SYS_DVBS },
3538c2ecf20Sopenharmony_ci	.info = {
3548c2ecf20Sopenharmony_ci		.name			= "Genpix DVB-S",
3558c2ecf20Sopenharmony_ci		.frequency_min_hz	=  800 * MHz,
3568c2ecf20Sopenharmony_ci		.frequency_max_hz	= 2250 * MHz,
3578c2ecf20Sopenharmony_ci		.frequency_stepsize_hz	=  100 * kHz,
3588c2ecf20Sopenharmony_ci		.symbol_rate_min        = 1000000,
3598c2ecf20Sopenharmony_ci		.symbol_rate_max        = 45000000,
3608c2ecf20Sopenharmony_ci		.symbol_rate_tolerance  = 500,  /* ppm */
3618c2ecf20Sopenharmony_ci		.caps = FE_CAN_INVERSION_AUTO |
3628c2ecf20Sopenharmony_ci			FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
3638c2ecf20Sopenharmony_ci			FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
3648c2ecf20Sopenharmony_ci			/*
3658c2ecf20Sopenharmony_ci			 * FE_CAN_QAM_16 is for compatibility
3668c2ecf20Sopenharmony_ci			 * (Myth incorrectly detects Turbo-QPSK as plain QAM-16)
3678c2ecf20Sopenharmony_ci			 */
3688c2ecf20Sopenharmony_ci			FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_TURBO_FEC
3698c2ecf20Sopenharmony_ci	},
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	.release = gp8psk_fe_release,
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	.init = NULL,
3748c2ecf20Sopenharmony_ci	.sleep = NULL,
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	.set_frontend = gp8psk_fe_set_frontend,
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	.get_tune_settings = gp8psk_fe_get_tune_settings,
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	.read_status = gp8psk_fe_read_status,
3818c2ecf20Sopenharmony_ci	.read_ber = gp8psk_fe_read_ber,
3828c2ecf20Sopenharmony_ci	.read_signal_strength = gp8psk_fe_read_signal_strength,
3838c2ecf20Sopenharmony_ci	.read_snr = gp8psk_fe_read_snr,
3848c2ecf20Sopenharmony_ci	.read_ucblocks = gp8psk_fe_read_unc_blocks,
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	.diseqc_send_master_cmd = gp8psk_fe_send_diseqc_msg,
3878c2ecf20Sopenharmony_ci	.diseqc_send_burst = gp8psk_fe_send_diseqc_burst,
3888c2ecf20Sopenharmony_ci	.set_tone = gp8psk_fe_set_tone,
3898c2ecf20Sopenharmony_ci	.set_voltage = gp8psk_fe_set_voltage,
3908c2ecf20Sopenharmony_ci	.dishnetwork_send_legacy_command = gp8psk_fe_send_legacy_dish_cmd,
3918c2ecf20Sopenharmony_ci	.enable_high_lnb_voltage = gp8psk_fe_enable_high_lnb_voltage
3928c2ecf20Sopenharmony_ci};
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alan Nisota <alannisota@gamil.com>");
3958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Frontend Driver for Genpix DVB-S");
3968c2ecf20Sopenharmony_ciMODULE_VERSION("1.1");
3978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
398