[PATCH 10/11] qm1d1c0042: namespace cleanup & remodelling

From: ÐÑÐÐ ÐÐÐÐÐÑÐ, AreMa Inc
Date: Sun Oct 05 2014 - 05:00:48 EST


- minimize exports
- use client->dev.platform_data to point frontend
- use real address for I2C
- no freq limits in FE, it is non sense. move here...
- try to treat invalid frequency as channel number

Signed-off-by: ÐÑÐÐ ÐÐÐÐÐÑÐ, AreMa Inc <knightrider@xxxxxx>
---
drivers/media/tuners/qm1d1c0042.c | 644 ++++++++++++++++++--------------------
1 file changed, 296 insertions(+), 348 deletions(-)

diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c
index 18bc745..39adc0a 100644
--- a/drivers/media/tuners/qm1d1c0042.c
+++ b/drivers/media/tuners/qm1d1c0042.c
@@ -1,12 +1,12 @@
/*
- * Sharp QM1D1C0042 8PSK tuner driver
+ * Sharp VA4M6JC2103 - Earthsoft PT3 ISDB-S tuner driver QM1D1C0042
*
- * Copyright (C) 2014 Akihiro Tsukada <tskd08@xxxxxxxxx>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
+ * Copyright (C) 2014 Budi Rachmanto, AreMa Inc. <info@xxxxxx>
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -14,156 +14,145 @@
* GNU General Public License for more details.
*/

-/*
- * NOTICE:
- * As the disclosed information on the chip is very limited,
- * this driver lacks some features, including chip config like IF freq.
- * It assumes that users of this driver (such as a PCI bridge of
- * DTV receiver cards) know the relevant info and
- * configure the chip via I2C if necessary.
- *
- * Currently, PT3 driver is the only one that uses this driver,
- * and contains init/config code in its firmware.
- * Thus some part of the code might be dependent on PT3 specific config.
- */
-
-#include <linux/kernel.h>
-#include <linux/math64.h>
+#include "dvb_frontend.h"
#include "qm1d1c0042.h"

-#define QM1D1C0042_NUM_REGS 0x20
+struct qm1d1c0042 {
+ struct dvb_frontend *fe;
+ u8 addr_tuner, idx, reg[32];
+ u32 freq;
+ int (*read)(struct dvb_frontend *fe, u8 *buf, int buflen);
+};

-static const u8 reg_initval[QM1D1C0042_NUM_REGS] = {
+static const u8 qm1d1c0042_reg_rw[] = {
0x48, 0x1c, 0xa0, 0x10, 0xbc, 0xc5, 0x20, 0x33,
0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0xff, 0xf3, 0x00, 0x2a, 0x64, 0xa6, 0x86,
- 0x8c, 0xcf, 0xb8, 0xf1, 0xa8, 0xf2, 0x89, 0x00
-};
-
-static const struct qm1d1c0042_config default_cfg = {
- .xtal_freq = 16000,
- .lpf = 1,
- .fast_srch = 0,
- .lpf_wait = 20,
- .fast_srch_wait = 4,
- .normal_srch_wait = 15,
+ 0x8c, 0xcf, 0xb8, 0xf1, 0xa8, 0xf2, 0x89, 0x00,
};

-struct qm1d1c0042_state {
- struct qm1d1c0042_config cfg;
- struct i2c_client *i2c;
- u8 regs[QM1D1C0042_NUM_REGS];
-};
-
-static struct qm1d1c0042_state *cfg_to_state(struct qm1d1c0042_config *c)
-{
- return container_of(c, struct qm1d1c0042_state, cfg);
-}
-
-static int reg_write(struct qm1d1c0042_state *state, u8 reg, u8 val)
+/* read via demodulator */
+int qm1d1c0042_fe_read(struct dvb_frontend *fe, u8 addr_data, u8 *data)
{
- u8 wbuf[2] = { reg, val };
int ret;
+ struct qm1d1c0042 *qm = fe->tuner_priv;
+ u8 addr[] = { qm->addr_tuner, addr_data };

- ret = i2c_master_send(state->i2c, wbuf, sizeof(wbuf));
- if (ret >= 0 && ret < sizeof(wbuf))
- ret = -EIO;
- return (ret == sizeof(wbuf)) ? 0 : ret;
+ if ((addr_data != 0x00) && (addr_data != 0x0d))
+ return -EFAULT;
+ ret = qm->read(fe, addr, ARRAY_SIZE(addr));
+ if (ret < 0)
+ return ret;
+ *data = ret;
+ return 0;
}

-static int reg_read(struct qm1d1c0042_state *state, u8 reg, u8 *val)
+/* write via demodulator */
+int qm1d1c0042_fe_write_data(struct dvb_frontend *fe, u8 addr_data, u8 *data, int len)
{
- struct i2c_msg msgs[2] = {
- {
- .addr = state->i2c->addr,
- .flags = 0,
- .buf = &reg,
- .len = 1,
- },
- {
- .addr = state->i2c->addr,
- .flags = I2C_M_RD,
- .buf = val,
- .len = 1,
- },
- };
- int ret;
+ u8 buf[len + 1];

- ret = i2c_transfer(state->i2c->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret >= 0 && ret < ARRAY_SIZE(msgs))
- ret = -EIO;
- return (ret == ARRAY_SIZE(msgs)) ? 0 : ret;
+ buf[0] = addr_data;
+ memcpy(buf + 1, data, len);
+ return fe->ops.write(fe, buf, len + 1);
}

+#define QM1D1C0042_FE_PASSTHROUGH 0xfe

-static int qm1d1c0042_set_srch_mode(struct qm1d1c0042_state *state, bool fast)
+int qm1d1c0042_fe_write_tuner(struct dvb_frontend *fe, u8 *data, int len)
{
- if (fast)
- state->regs[0x03] |= 0x01; /* set fast search mode */
- else
- state->regs[0x03] &= ~0x01 & 0xff;
+ u8 buf[len + 2];

- return reg_write(state, 0x03, state->regs[0x03]);
+ buf[0] = QM1D1C0042_FE_PASSTHROUGH;
+ buf[1] = ((struct qm1d1c0042 *)fe->tuner_priv)->addr_tuner << 1;
+ memcpy(buf + 2, data, len);
+ return fe->ops.write(fe, buf, len + 2);
}

-static int qm1d1c0042_wakeup(struct qm1d1c0042_state *state)
+int qm1d1c0042_write(struct dvb_frontend *fe, u8 addr, u8 data)
{
- int ret;
-
- state->regs[0x01] |= 1 << 3; /* BB_Reg_enable */
- state->regs[0x01] &= (~(1 << 0)) & 0xff; /* NORMAL (wake-up) */
- state->regs[0x05] &= (~(1 << 3)) & 0xff; /* pfd_rst NORMAL */
- ret = reg_write(state, 0x01, state->regs[0x01]);
- if (ret == 0)
- ret = reg_write(state, 0x05, state->regs[0x05]);
+ struct qm1d1c0042 *qm = fe->tuner_priv;
+ u8 buf[] = { addr, data };
+ int err = qm1d1c0042_fe_write_tuner(fe, buf, sizeof(buf));

- if (ret < 0)
- dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
- __func__, state->cfg.fe->dvb->num, state->cfg.fe->id);
- return ret;
+ qm->reg[addr] = buf[1];
+ return err;
}

-/* tuner_ops */
-
-static int qm1d1c0042_set_config(struct dvb_frontend *fe, void *priv_cfg)
+int qm1d1c0042_write_pskmsrst(struct dvb_frontend *fe)
{
- struct qm1d1c0042_state *state;
- struct qm1d1c0042_config *cfg;
-
- state = fe->tuner_priv;
- cfg = priv_cfg;
+ u8 data = 0x01;

- if (cfg->fe)
- state->cfg.fe = cfg->fe;
+ return qm1d1c0042_fe_write_data(fe, 0x03, &data, 1);
+}

- if (cfg->xtal_freq != QM1D1C0042_CFG_XTAL_DFLT)
- dev_warn(&state->i2c->dev,
- "(%s) changing xtal_freq not supported. ", __func__);
- state->cfg.xtal_freq = default_cfg.xtal_freq;
+enum qm1d1c0042_agc {
+ QM1D1C0042_AGC_AUTO,
+ QM1D1C0042_AGC_MANUAL,
+};

- state->cfg.lpf = cfg->lpf;
- state->cfg.fast_srch = cfg->fast_srch;
+int qm1d1c0042_set_agc(struct dvb_frontend *fe, enum qm1d1c0042_agc agc)
+{
+ u8 data = (agc == QM1D1C0042_AGC_AUTO) ? 0xff : 0x00;
+ int err = qm1d1c0042_fe_write_data(fe, 0x0a, &data, 1);
+
+ if (err)
+ return err;
+ data = 0xb0 | ((agc == QM1D1C0042_AGC_AUTO) ? 1 : 0);
+ err = qm1d1c0042_fe_write_data(fe, 0x10, &data, 1);
+ if (err)
+ return err;
+
+ data = (agc == QM1D1C0042_AGC_AUTO) ? 0x40 : 0x00;
+ return (err = qm1d1c0042_fe_write_data(fe, 0x11, &data, 1)) ? err : qm1d1c0042_write_pskmsrst(fe);
+}

- if (cfg->lpf_wait != QM1D1C0042_CFG_WAIT_DFLT)
- state->cfg.lpf_wait = cfg->lpf_wait;
- else
- state->cfg.lpf_wait = default_cfg.lpf_wait;
+int qm1d1c0042_sleep(struct dvb_frontend *fe)
+{
+ struct qm1d1c0042 *qm = fe->tuner_priv;
+ u8 buf = 1;
+
+ dev_dbg(fe->dvb->device, "#%d %s\n", qm->idx, __func__);
+ qm->reg[0x01] &= (~(1 << 3)) & 0xff;
+ qm->reg[0x01] |= 1 << 0;
+ qm->reg[0x05] |= 1 << 3;
+ return qm1d1c0042_set_agc(fe, QM1D1C0042_AGC_MANUAL) ||
+ qm1d1c0042_write(fe, 0x05, qm->reg[0x05]) ||
+ qm1d1c0042_write(fe, 0x01, qm->reg[0x01]) ||
+ qm1d1c0042_fe_write_data(fe, 0x17, &buf, 1);
+}

- if (cfg->fast_srch_wait != QM1D1C0042_CFG_WAIT_DFLT)
- state->cfg.fast_srch_wait = cfg->fast_srch_wait;
- else
- state->cfg.fast_srch_wait = default_cfg.fast_srch_wait;
+int qm1d1c0042_wakeup(struct dvb_frontend *fe)
+{
+ struct qm1d1c0042 *qm = fe->tuner_priv;
+ u8 buf = 0;
+
+ dev_dbg(fe->dvb->device, "#%d %s\n", qm->idx, __func__);
+ qm->reg[0x01] |= 1 << 3;
+ qm->reg[0x01] &= (~(1 << 0)) & 0xff;
+ qm->reg[0x05] &= (~(1 << 3)) & 0xff;
+ return qm1d1c0042_fe_write_data(fe, 0x17, &buf, 1) ||
+ qm1d1c0042_write(fe, 0x01, qm->reg[0x01]) ||
+ qm1d1c0042_write(fe, 0x05, qm->reg[0x05]);
+}

- if (cfg->normal_srch_wait != QM1D1C0042_CFG_WAIT_DFLT)
- state->cfg.normal_srch_wait = cfg->normal_srch_wait;
- else
- state->cfg.normal_srch_wait = default_cfg.normal_srch_wait;
- return 0;
+void qm1d1c0042_ch2freq(u32 channel, u32 *number, u32 *freq)
+{
+ if (channel < 12) {
+ *number = 1 + 2 * channel;
+ *freq = 104948 + 3836 * channel;
+ } else if (channel < 24) {
+ channel -= 12;
+ *number = 2 + 2 * channel;
+ *freq = 161300 + 4000 * channel;
+ } else {
+ channel -= 24;
+ *number = 1 + 2 * channel;
+ *freq = 159300 + 4000 * channel;
+ }
}

