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

📄 s5h1420.c

📁 trident tm5600的linux驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Driver for *    Samsung S5H1420 and *    PnpNetwork PN1010 QPSK Demodulator * * Copyright (C) 2005 Andrew de Quincey <adq_dvb@lidskialf.net> * Copyright (C) 2005-8 Patrick Boettcher <pb@linuxtv.org> * * 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 <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 <linux/i2c.h>#include "dvb_frontend.h"#include "s5h1420.h"#include "s5h1420_priv.h"#define TONE_FREQ 22000struct s5h1420_state {	struct i2c_adapter* i2c;	const struct s5h1420_config* config;	struct dvb_frontend frontend;	struct i2c_adapter tuner_i2c_adapter;	u8 CON_1_val;	u8 postlocked:1;	u32 fclk;	u32 tunedfreq;	fe_code_rate_t fec_inner;	u32 symbol_rate;	/* FIXME: ugly workaround for flexcop's incapable i2c-controller	 * it does not support repeated-start, workaround: write addr-1	 * and then read	 */	u8 shadow[256];};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;module_param(debug, int, 0644);MODULE_PARM_DESC(debug, "enable debugging");#define dprintk(x...) do { \	if (debug) \		printk(KERN_DEBUG "S5H1420: " x); \} while (0)static u8 s5h1420_readreg(struct s5h1420_state *state, u8 reg){	int ret;	u8 b[2];	struct i2c_msg msg[] = {		{ .addr = state->config->demod_address, .flags = 0, .buf = b, .len = 2 },		{ .addr = state->config->demod_address, .flags = 0, .buf = &reg, .len = 1 },		{ .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b, .len = 1 },	};	b[0] = (reg - 1) & 0xff;	b[1] = state->shadow[(reg - 1) & 0xff];	if (state->config->repeated_start_workaround) {		ret = i2c_transfer(state->i2c, msg, 3);		if (ret != 3)			return ret;	} else {		ret = i2c_transfer(state->i2c, &msg[1], 1);		if (ret != 1)			return ret;		ret = i2c_transfer(state->i2c, &msg[2], 1);		if (ret != 1)			return ret;	}	/* dprintk("rd(%02x): %02x %02x\n", state->config->demod_address, reg, b[0]); */	return b[0];}static 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;	/* dprintk("wr(%02x): %02x %02x\n", state->config->demod_address, reg, data); */	err = i2c_transfer(state->i2c, &msg, 1);	if (err != 1) {		dprintk("%s: writereg error (err == %i, reg == 0x%02x, data == 0x%02x)\n", __func__, err, reg, data);		return -EREMOTEIO;	}	state->shadow[reg] = data;	return 0;}static int s5h1420_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t voltage){	struct s5h1420_state* state = fe->demodulator_priv;	dprintk("enter %s\n", __func__);	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;	}	dprintk("leave %s\n", __func__);	return 0;}static int s5h1420_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone){	struct s5h1420_state* state = fe->demodulator_priv;	dprintk("enter %s\n", __func__);	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;	}	dprintk("leave %s\n", __func__);	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;	dprintk("enter %s\n", __func__);	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);	dprintk("leave %s\n", __func__);	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;	dprintk("enter %s\n", __func__);	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, Vit10);		if ((val & 0x07) == 0x03) {			if (val & 0x08)				s5h1420_writereg(state, Vit09, 0x13);			else				s5h1420_writereg(state, Vit09, 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, Vit10) & 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(KERN_ERR "s5h1420: avoided division by 0\n");			tmp = 1;		}		tmp = state->fclk / tmp;		/* set the MPEG_CLK_INTL for the calculated data rate */		if (tmp < 2)			val = 0x00;		else if (tmp < 5)			val = 0x01;		else if (tmp < 9)			val = 0x02;		else if (tmp < 13)			val = 0x03;		else if (tmp < 17)			val = 0x04;		else if (tmp < 25)			val = 0x05;		else if (tmp < 33)			val = 0x06;		else			val = 0x07;		dprintk("for MPEG_CLK_INTL %d %x\n", tmp, val);		s5h1420_writereg(state, FEC01, 0x18);		s5h1420_writereg(state, FEC01, 0x10);		s5h1420_writereg(state, FEC01, val);		/* Enable "MPEG_Out" */		val = s5h1420_readreg(state, Mpeg02);		s5h1420_writereg(state, Mpeg02, val | (1 << 6));		/* kicker disable */		val = s5h1420_readreg(state, QPSK01) & 0x7f;		s5h1420_writereg(state, QPSK01, val);		/* DC freeze TODO it was never activated by default or it can stay activated */#if 0		val = s5h1420_readreg(state, 0x1f);		s5h1420_writereg(state, 0x1f, val | 0x01);#endif		if (s5h1420_getsymbolrate(state) >= 20000000) {			s5h1420_writereg(state, Loop04, 0x8a);			s5h1420_writereg(state, Loop05, 0x6a);		} else {			s5h1420_writereg(state, Loop04, 0x58);			s5h1420_writereg(state, Loop05, 0x27);		}		/* post-lock processing has been done! */		state->postlocked = 1;	}	dprintk("leave %s\n", __func__);	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;}static void s5h1420_reset(struct s5h1420_state* state){	dprintk("%s\n", __func__);	s5h1420_writereg (state, 0x01, 0x08);	s5h1420_writereg (state, 0x01, 0x00);	udelay(10);}static void s5h1420_setsymbolrate(struct s5h1420_state* state,				  struct dvb_frontend_parameters *p){	u8 v;	u64 val;	dprintk("enter %s\n", __func__);	val = ((u64) p->u.qpsk.symbol_rate / 1000ULL) * (1ULL<<24);	if (p->u.qpsk.symbol_rate < 29000000)		val *= 2;	do_div(val, (state->fclk / 1000));	dprintk("symbol rate register: %06llx\n", (unsigned long long)val);	v = s5h1420_readreg(state, Loop01);	s5h1420_writereg(state, Loop01, v & 0x7f);	s5h1420_writereg(state, Tnco01, val >> 16);	s5h1420_writereg(state, Tnco02, val >> 8);	s5h1420_writereg(state, Tnco03, val & 0xff);	s5h1420_writereg(state, Loop01,  v | 0x80);	dprintk("leave %s\n", __func__);}static u32 s5h1420_getsymbolrate(struct s5h1420_state* state){#if 0	/* TODO getsymbolrate gives strange values - something is wrong with	 * configuring the read	 */

⌨️ 快捷键说明

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