📄 lgdt330x.c
字号:
/* * Support for LGDT3302 and LGDT3303 - VSB/QAM * * Copyright (C) 2005 Wilson Michaels <wilsonmichaels@earthlink.net> * * 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. * *//* * NOTES ABOUT THIS DRIVER * * This Linux driver supports: * DViCO FusionHDTV 3 Gold-Q * DViCO FusionHDTV 3 Gold-T * DViCO FusionHDTV 5 Gold * DViCO FusionHDTV 5 Lite * Air2PC/AirStar 2 ATSC 3rd generation (HD5000) * * TODO: * signal strength always returns 0. * */#include <linux/kernel.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/slab.h>#include <asm/byteorder.h>#include "dvb_frontend.h"#include "lgdt330x_priv.h"#include "lgdt330x.h"static int debug = 0;module_param(debug, int, 0644);MODULE_PARM_DESC(debug,"Turn on/off lgdt330x frontend debugging (default:off).");#define dprintk(args...) \do { \if (debug) printk(KERN_DEBUG "lgdt330x: " args); \} while (0)struct lgdt330x_state{ struct i2c_adapter* i2c; struct dvb_frontend_ops ops; /* Configuration settings */ const struct lgdt330x_config* config; struct dvb_frontend frontend; /* Demodulator private data */ fe_modulation_t current_modulation; /* Tuner private data */ u32 current_frequency;};static int i2c_write_demod_bytes (struct lgdt330x_state* state, u8 *buf, /* data bytes to send */ int len /* number of bytes to send */ ){ struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; int i; int err; for (i=0; i<len-1; i+=2){ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { printk(KERN_WARNING "lgdt330x: %s error (addr %02x <- %02x, err = %i)\n", __FUNCTION__, msg.buf[0], msg.buf[1], err); if (err < 0) return err; else return -EREMOTEIO; } msg.buf += 2; } return 0;}/* * This routine writes the register (reg) to the demod bus * then reads the data returned for (len) bytes. */static u8 i2c_read_demod_bytes (struct lgdt330x_state* state, enum I2C_REG reg, u8* buf, int len){ u8 wr [] = { reg }; struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = wr, .len = 1 }, { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = buf, .len = len }, }; int ret; ret = i2c_transfer(state->i2c, msg, 2); if (ret != 2) { printk(KERN_WARNING "lgdt330x: %s: addr 0x%02x select 0x%02x error (ret == %i)\n", __FUNCTION__, state->config->demod_address, reg, ret); } else { ret = 0; } return ret;}/* Software reset */static int lgdt3302_SwReset(struct lgdt330x_state* state){ u8 ret; u8 reset[] = { IRQ_MASK, 0x00 /* bit 6 is active low software reset * bits 5-0 are 1 to mask interrupts */ }; ret = i2c_write_demod_bytes(state, reset, sizeof(reset)); if (ret == 0) { /* force reset high (inactive) and unmask interrupts */ reset[1] = 0x7f; ret = i2c_write_demod_bytes(state, reset, sizeof(reset)); } return ret;}static int lgdt3303_SwReset(struct lgdt330x_state* state){ u8 ret; u8 reset[] = { 0x02, 0x00 /* bit 0 is active low software reset */ }; ret = i2c_write_demod_bytes(state, reset, sizeof(reset)); if (ret == 0) { /* force reset high (inactive) */ reset[1] = 0x01; ret = i2c_write_demod_bytes(state, reset, sizeof(reset)); } return ret;}static int lgdt330x_SwReset(struct lgdt330x_state* state){ switch (state->config->demod_chip) { case LGDT3302: return lgdt3302_SwReset(state); case LGDT3303: return lgdt3303_SwReset(state); default: return -ENODEV; }}static int lgdt330x_init(struct dvb_frontend* fe){ /* Hardware reset is done using gpio[0] of cx23880x chip. * I'd like to do it here, but don't know how to find chip address. * cx88-cards.c arranges for the reset bit to be inactive (high). * Maybe there needs to be a callable function in cx88-core or * the caller of this function needs to do it. */ /* * Array of byte pairs <address, value> * to initialize each different chip */ static u8 lgdt3302_init_data[] = { /* Use 50MHz parameter values from spec sheet since xtal is 50 */ /* Change the value of NCOCTFV[25:0] of carrier recovery center frequency register */ VSB_CARRIER_FREQ0, 0x00, VSB_CARRIER_FREQ1, 0x87, VSB_CARRIER_FREQ2, 0x8e, VSB_CARRIER_FREQ3, 0x01, /* Change the TPCLK pin polarity data is valid on falling clock */ DEMUX_CONTROL, 0xfb, /* Change the value of IFBW[11:0] of AGC IF/RF loop filter bandwidth register */ AGC_RF_BANDWIDTH0, 0x40, AGC_RF_BANDWIDTH1, 0x93, AGC_RF_BANDWIDTH2, 0x00, /* Change the value of bit 6, 'nINAGCBY' and 'NSSEL[1:0] of ACG function control register 2 */ AGC_FUNC_CTRL2, 0xc6, /* Change the value of bit 6 'RFFIX' of AGC function control register 3 */ AGC_FUNC_CTRL3, 0x40, /* Set the value of 'INLVTHD' register 0x2a/0x2c to 0x7fe */ AGC_DELAY0, 0x07, AGC_DELAY2, 0xfe, /* Change the value of IAGCBW[15:8] of inner AGC loop filter bandwith */ AGC_LOOP_BANDWIDTH0, 0x08, AGC_LOOP_BANDWIDTH1, 0x9a }; static u8 lgdt3303_init_data[] = { 0x4c, 0x14 }; static u8 flip_lgdt3303_init_data[] = { 0x4c, 0x14, 0x87, 0xf3 }; struct lgdt330x_state* state = fe->demodulator_priv; char *chip_name; int err; switch (state->config->demod_chip) { case LGDT3302: chip_name = "LGDT3302"; err = i2c_write_demod_bytes(state, lgdt3302_init_data, sizeof(lgdt3302_init_data)); break; case LGDT3303: chip_name = "LGDT3303"; if (state->config->clock_polarity_flip) { err = i2c_write_demod_bytes(state, flip_lgdt3303_init_data, sizeof(flip_lgdt3303_init_data)); } else { err = i2c_write_demod_bytes(state, lgdt3303_init_data, sizeof(lgdt3303_init_data)); } break; default: chip_name = "undefined"; printk (KERN_WARNING "Only LGDT3302 and LGDT3303 are supported chips.\n"); err = -ENODEV; } dprintk("%s entered as %s\n", __FUNCTION__, chip_name); if (err < 0) return err; return lgdt330x_SwReset(state);}static int lgdt330x_read_ber(struct dvb_frontend* fe, u32* ber){ *ber = 0; /* Not supplied by the demod chips */ return 0;}static int lgdt330x_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks){ struct lgdt330x_state* state = fe->demodulator_priv; int err; u8 buf[2]; switch (state->config->demod_chip) { case LGDT3302: err = i2c_read_demod_bytes(state, LGDT3302_PACKET_ERR_COUNTER1, buf, sizeof(buf)); break; case LGDT3303: err = i2c_read_demod_bytes(state, LGDT3303_PACKET_ERR_COUNTER1, buf, sizeof(buf)); break; default: printk(KERN_WARNING "Only LGDT3302 and LGDT3303 are supported chips.\n"); err = -ENODEV; } *ucblocks = (buf[0] << 8) | buf[1]; return 0;}static int lgdt330x_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *param){ /* * Array of byte pairs <address, value> * to initialize 8VSB for lgdt3303 chip 50 MHz IF */ static u8 lgdt3303_8vsb_44_data[] = { 0x04, 0x00, 0x0d, 0x40, 0x0e, 0x87, 0x0f, 0x8e, 0x10, 0x01, 0x47, 0x8b }; /* * Array of byte pairs <address, value> * to initialize QAM for lgdt3303 chip */ static u8 lgdt3303_qam_data[] = { 0x04, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x51, 0x63, 0x47, 0x66, 0x48, 0x66, 0x4d, 0x1a, 0x49, 0x08, 0x4a, 0x9b }; struct lgdt330x_state* state = fe->demodulator_priv; static u8 top_ctrl_cfg[] = { TOP_CONTROL, 0x03 }; int err; /* Change only if we are actually changing the modulation */ if (state->current_modulation != param->u.vsb.modulation) { switch(param->u.vsb.modulation) { case VSB_8: dprintk("%s: VSB_8 MODE\n", __FUNCTION__); /* Select VSB mode */ top_ctrl_cfg[1] = 0x03; /* Select ANT connector if supported by card */ if (state->config->pll_rf_set) state->config->pll_rf_set(fe, 1); if (state->config->demod_chip == LGDT3303) { err = i2c_write_demod_bytes(state, lgdt3303_8vsb_44_data, sizeof(lgdt3303_8vsb_44_data)); } break; case QAM_64: dprintk("%s: QAM_64 MODE\n", __FUNCTION__); /* Select QAM_64 mode */ top_ctrl_cfg[1] = 0x00; /* Select CABLE connector if supported by card */ if (state->config->pll_rf_set) state->config->pll_rf_set(fe, 0); if (state->config->demod_chip == LGDT3303) { err = i2c_write_demod_bytes(state, lgdt3303_qam_data, sizeof(lgdt3303_qam_data)); } break; case QAM_256: dprintk("%s: QAM_256 MODE\n", __FUNCTION__); /* Select QAM_256 mode */ top_ctrl_cfg[1] = 0x01; /* Select CABLE connector if supported by card */ if (state->config->pll_rf_set) state->config->pll_rf_set(fe, 0); if (state->config->demod_chip == LGDT3303) { err = i2c_write_demod_bytes(state, lgdt3303_qam_data, sizeof(lgdt3303_qam_data)); } break; default: printk(KERN_WARNING "lgdt330x: %s: Modulation type(%d) UNSUPPORTED\n", __FUNCTION__, param->u.vsb.modulation); return -1; } /* * select serial or parallel MPEG harware interface * Serial: 0x04 for LGDT3302 or 0x40 for LGDT3303 * Parallel: 0x00 */ top_ctrl_cfg[1] |= state->config->serial_mpeg; /* Select the requested mode */ i2c_write_demod_bytes(state, top_ctrl_cfg, sizeof(top_ctrl_cfg)); if (state->config->set_ts_params) state->config->set_ts_params(fe, 0); state->current_modulation = param->u.vsb.modulation; } /* Tune to the specified frequency */ if (state->config->pll_set) state->config->pll_set(fe, param); /* Keep track of the new frequency */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -