162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Quantek QT1010 silicon tuner 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2006 Antti Palosaari <crope@iki.fi> 662306a36Sopenharmony_ci * Aapo Tahkola <aet@rasterburn.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include "qt1010.h" 962306a36Sopenharmony_ci#include "qt1010_priv.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* read single register */ 1262306a36Sopenharmony_cistatic int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci struct i2c_msg msg[2] = { 1562306a36Sopenharmony_ci { .addr = priv->cfg->i2c_address, 1662306a36Sopenharmony_ci .flags = 0, .buf = ®, .len = 1 }, 1762306a36Sopenharmony_ci { .addr = priv->cfg->i2c_address, 1862306a36Sopenharmony_ci .flags = I2C_M_RD, .buf = val, .len = 1 }, 1962306a36Sopenharmony_ci }; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci if (i2c_transfer(priv->i2c, msg, 2) != 2) { 2262306a36Sopenharmony_ci dev_warn(&priv->i2c->dev, "%s: i2c rd failed reg=%02x\n", 2362306a36Sopenharmony_ci KBUILD_MODNAME, reg); 2462306a36Sopenharmony_ci return -EREMOTEIO; 2562306a36Sopenharmony_ci } 2662306a36Sopenharmony_ci return 0; 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* write single register */ 3062306a36Sopenharmony_cistatic int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci u8 buf[2] = { reg, val }; 3362306a36Sopenharmony_ci struct i2c_msg msg = { .addr = priv->cfg->i2c_address, 3462306a36Sopenharmony_ci .flags = 0, .buf = buf, .len = 2 }; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (i2c_transfer(priv->i2c, &msg, 1) != 1) { 3762306a36Sopenharmony_ci dev_warn(&priv->i2c->dev, "%s: i2c wr failed reg=%02x\n", 3862306a36Sopenharmony_ci KBUILD_MODNAME, reg); 3962306a36Sopenharmony_ci return -EREMOTEIO; 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int qt1010_set_params(struct dvb_frontend *fe) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 4762306a36Sopenharmony_ci struct qt1010_priv *priv; 4862306a36Sopenharmony_ci int err; 4962306a36Sopenharmony_ci u32 freq, div, mod1, mod2; 5062306a36Sopenharmony_ci u8 i, tmpval, reg05; 5162306a36Sopenharmony_ci qt1010_i2c_oper_t rd[48] = { 5262306a36Sopenharmony_ci { QT1010_WR, 0x01, 0x80 }, 5362306a36Sopenharmony_ci { QT1010_WR, 0x02, 0x3f }, 5462306a36Sopenharmony_ci { QT1010_WR, 0x05, 0xff }, /* 02 c write */ 5562306a36Sopenharmony_ci { QT1010_WR, 0x06, 0x44 }, 5662306a36Sopenharmony_ci { QT1010_WR, 0x07, 0xff }, /* 04 c write */ 5762306a36Sopenharmony_ci { QT1010_WR, 0x08, 0x08 }, 5862306a36Sopenharmony_ci { QT1010_WR, 0x09, 0xff }, /* 06 c write */ 5962306a36Sopenharmony_ci { QT1010_WR, 0x0a, 0xff }, /* 07 c write */ 6062306a36Sopenharmony_ci { QT1010_WR, 0x0b, 0xff }, /* 08 c write */ 6162306a36Sopenharmony_ci { QT1010_WR, 0x0c, 0xe1 }, 6262306a36Sopenharmony_ci { QT1010_WR, 0x1a, 0xff }, /* 10 c write */ 6362306a36Sopenharmony_ci { QT1010_WR, 0x1b, 0x00 }, 6462306a36Sopenharmony_ci { QT1010_WR, 0x1c, 0x89 }, 6562306a36Sopenharmony_ci { QT1010_WR, 0x11, 0xff }, /* 13 c write */ 6662306a36Sopenharmony_ci { QT1010_WR, 0x12, 0xff }, /* 14 c write */ 6762306a36Sopenharmony_ci { QT1010_WR, 0x22, 0xff }, /* 15 c write */ 6862306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 6962306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0xd0 }, 7062306a36Sopenharmony_ci { QT1010_RD, 0x22, 0xff }, /* 16 c read */ 7162306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 7262306a36Sopenharmony_ci { QT1010_RD, 0x05, 0xff }, /* 20 c read */ 7362306a36Sopenharmony_ci { QT1010_RD, 0x22, 0xff }, /* 21 c read */ 7462306a36Sopenharmony_ci { QT1010_WR, 0x23, 0xd0 }, 7562306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 7662306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0xe0 }, 7762306a36Sopenharmony_ci { QT1010_RD, 0x23, 0xff }, /* 25 c read */ 7862306a36Sopenharmony_ci { QT1010_RD, 0x23, 0xff }, /* 26 c read */ 7962306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 8062306a36Sopenharmony_ci { QT1010_WR, 0x24, 0xd0 }, 8162306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 8262306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0xf0 }, 8362306a36Sopenharmony_ci { QT1010_RD, 0x24, 0xff }, /* 31 c read */ 8462306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 8562306a36Sopenharmony_ci { QT1010_WR, 0x14, 0x7f }, 8662306a36Sopenharmony_ci { QT1010_WR, 0x15, 0x7f }, 8762306a36Sopenharmony_ci { QT1010_WR, 0x05, 0xff }, /* 35 c write */ 8862306a36Sopenharmony_ci { QT1010_WR, 0x06, 0x00 }, 8962306a36Sopenharmony_ci { QT1010_WR, 0x15, 0x1f }, 9062306a36Sopenharmony_ci { QT1010_WR, 0x16, 0xff }, 9162306a36Sopenharmony_ci { QT1010_WR, 0x18, 0xff }, 9262306a36Sopenharmony_ci { QT1010_WR, 0x1f, 0xff }, /* 40 c write */ 9362306a36Sopenharmony_ci { QT1010_WR, 0x20, 0xff }, /* 41 c write */ 9462306a36Sopenharmony_ci { QT1010_WR, 0x21, 0x53 }, 9562306a36Sopenharmony_ci { QT1010_WR, 0x25, 0xff }, /* 43 c write */ 9662306a36Sopenharmony_ci { QT1010_WR, 0x26, 0x15 }, 9762306a36Sopenharmony_ci { QT1010_WR, 0x00, 0xff }, /* 45 c write */ 9862306a36Sopenharmony_ci { QT1010_WR, 0x02, 0x00 }, 9962306a36Sopenharmony_ci { QT1010_WR, 0x01, 0x00 } 10062306a36Sopenharmony_ci }; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define FREQ1 32000000 /* 32 MHz */ 10362306a36Sopenharmony_ci#define FREQ2 4000000 /* 4 MHz Quartz oscillator in the stick? */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci priv = fe->tuner_priv; 10662306a36Sopenharmony_ci freq = c->frequency; 10762306a36Sopenharmony_ci div = (freq + QT1010_OFFSET) / QT1010_STEP; 10862306a36Sopenharmony_ci freq = (div * QT1010_STEP) - QT1010_OFFSET; 10962306a36Sopenharmony_ci mod1 = (freq + QT1010_OFFSET) % FREQ1; 11062306a36Sopenharmony_ci mod2 = (freq + QT1010_OFFSET) % FREQ2; 11162306a36Sopenharmony_ci priv->frequency = freq; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 11462306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* reg 05 base value */ 11762306a36Sopenharmony_ci if (freq < 290000000) reg05 = 0x14; /* 290 MHz */ 11862306a36Sopenharmony_ci else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */ 11962306a36Sopenharmony_ci else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */ 12062306a36Sopenharmony_ci else reg05 = 0x74; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* 0x5 */ 12362306a36Sopenharmony_ci rd[2].val = reg05; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* 07 - set frequency: 32 MHz scale */ 12662306a36Sopenharmony_ci rd[4].val = (freq + QT1010_OFFSET) / FREQ1; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* 09 - changes every 8/24 MHz */ 12962306a36Sopenharmony_ci if (mod1 < 8000000) rd[6].val = 0x1d; 13062306a36Sopenharmony_ci else rd[6].val = 0x1c; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* 0a - set frequency: 4 MHz scale (max 28 MHz) */ 13362306a36Sopenharmony_ci if (mod1 < 1*FREQ2) rd[7].val = 0x09; /* +0 MHz */ 13462306a36Sopenharmony_ci else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /* +4 MHz */ 13562306a36Sopenharmony_ci else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /* +8 MHz */ 13662306a36Sopenharmony_ci else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */ 13762306a36Sopenharmony_ci else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */ 13862306a36Sopenharmony_ci else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */ 13962306a36Sopenharmony_ci else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */ 14062306a36Sopenharmony_ci else rd[7].val = 0x0a; /* +28 MHz */ 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* 0b - changes every 2/2 MHz */ 14362306a36Sopenharmony_ci if (mod2 < 2000000) rd[8].val = 0x45; 14462306a36Sopenharmony_ci else rd[8].val = 0x44; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/ 14762306a36Sopenharmony_ci tmpval = 0x78; /* byte, overflows intentionally */ 14862306a36Sopenharmony_ci rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 11 */ 15162306a36Sopenharmony_ci rd[13].val = 0xfd; /* TODO: correct value calculation */ 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* 12 */ 15462306a36Sopenharmony_ci rd[14].val = 0x91; /* TODO: correct value calculation */ 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* 22 */ 15762306a36Sopenharmony_ci if (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */ 15862306a36Sopenharmony_ci else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */ 15962306a36Sopenharmony_ci else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */ 16062306a36Sopenharmony_ci else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */ 16162306a36Sopenharmony_ci else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */ 16262306a36Sopenharmony_ci else rd[15].val = 0xd0; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* 05 */ 16562306a36Sopenharmony_ci rd[35].val = (reg05 & 0xf0); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* 1f */ 16862306a36Sopenharmony_ci if (mod1 < 8000000) tmpval = 0x00; 16962306a36Sopenharmony_ci else if (mod1 < 12000000) tmpval = 0x01; 17062306a36Sopenharmony_ci else if (mod1 < 16000000) tmpval = 0x02; 17162306a36Sopenharmony_ci else if (mod1 < 24000000) tmpval = 0x03; 17262306a36Sopenharmony_ci else if (mod1 < 28000000) tmpval = 0x04; 17362306a36Sopenharmony_ci else tmpval = 0x05; 17462306a36Sopenharmony_ci rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* 20 */ 17762306a36Sopenharmony_ci if (mod1 < 8000000) tmpval = 0x00; 17862306a36Sopenharmony_ci else if (mod1 < 12000000) tmpval = 0x01; 17962306a36Sopenharmony_ci else if (mod1 < 20000000) tmpval = 0x02; 18062306a36Sopenharmony_ci else if (mod1 < 24000000) tmpval = 0x03; 18162306a36Sopenharmony_ci else if (mod1 < 28000000) tmpval = 0x04; 18262306a36Sopenharmony_ci else tmpval = 0x05; 18362306a36Sopenharmony_ci rd[41].val = (priv->reg20_init_val + 0x0d + tmpval); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* 25 */ 18662306a36Sopenharmony_ci rd[43].val = priv->reg25_init_val; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* 00 */ 18962306a36Sopenharmony_ci rd[45].val = 0x92; /* TODO: correct value calculation */ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci dev_dbg(&priv->i2c->dev, 19262306a36Sopenharmony_ci "%s: freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x " \ 19362306a36Sopenharmony_ci "1a:%02x 11:%02x 12:%02x 22:%02x 05:%02x 1f:%02x " \ 19462306a36Sopenharmony_ci "20:%02x 25:%02x 00:%02x\n", __func__, \ 19562306a36Sopenharmony_ci freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, \ 19662306a36Sopenharmony_ci rd[8].val, rd[10].val, rd[13].val, rd[14].val, \ 19762306a36Sopenharmony_ci rd[15].val, rd[35].val, rd[40].val, rd[41].val, \ 19862306a36Sopenharmony_ci rd[43].val, rd[45].val); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rd); i++) { 20162306a36Sopenharmony_ci if (rd[i].oper == QT1010_WR) { 20262306a36Sopenharmony_ci err = qt1010_writereg(priv, rd[i].reg, rd[i].val); 20362306a36Sopenharmony_ci } else { /* read is required to proper locking */ 20462306a36Sopenharmony_ci err = qt1010_readreg(priv, rd[i].reg, &tmpval); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci if (err) return err; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 21062306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int qt1010_init_meas1(struct qt1010_priv *priv, 21662306a36Sopenharmony_ci u8 oper, u8 reg, u8 reg_init_val, u8 *retval) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci u8 i, val1, val2; 21962306a36Sopenharmony_ci int err; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci qt1010_i2c_oper_t i2c_data[] = { 22262306a36Sopenharmony_ci { QT1010_WR, reg, reg_init_val }, 22362306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 22462306a36Sopenharmony_ci { QT1010_WR, 0x1e, oper }, 22562306a36Sopenharmony_ci }; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { 22862306a36Sopenharmony_ci err = qt1010_writereg(priv, i2c_data[i].reg, 22962306a36Sopenharmony_ci i2c_data[i].val); 23062306a36Sopenharmony_ci if (err) 23162306a36Sopenharmony_ci return err; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci err = qt1010_readreg(priv, reg, &val2); 23562306a36Sopenharmony_ci if (err) 23662306a36Sopenharmony_ci return err; 23762306a36Sopenharmony_ci do { 23862306a36Sopenharmony_ci val1 = val2; 23962306a36Sopenharmony_ci err = qt1010_readreg(priv, reg, &val2); 24062306a36Sopenharmony_ci if (err) 24162306a36Sopenharmony_ci return err; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci dev_dbg(&priv->i2c->dev, "%s: compare reg:%02x %02x %02x\n", 24462306a36Sopenharmony_ci __func__, reg, val1, val2); 24562306a36Sopenharmony_ci } while (val1 != val2); 24662306a36Sopenharmony_ci *retval = val1; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return qt1010_writereg(priv, 0x1e, 0x00); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int qt1010_init_meas2(struct qt1010_priv *priv, 25262306a36Sopenharmony_ci u8 reg_init_val, u8 *retval) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci u8 i, val = 0xff; 25562306a36Sopenharmony_ci int err; 25662306a36Sopenharmony_ci qt1010_i2c_oper_t i2c_data[] = { 25762306a36Sopenharmony_ci { QT1010_WR, 0x07, reg_init_val }, 25862306a36Sopenharmony_ci { QT1010_WR, 0x22, 0xd0 }, 25962306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 26062306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0xd0 }, 26162306a36Sopenharmony_ci { QT1010_RD, 0x22, 0xff }, 26262306a36Sopenharmony_ci { QT1010_WR, 0x1e, 0x00 }, 26362306a36Sopenharmony_ci { QT1010_WR, 0x22, 0xff } 26462306a36Sopenharmony_ci }; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { 26762306a36Sopenharmony_ci if (i2c_data[i].oper == QT1010_WR) { 26862306a36Sopenharmony_ci err = qt1010_writereg(priv, i2c_data[i].reg, 26962306a36Sopenharmony_ci i2c_data[i].val); 27062306a36Sopenharmony_ci } else { 27162306a36Sopenharmony_ci err = qt1010_readreg(priv, i2c_data[i].reg, &val); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci if (err) 27462306a36Sopenharmony_ci return err; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci *retval = val; 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int qt1010_init(struct dvb_frontend *fe) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct qt1010_priv *priv = fe->tuner_priv; 28362306a36Sopenharmony_ci struct dtv_frontend_properties *c = &fe->dtv_property_cache; 28462306a36Sopenharmony_ci int err = 0; 28562306a36Sopenharmony_ci u8 i, tmpval, *valptr = NULL; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci static const qt1010_i2c_oper_t i2c_data[] = { 28862306a36Sopenharmony_ci { QT1010_WR, 0x01, 0x80 }, 28962306a36Sopenharmony_ci { QT1010_WR, 0x0d, 0x84 }, 29062306a36Sopenharmony_ci { QT1010_WR, 0x0e, 0xb7 }, 29162306a36Sopenharmony_ci { QT1010_WR, 0x2a, 0x23 }, 29262306a36Sopenharmony_ci { QT1010_WR, 0x2c, 0xdc }, 29362306a36Sopenharmony_ci { QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */ 29462306a36Sopenharmony_ci { QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */ 29562306a36Sopenharmony_ci { QT1010_WR, 0x2b, 0x70 }, 29662306a36Sopenharmony_ci { QT1010_WR, 0x2a, 0x23 }, 29762306a36Sopenharmony_ci { QT1010_M1, 0x26, 0x08 }, 29862306a36Sopenharmony_ci { QT1010_M1, 0x82, 0xff }, 29962306a36Sopenharmony_ci { QT1010_WR, 0x05, 0x14 }, 30062306a36Sopenharmony_ci { QT1010_WR, 0x06, 0x44 }, 30162306a36Sopenharmony_ci { QT1010_WR, 0x07, 0x28 }, 30262306a36Sopenharmony_ci { QT1010_WR, 0x08, 0x0b }, 30362306a36Sopenharmony_ci { QT1010_WR, 0x11, 0xfd }, 30462306a36Sopenharmony_ci { QT1010_M1, 0x22, 0x0d }, 30562306a36Sopenharmony_ci { QT1010_M1, 0xd0, 0xff }, 30662306a36Sopenharmony_ci { QT1010_WR, 0x06, 0x40 }, 30762306a36Sopenharmony_ci { QT1010_WR, 0x16, 0xf0 }, 30862306a36Sopenharmony_ci { QT1010_WR, 0x02, 0x38 }, 30962306a36Sopenharmony_ci { QT1010_WR, 0x03, 0x18 }, 31062306a36Sopenharmony_ci { QT1010_WR, 0x20, 0xe0 }, 31162306a36Sopenharmony_ci { QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */ 31262306a36Sopenharmony_ci { QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */ 31362306a36Sopenharmony_ci { QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */ 31462306a36Sopenharmony_ci { QT1010_WR, 0x03, 0x19 }, 31562306a36Sopenharmony_ci { QT1010_WR, 0x02, 0x3f }, 31662306a36Sopenharmony_ci { QT1010_WR, 0x21, 0x53 }, 31762306a36Sopenharmony_ci { QT1010_RD, 0x21, 0xff }, 31862306a36Sopenharmony_ci { QT1010_WR, 0x11, 0xfd }, 31962306a36Sopenharmony_ci { QT1010_WR, 0x05, 0x34 }, 32062306a36Sopenharmony_ci { QT1010_WR, 0x06, 0x44 }, 32162306a36Sopenharmony_ci { QT1010_WR, 0x08, 0x08 } 32262306a36Sopenharmony_ci }; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 32562306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { 32862306a36Sopenharmony_ci switch (i2c_data[i].oper) { 32962306a36Sopenharmony_ci case QT1010_WR: 33062306a36Sopenharmony_ci err = qt1010_writereg(priv, i2c_data[i].reg, 33162306a36Sopenharmony_ci i2c_data[i].val); 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case QT1010_RD: 33462306a36Sopenharmony_ci if (i2c_data[i].val == 0x20) 33562306a36Sopenharmony_ci valptr = &priv->reg20_init_val; 33662306a36Sopenharmony_ci else 33762306a36Sopenharmony_ci valptr = &tmpval; 33862306a36Sopenharmony_ci err = qt1010_readreg(priv, i2c_data[i].reg, valptr); 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case QT1010_M1: 34162306a36Sopenharmony_ci if (i2c_data[i].val == 0x25) 34262306a36Sopenharmony_ci valptr = &priv->reg25_init_val; 34362306a36Sopenharmony_ci else if (i2c_data[i].val == 0x1f) 34462306a36Sopenharmony_ci valptr = &priv->reg1f_init_val; 34562306a36Sopenharmony_ci else 34662306a36Sopenharmony_ci valptr = &tmpval; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (i >= ARRAY_SIZE(i2c_data) - 1) 34962306a36Sopenharmony_ci err = -EIO; 35062306a36Sopenharmony_ci else 35162306a36Sopenharmony_ci err = qt1010_init_meas1(priv, i2c_data[i + 1].reg, 35262306a36Sopenharmony_ci i2c_data[i].reg, 35362306a36Sopenharmony_ci i2c_data[i].val, valptr); 35462306a36Sopenharmony_ci i++; 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci if (err) 35862306a36Sopenharmony_ci return err; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */ 36262306a36Sopenharmony_ci if ((err = qt1010_init_meas2(priv, i, &tmpval))) 36362306a36Sopenharmony_ci return err; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!c->frequency) 36662306a36Sopenharmony_ci c->frequency = 545000000; /* Sigmatek DVB-110 545000000 */ 36762306a36Sopenharmony_ci /* MSI Megasky 580 GL861 533000000 */ 36862306a36Sopenharmony_ci return qt1010_set_params(fe); 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic void qt1010_release(struct dvb_frontend *fe) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci kfree(fe->tuner_priv); 37462306a36Sopenharmony_ci fe->tuner_priv = NULL; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct qt1010_priv *priv = fe->tuner_priv; 38062306a36Sopenharmony_ci *frequency = priv->frequency; 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int qt1010_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci *frequency = 36125000; 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic const struct dvb_tuner_ops qt1010_tuner_ops = { 39162306a36Sopenharmony_ci .info = { 39262306a36Sopenharmony_ci .name = "Quantek QT1010", 39362306a36Sopenharmony_ci .frequency_min_hz = QT1010_MIN_FREQ, 39462306a36Sopenharmony_ci .frequency_max_hz = QT1010_MAX_FREQ, 39562306a36Sopenharmony_ci .frequency_step_hz = QT1010_STEP, 39662306a36Sopenharmony_ci }, 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci .release = qt1010_release, 39962306a36Sopenharmony_ci .init = qt1010_init, 40062306a36Sopenharmony_ci /* TODO: implement sleep */ 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci .set_params = qt1010_set_params, 40362306a36Sopenharmony_ci .get_frequency = qt1010_get_frequency, 40462306a36Sopenharmony_ci .get_if_frequency = qt1010_get_if_frequency, 40562306a36Sopenharmony_ci}; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistruct dvb_frontend * qt1010_attach(struct dvb_frontend *fe, 40862306a36Sopenharmony_ci struct i2c_adapter *i2c, 40962306a36Sopenharmony_ci struct qt1010_config *cfg) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct qt1010_priv *priv = NULL; 41262306a36Sopenharmony_ci u8 id; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL); 41562306a36Sopenharmony_ci if (priv == NULL) 41662306a36Sopenharmony_ci return NULL; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci priv->cfg = cfg; 41962306a36Sopenharmony_ci priv->i2c = i2c; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 42262306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* Try to detect tuner chip. Probably this is not correct register. */ 42662306a36Sopenharmony_ci if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) { 42762306a36Sopenharmony_ci kfree(priv); 42862306a36Sopenharmony_ci return NULL; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (fe->ops.i2c_gate_ctrl) 43262306a36Sopenharmony_ci fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci dev_info(&priv->i2c->dev, 43562306a36Sopenharmony_ci "%s: Quantek QT1010 successfully identified\n", 43662306a36Sopenharmony_ci KBUILD_MODNAME); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops, 43962306a36Sopenharmony_ci sizeof(struct dvb_tuner_ops)); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci fe->tuner_priv = priv; 44262306a36Sopenharmony_ci return fe; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qt1010_attach); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ciMODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver"); 44762306a36Sopenharmony_ciMODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); 44862306a36Sopenharmony_ciMODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>"); 44962306a36Sopenharmony_ciMODULE_VERSION("0.1"); 45062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 451