162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * cx18 functions for DVB support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> 662306a36Sopenharmony_ci * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "cx18-version.h" 1062306a36Sopenharmony_ci#include "cx18-dvb.h" 1162306a36Sopenharmony_ci#include "cx18-io.h" 1262306a36Sopenharmony_ci#include "cx18-queue.h" 1362306a36Sopenharmony_ci#include "cx18-streams.h" 1462306a36Sopenharmony_ci#include "cx18-cards.h" 1562306a36Sopenharmony_ci#include "cx18-gpio.h" 1662306a36Sopenharmony_ci#include "s5h1409.h" 1762306a36Sopenharmony_ci#include "mxl5005s.h" 1862306a36Sopenharmony_ci#include "s5h1411.h" 1962306a36Sopenharmony_ci#include "tda18271.h" 2062306a36Sopenharmony_ci#include "zl10353.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/firmware.h> 2362306a36Sopenharmony_ci#include "mt352.h" 2462306a36Sopenharmony_ci#include "mt352_priv.h" 2562306a36Sopenharmony_ci#include "xc2028.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciDVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define FWFILE "dvb-cx18-mpc718-mt352.fw" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 3262306a36Sopenharmony_ci#define CX18_CLOCK_ENABLE2 0xc71024 3362306a36Sopenharmony_ci#define CX18_DMUX_CLK_MASK 0x0080 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * CX18_CARD_HVR_1600_ESMT 3762306a36Sopenharmony_ci * CX18_CARD_HVR_1600_SAMSUNG 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic struct mxl5005s_config hauppauge_hvr1600_tuner = { 4162306a36Sopenharmony_ci .i2c_address = 0xC6 >> 1, 4262306a36Sopenharmony_ci .if_freq = IF_FREQ_5380000HZ, 4362306a36Sopenharmony_ci .xtal_freq = CRYSTAL_FREQ_16000000HZ, 4462306a36Sopenharmony_ci .agc_mode = MXL_SINGLE_AGC, 4562306a36Sopenharmony_ci .tracking_filter = MXL_TF_C_H, 4662306a36Sopenharmony_ci .rssi_enable = MXL_RSSI_ENABLE, 4762306a36Sopenharmony_ci .cap_select = MXL_CAP_SEL_ENABLE, 4862306a36Sopenharmony_ci .div_out = MXL_DIV_OUT_4, 4962306a36Sopenharmony_ci .clock_out = MXL_CLOCK_OUT_DISABLE, 5062306a36Sopenharmony_ci .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, 5162306a36Sopenharmony_ci .top = MXL5005S_TOP_25P2, 5262306a36Sopenharmony_ci .mod_mode = MXL_DIGITAL_MODE, 5362306a36Sopenharmony_ci .if_mode = MXL_ZERO_IF, 5462306a36Sopenharmony_ci .qam_gain = 0x02, 5562306a36Sopenharmony_ci .AgcMasterByte = 0x00, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic struct s5h1409_config hauppauge_hvr1600_config = { 5962306a36Sopenharmony_ci .demod_address = 0x32 >> 1, 6062306a36Sopenharmony_ci .output_mode = S5H1409_SERIAL_OUTPUT, 6162306a36Sopenharmony_ci .gpio = S5H1409_GPIO_ON, 6262306a36Sopenharmony_ci .qam_if = 44000, 6362306a36Sopenharmony_ci .inversion = S5H1409_INVERSION_OFF, 6462306a36Sopenharmony_ci .status_mode = S5H1409_DEMODLOCKING, 6562306a36Sopenharmony_ci .mpeg_timing = S5H1409_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, 6662306a36Sopenharmony_ci .hvr1600_opt = S5H1409_HVR1600_OPTIMIZE 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * CX18_CARD_HVR_1600_S5H1411 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic struct s5h1411_config hcw_s5h1411_config = { 7362306a36Sopenharmony_ci .output_mode = S5H1411_SERIAL_OUTPUT, 7462306a36Sopenharmony_ci .gpio = S5H1411_GPIO_OFF, 7562306a36Sopenharmony_ci .vsb_if = S5H1411_IF_44000, 7662306a36Sopenharmony_ci .qam_if = S5H1411_IF_4000, 7762306a36Sopenharmony_ci .inversion = S5H1411_INVERSION_ON, 7862306a36Sopenharmony_ci .status_mode = S5H1411_DEMODLOCKING, 7962306a36Sopenharmony_ci .mpeg_timing = S5H1411_MPEGTIMING_CONTINUOUS_NONINVERTING_CLOCK, 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic struct tda18271_std_map hauppauge_tda18271_std_map = { 8362306a36Sopenharmony_ci .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3, 8462306a36Sopenharmony_ci .if_lvl = 6, .rfagc_top = 0x37 }, 8562306a36Sopenharmony_ci .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, 8662306a36Sopenharmony_ci .if_lvl = 6, .rfagc_top = 0x37 }, 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic struct tda18271_config hauppauge_tda18271_config = { 9062306a36Sopenharmony_ci .std_map = &hauppauge_tda18271_std_map, 9162306a36Sopenharmony_ci .gate = TDA18271_GATE_DIGITAL, 9262306a36Sopenharmony_ci .output_opt = TDA18271_OUTPUT_LT_OFF, 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* 9662306a36Sopenharmony_ci * CX18_CARD_LEADTEK_DVR3100H 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci/* Information/confirmation of proper config values provided by Terry Wu */ 9962306a36Sopenharmony_cistatic struct zl10353_config leadtek_dvr3100h_demod = { 10062306a36Sopenharmony_ci .demod_address = 0x1e >> 1, /* Datasheet suggested straps */ 10162306a36Sopenharmony_ci .if2 = 45600, /* 4.560 MHz IF from the XC3028 */ 10262306a36Sopenharmony_ci .parallel_ts = 1, /* Not a serial TS */ 10362306a36Sopenharmony_ci .no_tuner = 1, /* XC3028 is not behind the gate */ 10462306a36Sopenharmony_ci .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */ 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * CX18_CARD_YUAN_MPC718 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci/* 11162306a36Sopenharmony_ci * Due to 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * 1. an absence of information on how to program the MT352 11462306a36Sopenharmony_ci * 2. the Linux mt352 module pushing MT352 initialization off onto us here 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * We have to use an init sequence that *you* must extract from the Windows 11762306a36Sopenharmony_ci * driver (yuanrap.sys) and which we load as a firmware. 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * If someone can provide me with a Zarlink MT352 (Intel CE6352?) Design Manual 12062306a36Sopenharmony_ci * with chip programming details, then I can remove this annoyance. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistatic int yuan_mpc718_mt352_reqfw(struct cx18_stream *stream, 12362306a36Sopenharmony_ci const struct firmware **fw) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct cx18 *cx = stream->cx; 12662306a36Sopenharmony_ci const char *fn = FWFILE; 12762306a36Sopenharmony_ci int ret; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ret = request_firmware(fw, fn, &cx->pci_dev->dev); 13062306a36Sopenharmony_ci if (ret) 13162306a36Sopenharmony_ci CX18_ERR("Unable to open firmware file %s\n", fn); 13262306a36Sopenharmony_ci else { 13362306a36Sopenharmony_ci size_t sz = (*fw)->size; 13462306a36Sopenharmony_ci if (sz < 2 || sz > 64 || (sz % 2) != 0) { 13562306a36Sopenharmony_ci CX18_ERR("Firmware %s has a bad size: %lu bytes\n", 13662306a36Sopenharmony_ci fn, (unsigned long) sz); 13762306a36Sopenharmony_ci ret = -EILSEQ; 13862306a36Sopenharmony_ci release_firmware(*fw); 13962306a36Sopenharmony_ci *fw = NULL; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (ret) { 14462306a36Sopenharmony_ci CX18_ERR("The MPC718 board variant with the MT352 DVB-T demodulator will not work without it\n"); 14562306a36Sopenharmony_ci CX18_ERR("Run 'linux/scripts/get_dvb_firmware mpc718' if you need the firmware\n"); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci return ret; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int yuan_mpc718_mt352_init(struct dvb_frontend *fe) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct cx18_dvb *dvb = container_of(fe->dvb, 15362306a36Sopenharmony_ci struct cx18_dvb, dvb_adapter); 15462306a36Sopenharmony_ci struct cx18_stream *stream = dvb->stream; 15562306a36Sopenharmony_ci const struct firmware *fw = NULL; 15662306a36Sopenharmony_ci int ret; 15762306a36Sopenharmony_ci int i; 15862306a36Sopenharmony_ci u8 buf[3]; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci ret = yuan_mpc718_mt352_reqfw(stream, &fw); 16162306a36Sopenharmony_ci if (ret) 16262306a36Sopenharmony_ci return ret; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* Loop through all the register-value pairs in the firmware file */ 16562306a36Sopenharmony_ci for (i = 0; i < fw->size; i += 2) { 16662306a36Sopenharmony_ci buf[0] = fw->data[i]; 16762306a36Sopenharmony_ci /* Intercept a few registers we want to set ourselves */ 16862306a36Sopenharmony_ci switch (buf[0]) { 16962306a36Sopenharmony_ci case TRL_NOMINAL_RATE_0: 17062306a36Sopenharmony_ci /* Set our custom OFDM bandwidth in the case below */ 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci case TRL_NOMINAL_RATE_1: 17362306a36Sopenharmony_ci /* 6 MHz: 64/7 * 6/8 / 20.48 * 2^16 = 0x55b6.db6 */ 17462306a36Sopenharmony_ci /* 7 MHz: 64/7 * 7/8 / 20.48 * 2^16 = 0x6400 */ 17562306a36Sopenharmony_ci /* 8 MHz: 64/7 * 8/8 / 20.48 * 2^16 = 0x7249.249 */ 17662306a36Sopenharmony_ci buf[1] = 0x72; 17762306a36Sopenharmony_ci buf[2] = 0x49; 17862306a36Sopenharmony_ci mt352_write(fe, buf, 3); 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci case INPUT_FREQ_0: 18162306a36Sopenharmony_ci /* Set our custom IF in the case below */ 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case INPUT_FREQ_1: 18462306a36Sopenharmony_ci /* 4.56 MHz IF: (20.48 - 4.56)/20.48 * 2^14 = 0x31c0 */ 18562306a36Sopenharmony_ci buf[1] = 0x31; 18662306a36Sopenharmony_ci buf[2] = 0xc0; 18762306a36Sopenharmony_ci mt352_write(fe, buf, 3); 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci default: 19062306a36Sopenharmony_ci /* Pass through the register-value pair from the fw */ 19162306a36Sopenharmony_ci buf[1] = fw->data[i+1]; 19262306a36Sopenharmony_ci mt352_write(fe, buf, 2); 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci buf[0] = (u8) TUNER_GO; 19862306a36Sopenharmony_ci buf[1] = 0x01; /* Go */ 19962306a36Sopenharmony_ci mt352_write(fe, buf, 2); 20062306a36Sopenharmony_ci release_firmware(fw); 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic struct mt352_config yuan_mpc718_mt352_demod = { 20562306a36Sopenharmony_ci .demod_address = 0x1e >> 1, 20662306a36Sopenharmony_ci .adc_clock = 20480, /* 20.480 MHz */ 20762306a36Sopenharmony_ci .if2 = 4560, /* 4.560 MHz */ 20862306a36Sopenharmony_ci .no_tuner = 1, /* XC3028 is not behind the gate */ 20962306a36Sopenharmony_ci .demod_init = yuan_mpc718_mt352_init, 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic struct zl10353_config yuan_mpc718_zl10353_demod = { 21362306a36Sopenharmony_ci .demod_address = 0x1e >> 1, /* Datasheet suggested straps */ 21462306a36Sopenharmony_ci .if2 = 45600, /* 4.560 MHz IF from the XC3028 */ 21562306a36Sopenharmony_ci .parallel_ts = 1, /* Not a serial TS */ 21662306a36Sopenharmony_ci .no_tuner = 1, /* XC3028 is not behind the gate */ 21762306a36Sopenharmony_ci .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */ 21862306a36Sopenharmony_ci}; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic struct zl10353_config gotview_dvd3_zl10353_demod = { 22162306a36Sopenharmony_ci .demod_address = 0x1e >> 1, /* Datasheet suggested straps */ 22262306a36Sopenharmony_ci .if2 = 45600, /* 4.560 MHz IF from the XC3028 */ 22362306a36Sopenharmony_ci .parallel_ts = 1, /* Not a serial TS */ 22462306a36Sopenharmony_ci .no_tuner = 1, /* XC3028 is not behind the gate */ 22562306a36Sopenharmony_ci .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */ 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int dvb_register(struct cx18_stream *stream); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* Kernel DVB framework calls this when the feed needs to start. 23162306a36Sopenharmony_ci * The CX18 framework should enable the transport DMA handling 23262306a36Sopenharmony_ci * and queue processing. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_cistatic int cx18_dvb_start_feed(struct dvb_demux_feed *feed) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct dvb_demux *demux = feed->demux; 23762306a36Sopenharmony_ci struct cx18_stream *stream = demux->priv; 23862306a36Sopenharmony_ci struct cx18 *cx; 23962306a36Sopenharmony_ci int ret; 24062306a36Sopenharmony_ci u32 v; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!stream) 24362306a36Sopenharmony_ci return -EINVAL; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci cx = stream->cx; 24662306a36Sopenharmony_ci CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", 24762306a36Sopenharmony_ci feed->pid, feed->index); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci mutex_lock(&cx->serialize_lock); 25062306a36Sopenharmony_ci ret = cx18_init_on_first_open(cx); 25162306a36Sopenharmony_ci mutex_unlock(&cx->serialize_lock); 25262306a36Sopenharmony_ci if (ret) { 25362306a36Sopenharmony_ci CX18_ERR("Failed to initialize firmware starting DVB feed\n"); 25462306a36Sopenharmony_ci return ret; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci ret = -EINVAL; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci switch (cx->card->type) { 25962306a36Sopenharmony_ci case CX18_CARD_HVR_1600_ESMT: 26062306a36Sopenharmony_ci case CX18_CARD_HVR_1600_SAMSUNG: 26162306a36Sopenharmony_ci case CX18_CARD_HVR_1600_S5H1411: 26262306a36Sopenharmony_ci v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL); 26362306a36Sopenharmony_ci v |= 0x00400000; /* Serial Mode */ 26462306a36Sopenharmony_ci v |= 0x00002000; /* Data Length - Byte */ 26562306a36Sopenharmony_ci v |= 0x00010000; /* Error - Polarity */ 26662306a36Sopenharmony_ci v |= 0x00020000; /* Error - Passthru */ 26762306a36Sopenharmony_ci v |= 0x000c0000; /* Error - Ignore */ 26862306a36Sopenharmony_ci cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci case CX18_CARD_LEADTEK_DVR3100H: 27262306a36Sopenharmony_ci case CX18_CARD_YUAN_MPC718: 27362306a36Sopenharmony_ci case CX18_CARD_GOTVIEW_PCI_DVD3: 27462306a36Sopenharmony_ci default: 27562306a36Sopenharmony_ci /* Assumption - Parallel transport - Signalling 27662306a36Sopenharmony_ci * undefined or default. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (!demux->dmx.frontend) 28262306a36Sopenharmony_ci return -EINVAL; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci mutex_lock(&stream->dvb->feedlock); 28562306a36Sopenharmony_ci if (stream->dvb->feeding++ == 0) { 28662306a36Sopenharmony_ci CX18_DEBUG_INFO("Starting Transport DMA\n"); 28762306a36Sopenharmony_ci mutex_lock(&cx->serialize_lock); 28862306a36Sopenharmony_ci set_bit(CX18_F_S_STREAMING, &stream->s_flags); 28962306a36Sopenharmony_ci ret = cx18_start_v4l2_encode_stream(stream); 29062306a36Sopenharmony_ci if (ret < 0) { 29162306a36Sopenharmony_ci CX18_DEBUG_INFO("Failed to start Transport DMA\n"); 29262306a36Sopenharmony_ci stream->dvb->feeding--; 29362306a36Sopenharmony_ci if (stream->dvb->feeding == 0) 29462306a36Sopenharmony_ci clear_bit(CX18_F_S_STREAMING, &stream->s_flags); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci mutex_unlock(&cx->serialize_lock); 29762306a36Sopenharmony_ci } else 29862306a36Sopenharmony_ci ret = 0; 29962306a36Sopenharmony_ci mutex_unlock(&stream->dvb->feedlock); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return ret; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci/* Kernel DVB framework calls this when the feed needs to stop. */ 30562306a36Sopenharmony_cistatic int cx18_dvb_stop_feed(struct dvb_demux_feed *feed) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct dvb_demux *demux = feed->demux; 30862306a36Sopenharmony_ci struct cx18_stream *stream = demux->priv; 30962306a36Sopenharmony_ci struct cx18 *cx; 31062306a36Sopenharmony_ci int ret = -EINVAL; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (stream) { 31362306a36Sopenharmony_ci cx = stream->cx; 31462306a36Sopenharmony_ci CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n", 31562306a36Sopenharmony_ci feed->pid, feed->index); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci mutex_lock(&stream->dvb->feedlock); 31862306a36Sopenharmony_ci if (--stream->dvb->feeding == 0) { 31962306a36Sopenharmony_ci CX18_DEBUG_INFO("Stopping Transport DMA\n"); 32062306a36Sopenharmony_ci mutex_lock(&cx->serialize_lock); 32162306a36Sopenharmony_ci ret = cx18_stop_v4l2_encode_stream(stream, 0); 32262306a36Sopenharmony_ci mutex_unlock(&cx->serialize_lock); 32362306a36Sopenharmony_ci } else 32462306a36Sopenharmony_ci ret = 0; 32562306a36Sopenharmony_ci mutex_unlock(&stream->dvb->feedlock); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci return ret; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ciint cx18_dvb_register(struct cx18_stream *stream) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct cx18 *cx = stream->cx; 33462306a36Sopenharmony_ci struct cx18_dvb *dvb = stream->dvb; 33562306a36Sopenharmony_ci struct dvb_adapter *dvb_adapter; 33662306a36Sopenharmony_ci struct dvb_demux *dvbdemux; 33762306a36Sopenharmony_ci struct dmx_demux *dmx; 33862306a36Sopenharmony_ci int ret; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (!dvb) 34162306a36Sopenharmony_ci return -EINVAL; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci dvb->enabled = 0; 34462306a36Sopenharmony_ci dvb->stream = stream; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ret = dvb_register_adapter(&dvb->dvb_adapter, 34762306a36Sopenharmony_ci CX18_DRIVER_NAME, 34862306a36Sopenharmony_ci THIS_MODULE, &cx->pci_dev->dev, adapter_nr); 34962306a36Sopenharmony_ci if (ret < 0) 35062306a36Sopenharmony_ci goto err_out; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci dvb_adapter = &dvb->dvb_adapter; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci dvbdemux = &dvb->demux; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci dvbdemux->priv = (void *)stream; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci dvbdemux->filternum = 256; 35962306a36Sopenharmony_ci dvbdemux->feednum = 256; 36062306a36Sopenharmony_ci dvbdemux->start_feed = cx18_dvb_start_feed; 36162306a36Sopenharmony_ci dvbdemux->stop_feed = cx18_dvb_stop_feed; 36262306a36Sopenharmony_ci dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | 36362306a36Sopenharmony_ci DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); 36462306a36Sopenharmony_ci ret = dvb_dmx_init(dvbdemux); 36562306a36Sopenharmony_ci if (ret < 0) 36662306a36Sopenharmony_ci goto err_dvb_unregister_adapter; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci dmx = &dvbdemux->dmx; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci dvb->hw_frontend.source = DMX_FRONTEND_0; 37162306a36Sopenharmony_ci dvb->mem_frontend.source = DMX_MEMORY_FE; 37262306a36Sopenharmony_ci dvb->dmxdev.filternum = 256; 37362306a36Sopenharmony_ci dvb->dmxdev.demux = dmx; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter); 37662306a36Sopenharmony_ci if (ret < 0) 37762306a36Sopenharmony_ci goto err_dvb_dmx_release; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ret = dmx->add_frontend(dmx, &dvb->hw_frontend); 38062306a36Sopenharmony_ci if (ret < 0) 38162306a36Sopenharmony_ci goto err_dvb_dmxdev_release; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ret = dmx->add_frontend(dmx, &dvb->mem_frontend); 38462306a36Sopenharmony_ci if (ret < 0) 38562306a36Sopenharmony_ci goto err_remove_hw_frontend; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci ret = dmx->connect_frontend(dmx, &dvb->hw_frontend); 38862306a36Sopenharmony_ci if (ret < 0) 38962306a36Sopenharmony_ci goto err_remove_mem_frontend; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci ret = dvb_register(stream); 39262306a36Sopenharmony_ci if (ret < 0) 39362306a36Sopenharmony_ci goto err_disconnect_frontend; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci CX18_INFO("DVB Frontend registered\n"); 39862306a36Sopenharmony_ci CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n", 39962306a36Sopenharmony_ci stream->dvb->dvb_adapter.num, stream->name, 40062306a36Sopenharmony_ci stream->buffers, stream->buf_size/1024, 40162306a36Sopenharmony_ci (stream->buf_size * 100 / 1024) % 100); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci mutex_init(&dvb->feedlock); 40462306a36Sopenharmony_ci dvb->enabled = 1; 40562306a36Sopenharmony_ci return ret; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cierr_disconnect_frontend: 40862306a36Sopenharmony_ci dmx->disconnect_frontend(dmx); 40962306a36Sopenharmony_cierr_remove_mem_frontend: 41062306a36Sopenharmony_ci dmx->remove_frontend(dmx, &dvb->mem_frontend); 41162306a36Sopenharmony_cierr_remove_hw_frontend: 41262306a36Sopenharmony_ci dmx->remove_frontend(dmx, &dvb->hw_frontend); 41362306a36Sopenharmony_cierr_dvb_dmxdev_release: 41462306a36Sopenharmony_ci dvb_dmxdev_release(&dvb->dmxdev); 41562306a36Sopenharmony_cierr_dvb_dmx_release: 41662306a36Sopenharmony_ci dvb_dmx_release(dvbdemux); 41762306a36Sopenharmony_cierr_dvb_unregister_adapter: 41862306a36Sopenharmony_ci dvb_unregister_adapter(dvb_adapter); 41962306a36Sopenharmony_cierr_out: 42062306a36Sopenharmony_ci return ret; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_civoid cx18_dvb_unregister(struct cx18_stream *stream) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci struct cx18 *cx = stream->cx; 42662306a36Sopenharmony_ci struct cx18_dvb *dvb = stream->dvb; 42762306a36Sopenharmony_ci struct dvb_adapter *dvb_adapter; 42862306a36Sopenharmony_ci struct dvb_demux *dvbdemux; 42962306a36Sopenharmony_ci struct dmx_demux *dmx; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci CX18_INFO("unregister DVB\n"); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (dvb == NULL || !dvb->enabled) 43462306a36Sopenharmony_ci return; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci dvb_adapter = &dvb->dvb_adapter; 43762306a36Sopenharmony_ci dvbdemux = &dvb->demux; 43862306a36Sopenharmony_ci dmx = &dvbdemux->dmx; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci dmx->close(dmx); 44162306a36Sopenharmony_ci dvb_net_release(&dvb->dvbnet); 44262306a36Sopenharmony_ci dmx->remove_frontend(dmx, &dvb->mem_frontend); 44362306a36Sopenharmony_ci dmx->remove_frontend(dmx, &dvb->hw_frontend); 44462306a36Sopenharmony_ci dvb_dmxdev_release(&dvb->dmxdev); 44562306a36Sopenharmony_ci dvb_dmx_release(dvbdemux); 44662306a36Sopenharmony_ci dvb_unregister_frontend(dvb->fe); 44762306a36Sopenharmony_ci dvb_frontend_detach(dvb->fe); 44862306a36Sopenharmony_ci dvb_unregister_adapter(dvb_adapter); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/* All the DVB attach calls go here, this function gets modified 45262306a36Sopenharmony_ci * for each new card. cx18_dvb_start_feed() will also need changes. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_cistatic int dvb_register(struct cx18_stream *stream) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct cx18_dvb *dvb = stream->dvb; 45762306a36Sopenharmony_ci struct cx18 *cx = stream->cx; 45862306a36Sopenharmony_ci int ret = 0; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci switch (cx->card->type) { 46162306a36Sopenharmony_ci case CX18_CARD_HVR_1600_ESMT: 46262306a36Sopenharmony_ci case CX18_CARD_HVR_1600_SAMSUNG: 46362306a36Sopenharmony_ci dvb->fe = dvb_attach(s5h1409_attach, 46462306a36Sopenharmony_ci &hauppauge_hvr1600_config, 46562306a36Sopenharmony_ci &cx->i2c_adap[0]); 46662306a36Sopenharmony_ci if (dvb->fe != NULL) { 46762306a36Sopenharmony_ci dvb_attach(mxl5005s_attach, dvb->fe, 46862306a36Sopenharmony_ci &cx->i2c_adap[0], 46962306a36Sopenharmony_ci &hauppauge_hvr1600_tuner); 47062306a36Sopenharmony_ci ret = 0; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci case CX18_CARD_HVR_1600_S5H1411: 47462306a36Sopenharmony_ci dvb->fe = dvb_attach(s5h1411_attach, 47562306a36Sopenharmony_ci &hcw_s5h1411_config, 47662306a36Sopenharmony_ci &cx->i2c_adap[0]); 47762306a36Sopenharmony_ci if (dvb->fe != NULL) 47862306a36Sopenharmony_ci dvb_attach(tda18271_attach, dvb->fe, 47962306a36Sopenharmony_ci 0x60, &cx->i2c_adap[0], 48062306a36Sopenharmony_ci &hauppauge_tda18271_config); 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci case CX18_CARD_LEADTEK_DVR3100H: 48362306a36Sopenharmony_ci dvb->fe = dvb_attach(zl10353_attach, 48462306a36Sopenharmony_ci &leadtek_dvr3100h_demod, 48562306a36Sopenharmony_ci &cx->i2c_adap[1]); 48662306a36Sopenharmony_ci if (dvb->fe != NULL) { 48762306a36Sopenharmony_ci struct dvb_frontend *fe; 48862306a36Sopenharmony_ci struct xc2028_config cfg = { 48962306a36Sopenharmony_ci .i2c_adap = &cx->i2c_adap[1], 49062306a36Sopenharmony_ci .i2c_addr = 0xc2 >> 1, 49162306a36Sopenharmony_ci .ctrl = NULL, 49262306a36Sopenharmony_ci }; 49362306a36Sopenharmony_ci static struct xc2028_ctrl ctrl = { 49462306a36Sopenharmony_ci .fname = XC2028_DEFAULT_FIRMWARE, 49562306a36Sopenharmony_ci .max_len = 64, 49662306a36Sopenharmony_ci .demod = XC3028_FE_ZARLINK456, 49762306a36Sopenharmony_ci .type = XC2028_AUTO, 49862306a36Sopenharmony_ci }; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci fe = dvb_attach(xc2028_attach, dvb->fe, &cfg); 50162306a36Sopenharmony_ci if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) 50262306a36Sopenharmony_ci fe->ops.tuner_ops.set_config(fe, &ctrl); 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci case CX18_CARD_YUAN_MPC718: 50662306a36Sopenharmony_ci /* 50762306a36Sopenharmony_ci * TODO 50862306a36Sopenharmony_ci * Apparently, these cards also could instead have a 50962306a36Sopenharmony_ci * DiBcom demod supported by one of the db7000 drivers 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ci dvb->fe = dvb_attach(mt352_attach, 51262306a36Sopenharmony_ci &yuan_mpc718_mt352_demod, 51362306a36Sopenharmony_ci &cx->i2c_adap[1]); 51462306a36Sopenharmony_ci if (dvb->fe == NULL) 51562306a36Sopenharmony_ci dvb->fe = dvb_attach(zl10353_attach, 51662306a36Sopenharmony_ci &yuan_mpc718_zl10353_demod, 51762306a36Sopenharmony_ci &cx->i2c_adap[1]); 51862306a36Sopenharmony_ci if (dvb->fe != NULL) { 51962306a36Sopenharmony_ci struct dvb_frontend *fe; 52062306a36Sopenharmony_ci struct xc2028_config cfg = { 52162306a36Sopenharmony_ci .i2c_adap = &cx->i2c_adap[1], 52262306a36Sopenharmony_ci .i2c_addr = 0xc2 >> 1, 52362306a36Sopenharmony_ci .ctrl = NULL, 52462306a36Sopenharmony_ci }; 52562306a36Sopenharmony_ci static struct xc2028_ctrl ctrl = { 52662306a36Sopenharmony_ci .fname = XC2028_DEFAULT_FIRMWARE, 52762306a36Sopenharmony_ci .max_len = 64, 52862306a36Sopenharmony_ci .demod = XC3028_FE_ZARLINK456, 52962306a36Sopenharmony_ci .type = XC2028_AUTO, 53062306a36Sopenharmony_ci }; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci fe = dvb_attach(xc2028_attach, dvb->fe, &cfg); 53362306a36Sopenharmony_ci if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) 53462306a36Sopenharmony_ci fe->ops.tuner_ops.set_config(fe, &ctrl); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci case CX18_CARD_GOTVIEW_PCI_DVD3: 53862306a36Sopenharmony_ci dvb->fe = dvb_attach(zl10353_attach, 53962306a36Sopenharmony_ci &gotview_dvd3_zl10353_demod, 54062306a36Sopenharmony_ci &cx->i2c_adap[1]); 54162306a36Sopenharmony_ci if (dvb->fe != NULL) { 54262306a36Sopenharmony_ci struct dvb_frontend *fe; 54362306a36Sopenharmony_ci struct xc2028_config cfg = { 54462306a36Sopenharmony_ci .i2c_adap = &cx->i2c_adap[1], 54562306a36Sopenharmony_ci .i2c_addr = 0xc2 >> 1, 54662306a36Sopenharmony_ci .ctrl = NULL, 54762306a36Sopenharmony_ci }; 54862306a36Sopenharmony_ci static struct xc2028_ctrl ctrl = { 54962306a36Sopenharmony_ci .fname = XC2028_DEFAULT_FIRMWARE, 55062306a36Sopenharmony_ci .max_len = 64, 55162306a36Sopenharmony_ci .demod = XC3028_FE_ZARLINK456, 55262306a36Sopenharmony_ci .type = XC2028_AUTO, 55362306a36Sopenharmony_ci }; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci fe = dvb_attach(xc2028_attach, dvb->fe, &cfg); 55662306a36Sopenharmony_ci if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) 55762306a36Sopenharmony_ci fe->ops.tuner_ops.set_config(fe, &ctrl); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci break; 56062306a36Sopenharmony_ci default: 56162306a36Sopenharmony_ci /* No Digital Tv Support */ 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (dvb->fe == NULL) { 56662306a36Sopenharmony_ci CX18_ERR("frontend initialization failed\n"); 56762306a36Sopenharmony_ci return -1; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci dvb->fe->callback = cx18_reset_tuner_gpio; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe); 57362306a36Sopenharmony_ci if (ret < 0) { 57462306a36Sopenharmony_ci if (dvb->fe->ops.release) 57562306a36Sopenharmony_ci dvb->fe->ops.release(dvb->fe); 57662306a36Sopenharmony_ci return ret; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* 58062306a36Sopenharmony_ci * The firmware seems to enable the TS DMUX clock 58162306a36Sopenharmony_ci * under various circumstances. However, since we know we 58262306a36Sopenharmony_ci * might use it, let's just turn it on ourselves here. 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ci cx18_write_reg_expect(cx, 58562306a36Sopenharmony_ci (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK, 58662306a36Sopenharmony_ci CX18_CLOCK_ENABLE2, 58762306a36Sopenharmony_ci CX18_DMUX_CLK_MASK, 58862306a36Sopenharmony_ci (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return ret; 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ciMODULE_FIRMWARE(FWFILE); 594