📄 adimtv102.c~
字号:
/* * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" * * Copyright (c) 2006 Olivier DANET <odanet@caramail.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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.= *//* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/delay.h>#include <linux/dvb/frontend.h>#include <linux/i2c.h>#include "dvb_frontend.h"#include "adimtv102.h"#include "adimtv102_priv.h"static int debug;module_param(debug, int, 0644);MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0)unsigned int LO2PLL_Freq(unsigned int freq, unsigned int *setting) { if(freq <= 1879999) { if(freq > 939999) { *setting = 4; return freq; } else if(freq > 469999) { *setting = 3; return freq * 2; } else if(freq > 234999) { *setting = 2; return freq * 4; } else if(freq > 117499) { *setting = 1; return freq * 8; } else if(freq > 58749) { *setting = 0; return freq * 16; } // no else case, just falls through } switch(*setting){ case 0: return freq * 16; case 1: return freq * 8; case 2: return freq * 4; case 3: return freq * 2; case 4: return freq; default: return freq * 2; }/* unsigned char tmp; tmp = freq > 939999 ? 1 : 0; if(freq <= 1879999) { if(!tmp) { // freq <= 939999 if(freq > 469999) { *setting = 3; return freq * 2; } else { // located @ 41 if(freq <= 234999) { if(freq > 117499) { *setting = 1; return freq * 8; } else { // located @ 89 if(freq > 58749) { *setting = 0; return freq * 16; } else { // located @ 34 switch(*setting) { case 0: return freq * 16; case 1: return freq * 8; case 2: return freq * 4; case 3: return freq * 2; case 4: return freq; default: return freq * 2; } } } } else { // located @ 70 *setting = 2; return freq * 4; } } } else { // located @ 60 *setting = 4; return freq; } } else { // located @ 28 if(tmp) { // freq > 939999, but this is always true wor... if(freq > 469999) { // this is also always true wor. switch(*setting){ case 0: return freq * 16; case 1: return freq * 8; case 2: return freq * 4; case 3: return freq * 2; case 4: return freq; default: return freq * 2; } } else { // located @ 41// and therefore this whole else block shouldn't be here afterall... if(freq <= 234999) { if(freq > 117499) { *setting = 1; return freq * 8; } else { // located @ 89 if(freq > 58749) { *setting = 0; return freq * 16; } else { // located @ 34 switch(*setting) { case 0: return freq * 16; case 1: return freq * 8; case 2: return freq * 4; case 3: return freq * 2; case 4: return freq; default: return freq * 2; } } } } else { // located @ 70 *setting = 2; return freq * 4; } } } else { // located @ 16 // and else block is also useless.. if(freq > 479999) { *setting = 3; return freq * 2; } else { // located @ 41 if(freq <= 234999) { if(freq > 117499) { *setting = 1; return freq * 8; } else { // located @ 89 if(freq > 58749) { *setting = 0; return freq * 16; } else { // located @ 34 switch(*setting) { case 0: return freq * 16; case 1: return freq * 8; case 2: return freq * 4; case 3: return freq * 2; case 4: return freq; default: return freq * 2; } } } } else { // located @ 70 *setting = 2; return freq * 4; } } } }*/}EXPORT_SYMBOL(LO2PLL_Freq);static int adimtv102_sleep(struct dvb_frontend *fe){ return 0;}static int adimtv102_init(struct dvb_frontend *fe){ printk("adimtv102_init\n"); return 0;}static int adimtv102_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth){ struct adimtv102_priv *priv = fe->tuner_priv; *bandwidth = priv->bandwidth; printk("adimtv102_get_bandwidth\n"); return 0;}static int adimtv102_get_frequency(struct dvb_frontend *fe, u32 *frequency){ struct adimtv102_priv *priv = fe->tuner_priv; *frequency = priv->frequency; printk("adimtv102_get_frequency\n"); return 0;}static int adimtv102_release(struct dvb_frontend *fe){ kfree(fe->tuner_priv); fe->tuner_priv = NULL; return 0;}// Writes a single registerstatic int adimtv102_writereg(struct adimtv102_priv *priv, u8 reg, u8 val){ u8 buf[2] = { reg, val }; struct i2c_msg msg = { .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 }; if (i2c_transfer(priv->i2c, &msg, 1) != 1) { printk(KERN_WARNING "adimtv102 I2C write failed\n"); return -EREMOTEIO; } return 0;}// Reads a single registerstatic int adimtv102_readreg(struct adimtv102_priv *priv, u8 reg, u8 *val){ u8 wbuf[2] = {reg, reg}; u8 rbuf[2]; struct i2c_msg msg[2] = { { .addr = priv->cfg->i2c_address, .flags = 0, .buf = wbuf, .len = 2 }, { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = rbuf, .len = 2 }, }; if (i2c_transfer(priv->i2c, msg, 2) != 2) { printk(KERN_WARNING "adimtv102 I2C read failed\n"); return -EREMOTEIO; } *val = rbuf[0]; return 0;}static int adimtv102_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params);static const struct dvb_tuner_ops adimtv102_tuner_ops = { .info = { .name = "ADI MTV102", .frequency_min = 48000000, .frequency_max = 860000000, .frequency_step = 50000, }, .release = adimtv102_release, .init = adimtv102_init, .sleep = adimtv102_sleep, .set_params = adimtv102_set_params, .get_frequency = adimtv102_get_frequency, .get_bandwidth = adimtv102_get_bandwidth};/* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */struct dvb_frontend * adimtv102_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct adimtv102_config *cfg, u16 if1){ struct adimtv102_priv *priv = NULL; u8 reg0; u8 reg1; u8 reg2; priv = kzalloc(sizeof(struct adimtv102_priv), GFP_KERNEL); if (priv == NULL) return NULL; priv->cfg = cfg; priv->i2c = i2c; priv->if1_freq = if1; printk("adimtv102_readreg 0x00\n"); adimtv102_readreg(priv, 0, ®0); printk("adimtv102_readreg 0x01\n"); adimtv102_readreg(priv, 1, ®1); printk("adimtv102_readreg 0x02\n"); adimtv102_readreg(priv, 2, ®2); printk("adimtv102: successfully identified (%x %x %x)\n", reg1, reg2, reg0); memcpy(&fe->ops.tuner_ops, &adimtv102_tuner_ops, sizeof(struct dvb_tuner_ops)); fe->tuner_priv = priv; return fe;}EXPORT_SYMBOL(adimtv102_attach);struct adimtv102_addr_val_pair{ u8 reg; u8 val;};struct adimtv102_reg_struct{ u16 frequency; struct adimtv102_addr_val_pair settings[43]; // seriously I don't know the exact size, just wild guess.};int adimtv102_compute_regs(struct adimtv102_priv *priv, struct adimtv102_reg_struct *data_buf) { unsigned char tmp; unsigned int i; unsigned int pllfreq; unsigned int pllfreq_shr14; unsigned int pllfreq_residue; unsigned int LO2PLL_Freq_result; // @ 0x14 unsigned char read_buf; // @ 0x1b unsigned char read_buf2; // @ 0x1a LO2PLL_Freq_result = 0;// {address, data} data arrangment data_buf->settings[0].reg = 0x10; data_buf->settings[0].val = 0x4c; data_buf->settings[1].reg = 0x11; data_buf->settings[1].val = 0xd8; data_buf->settings[2].reg = 0x12; data_buf->settings[2].val = 0xc0; data_buf->settings[3].reg = 0x17; data_buf->settings[3].val = 0x9a; data_buf->settings[4].reg = 0x1a; data_buf->settings[4].val = 0x7c; data_buf->settings[5].reg = 0x1b; data_buf->settings[5].val = 0x51; data_buf->settings[6].reg = 0x1c; data_buf->settings[6].val = 0xb4; data_buf->settings[7].reg = 0x1d; data_buf->settings[7].val = 0xa4; data_buf->settings[8].reg = 0x1e; data_buf->settings[8].val = 0xfb; data_buf->settings[9].reg = 0x1f; data_buf->settings[9].val = 0x17; data_buf->settings[10].reg = 0x20; data_buf->settings[10].val = 0xff; data_buf->settings[11].reg = 0x21; data_buf->settings[11].val = 0xa4; data_buf->settings[12].reg = 0x22; data_buf->settings[12].val = 0xa4; data_buf->settings[13].reg = 0x23; data_buf->settings[13].val = 0xdf; data_buf->settings[14].reg = 0x25; data_buf->settings[14].val = 0x07; data_buf->settings[15].reg = 0x26; data_buf->settings[15].val = 0xfa; data_buf->settings[16].reg = 0x27; data_buf->settings[16].val = 0x00; data_buf->settings[17].reg = 0x28; data_buf->settings[17].val = 0xff; data_buf->settings[18].reg = 0x29; data_buf->settings[18].val = 0xff; data_buf->settings[19].reg = 0x2a; data_buf->settings[19].val = 0xff; data_buf->settings[20].reg = 0x2b; data_buf->settings[20].val = 0xe7; data_buf->settings[21].reg = 0x2c; data_buf->settings[21].val = 0xff; data_buf->settings[22].reg = 0x2d; data_buf->settings[22].val = 0xff; data_buf->settings[23].reg = 0x2e; data_buf->settings[23].val = 0xfb; data_buf->settings[24].reg = 0x30; data_buf->settings[24].val = 0xf8; data_buf->settings[25].reg = 0x31; data_buf->settings[25].val = 0x04; data_buf->settings[26].reg = 0x32; data_buf->settings[26].val = 0x94; data_buf->settings[27].reg = 0x33; data_buf->settings[27].val = 0x80; data_buf->settings[28].reg = 0x34; data_buf->settings[28].val = 0xec; data_buf->settings[29].reg = 0x38; data_buf->settings[29].val = 0x50; data_buf->settings[30].reg = 0x39; data_buf->settings[30].val = 0x96; data_buf->settings[31].reg = 0x3a; data_buf->settings[31].val = 0xa0; data_buf->settings[32].reg = 0x3b; data_buf->settings[32].val = 0x05; data_buf->settings[33].reg = 0x3c; data_buf->settings[33].val = 0xd0; data_buf->settings[34].reg = 0x48; data_buf->settings[34].val = 0x21; data_buf->settings[35].reg = 0x49; data_buf->settings[35].val = 0x08; data_buf->settings[36].reg = 0x4a; data_buf->settings[36].val = 0xa0; data_buf->settings[37].reg = 0x4b; data_buf->settings[37].val = 0x9d; data_buf->settings[38].reg = 0x4c; data_buf->settings[38].val = 0x9d; data_buf->settings[39].reg = 0x4d; data_buf->settings[39].val = 0xc3; data_buf->settings[40].reg = 0x24; data_buf->settings[40].val = 0x1a; for(i = 0; i < 41; i++) { if(!adimtv102_writereg(priv, data_buf->settings[i].reg, data_buf->settings[i].val)) { return 0; } } if(adimtv102_writereg(priv, 0x25, 0x0b)) return 0; msleep(1); if(adimtv102_readreg(priv, 0x0f, &read_buf)) return 0; if(adimtv102_writereg(priv, 0x25, 0x0f)) return 0; if(adimtv102_writereg(priv, 0x25, (read_buf<<3)|0x07)) return 0; pllfreq = LO2PLL_Freq((data_buf->frequency)*1000, &LO2PLL_Freq_result); if(adimtv102_readreg(priv, 0x2f, &read_buf2)) return 0; pllfreq_shr14 = pllfreq >> 14; tmp = read_buf2 & 0x1f; if(pllfreq_shr14 <= 66) read_buf2 = tmp | 0x40; else read_buf2 = tmp; pllfreq_residue = (pllfreq - (pllfreq_shr14<<14)) << 6; printk("MTV102>>tp->freq=%d PLLF=%x PLLFREQ=%d MTV10x_REFCLK=%d !\n", data_buf->frequency, pllfreq_residue, pllfreq, 0x4000); data_buf->settings[0].val = (pllfreq_shr14 & 0xff); data_buf->settings[1].val = ((pllfreq_shr14 & 0x300) >> 8)|0x7c; data_buf->settings[3].val = (pllfreq_residue & 0xff00)>>8; data_buf->settings[2].val = (pllfreq_residue & 0xff); data_buf->settings[0].reg = 0x1b; data_buf->settings[1].reg = 0x1a; tmp = (pllfreq*2) > 2629999 ? 1 : 0; tmp <<= 7; data_buf->settings[4].val = tmp | ((pllfreq_residue & 0xf0000)>>16) | (LO2PLL_Freq_result << 4); data_buf->settings[2].reg = 0x1e; data_buf->settings[3].reg = 0x1d; data_buf->settings[4].reg = 0x1c; tmp = read_buf2; data_buf->settings[7].val = tmp | 0x80; data_buf->settings[5].reg = 0x15; data_buf->settings[5].val = 0x25; data_buf->settings[6].reg = 0x2f; data_buf->settings[6].val = tmp; // clear -> set -> clear sequence data_buf->settings[7].reg = 0x2f; data_buf->settings[8].reg = 0x2f; data_buf->settings[8].val = tmp; return 1;}EXPORT_SYMBOL(adimtv102_compute_regs);void adimtv102_starttuner(struct adimtv102_priv *priv, unsigned short frequency) { int i; unsigned char read_buf; struct adimtv102_reg_struct tp; // tuner parameters tp.frequency = frequency; if(!adimtv102_compute_regs(priv, &tp)) return; for(i = 0; i < 9; i++){ if(adimtv102_writereg(priv, tp.settings[i].reg, tp.settings[i].val)) return; } msleep(100); read_buf = 6; adimtv102_readreg(priv, 0x06, &read_buf);}EXPORT_SYMBOL(adimtv102_starttuner);static int adimtv102_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params){ struct adimtv102_priv *priv = fe->tuner_priv; int freq_div1k; int freq_div1m; freq_div1k = params->frequency / 1000; printk("adimtv102_set_params freq=%d\n", freq_div1k); freq_div1m = freq_div1k / 1000; adimtv102_starttuner(priv, freq_div1k/1000); return freq_div1k/1000;}MODULE_AUTHOR("JHA");MODULE_DESCRIPTION("ADI MTV102 silicon tuner driver");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -