⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lgdt330x.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *    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 + -