Path: blob/master/drivers/media/common/tuners/tda18218.c
15112 views
/*1* NXP TDA18218HN silicon tuner driver2*3* Copyright (C) 2010 Antti Palosaari <[email protected]>4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation; either version 2 of the License, or8* (at your option) any later version.9*10* This program is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License16* along with this program; if not, write to the Free Software17* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.18*/1920#include "tda18218.h"21#include "tda18218_priv.h"2223static int debug;24module_param(debug, int, 0644);25MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");2627/* write multiple registers */28static int tda18218_wr_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)29{30int ret = 0;31u8 buf[1+len], quotient, remainder, i, msg_len, msg_len_max;32struct i2c_msg msg[1] = {33{34.addr = priv->cfg->i2c_address,35.flags = 0,36.buf = buf,37}38};3940msg_len_max = priv->cfg->i2c_wr_max - 1;41quotient = len / msg_len_max;42remainder = len % msg_len_max;43msg_len = msg_len_max;44for (i = 0; (i <= quotient && remainder); i++) {45if (i == quotient) /* set len of the last msg */46msg_len = remainder;4748msg[0].len = msg_len + 1;49buf[0] = reg + i * msg_len_max;50memcpy(&buf[1], &val[i * msg_len_max], msg_len);5152ret = i2c_transfer(priv->i2c, msg, 1);53if (ret != 1)54break;55}5657if (ret == 1) {58ret = 0;59} else {60warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);61ret = -EREMOTEIO;62}6364return ret;65}6667/* read multiple registers */68static int tda18218_rd_regs(struct tda18218_priv *priv, u8 reg, u8 *val, u8 len)69{70int ret;71u8 buf[reg+len]; /* we must start read always from reg 0x00 */72struct i2c_msg msg[2] = {73{74.addr = priv->cfg->i2c_address,75.flags = 0,76.len = 1,77.buf = "\x00",78}, {79.addr = priv->cfg->i2c_address,80.flags = I2C_M_RD,81.len = sizeof(buf),82.buf = buf,83}84};8586ret = i2c_transfer(priv->i2c, msg, 2);87if (ret == 2) {88memcpy(val, &buf[reg], len);89ret = 0;90} else {91warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);92ret = -EREMOTEIO;93}9495return ret;96}9798/* write single register */99static int tda18218_wr_reg(struct tda18218_priv *priv, u8 reg, u8 val)100{101return tda18218_wr_regs(priv, reg, &val, 1);102}103104/* read single register */105106static int tda18218_rd_reg(struct tda18218_priv *priv, u8 reg, u8 *val)107{108return tda18218_rd_regs(priv, reg, val, 1);109}110111static int tda18218_set_params(struct dvb_frontend *fe,112struct dvb_frontend_parameters *params)113{114struct tda18218_priv *priv = fe->tuner_priv;115int ret;116u8 buf[3], i, BP_Filter, LP_Fc;117u32 LO_Frac;118/* TODO: find out correct AGC algorithm */119u8 agc[][2] = {120{ R20_AGC11, 0x60 },121{ R23_AGC21, 0x02 },122{ R20_AGC11, 0xa0 },123{ R23_AGC21, 0x09 },124{ R20_AGC11, 0xe0 },125{ R23_AGC21, 0x0c },126{ R20_AGC11, 0x40 },127{ R23_AGC21, 0x01 },128{ R20_AGC11, 0x80 },129{ R23_AGC21, 0x08 },130{ R20_AGC11, 0xc0 },131{ R23_AGC21, 0x0b },132{ R24_AGC22, 0x1c },133{ R24_AGC22, 0x0c },134};135136if (fe->ops.i2c_gate_ctrl)137fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */138139/* low-pass filter cut-off frequency */140switch (params->u.ofdm.bandwidth) {141case BANDWIDTH_6_MHZ:142LP_Fc = 0;143LO_Frac = params->frequency + 4000000;144break;145case BANDWIDTH_7_MHZ:146LP_Fc = 1;147LO_Frac = params->frequency + 3500000;148break;149case BANDWIDTH_8_MHZ:150default:151LP_Fc = 2;152LO_Frac = params->frequency + 4000000;153break;154}155156/* band-pass filter */157if (LO_Frac < 188000000)158BP_Filter = 3;159else if (LO_Frac < 253000000)160BP_Filter = 4;161else if (LO_Frac < 343000000)162BP_Filter = 5;163else164BP_Filter = 6;165166buf[0] = (priv->regs[R1A_IF1] & ~7) | BP_Filter; /* BP_Filter */167buf[1] = (priv->regs[R1B_IF2] & ~3) | LP_Fc; /* LP_Fc */168buf[2] = priv->regs[R1C_AGC2B];169ret = tda18218_wr_regs(priv, R1A_IF1, buf, 3);170if (ret)171goto error;172173buf[0] = (LO_Frac / 1000) >> 12; /* LO_Frac_0 */174buf[1] = (LO_Frac / 1000) >> 4; /* LO_Frac_1 */175buf[2] = (LO_Frac / 1000) << 4 |176(priv->regs[R0C_MD5] & 0x0f); /* LO_Frac_2 */177ret = tda18218_wr_regs(priv, R0A_MD3, buf, 3);178if (ret)179goto error;180181buf[0] = priv->regs[R0F_MD8] | (1 << 6); /* Freq_prog_Start */182ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);183if (ret)184goto error;185186buf[0] = priv->regs[R0F_MD8] & ~(1 << 6); /* Freq_prog_Start */187ret = tda18218_wr_regs(priv, R0F_MD8, buf, 1);188if (ret)189goto error;190191/* trigger AGC */192for (i = 0; i < ARRAY_SIZE(agc); i++) {193ret = tda18218_wr_reg(priv, agc[i][0], agc[i][1]);194if (ret)195goto error;196}197198error:199if (fe->ops.i2c_gate_ctrl)200fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */201202if (ret)203dbg("%s: failed ret:%d", __func__, ret);204205return ret;206}207208static int tda18218_sleep(struct dvb_frontend *fe)209{210struct tda18218_priv *priv = fe->tuner_priv;211int ret;212213if (fe->ops.i2c_gate_ctrl)214fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */215216/* standby */217ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));218219if (fe->ops.i2c_gate_ctrl)220fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */221222if (ret)223dbg("%s: failed ret:%d", __func__, ret);224225return ret;226}227228static int tda18218_init(struct dvb_frontend *fe)229{230struct tda18218_priv *priv = fe->tuner_priv;231int ret;232233/* TODO: calibrations */234235if (fe->ops.i2c_gate_ctrl)236fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */237238ret = tda18218_wr_regs(priv, R00_ID, priv->regs, TDA18218_NUM_REGS);239240if (fe->ops.i2c_gate_ctrl)241fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */242243if (ret)244dbg("%s: failed ret:%d", __func__, ret);245246return ret;247}248249static int tda18218_release(struct dvb_frontend *fe)250{251kfree(fe->tuner_priv);252fe->tuner_priv = NULL;253return 0;254}255256static const struct dvb_tuner_ops tda18218_tuner_ops = {257.info = {258.name = "NXP TDA18218",259260.frequency_min = 174000000,261.frequency_max = 864000000,262.frequency_step = 1000,263},264265.release = tda18218_release,266.init = tda18218_init,267.sleep = tda18218_sleep,268269.set_params = tda18218_set_params,270};271272struct dvb_frontend *tda18218_attach(struct dvb_frontend *fe,273struct i2c_adapter *i2c, struct tda18218_config *cfg)274{275struct tda18218_priv *priv = NULL;276u8 val;277int ret;278/* chip default registers values */279static u8 def_regs[] = {2800xc0, 0x88, 0x00, 0x8e, 0x03, 0x00, 0x00, 0xd0, 0x00, 0x40,2810x00, 0x00, 0x07, 0xff, 0x84, 0x09, 0x00, 0x13, 0x00, 0x00,2820x01, 0x84, 0x09, 0xf0, 0x19, 0x0a, 0x8e, 0x69, 0x98, 0x01,2830x00, 0x58, 0x10, 0x40, 0x8c, 0x00, 0x0c, 0x48, 0x85, 0xc9,2840xa7, 0x00, 0x00, 0x00, 0x30, 0x81, 0x80, 0x00, 0x39, 0x00,2850x8a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xf6286};287288priv = kzalloc(sizeof(struct tda18218_priv), GFP_KERNEL);289if (priv == NULL)290return NULL;291292priv->cfg = cfg;293priv->i2c = i2c;294fe->tuner_priv = priv;295296if (fe->ops.i2c_gate_ctrl)297fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */298299/* check if the tuner is there */300ret = tda18218_rd_reg(priv, R00_ID, &val);301dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);302if (ret || val != def_regs[R00_ID]) {303kfree(priv);304return NULL;305}306307info("NXP TDA18218HN successfully identified.");308309memcpy(&fe->ops.tuner_ops, &tda18218_tuner_ops,310sizeof(struct dvb_tuner_ops));311memcpy(priv->regs, def_regs, sizeof(def_regs));312313/* loop-through enabled chip default register values */314if (priv->cfg->loop_through) {315priv->regs[R17_PD1] = 0xb0;316priv->regs[R18_PD2] = 0x59;317}318319/* standby */320ret = tda18218_wr_reg(priv, R17_PD1, priv->regs[R17_PD1] | (1 << 0));321if (ret)322dbg("%s: failed ret:%d", __func__, ret);323324if (fe->ops.i2c_gate_ctrl)325fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */326327return fe;328}329EXPORT_SYMBOL(tda18218_attach);330331MODULE_DESCRIPTION("NXP TDA18218HN silicon tuner driver");332MODULE_AUTHOR("Antti Palosaari <[email protected]>");333MODULE_LICENSE("GPL");334335336