diff options
Diffstat (limited to 'drivers/media/dvb/dvb-usb')
-rw-r--r-- | drivers/media/dvb/dvb-usb/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 2 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/it913x.c | 105 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/mxl111sf-demod.c | 614 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/mxl111sf-demod.h | 55 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/mxl111sf.c | 228 | ||||
-rw-r--r-- | drivers/media/dvb/dvb-usb/mxl111sf.h | 2 |
7 files changed, 976 insertions, 31 deletions
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 7d0710bb1978..26c8b9e57050 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -102,6 +102,7 @@ obj-$(CONFIG_DVB_USB_IT913X) += dvb-usb-it913x.o dvb-usb-mxl111sf-objs = mxl111sf.o mxl111sf-phy.o mxl111sf-i2c.o mxl111sf-gpio.o obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o +obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 2ad33ba92ba2..2d08c9b5128a 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -37,6 +37,7 @@ #define USB_VID_HAUPPAUGE 0x2040 #define USB_VID_HYPER_PALTEK 0x1025 #define USB_VID_INTEL 0x8086 +#define USB_VID_ITETECH 0x048d #define USB_VID_KWORLD 0xeb2a #define USB_VID_KWORLD_2 0x1b80 #define USB_VID_KYE 0x0458 @@ -126,6 +127,7 @@ #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 #define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1 #define USB_PID_INTEL_CE9500 0x9500 +#define USB_PID_ITETECH_IT9135 0x9135 #define USB_PID_KWORLD_399U 0xe399 #define USB_PID_KWORLD_399U_2 0xe400 #define USB_PID_KWORLD_395U 0xe396 diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c index f027a2c1c3e8..c46226187143 100644 --- a/drivers/media/dvb/dvb-usb/it913x.c +++ b/drivers/media/dvb/dvb-usb/it913x.c @@ -60,6 +60,17 @@ struct it913x_state { u8 id; }; +struct ite_config { + u8 chip_ver; + u16 chip_type; + u32 firmware; + u8 tuner_id_0; + u8 tuner_id_1; + u8 dual_mode; +}; + +struct ite_config it913x_config; + static int it913x_bulk_write(struct usb_device *dev, u8 *snd, int len, u8 pipe) { @@ -191,18 +202,23 @@ static int it913x_read_reg(struct usb_device *udev, u32 reg) static u32 it913x_query(struct usb_device *udev, u8 pro) { int ret; - u32 res = 0; u8 data[4]; ret = it913x_io(udev, READ_LONG, pro, CMD_DEMOD_READ, - 0x1222, 0, &data[0], 1); - if (data[0] == 0x1) { - ret = it913x_io(udev, READ_SHORT, pro, + 0x1222, 0, &data[0], 3); + + it913x_config.chip_ver = data[0]; + it913x_config.chip_type = (u16)(data[2] << 8) + data[1]; + + info("Chip Version=%02x Chip Type=%04x", it913x_config.chip_ver, + it913x_config.chip_type); + + ret |= it913x_io(udev, READ_SHORT, pro, CMD_QUERYINFO, 0, 0x1, &data[0], 4); - res = (data[0] << 24) + (data[1] << 16) + + + it913x_config.firmware = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]; - } - return (ret < 0) ? 0 : res; + return (ret < 0) ? 0 : it913x_config.firmware; } static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) @@ -336,26 +352,35 @@ static int it913x_identify_state(struct usb_device *udev, int *cold) { int ret = 0, firm_no; - u8 reg, adap, ep, tun0, tun1; + u8 reg, remote; firm_no = it913x_return_status(udev); - ep = it913x_read_reg(udev, 0x49ac); - adap = it913x_read_reg(udev, 0x49c5); - tun0 = it913x_read_reg(udev, 0x49d0); - info("No. Adapters=%x Endpoints=%x Tuner Type=%x", adap, ep, tun0); + /* checnk for dual mode */ + it913x_config.dual_mode = it913x_read_reg(udev, 0x49c5); + + /* TODO different remotes */ + remote = it913x_read_reg(udev, 0x49ac); /* Remote */ + if (remote == 0) + props->rc.core.rc_codes = NULL; + + /* TODO at the moment tuner_id is always assigned to 0x38 */ + it913x_config.tuner_id_0 = it913x_read_reg(udev, 0x49d0); + + info("Dual mode=%x Remote=%x Tuner Type=%x", it913x_config.dual_mode + , remote, it913x_config.tuner_id_0); if (firm_no > 0) { *cold = 0; return 0; } - if (adap > 2) { - tun1 = it913x_read_reg(udev, 0x49e0); + if (it913x_config.dual_mode) { + it913x_config.tuner_id_1 = it913x_read_reg(udev, 0x49e0); ret = it913x_wr_reg(udev, DEV_0, GPIOH1_EN, 0x1); ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_ON, 0x1); ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x1); - msleep(50); /* Delay noticed reset cycle ? */ + msleep(50); ret |= it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x0); msleep(50); reg = it913x_read_reg(udev, GPIOH1_O); @@ -366,14 +391,19 @@ static int it913x_identify_state(struct usb_device *udev, ret = it913x_wr_reg(udev, DEV_0, GPIOH1_O, 0x0); } + props->num_adapters = 2; } else props->num_adapters = 1; reg = it913x_read_reg(udev, IO_MUX_POWER_CLK); - ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, CHIP2_I2C_ADDR); - - ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x1); + if (it913x_config.dual_mode) { + ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, CHIP2_I2C_ADDR); + ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x1); + } else { + ret |= it913x_wr_reg(udev, DEV_0, 0x4bfb, 0x0); + ret |= it913x_wr_reg(udev, DEV_0, CLK_O_EN, 0x0); + } *cold = 1; @@ -403,13 +433,11 @@ static int it913x_download_firmware(struct usb_device *udev, const struct firmware *fw) { int ret = 0, i; - u8 packet_size, dlen, tun1; + u8 packet_size, dlen; u8 *fw_data; packet_size = 0x29; - tun1 = it913x_read_reg(udev, 0x49e0); - ret = it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_100); info("FRM Starting Firmware Download"); @@ -444,11 +472,12 @@ static int it913x_download_firmware(struct usb_device *udev, ret |= it913x_wr_reg(udev, DEV_0, I2C_CLK, I2C_CLK_400); /* Tuner function */ - ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0xa0); + if (it913x_config.dual_mode) + ret |= it913x_wr_reg(udev, DEV_0_DMOD , 0xec4c, 0xa0); ret |= it913x_wr_reg(udev, DEV_0, PADODPU, 0x0); ret |= it913x_wr_reg(udev, DEV_0, AGC_O_D, 0x0); - if (tun1 > 0) { + if (it913x_config.dual_mode) { ret |= it913x_wr_reg(udev, DEV_1, PADODPU, 0x0); ret |= it913x_wr_reg(udev, DEV_1, AGC_O_D, 0x0); } @@ -475,9 +504,28 @@ static int it913x_frontend_attach(struct dvb_usb_adapter *adap) u8 adf = it913x_read_reg(udev, IO_MUX_POWER_CLK); u8 adap_addr = I2C_BASE_ADDR + (adap->id << 5); u16 ep_size = adap->props.fe[0].stream.u.bulk.buffersize; + u8 tuner_id, tuner_type; + + if (adap->id == 0) + tuner_id = it913x_config.tuner_id_0; + else + tuner_id = it913x_config.tuner_id_1; + + /* TODO we always use IT9137 possible references here*/ + /* Documentation suggests don't care */ + switch (tuner_id) { + case 0x51: + case 0x52: + case 0x60: + case 0x61: + case 0x62: + default: + case 0x38: + tuner_type = IT9137; + } adap->fe_adap[0].fe = dvb_attach(it913x_fe_attach, - &adap->dev->i2c_adap, adap_addr, adf, IT9137); + &adap->dev->i2c_adap, adap_addr, adf, tuner_type); if (adap->id == 0 && adap->fe_adap[0].fe) { ret = it913x_wr_reg(udev, DEV_0_DMOD, MP2_SW_RST, 0x1); @@ -533,6 +581,7 @@ static int it913x_probe(struct usb_interface *intf, static struct usb_device_id it913x_table[] = { { USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB499_2T_T09) }, + { USB_DEVICE(USB_VID_ITETECH, USB_PID_ITETECH_IT9135) }, {} /* Terminating entry */ }; @@ -608,12 +657,14 @@ static struct dvb_usb_device_properties it913x_properties = { .rc_codes = RC_MAP_KWORLD_315U, }, .i2c_algo = &it913x_i2c_algo, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { "Kworld UB499-2T T09(IT9137)", { &it913x_table[0], NULL }, }, - + { "ITE 9135 Generic", + { &it913x_table[1], NULL }, + }, } }; @@ -647,5 +698,5 @@ module_exit(it913x_module_exit); MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>"); MODULE_DESCRIPTION("it913x USB 2 Driver"); -MODULE_VERSION("1.06"); +MODULE_VERSION("1.07"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.c b/drivers/media/dvb/dvb-usb/mxl111sf-demod.c new file mode 100644 index 000000000000..d1f58371c711 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-demod.c @@ -0,0 +1,614 @@ +/* + * mxl111sf-demod.c - driver for the MaxLinear MXL111SF DVB-T demodulator + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "mxl111sf-demod.h" +#include "mxl111sf-reg.h" + +/* debug */ +static int mxl111sf_demod_debug; +module_param_named(debug, mxl111sf_demod_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able))."); + +#define mxl_dbg(fmt, arg...) \ + if (mxl111sf_demod_debug) \ + mxl_printk(KERN_DEBUG, fmt, ##arg) + +/* ------------------------------------------------------------------------ */ + +struct mxl111sf_demod_state { + struct mxl111sf_state *mxl_state; + + struct mxl111sf_demod_config *cfg; + + struct dvb_frontend fe; +}; + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_demod_read_reg(struct mxl111sf_demod_state *state, + u8 addr, u8 *data) +{ + return (state->cfg->read_reg) ? + state->cfg->read_reg(state->mxl_state, addr, data) : + -EINVAL; +} + +static int mxl111sf_demod_write_reg(struct mxl111sf_demod_state *state, + u8 addr, u8 data) +{ + return (state->cfg->write_reg) ? + state->cfg->write_reg(state->mxl_state, addr, data) : + -EINVAL; +} + +static +int mxl111sf_demod_program_regs(struct mxl111sf_demod_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info) +{ + return (state->cfg->program_regs) ? + state->cfg->program_regs(state->mxl_state, ctrl_reg_info) : + -EINVAL; +} + +/* ------------------------------------------------------------------------ */ +/* TPS */ + +static +int mxl1x1sf_demod_get_tps_code_rate(struct mxl111sf_demod_state *state, + fe_code_rate_t *code_rate) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_CODE_RATE_TPS_REG, &val); + /* bit<2:0> - 000:1/2, 001:2/3, 010:3/4, 011:5/6, 100:7/8 */ + if (mxl_fail(ret)) + goto fail; + + switch (val & V6_CODE_RATE_TPS_MASK) { + case 0: + *code_rate = FEC_1_2; + break; + case 1: + *code_rate = FEC_2_3; + break; + case 2: + *code_rate = FEC_3_4; + break; + case 3: + *code_rate = FEC_5_6; + break; + case 4: + *code_rate = FEC_7_8; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_constellation(struct mxl111sf_demod_state *state, + fe_modulation_t *constellation) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_MODORDER_TPS_REG, &val); + /* Constellation, 00 : QPSK, 01 : 16QAM, 10:64QAM */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_PARAM_CONSTELLATION_MASK) >> 4) { + case 0: + *constellation = QPSK; + break; + case 1: + *constellation = QAM_16; + break; + case 2: + *constellation = QAM_64; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_guard_fft_mode(struct mxl111sf_demod_state *state, + fe_transmit_mode_t *fft_mode) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_MODE_TPS_REG, &val); + /* FFT Mode, 00:2K, 01:8K, 10:4K */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_PARAM_FFT_MODE_MASK) >> 2) { + case 0: + *fft_mode = TRANSMISSION_MODE_2K; + break; + case 1: + *fft_mode = TRANSMISSION_MODE_8K; + break; + case 2: + *fft_mode = TRANSMISSION_MODE_4K; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_guard_interval(struct mxl111sf_demod_state *state, + fe_guard_interval_t *guard) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_CP_TPS_REG, &val); + /* 00:1/32, 01:1/16, 10:1/8, 11:1/4 */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_PARAM_GI_MASK) >> 4) { + case 0: + *guard = GUARD_INTERVAL_1_32; + break; + case 1: + *guard = GUARD_INTERVAL_1_16; + break; + case 2: + *guard = GUARD_INTERVAL_1_8; + break; + case 3: + *guard = GUARD_INTERVAL_1_4; + break; + } +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_hierarchy(struct mxl111sf_demod_state *state, + fe_hierarchy_t *hierarchy) +{ + u8 val; + int ret = mxl111sf_demod_read_reg(state, V6_TPS_HIERACHY_REG, &val); + /* bit<6:4> - 000:Non hierarchy, 001:1, 010:2, 011:4 */ + if (mxl_fail(ret)) + goto fail; + + switch ((val & V6_TPS_HIERARCHY_INFO_MASK) >> 6) { + case 0: + *hierarchy = HIERARCHY_NONE; + break; + case 1: + *hierarchy = HIERARCHY_1; + break; + case 2: + *hierarchy = HIERARCHY_2; + break; + case 3: + *hierarchy = HIERARCHY_4; + break; + } +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ +/* LOCKS */ + +static +int mxl1x1sf_demod_get_sync_lock_status(struct mxl111sf_demod_state *state, + int *sync_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_SYNC_LOCK_REG, &val); + if (mxl_fail(ret)) + goto fail; + *sync_lock = (val & SYNC_LOCK_MASK) >> 4; +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_rs_lock_status(struct mxl111sf_demod_state *state, + int *rs_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_RS_LOCK_DET_REG, &val); + if (mxl_fail(ret)) + goto fail; + *rs_lock = (val & RS_LOCK_DET_MASK) >> 3; +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_tps_lock_status(struct mxl111sf_demod_state *state, + int *tps_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_TPS_LOCK_REG, &val); + if (mxl_fail(ret)) + goto fail; + *tps_lock = (val & V6_PARAM_TPS_LOCK_MASK) >> 6; +fail: + return ret; +} + +static +int mxl1x1sf_demod_get_fec_lock_status(struct mxl111sf_demod_state *state, + int *fec_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_IRQ_STATUS_REG, &val); + if (mxl_fail(ret)) + goto fail; + *fec_lock = (val & IRQ_MASK_FEC_LOCK) >> 4; +fail: + return ret; +} + +#if 0 +static +int mxl1x1sf_demod_get_cp_lock_status(struct mxl111sf_demod_state *state, + int *cp_lock) +{ + u8 val = 0; + int ret = mxl111sf_demod_read_reg(state, V6_CP_LOCK_DET_REG, &val); + if (mxl_fail(ret)) + goto fail; + *cp_lock = (val & V6_CP_LOCK_DET_MASK) >> 2; +fail: + return ret; +} +#endif + +static int mxl1x1sf_demod_reset_irq_status(struct mxl111sf_demod_state *state) +{ + return mxl111sf_demod_write_reg(state, 0x0e, 0xff); +} + +/* ------------------------------------------------------------------------ */ + +static int mxl111sf_demod_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *param) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + int ret = 0; + + struct mxl111sf_reg_ctrl_info phy_pll_patch[] = { + {0x00, 0xff, 0x01}, /* change page to 1 */ + {0x40, 0xff, 0x05}, + {0x40, 0xff, 0x01}, + {0x41, 0xff, 0xca}, + {0x41, 0xff, 0xc0}, + {0x00, 0xff, 0x00}, /* change page to 0 */ + {0, 0, 0} + }; + + mxl_dbg("()"); + + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe, param); + if (mxl_fail(ret)) + goto fail; + msleep(50); + } + ret = mxl111sf_demod_program_regs(state, phy_pll_patch); + mxl_fail(ret); + msleep(50); + ret = mxl1x1sf_demod_reset_irq_status(state); + mxl_fail(ret); + msleep(100); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +#if 0 +/* resets TS Packet error count */ +/* After setting 7th bit of V5_PER_COUNT_RESET_REG, it should be reset to 0. */ +static +int mxl1x1sf_demod_reset_packet_error_count(struct mxl111sf_demod_state *state) +{ + struct mxl111sf_reg_ctrl_info reset_per_count[] = { + {0x20, 0x01, 0x01}, + {0x20, 0x01, 0x00}, + {0, 0, 0} + }; + return mxl111sf_demod_program_regs(state, reset_per_count); +} +#endif + +/* returns TS Packet error count */ +/* PER Count = FEC_PER_COUNT * (2 ** (FEC_PER_SCALE * 4)) */ +static int mxl111sf_demod_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + u32 fec_per_count, fec_per_scale; + u8 val; + int ret; + + *ucblocks = 0; + + /* FEC_PER_COUNT Register */ + ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_COUNT_REG, &val); + if (mxl_fail(ret)) + goto fail; + + fec_per_count = val; + + /* FEC_PER_SCALE Register */ + ret = mxl111sf_demod_read_reg(state, V6_FEC_PER_SCALE_REG, &val); + if (mxl_fail(ret)) + goto fail; + + val &= V6_FEC_PER_SCALE_MASK; + val *= 4; + + fec_per_scale = 1 << val; + + fec_per_count *= fec_per_scale; + + *ucblocks = fec_per_count; +fail: + return ret; +} + +#ifdef MXL111SF_DEMOD_ENABLE_CALCULATIONS +/* FIXME: leaving this enabled breaks the build on some architectures, + * and we shouldn't have any floating point math in the kernel, anyway. + * + * These macros need to be re-written, but it's harmless to simply + * return zero for now. */ +#define CALCULATE_BER(avg_errors, count) \ + ((u32)(avg_errors * 4)/(count*64*188*8)) +#define CALCULATE_SNR(data) \ + ((u32)((10 * (u32)data / 64) - 2.5)) +#else +#define CALCULATE_BER(avg_errors, count) 0 +#define CALCULATE_SNR(data) 0 +#endif + +static int mxl111sf_demod_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + u8 val1, val2, val3; + int ret; + + *ber = 0; + + ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_LSB_REG, &val1); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_demod_read_reg(state, V6_RS_AVG_ERRORS_MSB_REG, &val2); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_demod_read_reg(state, V6_N_ACCUMULATE_REG, &val3); + if (mxl_fail(ret)) + goto fail; + + *ber = CALCULATE_BER((val1 | (val2 << 8)), val3); +fail: + return ret; +} + +static int mxl111sf_demod_calc_snr(struct mxl111sf_demod_state *state, + u16 *snr) +{ + u8 val1, val2; + int ret; + + *snr = 0; + + ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_LSB_REG, &val1); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_demod_read_reg(state, V6_SNR_RB_MSB_REG, &val2); + if (mxl_fail(ret)) + goto fail; + + *snr = CALCULATE_SNR(val1 | ((val2 & 0x03) << 8)); +fail: + return ret; +} + +static int mxl111sf_demod_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + + int ret = mxl111sf_demod_calc_snr(state, snr); + if (mxl_fail(ret)) + goto fail; + + *snr /= 10; /* 0.1 dB */ +fail: + return ret; +} + +static int mxl111sf_demod_read_status(struct dvb_frontend *fe, + fe_status_t *status) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + int ret, locked, cr_lock, sync_lock, fec_lock; + + *status = 0; + + ret = mxl1x1sf_demod_get_rs_lock_status(state, &locked); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_demod_get_tps_lock_status(state, &cr_lock); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_demod_get_sync_lock_status(state, &sync_lock); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_demod_get_fec_lock_status(state, &fec_lock); + if (mxl_fail(ret)) + goto fail; + + if (locked) + *status |= FE_HAS_SIGNAL; + if (cr_lock) + *status |= FE_HAS_CARRIER; + if (sync_lock) + *status |= FE_HAS_SYNC; + if (fec_lock) /* false positives? */ + *status |= FE_HAS_VITERBI; + + if ((locked) && (cr_lock) && (sync_lock)) + *status |= FE_HAS_LOCK; +fail: + return ret; +} + +static int mxl111sf_demod_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + fe_modulation_t constellation; + u16 snr; + + mxl111sf_demod_calc_snr(state, &snr); + mxl1x1sf_demod_get_tps_constellation(state, &constellation); + + switch (constellation) { + case QPSK: + *signal_strength = (snr >= 1300) ? + min(65535, snr * 44) : snr * 38; + break; + case QAM_16: + *signal_strength = (snr >= 1500) ? + min(65535, snr * 38) : snr * 33; + break; + case QAM_64: + *signal_strength = (snr >= 2000) ? + min(65535, snr * 29) : snr * 25; + break; + default: + *signal_strength = 0; + return -EINVAL; + } + + return 0; +} + +static int mxl111sf_demod_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + + mxl_dbg("()"); +#if 0 + p->inversion = /* FIXME */ ? INVERSION_ON : INVERSION_OFF; +#endif + if (fe->ops.tuner_ops.get_bandwidth) + fe->ops.tuner_ops.get_bandwidth(fe, &p->u.ofdm.bandwidth); + if (fe->ops.tuner_ops.get_frequency) + fe->ops.tuner_ops.get_frequency(fe, &p->frequency); + mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_HP); + mxl1x1sf_demod_get_tps_code_rate(state, &p->u.ofdm.code_rate_LP); + mxl1x1sf_demod_get_tps_constellation(state, &p->u.ofdm.constellation); + mxl1x1sf_demod_get_tps_guard_fft_mode(state, + &p->u.ofdm.transmission_mode); + mxl1x1sf_demod_get_tps_guard_interval(state, + &p->u.ofdm.guard_interval); + mxl1x1sf_demod_get_tps_hierarchy(state, + &p->u.ofdm.hierarchy_information); + + return 0; +} + +static +int mxl111sf_demod_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static void mxl111sf_demod_release(struct dvb_frontend *fe) +{ + struct mxl111sf_demod_state *state = fe->demodulator_priv; + mxl_dbg("()"); + kfree(state); + fe->demodulator_priv = NULL; +} + +static struct dvb_frontend_ops mxl111sf_demod_ops = { + + .info = { + .name = "MaxLinear MxL111SF DVB-T demodulator", + .type = FE_OFDM, + .frequency_min = 177000000, + .frequency_max = 858000000, + .frequency_stepsize = 166666, + .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_HIERARCHY_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER + }, + .release = mxl111sf_demod_release, +#if 0 + .init = mxl111sf_init, + .i2c_gate_ctrl = mxl111sf_i2c_gate_ctrl, +#endif + .set_frontend = mxl111sf_demod_set_frontend, + .get_frontend = mxl111sf_demod_get_frontend, + .get_tune_settings = mxl111sf_demod_get_tune_settings, + .read_status = mxl111sf_demod_read_status, + .read_signal_strength = mxl111sf_demod_read_signal_strength, + .read_ber = mxl111sf_demod_read_ber, + .read_snr = mxl111sf_demod_read_snr, + .read_ucblocks = mxl111sf_demod_read_ucblocks, +}; + +struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, + struct mxl111sf_demod_config *cfg) +{ + struct mxl111sf_demod_state *state = NULL; + + mxl_dbg("()"); + + state = kzalloc(sizeof(struct mxl111sf_demod_state), GFP_KERNEL); + if (state == NULL) + return NULL; + + state->mxl_state = mxl_state; + state->cfg = cfg; + + memcpy(&state->fe.ops, &mxl111sf_demod_ops, + sizeof(struct dvb_frontend_ops)); + + state->fe.demodulator_priv = state; + return &state->fe; +} +EXPORT_SYMBOL_GPL(mxl111sf_demod_attach); + +MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver"); +MODULE_AUTHOR("Michael Krufky <mkrufky@kernellabs.com>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-demod.h b/drivers/media/dvb/dvb-usb/mxl111sf-demod.h new file mode 100644 index 000000000000..432706ae5274 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/mxl111sf-demod.h @@ -0,0 +1,55 @@ +/* + * mxl111sf-demod.h - driver for the MaxLinear MXL111SF DVB-T demodulator + * + * Copyright (C) 2010 Michael Krufky <mkrufky@kernellabs.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MXL111SF_DEMOD_H__ +#define __MXL111SF_DEMOD_H__ + +#include "dvb_frontend.h" +#include "mxl111sf.h" + +struct mxl111sf_demod_config { + int (*read_reg)(struct mxl111sf_state *state, u8 addr, u8 *data); + int (*write_reg)(struct mxl111sf_state *state, u8 addr, u8 data); + int (*program_regs)(struct mxl111sf_state *state, + struct mxl111sf_reg_ctrl_info *ctrl_reg_info); +}; + +#if defined(CONFIG_DVB_USB_MXL111SF) || \ + (defined(CONFIG_DVB_USB_MXL111SF_MODULE) && defined(MODULE)) +extern +struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, + struct mxl111sf_demod_config *cfg); +#else +static inline +struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, + struct mxl111sf_demod_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_USB_MXL111SF */ + +#endif /* __MXL111SF_DEMOD_H__ */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c index 546ba5915a5b..b5c98da5d9e2 100644 --- a/drivers/media/dvb/dvb-usb/mxl111sf.c +++ b/drivers/media/dvb/dvb-usb/mxl111sf.c @@ -17,6 +17,7 @@ #include "mxl111sf-i2c.h" #include "mxl111sf-gpio.h" +#include "mxl111sf-demod.h" #include "mxl111sf-tuner.h" #include "lgdt3305.h" @@ -362,6 +363,22 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) return ret; } +static int mxl111sf_ep4_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int ret = 0; + + deb_info("%s(%d)\n", __func__, onoff); + + if (onoff) { + ret = mxl111sf_enable_usb_output(state); + mxl_fail(ret); + } + + return ret; +} + /* ------------------------------------------------------------------------ */ static struct lgdt3305_config hauppauge_lgdt3305_config = { @@ -438,6 +455,70 @@ fail: return ret; } +static struct mxl111sf_demod_config mxl_demod_config = { + .read_reg = mxl111sf_read_reg, + .write_reg = mxl111sf_write_reg, + .program_regs = mxl111sf_ctrl_program_regs, +}; + +static int mxl111sf_attach_demod(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int fe_id = adap->num_frontends_initialized; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 1 : 2; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_DVBT; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_SOC_MODE; + adap_state->ep6_clockphase = 1; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + /* dont care if this fails */ + mxl111sf_init_port_expander(state); + + adap->fe_adap[fe_id].fe = dvb_attach(mxl111sf_demod_attach, state, + &mxl_demod_config); + if (adap->fe_adap[fe_id].fe) { + adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init; + adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep; + adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + static inline int mxl111sf_set_ant_path(struct mxl111sf_state *state, int antpath) { @@ -567,7 +648,8 @@ struct i2c_algorithm mxl111sf_i2c_algo = { #endif }; -/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties; static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties; static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties; @@ -580,9 +662,15 @@ static int mxl111sf_probe(struct usb_interface *intf, if (((dvb_usb_mxl111sf_isoc) && (0 == dvb_usb_device_init(intf, + &mxl111sf_dvbt_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, &mxl111sf_atsc_isoc_properties, THIS_MODULE, &d, adapter_nr))) || 0 == dvb_usb_device_init(intf, + &mxl111sf_dvbt_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, &mxl111sf_atsc_bulk_properties, THIS_MODULE, &d, adapter_nr) || 0) { @@ -669,6 +757,36 @@ static struct usb_device_id mxl111sf_table[] = { MODULE_DEVICE_TABLE(usb, mxl111sf_table); +#define MXL111SF_EP4_BULK_STREAMING_CONFIG \ + .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \ + .stream = { \ + .type = USB_BULK, \ + .count = 5, \ + .endpoint = 0x04, \ + .u = { \ + .bulk = { \ + .buffersize = 8192, \ + } \ + } \ + } + +/* FIXME: works for v6 but not v8 silicon */ +#define MXL111SF_EP4_ISOC_STREAMING_CONFIG \ + .streaming_ctrl = mxl111sf_ep4_streaming_ctrl, \ + .stream = { \ + .type = USB_ISOC, \ + .count = 5, \ + .endpoint = 0x04, \ + .u = { \ + .isoc = { \ + .framesperurb = 96, \ + /* FIXME: v6 SILICON: */ \ + .framesize = 564, \ + .interval = 1, \ + } \ + } \ + } + #define MXL111SF_EP6_BULK_STREAMING_CONFIG \ .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \ .stream = { \ @@ -712,7 +830,7 @@ MODULE_DEVICE_TABLE(usb, mxl111sf_table); .generic_bulk_ctrl_endpoint_response = MXL_EP1_REG_READ, \ .size_of_priv = sizeof(struct mxl111sf_state) -static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { +static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = { MXL111SF_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, @@ -723,10 +841,106 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { .fe = {{ .size_of_priv = sizeof(struct mxl111sf_adap_state), + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + } }, + }, + }, + .num_device_descs = 4, + .devices = { + { "Hauppauge 126xxx DVBT (bulk)", + { NULL }, + { &mxl111sf_table[4], &mxl111sf_table[8], + NULL }, + }, + { "Hauppauge 117xxx DVBT (bulk)", + { NULL }, + { &mxl111sf_table[15], &mxl111sf_table[18], + NULL }, + }, + { "Hauppauge 138xxx DVBT (bulk)", + { NULL }, + { &mxl111sf_table[20], &mxl111sf_table[22], + &mxl111sf_table[24], &mxl111sf_table[26], + NULL }, + }, + { "Hauppauge 126xxx (tp-bulk)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 1, + .fe = {{ + .size_of_priv = sizeof(struct mxl111sf_adap_state), + + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, + } }, + }, + }, + .num_device_descs = 4, + .devices = { + { "Hauppauge 126xxx DVBT (isoc)", + { NULL }, + { &mxl111sf_table[4], &mxl111sf_table[8], + NULL }, + }, + { "Hauppauge 117xxx DVBT (isoc)", + { NULL }, + { &mxl111sf_table[15], &mxl111sf_table[18], + NULL }, + }, + { "Hauppauge 138xxx DVBT (isoc)", + { NULL }, + { &mxl111sf_table[20], &mxl111sf_table[22], + &mxl111sf_table[24], &mxl111sf_table[26], + NULL }, + }, + { "Hauppauge 126xxx (tp-isoc)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 2, + .fe = {{ + .size_of_priv = sizeof(struct mxl111sf_adap_state), + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, .tuner_attach = mxl111sf_attach_tuner, MXL111SF_EP6_BULK_STREAMING_CONFIG, + }, + { + .size_of_priv = sizeof(struct mxl111sf_adap_state), + + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, }}, }, }, @@ -776,7 +990,7 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { .adapter = { { .fe_ioctl_override = mxl111sf_fe_ioctl_override, - .num_frontends = 1, + .num_frontends = 2, .fe = {{ .size_of_priv = sizeof(struct mxl111sf_adap_state), @@ -784,6 +998,14 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { .tuner_attach = mxl111sf_attach_tuner, MXL111SF_EP6_ISOC_STREAMING_CONFIG, + }, + { + .size_of_priv = sizeof(struct mxl111sf_adap_state), + + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, }}, }, }, diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.h b/drivers/media/dvb/dvb-usb/mxl111sf.h index 5a2c7bb386cd..364d89f826bd 100644 --- a/drivers/media/dvb/dvb-usb/mxl111sf.h +++ b/drivers/media/dvb/dvb-usb/mxl111sf.h @@ -133,7 +133,7 @@ extern int dvb_usb_mxl111sf_debug; /* The following allows the mxl_fail() macro defined below to work * in externel modules, such as mxl111sf-tuner.ko, even though * dvb_usb_mxl111sf_debug is not defined within those modules */ -#ifdef __MXL111SF_TUNER_H__ +#if (defined(__MXL111SF_TUNER_H__)) || (defined(__MXL111SF_DEMOD_H__)) #define MXL_ADV_DEBUG_ENABLED MXL_ADV_DBG #else #define MXL_ADV_DEBUG_ENABLED dvb_usb_mxl111sf_debug |