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 * 7*a8bd461cSMauro Carvalho Chehab * When this module is loaded, it will attempt to modprobe 'dvb_vidtv_tuner' 8*a8bd461cSMauro 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> 20f90cf607SDaniel W. S. Almeida 21f90cf607SDaniel W. S. Almeida #include "vidtv_bridge.h" 22f90cf607SDaniel W. S. Almeida #include "vidtv_common.h" 238922e393SMauro Carvalho Chehab #include "vidtv_demod.h" 248922e393SMauro Carvalho Chehab #include "vidtv_mux.h" 258922e393SMauro Carvalho Chehab #include "vidtv_ts.h" 268922e393SMauro Carvalho Chehab #include "vidtv_tuner.h" 27f90cf607SDaniel W. S. Almeida 28*a8bd461cSMauro Carvalho Chehab #define MUX_BUF_MIN_SZ 90164 29*a8bd461cSMauro Carvalho Chehab #define MUX_BUF_MAX_SZ (MUX_BUF_MIN_SZ * 10) 30f90cf607SDaniel W. S. Almeida #define TUNER_DEFAULT_ADDR 0x68 31f90cf607SDaniel W. S. Almeida #define DEMOD_DEFAULT_ADDR 0x60 32c2f78f0cSDaniel W. S. Almeida #define VIDTV_DEFAULT_NETWORK_ID 0x744 33c2f78f0cSDaniel W. S. Almeida #define VIDTV_DEFAULT_NETWORK_NAME "LinuxTV.org" 34f90cf607SDaniel W. S. Almeida 35*a8bd461cSMauro Carvalho Chehab /* 36*a8bd461cSMauro Carvalho Chehab * The LNBf fake parameters here are the ranges used by an 37*a8bd461cSMauro Carvalho Chehab * Universal (extended) European LNBf, which is likely the most common LNBf 38*a8bd461cSMauro Carvalho Chehab * found on Satellite digital TV system nowadays. 39*a8bd461cSMauro Carvalho Chehab */ 40*a8bd461cSMauro Carvalho Chehab #define LNB_CUT_FREQUENCY 11700000 /* high IF frequency */ 41*a8bd461cSMauro Carvalho Chehab #define LNB_LOW_FREQ 9750000 /* low IF frequency */ 42*a8bd461cSMauro Carvalho Chehab #define LNB_HIGH_FREQ 10600000 /* transition frequency */ 439ec6f4bbSMauro Carvalho Chehab 44f90cf607SDaniel W. S. Almeida static unsigned int drop_tslock_prob_on_low_snr; 45f90cf607SDaniel W. S. Almeida module_param(drop_tslock_prob_on_low_snr, uint, 0); 46f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(drop_tslock_prob_on_low_snr, 47f90cf607SDaniel W. S. Almeida "Probability of losing the TS lock if the signal quality is bad"); 48f90cf607SDaniel W. S. Almeida 49f90cf607SDaniel W. S. Almeida static unsigned int recover_tslock_prob_on_good_snr; 50f90cf607SDaniel W. S. Almeida module_param(recover_tslock_prob_on_good_snr, uint, 0); 51f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(recover_tslock_prob_on_good_snr, 52f90cf607SDaniel W. S. Almeida "Probability recovering the TS lock when the signal improves"); 53f90cf607SDaniel W. S. Almeida 54f90cf607SDaniel W. S. Almeida static unsigned int mock_power_up_delay_msec; 55f90cf607SDaniel W. S. Almeida module_param(mock_power_up_delay_msec, uint, 0); 56f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(mock_power_up_delay_msec, "Simulate a power up delay"); 57f90cf607SDaniel W. S. Almeida 58f90cf607SDaniel W. S. Almeida static unsigned int mock_tune_delay_msec; 59f90cf607SDaniel W. S. Almeida module_param(mock_tune_delay_msec, uint, 0); 60f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(mock_tune_delay_msec, "Simulate a tune delay"); 61f90cf607SDaniel W. S. Almeida 621cb23db9SMauro Carvalho Chehab static unsigned int vidtv_valid_dvb_t_freqs[NUM_VALID_TUNER_FREQS] = { 631cb23db9SMauro Carvalho Chehab 474000000 641cb23db9SMauro Carvalho Chehab }; 651cb23db9SMauro Carvalho Chehab 66f90cf607SDaniel W. S. Almeida module_param_array(vidtv_valid_dvb_t_freqs, uint, NULL, 0); 67f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(vidtv_valid_dvb_t_freqs, 689ec6f4bbSMauro Carvalho Chehab "Valid DVB-T frequencies to simulate, in Hz"); 69f90cf607SDaniel W. S. Almeida 701cb23db9SMauro Carvalho Chehab static unsigned int vidtv_valid_dvb_c_freqs[NUM_VALID_TUNER_FREQS] = { 711cb23db9SMauro Carvalho Chehab 474000000 721cb23db9SMauro Carvalho Chehab }; 731cb23db9SMauro Carvalho Chehab 74f90cf607SDaniel W. S. Almeida module_param_array(vidtv_valid_dvb_c_freqs, uint, NULL, 0); 75f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(vidtv_valid_dvb_c_freqs, 769ec6f4bbSMauro Carvalho Chehab "Valid DVB-C frequencies to simulate, in Hz"); 77f90cf607SDaniel W. S. Almeida 781cb23db9SMauro Carvalho Chehab static unsigned int vidtv_valid_dvb_s_freqs[NUM_VALID_TUNER_FREQS] = { 799ec6f4bbSMauro Carvalho Chehab 11362000 801cb23db9SMauro Carvalho Chehab }; 81f90cf607SDaniel W. S. Almeida module_param_array(vidtv_valid_dvb_s_freqs, uint, NULL, 0); 82f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(vidtv_valid_dvb_s_freqs, 839ec6f4bbSMauro Carvalho Chehab "Valid DVB-S/S2 frequencies to simulate at Ku-Band, in kHz"); 84f90cf607SDaniel W. S. Almeida 85f90cf607SDaniel W. S. Almeida static unsigned int max_frequency_shift_hz; 86f90cf607SDaniel W. S. Almeida module_param(max_frequency_shift_hz, uint, 0); 87f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(max_frequency_shift_hz, 88f90cf607SDaniel W. S. Almeida "Maximum shift in HZ allowed when tuning in a channel"); 89f90cf607SDaniel W. S. Almeida 90f90cf607SDaniel W. S. Almeida DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nums); 91f90cf607SDaniel W. S. Almeida 92f90cf607SDaniel W. S. Almeida /* 93f90cf607SDaniel W. S. Almeida * Influences the signal acquisition time. See ISO/IEC 13818-1 : 2000. p. 113. 94f90cf607SDaniel W. S. Almeida */ 95f90cf607SDaniel W. S. Almeida static unsigned int si_period_msec = 40; 96f90cf607SDaniel W. S. Almeida module_param(si_period_msec, uint, 0); 97f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(si_period_msec, "How often to send SI packets. Default: 40ms"); 98f90cf607SDaniel W. S. Almeida 99f90cf607SDaniel W. S. Almeida static unsigned int pcr_period_msec = 40; 100f90cf607SDaniel W. S. Almeida module_param(pcr_period_msec, uint, 0); 101*a8bd461cSMauro Carvalho Chehab MODULE_PARM_DESC(pcr_period_msec, 102*a8bd461cSMauro Carvalho Chehab "How often to send PCR packets. Default: 40ms"); 103f90cf607SDaniel W. S. Almeida 104f90cf607SDaniel W. S. Almeida static unsigned int mux_rate_kbytes_sec = 4096; 105f90cf607SDaniel W. S. Almeida module_param(mux_rate_kbytes_sec, uint, 0); 106f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(mux_rate_kbytes_sec, "Mux rate: will pad stream if below"); 107f90cf607SDaniel W. S. Almeida 108f90cf607SDaniel W. S. Almeida static unsigned int pcr_pid = 0x200; 109f90cf607SDaniel W. S. Almeida module_param(pcr_pid, uint, 0); 110f90cf607SDaniel W. S. Almeida MODULE_PARM_DESC(pcr_pid, "PCR PID for all channels: defaults to 0x200"); 111f90cf607SDaniel W. S. Almeida 112f90cf607SDaniel W. S. Almeida static unsigned int mux_buf_sz_pkts; 113f90cf607SDaniel W. S. Almeida module_param(mux_buf_sz_pkts, uint, 0); 114*a8bd461cSMauro Carvalho Chehab MODULE_PARM_DESC(mux_buf_sz_pkts, 115*a8bd461cSMauro Carvalho Chehab "Size for the internal mux buffer in multiples of 188 bytes"); 116ad458524SMauro Carvalho Chehab 117f90cf607SDaniel W. S. Almeida static u32 vidtv_bridge_mux_buf_sz_for_mux_rate(void) 118f90cf607SDaniel W. S. Almeida { 119ad458524SMauro Carvalho Chehab u32 max_elapsed_time_msecs = VIDTV_MAX_SLEEP_USECS / USEC_PER_MSEC; 120f90cf607SDaniel W. S. Almeida u32 mux_buf_sz = mux_buf_sz_pkts * TS_PACKET_LEN; 121*a8bd461cSMauro Carvalho Chehab u32 nbytes_expected; 122f90cf607SDaniel W. S. Almeida 123ad458524SMauro Carvalho Chehab nbytes_expected = mux_rate_kbytes_sec; 124f90cf607SDaniel W. S. Almeida nbytes_expected *= max_elapsed_time_msecs; 125f90cf607SDaniel W. S. Almeida 126f90cf607SDaniel W. S. Almeida mux_buf_sz = roundup(nbytes_expected, TS_PACKET_LEN); 127ad458524SMauro Carvalho Chehab mux_buf_sz += mux_buf_sz / 10; 128f90cf607SDaniel W. S. Almeida 129ad458524SMauro Carvalho Chehab if (mux_buf_sz < MUX_BUF_MIN_SZ) 130ad458524SMauro Carvalho Chehab mux_buf_sz = MUX_BUF_MIN_SZ; 131f90cf607SDaniel W. S. Almeida 132ad458524SMauro Carvalho Chehab if (mux_buf_sz > MUX_BUF_MAX_SZ) 133ad458524SMauro Carvalho Chehab mux_buf_sz = MUX_BUF_MAX_SZ; 134f90cf607SDaniel W. S. Almeida 135ad458524SMauro Carvalho Chehab return mux_buf_sz; 136f90cf607SDaniel W. S. Almeida } 137f90cf607SDaniel W. S. Almeida 138f90cf607SDaniel W. S. Almeida static bool vidtv_bridge_check_demod_lock(struct vidtv_dvb *dvb, u32 n) 139f90cf607SDaniel W. S. Almeida { 140f90cf607SDaniel W. S. Almeida enum fe_status status; 141f90cf607SDaniel W. S. Almeida 142f90cf607SDaniel W. S. Almeida dvb->fe[n]->ops.read_status(dvb->fe[n], &status); 143f90cf607SDaniel W. S. Almeida 144f90cf607SDaniel W. S. Almeida return status == (FE_HAS_SIGNAL | 145f90cf607SDaniel W. S. Almeida FE_HAS_CARRIER | 146f90cf607SDaniel W. S. Almeida FE_HAS_VITERBI | 147f90cf607SDaniel W. S. Almeida FE_HAS_SYNC | 148f90cf607SDaniel W. S. Almeida FE_HAS_LOCK); 149f90cf607SDaniel W. S. Almeida } 150f90cf607SDaniel W. S. Almeida 151f90cf607SDaniel W. S. Almeida /* 152*a8bd461cSMauro Carvalho Chehab * called on a separate thread by the mux when new packets become available 153f90cf607SDaniel W. S. Almeida */ 154*a8bd461cSMauro Carvalho Chehab static void vidtv_bridge_on_new_pkts_avail(void *priv, u8 *buf, u32 npkts) 155*a8bd461cSMauro Carvalho Chehab { 156*a8bd461cSMauro Carvalho Chehab struct vidtv_dvb *dvb = priv; 157f90cf607SDaniel W. S. Almeida 158f90cf607SDaniel W. S. Almeida /* drop packets if we lose the lock */ 159f90cf607SDaniel W. S. Almeida if (vidtv_bridge_check_demod_lock(dvb, 0)) 160f90cf607SDaniel W. S. Almeida dvb_dmx_swfilter_packets(&dvb->demux, buf, npkts); 161f90cf607SDaniel W. S. Almeida } 162f90cf607SDaniel W. S. Almeida 163f90cf607SDaniel W. S. Almeida static int vidtv_start_streaming(struct vidtv_dvb *dvb) 164f90cf607SDaniel W. S. Almeida { 1650a33ab16SMauro Carvalho Chehab struct vidtv_mux_init_args mux_args = { 1660a33ab16SMauro Carvalho Chehab .mux_rate_kbytes_sec = mux_rate_kbytes_sec, 1670a33ab16SMauro Carvalho Chehab .on_new_packets_available_cb = vidtv_bridge_on_new_pkts_avail, 1680a33ab16SMauro Carvalho Chehab .pcr_period_usecs = pcr_period_msec * USEC_PER_MSEC, 1690a33ab16SMauro Carvalho Chehab .si_period_usecs = si_period_msec * USEC_PER_MSEC, 1700a33ab16SMauro Carvalho Chehab .pcr_pid = pcr_pid, 1710a33ab16SMauro Carvalho Chehab .transport_stream_id = VIDTV_DEFAULT_TS_ID, 1720a33ab16SMauro Carvalho Chehab .network_id = VIDTV_DEFAULT_NETWORK_ID, 1730a33ab16SMauro Carvalho Chehab .network_name = VIDTV_DEFAULT_NETWORK_NAME, 1740a33ab16SMauro Carvalho Chehab .priv = dvb, 1750a33ab16SMauro Carvalho Chehab }; 1769cfb4d36SMauro Carvalho Chehab struct device *dev = &dvb->pdev->dev; 177f90cf607SDaniel W. S. Almeida u32 mux_buf_sz; 178f90cf607SDaniel W. S. Almeida 179f90cf607SDaniel W. S. Almeida if (dvb->streaming) { 1809cfb4d36SMauro Carvalho Chehab dev_warn_ratelimited(dev, "Already streaming. Skipping.\n"); 181f90cf607SDaniel W. S. Almeida return 0; 182f90cf607SDaniel W. S. Almeida } 183f90cf607SDaniel W. S. Almeida 184*a8bd461cSMauro Carvalho Chehab if (mux_buf_sz_pkts) 185*a8bd461cSMauro Carvalho Chehab mux_buf_sz = mux_buf_sz_pkts; 186*a8bd461cSMauro Carvalho Chehab else 187*a8bd461cSMauro Carvalho Chehab mux_buf_sz = vidtv_bridge_mux_buf_sz_for_mux_rate(); 188f90cf607SDaniel W. S. Almeida 189f90cf607SDaniel W. S. Almeida mux_args.mux_buf_sz = mux_buf_sz; 190f90cf607SDaniel W. S. Almeida 191f90cf607SDaniel W. S. Almeida dvb->streaming = true; 1920a33ab16SMauro Carvalho Chehab dvb->mux = vidtv_mux_init(dvb->fe[0], dev, &mux_args); 1933be80379SMauro Carvalho Chehab if (!dvb->mux) 1943be80379SMauro Carvalho Chehab return -ENOMEM; 195f90cf607SDaniel W. S. Almeida vidtv_mux_start_thread(dvb->mux); 196f90cf607SDaniel W. S. Almeida 1979cfb4d36SMauro Carvalho Chehab dev_dbg_ratelimited(dev, "Started streaming\n"); 198f90cf607SDaniel W. S. Almeida return 0; 199f90cf607SDaniel W. S. Almeida } 200f90cf607SDaniel W. S. Almeida 201f90cf607SDaniel W. S. Almeida static int vidtv_stop_streaming(struct vidtv_dvb *dvb) 202f90cf607SDaniel W. S. Almeida { 2039cfb4d36SMauro Carvalho Chehab struct device *dev = &dvb->pdev->dev; 2049cfb4d36SMauro Carvalho Chehab 205f90cf607SDaniel W. S. Almeida dvb->streaming = false; 206f90cf607SDaniel W. S. Almeida vidtv_mux_stop_thread(dvb->mux); 207f90cf607SDaniel W. S. Almeida vidtv_mux_destroy(dvb->mux); 208f90cf607SDaniel W. S. Almeida dvb->mux = NULL; 209f90cf607SDaniel W. S. Almeida 2109cfb4d36SMauro Carvalho Chehab dev_dbg_ratelimited(dev, "Stopped streaming\n"); 211f90cf607SDaniel W. S. Almeida return 0; 212f90cf607SDaniel W. S. Almeida } 213f90cf607SDaniel W. S. Almeida 214f90cf607SDaniel W. S. Almeida static int vidtv_start_feed(struct dvb_demux_feed *feed) 215f90cf607SDaniel W. S. Almeida { 216f90cf607SDaniel W. S. Almeida struct dvb_demux *demux = feed->demux; 217f90cf607SDaniel W. S. Almeida struct vidtv_dvb *dvb = demux->priv; 218f90cf607SDaniel W. S. Almeida int ret; 219*a8bd461cSMauro Carvalho Chehab int rc; 220f90cf607SDaniel W. S. Almeida 221f90cf607SDaniel W. S. Almeida if (!demux->dmx.frontend) 222f90cf607SDaniel W. S. Almeida return -EINVAL; 223f90cf607SDaniel W. S. Almeida 224f90cf607SDaniel W. S. Almeida mutex_lock(&dvb->feed_lock); 225f90cf607SDaniel W. S. Almeida 226f90cf607SDaniel W. S. Almeida dvb->nfeeds++; 227f90cf607SDaniel W. S. Almeida rc = dvb->nfeeds; 228f90cf607SDaniel W. S. Almeida 229f90cf607SDaniel W. S. Almeida if (dvb->nfeeds == 1) { 230f90cf607SDaniel W. S. Almeida ret = vidtv_start_streaming(dvb); 231f90cf607SDaniel W. S. Almeida if (ret < 0) 232f90cf607SDaniel W. S. Almeida rc = ret; 233f90cf607SDaniel W. S. Almeida } 234f90cf607SDaniel W. S. Almeida 235f90cf607SDaniel W. S. Almeida mutex_unlock(&dvb->feed_lock); 236f90cf607SDaniel W. S. Almeida return rc; 237f90cf607SDaniel W. S. Almeida } 238f90cf607SDaniel W. S. Almeida 239f90cf607SDaniel W. S. Almeida static int vidtv_stop_feed(struct dvb_demux_feed *feed) 240f90cf607SDaniel W. S. Almeida { 241f90cf607SDaniel W. S. Almeida struct dvb_demux *demux = feed->demux; 242f90cf607SDaniel W. S. Almeida struct vidtv_dvb *dvb = demux->priv; 243f90cf607SDaniel W. S. Almeida int err = 0; 244f90cf607SDaniel W. S. Almeida 245f90cf607SDaniel W. S. Almeida mutex_lock(&dvb->feed_lock); 246f90cf607SDaniel W. S. Almeida dvb->nfeeds--; 247f90cf607SDaniel W. S. Almeida 248f90cf607SDaniel W. S. Almeida if (!dvb->nfeeds) 249f90cf607SDaniel W. S. Almeida err = vidtv_stop_streaming(dvb); 250f90cf607SDaniel W. S. Almeida 251f90cf607SDaniel W. S. Almeida mutex_unlock(&dvb->feed_lock); 252f90cf607SDaniel W. S. Almeida return err; 253f90cf607SDaniel W. S. Almeida } 254f90cf607SDaniel W. S. Almeida 255f90cf607SDaniel W. S. Almeida static struct dvb_frontend *vidtv_get_frontend_ptr(struct i2c_client *c) 256f90cf607SDaniel W. S. Almeida { 257f90cf607SDaniel W. S. Almeida struct vidtv_demod_state *state = i2c_get_clientdata(c); 258f90cf607SDaniel W. S. Almeida 259*a8bd461cSMauro Carvalho Chehab /* the demod will set this when its probe function runs */ 260f90cf607SDaniel W. S. Almeida return &state->frontend; 261f90cf607SDaniel W. S. Almeida } 262f90cf607SDaniel W. S. Almeida 263f90cf607SDaniel W. S. Almeida static int vidtv_master_xfer(struct i2c_adapter *i2c_adap, 264f90cf607SDaniel W. S. Almeida struct i2c_msg msgs[], 265f90cf607SDaniel W. S. Almeida int num) 266f90cf607SDaniel W. S. Almeida { 267*a8bd461cSMauro Carvalho Chehab /* 268*a8bd461cSMauro Carvalho Chehab * Right now, this virtual driver doesn't really send or receive 269*a8bd461cSMauro Carvalho Chehab * messages from I2C. A real driver will require an implementation 270*a8bd461cSMauro Carvalho Chehab * here. 271*a8bd461cSMauro Carvalho Chehab */ 272f90cf607SDaniel W. S. Almeida return 0; 273f90cf607SDaniel W. S. Almeida } 274f90cf607SDaniel W. S. Almeida 275f90cf607SDaniel W. S. Almeida static u32 vidtv_i2c_func(struct i2c_adapter *adapter) 276f90cf607SDaniel W. S. Almeida { 277f90cf607SDaniel W. S. Almeida return I2C_FUNC_I2C; 278f90cf607SDaniel W. S. Almeida } 279f90cf607SDaniel W. S. Almeida 280f90cf607SDaniel W. S. Almeida static const struct i2c_algorithm vidtv_i2c_algorithm = { 281f90cf607SDaniel W. S. Almeida .master_xfer = vidtv_master_xfer, 282f90cf607SDaniel W. S. Almeida .functionality = vidtv_i2c_func, 283f90cf607SDaniel W. S. Almeida }; 284f90cf607SDaniel W. S. Almeida 285f90cf607SDaniel W. S. Almeida static int vidtv_bridge_i2c_register_adap(struct vidtv_dvb *dvb) 286f90cf607SDaniel W. S. Almeida { 287f90cf607SDaniel W. S. Almeida struct i2c_adapter *i2c_adapter = &dvb->i2c_adapter; 288f90cf607SDaniel W. S. Almeida 289f90cf607SDaniel W. S. Almeida strscpy(i2c_adapter->name, "vidtv_i2c", sizeof(i2c_adapter->name)); 290f90cf607SDaniel W. S. Almeida i2c_adapter->owner = THIS_MODULE; 291f90cf607SDaniel W. S. Almeida i2c_adapter->algo = &vidtv_i2c_algorithm; 292f90cf607SDaniel W. S. Almeida i2c_adapter->algo_data = NULL; 293f90cf607SDaniel W. S. Almeida i2c_adapter->timeout = 500; 294f90cf607SDaniel W. S. Almeida i2c_adapter->retries = 3; 295f90cf607SDaniel W. S. Almeida i2c_adapter->dev.parent = &dvb->pdev->dev; 296f90cf607SDaniel W. S. Almeida 297f90cf607SDaniel W. S. Almeida i2c_set_adapdata(i2c_adapter, dvb); 298f90cf607SDaniel W. S. Almeida return i2c_add_adapter(&dvb->i2c_adapter); 299f90cf607SDaniel W. S. Almeida } 300f90cf607SDaniel W. S. Almeida 301f90cf607SDaniel W. S. Almeida static int vidtv_bridge_register_adap(struct vidtv_dvb *dvb) 302f90cf607SDaniel W. S. Almeida { 303f90cf607SDaniel W. S. Almeida int ret = 0; 304f90cf607SDaniel W. S. Almeida 305f90cf607SDaniel W. S. Almeida ret = dvb_register_adapter(&dvb->adapter, 306f90cf607SDaniel W. S. Almeida KBUILD_MODNAME, 307f90cf607SDaniel W. S. Almeida THIS_MODULE, 308f90cf607SDaniel W. S. Almeida &dvb->i2c_adapter.dev, 309f90cf607SDaniel W. S. Almeida adapter_nums); 310f90cf607SDaniel W. S. Almeida 311f90cf607SDaniel W. S. Almeida return ret; 312f90cf607SDaniel W. S. Almeida } 313f90cf607SDaniel W. S. Almeida 314f90cf607SDaniel W. S. Almeida static int vidtv_bridge_dmx_init(struct vidtv_dvb *dvb) 315f90cf607SDaniel W. S. Almeida { 316f90cf607SDaniel W. S. Almeida dvb->demux.dmx.capabilities = DMX_TS_FILTERING | 317f90cf607SDaniel W. S. Almeida DMX_SECTION_FILTERING; 318f90cf607SDaniel W. S. Almeida 319f90cf607SDaniel W. S. Almeida dvb->demux.priv = dvb; 320f90cf607SDaniel W. S. Almeida dvb->demux.filternum = 256; 321f90cf607SDaniel W. S. Almeida dvb->demux.feednum = 256; 322f90cf607SDaniel W. S. Almeida dvb->demux.start_feed = vidtv_start_feed; 323f90cf607SDaniel W. S. Almeida dvb->demux.stop_feed = vidtv_stop_feed; 324f90cf607SDaniel W. S. Almeida 325f90cf607SDaniel W. S. Almeida return dvb_dmx_init(&dvb->demux); 326f90cf607SDaniel W. S. Almeida } 327f90cf607SDaniel W. S. Almeida 328f90cf607SDaniel W. S. Almeida static int vidtv_bridge_dmxdev_init(struct vidtv_dvb *dvb) 329f90cf607SDaniel W. S. Almeida { 330f90cf607SDaniel W. S. Almeida dvb->dmx_dev.filternum = 256; 331f90cf607SDaniel W. S. Almeida dvb->dmx_dev.demux = &dvb->demux.dmx; 332f90cf607SDaniel W. S. Almeida dvb->dmx_dev.capabilities = 0; 333f90cf607SDaniel W. S. Almeida 334f90cf607SDaniel W. S. Almeida return dvb_dmxdev_init(&dvb->dmx_dev, &dvb->adapter); 335f90cf607SDaniel W. S. Almeida } 336f90cf607SDaniel W. S. Almeida 337f90cf607SDaniel W. S. Almeida static int vidtv_bridge_probe_demod(struct vidtv_dvb *dvb, u32 n) 338f90cf607SDaniel W. S. Almeida { 339*a8bd461cSMauro Carvalho Chehab struct vidtv_demod_config cfg = { 340*a8bd461cSMauro Carvalho Chehab .drop_tslock_prob_on_low_snr = drop_tslock_prob_on_low_snr, 341*a8bd461cSMauro Carvalho Chehab .recover_tslock_prob_on_good_snr = recover_tslock_prob_on_good_snr, 342*a8bd461cSMauro Carvalho Chehab }; 343f90cf607SDaniel W. S. Almeida dvb->i2c_client_demod[n] = dvb_module_probe("dvb_vidtv_demod", 344f90cf607SDaniel W. S. Almeida NULL, 345f90cf607SDaniel W. S. Almeida &dvb->i2c_adapter, 346f90cf607SDaniel W. S. Almeida DEMOD_DEFAULT_ADDR, 347f90cf607SDaniel W. S. Almeida &cfg); 348f90cf607SDaniel W. S. Almeida 349f90cf607SDaniel W. S. Almeida /* driver will not work anyways so bail out */ 350f90cf607SDaniel W. S. Almeida if (!dvb->i2c_client_demod[n]) 351f90cf607SDaniel W. S. Almeida return -ENODEV; 352f90cf607SDaniel W. S. Almeida 353f90cf607SDaniel W. S. Almeida /* retrieve a ptr to the frontend state */ 354f90cf607SDaniel W. S. Almeida dvb->fe[n] = vidtv_get_frontend_ptr(dvb->i2c_client_demod[n]); 355f90cf607SDaniel W. S. Almeida 356f90cf607SDaniel W. S. Almeida return 0; 357f90cf607SDaniel W. S. Almeida } 358f90cf607SDaniel W. S. Almeida 359f90cf607SDaniel W. S. Almeida static int vidtv_bridge_probe_tuner(struct vidtv_dvb *dvb, u32 n) 360f90cf607SDaniel W. S. Almeida { 361*a8bd461cSMauro Carvalho Chehab struct vidtv_tuner_config cfg = { 362*a8bd461cSMauro Carvalho Chehab .fe = dvb->fe[n], 363*a8bd461cSMauro Carvalho Chehab .mock_power_up_delay_msec = mock_power_up_delay_msec, 364*a8bd461cSMauro Carvalho Chehab .mock_tune_delay_msec = mock_tune_delay_msec, 365*a8bd461cSMauro Carvalho Chehab }; 3669ec6f4bbSMauro Carvalho Chehab u32 freq; 3679ec6f4bbSMauro Carvalho Chehab int i; 368f90cf607SDaniel W. S. Almeida 3699ec6f4bbSMauro Carvalho Chehab /* TODO: check if the frequencies are at a valid range */ 3709ec6f4bbSMauro Carvalho Chehab 371f90cf607SDaniel W. S. Almeida memcpy(cfg.vidtv_valid_dvb_t_freqs, 372f90cf607SDaniel W. S. Almeida vidtv_valid_dvb_t_freqs, 373f90cf607SDaniel W. S. Almeida sizeof(vidtv_valid_dvb_t_freqs)); 374f90cf607SDaniel W. S. Almeida 375f90cf607SDaniel W. S. Almeida memcpy(cfg.vidtv_valid_dvb_c_freqs, 376f90cf607SDaniel W. S. Almeida vidtv_valid_dvb_c_freqs, 377f90cf607SDaniel W. S. Almeida sizeof(vidtv_valid_dvb_c_freqs)); 378f90cf607SDaniel W. S. Almeida 3799ec6f4bbSMauro Carvalho Chehab /* 3809ec6f4bbSMauro Carvalho Chehab * Convert Satellite frequencies from Ku-band in kHZ into S-band 3819ec6f4bbSMauro Carvalho Chehab * frequencies in Hz. 3829ec6f4bbSMauro Carvalho Chehab */ 3839ec6f4bbSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(vidtv_valid_dvb_s_freqs); i++) { 3849ec6f4bbSMauro Carvalho Chehab freq = vidtv_valid_dvb_s_freqs[i]; 3859ec6f4bbSMauro Carvalho Chehab if (freq) { 3869ec6f4bbSMauro Carvalho Chehab if (freq < LNB_CUT_FREQUENCY) 3879ec6f4bbSMauro Carvalho Chehab freq = abs(freq - LNB_LOW_FREQ); 3889ec6f4bbSMauro Carvalho Chehab else 3899ec6f4bbSMauro Carvalho Chehab freq = abs(freq - LNB_HIGH_FREQ); 3909ec6f4bbSMauro Carvalho Chehab } 3919ec6f4bbSMauro Carvalho Chehab cfg.vidtv_valid_dvb_s_freqs[i] = freq; 3929ec6f4bbSMauro Carvalho Chehab } 393f90cf607SDaniel W. S. Almeida 394f90cf607SDaniel W. S. Almeida cfg.max_frequency_shift_hz = max_frequency_shift_hz; 395f90cf607SDaniel W. S. Almeida 396f90cf607SDaniel W. S. Almeida dvb->i2c_client_tuner[n] = dvb_module_probe("dvb_vidtv_tuner", 397f90cf607SDaniel W. S. Almeida NULL, 398f90cf607SDaniel W. S. Almeida &dvb->i2c_adapter, 399f90cf607SDaniel W. S. Almeida TUNER_DEFAULT_ADDR, 400f90cf607SDaniel W. S. Almeida &cfg); 401f90cf607SDaniel W. S. Almeida 402f90cf607SDaniel W. S. Almeida return (dvb->i2c_client_tuner[n]) ? 0 : -ENODEV; 403f90cf607SDaniel W. S. Almeida } 404f90cf607SDaniel W. S. Almeida 405f90cf607SDaniel W. S. Almeida static int vidtv_bridge_dvb_init(struct vidtv_dvb *dvb) 406f90cf607SDaniel W. S. Almeida { 407*a8bd461cSMauro Carvalho Chehab int ret, i, j; 408f90cf607SDaniel W. S. Almeida 409f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_i2c_register_adap(dvb); 410f90cf607SDaniel W. S. Almeida if (ret < 0) 411f90cf607SDaniel W. S. Almeida goto fail_i2c; 412f90cf607SDaniel W. S. Almeida 413f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_register_adap(dvb); 414f90cf607SDaniel W. S. Almeida if (ret < 0) 415f90cf607SDaniel W. S. Almeida goto fail_adapter; 416f90cf607SDaniel W. S. Almeida 417f90cf607SDaniel W. S. Almeida for (i = 0; i < NUM_FE; ++i) { 418f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_probe_demod(dvb, i); 419f90cf607SDaniel W. S. Almeida if (ret < 0) 420f90cf607SDaniel W. S. Almeida goto fail_demod_probe; 421f90cf607SDaniel W. S. Almeida 422f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_probe_tuner(dvb, i); 423f90cf607SDaniel W. S. Almeida if (ret < 0) 424f90cf607SDaniel W. S. Almeida goto fail_tuner_probe; 425f90cf607SDaniel W. S. Almeida 426f90cf607SDaniel W. S. Almeida ret = dvb_register_frontend(&dvb->adapter, dvb->fe[i]); 427f90cf607SDaniel W. S. Almeida if (ret < 0) 428f90cf607SDaniel W. S. Almeida goto fail_fe; 429f90cf607SDaniel W. S. Almeida } 430f90cf607SDaniel W. S. Almeida 431f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_dmx_init(dvb); 432f90cf607SDaniel W. S. Almeida if (ret < 0) 433f90cf607SDaniel W. S. Almeida goto fail_dmx; 434f90cf607SDaniel W. S. Almeida 435f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_dmxdev_init(dvb); 436f90cf607SDaniel W. S. Almeida if (ret < 0) 437f90cf607SDaniel W. S. Almeida goto fail_dmx_dev; 438f90cf607SDaniel W. S. Almeida 439f90cf607SDaniel W. S. Almeida for (j = 0; j < NUM_FE; ++j) { 440f90cf607SDaniel W. S. Almeida ret = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, 441f90cf607SDaniel W. S. Almeida &dvb->dmx_fe[j]); 442f90cf607SDaniel W. S. Almeida if (ret < 0) 443f90cf607SDaniel W. S. Almeida goto fail_dmx_conn; 444f90cf607SDaniel W. S. Almeida 445f90cf607SDaniel W. S. Almeida /* 446f90cf607SDaniel W. S. Almeida * The source of the demux is a frontend connected 447f90cf607SDaniel W. S. Almeida * to the demux. 448f90cf607SDaniel W. S. Almeida */ 449f90cf607SDaniel W. S. Almeida dvb->dmx_fe[j].source = DMX_FRONTEND_0; 450f90cf607SDaniel W. S. Almeida } 451f90cf607SDaniel W. S. Almeida 452f90cf607SDaniel W. S. Almeida return ret; 453f90cf607SDaniel W. S. Almeida 454f90cf607SDaniel W. S. Almeida fail_dmx_conn: 455f90cf607SDaniel W. S. Almeida for (j = j - 1; j >= 0; --j) 456f90cf607SDaniel W. S. Almeida dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, 457f90cf607SDaniel W. S. Almeida &dvb->dmx_fe[j]); 458f90cf607SDaniel W. S. Almeida fail_dmx_dev: 459f90cf607SDaniel W. S. Almeida dvb_dmxdev_release(&dvb->dmx_dev); 460f90cf607SDaniel W. S. Almeida fail_dmx: 461f90cf607SDaniel W. S. Almeida dvb_dmx_release(&dvb->demux); 462f90cf607SDaniel W. S. Almeida fail_fe: 463f90cf607SDaniel W. S. Almeida for (j = i; j >= 0; --j) 464f90cf607SDaniel W. S. Almeida dvb_unregister_frontend(dvb->fe[j]); 465f90cf607SDaniel W. S. Almeida fail_tuner_probe: 466f90cf607SDaniel W. S. Almeida for (j = i; j >= 0; --j) 467f90cf607SDaniel W. S. Almeida if (dvb->i2c_client_tuner[j]) 468f90cf607SDaniel W. S. Almeida dvb_module_release(dvb->i2c_client_tuner[j]); 469f90cf607SDaniel W. S. Almeida 470f90cf607SDaniel W. S. Almeida fail_demod_probe: 471f90cf607SDaniel W. S. Almeida for (j = i; j >= 0; --j) 472f90cf607SDaniel W. S. Almeida if (dvb->i2c_client_demod[j]) 473f90cf607SDaniel W. S. Almeida dvb_module_release(dvb->i2c_client_demod[j]); 474f90cf607SDaniel W. S. Almeida 475f90cf607SDaniel W. S. Almeida fail_adapter: 476f90cf607SDaniel W. S. Almeida dvb_unregister_adapter(&dvb->adapter); 477f90cf607SDaniel W. S. Almeida 478f90cf607SDaniel W. S. Almeida fail_i2c: 479f90cf607SDaniel W. S. Almeida i2c_del_adapter(&dvb->i2c_adapter); 480f90cf607SDaniel W. S. Almeida 481f90cf607SDaniel W. S. Almeida return ret; 482f90cf607SDaniel W. S. Almeida } 483f90cf607SDaniel W. S. Almeida 484f90cf607SDaniel W. S. Almeida static int vidtv_bridge_probe(struct platform_device *pdev) 485f90cf607SDaniel W. S. Almeida { 486f90cf607SDaniel W. S. Almeida struct vidtv_dvb *dvb; 4879cfb4d36SMauro Carvalho Chehab int ret; 488f90cf607SDaniel W. S. Almeida 489f90cf607SDaniel W. S. Almeida dvb = kzalloc(sizeof(*dvb), GFP_KERNEL); 490f90cf607SDaniel W. S. Almeida if (!dvb) 491f90cf607SDaniel W. S. Almeida return -ENOMEM; 492f90cf607SDaniel W. S. Almeida 493f90cf607SDaniel W. S. Almeida dvb->pdev = pdev; 494f90cf607SDaniel W. S. Almeida 495f90cf607SDaniel W. S. Almeida ret = vidtv_bridge_dvb_init(dvb); 496f90cf607SDaniel W. S. Almeida if (ret < 0) 497f90cf607SDaniel W. S. Almeida goto err_dvb; 498f90cf607SDaniel W. S. Almeida 499f90cf607SDaniel W. S. Almeida mutex_init(&dvb->feed_lock); 500f90cf607SDaniel W. S. Almeida 501f90cf607SDaniel W. S. Almeida platform_set_drvdata(pdev, dvb); 502f90cf607SDaniel W. S. Almeida 5039cfb4d36SMauro Carvalho Chehab dev_info(&pdev->dev, "Successfully initialized vidtv!\n"); 504f90cf607SDaniel W. S. Almeida return ret; 505f90cf607SDaniel W. S. Almeida 506f90cf607SDaniel W. S. Almeida err_dvb: 507f90cf607SDaniel W. S. Almeida kfree(dvb); 508f90cf607SDaniel W. S. Almeida return ret; 509f90cf607SDaniel W. S. Almeida } 510f90cf607SDaniel W. S. Almeida 511f90cf607SDaniel W. S. Almeida static int vidtv_bridge_remove(struct platform_device *pdev) 512f90cf607SDaniel W. S. Almeida { 513f90cf607SDaniel W. S. Almeida struct vidtv_dvb *dvb; 514f90cf607SDaniel W. S. Almeida u32 i; 515f90cf607SDaniel W. S. Almeida 516f90cf607SDaniel W. S. Almeida dvb = platform_get_drvdata(pdev); 517f90cf607SDaniel W. S. Almeida 518f90cf607SDaniel W. S. Almeida mutex_destroy(&dvb->feed_lock); 519f90cf607SDaniel W. S. Almeida 52063101b75SMauro Carvalho Chehab for (i = 0; i < NUM_FE; ++i) { 52163101b75SMauro Carvalho Chehab dvb_unregister_frontend(dvb->fe[i]); 52263101b75SMauro Carvalho Chehab dvb_module_release(dvb->i2c_client_tuner[i]); 52363101b75SMauro Carvalho Chehab dvb_module_release(dvb->i2c_client_demod[i]); 52463101b75SMauro Carvalho Chehab } 525f90cf607SDaniel W. S. Almeida 526f90cf607SDaniel W. S. Almeida dvb_dmxdev_release(&dvb->dmx_dev); 527f90cf607SDaniel W. S. Almeida dvb_dmx_release(&dvb->demux); 528f90cf607SDaniel W. S. Almeida dvb_unregister_adapter(&dvb->adapter); 529f90cf607SDaniel W. S. Almeida 530f90cf607SDaniel W. S. Almeida return 0; 531f90cf607SDaniel W. S. Almeida } 532f90cf607SDaniel W. S. Almeida 533f90cf607SDaniel W. S. Almeida static void vidtv_bridge_dev_release(struct device *dev) 534f90cf607SDaniel W. S. Almeida { 535f90cf607SDaniel W. S. Almeida } 536f90cf607SDaniel W. S. Almeida 537f90cf607SDaniel W. S. Almeida static struct platform_device vidtv_bridge_dev = { 538f90cf607SDaniel W. S. Almeida .name = "vidtv_bridge", 539f90cf607SDaniel W. S. Almeida .dev.release = vidtv_bridge_dev_release, 540f90cf607SDaniel W. S. Almeida }; 541f90cf607SDaniel W. S. Almeida 542f90cf607SDaniel W. S. Almeida static struct platform_driver vidtv_bridge_driver = { 543f90cf607SDaniel W. S. Almeida .driver = { 544f90cf607SDaniel W. S. Almeida .name = "vidtv_bridge", 545f90cf607SDaniel W. S. Almeida .suppress_bind_attrs = true, 546f90cf607SDaniel W. S. Almeida }, 547f90cf607SDaniel W. S. Almeida .probe = vidtv_bridge_probe, 548f90cf607SDaniel W. S. Almeida .remove = vidtv_bridge_remove, 549f90cf607SDaniel W. S. Almeida }; 550f90cf607SDaniel W. S. Almeida 551f90cf607SDaniel W. S. Almeida static void __exit vidtv_bridge_exit(void) 552f90cf607SDaniel W. S. Almeida { 553f90cf607SDaniel W. S. Almeida platform_driver_unregister(&vidtv_bridge_driver); 554f90cf607SDaniel W. S. Almeida platform_device_unregister(&vidtv_bridge_dev); 555f90cf607SDaniel W. S. Almeida } 556f90cf607SDaniel W. S. Almeida 557f90cf607SDaniel W. S. Almeida static int __init vidtv_bridge_init(void) 558f90cf607SDaniel W. S. Almeida { 559f90cf607SDaniel W. S. Almeida int ret; 560f90cf607SDaniel W. S. Almeida 561f90cf607SDaniel W. S. Almeida ret = platform_device_register(&vidtv_bridge_dev); 562f90cf607SDaniel W. S. Almeida if (ret) 563f90cf607SDaniel W. S. Almeida return ret; 564f90cf607SDaniel W. S. Almeida 565f90cf607SDaniel W. S. Almeida ret = platform_driver_register(&vidtv_bridge_driver); 566f90cf607SDaniel W. S. Almeida if (ret) 567f90cf607SDaniel W. S. Almeida platform_device_unregister(&vidtv_bridge_dev); 568f90cf607SDaniel W. S. Almeida 569f90cf607SDaniel W. S. Almeida return ret; 570f90cf607SDaniel W. S. Almeida } 571f90cf607SDaniel W. S. Almeida 572f90cf607SDaniel W. S. Almeida module_init(vidtv_bridge_init); 573f90cf607SDaniel W. S. Almeida module_exit(vidtv_bridge_exit); 574f90cf607SDaniel W. S. Almeida 575f90cf607SDaniel W. S. Almeida MODULE_DESCRIPTION("Virtual Digital TV Test Driver"); 576f90cf607SDaniel W. S. Almeida MODULE_AUTHOR("Daniel W. S. Almeida"); 577f90cf607SDaniel W. S. Almeida MODULE_LICENSE("GPL"); 578c9f968faSMauro Carvalho Chehab MODULE_ALIAS("vidtv"); 579c9f968faSMauro Carvalho Chehab MODULE_ALIAS("dvb_vidtv"); 580