18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Infineon tua6100 pll. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) 2006 Andrew de Quincey 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on code found in budget-av.c, which has the following: 88c2ecf20Sopenharmony_ci * Compiled from various sources by Michael Hunold <michael@mihu.de> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> & 118c2ecf20Sopenharmony_ci * Andrew de Quincey <adq_dvb@lidskialf.net> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Copyright (C) 1999-2002 Ralph Metzler 168c2ecf20Sopenharmony_ci * & Marcus Metzler for convergence integrated media GmbH 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/dvb/frontend.h> 228c2ecf20Sopenharmony_ci#include <asm/types.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "tua6100.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct tua6100_priv { 278c2ecf20Sopenharmony_ci /* i2c details */ 288c2ecf20Sopenharmony_ci int i2c_address; 298c2ecf20Sopenharmony_ci struct i2c_adapter *i2c; 308c2ecf20Sopenharmony_ci u32 frequency; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void tua6100_release(struct dvb_frontend *fe) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci kfree(fe->tuner_priv); 368c2ecf20Sopenharmony_ci fe->tuner_priv = NULL; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int tua6100_sleep(struct dvb_frontend *fe) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct tua6100_priv *priv = fe->tuner_priv; 428c2ecf20Sopenharmony_ci int ret; 438c2ecf20Sopenharmony_ci u8 reg0[] = { 0x00, 0x00 }; 448c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 }; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 478c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 488c2ecf20Sopenharmony_ci if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) { 498c2ecf20Sopenharmony_ci printk("%s: i2c error\n", __func__); 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 528c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return (ret == 1) ? 0 : ret; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int tua6100_set_params(struct dvb_frontend *fe) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 608c2ecf20Sopenharmony_ci struct tua6100_priv *priv = fe->tuner_priv; 618c2ecf20Sopenharmony_ci u32 div; 628c2ecf20Sopenharmony_ci u32 prediv; 638c2ecf20Sopenharmony_ci u8 reg0[] = { 0x00, 0x00 }; 648c2ecf20Sopenharmony_ci u8 reg1[] = { 0x01, 0x00, 0x00, 0x00 }; 658c2ecf20Sopenharmony_ci u8 reg2[] = { 0x02, 0x00, 0x00 }; 668c2ecf20Sopenharmony_ci struct i2c_msg msg0 = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 }; 678c2ecf20Sopenharmony_ci struct i2c_msg msg1 = { .addr = priv->i2c_address, .flags = 0, .buf = reg1, .len = 4 }; 688c2ecf20Sopenharmony_ci struct i2c_msg msg2 = { .addr = priv->i2c_address, .flags = 0, .buf = reg2, .len = 3 }; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define _R_VAL 4 718c2ecf20Sopenharmony_ci#define _P_VAL 32 728c2ecf20Sopenharmony_ci#define _ri 4000000 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci // setup register 0 758c2ecf20Sopenharmony_ci if (c->frequency < 2000000) 768c2ecf20Sopenharmony_ci reg0[1] = 0x03; 778c2ecf20Sopenharmony_ci else 788c2ecf20Sopenharmony_ci reg0[1] = 0x07; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci // setup register 1 818c2ecf20Sopenharmony_ci if (c->frequency < 1630000) 828c2ecf20Sopenharmony_ci reg1[1] = 0x2c; 838c2ecf20Sopenharmony_ci else 848c2ecf20Sopenharmony_ci reg1[1] = 0x0c; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (_P_VAL == 64) 878c2ecf20Sopenharmony_ci reg1[1] |= 0x40; 888c2ecf20Sopenharmony_ci if (c->frequency >= 1525000) 898c2ecf20Sopenharmony_ci reg1[1] |= 0x80; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci // register 2 928c2ecf20Sopenharmony_ci reg2[1] = (_R_VAL >> 8) & 0x03; 938c2ecf20Sopenharmony_ci reg2[2] = _R_VAL; 948c2ecf20Sopenharmony_ci if (c->frequency < 1455000) 958c2ecf20Sopenharmony_ci reg2[1] |= 0x1c; 968c2ecf20Sopenharmony_ci else if (c->frequency < 1630000) 978c2ecf20Sopenharmony_ci reg2[1] |= 0x0c; 988c2ecf20Sopenharmony_ci else 998c2ecf20Sopenharmony_ci reg2[1] |= 0x1c; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* 1028c2ecf20Sopenharmony_ci * The N divisor ratio (note: c->frequency is in kHz, but we 1038c2ecf20Sopenharmony_ci * need it in Hz) 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci prediv = (c->frequency * _R_VAL) / (_ri / 1000); 1068c2ecf20Sopenharmony_ci div = prediv / _P_VAL; 1078c2ecf20Sopenharmony_ci reg1[1] |= (div >> 9) & 0x03; 1088c2ecf20Sopenharmony_ci reg1[2] = div >> 1; 1098c2ecf20Sopenharmony_ci reg1[3] = (div << 7); 1108c2ecf20Sopenharmony_ci priv->frequency = ((div * _P_VAL) * (_ri / 1000)) / _R_VAL; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci // Finally, calculate and store the value for A 1138c2ecf20Sopenharmony_ci reg1[3] |= (prediv - (div*_P_VAL)) & 0x7f; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci#undef _R_VAL 1168c2ecf20Sopenharmony_ci#undef _P_VAL 1178c2ecf20Sopenharmony_ci#undef _ri 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 1208c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 1218c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, &msg0, 1) != 1) 1228c2ecf20Sopenharmony_ci return -EIO; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 1258c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 1268c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, &msg2, 1) != 1) 1278c2ecf20Sopenharmony_ci return -EIO; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 1308c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 1318c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, &msg1, 1) != 1) 1328c2ecf20Sopenharmony_ci return -EIO; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 1358c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct tua6100_priv *priv = fe->tuner_priv; 1438c2ecf20Sopenharmony_ci *frequency = priv->frequency; 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops tua6100_tuner_ops = { 1488c2ecf20Sopenharmony_ci .info = { 1498c2ecf20Sopenharmony_ci .name = "Infineon TUA6100", 1508c2ecf20Sopenharmony_ci .frequency_min_hz = 950 * MHz, 1518c2ecf20Sopenharmony_ci .frequency_max_hz = 2150 * MHz, 1528c2ecf20Sopenharmony_ci .frequency_step_hz = 1 * MHz, 1538c2ecf20Sopenharmony_ci }, 1548c2ecf20Sopenharmony_ci .release = tua6100_release, 1558c2ecf20Sopenharmony_ci .sleep = tua6100_sleep, 1568c2ecf20Sopenharmony_ci .set_params = tua6100_set_params, 1578c2ecf20Sopenharmony_ci .get_frequency = tua6100_get_frequency, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistruct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct tua6100_priv *priv = NULL; 1638c2ecf20Sopenharmony_ci u8 b1 [] = { 0x80 }; 1648c2ecf20Sopenharmony_ci u8 b2 [] = { 0x00 }; 1658c2ecf20Sopenharmony_ci struct i2c_msg msg [] = { { .addr = addr, .flags = 0, .buf = b1, .len = 1 }, 1668c2ecf20Sopenharmony_ci { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } }; 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 1708c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); 1718c2ecf20Sopenharmony_ci ret = i2c_transfer (i2c, msg, 2); 1728c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 1738c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (ret != 2) 1768c2ecf20Sopenharmony_ci return NULL; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct tua6100_priv), GFP_KERNEL); 1798c2ecf20Sopenharmony_ci if (priv == NULL) 1808c2ecf20Sopenharmony_ci return NULL; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci priv->i2c_address = addr; 1838c2ecf20Sopenharmony_ci priv->i2c = i2c; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &tua6100_tuner_ops, sizeof(struct dvb_tuner_ops)); 1868c2ecf20Sopenharmony_ci fe->tuner_priv = priv; 1878c2ecf20Sopenharmony_ci return fe; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tua6100_attach); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DVB tua6100 driver"); 1928c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrew de Quincey"); 1938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 194