124fb190eSDaniel W. S. Almeida // SPDX-License-Identifier: GPL-2.0 224fb190eSDaniel W. S. Almeida /* 324fb190eSDaniel W. S. Almeida * The Virtual DVB test driver serves as a reference DVB driver and helps 424fb190eSDaniel W. S. Almeida * validate the existing APIs in the media subsystem. It can also aid 524fb190eSDaniel W. S. Almeida * developers working on userspace applications. 624fb190eSDaniel W. S. Almeida * 724fb190eSDaniel W. S. Almeida * The vidtv tuner should support common TV standards such as 824fb190eSDaniel W. S. Almeida * DVB-T/T2/S/S2, ISDB-T and ATSC when completed. 924fb190eSDaniel W. S. Almeida * 1024fb190eSDaniel W. S. Almeida * Copyright (C) 2020 Daniel W. S. Almeida 1124fb190eSDaniel W. S. Almeida */ 1224fb190eSDaniel W. S. Almeida 1324fb190eSDaniel W. S. Almeida #include <linux/errno.h> 1424fb190eSDaniel W. S. Almeida #include <linux/i2c.h> 1524fb190eSDaniel W. S. Almeida #include <linux/module.h> 1624fb190eSDaniel W. S. Almeida #include <linux/printk.h> 1724fb190eSDaniel W. S. Almeida #include <linux/ratelimit.h> 188922e393SMauro Carvalho Chehab #include <linux/slab.h> 198922e393SMauro Carvalho Chehab #include <linux/types.h> 208922e393SMauro Carvalho Chehab 218922e393SMauro Carvalho Chehab #include <media/dvb_frontend.h> 2224fb190eSDaniel W. S. Almeida 2324fb190eSDaniel W. S. Almeida #include "vidtv_tuner.h" 2424fb190eSDaniel W. S. Almeida 2524fb190eSDaniel W. S. Almeida struct vidtv_tuner_cnr_to_qual_s { 2624fb190eSDaniel W. S. Almeida /* attempt to use the same values as libdvbv5 */ 2724fb190eSDaniel W. S. Almeida u32 modulation; 2824fb190eSDaniel W. S. Almeida u32 fec; 2924fb190eSDaniel W. S. Almeida u32 cnr_ok; 3024fb190eSDaniel W. S. Almeida u32 cnr_good; 3124fb190eSDaniel W. S. Almeida }; 3224fb190eSDaniel W. S. Almeida 3324fb190eSDaniel W. S. Almeida static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_c_cnr_2_qual[] = { 3424fb190eSDaniel W. S. Almeida /* from libdvbv5 source code, in milli db */ 3524fb190eSDaniel W. S. Almeida { QAM_256, FEC_NONE, 34000, 38000}, 3624fb190eSDaniel W. S. Almeida { QAM_64, FEC_NONE, 30000, 34000}, 3724fb190eSDaniel W. S. Almeida }; 3824fb190eSDaniel W. S. Almeida 3924fb190eSDaniel W. S. Almeida static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_s_cnr_2_qual[] = { 4024fb190eSDaniel W. S. Almeida /* from libdvbv5 source code, in milli db */ 4124fb190eSDaniel W. S. Almeida { QPSK, FEC_1_2, 7000, 10000}, 4224fb190eSDaniel W. S. Almeida { QPSK, FEC_2_3, 9000, 12000}, 4324fb190eSDaniel W. S. Almeida { QPSK, FEC_3_4, 10000, 13000}, 4424fb190eSDaniel W. S. Almeida { QPSK, FEC_5_6, 11000, 14000}, 4524fb190eSDaniel W. S. Almeida { QPSK, FEC_7_8, 12000, 15000}, 4624fb190eSDaniel W. S. Almeida }; 4724fb190eSDaniel W. S. Almeida 4824fb190eSDaniel W. S. Almeida static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_s2_cnr_2_qual[] = { 4924fb190eSDaniel W. S. Almeida /* from libdvbv5 source code, in milli db */ 5024fb190eSDaniel W. S. Almeida { QPSK, FEC_1_2, 9000, 12000}, 5124fb190eSDaniel W. S. Almeida { QPSK, FEC_2_3, 11000, 14000}, 5224fb190eSDaniel W. S. Almeida { QPSK, FEC_3_4, 12000, 15000}, 5324fb190eSDaniel W. S. Almeida { QPSK, FEC_5_6, 12000, 15000}, 5424fb190eSDaniel W. S. Almeida { QPSK, FEC_8_9, 13000, 16000}, 5524fb190eSDaniel W. S. Almeida { QPSK, FEC_9_10, 13500, 16500}, 5624fb190eSDaniel W. S. Almeida { PSK_8, FEC_2_3, 14500, 17500}, 5724fb190eSDaniel W. S. Almeida { PSK_8, FEC_3_4, 16000, 19000}, 5824fb190eSDaniel W. S. Almeida { PSK_8, FEC_5_6, 17500, 20500}, 5924fb190eSDaniel W. S. Almeida { PSK_8, FEC_8_9, 19000, 22000}, 6024fb190eSDaniel W. S. Almeida }; 6124fb190eSDaniel W. S. Almeida 6224fb190eSDaniel W. S. Almeida static const struct vidtv_tuner_cnr_to_qual_s vidtv_tuner_t_cnr_2_qual[] = { 6324fb190eSDaniel W. S. Almeida /* from libdvbv5 source code, in milli db*/ 6424fb190eSDaniel W. S. Almeida { QPSK, FEC_1_2, 4100, 5900}, 6524fb190eSDaniel W. S. Almeida { QPSK, FEC_2_3, 6100, 9600}, 6624fb190eSDaniel W. S. Almeida { QPSK, FEC_3_4, 7200, 12400}, 6724fb190eSDaniel W. S. Almeida { QPSK, FEC_5_6, 8500, 15600}, 6824fb190eSDaniel W. S. Almeida { QPSK, FEC_7_8, 9200, 17500}, 6924fb190eSDaniel W. S. Almeida { QAM_16, FEC_1_2, 9800, 11800}, 7024fb190eSDaniel W. S. Almeida { QAM_16, FEC_2_3, 12100, 15300}, 7124fb190eSDaniel W. S. Almeida { QAM_16, FEC_3_4, 13400, 18100}, 7224fb190eSDaniel W. S. Almeida { QAM_16, FEC_5_6, 14800, 21300}, 7324fb190eSDaniel W. S. Almeida { QAM_16, FEC_7_8, 15700, 23600}, 7424fb190eSDaniel W. S. Almeida { QAM_64, FEC_1_2, 14000, 16000}, 7524fb190eSDaniel W. S. Almeida { QAM_64, FEC_2_3, 19900, 25400}, 7624fb190eSDaniel W. S. Almeida { QAM_64, FEC_3_4, 24900, 27900}, 7724fb190eSDaniel W. S. Almeida { QAM_64, FEC_5_6, 21300, 23300}, 7824fb190eSDaniel W. S. Almeida { QAM_64, FEC_7_8, 22000, 24000}, 7924fb190eSDaniel W. S. Almeida }; 8024fb190eSDaniel W. S. Almeida 8124fb190eSDaniel W. S. Almeida /** 8224fb190eSDaniel W. S. Almeida * struct vidtv_tuner_hardware_state - Simulate the tuner hardware status 8324fb190eSDaniel W. S. Almeida * @asleep: whether the tuner is asleep, i.e whether _sleep() or _suspend() was 8424fb190eSDaniel W. S. Almeida * called. 8524fb190eSDaniel W. S. Almeida * @lock_status: Whether the tuner has managed to lock on the requested 8624fb190eSDaniel W. S. Almeida * frequency. 8724fb190eSDaniel W. S. Almeida * @if_frequency: The tuner's intermediate frequency. Hardcoded for the purposes 8824fb190eSDaniel W. S. Almeida * of simulation. 8924fb190eSDaniel W. S. Almeida * @tuned_frequency: The actual tuned frequency. 9024fb190eSDaniel W. S. Almeida * @bandwidth: The actual bandwidth. 9124fb190eSDaniel W. S. Almeida * 9224fb190eSDaniel W. S. Almeida * This structure is meant to simulate the status of the tuner hardware, as if 9324fb190eSDaniel W. S. Almeida * we had a physical tuner hardware. 9424fb190eSDaniel W. S. Almeida */ 9524fb190eSDaniel W. S. Almeida struct vidtv_tuner_hardware_state { 9624fb190eSDaniel W. S. Almeida bool asleep; 9724fb190eSDaniel W. S. Almeida u32 lock_status; 9824fb190eSDaniel W. S. Almeida u32 if_frequency; 9924fb190eSDaniel W. S. Almeida u32 tuned_frequency; 10024fb190eSDaniel W. S. Almeida u32 bandwidth; 10124fb190eSDaniel W. S. Almeida }; 10224fb190eSDaniel W. S. Almeida 10324fb190eSDaniel W. S. Almeida /** 10424fb190eSDaniel W. S. Almeida * struct vidtv_tuner_dev - The tuner struct 10524fb190eSDaniel W. S. Almeida * @fe: A pointer to the dvb_frontend structure allocated by vidtv_demod 10624fb190eSDaniel W. S. Almeida * @hw_state: A struct to simulate the tuner's hardware state as if we had a 10724fb190eSDaniel W. S. Almeida * physical tuner hardware. 10824fb190eSDaniel W. S. Almeida * @config: The configuration used to start the tuner module, usually filled 10924fb190eSDaniel W. S. Almeida * by a bridge driver. For vidtv, this is filled by vidtv_bridge before the 11024fb190eSDaniel W. S. Almeida * tuner module is probed. 11124fb190eSDaniel W. S. Almeida */ 11224fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev { 11324fb190eSDaniel W. S. Almeida struct dvb_frontend *fe; 11424fb190eSDaniel W. S. Almeida struct vidtv_tuner_hardware_state hw_state; 11524fb190eSDaniel W. S. Almeida struct vidtv_tuner_config config; 11624fb190eSDaniel W. S. Almeida }; 11724fb190eSDaniel W. S. Almeida 11824fb190eSDaniel W. S. Almeida static struct vidtv_tuner_dev* 11924fb190eSDaniel W. S. Almeida vidtv_tuner_get_dev(struct dvb_frontend *fe) 12024fb190eSDaniel W. S. Almeida { 12124fb190eSDaniel W. S. Almeida return i2c_get_clientdata(fe->tuner_priv); 12224fb190eSDaniel W. S. Almeida } 12324fb190eSDaniel W. S. Almeida 124f3ea9da2SMauro Carvalho Chehab static int vidtv_tuner_check_frequency_shift(struct dvb_frontend *fe) 12524fb190eSDaniel W. S. Almeida { 12624fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 12724fb190eSDaniel W. S. Almeida struct dtv_frontend_properties *c = &fe->dtv_property_cache; 12824fb190eSDaniel W. S. Almeida struct vidtv_tuner_config config = tuner_dev->config; 12924fb190eSDaniel W. S. Almeida u32 *valid_freqs = NULL; 13024fb190eSDaniel W. S. Almeida u32 array_sz = 0; 13124fb190eSDaniel W. S. Almeida u32 i; 13224fb190eSDaniel W. S. Almeida u32 shift; 13324fb190eSDaniel W. S. Almeida 13424fb190eSDaniel W. S. Almeida switch (c->delivery_system) { 13524fb190eSDaniel W. S. Almeida case SYS_DVBT: 13624fb190eSDaniel W. S. Almeida case SYS_DVBT2: 13724fb190eSDaniel W. S. Almeida valid_freqs = config.vidtv_valid_dvb_t_freqs; 13824fb190eSDaniel W. S. Almeida array_sz = ARRAY_SIZE(config.vidtv_valid_dvb_t_freqs); 13924fb190eSDaniel W. S. Almeida break; 14024fb190eSDaniel W. S. Almeida case SYS_DVBS: 14124fb190eSDaniel W. S. Almeida case SYS_DVBS2: 14224fb190eSDaniel W. S. Almeida valid_freqs = config.vidtv_valid_dvb_s_freqs; 14324fb190eSDaniel W. S. Almeida array_sz = ARRAY_SIZE(config.vidtv_valid_dvb_s_freqs); 14424fb190eSDaniel W. S. Almeida break; 14524fb190eSDaniel W. S. Almeida case SYS_DVBC_ANNEX_A: 14624fb190eSDaniel W. S. Almeida valid_freqs = config.vidtv_valid_dvb_c_freqs; 14724fb190eSDaniel W. S. Almeida array_sz = ARRAY_SIZE(config.vidtv_valid_dvb_c_freqs); 14824fb190eSDaniel W. S. Almeida break; 14924fb190eSDaniel W. S. Almeida 15024fb190eSDaniel W. S. Almeida default: 1519cfb4d36SMauro Carvalho Chehab dev_warn(fe->dvb->device, 1529cfb4d36SMauro Carvalho Chehab "%s: unsupported delivery system: %u\n", 15324fb190eSDaniel W. S. Almeida __func__, 15424fb190eSDaniel W. S. Almeida c->delivery_system); 15524fb190eSDaniel W. S. Almeida 15624fb190eSDaniel W. S. Almeida return -EINVAL; 15724fb190eSDaniel W. S. Almeida } 15824fb190eSDaniel W. S. Almeida 15924fb190eSDaniel W. S. Almeida for (i = 0; i < array_sz; i++) { 160f3ea9da2SMauro Carvalho Chehab if (!valid_freqs[i]) 161f3ea9da2SMauro Carvalho Chehab break; 16224fb190eSDaniel W. S. Almeida shift = abs(c->frequency - valid_freqs[i]); 16324fb190eSDaniel W. S. Almeida 16424fb190eSDaniel W. S. Almeida if (!shift) 16524fb190eSDaniel W. S. Almeida return 0; 16624fb190eSDaniel W. S. Almeida 16724fb190eSDaniel W. S. Almeida /* 16824fb190eSDaniel W. S. Almeida * This will provide a value from 0 to 100 that would 16924fb190eSDaniel W. S. Almeida * indicate how far is the tuned frequency from the 17024fb190eSDaniel W. S. Almeida * right one. 17124fb190eSDaniel W. S. Almeida */ 17224fb190eSDaniel W. S. Almeida if (shift < config.max_frequency_shift_hz) 17324fb190eSDaniel W. S. Almeida return shift * 100 / config.max_frequency_shift_hz; 17424fb190eSDaniel W. S. Almeida } 17524fb190eSDaniel W. S. Almeida 17624fb190eSDaniel W. S. Almeida return -EINVAL; 17724fb190eSDaniel W. S. Almeida } 17824fb190eSDaniel W. S. Almeida 17924fb190eSDaniel W. S. Almeida static int 18024fb190eSDaniel W. S. Almeida vidtv_tuner_get_signal_strength(struct dvb_frontend *fe, u16 *strength) 18124fb190eSDaniel W. S. Almeida { 18224fb190eSDaniel W. S. Almeida struct dtv_frontend_properties *c = &fe->dtv_property_cache; 183f3ea9da2SMauro Carvalho Chehab struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 18424fb190eSDaniel W. S. Almeida const struct vidtv_tuner_cnr_to_qual_s *cnr2qual = NULL; 1859cfb4d36SMauro Carvalho Chehab struct device *dev = fe->dvb->device; 18624fb190eSDaniel W. S. Almeida u32 array_size = 0; 18724fb190eSDaniel W. S. Almeida s32 shift; 18824fb190eSDaniel W. S. Almeida u32 i; 18924fb190eSDaniel W. S. Almeida 19024fb190eSDaniel W. S. Almeida shift = vidtv_tuner_check_frequency_shift(fe); 191f3ea9da2SMauro Carvalho Chehab if (shift < 0) { 192f3ea9da2SMauro Carvalho Chehab tuner_dev->hw_state.lock_status = 0; 193f3ea9da2SMauro Carvalho Chehab *strength = 0; 194f3ea9da2SMauro Carvalho Chehab return 0; 195f3ea9da2SMauro Carvalho Chehab } 19624fb190eSDaniel W. S. Almeida 19724fb190eSDaniel W. S. Almeida switch (c->delivery_system) { 19824fb190eSDaniel W. S. Almeida case SYS_DVBT: 19924fb190eSDaniel W. S. Almeida case SYS_DVBT2: 20024fb190eSDaniel W. S. Almeida cnr2qual = vidtv_tuner_t_cnr_2_qual; 20124fb190eSDaniel W. S. Almeida array_size = ARRAY_SIZE(vidtv_tuner_t_cnr_2_qual); 20224fb190eSDaniel W. S. Almeida break; 20324fb190eSDaniel W. S. Almeida 20424fb190eSDaniel W. S. Almeida case SYS_DVBS: 20524fb190eSDaniel W. S. Almeida cnr2qual = vidtv_tuner_s_cnr_2_qual; 20624fb190eSDaniel W. S. Almeida array_size = ARRAY_SIZE(vidtv_tuner_s_cnr_2_qual); 20724fb190eSDaniel W. S. Almeida break; 20824fb190eSDaniel W. S. Almeida 20924fb190eSDaniel W. S. Almeida case SYS_DVBS2: 21024fb190eSDaniel W. S. Almeida cnr2qual = vidtv_tuner_s2_cnr_2_qual; 21124fb190eSDaniel W. S. Almeida array_size = ARRAY_SIZE(vidtv_tuner_s2_cnr_2_qual); 21224fb190eSDaniel W. S. Almeida break; 21324fb190eSDaniel W. S. Almeida 21424fb190eSDaniel W. S. Almeida case SYS_DVBC_ANNEX_A: 21524fb190eSDaniel W. S. Almeida cnr2qual = vidtv_tuner_c_cnr_2_qual; 21624fb190eSDaniel W. S. Almeida array_size = ARRAY_SIZE(vidtv_tuner_c_cnr_2_qual); 21724fb190eSDaniel W. S. Almeida break; 21824fb190eSDaniel W. S. Almeida 21924fb190eSDaniel W. S. Almeida default: 2209cfb4d36SMauro Carvalho Chehab dev_warn_ratelimited(dev, 2219cfb4d36SMauro Carvalho Chehab "%s: unsupported delivery system: %u\n", 22224fb190eSDaniel W. S. Almeida __func__, 22324fb190eSDaniel W. S. Almeida c->delivery_system); 22424fb190eSDaniel W. S. Almeida return -EINVAL; 22524fb190eSDaniel W. S. Almeida } 22624fb190eSDaniel W. S. Almeida 22724fb190eSDaniel W. S. Almeida for (i = 0; i < array_size; i++) { 22824fb190eSDaniel W. S. Almeida if (cnr2qual[i].modulation != c->modulation || 22924fb190eSDaniel W. S. Almeida cnr2qual[i].fec != c->fec_inner) 23024fb190eSDaniel W. S. Almeida continue; 23124fb190eSDaniel W. S. Almeida 23224fb190eSDaniel W. S. Almeida if (!shift) { 23324fb190eSDaniel W. S. Almeida *strength = cnr2qual[i].cnr_good; 23424fb190eSDaniel W. S. Almeida return 0; 23524fb190eSDaniel W. S. Almeida } 23624fb190eSDaniel W. S. Almeida /* 23724fb190eSDaniel W. S. Almeida * Channel tuned at wrong frequency. Simulate that the 23824fb190eSDaniel W. S. Almeida * Carrier S/N ratio is not too good. 23924fb190eSDaniel W. S. Almeida */ 24024fb190eSDaniel W. S. Almeida 24124fb190eSDaniel W. S. Almeida *strength = cnr2qual[i].cnr_ok - 24224fb190eSDaniel W. S. Almeida (cnr2qual[i].cnr_good - cnr2qual[i].cnr_ok); 24324fb190eSDaniel W. S. Almeida return 0; 24424fb190eSDaniel W. S. Almeida } 24524fb190eSDaniel W. S. Almeida 24624fb190eSDaniel W. S. Almeida /* 24724fb190eSDaniel W. S. Almeida * do a linear interpolation between 34dB and 10dB if we can't 24824fb190eSDaniel W. S. Almeida * match against the table 24924fb190eSDaniel W. S. Almeida */ 2503e51a496SMauro Carvalho Chehab *strength = 34000 - 24000 * shift / 100; 25124fb190eSDaniel W. S. Almeida return 0; 25224fb190eSDaniel W. S. Almeida } 25324fb190eSDaniel W. S. Almeida 25424fb190eSDaniel W. S. Almeida static int vidtv_tuner_init(struct dvb_frontend *fe) 25524fb190eSDaniel W. S. Almeida { 25624fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 25724fb190eSDaniel W. S. Almeida struct vidtv_tuner_config config = tuner_dev->config; 25824fb190eSDaniel W. S. Almeida 25924fb190eSDaniel W. S. Almeida msleep_interruptible(config.mock_power_up_delay_msec); 26024fb190eSDaniel W. S. Almeida 26124fb190eSDaniel W. S. Almeida tuner_dev->hw_state.asleep = false; 26224fb190eSDaniel W. S. Almeida tuner_dev->hw_state.if_frequency = 5000; 26324fb190eSDaniel W. S. Almeida 26424fb190eSDaniel W. S. Almeida return 0; 26524fb190eSDaniel W. S. Almeida } 26624fb190eSDaniel W. S. Almeida 26724fb190eSDaniel W. S. Almeida static int vidtv_tuner_sleep(struct dvb_frontend *fe) 26824fb190eSDaniel W. S. Almeida { 26924fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 27024fb190eSDaniel W. S. Almeida 27124fb190eSDaniel W. S. Almeida tuner_dev->hw_state.asleep = true; 27224fb190eSDaniel W. S. Almeida return 0; 27324fb190eSDaniel W. S. Almeida } 27424fb190eSDaniel W. S. Almeida 27524fb190eSDaniel W. S. Almeida static int vidtv_tuner_suspend(struct dvb_frontend *fe) 27624fb190eSDaniel W. S. Almeida { 27724fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 27824fb190eSDaniel W. S. Almeida 27924fb190eSDaniel W. S. Almeida tuner_dev->hw_state.asleep = true; 28024fb190eSDaniel W. S. Almeida return 0; 28124fb190eSDaniel W. S. Almeida } 28224fb190eSDaniel W. S. Almeida 28324fb190eSDaniel W. S. Almeida static int vidtv_tuner_resume(struct dvb_frontend *fe) 28424fb190eSDaniel W. S. Almeida { 28524fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 28624fb190eSDaniel W. S. Almeida 28724fb190eSDaniel W. S. Almeida tuner_dev->hw_state.asleep = false; 28824fb190eSDaniel W. S. Almeida return 0; 28924fb190eSDaniel W. S. Almeida } 29024fb190eSDaniel W. S. Almeida 29124fb190eSDaniel W. S. Almeida static int vidtv_tuner_set_params(struct dvb_frontend *fe) 29224fb190eSDaniel W. S. Almeida { 29324fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 29424fb190eSDaniel W. S. Almeida struct vidtv_tuner_config config = tuner_dev->config; 29524fb190eSDaniel W. S. Almeida struct dtv_frontend_properties *c = &fe->dtv_property_cache; 296f3ea9da2SMauro Carvalho Chehab s32 shift; 29724fb190eSDaniel W. S. Almeida 29824fb190eSDaniel W. S. Almeida u32 min_freq = fe->ops.tuner_ops.info.frequency_min_hz; 29924fb190eSDaniel W. S. Almeida u32 max_freq = fe->ops.tuner_ops.info.frequency_max_hz; 30024fb190eSDaniel W. S. Almeida u32 min_bw = fe->ops.tuner_ops.info.bandwidth_min; 30124fb190eSDaniel W. S. Almeida u32 max_bw = fe->ops.tuner_ops.info.bandwidth_max; 30224fb190eSDaniel W. S. Almeida 30324fb190eSDaniel W. S. Almeida if (c->frequency < min_freq || c->frequency > max_freq || 30424fb190eSDaniel W. S. Almeida c->bandwidth_hz < min_bw || c->bandwidth_hz > max_bw) { 30524fb190eSDaniel W. S. Almeida tuner_dev->hw_state.lock_status = 0; 30624fb190eSDaniel W. S. Almeida return -EINVAL; 30724fb190eSDaniel W. S. Almeida } 30824fb190eSDaniel W. S. Almeida 30924fb190eSDaniel W. S. Almeida tuner_dev->hw_state.tuned_frequency = c->frequency; 31024fb190eSDaniel W. S. Almeida tuner_dev->hw_state.bandwidth = c->bandwidth_hz; 31124fb190eSDaniel W. S. Almeida tuner_dev->hw_state.lock_status = TUNER_STATUS_LOCKED; 31224fb190eSDaniel W. S. Almeida 31324fb190eSDaniel W. S. Almeida msleep_interruptible(config.mock_tune_delay_msec); 314f3ea9da2SMauro Carvalho Chehab 315f3ea9da2SMauro Carvalho Chehab shift = vidtv_tuner_check_frequency_shift(fe); 316f3ea9da2SMauro Carvalho Chehab if (shift < 0) { 317f3ea9da2SMauro Carvalho Chehab tuner_dev->hw_state.lock_status = 0; 318f3ea9da2SMauro Carvalho Chehab return shift; 319f3ea9da2SMauro Carvalho Chehab } 320f3ea9da2SMauro Carvalho Chehab 32124fb190eSDaniel W. S. Almeida return 0; 32224fb190eSDaniel W. S. Almeida } 32324fb190eSDaniel W. S. Almeida 32424fb190eSDaniel W. S. Almeida static int vidtv_tuner_set_config(struct dvb_frontend *fe, 32524fb190eSDaniel W. S. Almeida void *priv_cfg) 32624fb190eSDaniel W. S. Almeida { 32724fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 32824fb190eSDaniel W. S. Almeida 32924fb190eSDaniel W. S. Almeida memcpy(&tuner_dev->config, priv_cfg, sizeof(tuner_dev->config)); 33024fb190eSDaniel W. S. Almeida 33124fb190eSDaniel W. S. Almeida return 0; 33224fb190eSDaniel W. S. Almeida } 33324fb190eSDaniel W. S. Almeida 33424fb190eSDaniel W. S. Almeida static int vidtv_tuner_get_frequency(struct dvb_frontend *fe, 33524fb190eSDaniel W. S. Almeida u32 *frequency) 33624fb190eSDaniel W. S. Almeida { 33724fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 33824fb190eSDaniel W. S. Almeida 33924fb190eSDaniel W. S. Almeida *frequency = tuner_dev->hw_state.tuned_frequency; 34024fb190eSDaniel W. S. Almeida 34124fb190eSDaniel W. S. Almeida return 0; 34224fb190eSDaniel W. S. Almeida } 34324fb190eSDaniel W. S. Almeida 34424fb190eSDaniel W. S. Almeida static int vidtv_tuner_get_bandwidth(struct dvb_frontend *fe, 34524fb190eSDaniel W. S. Almeida u32 *bandwidth) 34624fb190eSDaniel W. S. Almeida { 34724fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 34824fb190eSDaniel W. S. Almeida 34924fb190eSDaniel W. S. Almeida *bandwidth = tuner_dev->hw_state.bandwidth; 35024fb190eSDaniel W. S. Almeida 35124fb190eSDaniel W. S. Almeida return 0; 35224fb190eSDaniel W. S. Almeida } 35324fb190eSDaniel W. S. Almeida 35424fb190eSDaniel W. S. Almeida static int vidtv_tuner_get_if_frequency(struct dvb_frontend *fe, 35524fb190eSDaniel W. S. Almeida u32 *frequency) 35624fb190eSDaniel W. S. Almeida { 35724fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 35824fb190eSDaniel W. S. Almeida 35924fb190eSDaniel W. S. Almeida *frequency = tuner_dev->hw_state.if_frequency; 36024fb190eSDaniel W. S. Almeida 36124fb190eSDaniel W. S. Almeida return 0; 36224fb190eSDaniel W. S. Almeida } 36324fb190eSDaniel W. S. Almeida 36424fb190eSDaniel W. S. Almeida static int vidtv_tuner_get_status(struct dvb_frontend *fe, u32 *status) 36524fb190eSDaniel W. S. Almeida { 36624fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = vidtv_tuner_get_dev(fe); 36724fb190eSDaniel W. S. Almeida 36824fb190eSDaniel W. S. Almeida *status = tuner_dev->hw_state.lock_status; 36924fb190eSDaniel W. S. Almeida 37024fb190eSDaniel W. S. Almeida return 0; 37124fb190eSDaniel W. S. Almeida } 37224fb190eSDaniel W. S. Almeida 37324fb190eSDaniel W. S. Almeida static const struct dvb_tuner_ops vidtv_tuner_ops = { 37424fb190eSDaniel W. S. Almeida .init = vidtv_tuner_init, 37524fb190eSDaniel W. S. Almeida .sleep = vidtv_tuner_sleep, 37624fb190eSDaniel W. S. Almeida .suspend = vidtv_tuner_suspend, 37724fb190eSDaniel W. S. Almeida .resume = vidtv_tuner_resume, 37824fb190eSDaniel W. S. Almeida .set_params = vidtv_tuner_set_params, 37924fb190eSDaniel W. S. Almeida .set_config = vidtv_tuner_set_config, 38024fb190eSDaniel W. S. Almeida .get_bandwidth = vidtv_tuner_get_bandwidth, 38124fb190eSDaniel W. S. Almeida .get_frequency = vidtv_tuner_get_frequency, 38224fb190eSDaniel W. S. Almeida .get_if_frequency = vidtv_tuner_get_if_frequency, 38324fb190eSDaniel W. S. Almeida .get_status = vidtv_tuner_get_status, 38424fb190eSDaniel W. S. Almeida .get_rf_strength = vidtv_tuner_get_signal_strength 38524fb190eSDaniel W. S. Almeida }; 38624fb190eSDaniel W. S. Almeida 38724fb190eSDaniel W. S. Almeida static const struct i2c_device_id vidtv_tuner_i2c_id_table[] = { 388*cc4cbd4bSUwe Kleine-König { "dvb_vidtv_tuner" }, 38924fb190eSDaniel W. S. Almeida {} 39024fb190eSDaniel W. S. Almeida }; 39124fb190eSDaniel W. S. Almeida MODULE_DEVICE_TABLE(i2c, vidtv_tuner_i2c_id_table); 39224fb190eSDaniel W. S. Almeida 3937d4833b1SUwe Kleine-König static int vidtv_tuner_i2c_probe(struct i2c_client *client) 39424fb190eSDaniel W. S. Almeida { 39524fb190eSDaniel W. S. Almeida struct vidtv_tuner_config *config = client->dev.platform_data; 39624fb190eSDaniel W. S. Almeida struct dvb_frontend *fe = config->fe; 39724fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = NULL; 39824fb190eSDaniel W. S. Almeida 39924fb190eSDaniel W. S. Almeida tuner_dev = kzalloc(sizeof(*tuner_dev), GFP_KERNEL); 40024fb190eSDaniel W. S. Almeida if (!tuner_dev) 40124fb190eSDaniel W. S. Almeida return -ENOMEM; 40224fb190eSDaniel W. S. Almeida 40324fb190eSDaniel W. S. Almeida tuner_dev->fe = config->fe; 40424fb190eSDaniel W. S. Almeida i2c_set_clientdata(client, tuner_dev); 40524fb190eSDaniel W. S. Almeida 40624fb190eSDaniel W. S. Almeida memcpy(&fe->ops.tuner_ops, 40724fb190eSDaniel W. S. Almeida &vidtv_tuner_ops, 40824fb190eSDaniel W. S. Almeida sizeof(struct dvb_tuner_ops)); 40924fb190eSDaniel W. S. Almeida 410f3ea9da2SMauro Carvalho Chehab memcpy(&tuner_dev->config, config, sizeof(tuner_dev->config)); 41124fb190eSDaniel W. S. Almeida fe->tuner_priv = client; 41224fb190eSDaniel W. S. Almeida 41324fb190eSDaniel W. S. Almeida return 0; 41424fb190eSDaniel W. S. Almeida } 41524fb190eSDaniel W. S. Almeida 416ed5c2f5fSUwe Kleine-König static void vidtv_tuner_i2c_remove(struct i2c_client *client) 41724fb190eSDaniel W. S. Almeida { 41824fb190eSDaniel W. S. Almeida struct vidtv_tuner_dev *tuner_dev = i2c_get_clientdata(client); 41924fb190eSDaniel W. S. Almeida 42024fb190eSDaniel W. S. Almeida kfree(tuner_dev); 42124fb190eSDaniel W. S. Almeida } 42224fb190eSDaniel W. S. Almeida 42324fb190eSDaniel W. S. Almeida static struct i2c_driver vidtv_tuner_i2c_driver = { 42424fb190eSDaniel W. S. Almeida .driver = { 42524fb190eSDaniel W. S. Almeida .name = "dvb_vidtv_tuner", 42624fb190eSDaniel W. S. Almeida .suppress_bind_attrs = true, 42724fb190eSDaniel W. S. Almeida }, 428aaeb31c0SUwe Kleine-König .probe = vidtv_tuner_i2c_probe, 42924fb190eSDaniel W. S. Almeida .remove = vidtv_tuner_i2c_remove, 43024fb190eSDaniel W. S. Almeida .id_table = vidtv_tuner_i2c_id_table, 43124fb190eSDaniel W. S. Almeida }; 43224fb190eSDaniel W. S. Almeida module_i2c_driver(vidtv_tuner_i2c_driver); 43324fb190eSDaniel W. S. Almeida 43424fb190eSDaniel W. S. Almeida MODULE_DESCRIPTION("Virtual DVB Tuner"); 43524fb190eSDaniel W. S. Almeida MODULE_AUTHOR("Daniel W. S. Almeida"); 43624fb190eSDaniel W. S. Almeida MODULE_LICENSE("GPL"); 437