18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Quantek QT1010 silicon tuner 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Antti Palosaari <crope@iki.fi> 68c2ecf20Sopenharmony_ci * Aapo Tahkola <aet@rasterburn.org> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include "qt1010.h" 98c2ecf20Sopenharmony_ci#include "qt1010_priv.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci/* read single register */ 128c2ecf20Sopenharmony_cistatic int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci struct i2c_msg msg[2] = { 158c2ecf20Sopenharmony_ci { .addr = priv->cfg->i2c_address, 168c2ecf20Sopenharmony_ci .flags = 0, .buf = ®, .len = 1 }, 178c2ecf20Sopenharmony_ci { .addr = priv->cfg->i2c_address, 188c2ecf20Sopenharmony_ci .flags = I2C_M_RD, .buf = val, .len = 1 }, 198c2ecf20Sopenharmony_ci }; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, msg, 2) != 2) { 228c2ecf20Sopenharmony_ci dev_warn(&priv->i2c->dev, "%s: i2c rd failed reg=%02x\n", 238c2ecf20Sopenharmony_ci KBUILD_MODNAME, reg); 248c2ecf20Sopenharmony_ci return -EREMOTEIO; 258c2ecf20Sopenharmony_ci } 268c2ecf20Sopenharmony_ci return 0; 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* write single register */ 308c2ecf20Sopenharmony_cistatic int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci u8 buf[2] = { reg, val }; 338c2ecf20Sopenharmony_ci struct i2c_msg msg = { .addr = priv->cfg->i2c_address, 348c2ecf20Sopenharmony_ci .flags = 0, .buf = buf, .len = 2 }; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 378c2ecf20Sopenharmony_ci dev_warn(&priv->i2c->dev, "%s: i2c wr failed reg=%02x\n", 388c2ecf20Sopenharmony_ci KBUILD_MODNAME, reg); 398c2ecf20Sopenharmony_ci return -EREMOTEIO; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int qt1010_set_params(struct dvb_frontend *fe) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 478c2ecf20Sopenharmony_ci struct qt1010_priv *priv; 488c2ecf20Sopenharmony_ci int err; 498c2ecf20Sopenharmony_ci u32 freq, div, mod1, mod2; 508c2ecf20Sopenharmony_ci u8 i, tmpval, reg05; 518c2ecf20Sopenharmony_ci qt1010_i2c_oper_t rd[48] = { 528c2ecf20Sopenharmony_ci { QT1010_WR, 0x01, 0x80 }, 538c2ecf20Sopenharmony_ci { QT1010_WR, 0x02, 0x3f }, 548c2ecf20Sopenharmony_ci { QT1010_WR, 0x05, 0xff }, /* 02 c write */ 558c2ecf20Sopenharmony_ci { QT1010_WR, 0x06, 0x44 }, 568c2ecf20Sopenharmony_ci { QT1010_WR, 0x07, 0xff }, /* 04 c write */ 578c2ecf20Sopenharmony_ci { QT1010_WR, 0x08, 0x08 }, 588c2ecf20Sopenharmony_ci { QT1010_WR, 0x09, 0xff }, /* 06 c write */ 598c2ecf20Sopenharmony_ci { QT1010_WR, 0x0a, 0xff }, /* 07 c write */ 608c2ecf20Sopenharmony_ci { QT1010_WR, 0x0b, 0xff }, /* 08 c write */ 618c2ecf20Sopenharmony_ci { QT1010_WR, 0x0c, 0xe1 }, 628c2ecf20Sopenharmony_ci { QT1010_WR, 0x1a, 0xff }, /* 10 c write */ 638c2ecf20Sopenharmony_ci { QT1010_WR, 0x1b, 0x00 }, 648c2ecf20Sopenharmony_ci { QT1010_WR, 0x1c, 0x89 }, 658c2ecf20Sopenharmony_ci { QT1010_WR, 0x11, 0xff }, /* 13 c write */ 668c2ecf20Sopenharmony_ci { QT1010_WR, 0x12, 0xff }, /* 14 c write */ 678c2ecf20Sopenharmony_ci { QT1010_WR, 0x22, 0xff }, /* 15 c write */ 688c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 698c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0xd0 }, 708c2ecf20Sopenharmony_ci { QT1010_RD, 0x22, 0xff }, /* 16 c read */ 718c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 728c2ecf20Sopenharmony_ci { QT1010_RD, 0x05, 0xff }, /* 20 c read */ 738c2ecf20Sopenharmony_ci { QT1010_RD, 0x22, 0xff }, /* 21 c read */ 748c2ecf20Sopenharmony_ci { QT1010_WR, 0x23, 0xd0 }, 758c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 768c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0xe0 }, 778c2ecf20Sopenharmony_ci { QT1010_RD, 0x23, 0xff }, /* 25 c read */ 788c2ecf20Sopenharmony_ci { QT1010_RD, 0x23, 0xff }, /* 26 c read */ 798c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 808c2ecf20Sopenharmony_ci { QT1010_WR, 0x24, 0xd0 }, 818c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 828c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0xf0 }, 838c2ecf20Sopenharmony_ci { QT1010_RD, 0x24, 0xff }, /* 31 c read */ 848c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 858c2ecf20Sopenharmony_ci { QT1010_WR, 0x14, 0x7f }, 868c2ecf20Sopenharmony_ci { QT1010_WR, 0x15, 0x7f }, 878c2ecf20Sopenharmony_ci { QT1010_WR, 0x05, 0xff }, /* 35 c write */ 888c2ecf20Sopenharmony_ci { QT1010_WR, 0x06, 0x00 }, 898c2ecf20Sopenharmony_ci { QT1010_WR, 0x15, 0x1f }, 908c2ecf20Sopenharmony_ci { QT1010_WR, 0x16, 0xff }, 918c2ecf20Sopenharmony_ci { QT1010_WR, 0x18, 0xff }, 928c2ecf20Sopenharmony_ci { QT1010_WR, 0x1f, 0xff }, /* 40 c write */ 938c2ecf20Sopenharmony_ci { QT1010_WR, 0x20, 0xff }, /* 41 c write */ 948c2ecf20Sopenharmony_ci { QT1010_WR, 0x21, 0x53 }, 958c2ecf20Sopenharmony_ci { QT1010_WR, 0x25, 0xff }, /* 43 c write */ 968c2ecf20Sopenharmony_ci { QT1010_WR, 0x26, 0x15 }, 978c2ecf20Sopenharmony_ci { QT1010_WR, 0x00, 0xff }, /* 45 c write */ 988c2ecf20Sopenharmony_ci { QT1010_WR, 0x02, 0x00 }, 998c2ecf20Sopenharmony_ci { QT1010_WR, 0x01, 0x00 } 1008c2ecf20Sopenharmony_ci }; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#define FREQ1 32000000 /* 32 MHz */ 1038c2ecf20Sopenharmony_ci#define FREQ2 4000000 /* 4 MHz Quartz oscillator in the stick? */ 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci priv = fe->tuner_priv; 1068c2ecf20Sopenharmony_ci freq = c->frequency; 1078c2ecf20Sopenharmony_ci div = (freq + QT1010_OFFSET) / QT1010_STEP; 1088c2ecf20Sopenharmony_ci freq = (div * QT1010_STEP) - QT1010_OFFSET; 1098c2ecf20Sopenharmony_ci mod1 = (freq + QT1010_OFFSET) % FREQ1; 1108c2ecf20Sopenharmony_ci mod2 = (freq + QT1010_OFFSET) % FREQ2; 1118c2ecf20Sopenharmony_ci priv->frequency = freq; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 1148c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* reg 05 base value */ 1178c2ecf20Sopenharmony_ci if (freq < 290000000) reg05 = 0x14; /* 290 MHz */ 1188c2ecf20Sopenharmony_ci else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */ 1198c2ecf20Sopenharmony_ci else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */ 1208c2ecf20Sopenharmony_ci else reg05 = 0x74; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* 0x5 */ 1238c2ecf20Sopenharmony_ci rd[2].val = reg05; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* 07 - set frequency: 32 MHz scale */ 1268c2ecf20Sopenharmony_ci rd[4].val = (freq + QT1010_OFFSET) / FREQ1; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* 09 - changes every 8/24 MHz */ 1298c2ecf20Sopenharmony_ci if (mod1 < 8000000) rd[6].val = 0x1d; 1308c2ecf20Sopenharmony_ci else rd[6].val = 0x1c; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* 0a - set frequency: 4 MHz scale (max 28 MHz) */ 1338c2ecf20Sopenharmony_ci if (mod1 < 1*FREQ2) rd[7].val = 0x09; /* +0 MHz */ 1348c2ecf20Sopenharmony_ci else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /* +4 MHz */ 1358c2ecf20Sopenharmony_ci else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /* +8 MHz */ 1368c2ecf20Sopenharmony_ci else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */ 1378c2ecf20Sopenharmony_ci else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */ 1388c2ecf20Sopenharmony_ci else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */ 1398c2ecf20Sopenharmony_ci else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */ 1408c2ecf20Sopenharmony_ci else rd[7].val = 0x0a; /* +28 MHz */ 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 0b - changes every 2/2 MHz */ 1438c2ecf20Sopenharmony_ci if (mod2 < 2000000) rd[8].val = 0x45; 1448c2ecf20Sopenharmony_ci else rd[8].val = 0x44; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/ 1478c2ecf20Sopenharmony_ci tmpval = 0x78; /* byte, overflows intentionally */ 1488c2ecf20Sopenharmony_ci rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* 11 */ 1518c2ecf20Sopenharmony_ci rd[13].val = 0xfd; /* TODO: correct value calculation */ 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* 12 */ 1548c2ecf20Sopenharmony_ci rd[14].val = 0x91; /* TODO: correct value calculation */ 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 22 */ 1578c2ecf20Sopenharmony_ci if (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */ 1588c2ecf20Sopenharmony_ci else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */ 1598c2ecf20Sopenharmony_ci else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */ 1608c2ecf20Sopenharmony_ci else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */ 1618c2ecf20Sopenharmony_ci else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */ 1628c2ecf20Sopenharmony_ci else rd[15].val = 0xd0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* 05 */ 1658c2ecf20Sopenharmony_ci rd[35].val = (reg05 & 0xf0); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* 1f */ 1688c2ecf20Sopenharmony_ci if (mod1 < 8000000) tmpval = 0x00; 1698c2ecf20Sopenharmony_ci else if (mod1 < 12000000) tmpval = 0x01; 1708c2ecf20Sopenharmony_ci else if (mod1 < 16000000) tmpval = 0x02; 1718c2ecf20Sopenharmony_ci else if (mod1 < 24000000) tmpval = 0x03; 1728c2ecf20Sopenharmony_ci else if (mod1 < 28000000) tmpval = 0x04; 1738c2ecf20Sopenharmony_ci else tmpval = 0x05; 1748c2ecf20Sopenharmony_ci rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* 20 */ 1778c2ecf20Sopenharmony_ci if (mod1 < 8000000) tmpval = 0x00; 1788c2ecf20Sopenharmony_ci else if (mod1 < 12000000) tmpval = 0x01; 1798c2ecf20Sopenharmony_ci else if (mod1 < 20000000) tmpval = 0x02; 1808c2ecf20Sopenharmony_ci else if (mod1 < 24000000) tmpval = 0x03; 1818c2ecf20Sopenharmony_ci else if (mod1 < 28000000) tmpval = 0x04; 1828c2ecf20Sopenharmony_ci else tmpval = 0x05; 1838c2ecf20Sopenharmony_ci rd[41].val = (priv->reg20_init_val + 0x0d + tmpval); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* 25 */ 1868c2ecf20Sopenharmony_ci rd[43].val = priv->reg25_init_val; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* 00 */ 1898c2ecf20Sopenharmony_ci rd[45].val = 0x92; /* TODO: correct value calculation */ 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci dev_dbg(&priv->i2c->dev, 1928c2ecf20Sopenharmony_ci "%s: freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x " \ 1938c2ecf20Sopenharmony_ci "1a:%02x 11:%02x 12:%02x 22:%02x 05:%02x 1f:%02x " \ 1948c2ecf20Sopenharmony_ci "20:%02x 25:%02x 00:%02x\n", __func__, \ 1958c2ecf20Sopenharmony_ci freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, \ 1968c2ecf20Sopenharmony_ci rd[8].val, rd[10].val, rd[13].val, rd[14].val, \ 1978c2ecf20Sopenharmony_ci rd[15].val, rd[35].val, rd[40].val, rd[41].val, \ 1988c2ecf20Sopenharmony_ci rd[43].val, rd[45].val); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rd); i++) { 2018c2ecf20Sopenharmony_ci if (rd[i].oper == QT1010_WR) { 2028c2ecf20Sopenharmony_ci err = qt1010_writereg(priv, rd[i].reg, rd[i].val); 2038c2ecf20Sopenharmony_ci } else { /* read is required to proper locking */ 2048c2ecf20Sopenharmony_ci err = qt1010_readreg(priv, rd[i].reg, &tmpval); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci if (err) return err; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 2108c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int qt1010_init_meas1(struct qt1010_priv *priv, 2168c2ecf20Sopenharmony_ci u8 oper, u8 reg, u8 reg_init_val, u8 *retval) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci u8 i, val1, val2; 2198c2ecf20Sopenharmony_ci int err; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci qt1010_i2c_oper_t i2c_data[] = { 2228c2ecf20Sopenharmony_ci { QT1010_WR, reg, reg_init_val }, 2238c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 2248c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, oper }, 2258c2ecf20Sopenharmony_ci }; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { 2288c2ecf20Sopenharmony_ci err = qt1010_writereg(priv, i2c_data[i].reg, 2298c2ecf20Sopenharmony_ci i2c_data[i].val); 2308c2ecf20Sopenharmony_ci if (err) 2318c2ecf20Sopenharmony_ci return err; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci err = qt1010_readreg(priv, reg, &val2); 2358c2ecf20Sopenharmony_ci if (err) 2368c2ecf20Sopenharmony_ci return err; 2378c2ecf20Sopenharmony_ci do { 2388c2ecf20Sopenharmony_ci val1 = val2; 2398c2ecf20Sopenharmony_ci err = qt1010_readreg(priv, reg, &val2); 2408c2ecf20Sopenharmony_ci if (err) 2418c2ecf20Sopenharmony_ci return err; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci dev_dbg(&priv->i2c->dev, "%s: compare reg:%02x %02x %02x\n", 2448c2ecf20Sopenharmony_ci __func__, reg, val1, val2); 2458c2ecf20Sopenharmony_ci } while (val1 != val2); 2468c2ecf20Sopenharmony_ci *retval = val1; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return qt1010_writereg(priv, 0x1e, 0x00); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int qt1010_init_meas2(struct qt1010_priv *priv, 2528c2ecf20Sopenharmony_ci u8 reg_init_val, u8 *retval) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci u8 i, val = 0xff; 2558c2ecf20Sopenharmony_ci int err; 2568c2ecf20Sopenharmony_ci qt1010_i2c_oper_t i2c_data[] = { 2578c2ecf20Sopenharmony_ci { QT1010_WR, 0x07, reg_init_val }, 2588c2ecf20Sopenharmony_ci { QT1010_WR, 0x22, 0xd0 }, 2598c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 2608c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0xd0 }, 2618c2ecf20Sopenharmony_ci { QT1010_RD, 0x22, 0xff }, 2628c2ecf20Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 2638c2ecf20Sopenharmony_ci { QT1010_WR, 0x22, 0xff } 2648c2ecf20Sopenharmony_ci }; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { 2678c2ecf20Sopenharmony_ci if (i2c_data[i].oper == QT1010_WR) { 2688c2ecf20Sopenharmony_ci err = qt1010_writereg(priv, i2c_data[i].reg, 2698c2ecf20Sopenharmony_ci i2c_data[i].val); 2708c2ecf20Sopenharmony_ci } else { 2718c2ecf20Sopenharmony_ci err = qt1010_readreg(priv, i2c_data[i].reg, &val); 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci if (err) 2748c2ecf20Sopenharmony_ci return err; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci *retval = val; 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int qt1010_init(struct dvb_frontend *fe) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct qt1010_priv *priv = fe->tuner_priv; 2838c2ecf20Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 2848c2ecf20Sopenharmony_ci int err = 0; 2858c2ecf20Sopenharmony_ci u8 i, tmpval, *valptr = NULL; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci static const qt1010_i2c_oper_t i2c_data[] = { 2888c2ecf20Sopenharmony_ci { QT1010_WR, 0x01, 0x80 }, 2898c2ecf20Sopenharmony_ci { QT1010_WR, 0x0d, 0x84 }, 2908c2ecf20Sopenharmony_ci { QT1010_WR, 0x0e, 0xb7 }, 2918c2ecf20Sopenharmony_ci { QT1010_WR, 0x2a, 0x23 }, 2928c2ecf20Sopenharmony_ci { QT1010_WR, 0x2c, 0xdc }, 2938c2ecf20Sopenharmony_ci { QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */ 2948c2ecf20Sopenharmony_ci { QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */ 2958c2ecf20Sopenharmony_ci { QT1010_WR, 0x2b, 0x70 }, 2968c2ecf20Sopenharmony_ci { QT1010_WR, 0x2a, 0x23 }, 2978c2ecf20Sopenharmony_ci { QT1010_M1, 0x26, 0x08 }, 2988c2ecf20Sopenharmony_ci { QT1010_M1, 0x82, 0xff }, 2998c2ecf20Sopenharmony_ci { QT1010_WR, 0x05, 0x14 }, 3008c2ecf20Sopenharmony_ci { QT1010_WR, 0x06, 0x44 }, 3018c2ecf20Sopenharmony_ci { QT1010_WR, 0x07, 0x28 }, 3028c2ecf20Sopenharmony_ci { QT1010_WR, 0x08, 0x0b }, 3038c2ecf20Sopenharmony_ci { QT1010_WR, 0x11, 0xfd }, 3048c2ecf20Sopenharmony_ci { QT1010_M1, 0x22, 0x0d }, 3058c2ecf20Sopenharmony_ci { QT1010_M1, 0xd0, 0xff }, 3068c2ecf20Sopenharmony_ci { QT1010_WR, 0x06, 0x40 }, 3078c2ecf20Sopenharmony_ci { QT1010_WR, 0x16, 0xf0 }, 3088c2ecf20Sopenharmony_ci { QT1010_WR, 0x02, 0x38 }, 3098c2ecf20Sopenharmony_ci { QT1010_WR, 0x03, 0x18 }, 3108c2ecf20Sopenharmony_ci { QT1010_WR, 0x20, 0xe0 }, 3118c2ecf20Sopenharmony_ci { QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */ 3128c2ecf20Sopenharmony_ci { QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */ 3138c2ecf20Sopenharmony_ci { QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */ 3148c2ecf20Sopenharmony_ci { QT1010_WR, 0x03, 0x19 }, 3158c2ecf20Sopenharmony_ci { QT1010_WR, 0x02, 0x3f }, 3168c2ecf20Sopenharmony_ci { QT1010_WR, 0x21, 0x53 }, 3178c2ecf20Sopenharmony_ci { QT1010_RD, 0x21, 0xff }, 3188c2ecf20Sopenharmony_ci { QT1010_WR, 0x11, 0xfd }, 3198c2ecf20Sopenharmony_ci { QT1010_WR, 0x05, 0x34 }, 3208c2ecf20Sopenharmony_ci { QT1010_WR, 0x06, 0x44 }, 3218c2ecf20Sopenharmony_ci { QT1010_WR, 0x08, 0x08 } 3228c2ecf20Sopenharmony_ci }; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 3258c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { 3288c2ecf20Sopenharmony_ci switch (i2c_data[i].oper) { 3298c2ecf20Sopenharmony_ci case QT1010_WR: 3308c2ecf20Sopenharmony_ci err = qt1010_writereg(priv, i2c_data[i].reg, 3318c2ecf20Sopenharmony_ci i2c_data[i].val); 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case QT1010_RD: 3348c2ecf20Sopenharmony_ci if (i2c_data[i].val == 0x20) 3358c2ecf20Sopenharmony_ci valptr = &priv->reg20_init_val; 3368c2ecf20Sopenharmony_ci else 3378c2ecf20Sopenharmony_ci valptr = &tmpval; 3388c2ecf20Sopenharmony_ci err = qt1010_readreg(priv, i2c_data[i].reg, valptr); 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci case QT1010_M1: 3418c2ecf20Sopenharmony_ci if (i2c_data[i].val == 0x25) 3428c2ecf20Sopenharmony_ci valptr = &priv->reg25_init_val; 3438c2ecf20Sopenharmony_ci else if (i2c_data[i].val == 0x1f) 3448c2ecf20Sopenharmony_ci valptr = &priv->reg1f_init_val; 3458c2ecf20Sopenharmony_ci else 3468c2ecf20Sopenharmony_ci valptr = &tmpval; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(i2c_data) - 1) 3498c2ecf20Sopenharmony_ci err = -EIO; 3508c2ecf20Sopenharmony_ci else 3518c2ecf20Sopenharmony_ci err = qt1010_init_meas1(priv, i2c_data[i + 1].reg, 3528c2ecf20Sopenharmony_ci i2c_data[i].reg, 3538c2ecf20Sopenharmony_ci i2c_data[i].val, valptr); 3548c2ecf20Sopenharmony_ci i++; 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci if (err) 3588c2ecf20Sopenharmony_ci return err; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */ 3628c2ecf20Sopenharmony_ci if ((err = qt1010_init_meas2(priv, i, &tmpval))) 3638c2ecf20Sopenharmony_ci return err; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (!c->frequency) 3668c2ecf20Sopenharmony_ci c->frequency = 545000000; /* Sigmatek DVB-110 545000000 */ 3678c2ecf20Sopenharmony_ci /* MSI Megasky 580 GL861 533000000 */ 3688c2ecf20Sopenharmony_ci return qt1010_set_params(fe); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic void qt1010_release(struct dvb_frontend *fe) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci kfree(fe->tuner_priv); 3748c2ecf20Sopenharmony_ci fe->tuner_priv = NULL; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct qt1010_priv *priv = fe->tuner_priv; 3808c2ecf20Sopenharmony_ci *frequency = priv->frequency; 3818c2ecf20Sopenharmony_ci return 0; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic int qt1010_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci *frequency = 36125000; 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic const struct dvb_tuner_ops qt1010_tuner_ops = { 3918c2ecf20Sopenharmony_ci .info = { 3928c2ecf20Sopenharmony_ci .name = "Quantek QT1010", 3938c2ecf20Sopenharmony_ci .frequency_min_hz = QT1010_MIN_FREQ, 3948c2ecf20Sopenharmony_ci .frequency_max_hz = QT1010_MAX_FREQ, 3958c2ecf20Sopenharmony_ci .frequency_step_hz = QT1010_STEP, 3968c2ecf20Sopenharmony_ci }, 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci .release = qt1010_release, 3998c2ecf20Sopenharmony_ci .init = qt1010_init, 4008c2ecf20Sopenharmony_ci /* TODO: implement sleep */ 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci .set_params = qt1010_set_params, 4038c2ecf20Sopenharmony_ci .get_frequency = qt1010_get_frequency, 4048c2ecf20Sopenharmony_ci .get_if_frequency = qt1010_get_if_frequency, 4058c2ecf20Sopenharmony_ci}; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistruct dvb_frontend * qt1010_attach(struct dvb_frontend *fe, 4088c2ecf20Sopenharmony_ci struct i2c_adapter *i2c, 4098c2ecf20Sopenharmony_ci struct qt1010_config *cfg) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct qt1010_priv *priv = NULL; 4128c2ecf20Sopenharmony_ci u8 id; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL); 4158c2ecf20Sopenharmony_ci if (priv == NULL) 4168c2ecf20Sopenharmony_ci return NULL; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci priv->cfg = cfg; 4198c2ecf20Sopenharmony_ci priv->i2c = i2c; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 4228c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* Try to detect tuner chip. Probably this is not correct register. */ 4268c2ecf20Sopenharmony_ci if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) { 4278c2ecf20Sopenharmony_ci kfree(priv); 4288c2ecf20Sopenharmony_ci return NULL; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 4328c2ecf20Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci dev_info(&priv->i2c->dev, 4358c2ecf20Sopenharmony_ci "%s: Quantek QT1010 successfully identified\n", 4368c2ecf20Sopenharmony_ci KBUILD_MODNAME); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops, 4398c2ecf20Sopenharmony_ci sizeof(struct dvb_tuner_ops)); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci fe->tuner_priv = priv; 4428c2ecf20Sopenharmony_ci return fe; 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(qt1010_attach); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver"); 4478c2ecf20Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 4488c2ecf20Sopenharmony_ciMODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>"); 4498c2ecf20Sopenharmony_ciMODULE_VERSION("0.1"); 4508c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 451