18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * The Virtual DVB test driver serves as a reference DVB driver and helps 48c2ecf20Sopenharmony_ci * validate the existing APIs in the media subsystem. It can also aid 58c2ecf20Sopenharmony_ci * developers working on userspace applications. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2020 Daniel W. S. Almeida 88c2ecf20Sopenharmony_ci * Based on the example driver written by Emard <emard@softhome.net> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/printk.h> 178c2ecf20Sopenharmony_ci#include <linux/random.h> 188c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/string.h> 218c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "vidtv_demod.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define POLL_THRD_TIME 2000 /* ms */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic const struct vidtv_demod_cnr_to_qual_s vidtv_demod_c_cnr_2_qual[] = { 308c2ecf20Sopenharmony_ci /* from libdvbv5 source code, in milli db */ 318c2ecf20Sopenharmony_ci { QAM_256, FEC_NONE, 34000, 38000}, 328c2ecf20Sopenharmony_ci { QAM_64, FEC_NONE, 30000, 34000}, 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const struct vidtv_demod_cnr_to_qual_s vidtv_demod_s_cnr_2_qual[] = { 368c2ecf20Sopenharmony_ci /* from libdvbv5 source code, in milli db */ 378c2ecf20Sopenharmony_ci { QPSK, FEC_1_2, 7000, 10000}, 388c2ecf20Sopenharmony_ci { QPSK, FEC_2_3, 9000, 12000}, 398c2ecf20Sopenharmony_ci { QPSK, FEC_3_4, 10000, 13000}, 408c2ecf20Sopenharmony_ci { QPSK, FEC_5_6, 11000, 14000}, 418c2ecf20Sopenharmony_ci { QPSK, FEC_7_8, 12000, 15000}, 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic const struct vidtv_demod_cnr_to_qual_s vidtv_demod_s2_cnr_2_qual[] = { 458c2ecf20Sopenharmony_ci /* from libdvbv5 source code, in milli db */ 468c2ecf20Sopenharmony_ci { QPSK, FEC_1_2, 9000, 12000}, 478c2ecf20Sopenharmony_ci { QPSK, FEC_2_3, 11000, 14000}, 488c2ecf20Sopenharmony_ci { QPSK, FEC_3_4, 12000, 15000}, 498c2ecf20Sopenharmony_ci { QPSK, FEC_5_6, 12000, 15000}, 508c2ecf20Sopenharmony_ci { QPSK, FEC_8_9, 13000, 16000}, 518c2ecf20Sopenharmony_ci { QPSK, FEC_9_10, 13500, 16500}, 528c2ecf20Sopenharmony_ci { PSK_8, FEC_2_3, 14500, 17500}, 538c2ecf20Sopenharmony_ci { PSK_8, FEC_3_4, 16000, 19000}, 548c2ecf20Sopenharmony_ci { PSK_8, FEC_5_6, 17500, 20500}, 558c2ecf20Sopenharmony_ci { PSK_8, FEC_8_9, 19000, 22000}, 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic const struct vidtv_demod_cnr_to_qual_s vidtv_demod_t_cnr_2_qual[] = { 598c2ecf20Sopenharmony_ci /* from libdvbv5 source code, in milli db*/ 608c2ecf20Sopenharmony_ci { QPSK, FEC_1_2, 4100, 5900}, 618c2ecf20Sopenharmony_ci { QPSK, FEC_2_3, 6100, 9600}, 628c2ecf20Sopenharmony_ci { QPSK, FEC_3_4, 7200, 12400}, 638c2ecf20Sopenharmony_ci { QPSK, FEC_5_6, 8500, 15600}, 648c2ecf20Sopenharmony_ci { QPSK, FEC_7_8, 9200, 17500}, 658c2ecf20Sopenharmony_ci { QAM_16, FEC_1_2, 9800, 11800}, 668c2ecf20Sopenharmony_ci { QAM_16, FEC_2_3, 12100, 15300}, 678c2ecf20Sopenharmony_ci { QAM_16, FEC_3_4, 13400, 18100}, 688c2ecf20Sopenharmony_ci { QAM_16, FEC_5_6, 14800, 21300}, 698c2ecf20Sopenharmony_ci { QAM_16, FEC_7_8, 15700, 23600}, 708c2ecf20Sopenharmony_ci { QAM_64, FEC_1_2, 14000, 16000}, 718c2ecf20Sopenharmony_ci { QAM_64, FEC_2_3, 19900, 25400}, 728c2ecf20Sopenharmony_ci { QAM_64, FEC_3_4, 24900, 27900}, 738c2ecf20Sopenharmony_ci { QAM_64, FEC_5_6, 21300, 23300}, 748c2ecf20Sopenharmony_ci { QAM_64, FEC_7_8, 22000, 24000}, 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const struct vidtv_demod_cnr_to_qual_s *vidtv_match_cnr_s(struct dvb_frontend *fe) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci const struct vidtv_demod_cnr_to_qual_s *cnr2qual = NULL; 808c2ecf20Sopenharmony_ci struct device *dev = fe->dvb->device; 818c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c; 828c2ecf20Sopenharmony_ci u32 array_size = 0; 838c2ecf20Sopenharmony_ci u32 i; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci c = &fe->dtv_property_cache; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci switch (c->delivery_system) { 888c2ecf20Sopenharmony_ci case SYS_DVBT: 898c2ecf20Sopenharmony_ci case SYS_DVBT2: 908c2ecf20Sopenharmony_ci cnr2qual = vidtv_demod_t_cnr_2_qual; 918c2ecf20Sopenharmony_ci array_size = ARRAY_SIZE(vidtv_demod_t_cnr_2_qual); 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci case SYS_DVBS: 958c2ecf20Sopenharmony_ci cnr2qual = vidtv_demod_s_cnr_2_qual; 968c2ecf20Sopenharmony_ci array_size = ARRAY_SIZE(vidtv_demod_s_cnr_2_qual); 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci case SYS_DVBS2: 1008c2ecf20Sopenharmony_ci cnr2qual = vidtv_demod_s2_cnr_2_qual; 1018c2ecf20Sopenharmony_ci array_size = ARRAY_SIZE(vidtv_demod_s2_cnr_2_qual); 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci case SYS_DVBC_ANNEX_A: 1058c2ecf20Sopenharmony_ci cnr2qual = vidtv_demod_c_cnr_2_qual; 1068c2ecf20Sopenharmony_ci array_size = ARRAY_SIZE(vidtv_demod_c_cnr_2_qual); 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci default: 1108c2ecf20Sopenharmony_ci dev_warn_ratelimited(dev, 1118c2ecf20Sopenharmony_ci "%s: unsupported delivery system: %u\n", 1128c2ecf20Sopenharmony_ci __func__, 1138c2ecf20Sopenharmony_ci c->delivery_system); 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci for (i = 0; i < array_size; i++) 1188c2ecf20Sopenharmony_ci if (cnr2qual[i].modulation == c->modulation && 1198c2ecf20Sopenharmony_ci cnr2qual[i].fec == c->fec_inner) 1208c2ecf20Sopenharmony_ci return &cnr2qual[i]; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return NULL; /* not found */ 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void vidtv_clean_stats(struct dvb_frontend *fe) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* Fill the length of each status counter */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Signal is always available */ 1328c2ecf20Sopenharmony_ci c->strength.len = 1; 1338c2ecf20Sopenharmony_ci c->strength.stat[0].scale = FE_SCALE_DECIBEL; 1348c2ecf20Sopenharmony_ci c->strength.stat[0].svalue = 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Usually available only after Viterbi lock */ 1378c2ecf20Sopenharmony_ci c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1388c2ecf20Sopenharmony_ci c->cnr.stat[0].svalue = 0; 1398c2ecf20Sopenharmony_ci c->cnr.len = 1; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Those depends on full lock */ 1428c2ecf20Sopenharmony_ci c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1438c2ecf20Sopenharmony_ci c->pre_bit_error.stat[0].uvalue = 0; 1448c2ecf20Sopenharmony_ci c->pre_bit_error.len = 1; 1458c2ecf20Sopenharmony_ci c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1468c2ecf20Sopenharmony_ci c->pre_bit_count.stat[0].uvalue = 0; 1478c2ecf20Sopenharmony_ci c->pre_bit_count.len = 1; 1488c2ecf20Sopenharmony_ci c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1498c2ecf20Sopenharmony_ci c->post_bit_error.stat[0].uvalue = 0; 1508c2ecf20Sopenharmony_ci c->post_bit_error.len = 1; 1518c2ecf20Sopenharmony_ci c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1528c2ecf20Sopenharmony_ci c->post_bit_count.stat[0].uvalue = 0; 1538c2ecf20Sopenharmony_ci c->post_bit_count.len = 1; 1548c2ecf20Sopenharmony_ci c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1558c2ecf20Sopenharmony_ci c->block_error.stat[0].uvalue = 0; 1568c2ecf20Sopenharmony_ci c->block_error.len = 1; 1578c2ecf20Sopenharmony_ci c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1588c2ecf20Sopenharmony_ci c->block_count.stat[0].uvalue = 0; 1598c2ecf20Sopenharmony_ci c->block_count.len = 1; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void vidtv_demod_update_stats(struct dvb_frontend *fe) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 1658c2ecf20Sopenharmony_ci struct vidtv_demod_state *state = fe->demodulator_priv; 1668c2ecf20Sopenharmony_ci u32 scale; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (state->status & FE_HAS_LOCK) { 1698c2ecf20Sopenharmony_ci scale = FE_SCALE_COUNTER; 1708c2ecf20Sopenharmony_ci c->cnr.stat[0].scale = FE_SCALE_DECIBEL; 1718c2ecf20Sopenharmony_ci } else { 1728c2ecf20Sopenharmony_ci scale = FE_SCALE_NOT_AVAILABLE; 1738c2ecf20Sopenharmony_ci c->cnr.stat[0].scale = scale; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci c->pre_bit_error.stat[0].scale = scale; 1778c2ecf20Sopenharmony_ci c->pre_bit_count.stat[0].scale = scale; 1788c2ecf20Sopenharmony_ci c->post_bit_error.stat[0].scale = scale; 1798c2ecf20Sopenharmony_ci c->post_bit_count.stat[0].scale = scale; 1808c2ecf20Sopenharmony_ci c->block_error.stat[0].scale = scale; 1818c2ecf20Sopenharmony_ci c->block_count.stat[0].scale = scale; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * Add a 0.5% of randomness at the signal strength and CNR, 1858c2ecf20Sopenharmony_ci * and make them different, as we want to have something closer 1868c2ecf20Sopenharmony_ci * to a real case scenario. 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * Also, usually, signal strength is a negative number in dBm. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci c->strength.stat[0].svalue = state->tuner_cnr; 1918c2ecf20Sopenharmony_ci c->strength.stat[0].svalue -= prandom_u32_max(state->tuner_cnr / 50); 1928c2ecf20Sopenharmony_ci c->strength.stat[0].svalue -= 68000; /* Adjust to a better range */ 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci c->cnr.stat[0].svalue = state->tuner_cnr; 1958c2ecf20Sopenharmony_ci c->cnr.stat[0].svalue -= prandom_u32_max(state->tuner_cnr / 50); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int vidtv_demod_read_status(struct dvb_frontend *fe, 1998c2ecf20Sopenharmony_ci enum fe_status *status) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct vidtv_demod_state *state = fe->demodulator_priv; 2028c2ecf20Sopenharmony_ci const struct vidtv_demod_cnr_to_qual_s *cnr2qual = NULL; 2038c2ecf20Sopenharmony_ci struct vidtv_demod_config *config = &state->config; 2048c2ecf20Sopenharmony_ci u16 snr = 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Simulate random lost of signal due to a bad-tuned channel */ 2078c2ecf20Sopenharmony_ci cnr2qual = vidtv_match_cnr_s(&state->frontend); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (cnr2qual && state->tuner_cnr < cnr2qual->cnr_good && 2108c2ecf20Sopenharmony_ci state->frontend.ops.tuner_ops.get_rf_strength) { 2118c2ecf20Sopenharmony_ci state->frontend.ops.tuner_ops.get_rf_strength(&state->frontend, 2128c2ecf20Sopenharmony_ci &snr); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (snr < cnr2qual->cnr_ok) { 2158c2ecf20Sopenharmony_ci /* eventually lose the TS lock */ 2168c2ecf20Sopenharmony_ci if (prandom_u32_max(100) < config->drop_tslock_prob_on_low_snr) 2178c2ecf20Sopenharmony_ci state->status = 0; 2188c2ecf20Sopenharmony_ci } else { 2198c2ecf20Sopenharmony_ci /* recover if the signal improves */ 2208c2ecf20Sopenharmony_ci if (prandom_u32_max(100) < 2218c2ecf20Sopenharmony_ci config->recover_tslock_prob_on_good_snr) 2228c2ecf20Sopenharmony_ci state->status = FE_HAS_SIGNAL | 2238c2ecf20Sopenharmony_ci FE_HAS_CARRIER | 2248c2ecf20Sopenharmony_ci FE_HAS_VITERBI | 2258c2ecf20Sopenharmony_ci FE_HAS_SYNC | 2268c2ecf20Sopenharmony_ci FE_HAS_LOCK; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci vidtv_demod_update_stats(&state->frontend); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci *status = state->status; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int vidtv_demod_read_signal_strength(struct dvb_frontend *fe, 2388c2ecf20Sopenharmony_ci u16 *strength) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci *strength = c->strength.stat[0].uvalue; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * NOTE: 2498c2ecf20Sopenharmony_ci * This is implemented here just to be used as an example for real 2508c2ecf20Sopenharmony_ci * demod drivers. 2518c2ecf20Sopenharmony_ci * 2528c2ecf20Sopenharmony_ci * Should only be implemented if it actually reads something from the hardware. 2538c2ecf20Sopenharmony_ci * Also, it should check for the locks, in order to avoid report wrong data 2548c2ecf20Sopenharmony_ci * to userspace. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_cistatic int vidtv_demod_get_frontend(struct dvb_frontend *fe, 2578c2ecf20Sopenharmony_ci struct dtv_frontend_properties *p) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int vidtv_demod_set_frontend(struct dvb_frontend *fe) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct vidtv_demod_state *state = fe->demodulator_priv; 2658c2ecf20Sopenharmony_ci u32 tuner_status = 0; 2668c2ecf20Sopenharmony_ci int ret; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (!fe->ops.tuner_ops.set_params) 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci fe->ops.tuner_ops.set_params(fe); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* store the CNR returned by the tuner */ 2748c2ecf20Sopenharmony_ci ret = fe->ops.tuner_ops.get_rf_strength(fe, &state->tuner_cnr); 2758c2ecf20Sopenharmony_ci if (ret < 0) 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci fe->ops.tuner_ops.get_status(fe, &tuner_status); 2798c2ecf20Sopenharmony_ci state->status = (state->tuner_cnr > 0) ? FE_HAS_SIGNAL | 2808c2ecf20Sopenharmony_ci FE_HAS_CARRIER | 2818c2ecf20Sopenharmony_ci FE_HAS_VITERBI | 2828c2ecf20Sopenharmony_ci FE_HAS_SYNC | 2838c2ecf20Sopenharmony_ci FE_HAS_LOCK : 2848c2ecf20Sopenharmony_ci 0; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci vidtv_demod_update_stats(fe); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 2898c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * NOTE: 2968c2ecf20Sopenharmony_ci * This is implemented here just to be used as an example for real 2978c2ecf20Sopenharmony_ci * demod drivers. 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * Should only be implemented if the demod has support for DVB-S or DVB-S2 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_cistatic int vidtv_demod_set_tone(struct dvb_frontend *fe, 3028c2ecf20Sopenharmony_ci enum fe_sec_tone_mode tone) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci/* 3088c2ecf20Sopenharmony_ci * NOTE: 3098c2ecf20Sopenharmony_ci * This is implemented here just to be used as an example for real 3108c2ecf20Sopenharmony_ci * demod drivers. 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * Should only be implemented if the demod has support for DVB-S or DVB-S2 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_cistatic int vidtv_demod_set_voltage(struct dvb_frontend *fe, 3158c2ecf20Sopenharmony_ci enum fe_sec_voltage voltage) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/* 3218c2ecf20Sopenharmony_ci * NOTE: 3228c2ecf20Sopenharmony_ci * This is implemented here just to be used as an example for real 3238c2ecf20Sopenharmony_ci * demod drivers. 3248c2ecf20Sopenharmony_ci * 3258c2ecf20Sopenharmony_ci * Should only be implemented if the demod has support for DVB-S or DVB-S2 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_cistatic int vidtv_send_diseqc_msg(struct dvb_frontend *fe, 3288c2ecf20Sopenharmony_ci struct dvb_diseqc_master_cmd *cmd) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* 3348c2ecf20Sopenharmony_ci * NOTE: 3358c2ecf20Sopenharmony_ci * This is implemented here just to be used as an example for real 3368c2ecf20Sopenharmony_ci * demod drivers. 3378c2ecf20Sopenharmony_ci * 3388c2ecf20Sopenharmony_ci * Should only be implemented if the demod has support for DVB-S or DVB-S2 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_cistatic int vidtv_diseqc_send_burst(struct dvb_frontend *fe, 3418c2ecf20Sopenharmony_ci enum fe_sec_mini_cmd burst) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic void vidtv_demod_release(struct dvb_frontend *fe) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct vidtv_demod_state *state = fe->demodulator_priv; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci kfree(state); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic const struct dvb_frontend_ops vidtv_demod_ops = { 3548c2ecf20Sopenharmony_ci .delsys = { 3558c2ecf20Sopenharmony_ci SYS_DVBT, 3568c2ecf20Sopenharmony_ci SYS_DVBT2, 3578c2ecf20Sopenharmony_ci SYS_DVBC_ANNEX_A, 3588c2ecf20Sopenharmony_ci SYS_DVBS, 3598c2ecf20Sopenharmony_ci SYS_DVBS2, 3608c2ecf20Sopenharmony_ci }, 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci .info = { 3638c2ecf20Sopenharmony_ci .name = "Dummy demod for DVB-T/T2/C/S/S2", 3648c2ecf20Sopenharmony_ci .frequency_min_hz = 51 * MHz, 3658c2ecf20Sopenharmony_ci .frequency_max_hz = 2150 * MHz, 3668c2ecf20Sopenharmony_ci .frequency_stepsize_hz = 62500, 3678c2ecf20Sopenharmony_ci .frequency_tolerance_hz = 29500 * kHz, 3688c2ecf20Sopenharmony_ci .symbol_rate_min = 1000000, 3698c2ecf20Sopenharmony_ci .symbol_rate_max = 45000000, 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci .caps = FE_CAN_FEC_1_2 | 3728c2ecf20Sopenharmony_ci FE_CAN_FEC_2_3 | 3738c2ecf20Sopenharmony_ci FE_CAN_FEC_3_4 | 3748c2ecf20Sopenharmony_ci FE_CAN_FEC_4_5 | 3758c2ecf20Sopenharmony_ci FE_CAN_FEC_5_6 | 3768c2ecf20Sopenharmony_ci FE_CAN_FEC_6_7 | 3778c2ecf20Sopenharmony_ci FE_CAN_FEC_7_8 | 3788c2ecf20Sopenharmony_ci FE_CAN_FEC_8_9 | 3798c2ecf20Sopenharmony_ci FE_CAN_QAM_16 | 3808c2ecf20Sopenharmony_ci FE_CAN_QAM_64 | 3818c2ecf20Sopenharmony_ci FE_CAN_QAM_32 | 3828c2ecf20Sopenharmony_ci FE_CAN_QAM_128 | 3838c2ecf20Sopenharmony_ci FE_CAN_QAM_256 | 3848c2ecf20Sopenharmony_ci FE_CAN_QAM_AUTO | 3858c2ecf20Sopenharmony_ci FE_CAN_QPSK | 3868c2ecf20Sopenharmony_ci FE_CAN_FEC_AUTO | 3878c2ecf20Sopenharmony_ci FE_CAN_INVERSION_AUTO | 3888c2ecf20Sopenharmony_ci FE_CAN_TRANSMISSION_MODE_AUTO | 3898c2ecf20Sopenharmony_ci FE_CAN_GUARD_INTERVAL_AUTO | 3908c2ecf20Sopenharmony_ci FE_CAN_HIERARCHY_AUTO, 3918c2ecf20Sopenharmony_ci }, 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci .release = vidtv_demod_release, 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci .set_frontend = vidtv_demod_set_frontend, 3968c2ecf20Sopenharmony_ci .get_frontend = vidtv_demod_get_frontend, 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci .read_status = vidtv_demod_read_status, 3998c2ecf20Sopenharmony_ci .read_signal_strength = vidtv_demod_read_signal_strength, 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* For DVB-S/S2 */ 4028c2ecf20Sopenharmony_ci .set_voltage = vidtv_demod_set_voltage, 4038c2ecf20Sopenharmony_ci .set_tone = vidtv_demod_set_tone, 4048c2ecf20Sopenharmony_ci .diseqc_send_master_cmd = vidtv_send_diseqc_msg, 4058c2ecf20Sopenharmony_ci .diseqc_send_burst = vidtv_diseqc_send_burst, 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci}; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic const struct i2c_device_id vidtv_demod_i2c_id_table[] = { 4108c2ecf20Sopenharmony_ci {"dvb_vidtv_demod", 0}, 4118c2ecf20Sopenharmony_ci {} 4128c2ecf20Sopenharmony_ci}; 4138c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic int vidtv_demod_i2c_probe(struct i2c_client *client, 4168c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct vidtv_tuner_config *config = client->dev.platform_data; 4198c2ecf20Sopenharmony_ci struct vidtv_demod_state *state; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* allocate memory for the internal state */ 4228c2ecf20Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 4238c2ecf20Sopenharmony_ci if (!state) 4248c2ecf20Sopenharmony_ci return -ENOMEM; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* create dvb_frontend */ 4278c2ecf20Sopenharmony_ci memcpy(&state->frontend.ops, 4288c2ecf20Sopenharmony_ci &vidtv_demod_ops, 4298c2ecf20Sopenharmony_ci sizeof(struct dvb_frontend_ops)); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci memcpy(&state->config, config, sizeof(state->config)); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci state->frontend.demodulator_priv = state; 4348c2ecf20Sopenharmony_ci i2c_set_clientdata(client, state); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci vidtv_clean_stats(&state->frontend); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic int vidtv_demod_i2c_remove(struct i2c_client *client) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct vidtv_demod_state *state = i2c_get_clientdata(client); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci kfree(state); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic struct i2c_driver vidtv_demod_i2c_driver = { 4518c2ecf20Sopenharmony_ci .driver = { 4528c2ecf20Sopenharmony_ci .name = "dvb_vidtv_demod", 4538c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 4548c2ecf20Sopenharmony_ci }, 4558c2ecf20Sopenharmony_ci .probe = vidtv_demod_i2c_probe, 4568c2ecf20Sopenharmony_ci .remove = vidtv_demod_i2c_remove, 4578c2ecf20Sopenharmony_ci .id_table = vidtv_demod_i2c_id_table, 4588c2ecf20Sopenharmony_ci}; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cimodule_i2c_driver(vidtv_demod_i2c_driver); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Virtual DVB Demodulator Driver"); 4638c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel W. S. Almeida"); 4648c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 465