1f90cf607SDaniel W. S. Almeida // SPDX-License-Identifier: GPL-2.0 2f90cf607SDaniel W. S. Almeida /* 3f90cf607SDaniel W. S. Almeida * The Virtual DTV test driver serves as a reference DVB driver and helps 4f90cf607SDaniel W. S. Almeida * validate the existing APIs in the media subsystem. It can also aid 5f90cf607SDaniel W. S. Almeida * developers working on userspace applications. 6f90cf607SDaniel W. S. Almeida * 7a8bd461cSMauro Carvalho Chehab * When this module is loaded, it will attempt to modprobe 'dvb_vidtv_tuner' 8a8bd461cSMauro Carvalho Chehab * and 'dvb_vidtv_demod'. 9f90cf607SDaniel W. S. Almeida * 10f90cf607SDaniel W. S. Almeida * Copyright (C) 2020 Daniel W. S. Almeida 11f90cf607SDaniel W. S. Almeida */ 12f90cf607SDaniel W. S. Almeida 138922e393SMauro Carvalho Chehab #include <linux/dev_printk.h> 14f90cf607SDaniel W. S. Almeida #include <linux/moduleparam.h> 15f90cf607SDaniel W. S. Almeida #include <linux/mutex.h> 16f90cf607SDaniel W. S. Almeida #include <linux/platform_device.h> 17f90cf607SDaniel W. S. Almeida #include <linux/time.h> 18f90cf607SDaniel W. S. Almeida #include <linux/types.h> 19f90cf607SDaniel W. S. Almeida #include <linux/workqueue.h> 20e259572dSDaniel W. S. Almeida #include <media/dvbdev.h> 21e259572dSDaniel W. S. Almeida #include <media/media-device.h> 22f90cf607SDaniel W. S. Almeida 23f90cf607SDaniel W. S. Almeida #include "vidtv_bridge.h" 24f90cf607SDaniel W. S. Almeida #include "vidtv_common.h" 258922e393SMauro Carvalho Chehab #include "vidtv_demod.h" 268922e393SMauro Carvalho Chehab #include "vidtv_mux.h" 278922e393SMauro Carvalho Chehab #include "vidtv_ts.h" 288922e393SMauro Carvalho Chehab #include "vidtv_tuner.h" 29f90cf607SDaniel W. S. Almeida 30a8bd461cSMauro Carvalho Chehab #define MUX_BUF_MIN_SZ 90164 31a8bd461cSMauro Carvalho Chehab #define MUX_BUF_MAX_SZ (MUX_BUF_MIN_SZ * 10) 32f90cf607SDaniel W. S. Almeida #define TUNER_DEFAULT_ADDR 0x68 33f90cf607SDaniel W. S. Almeida #define DEMOD_DEFAULT_ADDR 0x60 341d2b2a6dSMauro Carvalho Chehab #define VIDTV_DEFAULT_NETWORK_ID 0xff44 35c2f78f0cSDaniel W. S. Almeida #define VIDTV_DEFAULT_NETWORK_NAME "LinuxTV.org" 361d2b2a6dSMauro Carvalho Chehab #define VIDTV_DEFAULT_TS_ID 0x4081 37f90cf607SDaniel W. S. Almeida 38a8bd461cSMauro Carvalho Chehab /* 39a8bd461cSMauro Carvalho Chehab * The LNBf fake parameters here are the ranges used by an 40a8bd461cSMauro Carvalho Chehab * Universal (extended) European LNBf, which is likely the most common LNBf 41a8bd461cSMauro Carvalho Chehab * found on Satellite digital TV system nowadays. 42a8bd461cSMauro Carvalho Chehab */ 43a8bd461cSMauro Carvalho Chehab #define LNB_CUT_FREQUENCY 11700000 /* high IF frequency */ 44a8bd461cSMauro Carvalho Chehab #define LNB_LOW_FREQ 9750000 /* low IF frequency */ 45a8bd461cSMauro Carvalho Chehab #define LNB_HIGH_FREQ 10600000 /* transition frequency */ 469ec6f4bbSMauro Carvalho Chehab 47f90cf607SDaniel W. S. Almeida static unsigned int drop_tslock_prob_on_low_snr; 48f90cf607SDaniel W. S. Almeida module_param(drop_tslock_prob_on_low_snr, uint, 0); 49f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(drop_tslock_prob_on_low_snr, 50f90cf607SDaniel W. S. Almeida "Probability of losing the TS lock if the signal quality is bad"); 51f90cf607SDaniel W. S. Almeida 52f90cf607SDaniel W. S. Almeida static unsigned int recover_tslock_prob_on_good_snr; 53f90cf607SDaniel W. S. Almeida module_param(recover_tslock_prob_on_good_snr, uint, 0); 54f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(recover_tslock_prob_on_good_snr, 55f90cf607SDaniel W. S. Almeida "Probability recovering the TS lock when the signal improves"); 56f90cf607SDaniel W. S. Almeida 57f90cf607SDaniel W. S. Almeida static unsigned int mock_power_up_delay_msec; 58f90cf607SDaniel W. S. Almeida module_param(mock_power_up_delay_msec, uint, 0); 59f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(mock_power_up_delay_msec, "Simulate a power up delay"); 60f90cf607SDaniel W. S. Almeida 61f90cf607SDaniel W. S. Almeida static unsigned int mock_tune_delay_msec; 62f90cf607SDaniel W. S. Almeida module_param(mock_tune_delay_msec, uint, 0); 63f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(mock_tune_delay_msec, "Simulate a tune delay"); 64f90cf607SDaniel W. S. Almeida 651cb23db9SMauro Carvalho Chehab static unsigned int vidtv_valid_dvb_t_freqs[NUM_VALID_TUNER_FREQS] = { 661cb23db9SMauro Carvalho Chehab 474000000 671cb23db9SMauro Carvalho Chehab }; 681cb23db9SMauro Carvalho Chehab 69f90cf607SDaniel W. S. Almeida module_param_array(vidtv_valid_dvb_t_freqs, uint, NULL, 0); 70f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(vidtv_valid_dvb_t_freqs, 719ec6f4bbSMauro Carvalho Chehab "Valid DVB-T frequencies to simulate, in Hz"); 72f90cf607SDaniel W. S. Almeida 731cb23db9SMauro Carvalho Chehab static unsigned int vidtv_valid_dvb_c_freqs[NUM_VALID_TUNER_FREQS] = { 741cb23db9SMauro Carvalho Chehab 474000000 751cb23db9SMauro Carvalho Chehab }; 761cb23db9SMauro Carvalho Chehab 77f90cf607SDaniel W. S. Almeida module_param_array(vidtv_valid_dvb_c_freqs, uint, NULL, 0); 78f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(vidtv_valid_dvb_c_freqs, 799ec6f4bbSMauro Carvalho Chehab "Valid DVB-C frequencies to simulate, in Hz"); 80f90cf607SDaniel W. S. Almeida 811cb23db9SMauro Carvalho Chehab static unsigned int vidtv_valid_dvb_s_freqs[NUM_VALID_TUNER_FREQS] = { 829ec6f4bbSMauro Carvalho Chehab 11362000 831cb23db9SMauro Carvalho Chehab }; 84f90cf607SDaniel W. S. Almeida module_param_array(vidtv_valid_dvb_s_freqs, uint, NULL, 0); 85f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(vidtv_valid_dvb_s_freqs, 869ec6f4bbSMauro Carvalho Chehab "Valid DVB-S/S2 frequencies to simulate at Ku-Band, in kHz"); 87f90cf607SDaniel W. S. Almeida 88f90cf607SDaniel W. S. Almeida static unsigned int max_frequency_shift_hz; 89f90cf607SDaniel W. S. Almeida module_param(max_frequency_shift_hz, uint, 0); 90f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(max_frequency_shift_hz, 91f90cf607SDaniel W. S. Almeida "Maximum shift in HZ allowed when tuning in a channel"); 92f90cf607SDaniel W. S. Almeida 93f90cf607SDaniel W. S. Almeida DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nums); 94f90cf607SDaniel W. S. Almeida 95f90cf607SDaniel W. S. Almeida /* 96f90cf607SDaniel W. S. Almeida * Influences the signal acquisition time. See ISO/IEC 13818-1 : 2000. p. 113. 97f90cf607SDaniel W. S. Almeida */ 98f90cf607SDaniel W. S. Almeida static unsigned int si_period_msec = 40; 99f90cf607SDaniel W. S. Almeida module_param(si_period_msec, uint, 0); 100f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(si_period_msec, "How often to send SI packets. Default: 40ms"); 101f90cf607SDaniel W. S. Almeida 102f90cf607SDaniel W. S. Almeida static unsigned int pcr_period_msec = 40; 103f90cf607SDaniel W. S. Almeida module_param(pcr_period_msec, uint, 0); 104a8bd461cSMauro Carvalho Chehab MODULE_PARM_DESC(pcr_period_msec, 105a8bd461cSMauro Carvalho Chehab "How often to send PCR packets. Default: 40ms"); 106f90cf607SDaniel W. S. Almeida 107f90cf607SDaniel W. S. Almeida static unsigned int mux_rate_kbytes_sec = 4096; 108f90cf607SDaniel W. S. Almeida module_param(mux_rate_kbytes_sec, uint, 0); 109f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(mux_rate_kbytes_sec, "Mux rate: will pad stream if below"); 110f90cf607SDaniel W. S. Almeida 111f90cf607SDaniel W. S. Almeida static unsigned int pcr_pid = 0x200; 112f90cf607SDaniel W. S. Almeida module_param(pcr_pid, uint, 0); 113f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(pcr_pid, "PCR PID for all channels: defaults to 0x200"); 114f90cf607SDaniel W. S. Almeida 115f90cf607SDaniel W. S. Almeida static unsigned int mux_buf_sz_pkts; 116f90cf607SDaniel W. S. Almeida module_param(mux_buf_sz_pkts, uint, 0); 117a8bd461cSMauro Carvalho Chehab MODULE_PARM_DESC(mux_buf_sz_pkts, 118a8bd461cSMauro Carvalho Chehab "Size for the internal mux buffer in multiples of 188 bytes"); 119ad458524SMauro Carvalho Chehab 120f90cf607SDaniel W. S. Almeida static u32 vidtv_bridge_mux_buf_sz_for_mux_rate(void) 121f90cf607SDaniel W. S. Almeida { 122ad458524SMauro Carvalho Chehab u32 max_elapsed_time_msecs = VIDTV_MAX_SLEEP_USECS / USEC_PER_MSEC; 123f90cf607SDaniel W. S. Almeida u32 mux_buf_sz = mux_buf_sz_pkts * TS_PACKET_LEN; 124a8bd461cSMauro Carvalho Chehab u32 nbytes_expected; 125f90cf607SDaniel W. S. Almeida 126ad458524SMauro Carvalho Chehab nbytes_expected = mux_rate_kbytes_sec; 127f90cf607SDaniel W. S. Almeida nbytes_expected *= max_elapsed_time_msecs; 128f90cf607SDaniel W. S. Almeida 129f90cf607SDaniel W. S. Almeida mux_buf_sz = roundup(nbytes_expected, TS_PACKET_LEN); 130ad458524SMauro Carvalho Chehab mux_buf_sz += mux_buf_sz / 10; 131f90cf607SDaniel W. S. Almeida 132ad458524SMauro Carvalho Chehab if (mux_buf_sz < MUX_BUF_MIN_SZ) 133ad458524SMauro Carvalho Chehab mux_buf_sz = MUX_BUF_MIN_SZ; 134f90cf607SDaniel W. S. Almeida 135ad458524SMauro Carvalho Chehab if (mux_buf_sz > MUX_BUF_MAX_SZ) 136ad458524SMauro Carvalho Chehab mux_buf_sz = MUX_BUF_MAX_SZ; 137f90cf607SDaniel W. S. Almeida 138ad458524SMauro Carvalho Chehab return mux_buf_sz; 139f90cf607SDaniel W. S. Almeida } 140f90cf607SDaniel W. S. Almeida 141f90cf607SDaniel W. S. Almeida static bool vidtv_bridge_check_demod_lock(struct vidtv_dvb *dvb, u32 n) 142f90cf607SDaniel W. S. Almeida { 143f90cf607SDaniel W. S. Almeida enum fe_status status; 144f90cf607SDaniel W. S. Almeida 145f90cf607SDaniel W. S. Almeida dvb->fe[n]->ops.read_status(dvb->fe[n], &status); 146f90cf607SDaniel W. S. Almeida 147f90cf607SDaniel W. S. Almeida return status == (FE_HAS_SIGNAL | 148f90cf607SDaniel W. S. Almeida FE_HAS_CARRIER | 149f90cf607SDaniel W. S. Almeida FE_HAS_VITERBI | 150f90cf607SDaniel W. S. Almeida FE_HAS_SYNC | 151f90cf607SDaniel W. S. Almeida FE_HAS_LOCK); 152f90cf607SDaniel W. S. Almeida } 153f90cf607SDaniel W. S. Almeida 154f90cf607SDaniel W. S. Almeida /* 155a8bd461cSMauro Carvalho Chehab * called on a separate thread by the mux when new packets become available 156f90cf607SDaniel W. S. Almeida */ 157a8bd461cSMauro Carvalho Chehab static void vidtv_bridge_on_new_pkts_avail(void *priv, u8 *buf, u32 npkts) 158a8bd461cSMauro Carvalho Chehab { 159a8bd461cSMauro Carvalho Chehab struct vidtv_dvb *dvb = priv; 160f90cf607SDaniel W. S. Almeida 161f90cf607SDaniel W. S. Almeida /* drop packets if we lose the lock */ 162f90cf607SDaniel W. S. Almeida if (vidtv_bridge_check_demod_lock(dvb, 0)) 163f90cf607SDaniel W. S. Almeida dvb_dmx_swfilter_packets(&dvb->demux, buf, npkts); 164f90cf607SDaniel W. S. Almeida } 165f90cf607SDaniel W. S. Almeida 166f90cf607SDaniel W. S. Almeida static int vidtv_start_streaming(struct vidtv_dvb *dvb) 167f90cf607SDaniel W. S. Almeida { 1680a33ab16SMauro Carvalho Chehab struct vidtv_mux_init_args mux_args = { 1690a33ab16SMauro Carvalho Chehab .mux_rate_kbytes_sec = mux_rate_kbytes_sec, 1700a33ab16SMauro Carvalho Chehab .on_new_packets_available_cb = vidtv_bridge_on_new_pkts_avail, 1710a33ab16SMauro Carvalho Chehab .pcr_period_usecs = pcr_period_msec * USEC_PER_MSEC, 1720a33ab16SMauro Carvalho Chehab .si_period_usecs = si_period_msec * USEC_PER_MSEC, 1730a33ab16SMauro Carvalho Chehab .pcr_pid = pcr_pid, 1740a33ab16SMauro Carvalho Chehab .transport_stream_id = VIDTV_DEFAULT_TS_ID, 1750a33ab16SMauro Carvalho Chehab .network_id = VIDTV_DEFAULT_NETWORK_ID, 1760a33ab16SMauro Carvalho Chehab .network_name = VIDTV_DEFAULT_NETWORK_NAME, 1770a33ab16SMauro Carvalho Chehab .priv = dvb, 1780a33ab16SMauro Carvalho Chehab }; 1799cfb4d36SMauro Carvalho Chehab struct device *dev = &dvb->pdev->dev; 180f90cf607SDaniel W. S. Almeida u32 mux_buf_sz; 181f90cf607SDaniel W. S. Almeida 182f90cf607SDaniel W. S. Almeida if (dvb->streaming) { 1839cfb4d36SMauro Carvalho Chehab dev_warn_ratelimited(dev, "Already streaming. Skipping.\n"); 184f90cf607SDaniel W. S. Almeida return 0; 185f90cf607SDaniel W. S. Almeida } 186f90cf607SDaniel W. S. Almeida 187a8bd461cSMauro Carvalho Chehab if (mux_buf_sz_pkts) 188a8bd461cSMauro Carvalho Chehab mux_buf_sz = mux_buf_sz_pkts; 189a8bd461cSMauro Carvalho Chehab else 190a8bd461cSMauro Carvalho Chehab mux_buf_sz = vidtv_bridge_mux_buf_sz_for_mux_rate(); 191f90cf607SDaniel W. S. Almeida 192f90cf607SDaniel W. S. Almeida mux_args.mux_buf_sz = mux_buf_sz; 193f90cf607SDaniel W. S. Almeida 194f90cf607SDaniel W. S. Almeida dvb->streaming = true; 1950a33ab16SMauro Carvalho Chehab dvb->mux = vidtv_mux_init(dvb->fe[0], dev, &mux_args); 1963be80379SMauro Carvalho Chehab if (!dvb->mux) 1973be80379SMauro Carvalho Chehab return -ENOMEM; 198f90cf607SDaniel W. S. Almeida vidtv_mux_start_thread(dvb->mux); 199f90cf607SDaniel W. S. Almeida 2009cfb4d36SMauro Carvalho Chehab dev_dbg_ratelimited(dev, "Started streaming\n"); 201f90cf607SDaniel W. S. Almeida return 0; 202f90cf607SDaniel W. S. Almeida } 203f90cf607SDaniel W. S. Almeida 204f90cf607SDaniel W. S. Almeida static int vidtv_stop_streaming(struct vidtv_dvb *dvb) 205f90cf607SDaniel W. S. Almeida { 2069cfb4d36SMauro Carvalho Chehab struct device *dev = &dvb->pdev->dev; 2079cfb4d36SMauro Carvalho Chehab 208f90cf607SDaniel W. S. Almeida dvb->streaming = false; 209f90cf607SDaniel W. S. Almeida vidtv_mux_stop_thread(dvb->mux); 210f90cf607SDaniel W. S. Almeida vidtv_mux_destroy(dvb->mux); 211f90cf607SDaniel W. S. Almeida dvb->mux = NULL; 212f90cf607SDaniel W. S. Almeida 2139cfb4d36SMauro Carvalho Chehab dev_dbg_ratelimited(dev, "Stopped streaming\n"); 214f90cf607SDaniel W. S. Almeida return 0; 215f90cf607SDaniel W. S. Almeida } 216f90cf607SDaniel W. S. Almeida 217f90cf607SDaniel W. S. Almeida static int vidtv_start_feed(struct dvb_demux_feed *feed) 218f90cf607SDaniel W. S. Almeida { 219f90cf607SDaniel W. S. Almeida struct dvb_demux *demux = feed->demux; 220f90cf607SDaniel W. S. Almeida struct vidtv_dvb *dvb = demux->priv; 221f90cf607SDaniel W. S. Almeida int ret; 222a8bd461cSMauro Carvalho Chehab int rc; 223f90cf607SDaniel W. S. Almeida 224f90cf607SDaniel W. S. Almeida if (!demux->dmx.frontend) 225f90cf607SDaniel W. S. Almeida return -EINVAL; 226f90cf607SDaniel W. S. Almeida 227f90cf607SDaniel W. S. Almeida mutex_lock(&dvb->feed_lock); 228f90cf607SDaniel W. S. Almeida 229f90cf607SDaniel W. S. Almeida dvb->nfeeds++; 230f90cf607SDaniel W. S. Almeida rc = dvb->nfeeds; 231f90cf607SDaniel W. S. Almeida 232f90cf607SDaniel W. S. Almeida if (dvb->nfeeds == 1) { 233f90cf607SDaniel W. S. Almeida ret = vidtv_start_streaming(dvb); 234f90cf607SDaniel W. S. Almeida if (ret < 0) 235f90cf607SDaniel W. S. Almeida rc = ret; 236f90cf607SDaniel W. S. Almeida } 237f90cf607SDaniel W. S. Almeida 238f90cf607SDaniel W. S. Almeida mutex_unlock(&dvb->feed_lock); 239f90cf607SDaniel W. S. Almeida return rc; 240f90cf607SDaniel W. S. Almeida } 241f90cf607SDaniel W. S. Almeida 242f90cf607SDaniel W. S. Almeida static int vidtv_stop_feed(struct dvb_demux_feed *feed) 243f90cf607SDaniel W. S. Almeida { 244f90cf607SDaniel W. S. Almeida struct dvb_demux *demux = feed->demux; 245f90cf607SDaniel W. S. Almeida struct vidtv_dvb *dvb = demux->priv; 246f90cf607SDaniel W. S. Almeida int err = 0; 247f90cf607SDaniel W. S. Almeida 248f90cf607SDaniel W. S. Almeida mutex_lock(&dvb->feed_lock); 249f90cf607SDaniel W. S. Almeida dvb->nfeeds--; 250f90cf607SDaniel W. S. Almeida 251f90cf607SDaniel W. S. Almeida if (!dvb->nfeeds) 252f90cf607SDaniel W. S. Almeida err = vidtv_stop_streaming(dvb); 253f90cf607SDaniel W. S. Almeida 254f90cf607SDaniel W. S. Almeida mutex_unlock(&dvb->feed_lock); 255f90cf607SDaniel W. S. Almeida return err; 256f90cf607SDaniel W. S. Almeida } 257f90cf607SDaniel W. S. Almeida 258f90cf607SDaniel W. S. Almeida static struct dvb_frontend *vidtv_get_frontend_ptr(struct i2c_client *c) 259f90cf607SDaniel W. S. Almeida { 260f90cf607SDaniel W. S. Almeida struct vidtv_demod_state *state = i2c_get_clientdata(c); 261f90cf607SDaniel W. S. Almeida 262a8bd461cSMauro Carvalho Chehab /* the demod will set this when its probe function runs */ 263f90cf607SDaniel W. S. Almeida return &state->frontend; 264f90cf607SDaniel W. S. Almeida } 265f90cf607SDaniel W. S. Almeida 266f90cf607SDaniel W. S. Almeida static int vidtv_master_xfer(struct i2c_adapter *i2c_adap, 267f90cf607SDaniel W. S. Almeida struct i2c_msg msgs[], 268f90cf607SDaniel W. S. Almeida int num) 269f90cf607SDaniel W. S. Almeida { 270a8bd461cSMauro Carvalho Chehab /* 271a8bd461cSMauro Carvalho Chehab * Right now, this virtual driver doesn't really send or receive 272a8bd461cSMauro Carvalho Chehab * messages from I2C. A real driver will require an implementation 273a8bd461cSMauro Carvalho Chehab * here. 274a8bd461cSMauro Carvalho Chehab */ 275f90cf607SDaniel W. S. Almeida return 0; 276f90cf607SDaniel W. S. Almeida } 277f90cf607SDaniel W. S. Almeida 278f90cf607SDaniel W. S. Almeida static u32 vidtv_i2c_func(struct i2c_adapter *adapter) 279f90cf607SDaniel W. S. Almeida { 280f90cf607SDaniel W. S. Almeida return I2C_FUNC_I2C; 281f90cf607SDaniel W. S. Almeida } 282f90cf607SDaniel W. S. Almeida 283f90cf607SDaniel W. S. Almeida static const struct i2c_algorithm vidtv_i2c_algorithm = { 284f90cf607SDaniel W. S. Almeida .master_xfer = vidtv_master_xfer, 285f90cf607SDaniel W. S. Almeida .functionality = vidtv_i2c_func, 286f90cf607SDaniel W. S. Almeida }; 287f90cf607SDaniel W. S. Almeida 288f90cf607SDaniel W. S. Almeida static int vidtv_bridge_i2c_register_adap(struct vidtv_dvb *dvb) 289f90cf607SDaniel W. S. Almeida { 290f90cf607SDaniel W. S. Almeida struct i2c_adapter *i2c_adapter = &dvb->i2c_adapter; 291f90cf607SDaniel W. S. Almeida 292f90cf607SDaniel W. S. Almeida strscpy(i2c_adapter->name, "vidtv_i2c", sizeof(i2c_adapter->name)); 293f90cf607SDaniel W. S. Almeida i2c_adapter->owner = THIS_MODULE; 294f90cf607SDaniel W. S. Almeida i2c_adapter->algo = &vidtv_i2c_algorithm; 295f90cf607SDaniel W. S. Almeida i2c_adapter->algo_data = NULL; 296f90cf607SDaniel W. S. Almeida i2c_adapter->timeout = 500; 297f90cf607SDaniel W. S. Almeida i2c_adapter->retries = 3; 298f90cf607SDaniel W. S. Almeida i2c_adapter->dev.parent = &dvb->pdev->dev; 299f90cf607SDaniel W. S. Almeida 300f90cf607SDaniel W. S. Almeida i2c_set_adapdata(i2c_adapter, dvb); 301f90cf607SDaniel W. S. Almeida return i2c_add_adapter(&dvb->i2c_adapter); 302f90cf607SDaniel W. S. Almeida } 303f90cf607SDaniel W. S. Almeida 304f90cf607SDaniel W. S. Almeida static int vidtv_bridge_register_adap(struct vidtv_dvb *dvb) 305f90cf607SDaniel W. S. Almeida { 306f90cf607SDaniel W. S. Almeida int ret = 0; 307f90cf607SDaniel W. S. Almeida 308f90cf607SDaniel W. S. Almeida ret = dvb_register_adapter(&dvb->adapter, 309f90cf607SDaniel W. S. Almeida KBUILD_MODNAME, 310f90cf607SDaniel W. S. Almeida THIS_MODULE, 311f90cf607SDaniel W. S. Almeida &dvb->i2c_adapter.dev, 312f90cf607SDaniel W. S. Almeida adapter_nums); 313f90cf607SDaniel W. S. Almeida 314f90cf607SDaniel W. S. Almeida return ret; 315f90cf607SDaniel W. S. Almeida } 316f90cf607SDaniel W. S. Almeida 317f90cf607SDaniel W. S. Almeida static int vidtv_bridge_dmx_init(struct vidtv_dvb *dvb) 318f90cf607SDaniel W. S. Almeida { 319f90cf607SDaniel W. S. Almeida dvb->demux.dmx.capabilities = DMX_TS_FILTERING | 320f90cf607SDaniel W. S. Almeida DMX_SECTION_FILTERING; 321f90cf607SDaniel W. S. Almeida 322f90cf607SDaniel W. S. Almeida dvb->demux.priv = dvb; 323f90cf607SDaniel W. S. Almeida dvb->demux.filternum = 256; 324f90cf607SDaniel W. S. Almeida dvb->demux.feednum = 256; 325f90cf607SDaniel W. S. Almeida dvb->demux.start_feed = vidtv_start_feed; 326f90cf607SDaniel W. S. Almeida dvb->demux.stop_feed = vidtv_stop_feed; 327f90cf607SDaniel W. S. Almeida 328f90cf607SDaniel W. S. Almeida return dvb_dmx_init(&dvb->demux); 329f90cf607SDaniel W. S. Almeida } 330f90cf607SDaniel W. S. Almeida 331f90cf607SDaniel W. S. Almeida static int vidtv_bridge_dmxdev_init(struct vidtv_dvb *dvb) 332f90cf607SDaniel W. S. Almeida { 333f90cf607SDaniel W. S. Almeida dvb->dmx_dev.filternum = 256; 334f90cf607SDaniel W. S. Almeida dvb->dmx_dev.demux = &dvb->demux.dmx; 335f90cf607SDaniel W. S. Almeida dvb->dmx_dev.capabilities = 0; 336f90cf607SDaniel W. S. Almeida 337f90cf607SDaniel W. S. Almeida return dvb_dmxdev_init(&dvb->dmx_dev, &dvb->adapter); 338f90cf607SDaniel W. S. Almeida } 339f90cf607SDaniel W. S. Almeida 340f90cf607SDaniel W. S. Almeida static int vidtv_bridge_probe_demod(struct vidtv_dvb *dvb, u32 n) 341f90cf607SDaniel W. S. Almeida { 342a8bd461cSMauro Carvalho Chehab struct vidtv_demod_config cfg = { 343a8bd461cSMauro Carvalho Chehab .drop_tslock_prob_on_low_snr = drop_tslock_prob_on_low_snr, 344a8bd461cSMauro Carvalho Chehab .recover_tslock_prob_on_good_snr = recover_tslock_prob_on_good_snr, 345a8bd461cSMauro Carvalho Chehab }; 346f90cf607SDaniel W. S. Almeida dvb->i2c_client_demod[n] = dvb_module_probe("dvb_vidtv_demod", 347f90cf607SDaniel W. S. Almeida NULL, 348f90cf607SDaniel W. S. Almeida &dvb->i2c_adapter, 349f90cf607SDaniel W. S. Almeida DEMOD_DEFAULT_ADDR, 350f90cf607SDaniel W. S. Almeida &cfg); 351f90cf607SDaniel W. S. Almeida 352f90cf607SDaniel W. S. Almeida /* driver will not work anyways so bail out */ 353f90cf607SDaniel W. S. Almeida if (!dvb->i2c_client_demod[n]) 354f90cf607SDaniel W. S. Almeida return -ENODEV; 355f90cf607SDaniel W. S. Almeida 356f90cf607SDaniel W. S. Almeida /* retrieve a ptr to the frontend state */ 357f90cf607SDaniel W. S. Almeida dvb->fe[n] = vidtv_get_frontend_ptr(dvb->i2c_client_demod[n]); 358f90cf607SDaniel W. S. Almeida 359f90cf607SDaniel W. S. Almeida return 0; 360f90cf607SDaniel W. S. Almeida } 361f90cf607SDaniel W. S. Almeida 362f90cf607SDaniel W. S. Almeida static int vidtv_bridge_probe_tuner(struct vidtv_dvb *dvb, u32 n) 363f90cf607SDaniel W. S. Almeida { 364a8bd461cSMauro Carvalho Chehab struct vidtv_tuner_config cfg = { 365a8bd461cSMauro Carvalho Chehab .fe = dvb->fe[n], 366a8bd461cSMauro Carvalho Chehab .mock_power_up_delay_msec = mock_power_up_delay_msec, 367a8bd461cSMauro Carvalho Chehab .mock_tune_delay_msec = mock_tune_delay_msec, 368a8bd461cSMauro Carvalho Chehab }; 3699ec6f4bbSMauro Carvalho Chehab u32 freq; 3709ec6f4bbSMauro Carvalho Chehab int i; 371f90cf607SDaniel W. S. Almeida 3729ec6f4bbSMauro Carvalho Chehab /* TODO: check if the frequencies are at a valid range */ 3739ec6f4bbSMauro Carvalho Chehab 374f90cf607SDaniel W. S. Almeida memcpy(cfg.vidtv_valid_dvb_t_freqs, 375f90cf607SDaniel W. S. Almeida vidtv_valid_dvb_t_freqs, 376f90cf607SDaniel W. S. Almeida sizeof(vidtv_valid_dvb_t_freqs)); 377f90cf607SDaniel W. S. Almeida 378f90cf607SDaniel W. S. Almeida memcpy(cfg.vidtv_valid_dvb_c_freqs, 379f90cf607SDaniel W. S. Almeida vidtv_valid_dvb_c_freqs, 380f90cf607SDaniel W. S. Almeida sizeof(vidtv_valid_dvb_c_freqs)); 381f90cf607SDaniel W. S. Almeida 3829ec6f4bbSMauro Carvalho Chehab /* 3839ec6f4bbSMauro Carvalho Chehab * Convert Satellite frequencies from Ku-band in kHZ into S-band 3849ec6f4bbSMauro Carvalho Chehab * frequencies in Hz. 3859ec6f4bbSMauro Carvalho Chehab */ 3869ec6f4bbSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(vidtv_valid_dvb_s_freqs); i++) { 3879ec6f4bbSMauro Carvalho Chehab freq = vidtv_valid_dvb_s_freqs[i]; 3889ec6f4bbSMauro Carvalho Chehab if (freq) { 3899ec6f4bbSMauro Carvalho Chehab if (freq < LNB_CUT_FREQUENCY) 3909ec6f4bbSMauro Carvalho Chehab freq = abs(freq - LNB_LOW_FREQ); 3919ec6f4bbSMauro Carvalho Chehab else 3929ec6f4bbSMauro Carvalho Chehab freq = abs(freq - LNB_HIGH_FREQ); 3939ec6f4bbSMauro Carvalho Chehab } 3949ec6f4bbSMauro Carvalho Chehab cfg.vidtv_valid_dvb_s_freqs[i] = freq; 3959ec6f4bbSMauro Carvalho Chehab } 396f90cf607SDaniel W. S. Almeida 397f90cf607SDaniel W. S. Almeida cfg.max_frequency_shift_hz = max_frequency_shift_hz; 398f90cf607SDaniel W. S. Almeida 399f90cf607SDaniel W. S. Almeida dvb->i2c_client_tuner[n] = dvb_module_probe("dvb_vidtv_tuner", 400f90cf607SDaniel W. S. Almeida NULL, 401f90cf607SDaniel W. S. Almeida &dvb->i2c_adapter, 402f90cf607SDaniel W. S. Almeida TUNER_DEFAULT_ADDR, 403f90cf607SDaniel W. S. Almeida &cfg); 404f90cf607SDaniel W. S. Almeida 405f90cf607SDaniel W. S. Almeida return (dvb->i2c_client_tuner[n]) ? 0 : -ENODEV; 406f90cf607SDaniel W. S. Almeida } 407f90cf607SDaniel W. S. Almeida 408f90cf607SDaniel W. S. Almeida static int vidtv_bridge_dvb_init(struct vidtv_dvb *dvb) 409f90cf607SDaniel W. S. Almeida { 410a8bd461cSMauro Carvalho Chehab int ret, i, j; 411f90cf607SDaniel W. S. Almeida 412f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_i2c_register_adap(dvb); 413f90cf607SDaniel W. S. Almeida if (ret < 0) 414f90cf607SDaniel W. S. Almeida goto fail_i2c; 415f90cf607SDaniel W. S. Almeida 416f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_register_adap(dvb); 417f90cf607SDaniel W. S. Almeida if (ret < 0) 418f90cf607SDaniel W. S. Almeida goto fail_adapter; 419f90cf607SDaniel W. S. Almeida 420f90cf607SDaniel W. S. Almeida for (i = 0; i < NUM_FE; ++i) { 421f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_probe_demod(dvb, i); 422f90cf607SDaniel W. S. Almeida if (ret < 0) 423f90cf607SDaniel W. S. Almeida goto fail_demod_probe; 424f90cf607SDaniel W. S. Almeida 425f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_probe_tuner(dvb, i); 426f90cf607SDaniel W. S. Almeida if (ret < 0) 427f90cf607SDaniel W. S. Almeida goto fail_tuner_probe; 428f90cf607SDaniel W. S. Almeida 429f90cf607SDaniel W. S. Almeida ret = dvb_register_frontend(&dvb->adapter, dvb->fe[i]); 430f90cf607SDaniel W. S. Almeida if (ret < 0) 431f90cf607SDaniel W. S. Almeida goto fail_fe; 432f90cf607SDaniel W. S. Almeida } 433f90cf607SDaniel W. S. Almeida 434f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_dmx_init(dvb); 435f90cf607SDaniel W. S. Almeida if (ret < 0) 436f90cf607SDaniel W. S. Almeida goto fail_dmx; 437f90cf607SDaniel W. S. Almeida 438f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_dmxdev_init(dvb); 439f90cf607SDaniel W. S. Almeida if (ret < 0) 440f90cf607SDaniel W. S. Almeida goto fail_dmx_dev; 441f90cf607SDaniel W. S. Almeida 442f90cf607SDaniel W. S. Almeida for (j = 0; j < NUM_FE; ++j) { 443f90cf607SDaniel W. S. Almeida ret = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, 444f90cf607SDaniel W. S. Almeida &dvb->dmx_fe[j]); 445f90cf607SDaniel W. S. Almeida if (ret < 0) 446f90cf607SDaniel W. S. Almeida goto fail_dmx_conn; 447f90cf607SDaniel W. S. Almeida 448f90cf607SDaniel W. S. Almeida /* 449f90cf607SDaniel W. S. Almeida * The source of the demux is a frontend connected 450f90cf607SDaniel W. S. Almeida * to the demux. 451f90cf607SDaniel W. S. Almeida */ 452f90cf607SDaniel W. S. Almeida dvb->dmx_fe[j].source = DMX_FRONTEND_0; 453f90cf607SDaniel W. S. Almeida } 454f90cf607SDaniel W. S. Almeida 455f90cf607SDaniel W. S. Almeida return ret; 456f90cf607SDaniel W. S. Almeida 457f90cf607SDaniel W. S. Almeida fail_dmx_conn: 458f90cf607SDaniel W. S. Almeida for (j = j - 1; j >= 0; --j) 459f90cf607SDaniel W. S. Almeida dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, 460f90cf607SDaniel W. S. Almeida &dvb->dmx_fe[j]); 461f90cf607SDaniel W. S. Almeida fail_dmx_dev: 462f90cf607SDaniel W. S. Almeida dvb_dmxdev_release(&dvb->dmx_dev); 463f90cf607SDaniel W. S. Almeida fail_dmx: 464f90cf607SDaniel W. S. Almeida dvb_dmx_release(&dvb->demux); 465f90cf607SDaniel W. S. Almeida fail_fe: 466f90cf607SDaniel W. S. Almeida for (j = i; j >= 0; --j) 467f90cf607SDaniel W. S. Almeida dvb_unregister_frontend(dvb->fe[j]); 468f90cf607SDaniel W. S. Almeida fail_tuner_probe: 469f90cf607SDaniel W. S. Almeida for (j = i; j >= 0; --j) 470f90cf607SDaniel W. S. Almeida if (dvb->i2c_client_tuner[j]) 471f90cf607SDaniel W. S. Almeida dvb_module_release(dvb->i2c_client_tuner[j]); 472f90cf607SDaniel W. S. Almeida 473f90cf607SDaniel W. S. Almeida fail_demod_probe: 474f90cf607SDaniel W. S. Almeida for (j = i; j >= 0; --j) 475f90cf607SDaniel W. S. Almeida if (dvb->i2c_client_demod[j]) 476f90cf607SDaniel W. S. Almeida dvb_module_release(dvb->i2c_client_demod[j]); 477f90cf607SDaniel W. S. Almeida 478f90cf607SDaniel W. S. Almeida fail_adapter: 479f90cf607SDaniel W. S. Almeida dvb_unregister_adapter(&dvb->adapter); 480f90cf607SDaniel W. S. Almeida 481f90cf607SDaniel W. S. Almeida fail_i2c: 482f90cf607SDaniel W. S. Almeida i2c_del_adapter(&dvb->i2c_adapter); 483f90cf607SDaniel W. S. Almeida 484f90cf607SDaniel W. S. Almeida return ret; 485f90cf607SDaniel W. S. Almeida } 486f90cf607SDaniel W. S. Almeida 487f90cf607SDaniel W. S. Almeida static int vidtv_bridge_probe(struct platform_device *pdev) 488f90cf607SDaniel W. S. Almeida { 489f90cf607SDaniel W. S. Almeida struct vidtv_dvb *dvb; 4909cfb4d36SMauro Carvalho Chehab int ret; 491f90cf607SDaniel W. S. Almeida 492f90cf607SDaniel W. S. Almeida dvb = kzalloc(sizeof(*dvb), GFP_KERNEL); 493f90cf607SDaniel W. S. Almeida if (!dvb) 494f90cf607SDaniel W. S. Almeida return -ENOMEM; 495f90cf607SDaniel W. S. Almeida 496f90cf607SDaniel W. S. Almeida dvb->pdev = pdev; 497f90cf607SDaniel W. S. Almeida 498f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_dvb_init(dvb); 499f90cf607SDaniel W. S. Almeida if (ret < 0) 500f90cf607SDaniel W. S. Almeida goto err_dvb; 501f90cf607SDaniel W. S. Almeida 502f90cf607SDaniel W. S. Almeida mutex_init(&dvb->feed_lock); 503f90cf607SDaniel W. S. Almeida 504f90cf607SDaniel W. S. Almeida platform_set_drvdata(pdev, dvb); 505f90cf607SDaniel W. S. Almeida 506e259572dSDaniel W. S. Almeida #ifdef CONFIG_MEDIA_CONTROLLER_DVB 507e259572dSDaniel W. S. Almeida dvb->mdev.dev = &pdev->dev; 508e259572dSDaniel W. S. Almeida 509e259572dSDaniel W. S. Almeida strscpy(dvb->mdev.model, "vidtv", sizeof(dvb->mdev.model)); 510e259572dSDaniel W. S. Almeida strscpy(dvb->mdev.bus_info, "platform:vidtv", sizeof(dvb->mdev.bus_info)); 511e259572dSDaniel W. S. Almeida 512e259572dSDaniel W. S. Almeida media_device_init(&dvb->mdev); 513e259572dSDaniel W. S. Almeida ret = media_device_register(&dvb->mdev); 514e259572dSDaniel W. S. Almeida if (ret) { 515e259572dSDaniel W. S. Almeida dev_err(dvb->mdev.dev, 516e259572dSDaniel W. S. Almeida "media device register failed (err=%d)\n", ret); 517e259572dSDaniel W. S. Almeida goto err_media_device_register; 518e259572dSDaniel W. S. Almeida } 519e259572dSDaniel W. S. Almeida 520e259572dSDaniel W. S. Almeida dvb_register_media_controller(&dvb->adapter, &dvb->mdev); 521e259572dSDaniel W. S. Almeida #endif /* CONFIG_MEDIA_CONTROLLER_DVB */ 522e259572dSDaniel W. S. Almeida 5239cfb4d36SMauro Carvalho Chehab dev_info(&pdev->dev, "Successfully initialized vidtv!\n"); 524f90cf607SDaniel W. S. Almeida return ret; 525f90cf607SDaniel W. S. Almeida 526e259572dSDaniel W. S. Almeida #ifdef CONFIG_MEDIA_CONTROLLER_DVB 527e259572dSDaniel W. S. Almeida err_media_device_register: 528e259572dSDaniel W. S. Almeida media_device_cleanup(&dvb->mdev); 529e259572dSDaniel W. S. Almeida #endif /* CONFIG_MEDIA_CONTROLLER_DVB */ 530f90cf607SDaniel W. S. Almeida err_dvb: 531f90cf607SDaniel W. S. Almeida kfree(dvb); 532f90cf607SDaniel W. S. Almeida return ret; 533f90cf607SDaniel W. S. Almeida } 534f90cf607SDaniel W. S. Almeida 535f90cf607SDaniel W. S. Almeida static int vidtv_bridge_remove(struct platform_device *pdev) 536f90cf607SDaniel W. S. Almeida { 537f90cf607SDaniel W. S. Almeida struct vidtv_dvb *dvb; 538f90cf607SDaniel W. S. Almeida u32 i; 539f90cf607SDaniel W. S. Almeida 540f90cf607SDaniel W. S. Almeida dvb = platform_get_drvdata(pdev); 541f90cf607SDaniel W. S. Almeida 542e259572dSDaniel W. S. Almeida #ifdef CONFIG_MEDIA_CONTROLLER_DVB 543e259572dSDaniel W. S. Almeida media_device_unregister(&dvb->mdev); 544e259572dSDaniel W. S. Almeida media_device_cleanup(&dvb->mdev); 545e259572dSDaniel W. S. Almeida #endif /* CONFIG_MEDIA_CONTROLLER_DVB */ 546e259572dSDaniel W. S. Almeida 547f90cf607SDaniel W. S. Almeida mutex_destroy(&dvb->feed_lock); 548f90cf607SDaniel W. S. Almeida 54963101b75SMauro Carvalho Chehab for (i = 0; i < NUM_FE; ++i) { 55063101b75SMauro Carvalho Chehab dvb_unregister_frontend(dvb->fe[i]); 55163101b75SMauro Carvalho Chehab dvb_module_release(dvb->i2c_client_tuner[i]); 55263101b75SMauro Carvalho Chehab dvb_module_release(dvb->i2c_client_demod[i]); 55363101b75SMauro Carvalho Chehab } 554f90cf607SDaniel W. S. Almeida 555f90cf607SDaniel W. S. Almeida dvb_dmxdev_release(&dvb->dmx_dev); 556f90cf607SDaniel W. S. Almeida dvb_dmx_release(&dvb->demux); 557f90cf607SDaniel W. S. Almeida dvb_unregister_adapter(&dvb->adapter); 558*ed35980aSDaniel W. S. Almeida dev_info(&pdev->dev, "Successfully removed vidtv\n"); 559f90cf607SDaniel W. S. Almeida 560f90cf607SDaniel W. S. Almeida return 0; 561f90cf607SDaniel W. S. Almeida } 562f90cf607SDaniel W. S. Almeida 563f90cf607SDaniel W. S. Almeida static void vidtv_bridge_dev_release(struct device *dev) 564f90cf607SDaniel W. S. Almeida { 565f90cf607SDaniel W. S. Almeida } 566f90cf607SDaniel W. S. Almeida 567f90cf607SDaniel W. S. Almeida static struct platform_device vidtv_bridge_dev = { 5680b8f1d4aSDaniel W. S. Almeida .name = VIDTV_PDEV_NAME, 569f90cf607SDaniel W. S. Almeida .dev.release = vidtv_bridge_dev_release, 570f90cf607SDaniel W. S. Almeida }; 571f90cf607SDaniel W. S. Almeida 572f90cf607SDaniel W. S. Almeida static struct platform_driver vidtv_bridge_driver = { 573f90cf607SDaniel W. S. Almeida .driver = { 5740b8f1d4aSDaniel W. S. Almeida .name = VIDTV_PDEV_NAME, 575f90cf607SDaniel W. S. Almeida }, 576f90cf607SDaniel W. S. Almeida .probe = vidtv_bridge_probe, 577f90cf607SDaniel W. S. Almeida .remove = vidtv_bridge_remove, 578f90cf607SDaniel W. S. Almeida }; 579f90cf607SDaniel W. S. Almeida 580f90cf607SDaniel W. S. Almeida static void __exit vidtv_bridge_exit(void) 581f90cf607SDaniel W. S. Almeida { 582f90cf607SDaniel W. S. Almeida platform_driver_unregister(&vidtv_bridge_driver); 583f90cf607SDaniel W. S. Almeida platform_device_unregister(&vidtv_bridge_dev); 584f90cf607SDaniel W. S. Almeida } 585f90cf607SDaniel W. S. Almeida 586f90cf607SDaniel W. S. Almeida static int __init vidtv_bridge_init(void) 587f90cf607SDaniel W. S. Almeida { 588f90cf607SDaniel W. S. Almeida int ret; 589f90cf607SDaniel W. S. Almeida 590f90cf607SDaniel W. S. Almeida ret = platform_device_register(&vidtv_bridge_dev); 591f90cf607SDaniel W. S. Almeida if (ret) 592f90cf607SDaniel W. S. Almeida return ret; 593f90cf607SDaniel W. S. Almeida 594f90cf607SDaniel W. S. Almeida ret = platform_driver_register(&vidtv_bridge_driver); 595f90cf607SDaniel W. S. Almeida if (ret) 596f90cf607SDaniel W. S. Almeida platform_device_unregister(&vidtv_bridge_dev); 597f90cf607SDaniel W. S. Almeida 598f90cf607SDaniel W. S. Almeida return ret; 599f90cf607SDaniel W. S. Almeida } 600f90cf607SDaniel W. S. Almeida 601f90cf607SDaniel W. S. Almeida module_init(vidtv_bridge_init); 602f90cf607SDaniel W. S. Almeida module_exit(vidtv_bridge_exit); 603f90cf607SDaniel W. S. Almeida 604f90cf607SDaniel W. S. Almeida MODULE_DESCRIPTION("Virtual Digital TV Test Driver"); 605f90cf607SDaniel W. S. Almeida MODULE_AUTHOR("Daniel W. S. Almeida"); 606f90cf607SDaniel W. S. Almeida MODULE_LICENSE("GPL"); 607c9f968faSMauro Carvalho Chehab MODULE_ALIAS("vidtv"); 608c9f968faSMauro Carvalho Chehab MODULE_ALIAS("dvb_vidtv"); 609