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

📄 s5h1420.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 2 页
字号:
/*Driver for Samsung S5H1420 QPSK DemodulatorCopyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net>This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/jiffies.h>#include <asm/div64.h>#include "dvb_frontend.h"#include "s5h1420.h"#define TONE_FREQ 22000struct s5h1420_state {	struct i2c_adapter* i2c;	struct dvb_frontend_ops ops;	const struct s5h1420_config* config;	struct dvb_frontend frontend;	u8 postlocked:1;	u32 fclk;	u32 tunedfreq;	fe_code_rate_t fec_inner;	u32 symbol_rate;};static u32 s5h1420_getsymbolrate(struct s5h1420_state* state);static int s5h1420_get_tune_settings(struct dvb_frontend* fe,				     struct dvb_frontend_tune_settings* fesettings);static int debug = 0;#define dprintk if (debug) printkstatic int s5h1420_writereg (struct s5h1420_state* state, u8 reg, u8 data){	u8 buf [] = { reg, data };	struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };	int err;	if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {		dprintk ("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __FUNCTION__, err, reg, data);		return -EREMOTEIO;	}	return 0;}static u8 s5h1420_readreg (struct s5h1420_state* state, u8 reg){	int ret;	u8 b0 [] = { reg };	u8 b1 [] = { 0 };	struct i2c_msg msg1 = { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 };	struct i2c_msg msg2 = { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 };	if ((ret = i2c_transfer (state->i2c, &msg1, 1)) != 1)		return ret;	if ((ret = i2c_transfer (state->i2c, &msg2, 1)) != 1)		return ret;	return b1[0];}static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage){	struct s5h1420_state* state = fe->demodulator_priv;	switch(voltage) {	case SEC_VOLTAGE_13:		s5h1420_writereg(state, 0x3c,				 (s5h1420_readreg(state, 0x3c) & 0xfe) | 0x02);		break;	case SEC_VOLTAGE_18:		s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) | 0x03);		break;	case SEC_VOLTAGE_OFF:		s5h1420_writereg(state, 0x3c, s5h1420_readreg(state, 0x3c) & 0xfd);		break;	}	return 0;}static int s5h1420_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone){	struct s5h1420_state* state = fe->demodulator_priv;	switch(tone) {	case SEC_TONE_ON:		s5h1420_writereg(state, 0x3b,				 (s5h1420_readreg(state, 0x3b) & 0x74) | 0x08);		break;	case SEC_TONE_OFF:		s5h1420_writereg(state, 0x3b,				 (s5h1420_readreg(state, 0x3b) & 0x74) | 0x01);		break;	}	return 0;}static int s5h1420_send_master_cmd (struct dvb_frontend* fe,				    struct dvb_diseqc_master_cmd* cmd){	struct s5h1420_state* state = fe->demodulator_priv;	u8 val;	int i;	unsigned long timeout;	int result = 0;	if (cmd->msg_len > 8)		return -EINVAL;	/* setup for DISEQC */	val = s5h1420_readreg(state, 0x3b);	s5h1420_writereg(state, 0x3b, 0x02);	msleep(15);	/* write the DISEQC command bytes */	for(i=0; i< cmd->msg_len; i++) {		s5h1420_writereg(state, 0x3d + i, cmd->msg[i]);	}	/* kick off transmission */	s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) |				      ((cmd->msg_len-1) << 4) | 0x08);	/* wait for transmission to complete */	timeout = jiffies + ((100*HZ) / 1000);	while(time_before(jiffies, timeout)) {		if (!(s5h1420_readreg(state, 0x3b) & 0x08))			break;		msleep(5);	}	if (time_after(jiffies, timeout))		result = -ETIMEDOUT;	/* restore original settings */	s5h1420_writereg(state, 0x3b, val);	msleep(15);	return result;}static int s5h1420_recv_slave_reply (struct dvb_frontend* fe,				     struct dvb_diseqc_slave_reply* reply){	struct s5h1420_state* state = fe->demodulator_priv;	u8 val;	int i;	int length;	unsigned long timeout;	int result = 0;	/* setup for DISEQC recieve */	val = s5h1420_readreg(state, 0x3b);	s5h1420_writereg(state, 0x3b, 0x82); /* FIXME: guess - do we need to set DIS_RDY(0x08) in receive mode? */	msleep(15);	/* wait for reception to complete */	timeout = jiffies + ((reply->timeout*HZ) / 1000);	while(time_before(jiffies, timeout)) {		if (!(s5h1420_readreg(state, 0x3b) & 0x80)) /* FIXME: do we test DIS_RDY(0x08) or RCV_EN(0x80)? */			break;		msleep(5);	}	if (time_after(jiffies, timeout)) {		result = -ETIMEDOUT;		goto exit;	}	/* check error flag - FIXME: not sure what this does - docs do not describe	 * beyond "error flag for diseqc receive data :( */	if (s5h1420_readreg(state, 0x49)) {		result = -EIO;		goto exit;	}	/* check length */	length = (s5h1420_readreg(state, 0x3b) & 0x70) >> 4;	if (length > sizeof(reply->msg)) {		result = -EOVERFLOW;		goto exit;	}	reply->msg_len = length;	/* extract data */	for(i=0; i< length; i++) {		reply->msg[i] = s5h1420_readreg(state, 0x3d + i);	}exit:	/* restore original settings */	s5h1420_writereg(state, 0x3b, val);	msleep(15);	return result;}static int s5h1420_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd){	struct s5h1420_state* state = fe->demodulator_priv;	u8 val;	int result = 0;	unsigned long timeout;	/* setup for tone burst */	val = s5h1420_readreg(state, 0x3b);	s5h1420_writereg(state, 0x3b, (s5h1420_readreg(state, 0x3b) & 0x70) | 0x01);	/* set value for B position if requested */	if (minicmd == SEC_MINI_B) {		s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x04);	}	msleep(15);	/* start transmission */	s5h1420_writereg(state, 0x3b, s5h1420_readreg(state, 0x3b) | 0x08);	/* wait for transmission to complete */	timeout = jiffies + ((100*HZ) / 1000);	while(time_before(jiffies, timeout)) {		if (!(s5h1420_readreg(state, 0x3b) & 0x08))			break;		msleep(5);	}	if (time_after(jiffies, timeout))		result = -ETIMEDOUT;	/* restore original settings */	s5h1420_writereg(state, 0x3b, val);	msleep(15);	return result;}static fe_status_t s5h1420_get_status_bits(struct s5h1420_state* state){	u8 val;	fe_status_t status = 0;	val = s5h1420_readreg(state, 0x14);	if (val & 0x02)		status |=  FE_HAS_SIGNAL;	if (val & 0x01)		status |=  FE_HAS_CARRIER;	val = s5h1420_readreg(state, 0x36);	if (val & 0x01)		status |=  FE_HAS_VITERBI;	if (val & 0x20)		status |=  FE_HAS_SYNC;	if (status == (FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI|FE_HAS_SYNC))		status |=  FE_HAS_LOCK;	return status;}static int s5h1420_read_status(struct dvb_frontend* fe, fe_status_t* status){	struct s5h1420_state* state = fe->demodulator_priv;	u8 val;	if (status == NULL)		return -EINVAL;	/* determine lock state */	*status = s5h1420_get_status_bits(state);	/* fix for FEC 5/6 inversion issue - if it doesn't quite lock, invert	the inversion, wait a bit and check again */	if (*status == (FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI)) {		val = s5h1420_readreg(state, 0x32);		if ((val & 0x07) == 0x03) {			if (val & 0x08)				s5h1420_writereg(state, 0x31, 0x13);			else				s5h1420_writereg(state, 0x31, 0x1b);			/* wait a bit then update lock status */			mdelay(200);			*status = s5h1420_get_status_bits(state);		}	}	/* perform post lock setup */	if ((*status & FE_HAS_LOCK) && (!state->postlocked)) {		/* calculate the data rate */		u32 tmp = s5h1420_getsymbolrate(state);		switch(s5h1420_readreg(state, 0x32) & 0x07) {		case 0:			tmp = (tmp * 2 * 1) / 2;			break;		case 1:			tmp = (tmp * 2 * 2) / 3;			break;		case 2:			tmp = (tmp * 2 * 3) / 4;			break;		case 3:			tmp = (tmp * 2 * 5) / 6;			break;		case 4:			tmp = (tmp * 2 * 6) / 7;			break;		case 5:			tmp = (tmp * 2 * 7) / 8;			break;		}		if (tmp == 0) {			printk("s5h1420: avoided division by 0\n");			tmp = 1;		}		tmp = state->fclk / tmp;		/* set the MPEG_CLK_INTL for the calculated data rate */		if (tmp < 4)			val = 0x00;		else if (tmp < 8)			val = 0x01;		else if (tmp < 12)			val = 0x02;		else if (tmp < 16)			val = 0x03;		else if (tmp < 24)			val = 0x04;		else if (tmp < 32)			val = 0x05;		else			val = 0x06;		s5h1420_writereg(state, 0x22, val);		/* DC freeze */		s5h1420_writereg(state, 0x1f, s5h1420_readreg(state, 0x1f) | 0x01);		/* kicker disable + remove DC offset */		s5h1420_writereg(state, 0x05, s5h1420_readreg(state, 0x05) & 0x6f);		/* post-lock processing has been done! */		state->postlocked = 1;	}	return 0;}static int s5h1420_read_ber(struct dvb_frontend* fe, u32* ber){	struct s5h1420_state* state = fe->demodulator_priv;	s5h1420_writereg(state, 0x46, 0x1d);	mdelay(25);	*ber = (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47);	return 0;}static int s5h1420_read_signal_strength(struct dvb_frontend* fe, u16* strength){	struct s5h1420_state* state = fe->demodulator_priv;	u8 val = s5h1420_readreg(state, 0x15);	*strength =  (u16) ((val << 8) | val);	return 0;}static int s5h1420_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks){	struct s5h1420_state* state = fe->demodulator_priv;	s5h1420_writereg(state, 0x46, 0x1f);	mdelay(25);	*ucblocks = (s5h1420_readreg(state, 0x48) << 8) | s5h1420_readreg(state, 0x47);	return 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -