1a10e763bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2786baecfSMauro Carvalho Chehab /* Frontend part of the Linux driver for the WideView/ Yakumo/ Hama/
3786baecfSMauro Carvalho Chehab  * Typhoon/ Yuan DVB-T USB2.0 receiver.
4786baecfSMauro Carvalho Chehab  *
599e44da7SPatrick Boettcher  * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@posteo.de>
6786baecfSMauro Carvalho Chehab  *
7577a7ad3SMauro Carvalho Chehab  * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information
8786baecfSMauro Carvalho Chehab  */
9786baecfSMauro Carvalho Chehab #include "dtt200u.h"
10786baecfSMauro Carvalho Chehab 
11786baecfSMauro Carvalho Chehab struct dtt200u_fe_state {
12786baecfSMauro Carvalho Chehab 	struct dvb_usb_device *d;
13786baecfSMauro Carvalho Chehab 
140df289a2SMauro Carvalho Chehab 	enum fe_status stat;
15786baecfSMauro Carvalho Chehab 
16786baecfSMauro Carvalho Chehab 	struct dtv_frontend_properties fep;
17786baecfSMauro Carvalho Chehab 	struct dvb_frontend frontend;
1889919b51SMauro Carvalho Chehab 
1989919b51SMauro Carvalho Chehab 	unsigned char data[80];
2089919b51SMauro Carvalho Chehab 	struct mutex data_mutex;
21786baecfSMauro Carvalho Chehab };
22786baecfSMauro Carvalho Chehab 
dtt200u_fe_read_status(struct dvb_frontend * fe,enum fe_status * stat)230df289a2SMauro Carvalho Chehab static int dtt200u_fe_read_status(struct dvb_frontend *fe,
240df289a2SMauro Carvalho Chehab 				  enum fe_status *stat)
25786baecfSMauro Carvalho Chehab {
26786baecfSMauro Carvalho Chehab 	struct dtt200u_fe_state *state = fe->demodulator_priv;
27ba705a62SMauro Carvalho Chehab 	int ret;
28786baecfSMauro Carvalho Chehab 
2989919b51SMauro Carvalho Chehab 	mutex_lock(&state->data_mutex);
3089919b51SMauro Carvalho Chehab 	state->data[0] = GET_TUNE_STATUS;
31786baecfSMauro Carvalho Chehab 
32ba705a62SMauro Carvalho Chehab 	ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 3, 0);
33ba705a62SMauro Carvalho Chehab 	if (ret < 0) {
34ba705a62SMauro Carvalho Chehab 		*stat = 0;
35ba705a62SMauro Carvalho Chehab 		mutex_unlock(&state->data_mutex);
36ba705a62SMauro Carvalho Chehab 		return ret;
37ba705a62SMauro Carvalho Chehab 	}
3889919b51SMauro Carvalho Chehab 
3989919b51SMauro Carvalho Chehab 	switch (state->data[0]) {
40786baecfSMauro Carvalho Chehab 		case 0x01:
41786baecfSMauro Carvalho Chehab 			*stat = FE_HAS_SIGNAL | FE_HAS_CARRIER |
42786baecfSMauro Carvalho Chehab 				FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
43786baecfSMauro Carvalho Chehab 			break;
44786baecfSMauro Carvalho Chehab 		case 0x00: /* pending */
45786baecfSMauro Carvalho Chehab 			*stat = FE_TIMEDOUT; /* during set_frontend */
46786baecfSMauro Carvalho Chehab 			break;
47786baecfSMauro Carvalho Chehab 		default:
48786baecfSMauro Carvalho Chehab 		case 0x02: /* failed */
49786baecfSMauro Carvalho Chehab 			*stat = 0;
50786baecfSMauro Carvalho Chehab 			break;
51786baecfSMauro Carvalho Chehab 	}
5289919b51SMauro Carvalho Chehab 	mutex_unlock(&state->data_mutex);
53786baecfSMauro Carvalho Chehab 	return 0;
54786baecfSMauro Carvalho Chehab }
55786baecfSMauro Carvalho Chehab 
dtt200u_fe_read_ber(struct dvb_frontend * fe,u32 * ber)56786baecfSMauro Carvalho Chehab static int dtt200u_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
57786baecfSMauro Carvalho Chehab {
58786baecfSMauro Carvalho Chehab 	struct dtt200u_fe_state *state = fe->demodulator_priv;
59ba705a62SMauro Carvalho Chehab 	int ret;
6089919b51SMauro Carvalho Chehab 
6189919b51SMauro Carvalho Chehab 	mutex_lock(&state->data_mutex);
6289919b51SMauro Carvalho Chehab 	state->data[0] = GET_VIT_ERR_CNT;
6389919b51SMauro Carvalho Chehab 
64ba705a62SMauro Carvalho Chehab 	ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 3, 0);
65ba705a62SMauro Carvalho Chehab 	if (ret >= 0)
6689919b51SMauro Carvalho Chehab 		*ber = (state->data[0] << 16) | (state->data[1] << 8) | state->data[2];
6789919b51SMauro Carvalho Chehab 
6889919b51SMauro Carvalho Chehab 	mutex_unlock(&state->data_mutex);
69ba705a62SMauro Carvalho Chehab 	return ret;
70786baecfSMauro Carvalho Chehab }
71786baecfSMauro Carvalho Chehab 
dtt200u_fe_read_unc_blocks(struct dvb_frontend * fe,u32 * unc)72786baecfSMauro Carvalho Chehab static int dtt200u_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
73786baecfSMauro Carvalho Chehab {
74786baecfSMauro Carvalho Chehab 	struct dtt200u_fe_state *state = fe->demodulator_priv;
75ba705a62SMauro Carvalho Chehab 	int ret;
76786baecfSMauro Carvalho Chehab 
7789919b51SMauro Carvalho Chehab 	mutex_lock(&state->data_mutex);
7889919b51SMauro Carvalho Chehab 	state->data[0] = GET_RS_UNCOR_BLK_CNT;
7989919b51SMauro Carvalho Chehab 
80ba705a62SMauro Carvalho Chehab 	ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 2, 0);
81ba705a62SMauro Carvalho Chehab 	if (ret >= 0)
82ba705a62SMauro Carvalho Chehab 		*unc = (state->data[0] << 8) | state->data[1];
8389919b51SMauro Carvalho Chehab 
8489919b51SMauro Carvalho Chehab 	mutex_unlock(&state->data_mutex);
8589919b51SMauro Carvalho Chehab 	return ret;
86786baecfSMauro Carvalho Chehab }
87786baecfSMauro Carvalho Chehab 
dtt200u_fe_read_signal_strength(struct dvb_frontend * fe,u16 * strength)88786baecfSMauro Carvalho Chehab static int dtt200u_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
89786baecfSMauro Carvalho Chehab {
90786baecfSMauro Carvalho Chehab 	struct dtt200u_fe_state *state = fe->demodulator_priv;
91ba705a62SMauro Carvalho Chehab 	int ret;
9289919b51SMauro Carvalho Chehab 
9389919b51SMauro Carvalho Chehab 	mutex_lock(&state->data_mutex);
9489919b51SMauro Carvalho Chehab 	state->data[0] = GET_AGC;
9589919b51SMauro Carvalho Chehab 
96ba705a62SMauro Carvalho Chehab 	ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 1, 0);
97ba705a62SMauro Carvalho Chehab 	if (ret >= 0)
98ba705a62SMauro Carvalho Chehab 		*strength = (state->data[0] << 8) | state->data[0];
9989919b51SMauro Carvalho Chehab 
10089919b51SMauro Carvalho Chehab 	mutex_unlock(&state->data_mutex);
10189919b51SMauro Carvalho Chehab 	return ret;
102786baecfSMauro Carvalho Chehab }
103786baecfSMauro Carvalho Chehab 
dtt200u_fe_read_snr(struct dvb_frontend * fe,u16 * snr)104786baecfSMauro Carvalho Chehab static int dtt200u_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
105786baecfSMauro Carvalho Chehab {
106786baecfSMauro Carvalho Chehab 	struct dtt200u_fe_state *state = fe->demodulator_priv;
107ba705a62SMauro Carvalho Chehab 	int ret;
10889919b51SMauro Carvalho Chehab 
10989919b51SMauro Carvalho Chehab 	mutex_lock(&state->data_mutex);
11089919b51SMauro Carvalho Chehab 	state->data[0] = GET_SNR;
11189919b51SMauro Carvalho Chehab 
112ba705a62SMauro Carvalho Chehab 	ret = dvb_usb_generic_rw(state->d, state->data, 1, state->data, 1, 0);
113ba705a62SMauro Carvalho Chehab 	if (ret >= 0)
114ba705a62SMauro Carvalho Chehab 		*snr = ~((state->data[0] << 8) | state->data[0]);
11589919b51SMauro Carvalho Chehab 
11689919b51SMauro Carvalho Chehab 	mutex_unlock(&state->data_mutex);
11789919b51SMauro Carvalho Chehab 	return ret;
118786baecfSMauro Carvalho Chehab }
119786baecfSMauro Carvalho Chehab 
dtt200u_fe_init(struct dvb_frontend * fe)120786baecfSMauro Carvalho Chehab static int dtt200u_fe_init(struct dvb_frontend* fe)
121786baecfSMauro Carvalho Chehab {
122786baecfSMauro Carvalho Chehab 	struct dtt200u_fe_state *state = fe->demodulator_priv;
12389919b51SMauro Carvalho Chehab 	int ret;
12489919b51SMauro Carvalho Chehab 
12589919b51SMauro Carvalho Chehab 	mutex_lock(&state->data_mutex);
12689919b51SMauro Carvalho Chehab 	state->data[0] = SET_INIT;
12789919b51SMauro Carvalho Chehab 
12889919b51SMauro Carvalho Chehab 	ret = dvb_usb_generic_write(state->d, state->data, 1);
12989919b51SMauro Carvalho Chehab 	mutex_unlock(&state->data_mutex);
13089919b51SMauro Carvalho Chehab 
13189919b51SMauro Carvalho Chehab 	return ret;
132786baecfSMauro Carvalho Chehab }
133786baecfSMauro Carvalho Chehab 
dtt200u_fe_sleep(struct dvb_frontend * fe)134786baecfSMauro Carvalho Chehab static int dtt200u_fe_sleep(struct dvb_frontend* fe)
135786baecfSMauro Carvalho Chehab {
136786baecfSMauro Carvalho Chehab 	return dtt200u_fe_init(fe);
137786baecfSMauro Carvalho Chehab }
138786baecfSMauro Carvalho Chehab 
dtt200u_fe_get_tune_settings(struct dvb_frontend * fe,struct dvb_frontend_tune_settings * tune)139786baecfSMauro Carvalho Chehab static int dtt200u_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
140786baecfSMauro Carvalho Chehab {
141786baecfSMauro Carvalho Chehab 	tune->min_delay_ms = 1500;
142786baecfSMauro Carvalho Chehab 	tune->step_size = 0;
143786baecfSMauro Carvalho Chehab 	tune->max_drift = 0;
144786baecfSMauro Carvalho Chehab 	return 0;
145786baecfSMauro Carvalho Chehab }
146786baecfSMauro Carvalho Chehab 
dtt200u_fe_set_frontend(struct dvb_frontend * fe)147786baecfSMauro Carvalho Chehab static int dtt200u_fe_set_frontend(struct dvb_frontend *fe)
148786baecfSMauro Carvalho Chehab {
149786baecfSMauro Carvalho Chehab 	struct dtv_frontend_properties *fep = &fe->dtv_property_cache;
150786baecfSMauro Carvalho Chehab 	struct dtt200u_fe_state *state = fe->demodulator_priv;
151ba705a62SMauro Carvalho Chehab 	int ret;
152786baecfSMauro Carvalho Chehab 	u16 freq = fep->frequency / 250000;
153786baecfSMauro Carvalho Chehab 
15489919b51SMauro Carvalho Chehab 	mutex_lock(&state->data_mutex);
15589919b51SMauro Carvalho Chehab 	state->data[0] = SET_BANDWIDTH;
156786baecfSMauro Carvalho Chehab 	switch (fep->bandwidth_hz) {
157786baecfSMauro Carvalho Chehab 	case 8000000:
15889919b51SMauro Carvalho Chehab 		state->data[1] = 8;
159786baecfSMauro Carvalho Chehab 		break;
160786baecfSMauro Carvalho Chehab 	case 7000000:
16189919b51SMauro Carvalho Chehab 		state->data[1] = 7;
162786baecfSMauro Carvalho Chehab 		break;
163786baecfSMauro Carvalho Chehab 	case 6000000:
16489919b51SMauro Carvalho Chehab 		state->data[1] = 6;
165786baecfSMauro Carvalho Chehab 		break;
166786baecfSMauro Carvalho Chehab 	default:
16789919b51SMauro Carvalho Chehab 		ret = -EINVAL;
16889919b51SMauro Carvalho Chehab 		goto ret;
169786baecfSMauro Carvalho Chehab 	}
170786baecfSMauro Carvalho Chehab 
171ba705a62SMauro Carvalho Chehab 	ret = dvb_usb_generic_write(state->d, state->data, 2);
172ba705a62SMauro Carvalho Chehab 	if (ret < 0)
173ba705a62SMauro Carvalho Chehab 		goto ret;
174786baecfSMauro Carvalho Chehab 
17589919b51SMauro Carvalho Chehab 	state->data[0] = SET_RF_FREQ;
17689919b51SMauro Carvalho Chehab 	state->data[1] = freq & 0xff;
17789919b51SMauro Carvalho Chehab 	state->data[2] = (freq >> 8) & 0xff;
178ba705a62SMauro Carvalho Chehab 	ret = dvb_usb_generic_write(state->d, state->data, 3);
179ba705a62SMauro Carvalho Chehab 	if (ret < 0)
180ba705a62SMauro Carvalho Chehab 		goto ret;
181786baecfSMauro Carvalho Chehab 
18289919b51SMauro Carvalho Chehab ret:
18389919b51SMauro Carvalho Chehab 	mutex_unlock(&state->data_mutex);
18489919b51SMauro Carvalho Chehab 	return ret;
185786baecfSMauro Carvalho Chehab }
186786baecfSMauro Carvalho Chehab 
dtt200u_fe_get_frontend(struct dvb_frontend * fe,struct dtv_frontend_properties * fep)1877e3e68bcSMauro Carvalho Chehab static int dtt200u_fe_get_frontend(struct dvb_frontend* fe,
1887e3e68bcSMauro Carvalho Chehab 				   struct dtv_frontend_properties *fep)
189786baecfSMauro Carvalho Chehab {
190786baecfSMauro Carvalho Chehab 	struct dtt200u_fe_state *state = fe->demodulator_priv;
1917e3e68bcSMauro Carvalho Chehab 
192786baecfSMauro Carvalho Chehab 	memcpy(fep, &state->fep, sizeof(struct dtv_frontend_properties));
193786baecfSMauro Carvalho Chehab 	return 0;
194786baecfSMauro Carvalho Chehab }
195786baecfSMauro Carvalho Chehab 
dtt200u_fe_release(struct dvb_frontend * fe)196786baecfSMauro Carvalho Chehab static void dtt200u_fe_release(struct dvb_frontend* fe)
197786baecfSMauro Carvalho Chehab {
198*663e7460SYu Zhe 	struct dtt200u_fe_state *state = fe->demodulator_priv;
199786baecfSMauro Carvalho Chehab 	kfree(state);
200786baecfSMauro Carvalho Chehab }
201786baecfSMauro Carvalho Chehab 
202bd336e63SMax Kellermann static const struct dvb_frontend_ops dtt200u_fe_ops;
203786baecfSMauro Carvalho Chehab 
dtt200u_fe_attach(struct dvb_usb_device * d)204786baecfSMauro Carvalho Chehab struct dvb_frontend* dtt200u_fe_attach(struct dvb_usb_device *d)
205786baecfSMauro Carvalho Chehab {
206786baecfSMauro Carvalho Chehab 	struct dtt200u_fe_state* state = NULL;
207786baecfSMauro Carvalho Chehab 
208786baecfSMauro Carvalho Chehab 	/* allocate memory for the internal state */
209786baecfSMauro Carvalho Chehab 	state = kzalloc(sizeof(struct dtt200u_fe_state), GFP_KERNEL);
210786baecfSMauro Carvalho Chehab 	if (state == NULL)
211786baecfSMauro Carvalho Chehab 		goto error;
212786baecfSMauro Carvalho Chehab 
213786baecfSMauro Carvalho Chehab 	deb_info("attaching frontend dtt200u\n");
214786baecfSMauro Carvalho Chehab 
215786baecfSMauro Carvalho Chehab 	state->d = d;
21689919b51SMauro Carvalho Chehab 	mutex_init(&state->data_mutex);
217786baecfSMauro Carvalho Chehab 
218786baecfSMauro Carvalho Chehab 	memcpy(&state->frontend.ops,&dtt200u_fe_ops,sizeof(struct dvb_frontend_ops));
219786baecfSMauro Carvalho Chehab 	state->frontend.demodulator_priv = state;
220786baecfSMauro Carvalho Chehab 
221786baecfSMauro Carvalho Chehab 	return &state->frontend;
222786baecfSMauro Carvalho Chehab error:
223786baecfSMauro Carvalho Chehab 	return NULL;
224786baecfSMauro Carvalho Chehab }
225786baecfSMauro Carvalho Chehab 
226bd336e63SMax Kellermann static const struct dvb_frontend_ops dtt200u_fe_ops = {
227786baecfSMauro Carvalho Chehab 	.delsys = { SYS_DVBT },
228786baecfSMauro Carvalho Chehab 	.info = {
229786baecfSMauro Carvalho Chehab 		.name			= "WideView USB DVB-T",
230f1b1eabfSMauro Carvalho Chehab 		.frequency_min_hz	=  44250 * kHz,
231f1b1eabfSMauro Carvalho Chehab 		.frequency_max_hz	= 867250 * kHz,
232f1b1eabfSMauro Carvalho Chehab 		.frequency_stepsize_hz	=    250 * kHz,
233786baecfSMauro Carvalho Chehab 		.caps = FE_CAN_INVERSION_AUTO |
234786baecfSMauro Carvalho Chehab 				FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
235786baecfSMauro Carvalho Chehab 				FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
236786baecfSMauro Carvalho Chehab 				FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
237786baecfSMauro Carvalho Chehab 				FE_CAN_TRANSMISSION_MODE_AUTO |
238786baecfSMauro Carvalho Chehab 				FE_CAN_GUARD_INTERVAL_AUTO |
239786baecfSMauro Carvalho Chehab 				FE_CAN_RECOVER |
240786baecfSMauro Carvalho Chehab 				FE_CAN_HIERARCHY_AUTO,
241786baecfSMauro Carvalho Chehab 	},
242786baecfSMauro Carvalho Chehab 
243786baecfSMauro Carvalho Chehab 	.release = dtt200u_fe_release,
244786baecfSMauro Carvalho Chehab 
245786baecfSMauro Carvalho Chehab 	.init = dtt200u_fe_init,
246786baecfSMauro Carvalho Chehab 	.sleep = dtt200u_fe_sleep,
247786baecfSMauro Carvalho Chehab 
248786baecfSMauro Carvalho Chehab 	.set_frontend = dtt200u_fe_set_frontend,
249786baecfSMauro Carvalho Chehab 	.get_frontend = dtt200u_fe_get_frontend,
250786baecfSMauro Carvalho Chehab 	.get_tune_settings = dtt200u_fe_get_tune_settings,
251786baecfSMauro Carvalho Chehab 
252786baecfSMauro Carvalho Chehab 	.read_status = dtt200u_fe_read_status,
253786baecfSMauro Carvalho Chehab 	.read_ber = dtt200u_fe_read_ber,
254786baecfSMauro Carvalho Chehab 	.read_signal_strength = dtt200u_fe_read_signal_strength,
255786baecfSMauro Carvalho Chehab 	.read_snr = dtt200u_fe_read_snr,
256786baecfSMauro Carvalho Chehab 	.read_ucblocks = dtt200u_fe_read_unc_blocks,
257786baecfSMauro Carvalho Chehab };
258