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