Path: blob/master/drivers/media/common/tuners/tda18212.c
15112 views
/*1* NXP TDA18212HN silicon tuner driver2*3* Copyright (C) 2011 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 License along16* with this program; if not, write to the Free Software Foundation, Inc.,17* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.18*/1920#include "tda18212_priv.h"2122static int debug;23module_param(debug, int, 0644);24MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");2526/* write multiple registers */27static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val,28int len)29{30int ret;31u8 buf[len+1];32struct i2c_msg msg[1] = {33{34.addr = priv->cfg->i2c_address,35.flags = 0,36.len = sizeof(buf),37.buf = buf,38}39};4041buf[0] = reg;42memcpy(&buf[1], val, len);4344ret = i2c_transfer(priv->i2c, msg, 1);45if (ret == 1) {46ret = 0;47} else {48warn("i2c wr failed ret:%d reg:%02x len:%d", ret, reg, len);49ret = -EREMOTEIO;50}51return ret;52}5354/* read multiple registers */55static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val,56int len)57{58int ret;59u8 buf[len];60struct i2c_msg msg[2] = {61{62.addr = priv->cfg->i2c_address,63.flags = 0,64.len = 1,65.buf = ®,66}, {67.addr = priv->cfg->i2c_address,68.flags = I2C_M_RD,69.len = sizeof(buf),70.buf = buf,71}72};7374ret = i2c_transfer(priv->i2c, msg, 2);75if (ret == 2) {76memcpy(val, buf, len);77ret = 0;78} else {79warn("i2c rd failed ret:%d reg:%02x len:%d", ret, reg, len);80ret = -EREMOTEIO;81}8283return ret;84}8586/* write single register */87static int tda18212_wr_reg(struct tda18212_priv *priv, u8 reg, u8 val)88{89return tda18212_wr_regs(priv, reg, &val, 1);90}9192/* read single register */93static int tda18212_rd_reg(struct tda18212_priv *priv, u8 reg, u8 *val)94{95return tda18212_rd_regs(priv, reg, val, 1);96}9798#if 0 /* keep, useful when developing driver */99static void tda18212_dump_regs(struct tda18212_priv *priv)100{101int i;102u8 buf[256];103104#define TDA18212_RD_LEN 32105for (i = 0; i < sizeof(buf); i += TDA18212_RD_LEN)106tda18212_rd_regs(priv, i, &buf[i], TDA18212_RD_LEN);107108print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, buf,109sizeof(buf), true);110111return;112}113#endif114115static int tda18212_set_params(struct dvb_frontend *fe,116struct dvb_frontend_parameters *p)117{118struct tda18212_priv *priv = fe->tuner_priv;119struct dtv_frontend_properties *c = &fe->dtv_property_cache;120int ret, i;121u32 if_khz;122u8 buf[9];123static const u8 bw_params[][3] = {124/* 0f 13 23 */125{ 0xb3, 0x20, 0x03 }, /* DVB-T 6 MHz */126{ 0xb3, 0x31, 0x01 }, /* DVB-T 7 MHz */127{ 0xb3, 0x22, 0x01 }, /* DVB-T 8 MHz */128{ 0x92, 0x53, 0x03 }, /* DVB-C */129};130131dbg("%s: delsys=%d RF=%d BW=%d", __func__,132c->delivery_system, c->frequency, c->bandwidth_hz);133134if (fe->ops.i2c_gate_ctrl)135fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */136137switch (c->delivery_system) {138case SYS_DVBT:139switch (c->bandwidth_hz) {140case 6000000:141if_khz = priv->cfg->if_dvbt_6;142i = 0;143break;144case 7000000:145if_khz = priv->cfg->if_dvbt_7;146i = 1;147break;148case 8000000:149if_khz = priv->cfg->if_dvbt_8;150i = 2;151break;152default:153ret = -EINVAL;154goto error;155}156break;157case SYS_DVBC_ANNEX_AC:158if_khz = priv->cfg->if_dvbc;159i = 3;160break;161default:162ret = -EINVAL;163goto error;164}165166ret = tda18212_wr_reg(priv, 0x23, bw_params[i][2]);167if (ret)168goto error;169170ret = tda18212_wr_reg(priv, 0x06, 0x00);171if (ret)172goto error;173174ret = tda18212_wr_reg(priv, 0x0f, bw_params[i][0]);175if (ret)176goto error;177178buf[0] = 0x02;179buf[1] = bw_params[i][1];180buf[2] = 0x03; /* default value */181buf[3] = if_khz / 50;182buf[4] = ((c->frequency / 1000) >> 16) & 0xff;183buf[5] = ((c->frequency / 1000) >> 8) & 0xff;184buf[6] = ((c->frequency / 1000) >> 0) & 0xff;185buf[7] = 0xc1;186buf[8] = 0x01;187ret = tda18212_wr_regs(priv, 0x12, buf, sizeof(buf));188if (ret)189goto error;190191exit:192if (fe->ops.i2c_gate_ctrl)193fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */194195return ret;196197error:198dbg("%s: failed:%d", __func__, ret);199goto exit;200}201202static int tda18212_release(struct dvb_frontend *fe)203{204kfree(fe->tuner_priv);205fe->tuner_priv = NULL;206return 0;207}208209static const struct dvb_tuner_ops tda18212_tuner_ops = {210.info = {211.name = "NXP TDA18212",212213.frequency_min = 48000000,214.frequency_max = 864000000,215.frequency_step = 1000,216},217218.release = tda18212_release,219220.set_params = tda18212_set_params,221};222223struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe,224struct i2c_adapter *i2c, struct tda18212_config *cfg)225{226struct tda18212_priv *priv = NULL;227int ret;228u8 val;229230priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL);231if (priv == NULL)232return NULL;233234priv->cfg = cfg;235priv->i2c = i2c;236fe->tuner_priv = priv;237238if (fe->ops.i2c_gate_ctrl)239fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */240241/* check if the tuner is there */242ret = tda18212_rd_reg(priv, 0x00, &val);243244if (fe->ops.i2c_gate_ctrl)245fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */246247dbg("%s: ret:%d chip ID:%02x", __func__, ret, val);248if (ret || val != 0xc7) {249kfree(priv);250return NULL;251}252253info("NXP TDA18212HN successfully identified.");254255memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops,256sizeof(struct dvb_tuner_ops));257258return fe;259}260EXPORT_SYMBOL(tda18212_attach);261262MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver");263MODULE_AUTHOR("Antti Palosaari <[email protected]>");264MODULE_LICENSE("GPL");265266267