drx397xd.c

来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,600 行 · 第 1/3 页

C
1,600
字号
/* * Driver for Micronas drx397xD demodulator * * Copyright (C) 2007 Henk Vergonet <Henk.Vergonet@gmail.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, see <http://www.gnu.org/licenses/>. */#define DEBUG			/* uncomment if you want debugging output */#include <linux/kernel.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/device.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/firmware.h>#include <asm/div64.h>#include "dvb_frontend.h"#include "drx397xD.h"#include "compat.h"static const char mod_name[] = "drx397xD";#define MAX_CLOCK_DRIFT		200	/* maximal 200 PPM allowed */#define F_SET_0D0h	1#define F_SET_0D4h	2enum fw_ix {#define _FW_ENTRY(a, b)		b#include "drx397xD_fw.h"};/* chip specifics */struct drx397xD_state {	struct i2c_adapter *i2c;	struct dvb_frontend frontend;	struct drx397xD_config config;	enum fw_ix chip_rev;	int flags;	u32 bandwidth_parm;	/* internal bandwidth conversions */	u32 f_osc;		/* w90: actual osc frequency [Hz] */};/* Firmware */static const char *blob_name[] = {#define _BLOB_ENTRY(a, b)		a#include "drx397xD_fw.h"};enum blob_ix {#define _BLOB_ENTRY(a, b)		b#include "drx397xD_fw.h"};static struct {	const char *name;	const struct firmware *file;	rwlock_t lock;	int refcnt;	const u8 *data[ARRAY_SIZE(blob_name)];} fw[] = {#define _FW_ENTRY(a, b)		{			\			.name	= a,			\			.file	= 0,			\			.lock	= RW_LOCK_UNLOCKED,	\			.refcnt = 0,			\			.data	= { }		}#include "drx397xD_fw.h"};/* use only with writer lock aquired */static void _drx_release_fw(struct drx397xD_state *s, enum fw_ix ix){	memset(&fw[ix].data[0], 0, sizeof(fw[0].data));	if (fw[ix].file)		release_firmware(fw[ix].file);}static void drx_release_fw(struct drx397xD_state *s){	enum fw_ix ix = s->chip_rev;	pr_debug("%s\n", __func__);	write_lock(&fw[ix].lock);	if (fw[ix].refcnt) {		fw[ix].refcnt--;		if (fw[ix].refcnt == 0)			_drx_release_fw(s, ix);	}	write_unlock(&fw[ix].lock);}static int drx_load_fw(struct drx397xD_state *s, enum fw_ix ix){	const u8 *data;	size_t size, len;	int i = 0, j, rc = -EINVAL;	pr_debug("%s\n", __func__);	if (ix < 0 || ix >= ARRAY_SIZE(fw))		return -EINVAL;	s->chip_rev = ix;	write_lock(&fw[ix].lock);	if (fw[ix].file) {		rc = 0;		goto exit_ok;	}	memset(&fw[ix].data[0], 0, sizeof(fw[0].data));	if (request_firmware(&fw[ix].file, fw[ix].name, &s->i2c->dev) != 0) {		printk(KERN_ERR "%s: Firmware \"%s\" not available\n",		       mod_name, fw[ix].name);		rc = -ENOENT;		goto exit_err;	}	if (!fw[ix].file->data || fw[ix].file->size < 10)		goto exit_corrupt;	data = fw[ix].file->data;	size = fw[ix].file->size;	if (data[i++] != 2)	/* check firmware version */		goto exit_corrupt;	do {		switch (data[i++]) {		case 0x00:	/* bytecode */			if (i >= size)				break;			i += data[i];		case 0x01:	/* reset */		case 0x02:	/* sleep */			i++;			break;		case 0xfe:	/* name */			len = strnlen(&data[i], size - i);			if (i + len + 1 >= size)				goto exit_corrupt;			if (data[i + len + 1] != 0)				goto exit_corrupt;			for (j = 0; j < ARRAY_SIZE(blob_name); j++) {				if (strcmp(blob_name[j], &data[i]) == 0) {					fw[ix].data[j] = &data[i + len + 1];					pr_debug("Loading %s\n", blob_name[j]);				}			}			i += len + 1;			break;		case 0xff:	/* file terminator */			if (i == size) {				rc = 0;				goto exit_ok;			}		default:			goto exit_corrupt;		}	} while (i < size);exit_corrupt:	printk(KERN_ERR "%s: Firmware is corrupt\n", mod_name);exit_err:	_drx_release_fw(s, ix);	fw[ix].refcnt--;exit_ok:	fw[ix].refcnt++;	write_unlock(&fw[ix].lock);	return rc;}/* i2c bus IO */static int write_fw(struct drx397xD_state *s, enum blob_ix ix){	const u8 *data;	int len, rc = 0, i = 0;	struct i2c_msg msg = {		.addr = s->config.demod_address,		.flags = 0	};	if (ix < 0 || ix >= ARRAY_SIZE(blob_name)) {		pr_debug("%s drx_fw_ix_t out of range\n", __func__);		return -EINVAL;	}	pr_debug("%s %s\n", __func__, blob_name[ix]);	read_lock(&fw[s->chip_rev].lock);	data = fw[s->chip_rev].data[ix];	if (!data) {		rc = -EINVAL;		goto exit_rc;	}	for (;;) {		switch (data[i++]) {		case 0:	/* bytecode */			len = data[i++];			msg.len = len;			msg.buf = (__u8 *) &data[i];			if (i2c_transfer(s->i2c, &msg, 1) != 1) {				rc = -EIO;				goto exit_rc;			}			i += len;			break;		case 1:	/* reset */		case 2:	/* sleep */			i++;			break;		default:			goto exit_rc;		}	}exit_rc:	read_unlock(&fw[s->chip_rev].lock);	return 0;}/* Function is not endian safe, use the RD16 wrapper below */static int _read16(struct drx397xD_state *s, __le32 i2c_adr){	int rc;	u8 a[4];	__le16 v;	struct i2c_msg msg[2] = {		{			.addr = s->config.demod_address,			.flags = 0,			.buf = a,			.len = sizeof(a)		}, {			.addr = s->config.demod_address,			.flags = I2C_M_RD,			.buf = (u8 *)&v,			.len = sizeof(v)		}	};	*(__le32 *) a = i2c_adr;	rc = i2c_transfer(s->i2c, msg, 2);	if (rc != 2)		return -EIO;	return le16_to_cpu(v);}/* Function is not endian safe, use the WR16.. wrappers below */static int _write16(struct drx397xD_state *s, __le32 i2c_adr, __le16 val){	u8 a[6];	int rc;	struct i2c_msg msg = {		.addr = s->config.demod_address,		.flags = 0,		.buf = a,		.len = sizeof(a)	};	*(__le32 *)a = i2c_adr;	*(__le16 *)&a[4] = val;	rc = i2c_transfer(s->i2c, &msg, 1);	if (rc != 1)		return -EIO;	return 0;}#define WR16(ss, adr, val) \		_write16(ss, I2C_ADR_C0(adr), cpu_to_le16(val))#define WR16_E0(ss, adr, val) \		_write16(ss, I2C_ADR_E0(adr), cpu_to_le16(val))#define RD16(ss, adr) \		_read16(ss, I2C_ADR_C0(adr))#define EXIT_RC(cmd)	\	if ((rc = (cmd)) < 0)	\		goto exit_rc/* Tuner callback */static int PLL_Set(struct drx397xD_state *s,		   struct dvb_frontend_parameters *fep, int *df_tuner){	struct dvb_frontend *fe = &s->frontend;	u32 f_tuner, f = fep->frequency;	int rc;	pr_debug("%s\n", __func__);	if ((f > s->frontend.ops.tuner_ops.info.frequency_max) ||	    (f < s->frontend.ops.tuner_ops.info.frequency_min))		return -EINVAL;	*df_tuner = 0;	if (!s->frontend.ops.tuner_ops.set_params ||	    !s->frontend.ops.tuner_ops.get_frequency)		return -ENOSYS;	rc = s->frontend.ops.tuner_ops.set_params(fe, fep);	if (rc < 0)		return rc;	rc = s->frontend.ops.tuner_ops.get_frequency(fe, &f_tuner);	if (rc < 0)		return rc;	*df_tuner = f_tuner - f;	pr_debug("%s requested %d [Hz] tuner %d [Hz]\n", __func__, f,		 f_tuner);	return 0;}/* Demodulator helper functions */static int SC_WaitForReady(struct drx397xD_state *s){	int cnt = 1000;	int rc;	pr_debug("%s\n", __func__);	while (cnt--) {		rc = RD16(s, 0x820043);		if (rc == 0)			return 0;	}	return -1;}static int SC_SendCommand(struct drx397xD_state *s, int cmd){	int rc;	pr_debug("%s\n", __func__);	WR16(s, 0x820043, cmd);	SC_WaitForReady(s);	rc = RD16(s, 0x820042);	if ((rc & 0xffff) == 0xffff)		return -1;	return 0;}static int HI_Command(struct drx397xD_state *s, u16 cmd){	int rc, cnt = 1000;	pr_debug("%s\n", __func__);	rc = WR16(s, 0x420032, cmd);	if (rc < 0)		return rc;	do {		rc = RD16(s, 0x420032);		if (rc == 0) {			rc = RD16(s, 0x420031);			return rc;		}		if (rc < 0)			return rc;	} while (--cnt);	return rc;}static int HI_CfgCommand(struct drx397xD_state *s){	pr_debug("%s\n", __func__);	WR16(s, 0x420033, 0x3973);	WR16(s, 0x420034, s->config.w50);	/* code 4, log 4 */	WR16(s, 0x420035, s->config.w52);	/* code 15,  log 9 */	WR16(s, 0x420036, s->config.demod_address << 1);	WR16(s, 0x420037, s->config.w56);	/* code (set_i2c ??  initX 1 ), log 1 */	/* WR16(s, 0x420033, 0x3973); */	if ((s->config.w56 & 8) == 0)		return HI_Command(s, 3);	return WR16(s, 0x420032, 0x3);}static const u8 fastIncrDecLUT_15273[] = {	0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14,	0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f};static const u8 slowIncrDecLUT_15272[] = {	3, 4, 4, 5, 6};static int SetCfgIfAgc(struct drx397xD_state *s, struct drx397xD_CfgIfAgc *agc){	u16 w06 = agc->w06;	u16 w08 = agc->w08;	u16 w0A = agc->w0A;	u16 w0C = agc->w0C;	int quot, rem, i, rc = -EINVAL;	pr_debug("%s\n", __func__);	if (agc->w04 > 0x3ff)		goto exit_rc;	if (agc->d00 == 1) {		EXIT_RC(RD16(s, 0x0c20010));		rc &= ~0x10;		EXIT_RC(WR16(s, 0x0c20010, rc));		return WR16(s, 0x0c20030, agc->w04 & 0x7ff);	}	if (agc->d00 != 0)		goto exit_rc;	if (w0A < w08)		goto exit_rc;	if (w0A > 0x3ff)		goto exit_rc;	if (w0C > 0x3ff)		goto exit_rc;	if (w06 > 0x3ff)		goto exit_rc;	EXIT_RC(RD16(s, 0x0c20010));	rc |= 0x10;	EXIT_RC(WR16(s, 0x0c20010, rc));	EXIT_RC(WR16(s, 0x0c20025, (w06 >> 1) & 0x1ff));	EXIT_RC(WR16(s, 0x0c20031, (w0A - w08) >> 1));	EXIT_RC(WR16(s, 0x0c20032, ((w0A + w08) >> 1) - 0x1ff));	quot = w0C / 113;	rem = w0C % 113;	if (quot <= 8) {		quot = 8 - quot;	} else {		quot = 0;		rem += 113;	}	EXIT_RC(WR16(s, 0x0c20024, quot));	i = fastIncrDecLUT_15273[rem / 8];	EXIT_RC(WR16(s, 0x0c2002d, i));	EXIT_RC(WR16(s, 0x0c2002e, i));	i = slowIncrDecLUT_15272[rem / 28];	EXIT_RC(WR16(s, 0x0c2002b, i));	rc = WR16(s, 0x0c2002c, i);exit_rc:	return rc;}static int SetCfgRfAgc(struct drx397xD_state *s, struct drx397xD_CfgRfAgc *agc){	u16 w04 = agc->w04;	u16 w06 = agc->w06;	int rc = -1;	pr_debug("%s %d 0x%x 0x%x\n", __func__, agc->d00, w04, w06);	if (w04 > 0x3ff)		goto exit_rc;	switch (agc->d00) {	case 1:		if (w04 == 0x3ff)			w04 = 0x400;		EXIT_RC(WR16(s, 0x0c20036, w04));		s->config.w9C &= ~2;		EXIT_RC(WR16(s, 0x0c20015, s->config.w9C));		EXIT_RC(RD16(s, 0x0c20010));		rc &= 0xbfdf;		EXIT_RC(WR16(s, 0x0c20010, rc));		EXIT_RC(RD16(s, 0x0c20013));		rc &= ~2;		break;	case 0:		/* loc_8000659 */		s->config.w9C &= ~2;		EXIT_RC(WR16(s, 0x0c20015, s->config.w9C));		EXIT_RC(RD16(s, 0x0c20010));		rc &= 0xbfdf;		rc |= 0x4000;		EXIT_RC(WR16(s, 0x0c20010, rc));		EXIT_RC(WR16(s, 0x0c20051, (w06 >> 4) & 0x3f));		EXIT_RC(RD16(s, 0x0c20013));		rc &= ~2;		break;	default:		s->config.w9C |= 2;		EXIT_RC(WR16(s, 0x0c20015, s->config.w9C));		EXIT_RC(RD16(s, 0x0c20010));		rc &= 0xbfdf;		EXIT_RC(WR16(s, 0x0c20010, rc));		EXIT_RC(WR16(s, 0x0c20036, 0));		EXIT_RC(RD16(s, 0x0c20013));		rc |= 2;	}	rc = WR16(s, 0x0c20013, rc);exit_rc:	return rc;}static int GetLockStatus(struct drx397xD_state *s, int *lockstat){	int rc;

⌨️ 快捷键说明

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