xref: /linux/drivers/media/test-drivers/vidtv/vidtv_tuner.c (revision 9f2c9170934eace462499ba0bfe042cc72900173)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * The Virtual DVB test driver serves as a reference DVB driver and helps
4  * validate the existing APIs in the media subsystem. It can also aid
5  * developers working on userspace applications.
6  *
7  * The vidtv tuner should support common TV standards such as
8  * DVB-T/T2/S/S2, ISDB-T and ATSC when completed.
9  *
10  * Copyright (C) 2020 Daniel W. S. Almeida
11  */
12 
13 #include <linux/errno.h>
14 #include <linux/i2c.h>
15 #include <linux/module.h>
16 #include <linux/printk.h>
17 #include <linux/ratelimit.h>
18 #include <linux/slab.h>
19 #include <linux/types.h>
20 
21 #include <media/dvb_frontend.h>
22 
23 #include "vidtv_tuner.h"
24 
25 struct vidtv_tuner_cnr_to_qual_s {
26 	/* attempt to use the same values as libdvbv5 */
27 	u32 modulation;
28 	u32 fec;
29 	u32 cnr_ok;
30 	u32 cnr_good;
31 };
32 
33 static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_c_cnr_2_qual[] = {
34 	/* from libdvbv5 source code, in milli db */
35 	{ QAM_256, FEC_NONE,  34000, 38000},
36 	{ QAM_64,  FEC_NONE,  30000, 34000},
37 };
38 
39 static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_s_cnr_2_qual[] = {
40 	/* from libdvbv5 source code, in milli db */
41 	{ QPSK, FEC_1_2,  7000, 10000},
42 	{ QPSK, FEC_2_3,  9000, 12000},
43 	{ QPSK, FEC_3_4, 10000, 13000},
44 	{ QPSK, FEC_5_6, 11000, 14000},
45 	{ QPSK, FEC_7_8, 12000, 15000},
46 };
47 
48 static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_s2_cnr_2_qual[] = {
49 	/* from libdvbv5 source code, in milli db */
50 	{ QPSK,  FEC_1_2,   9000,  12000},
51 	{ QPSK,  FEC_2_3,  11000,  14000},
52 	{ QPSK,  FEC_3_4,  12000,  15000},
53 	{ QPSK,  FEC_5_6,  12000,  15000},
54 	{ QPSK,  FEC_8_9,  13000,  16000},
55 	{ QPSK,  FEC_9_10, 13500,  16500},
56 	{ PSK_8, FEC_2_3,  14500,  17500},
57 	{ PSK_8, FEC_3_4,  16000,  19000},
58 	{ PSK_8, FEC_5_6,  17500,  20500},
59 	{ PSK_8, FEC_8_9,  19000,  22000},
60 };
61 
62 static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_t_cnr_2_qual[] = {
63 	/* from libdvbv5 source code, in milli db*/
64 	{   QPSK, FEC_1_2,  4100,  5900},
65 	{   QPSK, FEC_2_3,  6100,  9600},
66 	{   QPSK, FEC_3_4,  7200, 12400},
67 	{   QPSK, FEC_5_6,  8500, 15600},
68 	{   QPSK, FEC_7_8,  9200, 17500},
69 	{ QAM_16, FEC_1_2,  9800, 11800},
70 	{ QAM_16, FEC_2_3, 12100, 15300},
71 	{ QAM_16, FEC_3_4, 13400, 18100},
72 	{ QAM_16, FEC_5_6, 14800, 21300},
73 	{ QAM_16, FEC_7_8, 15700, 23600},
74 	{ QAM_64, FEC_1_2, 14000, 16000},
75 	{ QAM_64, FEC_2_3, 19900, 25400},
76 	{ QAM_64, FEC_3_4, 24900, 27900},
77 	{ QAM_64, FEC_5_6, 21300, 23300},
78 	{ QAM_64, FEC_7_8, 22000, 24000},
79 };
80 
81 /**
82  * struct vidtv_tuner_hardware_state - Simulate the tuner hardware status
83  * @asleep: whether the tuner is asleep, i.e whether _sleep() or _suspend() was
84  * called.
85  * @lock_status: Whether the tuner has managed to lock on the requested
86  * frequency.
87  * @if_frequency: The tuner's intermediate frequency. Hardcoded for the purposes
88  * of simulation.
89  * @tuned_frequency: The actual tuned frequency.
90  * @bandwidth: The actual bandwidth.
91  *
92  * This structure is meant to simulate the status of the tuner hardware, as if
93  * we had a physical tuner hardware.
94  */
95 struct vidtv_tuner_hardware_state {
96 	bool asleep;
97 	u32 lock_status;
98 	u32 if_frequency;
99 	u32 tuned_frequency;
100 	u32 bandwidth;
101 };
102 
103 /**
104  * struct vidtv_tuner_dev - The tuner struct
105  * @fe: A pointer to the dvb_frontend structure allocated by vidtv_demod
106  * @hw_state: A struct to simulate the tuner's hardware state as if we had a
107  * physical tuner hardware.
108  * @config: The configuration used to start the tuner module, usually filled
109  * by a bridge driver. For vidtv, this is filled by vidtv_bridge before the
110  * tuner module is probed.
111  */
112 struct vidtv_tuner_dev {
113 	struct dvb_frontend *fe;
114 	struct vidtv_tuner_hardware_state hw_state;
115 	struct vidtv_tuner_config config;
116 };
117 
118 static struct vidtv_tuner_dev*
119 vidtv_tuner_get_dev(struct dvb_frontend *fe)
120 {
121 	return i2c_get_clientdata(fe->tuner_priv);
122 }
123 
124 static int vidtv_tuner_check_frequency_shift(struct dvb_frontend *fe)
125 {
126 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
127 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
128 	struct vidtv_tuner_config config  = tuner_dev->config;
129 	u32 *valid_freqs = NULL;
130 	u32 array_sz = 0;
131 	u32 i;
132 	u32 shift;
133 
134 	switch (c->delivery_system) {
135 	case SYS_DVBT:
136 	case SYS_DVBT2:
137 		valid_freqs = config.vidtv_valid_dvb_t_freqs;
138 		array_sz    = ARRAY_SIZE(config.vidtv_valid_dvb_t_freqs);
139 		break;
140 	case SYS_DVBS:
141 	case SYS_DVBS2:
142 		valid_freqs = config.vidtv_valid_dvb_s_freqs;
143 		array_sz    = ARRAY_SIZE(config.vidtv_valid_dvb_s_freqs);
144 		break;
145 	case SYS_DVBC_ANNEX_A:
146 		valid_freqs = config.vidtv_valid_dvb_c_freqs;
147 		array_sz    = ARRAY_SIZE(config.vidtv_valid_dvb_c_freqs);
148 		break;
149 
150 	default:
151 		dev_warn(fe->dvb->device,
152 			 "%s: unsupported delivery system: %u\n",
153 			 __func__,
154 			 c->delivery_system);
155 
156 		return -EINVAL;
157 	}
158 
159 	for (i = 0; i < array_sz; i++) {
160 		if (!valid_freqs[i])
161 			break;
162 		shift = abs(c->frequency - valid_freqs[i]);
163 
164 		if (!shift)
165 			return 0;
166 
167 		/*
168 		 * This will provide a value from 0 to 100 that would
169 		 * indicate how far is the tuned frequency from the
170 		 * right one.
171 		 */
172 		if (shift < config.max_frequency_shift_hz)
173 			return shift * 100 / config.max_frequency_shift_hz;
174 	}
175 
176 	return -EINVAL;
177 }
178 
179 static int
180 vidtv_tuner_get_signal_strength(struct dvb_frontend *fe, u16 *strength)
181 {
182 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
183 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
184 	const struct vidtv_tuner_cnr_to_qual_s *cnr2qual = NULL;
185 	struct device *dev = fe->dvb->device;
186 	u32 array_size = 0;
187 	s32 shift;
188 	u32 i;
189 
190 	shift = vidtv_tuner_check_frequency_shift(fe);
191 	if (shift < 0) {
192 		tuner_dev->hw_state.lock_status = 0;
193 		*strength = 0;
194 		return 0;
195 	}
196 
197 	switch (c->delivery_system) {
198 	case SYS_DVBT:
199 	case SYS_DVBT2:
200 		cnr2qual   = vidtv_tuner_t_cnr_2_qual;
201 		array_size = ARRAY_SIZE(vidtv_tuner_t_cnr_2_qual);
202 		break;
203 
204 	case SYS_DVBS:
205 		cnr2qual   = vidtv_tuner_s_cnr_2_qual;
206 		array_size = ARRAY_SIZE(vidtv_tuner_s_cnr_2_qual);
207 		break;
208 
209 	case SYS_DVBS2:
210 		cnr2qual   = vidtv_tuner_s2_cnr_2_qual;
211 		array_size = ARRAY_SIZE(vidtv_tuner_s2_cnr_2_qual);
212 		break;
213 
214 	case SYS_DVBC_ANNEX_A:
215 		cnr2qual   = vidtv_tuner_c_cnr_2_qual;
216 		array_size = ARRAY_SIZE(vidtv_tuner_c_cnr_2_qual);
217 		break;
218 
219 	default:
220 		dev_warn_ratelimited(dev,
221 				     "%s: unsupported delivery system: %u\n",
222 				     __func__,
223 				     c->delivery_system);
224 		return -EINVAL;
225 	}
226 
227 	for (i = 0; i < array_size; i++) {
228 		if (cnr2qual[i].modulation != c->modulation ||
229 		    cnr2qual[i].fec != c->fec_inner)
230 			continue;
231 
232 		if (!shift) {
233 			*strength = cnr2qual[i].cnr_good;
234 			return 0;
235 		}
236 		/*
237 		 * Channel tuned at wrong frequency. Simulate that the
238 		 * Carrier S/N ratio is not too good.
239 		 */
240 
241 		*strength = cnr2qual[i].cnr_ok -
242 			    (cnr2qual[i].cnr_good - cnr2qual[i].cnr_ok);
243 		return 0;
244 	}
245 
246 	/*
247 	 * do a linear interpolation between 34dB and 10dB if we can't
248 	 * match against the table
249 	 */
250 	*strength = 34000 - 24000 * shift / 100;
251 	return 0;
252 }
253 
254 static int vidtv_tuner_init(struct dvb_frontend *fe)
255 {
256 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
257 	struct vidtv_tuner_config config  = tuner_dev->config;
258 
259 	msleep_interruptible(config.mock_power_up_delay_msec);
260 
261 	tuner_dev->hw_state.asleep = false;
262 	tuner_dev->hw_state.if_frequency = 5000;
263 
264 	return 0;
265 }
266 
267 static int vidtv_tuner_sleep(struct dvb_frontend *fe)
268 {
269 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
270 
271 	tuner_dev->hw_state.asleep = true;
272 	return 0;
273 }
274 
275 static int vidtv_tuner_suspend(struct dvb_frontend *fe)
276 {
277 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
278 
279 	tuner_dev->hw_state.asleep = true;
280 	return 0;
281 }
282 
283 static int vidtv_tuner_resume(struct dvb_frontend *fe)
284 {
285 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
286 
287 	tuner_dev->hw_state.asleep = false;
288 	return 0;
289 }
290 
291 static int vidtv_tuner_set_params(struct dvb_frontend *fe)
292 {
293 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
294 	struct vidtv_tuner_config config  = tuner_dev->config;
295 	struct dtv_frontend_properties *c = &fe->dtv_property_cache;
296 	s32 shift;
297 
298 	u32 min_freq = fe->ops.tuner_ops.info.frequency_min_hz;
299 	u32 max_freq = fe->ops.tuner_ops.info.frequency_max_hz;
300 	u32 min_bw = fe->ops.tuner_ops.info.bandwidth_min;
301 	u32 max_bw = fe->ops.tuner_ops.info.bandwidth_max;
302 
303 	if (c->frequency < min_freq  || c->frequency > max_freq  ||
304 	    c->bandwidth_hz < min_bw || c->bandwidth_hz > max_bw) {
305 		tuner_dev->hw_state.lock_status = 0;
306 		return -EINVAL;
307 	}
308 
309 	tuner_dev->hw_state.tuned_frequency = c->frequency;
310 	tuner_dev->hw_state.bandwidth = c->bandwidth_hz;
311 	tuner_dev->hw_state.lock_status = TUNER_STATUS_LOCKED;
312 
313 	msleep_interruptible(config.mock_tune_delay_msec);
314 
315 	shift = vidtv_tuner_check_frequency_shift(fe);
316 	if (shift < 0) {
317 		tuner_dev->hw_state.lock_status = 0;
318 		return shift;
319 	}
320 
321 	return 0;
322 }
323 
324 static int vidtv_tuner_set_config(struct dvb_frontend *fe,
325 				  void *priv_cfg)
326 {
327 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
328 
329 	memcpy(&tuner_dev->config, priv_cfg, sizeof(tuner_dev->config));
330 
331 	return 0;
332 }
333 
334 static int vidtv_tuner_get_frequency(struct dvb_frontend *fe,
335 				     u32 *frequency)
336 {
337 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
338 
339 	*frequency = tuner_dev->hw_state.tuned_frequency;
340 
341 	return 0;
342 }
343 
344 static int vidtv_tuner_get_bandwidth(struct dvb_frontend *fe,
345 				     u32 *bandwidth)
346 {
347 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
348 
349 	*bandwidth = tuner_dev->hw_state.bandwidth;
350 
351 	return 0;
352 }
353 
354 static int vidtv_tuner_get_if_frequency(struct dvb_frontend *fe,
355 					u32 *frequency)
356 {
357 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
358 
359 	*frequency = tuner_dev->hw_state.if_frequency;
360 
361 	return 0;
362 }
363 
364 static int vidtv_tuner_get_status(struct dvb_frontend *fe, u32 *status)
365 {
366 	struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe);
367 
368 	*status = tuner_dev->hw_state.lock_status;
369 
370 	return 0;
371 }
372 
373 static const struct dvb_tuner_ops vidtv_tuner_ops = {
374 	.init             = vidtv_tuner_init,
375 	.sleep            = vidtv_tuner_sleep,
376 	.suspend          = vidtv_tuner_suspend,
377 	.resume           = vidtv_tuner_resume,
378 	.set_params       = vidtv_tuner_set_params,
379 	.set_config       = vidtv_tuner_set_config,
380 	.get_bandwidth    = vidtv_tuner_get_bandwidth,
381 	.get_frequency    = vidtv_tuner_get_frequency,
382 	.get_if_frequency = vidtv_tuner_get_if_frequency,
383 	.get_status       = vidtv_tuner_get_status,
384 	.get_rf_strength  = vidtv_tuner_get_signal_strength
385 };
386 
387 static const struct i2c_device_id vidtv_tuner_i2c_id_table[] = {
388 	{"dvb_vidtv_tuner", 0},
389 	{}
390 };
391 MODULE_DEVICE_TABLE(i2c, vidtv_tuner_i2c_id_table);
392 
393 static int vidtv_tuner_i2c_probe(struct i2c_client *client)
394 {
395 	struct vidtv_tuner_config *config = client->dev.platform_data;
396 	struct dvb_frontend *fe           = config->fe;
397 	struct vidtv_tuner_dev *tuner_dev = NULL;
398 
399 	tuner_dev = kzalloc(sizeof(*tuner_dev), GFP_KERNEL);
400 	if (!tuner_dev)
401 		return -ENOMEM;
402 
403 	tuner_dev->fe = config->fe;
404 	i2c_set_clientdata(client, tuner_dev);
405 
406 	memcpy(&fe->ops.tuner_ops,
407 	       &vidtv_tuner_ops,
408 	       sizeof(struct dvb_tuner_ops));
409 
410 	memcpy(&tuner_dev->config, config, sizeof(tuner_dev->config));
411 	fe->tuner_priv = client;
412 
413 	return 0;
414 }
415 
416 static void vidtv_tuner_i2c_remove(struct i2c_client *client)
417 {
418 	struct vidtv_tuner_dev *tuner_dev = i2c_get_clientdata(client);
419 
420 	kfree(tuner_dev);
421 }
422 
423 static struct i2c_driver vidtv_tuner_i2c_driver = {
424 	.driver = {
425 		.name                = "dvb_vidtv_tuner",
426 		.suppress_bind_attrs = true,
427 	},
428 	.probe_new = vidtv_tuner_i2c_probe,
429 	.remove   = vidtv_tuner_i2c_remove,
430 	.id_table = vidtv_tuner_i2c_id_table,
431 };
432 module_i2c_driver(vidtv_tuner_i2c_driver);
433 
434 MODULE_DESCRIPTION("Virtual DVB Tuner");
435 MODULE_AUTHOR("Daniel W. S. Almeida");
436 MODULE_LICENSE("GPL");
437