1 /* 2 * NXP TDA18212HN silicon tuner driver 3 * 4 * Copyright (C) 2011 Antti Palosaari <crope@iki.fi> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License along 17 * with this program; if not, write to the Free Software Foundation, Inc., 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 */ 20 21 #include "tda18212.h" 22 23 /* Max transfer size done by I2C transfer functions */ 24 #define MAX_XFER_SIZE 64 25 26 struct tda18212_priv { 27 struct tda18212_config *cfg; 28 struct i2c_adapter *i2c; 29 30 u32 if_frequency; 31 }; 32 33 /* write multiple registers */ 34 static int tda18212_wr_regs(struct tda18212_priv *priv, u8 reg, u8 *val, 35 int len) 36 { 37 int ret; 38 u8 buf[MAX_XFER_SIZE]; 39 struct i2c_msg msg[1] = { 40 { 41 .addr = priv->cfg->i2c_address, 42 .flags = 0, 43 .len = 1 + len, 44 .buf = buf, 45 } 46 }; 47 48 if (1 + len > sizeof(buf)) { 49 dev_warn(&priv->i2c->dev, 50 "%s: i2c wr reg=%04x: len=%d is too big!\n", 51 KBUILD_MODNAME, reg, len); 52 return -EINVAL; 53 } 54 55 buf[0] = reg; 56 memcpy(&buf[1], val, len); 57 58 ret = i2c_transfer(priv->i2c, msg, 1); 59 if (ret == 1) { 60 ret = 0; 61 } else { 62 dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ 63 "len=%d\n", KBUILD_MODNAME, ret, reg, len); 64 ret = -EREMOTEIO; 65 } 66 return ret; 67 } 68 69 /* read multiple registers */ 70 static int tda18212_rd_regs(struct tda18212_priv *priv, u8 reg, u8 *val, 71 int len) 72 { 73 int ret; 74 u8 buf[MAX_XFER_SIZE]; 75 struct i2c_msg msg[2] = { 76 { 77 .addr = priv->cfg->i2c_address, 78 .flags = 0, 79 .len = 1, 80 .buf = ®, 81 }, { 82 .addr = priv->cfg->i2c_address, 83 .flags = I2C_M_RD, 84 .len = len, 85 .buf = buf, 86 } 87 }; 88 89 if (len > sizeof(buf)) { 90 dev_warn(&priv->i2c->dev, 91 "%s: i2c rd reg=%04x: len=%d is too big!\n", 92 KBUILD_MODNAME, reg, len); 93 return -EINVAL; 94 } 95 96 ret = i2c_transfer(priv->i2c, msg, 2); 97 if (ret == 2) { 98 memcpy(val, buf, len); 99 ret = 0; 100 } else { 101 dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ 102 "len=%d\n", KBUILD_MODNAME, ret, reg, len); 103 ret = -EREMOTEIO; 104 } 105 106 return ret; 107 } 108 109 /* write single register */ 110 static int tda18212_wr_reg(struct tda18212_priv *priv, u8 reg, u8 val) 111 { 112 return tda18212_wr_regs(priv, reg, &val, 1); 113 } 114 115 /* read single register */ 116 static int tda18212_rd_reg(struct tda18212_priv *priv, u8 reg, u8 *val) 117 { 118 return tda18212_rd_regs(priv, reg, val, 1); 119 } 120 121 #if 0 /* keep, useful when developing driver */ 122 static void tda18212_dump_regs(struct tda18212_priv *priv) 123 { 124 int i; 125 u8 buf[256]; 126 127 #define TDA18212_RD_LEN 32 128 for (i = 0; i < sizeof(buf); i += TDA18212_RD_LEN) 129 tda18212_rd_regs(priv, i, &buf[i], TDA18212_RD_LEN); 130 131 print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 32, 1, buf, 132 sizeof(buf), true); 133 134 return; 135 } 136 #endif 137 138 static int tda18212_set_params(struct dvb_frontend *fe) 139 { 140 struct tda18212_priv *priv = fe->tuner_priv; 141 struct dtv_frontend_properties *c = &fe->dtv_property_cache; 142 int ret, i; 143 u32 if_khz; 144 u8 buf[9]; 145 #define DVBT_6 0 146 #define DVBT_7 1 147 #define DVBT_8 2 148 #define DVBT2_6 3 149 #define DVBT2_7 4 150 #define DVBT2_8 5 151 #define DVBC_6 6 152 #define DVBC_8 7 153 static const u8 bw_params[][3] = { 154 /* reg: 0f 13 23 */ 155 [DVBT_6] = { 0xb3, 0x20, 0x03 }, 156 [DVBT_7] = { 0xb3, 0x31, 0x01 }, 157 [DVBT_8] = { 0xb3, 0x22, 0x01 }, 158 [DVBT2_6] = { 0xbc, 0x20, 0x03 }, 159 [DVBT2_7] = { 0xbc, 0x72, 0x03 }, 160 [DVBT2_8] = { 0xbc, 0x22, 0x01 }, 161 [DVBC_6] = { 0x92, 0x50, 0x03 }, 162 [DVBC_8] = { 0x92, 0x53, 0x03 }, 163 }; 164 165 dev_dbg(&priv->i2c->dev, 166 "%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", 167 __func__, c->delivery_system, c->frequency, 168 c->bandwidth_hz); 169 170 if (fe->ops.i2c_gate_ctrl) 171 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ 172 173 switch (c->delivery_system) { 174 case SYS_DVBT: 175 switch (c->bandwidth_hz) { 176 case 6000000: 177 if_khz = priv->cfg->if_dvbt_6; 178 i = DVBT_6; 179 break; 180 case 7000000: 181 if_khz = priv->cfg->if_dvbt_7; 182 i = DVBT_7; 183 break; 184 case 8000000: 185 if_khz = priv->cfg->if_dvbt_8; 186 i = DVBT_8; 187 break; 188 default: 189 ret = -EINVAL; 190 goto error; 191 } 192 break; 193 case SYS_DVBT2: 194 switch (c->bandwidth_hz) { 195 case 6000000: 196 if_khz = priv->cfg->if_dvbt2_6; 197 i = DVBT2_6; 198 break; 199 case 7000000: 200 if_khz = priv->cfg->if_dvbt2_7; 201 i = DVBT2_7; 202 break; 203 case 8000000: 204 if_khz = priv->cfg->if_dvbt2_8; 205 i = DVBT2_8; 206 break; 207 default: 208 ret = -EINVAL; 209 goto error; 210 } 211 break; 212 case SYS_DVBC_ANNEX_A: 213 case SYS_DVBC_ANNEX_C: 214 if_khz = priv->cfg->if_dvbc; 215 i = DVBC_8; 216 break; 217 default: 218 ret = -EINVAL; 219 goto error; 220 } 221 222 ret = tda18212_wr_reg(priv, 0x23, bw_params[i][2]); 223 if (ret) 224 goto error; 225 226 ret = tda18212_wr_reg(priv, 0x06, 0x00); 227 if (ret) 228 goto error; 229 230 ret = tda18212_wr_reg(priv, 0x0f, bw_params[i][0]); 231 if (ret) 232 goto error; 233 234 buf[0] = 0x02; 235 buf[1] = bw_params[i][1]; 236 buf[2] = 0x03; /* default value */ 237 buf[3] = DIV_ROUND_CLOSEST(if_khz, 50); 238 buf[4] = ((c->frequency / 1000) >> 16) & 0xff; 239 buf[5] = ((c->frequency / 1000) >> 8) & 0xff; 240 buf[6] = ((c->frequency / 1000) >> 0) & 0xff; 241 buf[7] = 0xc1; 242 buf[8] = 0x01; 243 ret = tda18212_wr_regs(priv, 0x12, buf, sizeof(buf)); 244 if (ret) 245 goto error; 246 247 /* actual IF rounded as it is on register */ 248 priv->if_frequency = buf[3] * 50 * 1000; 249 250 exit: 251 if (fe->ops.i2c_gate_ctrl) 252 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 253 254 return ret; 255 256 error: 257 dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); 258 goto exit; 259 } 260 261 static int tda18212_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 262 { 263 struct tda18212_priv *priv = fe->tuner_priv; 264 265 *frequency = priv->if_frequency; 266 267 return 0; 268 } 269 270 static int tda18212_release(struct dvb_frontend *fe) 271 { 272 kfree(fe->tuner_priv); 273 fe->tuner_priv = NULL; 274 return 0; 275 } 276 277 static const struct dvb_tuner_ops tda18212_tuner_ops = { 278 .info = { 279 .name = "NXP TDA18212", 280 281 .frequency_min = 48000000, 282 .frequency_max = 864000000, 283 .frequency_step = 1000, 284 }, 285 286 .release = tda18212_release, 287 288 .set_params = tda18212_set_params, 289 .get_if_frequency = tda18212_get_if_frequency, 290 }; 291 292 struct dvb_frontend *tda18212_attach(struct dvb_frontend *fe, 293 struct i2c_adapter *i2c, struct tda18212_config *cfg) 294 { 295 struct tda18212_priv *priv = NULL; 296 int ret; 297 u8 val; 298 299 priv = kzalloc(sizeof(struct tda18212_priv), GFP_KERNEL); 300 if (priv == NULL) 301 return NULL; 302 303 priv->cfg = cfg; 304 priv->i2c = i2c; 305 fe->tuner_priv = priv; 306 307 if (fe->ops.i2c_gate_ctrl) 308 fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ 309 310 /* check if the tuner is there */ 311 ret = tda18212_rd_reg(priv, 0x00, &val); 312 313 if (fe->ops.i2c_gate_ctrl) 314 fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ 315 316 if (!ret) 317 dev_dbg(&priv->i2c->dev, "%s: chip id=%02x\n", __func__, val); 318 if (ret || val != 0xc7) { 319 kfree(priv); 320 return NULL; 321 } 322 323 dev_info(&priv->i2c->dev, 324 "%s: NXP TDA18212HN successfully identified\n", 325 KBUILD_MODNAME); 326 327 memcpy(&fe->ops.tuner_ops, &tda18212_tuner_ops, 328 sizeof(struct dvb_tuner_ops)); 329 330 return fe; 331 } 332 EXPORT_SYMBOL(tda18212_attach); 333 334 MODULE_DESCRIPTION("NXP TDA18212HN silicon tuner driver"); 335 MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 336 MODULE_LICENSE("GPL"); 337