1f5ffc3b6SDaniel W. S. Almeida // SPDX-License-Identifier: GPL-2.0-or-later 2f5ffc3b6SDaniel W. S. Almeida /* 3f5ffc3b6SDaniel W. S. Almeida * The Virtual DVB test driver serves as a reference DVB driver and helps 4f5ffc3b6SDaniel W. S. Almeida * validate the existing APIs in the media subsystem. It can also aid 5f5ffc3b6SDaniel W. S. Almeida * developers working on userspace applications. 6f5ffc3b6SDaniel W. S. Almeida * 7f5ffc3b6SDaniel W. S. Almeida * Copyright (C) 2020 Daniel W. S. Almeida 8f5ffc3b6SDaniel W. S. Almeida * Based on the example driver written by Emard <emard@softhome.net> 9f5ffc3b6SDaniel W. S. Almeida */ 10f5ffc3b6SDaniel W. S. Almeida 11f5ffc3b6SDaniel W. S. Almeida #include <linux/errno.h> 12f5ffc3b6SDaniel W. S. Almeida #include <linux/i2c.h> 13f5ffc3b6SDaniel W. S. Almeida #include <linux/init.h> 14f5ffc3b6SDaniel W. S. Almeida #include <linux/kernel.h> 15f5ffc3b6SDaniel W. S. Almeida #include <linux/module.h> 16f5ffc3b6SDaniel W. S. Almeida #include <linux/printk.h> 17f5ffc3b6SDaniel W. S. Almeida #include <linux/random.h> 18f5ffc3b6SDaniel W. S. Almeida #include <linux/ratelimit.h> 19f5ffc3b6SDaniel W. S. Almeida #include <linux/slab.h> 20f5ffc3b6SDaniel W. S. Almeida #include <linux/string.h> 21f5ffc3b6SDaniel W. S. Almeida #include <linux/workqueue.h> 228922e393SMauro Carvalho Chehab 23f5ffc3b6SDaniel W. S. Almeida #include <media/dvb_frontend.h> 24f5ffc3b6SDaniel W. S. Almeida 25f5ffc3b6SDaniel W. S. Almeida #include "vidtv_demod.h" 26f5ffc3b6SDaniel W. S. Almeida 27f5ffc3b6SDaniel W. S. Almeida #define POLL_THRD_TIME 2000 /* ms */ 28f5ffc3b6SDaniel W. S. Almeida 29f5ffc3b6SDaniel W. S. Almeida static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_c_cnr_2_qual[] = { 30f5ffc3b6SDaniel W. S. Almeida /* from libdvbv5 source code, in milli db */ 31f5ffc3b6SDaniel W. S. Almeida { QAM_256, FEC_NONE, 34000, 38000}, 32f5ffc3b6SDaniel W. S. Almeida { QAM_64, FEC_NONE, 30000, 34000}, 33f5ffc3b6SDaniel W. S. Almeida }; 34f5ffc3b6SDaniel W. S. Almeida 35f5ffc3b6SDaniel W. S. Almeida static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_s_cnr_2_qual[] = { 36f5ffc3b6SDaniel W. S. Almeida /* from libdvbv5 source code, in milli db */ 37f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_1_2, 7000, 10000}, 38f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_2_3, 9000, 12000}, 39f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_3_4, 10000, 13000}, 40f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_5_6, 11000, 14000}, 41f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_7_8, 12000, 15000}, 42f5ffc3b6SDaniel W. S. Almeida }; 43f5ffc3b6SDaniel W. S. Almeida 44f5ffc3b6SDaniel W. S. Almeida static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_s2_cnr_2_qual[] = { 45f5ffc3b6SDaniel W. S. Almeida /* from libdvbv5 source code, in milli db */ 46f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_1_2, 9000, 12000}, 47f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_2_3, 11000, 14000}, 48f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_3_4, 12000, 15000}, 49f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_5_6, 12000, 15000}, 50f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_8_9, 13000, 16000}, 51f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_9_10, 13500, 16500}, 52f5ffc3b6SDaniel W. S. Almeida { PSK_8, FEC_2_3, 14500, 17500}, 53f5ffc3b6SDaniel W. S. Almeida { PSK_8, FEC_3_4, 16000, 19000}, 54f5ffc3b6SDaniel W. S. Almeida { PSK_8, FEC_5_6, 17500, 20500}, 55f5ffc3b6SDaniel W. S. Almeida { PSK_8, FEC_8_9, 19000, 22000}, 56f5ffc3b6SDaniel W. S. Almeida }; 57f5ffc3b6SDaniel W. S. Almeida 58f5ffc3b6SDaniel W. S. Almeida static const struct vidtv_demod_cnr_to_qual_s vidtv_demod_t_cnr_2_qual[] = { 59f5ffc3b6SDaniel W. S. Almeida /* from libdvbv5 source code, in milli db*/ 60f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_1_2, 4100, 5900}, 61f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_2_3, 6100, 9600}, 62f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_3_4, 7200, 12400}, 63f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_5_6, 8500, 15600}, 64f5ffc3b6SDaniel W. S. Almeida { QPSK, FEC_7_8, 9200, 17500}, 65f5ffc3b6SDaniel W. S. Almeida { QAM_16, FEC_1_2, 9800, 11800}, 66f5ffc3b6SDaniel W. S. Almeida { QAM_16, FEC_2_3, 12100, 15300}, 67f5ffc3b6SDaniel W. S. Almeida { QAM_16, FEC_3_4, 13400, 18100}, 68f5ffc3b6SDaniel W. S. Almeida { QAM_16, FEC_5_6, 14800, 21300}, 69f5ffc3b6SDaniel W. S. Almeida { QAM_16, FEC_7_8, 15700, 23600}, 70f5ffc3b6SDaniel W. S. Almeida { QAM_64, FEC_1_2, 14000, 16000}, 71f5ffc3b6SDaniel W. S. Almeida { QAM_64, FEC_2_3, 19900, 25400}, 72f5ffc3b6SDaniel W. S. Almeida { QAM_64, FEC_3_4, 24900, 27900}, 73f5ffc3b6SDaniel W. S. Almeida { QAM_64, FEC_5_6, 21300, 23300}, 74f5ffc3b6SDaniel W. S. Almeida { QAM_64, FEC_7_8, 22000, 24000}, 75f5ffc3b6SDaniel W. S. Almeida }; 76f5ffc3b6SDaniel W. S. Almeida 77741043b0SMauro Carvalho Chehab static const struct vidtv_demod_cnr_to_qual_s *vidtv_match_cnr_s(struct dvb_frontend *fe) 78f5ffc3b6SDaniel W. S. Almeida { 79f5ffc3b6SDaniel W. S. Almeida const struct vidtv_demod_cnr_to_qual_s *cnr2qual = NULL; 809cfb4d36SMauro Carvalho Chehab struct device *dev = fe->dvb->device; 819cfb4d36SMauro Carvalho Chehab struct dtv_frontend_properties *c; 82f5ffc3b6SDaniel W. S. Almeida u32 array_size = 0; 83f5ffc3b6SDaniel W. S. Almeida u32 i; 84f5ffc3b6SDaniel W. S. Almeida 85f5ffc3b6SDaniel W. S. Almeida c = &fe->dtv_property_cache; 86f5ffc3b6SDaniel W. S. Almeida 87f5ffc3b6SDaniel W. S. Almeida switch (c->delivery_system) { 88f5ffc3b6SDaniel W. S. Almeida case SYS_DVBT: 89f5ffc3b6SDaniel W. S. Almeida case SYS_DVBT2: 90f5ffc3b6SDaniel W. S. Almeida cnr2qual = vidtv_demod_t_cnr_2_qual; 91f5ffc3b6SDaniel W. S. Almeida array_size = ARRAY_SIZE(vidtv_demod_t_cnr_2_qual); 92f5ffc3b6SDaniel W. S. Almeida break; 93f5ffc3b6SDaniel W. S. Almeida 94f5ffc3b6SDaniel W. S. Almeida case SYS_DVBS: 95f5ffc3b6SDaniel W. S. Almeida cnr2qual = vidtv_demod_s_cnr_2_qual; 96f5ffc3b6SDaniel W. S. Almeida array_size = ARRAY_SIZE(vidtv_demod_s_cnr_2_qual); 97f5ffc3b6SDaniel W. S. Almeida break; 98f5ffc3b6SDaniel W. S. Almeida 99f5ffc3b6SDaniel W. S. Almeida case SYS_DVBS2: 100f5ffc3b6SDaniel W. S. Almeida cnr2qual = vidtv_demod_s2_cnr_2_qual; 101f5ffc3b6SDaniel W. S. Almeida array_size = ARRAY_SIZE(vidtv_demod_s2_cnr_2_qual); 102f5ffc3b6SDaniel W. S. Almeida break; 103f5ffc3b6SDaniel W. S. Almeida 104f5ffc3b6SDaniel W. S. Almeida case SYS_DVBC_ANNEX_A: 105f5ffc3b6SDaniel W. S. Almeida cnr2qual = vidtv_demod_c_cnr_2_qual; 106f5ffc3b6SDaniel W. S. Almeida array_size = ARRAY_SIZE(vidtv_demod_c_cnr_2_qual); 107f5ffc3b6SDaniel W. S. Almeida break; 108f5ffc3b6SDaniel W. S. Almeida 109f5ffc3b6SDaniel W. S. Almeida default: 1109cfb4d36SMauro Carvalho Chehab dev_warn_ratelimited(dev, 1119cfb4d36SMauro Carvalho Chehab "%s: unsupported delivery system: %u\n", 112f5ffc3b6SDaniel W. S. Almeida __func__, 113f5ffc3b6SDaniel W. S. Almeida c->delivery_system); 114f5ffc3b6SDaniel W. S. Almeida break; 115f5ffc3b6SDaniel W. S. Almeida } 116f5ffc3b6SDaniel W. S. Almeida 117f5ffc3b6SDaniel W. S. Almeida for (i = 0; i < array_size; i++) 118f5ffc3b6SDaniel W. S. Almeida if (cnr2qual[i].modulation == c->modulation && 119f5ffc3b6SDaniel W. S. Almeida cnr2qual[i].fec == c->fec_inner) 120f5ffc3b6SDaniel W. S. Almeida return &cnr2qual[i]; 121f5ffc3b6SDaniel W. S. Almeida 122f5ffc3b6SDaniel W. S. Almeida return NULL; /* not found */ 123f5ffc3b6SDaniel W. S. Almeida } 124f5ffc3b6SDaniel W. S. Almeida 1253e51a496SMauro Carvalho Chehab static void vidtv_clean_stats(struct dvb_frontend *fe) 1263e51a496SMauro Carvalho Chehab { 1273e51a496SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 1283e51a496SMauro Carvalho Chehab 1293e51a496SMauro Carvalho Chehab /* Fill the length of each status counter */ 1303e51a496SMauro Carvalho Chehab 1313e51a496SMauro Carvalho Chehab /* Signal is always available */ 1323e51a496SMauro Carvalho Chehab c->strength.len = 1; 1333e51a496SMauro Carvalho Chehab c->strength.stat[0].scale = FE_SCALE_DECIBEL; 1343e51a496SMauro Carvalho Chehab c->strength.stat[0].svalue = 0; 1353e51a496SMauro Carvalho Chehab 1363e51a496SMauro Carvalho Chehab /* Usually available only after Viterbi lock */ 1373e51a496SMauro Carvalho Chehab c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1383e51a496SMauro Carvalho Chehab c->cnr.stat[0].svalue = 0; 1393e51a496SMauro Carvalho Chehab c->cnr.len = 1; 1403e51a496SMauro Carvalho Chehab 1413e51a496SMauro Carvalho Chehab /* Those depends on full lock */ 1423e51a496SMauro Carvalho Chehab c->pre_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1433e51a496SMauro Carvalho Chehab c->pre_bit_error.stat[0].uvalue = 0; 1443e51a496SMauro Carvalho Chehab c->pre_bit_error.len = 1; 1453e51a496SMauro Carvalho Chehab c->pre_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1463e51a496SMauro Carvalho Chehab c->pre_bit_count.stat[0].uvalue = 0; 1473e51a496SMauro Carvalho Chehab c->pre_bit_count.len = 1; 1483e51a496SMauro Carvalho Chehab c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1493e51a496SMauro Carvalho Chehab c->post_bit_error.stat[0].uvalue = 0; 1503e51a496SMauro Carvalho Chehab c->post_bit_error.len = 1; 1513e51a496SMauro Carvalho Chehab c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1523e51a496SMauro Carvalho Chehab c->post_bit_count.stat[0].uvalue = 0; 1533e51a496SMauro Carvalho Chehab c->post_bit_count.len = 1; 1543e51a496SMauro Carvalho Chehab c->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1553e51a496SMauro Carvalho Chehab c->block_error.stat[0].uvalue = 0; 1563e51a496SMauro Carvalho Chehab c->block_error.len = 1; 1573e51a496SMauro Carvalho Chehab c->block_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; 1583e51a496SMauro Carvalho Chehab c->block_count.stat[0].uvalue = 0; 1593e51a496SMauro Carvalho Chehab c->block_count.len = 1; 1603e51a496SMauro Carvalho Chehab } 1613e51a496SMauro Carvalho Chehab 1623e51a496SMauro Carvalho Chehab static void vidtv_demod_update_stats(struct dvb_frontend *fe) 1633e51a496SMauro Carvalho Chehab { 1643e51a496SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 1653e51a496SMauro Carvalho Chehab struct vidtv_demod_state *state = fe->demodulator_priv; 1663e51a496SMauro Carvalho Chehab u32 scale; 1673e51a496SMauro Carvalho Chehab 1683e51a496SMauro Carvalho Chehab if (state->status & FE_HAS_LOCK) { 1693e51a496SMauro Carvalho Chehab scale = FE_SCALE_COUNTER; 1703e51a496SMauro Carvalho Chehab c->cnr.stat[0].scale = FE_SCALE_DECIBEL; 1713e51a496SMauro Carvalho Chehab } else { 1723e51a496SMauro Carvalho Chehab scale = FE_SCALE_NOT_AVAILABLE; 1733e51a496SMauro Carvalho Chehab c->cnr.stat[0].scale = scale; 1743e51a496SMauro Carvalho Chehab } 1753e51a496SMauro Carvalho Chehab 1763e51a496SMauro Carvalho Chehab c->pre_bit_error.stat[0].scale = scale; 1773e51a496SMauro Carvalho Chehab c->pre_bit_count.stat[0].scale = scale; 1783e51a496SMauro Carvalho Chehab c->post_bit_error.stat[0].scale = scale; 1793e51a496SMauro Carvalho Chehab c->post_bit_count.stat[0].scale = scale; 1803e51a496SMauro Carvalho Chehab c->block_error.stat[0].scale = scale; 1813e51a496SMauro Carvalho Chehab c->block_count.stat[0].scale = scale; 1823e51a496SMauro Carvalho Chehab 1833e51a496SMauro Carvalho Chehab /* 184d859a712SMauro Carvalho Chehab * Add a 0.5% of randomness at the signal strength and CNR, 1853e51a496SMauro Carvalho Chehab * and make them different, as we want to have something closer 1863e51a496SMauro Carvalho Chehab * to a real case scenario. 187d859a712SMauro Carvalho Chehab * 188d859a712SMauro Carvalho Chehab * Also, usually, signal strength is a negative number in dBm. 1893e51a496SMauro Carvalho Chehab */ 190d859a712SMauro Carvalho Chehab c->strength.stat[0].svalue = state->tuner_cnr; 1918032bf12SJason A. Donenfeld c->strength.stat[0].svalue -= get_random_u32_below(state->tuner_cnr / 50); 192d859a712SMauro Carvalho Chehab c->strength.stat[0].svalue -= 68000; /* Adjust to a better range */ 193d859a712SMauro Carvalho Chehab 194d859a712SMauro Carvalho Chehab c->cnr.stat[0].svalue = state->tuner_cnr; 1958032bf12SJason A. Donenfeld c->cnr.stat[0].svalue -= get_random_u32_below(state->tuner_cnr / 50); 1963e51a496SMauro Carvalho Chehab } 1973e51a496SMauro Carvalho Chehab 198f58cac01SMauro Carvalho Chehab static int vidtv_demod_read_status(struct dvb_frontend *fe, 199f58cac01SMauro Carvalho Chehab enum fe_status *status) 200f5ffc3b6SDaniel W. S. Almeida { 201f58cac01SMauro Carvalho Chehab struct vidtv_demod_state *state = fe->demodulator_priv; 202f5ffc3b6SDaniel W. S. Almeida const struct vidtv_demod_cnr_to_qual_s *cnr2qual = NULL; 203f58cac01SMauro Carvalho Chehab struct vidtv_demod_config *config = &state->config; 204f5ffc3b6SDaniel W. S. Almeida u16 snr = 0; 205f5ffc3b6SDaniel W. S. Almeida 2063e51a496SMauro Carvalho Chehab /* Simulate random lost of signal due to a bad-tuned channel */ 207f5ffc3b6SDaniel W. S. Almeida cnr2qual = vidtv_match_cnr_s(&state->frontend); 2083e51a496SMauro Carvalho Chehab 2093e51a496SMauro Carvalho Chehab if (cnr2qual && state->tuner_cnr < cnr2qual->cnr_good && 2103e51a496SMauro Carvalho Chehab state->frontend.ops.tuner_ops.get_rf_strength) { 211f58cac01SMauro Carvalho Chehab state->frontend.ops.tuner_ops.get_rf_strength(&state->frontend, 212f58cac01SMauro Carvalho Chehab &snr); 213f5ffc3b6SDaniel W. S. Almeida 214f5ffc3b6SDaniel W. S. Almeida if (snr < cnr2qual->cnr_ok) { 215f5ffc3b6SDaniel W. S. Almeida /* eventually lose the TS lock */ 2168032bf12SJason A. Donenfeld if (get_random_u32_below(100) < config->drop_tslock_prob_on_low_snr) 217f5ffc3b6SDaniel W. S. Almeida state->status = 0; 218f5ffc3b6SDaniel W. S. Almeida } else { 219f5ffc3b6SDaniel W. S. Almeida /* recover if the signal improves */ 2208032bf12SJason A. Donenfeld if (get_random_u32_below(100) < 221f5ffc3b6SDaniel W. S. Almeida config->recover_tslock_prob_on_good_snr) 222f5ffc3b6SDaniel W. S. Almeida state->status = FE_HAS_SIGNAL | 223f5ffc3b6SDaniel W. S. Almeida FE_HAS_CARRIER | 224f5ffc3b6SDaniel W. S. Almeida FE_HAS_VITERBI | 225f5ffc3b6SDaniel W. S. Almeida FE_HAS_SYNC | 226f5ffc3b6SDaniel W. S. Almeida FE_HAS_LOCK; 227f5ffc3b6SDaniel W. S. Almeida } 2283e51a496SMauro Carvalho Chehab } 2293e51a496SMauro Carvalho Chehab 2303e51a496SMauro Carvalho Chehab vidtv_demod_update_stats(&state->frontend); 231f5ffc3b6SDaniel W. S. Almeida 232f5ffc3b6SDaniel W. S. Almeida *status = state->status; 233f5ffc3b6SDaniel W. S. Almeida 234f5ffc3b6SDaniel W. S. Almeida return 0; 235f5ffc3b6SDaniel W. S. Almeida } 236f5ffc3b6SDaniel W. S. Almeida 237f5ffc3b6SDaniel W. S. Almeida static int vidtv_demod_read_signal_strength(struct dvb_frontend *fe, 238f5ffc3b6SDaniel W. S. Almeida u16 *strength) 239f5ffc3b6SDaniel W. S. Almeida { 2403e51a496SMauro Carvalho Chehab struct dtv_frontend_properties *c = &fe->dtv_property_cache; 241f5ffc3b6SDaniel W. S. Almeida 2423e51a496SMauro Carvalho Chehab *strength = c->strength.stat[0].uvalue; 243f5ffc3b6SDaniel W. S. Almeida 244f5ffc3b6SDaniel W. S. Almeida return 0; 245f5ffc3b6SDaniel W. S. Almeida } 246f5ffc3b6SDaniel W. S. Almeida 247f5ffc3b6SDaniel W. S. Almeida /* 248f5ffc3b6SDaniel W. S. Almeida * NOTE: 249f5ffc3b6SDaniel W. S. Almeida * This is implemented here just to be used as an example for real 250f5ffc3b6SDaniel W. S. Almeida * demod drivers. 251f5ffc3b6SDaniel W. S. Almeida * 252f5ffc3b6SDaniel W. S. Almeida * Should only be implemented if it actually reads something from the hardware. 253f5ffc3b6SDaniel W. S. Almeida * Also, it should check for the locks, in order to avoid report wrong data 254f5ffc3b6SDaniel W. S. Almeida * to userspace. 255f5ffc3b6SDaniel W. S. Almeida */ 256f5ffc3b6SDaniel W. S. Almeida static int vidtv_demod_get_frontend(struct dvb_frontend *fe, 257f5ffc3b6SDaniel W. S. Almeida struct dtv_frontend_properties *p) 258f5ffc3b6SDaniel W. S. Almeida { 259f5ffc3b6SDaniel W. S. Almeida return 0; 260f5ffc3b6SDaniel W. S. Almeida } 261f5ffc3b6SDaniel W. S. Almeida 262f5ffc3b6SDaniel W. S. Almeida static int vidtv_demod_set_frontend(struct dvb_frontend *fe) 263f5ffc3b6SDaniel W. S. Almeida { 2643e51a496SMauro Carvalho Chehab struct vidtv_demod_state *state = fe->demodulator_priv; 265f5ffc3b6SDaniel W. S. Almeida u32 tuner_status = 0; 2663e51a496SMauro Carvalho Chehab int ret; 267f5ffc3b6SDaniel W. S. Almeida 2683e51a496SMauro Carvalho Chehab if (!fe->ops.tuner_ops.set_params) 2693e51a496SMauro Carvalho Chehab return 0; 2703e51a496SMauro Carvalho Chehab 271f5ffc3b6SDaniel W. S. Almeida fe->ops.tuner_ops.set_params(fe); 272f5ffc3b6SDaniel W. S. Almeida 273f5ffc3b6SDaniel W. S. Almeida /* store the CNR returned by the tuner */ 2743e51a496SMauro Carvalho Chehab ret = fe->ops.tuner_ops.get_rf_strength(fe, &state->tuner_cnr); 2753e51a496SMauro Carvalho Chehab if (ret < 0) 2763e51a496SMauro Carvalho Chehab return ret; 277f5ffc3b6SDaniel W. S. Almeida 278f5ffc3b6SDaniel W. S. Almeida fe->ops.tuner_ops.get_status(fe, &tuner_status); 279f5ffc3b6SDaniel W. S. Almeida state->status = (state->tuner_cnr > 0) ? FE_HAS_SIGNAL | 280f5ffc3b6SDaniel W. S. Almeida FE_HAS_CARRIER | 281f5ffc3b6SDaniel W. S. Almeida FE_HAS_VITERBI | 282f5ffc3b6SDaniel W. S. Almeida FE_HAS_SYNC | 283f5ffc3b6SDaniel W. S. Almeida FE_HAS_LOCK : 284f5ffc3b6SDaniel W. S. Almeida 0; 285f5ffc3b6SDaniel W. S. Almeida 2863e51a496SMauro Carvalho Chehab vidtv_demod_update_stats(fe); 2873e51a496SMauro Carvalho Chehab 288f5ffc3b6SDaniel W. S. Almeida if (fe->ops.i2c_gate_ctrl) 289f5ffc3b6SDaniel W. S. Almeida fe->ops.i2c_gate_ctrl(fe, 0); 290f5ffc3b6SDaniel W. S. Almeida 291f5ffc3b6SDaniel W. S. Almeida return 0; 292f5ffc3b6SDaniel W. S. Almeida } 293f5ffc3b6SDaniel W. S. Almeida 294f5ffc3b6SDaniel W. S. Almeida /* 295f5ffc3b6SDaniel W. S. Almeida * NOTE: 296f5ffc3b6SDaniel W. S. Almeida * This is implemented here just to be used as an example for real 297f5ffc3b6SDaniel W. S. Almeida * demod drivers. 298f5ffc3b6SDaniel W. S. Almeida * 299f5ffc3b6SDaniel W. S. Almeida * Should only be implemented if the demod has support for DVB-S or DVB-S2 300f5ffc3b6SDaniel W. S. Almeida */ 301f5ffc3b6SDaniel W. S. Almeida static int vidtv_demod_set_tone(struct dvb_frontend *fe, 302f5ffc3b6SDaniel W. S. Almeida enum fe_sec_tone_mode tone) 303f5ffc3b6SDaniel W. S. Almeida { 304f5ffc3b6SDaniel W. S. Almeida return 0; 305f5ffc3b6SDaniel W. S. Almeida } 306f5ffc3b6SDaniel W. S. Almeida 307f5ffc3b6SDaniel W. S. Almeida /* 308f5ffc3b6SDaniel W. S. Almeida * NOTE: 309f5ffc3b6SDaniel W. S. Almeida * This is implemented here just to be used as an example for real 310f5ffc3b6SDaniel W. S. Almeida * demod drivers. 311f5ffc3b6SDaniel W. S. Almeida * 312f5ffc3b6SDaniel W. S. Almeida * Should only be implemented if the demod has support for DVB-S or DVB-S2 313f5ffc3b6SDaniel W. S. Almeida */ 314f5ffc3b6SDaniel W. S. Almeida static int vidtv_demod_set_voltage(struct dvb_frontend *fe, 315f5ffc3b6SDaniel W. S. Almeida enum fe_sec_voltage voltage) 316f5ffc3b6SDaniel W. S. Almeida { 317f5ffc3b6SDaniel W. S. Almeida return 0; 318f5ffc3b6SDaniel W. S. Almeida } 319f5ffc3b6SDaniel W. S. Almeida 320d38829a5SMauro Carvalho Chehab /* 321d38829a5SMauro Carvalho Chehab * NOTE: 322d38829a5SMauro Carvalho Chehab * This is implemented here just to be used as an example for real 323d38829a5SMauro Carvalho Chehab * demod drivers. 324d38829a5SMauro Carvalho Chehab * 325d38829a5SMauro Carvalho Chehab * Should only be implemented if the demod has support for DVB-S or DVB-S2 326d38829a5SMauro Carvalho Chehab */ 327d38829a5SMauro Carvalho Chehab static int vidtv_send_diseqc_msg(struct dvb_frontend *fe, 328d38829a5SMauro Carvalho Chehab struct dvb_diseqc_master_cmd *cmd) 329d38829a5SMauro Carvalho Chehab { 330d38829a5SMauro Carvalho Chehab return 0; 331d38829a5SMauro Carvalho Chehab } 332d38829a5SMauro Carvalho Chehab 333d38829a5SMauro Carvalho Chehab /* 334d38829a5SMauro Carvalho Chehab * NOTE: 335d38829a5SMauro Carvalho Chehab * This is implemented here just to be used as an example for real 336d38829a5SMauro Carvalho Chehab * demod drivers. 337d38829a5SMauro Carvalho Chehab * 338d38829a5SMauro Carvalho Chehab * Should only be implemented if the demod has support for DVB-S or DVB-S2 339d38829a5SMauro Carvalho Chehab */ 340d38829a5SMauro Carvalho Chehab static int vidtv_diseqc_send_burst(struct dvb_frontend *fe, 341d38829a5SMauro Carvalho Chehab enum fe_sec_mini_cmd burst) 342d38829a5SMauro Carvalho Chehab { 343d38829a5SMauro Carvalho Chehab return 0; 344d38829a5SMauro Carvalho Chehab } 345d38829a5SMauro Carvalho Chehab 346f5ffc3b6SDaniel W. S. Almeida static void vidtv_demod_release(struct dvb_frontend *fe) 347f5ffc3b6SDaniel W. S. Almeida { 348f5ffc3b6SDaniel W. S. Almeida struct vidtv_demod_state *state = fe->demodulator_priv; 349f5ffc3b6SDaniel W. S. Almeida 350f5ffc3b6SDaniel W. S. Almeida kfree(state); 351f5ffc3b6SDaniel W. S. Almeida } 352f5ffc3b6SDaniel W. S. Almeida 353f5ffc3b6SDaniel W. S. Almeida static const struct dvb_frontend_ops vidtv_demod_ops = { 354f5ffc3b6SDaniel W. S. Almeida .delsys = { 355f5ffc3b6SDaniel W. S. Almeida SYS_DVBT, 356f5ffc3b6SDaniel W. S. Almeida SYS_DVBT2, 357f5ffc3b6SDaniel W. S. Almeida SYS_DVBC_ANNEX_A, 358f5ffc3b6SDaniel W. S. Almeida SYS_DVBS, 359f5ffc3b6SDaniel W. S. Almeida SYS_DVBS2, 360f5ffc3b6SDaniel W. S. Almeida }, 361f5ffc3b6SDaniel W. S. Almeida 362f5ffc3b6SDaniel W. S. Almeida .info = { 363f5ffc3b6SDaniel W. S. Almeida .name = "Dummy demod for DVB-T/T2/C/S/S2", 364f5ffc3b6SDaniel W. S. Almeida .frequency_min_hz = 51 * MHz, 365f5ffc3b6SDaniel W. S. Almeida .frequency_max_hz = 2150 * MHz, 366f5ffc3b6SDaniel W. S. Almeida .frequency_stepsize_hz = 62500, 367f5ffc3b6SDaniel W. S. Almeida .frequency_tolerance_hz = 29500 * kHz, 368f5ffc3b6SDaniel W. S. Almeida .symbol_rate_min = 1000000, 369f5ffc3b6SDaniel W. S. Almeida .symbol_rate_max = 45000000, 370f5ffc3b6SDaniel W. S. Almeida 371f5ffc3b6SDaniel W. S. Almeida .caps = FE_CAN_FEC_1_2 | 372f5ffc3b6SDaniel W. S. Almeida FE_CAN_FEC_2_3 | 373f5ffc3b6SDaniel W. S. Almeida FE_CAN_FEC_3_4 | 374f5ffc3b6SDaniel W. S. Almeida FE_CAN_FEC_4_5 | 375f5ffc3b6SDaniel W. S. Almeida FE_CAN_FEC_5_6 | 376f5ffc3b6SDaniel W. S. Almeida FE_CAN_FEC_6_7 | 377f5ffc3b6SDaniel W. S. Almeida FE_CAN_FEC_7_8 | 378f5ffc3b6SDaniel W. S. Almeida FE_CAN_FEC_8_9 | 379f5ffc3b6SDaniel W. S. Almeida FE_CAN_QAM_16 | 380f5ffc3b6SDaniel W. S. Almeida FE_CAN_QAM_64 | 381f5ffc3b6SDaniel W. S. Almeida FE_CAN_QAM_32 | 382f5ffc3b6SDaniel W. S. Almeida FE_CAN_QAM_128 | 383f5ffc3b6SDaniel W. S. Almeida FE_CAN_QAM_256 | 384f5ffc3b6SDaniel W. S. Almeida FE_CAN_QAM_AUTO | 385f5ffc3b6SDaniel W. S. Almeida FE_CAN_QPSK | 386f5ffc3b6SDaniel W. S. Almeida FE_CAN_FEC_AUTO | 387f5ffc3b6SDaniel W. S. Almeida FE_CAN_INVERSION_AUTO | 388f5ffc3b6SDaniel W. S. Almeida FE_CAN_TRANSMISSION_MODE_AUTO | 389f5ffc3b6SDaniel W. S. Almeida FE_CAN_GUARD_INTERVAL_AUTO | 390f5ffc3b6SDaniel W. S. Almeida FE_CAN_HIERARCHY_AUTO, 391f5ffc3b6SDaniel W. S. Almeida }, 392f5ffc3b6SDaniel W. S. Almeida 393f5ffc3b6SDaniel W. S. Almeida .release = vidtv_demod_release, 394f5ffc3b6SDaniel W. S. Almeida 395f5ffc3b6SDaniel W. S. Almeida .set_frontend = vidtv_demod_set_frontend, 396f5ffc3b6SDaniel W. S. Almeida .get_frontend = vidtv_demod_get_frontend, 397f5ffc3b6SDaniel W. S. Almeida 398f5ffc3b6SDaniel W. S. Almeida .read_status = vidtv_demod_read_status, 399f5ffc3b6SDaniel W. S. Almeida .read_signal_strength = vidtv_demod_read_signal_strength, 400f5ffc3b6SDaniel W. S. Almeida 401f5ffc3b6SDaniel W. S. Almeida /* For DVB-S/S2 */ 402f5ffc3b6SDaniel W. S. Almeida .set_voltage = vidtv_demod_set_voltage, 403f5ffc3b6SDaniel W. S. Almeida .set_tone = vidtv_demod_set_tone, 404d38829a5SMauro Carvalho Chehab .diseqc_send_master_cmd = vidtv_send_diseqc_msg, 405d38829a5SMauro Carvalho Chehab .diseqc_send_burst = vidtv_diseqc_send_burst, 406d38829a5SMauro Carvalho Chehab 407f5ffc3b6SDaniel W. S. Almeida }; 408f5ffc3b6SDaniel W. S. Almeida 409f5ffc3b6SDaniel W. S. Almeida static const struct i2c_device_id vidtv_demod_i2c_id_table[] = { 410*cc4cbd4bSUwe Kleine-König { "dvb_vidtv_demod" }, 411f5ffc3b6SDaniel W. S. Almeida {} 412f5ffc3b6SDaniel W. S. Almeida }; 413f5ffc3b6SDaniel W. S. Almeida MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table); 414f5ffc3b6SDaniel W. S. Almeida 4157d4833b1SUwe Kleine-König static int vidtv_demod_i2c_probe(struct i2c_client *client) 416f5ffc3b6SDaniel W. S. Almeida { 41796230dc1SMauro Carvalho Chehab struct vidtv_tuner_config *config = client->dev.platform_data; 418f5ffc3b6SDaniel W. S. Almeida struct vidtv_demod_state *state; 419f5ffc3b6SDaniel W. S. Almeida 420f5ffc3b6SDaniel W. S. Almeida /* allocate memory for the internal state */ 421f5ffc3b6SDaniel W. S. Almeida state = kzalloc(sizeof(*state), GFP_KERNEL); 422f5ffc3b6SDaniel W. S. Almeida if (!state) 423f5ffc3b6SDaniel W. S. Almeida return -ENOMEM; 424f5ffc3b6SDaniel W. S. Almeida 425f5ffc3b6SDaniel W. S. Almeida /* create dvb_frontend */ 426f5ffc3b6SDaniel W. S. Almeida memcpy(&state->frontend.ops, 427f5ffc3b6SDaniel W. S. Almeida &vidtv_demod_ops, 428f5ffc3b6SDaniel W. S. Almeida sizeof(struct dvb_frontend_ops)); 429f5ffc3b6SDaniel W. S. Almeida 43096230dc1SMauro Carvalho Chehab memcpy(&state->config, config, sizeof(state->config)); 43196230dc1SMauro Carvalho Chehab 432f5ffc3b6SDaniel W. S. Almeida state->frontend.demodulator_priv = state; 433f5ffc3b6SDaniel W. S. Almeida i2c_set_clientdata(client, state); 434f5ffc3b6SDaniel W. S. Almeida 4353e51a496SMauro Carvalho Chehab vidtv_clean_stats(&state->frontend); 4363e51a496SMauro Carvalho Chehab 437f5ffc3b6SDaniel W. S. Almeida return 0; 438f5ffc3b6SDaniel W. S. Almeida } 439f5ffc3b6SDaniel W. S. Almeida 440ed5c2f5fSUwe Kleine-König static void vidtv_demod_i2c_remove(struct i2c_client *client) 441f5ffc3b6SDaniel W. S. Almeida { 442f5ffc3b6SDaniel W. S. Almeida struct vidtv_demod_state *state = i2c_get_clientdata(client); 443f5ffc3b6SDaniel W. S. Almeida 444f5ffc3b6SDaniel W. S. Almeida kfree(state); 445f5ffc3b6SDaniel W. S. Almeida } 446f5ffc3b6SDaniel W. S. Almeida 447f5ffc3b6SDaniel W. S. Almeida static struct i2c_driver vidtv_demod_i2c_driver = { 448f5ffc3b6SDaniel W. S. Almeida .driver = { 449f5ffc3b6SDaniel W. S. Almeida .name = "dvb_vidtv_demod", 450f5ffc3b6SDaniel W. S. Almeida .suppress_bind_attrs = true, 451f5ffc3b6SDaniel W. S. Almeida }, 452aaeb31c0SUwe Kleine-König .probe = vidtv_demod_i2c_probe, 453f5ffc3b6SDaniel W. S. Almeida .remove = vidtv_demod_i2c_remove, 454f5ffc3b6SDaniel W. S. Almeida .id_table = vidtv_demod_i2c_id_table, 455f5ffc3b6SDaniel W. S. Almeida }; 456f5ffc3b6SDaniel W. S. Almeida 457f5ffc3b6SDaniel W. S. Almeida module_i2c_driver(vidtv_demod_i2c_driver); 458f5ffc3b6SDaniel W. S. Almeida 459f5ffc3b6SDaniel W. S. Almeida MODULE_DESCRIPTION("Virtual DVB Demodulator Driver"); 460f5ffc3b6SDaniel W. S. Almeida MODULE_AUTHOR("Daniel W. S. Almeida"); 461f5ffc3b6SDaniel W. S. Almeida MODULE_LICENSE("GPL"); 462