-/* divisor, vco_band parameters */
-/* {maxfreq, param1(band?), param2(div?) */
-static const u32 conv_table[9][3] = {
+static const u32 qm1d1c0042_freq_tab[9][3] = {
{ 2151000, 1, 7 },
{ 1950000, 1, 6 },
{ 1800000, 1, 5 },
@@ -175,274 +164,233 @@ static const u32 conv_table[9][3] = {
{ 950000, 0, 0 }
};

-static int qm1d1c0042_set_params(struct dvb_frontend *fe)
+static const u32 qm1d1c0042_sd_tab[24][3] = {
+ {0x38fae1, 0x0d, 0x5},
+ {0x3f570a, 0x0e, 0x3},
+ {0x05b333, 0x0e, 0x5},
+ {0x3c0f5c, 0x0f, 0x4},
+ {0x026b85, 0x0f, 0x6},
+ {0x38c7ae, 0x10, 0x5},
+ {0x3f23d7, 0x11, 0x3},
+ {0x058000, 0x11, 0x5},
+ {0x3bdc28, 0x12, 0x4},
+ {0x023851, 0x12, 0x6},
+ {0x38947a, 0x13, 0x5},
+ {0x3ef0a3, 0x14, 0x3},
+ {0x3c8000, 0x16, 0x4},
+ {0x048000, 0x16, 0x6},
+ {0x3c8000, 0x17, 0x5},
+ {0x048000, 0x18, 0x3},
+ {0x3c8000, 0x18, 0x6},
+ {0x048000, 0x19, 0x4},
+ {0x3c8000, 0x1a, 0x3},
+ {0x048000, 0x1a, 0x5},
+ {0x3c8000, 0x1b, 0x4},
+ {0x048000, 0x1b, 0x6},
+ {0x3c8000, 0x1c, 0x5},
+ {0x048000, 0x1d, 0x3},
+};
+
+static int qm1d1c0042_tuning(struct qm1d1c0042 *qm, u32 *sd, u32 channel)
{
- struct qm1d1c0042_state *state;
- u32 freq;
- int i, ret;
- u8 val, mask;
- u32 a, sd;
- s32 b;
-
- state = fe->tuner_priv;
- freq = fe->dtv_property_cache.frequency;
-
- state->regs[0x08] &= 0xf0;
- state->regs[0x08] |= 0x09;
-
- state->regs[0x13] &= 0x9f;
- state->regs[0x13] |= 0x20;
-
- /* div2/vco_band */
- val = state->regs[0x02] & 0x0f;
- for (i = 0; i < 8; i++)
- if (freq < conv_table[i][0] && freq >= conv_table[i + 1][0]) {
- val |= conv_table[i][1] << 7;
- val |= conv_table[i][2] << 4;
- break;
+ int ret;
+ struct dvb_frontend *fe = qm->fe;
+ u8 i_data;
+ u32 i, N, A;
+
+ qm->reg[0x08] &= 0xf0;
+ qm->reg[0x08] |= 0x09;
+
+ qm->reg[0x13] &= 0x9f;
+ qm->reg[0x13] |= 0x20;
+
+ for (i = 0; i < 8; i++) {
+ if ((qm1d1c0042_freq_tab[i+1][0] <= qm->freq) && (qm->freq < qm1d1c0042_freq_tab[i][0])) {
+ i_data = qm->reg[0x02];
+ i_data &= 0x0f;
+ i_data |= qm1d1c0042_freq_tab[i][1] << 7;
+ i_data |= qm1d1c0042_freq_tab[i][2] << 4;
+ qm1d1c0042_write(fe, 0x02, i_data);
}
- ret = reg_write(state, 0x02, val);
- if (ret < 0)
- return ret;
-
- a = (freq + state->cfg.xtal_freq / 2) / state->cfg.xtal_freq;
-
- state->regs[0x06] &= 0x40;
- state->regs[0x06] |= (a - 12) / 4;
- ret = reg_write(state, 0x06, state->regs[0x06]);
- if (ret < 0)
- return ret;
-
- state->regs[0x07] &= 0xf0;
- state->regs[0x07] |= (a - 4 * ((a - 12) / 4 + 1) - 5) & 0x0f;
- ret = reg_write(state, 0x07, state->regs[0x07]);
- if (ret < 0)
- return ret;
-
- /* LPF */
- val = state->regs[0x08];
- if (state->cfg.lpf) {
- /* LPF_CLK, LPF_FC */
- val &= 0xf0;
- val |= 0x02;
}
- ret = reg_write(state, 0x08, val);
- if (ret < 0)
- return ret;

- /*
- * b = (freq / state->cfg.xtal_freq - a) << 20;
- * sd = b (b >= 0)
- * 1<<22 + b (b < 0)
- */
- b = (s32)div64_s64(((s64) freq) << 20, state->cfg.xtal_freq)
- - (((s64) a) << 20);
+ *sd = qm1d1c0042_sd_tab[channel][0];
+ N = qm1d1c0042_sd_tab[channel][1];
+ A = qm1d1c0042_sd_tab[channel][2];

- if (b >= 0)
- sd = b;
- else
- sd = (1 << 22) + b;
-
- state->regs[0x09] &= 0xc0;
- state->regs[0x09] |= (sd >> 16) & 0x3f;
- state->regs[0x0a] = (sd >> 8) & 0xff;
- state->regs[0x0b] = sd & 0xff;
- ret = reg_write(state, 0x09, state->regs[0x09]);
- if (ret == 0)
- ret = reg_write(state, 0x0a, state->regs[0x0a]);
- if (ret == 0)
- ret = reg_write(state, 0x0b, state->regs[0x0b]);
- if (ret != 0)
- return ret;
-
- if (!state->cfg.lpf) {
- /* CSEL_Offset */
- ret = reg_write(state, 0x13, state->regs[0x13]);
- if (ret < 0)
- return ret;
- }
-
- /* VCO_TM, LPF_TM */
- mask = state->cfg.lpf ? 0x3f : 0x7f;
- val = state->regs[0x0c] & mask;
- ret = reg_write(state, 0x0c, val);
- if (ret < 0)
- return ret;
- usleep_range(2000, 3000);
- val = state->regs[0x0c] | ~mask;
- ret = reg_write(state, 0x0c, val);
- if (ret < 0)
+ qm->reg[0x06] &= 0x40;
+ qm->reg[0x06] |= N;
+ ret = qm1d1c0042_write(fe, 0x06, qm->reg[0x06]);
+ if (ret)
return ret;

- if (state->cfg.lpf)
- msleep(state->cfg.lpf_wait);
- else if (state->regs[0x03] & 0x01)
- msleep(state->cfg.fast_srch_wait);
- else
- msleep(state->cfg.normal_srch_wait);
-
- if (state->cfg.lpf) {
- /* LPF_FC */
- ret = reg_write(state, 0x08, 0x09);
- if (ret < 0)
- return ret;
-
- /* CSEL_Offset */
- ret = reg_write(state, 0x13, state->regs[0x13]);
- if (ret < 0)
- return ret;
- }
- return 0;
+ qm->reg[0x07] &= 0xf0;
+ qm->reg[0x07] |= A & 0x0f;
+ return qm1d1c0042_write(fe, 0x07, qm->reg[0x07]);
}

-static int qm1d1c0042_sleep(struct dvb_frontend *fe)
+static int qm1d1c0042_local_lpf_tuning(struct qm1d1c0042 *qm, u32 channel)
{
- struct qm1d1c0042_state *state;
- int ret;
-
- state = fe->tuner_priv;
- state->regs[0x01] &= (~(1 << 3)) & 0xff; /* BB_Reg_disable */
- state->regs[0x01] |= 1 << 0; /* STDBY */
- state->regs[0x05] |= 1 << 3; /* pfd_rst STANDBY */
- ret = reg_write(state, 0x05, state->regs[0x05]);
- if (ret == 0)
- ret = reg_write(state, 0x01, state->regs[0x01]);
- if (ret < 0)
- dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
- __func__, fe->dvb->num, fe->id);
- return ret;
+ struct dvb_frontend *fe = qm->fe;
+ u8 i_data;
+ u32 sd = 0;
+ int err = qm1d1c0042_tuning(qm, &sd, channel);
+
+ if (err)
+ return err;
+ i_data = qm->reg[0x08] & 0xf0;
+ i_data |= 2;
+ err = qm1d1c0042_write(fe, 0x08, i_data);
+ if (err)
+ return err;
+
+ qm->reg[0x09] &= 0xc0;
+ qm->reg[0x09] |= (sd >> 16) & 0x3f;
+ qm->reg[0x0a] = (sd >> 8) & 0xff;
+ qm->reg[0x0b] = (sd >> 0) & 0xff;
+ err = qm1d1c0042_write(fe, 0x09, qm->reg[0x09]) ||
+ qm1d1c0042_write(fe, 0x0a, qm->reg[0x0a]) ||
+ qm1d1c0042_write(qm->fe, 0x0b, qm->reg[0x0b]);
+ if (err)
+ return err;
+
+ i_data = qm->reg[0x0c];
+ i_data &= 0x3f;
+ err = qm1d1c0042_write(fe, 0x0c, i_data);
+ if (err)
+ return err;
+ msleep_interruptible(1);
+
+ i_data = qm->reg[0x0c];
+ i_data |= 0xc0;
+ return qm1d1c0042_write(fe, 0x0c, i_data) ||
+ qm1d1c0042_write(fe, 0x08, 0x09) ||
+ qm1d1c0042_write(fe, 0x13, qm->reg[0x13]);
}

-static int qm1d1c0042_init(struct dvb_frontend *fe)
+int qm1d1c0042_get_locked(struct qm1d1c0042 *qm, bool *locked)
{
- struct qm1d1c0042_state *state;
- u8 val;
- int i, ret;
-
- state = fe->tuner_priv;
- memcpy(state->regs, reg_initval, sizeof(reg_initval));
+ int err = qm1d1c0042_fe_read(qm->fe, 0x0d, &qm->reg[0x0d]);

- reg_write(state, 0x01, 0x0c);
- reg_write(state, 0x01, 0x0c);
-
- ret = reg_write(state, 0x01, 0x0c); /* soft reset on */
- if (ret < 0)
- goto failed;
- usleep_range(2000, 3000);
-
- val = state->regs[0x01] | 0x10;
- ret = reg_write(state, 0x01, val); /* soft reset off */
- if (ret < 0)
- goto failed;
+ if (err)
+ return err;
+ if (qm->reg[0x0d] & 0x40)
+ *locked = true;
+ else
+ *locked = false;
+ return err;
+}

- /* check ID */
- ret = reg_read(state, 0x00, &val);
- if (ret < 0 || val != 0x48)
- goto failed;
- usleep_range(2000, 3000);
+u32 qm1d1c0042_freq2ch(u32 frequency)
+{
+ u32 freq = frequency / 10,
+ ch0 = (freq - 104948) / 3836, diff0 = freq - (104948 + 3836 * ch0),
+ ch1 = (freq - 161300) / 4000, diff1 = freq - (161300 + 4000 * ch1),
+ ch2 = (freq - 159300) / 4000, diff2 = freq - (159300 + 4000 * ch2),
+ min = diff0 < diff1 ? diff0 : diff1;
+
+ if (frequency < 1024)
+ return frequency; /* consider as channel ID if low */
+ if (diff2 < min)
+ return ch2 + 24;
+ if (min == diff1)
+ return ch1 + 12;
+ return ch0;
+}

- state->regs[0x0c] |= 0x40;
- ret = reg_write(state, 0x0c, state->regs[0x0c]);
- if (ret < 0)
- goto failed;
- msleep(state->cfg.lpf_wait);
-
- /* set all writable registers */
- for (i = 1; i <= 0x0c ; i++) {
- ret = reg_write(state, i, state->regs[i]);
- if (ret < 0)
- goto failed;
- }
- for (i = 0x11; i < QM1D1C0042_NUM_REGS; i++) {
- ret = reg_write(state, i, state->regs[i]);
- if (ret < 0)
- goto failed;
+int qm1d1c0042_set_freq(struct dvb_frontend *fe, u32 frequency)
+{
+ struct qm1d1c0042 *qm = fe->tuner_priv;
+ u32 channel = qm1d1c0042_freq2ch(frequency);
+ u32 number, freq;
+ bool locked = false;
+ unsigned long timeout;
+ int err = qm1d1c0042_set_agc(fe, QM1D1C0042_AGC_MANUAL);
+
+ if (err)
+ return err;
+ qm1d1c0042_ch2freq(channel, &number, &freq);
+ qm->freq = freq * 10 - 500;
+ dev_dbg(fe->dvb->device, "#%d ch %d freq %d kHz\n", qm->idx, channel, qm->freq);
+
+ err = qm1d1c0042_local_lpf_tuning(qm, channel);
+ if (err)
+ return err;
+
+ timeout = jiffies + msecs_to_jiffies(1000); /* 1s */
+ while (time_before(jiffies, timeout)) {
+ err = qm1d1c0042_get_locked(qm, &locked);
+ if (err)
+ return err;
+ if (locked)
+ break;
+ msleep_interruptible(1);
}
-
- ret = qm1d1c0042_wakeup(state);
- if (ret < 0)
- goto failed;
-
- ret = qm1d1c0042_set_srch_mode(state, state->cfg.fast_srch);
- if (ret < 0)
- goto failed;
-
- return ret;
-
-failed:
- dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
- __func__, fe->dvb->num, fe->id);
- return ret;
+ dev_dbg(fe->dvb->device, "#%d %s %s\n", qm->idx, __func__, locked ? "LOCKED" : "TIMEOUT");
+ return locked ? qm1d1c0042_set_agc(fe, QM1D1C0042_AGC_AUTO) : -ETIMEDOUT;
}

-/* I2C driver functions */
-
-static const struct dvb_tuner_ops qm1d1c0042_ops = {
+static struct dvb_tuner_ops qm1d1c0042_ops = {
.info = {
- .name = "Sharp QM1D1C0042",
-
- .frequency_min = 950000,
- .frequency_max = 2150000,
+ .frequency_min = 1, /* freq under 1024 kHz is handled as channel */
+ .frequency_max = 2150000, /* kHz */
+ .frequency_step = 1000, /* = 1 MHz */
},
-
- .init = qm1d1c0042_init,
+ .set_frequency = qm1d1c0042_set_freq,
.sleep = qm1d1c0042_sleep,
- .set_config = qm1d1c0042_set_config,
- .set_params = qm1d1c0042_set_params,
+ .init = qm1d1c0042_wakeup,
};

-
-static int qm1d1c0042_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+int qm1d1c0042_remove(struct i2c_client *client)
{
- struct qm1d1c0042_state *state;
- struct qm1d1c0042_config *cfg;
- struct dvb_frontend *fe;
-
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
- state->i2c = client;
+ struct dvb_frontend *fe = i2c_get_clientdata(client);

- cfg = client->dev.platform_data;
- fe = cfg->fe;
- fe->tuner_priv = state;
- qm1d1c0042_set_config(fe, cfg);
- memcpy(&fe->ops.tuner_ops, &qm1d1c0042_ops, sizeof(qm1d1c0042_ops));
-
- i2c_set_clientdata(client, &state->cfg);
- dev_info(&client->dev, "Sharp QM1D1C0042 attached.\n");
+ dev_dbg(&client->dev, "%s\n", __func__);
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
return 0;
}

-static int qm1d1c0042_remove(struct i2c_client *client)
+int qm1d1c0042_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
- struct qm1d1c0042_state *state;
+ u8 d[] = { 0x10, 0x15, 0x04 };
+ struct dvb_frontend *fe = client->dev.platform_data;
+ struct qm1d1c0042 *qm = kzalloc(sizeof(struct qm1d1c0042), GFP_KERNEL);

- state = cfg_to_state(i2c_get_clientdata(client));
- state->cfg.fe->tuner_priv = NULL;
- kfree(state);
- return 0;
+ if (!qm)
+ return -ENOMEM;
+ fe->tuner_priv = qm;
+ qm->fe = fe;
+ qm->idx = !(client->addr & 1);
+ qm->addr_tuner = client->addr;
+ qm->read = fe->ops.tuner_ops.calc_regs;
+ memcpy(&fe->ops.tuner_ops, &qm1d1c0042_ops, sizeof(struct dvb_tuner_ops));
+ memcpy(qm->reg, qm1d1c0042_reg_rw, sizeof(qm1d1c0042_reg_rw));
+ qm->freq = 0;
+ i2c_set_clientdata(client, fe);
+ return qm1d1c0042_fe_write_data(fe, 0x1e, d, 1) ||
+ qm1d1c0042_fe_write_data(fe, 0x1c, d+1, 1) ||
+ qm1d1c0042_fe_write_data(fe, 0x1f, d+2, 1);
}

-
-static const struct i2c_device_id qm1d1c0042_id[] = {
- {"qm1d1c0042", 0},
- {}
+static struct i2c_device_id qm1d1c0042_id_table[] = {
+ { QM1D1C0042_DRVNAME, 0 },
+ { },
};
-MODULE_DEVICE_TABLE(i2c, qm1d1c0042_id);
+MODULE_DEVICE_TABLE(i2c, qm1d1c0042_id_table);

static struct i2c_driver qm1d1c0042_driver = {
.driver = {
- .name = "qm1d1c0042",
+ .owner = THIS_MODULE,
+ .name = qm1d1c0042_id_table->name,
},
.probe = qm1d1c0042_probe,
.remove = qm1d1c0042_remove,
- .id_table = qm1d1c0042_id,
+ .id_table = qm1d1c0042_id_table,
};
-
module_i2c_driver(qm1d1c0042_driver);

-MODULE_DESCRIPTION("Sharp QM1D1C0042 tuner");
-MODULE_AUTHOR("Akihiro TSUKADA");
+MODULE_AUTHOR("Budi Rachmanto, AreMa Inc. <knightrider(@)are.ma>");
+MODULE_DESCRIPTION("Earthsoft PT3 QM1D1C0042 ISDB-S tuner driver");
MODULE_LICENSE("GPL");
+
--
1.8.4.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/