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 + -
显示快捷键?