📄 af9013.c
字号:
/* * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver * * Copyright (C) 2007 Antti Palosaari <crope@iki.fi> * * Thanks to Afatech who kindly provided information. * * 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/moduleparam.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/firmware.h>#include "dvb_frontend.h"#include "af9013_priv.h"#include "af9013.h"int af9013_debug;struct af9013_state { struct i2c_adapter *i2c; struct dvb_frontend frontend; struct af9013_config config; u16 signal_strength; u32 ber; u32 ucblocks; u16 snr; u32 frequency; unsigned long next_statistics_check;};static u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };static int af9013_write_regs(struct af9013_state *state, u8 mbox, u16 reg, u8 *val, u8 len){ u8 buf[3+len]; struct i2c_msg msg = { .addr = state->config.demod_address, .flags = 0, .len = sizeof(buf), .buf = buf }; buf[0] = reg >> 8; buf[1] = reg & 0xff; buf[2] = mbox; memcpy(&buf[3], val, len); if (i2c_transfer(state->i2c, &msg, 1) != 1) { warn("I2C write failed reg:%04x len:%d", reg, len); return -EREMOTEIO; } return 0;}static int af9013_write_ofdm_regs(struct af9013_state *state, u16 reg, u8 *val, u8 len){ u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(0 << 6)|(0 << 7); return af9013_write_regs(state, mbox, reg, val, len);}static int af9013_write_ofsm_regs(struct af9013_state *state, u16 reg, u8 *val, u8 len){ u8 mbox = (1 << 0)|(1 << 1)|((len - 1) << 2)|(1 << 6)|(1 << 7); return af9013_write_regs(state, mbox, reg, val, len);}/* write single register */static int af9013_write_reg(struct af9013_state *state, u16 reg, u8 val){ return af9013_write_ofdm_regs(state, reg, &val, 1);}/* read single register */static int af9013_read_reg(struct af9013_state *state, u16 reg, u8 *val){ u8 obuf[3] = { reg >> 8, reg & 0xff, 0 }; u8 ibuf[1]; struct i2c_msg msg[2] = { { .addr = state->config.demod_address, .flags = 0, .len = sizeof(obuf), .buf = obuf }, { .addr = state->config.demod_address, .flags = I2C_M_RD, .len = sizeof(ibuf), .buf = ibuf } }; if (i2c_transfer(state->i2c, msg, 2) != 2) { warn("I2C read failed reg:%04x", reg); return -EREMOTEIO; } *val = ibuf[0]; return 0;}static int af9013_write_reg_bits(struct af9013_state *state, u16 reg, u8 pos, u8 len, u8 val){ int ret; u8 tmp, mask; ret = af9013_read_reg(state, reg, &tmp); if (ret) return ret; mask = regmask[len - 1] << pos; tmp = (tmp & ~mask) | ((val << pos) & mask); return af9013_write_reg(state, reg, tmp);}static int af9013_read_reg_bits(struct af9013_state *state, u16 reg, u8 pos, u8 len, u8 *val){ int ret; u8 tmp; ret = af9013_read_reg(state, reg, &tmp); if (ret) return ret; *val = (tmp >> pos) & regmask[len - 1]; return 0;}static int af9013_set_gpio(struct af9013_state *state, u8 gpio, u8 gpioval){ int ret; u8 pos; u16 addr; deb_info("%s: gpio:%d gpioval:%02x\n", __func__, gpio, gpioval);/* GPIO0 & GPIO1 0xd735 GPIO2 & GPIO3 0xd736 */ switch (gpio) { case 0: case 1: addr = 0xd735; break; case 2: case 3: addr = 0xd736; break; default: err("invalid gpio:%d\n", gpio); ret = -EINVAL; goto error; }; switch (gpio) { case 0: case 2: pos = 0; break; case 1: case 3: default: pos = 4; break; }; ret = af9013_write_reg_bits(state, addr, pos, 4, gpioval);error: return ret;}static u32 af913_div(u32 a, u32 b, u32 x){ u32 r = 0, c = 0, i; deb_info("%s: a:%d b:%d x:%d\n", __func__, a, b, x); if (a > b) { c = a / b; a = a - c * b; } for (i = 0; i < x; i++) { if (a >= b) { r += 1; a -= b; } a <<= 1; r <<= 1; } r = (c << (u32)x) + r; deb_info("%s: a:%d b:%d x:%d r:%d r:%x\n", __func__, a, b, x, r, r); return r;}static int af9013_set_coeff(struct af9013_state *state, fe_bandwidth_t bw){ int ret = 0; u8 i = 0; u8 buf[24]; u32 ns_coeff1_2048nu; u32 ns_coeff1_8191nu; u32 ns_coeff1_8192nu; u32 ns_coeff1_8193nu; u32 ns_coeff2_2k; u32 ns_coeff2_8k; deb_info("%s: adc_clock:%d bw:%d\n", __func__, state->config.adc_clock, bw); switch (state->config.adc_clock) { case 28800: /* 28.800 MHz */ switch (bw) { case BANDWIDTH_6_MHZ: ns_coeff1_2048nu = 0x01e79e7a; ns_coeff1_8191nu = 0x0079eb6e; ns_coeff1_8192nu = 0x0079e79e; ns_coeff1_8193nu = 0x0079e3cf; ns_coeff2_2k = 0x00f3cf3d; ns_coeff2_8k = 0x003cf3cf; break; case BANDWIDTH_7_MHZ: ns_coeff1_2048nu = 0x0238e38e; ns_coeff1_8191nu = 0x008e3d55; ns_coeff1_8192nu = 0x008e38e4; ns_coeff1_8193nu = 0x008e3472; ns_coeff2_2k = 0x011c71c7; ns_coeff2_8k = 0x00471c72; break; case BANDWIDTH_8_MHZ: ns_coeff1_2048nu = 0x028a28a3; ns_coeff1_8191nu = 0x00a28f3d; ns_coeff1_8192nu = 0x00a28a29; ns_coeff1_8193nu = 0x00a28514; ns_coeff2_2k = 0x01451451; ns_coeff2_8k = 0x00514514; break; default: ret = -EINVAL; } break; case 20480: /* 20.480 MHz */ switch (bw) { case BANDWIDTH_6_MHZ: ns_coeff1_2048nu = 0x02adb6dc; ns_coeff1_8191nu = 0x00ab7313; ns_coeff1_8192nu = 0x00ab6db7; ns_coeff1_8193nu = 0x00ab685c; ns_coeff2_2k = 0x0156db6e; ns_coeff2_8k = 0x0055b6dc; break; case BANDWIDTH_7_MHZ: ns_coeff1_2048nu = 0x03200001; ns_coeff1_8191nu = 0x00c80640; ns_coeff1_8192nu = 0x00c80000; ns_coeff1_8193nu = 0x00c7f9c0; ns_coeff2_2k = 0x01900000; ns_coeff2_8k = 0x00640000; break; case BANDWIDTH_8_MHZ: ns_coeff1_2048nu = 0x03924926; ns_coeff1_8191nu = 0x00e4996e; ns_coeff1_8192nu = 0x00e49249; ns_coeff1_8193nu = 0x00e48b25; ns_coeff2_2k = 0x01c92493; ns_coeff2_8k = 0x00724925; break; default: ret = -EINVAL; } break; case 28000: /* 28.000 MHz */ switch (bw) { case BANDWIDTH_6_MHZ: ns_coeff1_2048nu = 0x01f58d10; ns_coeff1_8191nu = 0x007d672f; ns_coeff1_8192nu = 0x007d6344; ns_coeff1_8193nu = 0x007d5f59; ns_coeff2_2k = 0x00fac688; ns_coeff2_8k = 0x003eb1a2; break; case BANDWIDTH_7_MHZ: ns_coeff1_2048nu = 0x02492492; ns_coeff1_8191nu = 0x00924db7; ns_coeff1_8192nu = 0x00924925; ns_coeff1_8193nu = 0x00924492; ns_coeff2_2k = 0x01249249; ns_coeff2_8k = 0x00492492; break; case BANDWIDTH_8_MHZ: ns_coeff1_2048nu = 0x029cbc15; ns_coeff1_8191nu = 0x00a7343f; ns_coeff1_8192nu = 0x00a72f05; ns_coeff1_8193nu = 0x00a729cc; ns_coeff2_2k = 0x014e5e0a; ns_coeff2_8k = 0x00539783; break; default: ret = -EINVAL; } break; case 25000: /* 25.000 MHz */ switch (bw) { case BANDWIDTH_6_MHZ: ns_coeff1_2048nu = 0x0231bcb5; ns_coeff1_8191nu = 0x008c7391; ns_coeff1_8192nu = 0x008c6f2d; ns_coeff1_8193nu = 0x008c6aca; ns_coeff2_2k = 0x0118de5b; ns_coeff2_8k = 0x00463797; break; case BANDWIDTH_7_MHZ: ns_coeff1_2048nu = 0x028f5c29; ns_coeff1_8191nu = 0x00a3dc29; ns_coeff1_8192nu = 0x00a3d70a; ns_coeff1_8193nu = 0x00a3d1ec; ns_coeff2_2k = 0x0147ae14; ns_coeff2_8k = 0x0051eb85; break; case BANDWIDTH_8_MHZ: ns_coeff1_2048nu = 0x02ecfb9d; ns_coeff1_8191nu = 0x00bb44c1; ns_coeff1_8192nu = 0x00bb3ee7; ns_coeff1_8193nu = 0x00bb390d; ns_coeff2_2k = 0x01767dce; ns_coeff2_8k = 0x005d9f74; break; default: ret = -EINVAL; } break; default: err("invalid xtal"); return -EINVAL; } if (ret) { err("invalid bandwidth"); return ret; } buf[i++] = (u8) ((ns_coeff1_2048nu & 0x03000000) >> 24); buf[i++] = (u8) ((ns_coeff1_2048nu & 0x00ff0000) >> 16); buf[i++] = (u8) ((ns_coeff1_2048nu & 0x0000ff00) >> 8); buf[i++] = (u8) ((ns_coeff1_2048nu & 0x000000ff)); buf[i++] = (u8) ((ns_coeff2_2k & 0x01c00000) >> 22); buf[i++] = (u8) ((ns_coeff2_2k & 0x003fc000) >> 14); buf[i++] = (u8) ((ns_coeff2_2k & 0x00003fc0) >> 6); buf[i++] = (u8) ((ns_coeff2_2k & 0x0000003f)); buf[i++] = (u8) ((ns_coeff1_8191nu & 0x03000000) >> 24); buf[i++] = (u8) ((ns_coeff1_8191nu & 0x00ffc000) >> 16); buf[i++] = (u8) ((ns_coeff1_8191nu & 0x0000ff00) >> 8); buf[i++] = (u8) ((ns_coeff1_8191nu & 0x000000ff)); buf[i++] = (u8) ((ns_coeff1_8192nu & 0x03000000) >> 24); buf[i++] = (u8) ((ns_coeff1_8192nu & 0x00ffc000) >> 16); buf[i++] = (u8) ((ns_coeff1_8192nu & 0x0000ff00) >> 8); buf[i++] = (u8) ((ns_coeff1_8192nu & 0x000000ff)); buf[i++] = (u8) ((ns_coeff1_8193nu & 0x03000000) >> 24); buf[i++] = (u8) ((ns_coeff1_8193nu & 0x00ffc000) >> 16); buf[i++] = (u8) ((ns_coeff1_8193nu & 0x0000ff00) >> 8); buf[i++] = (u8) ((ns_coeff1_8193nu & 0x000000ff)); buf[i++] = (u8) ((ns_coeff2_8k & 0x01c00000) >> 22); buf[i++] = (u8) ((ns_coeff2_8k & 0x003fc000) >> 14); buf[i++] = (u8) ((ns_coeff2_8k & 0x00003fc0) >> 6); buf[i++] = (u8) ((ns_coeff2_8k & 0x0000003f)); deb_info("%s: coeff:", __func__); debug_dump(buf, sizeof(buf), deb_info); /* program */ for (i = 0; i < sizeof(buf); i++) { ret = af9013_write_reg(state, 0xae00 + i, buf[i]); if (ret) break; } return ret;}static int af9013_set_adc_ctrl(struct af9013_state *state){ int ret; u8 buf[3], tmp, i; u32 adc_cw; deb_info("%s: adc_clock:%d\n", __func__, state->config.adc_clock); /* adc frequency type */ switch (state->config.adc_clock) { case 28800: /* 28.800 MHz */ tmp = 0; break; case 20480: /* 20.480 MHz */ tmp = 1; break; case 28000: /* 28.000 MHz */ tmp = 2; break; case 25000: /* 25.000 MHz */ tmp = 3; break; default: err("invalid xtal"); return -EINVAL; } adc_cw = af913_div(state->config.adc_clock*1000, 1000000ul, 19ul); buf[0] = (u8) ((adc_cw & 0x000000ff)); buf[1] = (u8) ((adc_cw & 0x0000ff00) >> 8); buf[2] = (u8) ((adc_cw & 0x00ff0000) >> 16); deb_info("%s: adc_cw:", __func__); debug_dump(buf, sizeof(buf), deb_info); /* program */ for (i = 0; i < sizeof(buf); i++) { ret = af9013_write_reg(state, 0xd180 + i, buf[i]); if (ret) goto error; } ret = af9013_write_reg_bits(state, 0x9bd2, 0, 4, tmp);error: return ret;}static int af9013_set_freq_ctrl(struct af9013_state *state, fe_bandwidth_t bw){ int ret; u16 addr; u8 buf[3], i, j; u32 adc_freq, freq_cw; s8 bfs_spec_inv; int if_sample_freq; for (j = 0; j < 3; j++) { if (j == 0) { addr = 0xd140; /* fcw normal */ bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1; } else if (j == 1) { addr = 0x9be7; /* fcw dummy ram */ bfs_spec_inv = state->config.rf_spec_inv ? -1 : 1; } else { addr = 0x9bea; /* fcw inverted */ bfs_spec_inv = state->config.rf_spec_inv ? 1 : -1; } adc_freq = state->config.adc_clock * 1000; if_sample_freq = state->config.tuner_if * 1000; /* TDA18271 uses different sampling freq for every bw */ if (state->config.tuner == AF9013_TUNER_TDA18271) { switch (bw) { case BANDWIDTH_6_MHZ: if_sample_freq = 3300000; /* 3.3 MHz */ break; case BANDWIDTH_7_MHZ: if_sample_freq = 3800000; /* 3.8 MHz */ break; case BANDWIDTH_8_MHZ: default: if_sample_freq = 4300000; /* 4.3 MHz */ break; } } while (if_sample_freq > (adc_freq / 2)) if_sample_freq = if_sample_freq - adc_freq; if (if_sample_freq >= 0) bfs_spec_inv = bfs_spec_inv * (-1); else if_sample_freq = if_sample_freq * (-1); freq_cw = af913_div(if_sample_freq, adc_freq, 23ul); if (bfs_spec_inv == -1) freq_cw = 0x00800000 - freq_cw; buf[0] = (u8) ((freq_cw & 0x000000ff)); buf[1] = (u8) ((freq_cw & 0x0000ff00) >> 8); buf[2] = (u8) ((freq_cw & 0x007f0000) >> 16); deb_info("%s: freq_cw:", __func__); debug_dump(buf, sizeof(buf), deb_info); /* program */ for (i = 0; i < sizeof(buf); i++) { ret = af9013_write_reg(state, addr++, buf[i]); if (ret) goto error; } }error: return ret;}static int af9013_set_ofdm_params(struct af9013_state *state, struct dvb_ofdm_parameters *params, u8 *auto_mode){ int ret; u8 i, buf[3] = {0, 0, 0}; *auto_mode = 0; /* set if parameters are requested to auto set */ switch (params->transmission_mode) { case TRANSMISSION_MODE_AUTO: *auto_mode = 1; case TRANSMISSION_MODE_2K: break; case TRANSMISSION_MODE_8K: buf[0] |= (1 << 0); break; default: return -EINVAL; } switch (params->guard_interval) { case GUARD_INTERVAL_AUTO: *auto_mode = 1; case GUARD_INTERVAL_1_32: break; case GUARD_INTERVAL_1_16: buf[0] |= (1 << 2); break; case GUARD_INTERVAL_1_8: buf[0] |= (2 << 2); break; case GUARD_INTERVAL_1_4: buf[0] |= (3 << 2); break; default: return -EINVAL; } switch (params->hierarchy_information) { case HIERARCHY_AUTO: *auto_mode = 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -