From: info@are.ma
To: linux-media@vger.kernel.org
Cc: "Буди Романто, AreMa Inc" <knightrider@are.ma>,
linux-kernel@vger.kernel.org, crope@iki.fi, m.chehab@samsung.com,
mchehab@osg.samsung.com, hdegoede@redhat.com,
laurent.pinchart@ideasonboard.com, mkrufky@linuxtv.org,
sylvester.nawrocki@gmail.com, g.liakhovetski@gmx.de,
peter.senna@gmail.com
Subject: [media 6/8] Sharp QM1D1C004x ISDB-S tuner driver for PT3 and PX-BCUD
Date: Fri, 1 Apr 2016 04:42:30 +0900 [thread overview]
Message-ID: <14864a3616de0610f2343924584c71f4f26909f4.1459450632.git.knightrider@are.ma> (raw)
In-Reply-To: <cover.1459450632.git.knightrider@are.ma>
In-Reply-To: <cover.1459450632.git.knightrider@are.ma>
From: Буди Романто, AreMa Inc <knightrider@are.ma>
Signed-off-by: Буди Романто, AreMa Inc <knightrider@are.ma>
---
drivers/media/tuners/qm1d1c004x.c | 242 ++++++++++++++++++++++++++++++++++++++
drivers/media/tuners/qm1d1c004x.h | 23 ++++
2 files changed, 265 insertions(+)
create mode 100644 drivers/media/tuners/qm1d1c004x.c
create mode 100644 drivers/media/tuners/qm1d1c004x.h
diff --git a/drivers/media/tuners/qm1d1c004x.c b/drivers/media/tuners/qm1d1c004x.c
new file mode 100644
index 0000000..843cfb2
--- /dev/null
+++ b/drivers/media/tuners/qm1d1c004x.c
@@ -0,0 +1,242 @@
+/*
+ Sharp VA4M6JC2103 QM1D1C004x ISDB-S tuner driver
+
+ Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+
+ CHIP VER. CARD
+ QM1D1C0042 0x48 Earthsoft PT3
+ QM1D1C0045 0x58
+ QM1D1C0045_2 0x68 PLEX PX-BCUD
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#include "dvb_frontend.h"
+#include "qm1d1c004x.h"
+
+struct qm1d1c004x {
+ u8 reg[32];
+};
+
+bool qm1d1c004x_r(struct dvb_frontend *fe, u8 slvadr, u8 *dat)
+{
+ struct i2c_client *d = fe->demodulator_priv,
+ *t = fe->tuner_priv;
+ u8 buf[] = {0xFE, t->addr << 1, slvadr, 0xFE, (t->addr << 1) | 1, 0};
+ struct i2c_msg msg[] = {
+ {.addr = d->addr, .flags = 0, .buf = buf, .len = 3,},
+ {.addr = d->addr, .flags = 0, .buf = buf + 3, .len = 2,},
+ {.addr = d->addr, .flags = I2C_M_RD, .buf = buf + 5, .len = 1,},
+ };
+ bool ret = i2c_transfer(d->adapter, msg, 3) == 3;
+
+ *dat = buf[5];
+ return ret;
+}
+
+int qm1d1c004x_w(struct dvb_frontend *fe, u8 slvadr, u8 *dat, int len)
+{
+ struct i2c_client *d = fe->demodulator_priv;
+ u8 buf[len + 1];
+ struct i2c_msg msg[] = {
+ {.addr = d->addr, .flags = 0, .buf = buf, .len = len + 1,},
+ };
+
+ buf[0] = slvadr;
+ memcpy(buf + 1, dat, len);
+ return i2c_transfer(d->adapter, msg, 1) == 1 ? 0 : -EIO;
+}
+
+int qm1d1c004x_w_tuner(struct dvb_frontend *fe, u8 adr, u8 dat)
+{
+ struct i2c_client *t = fe->tuner_priv;
+ struct qm1d1c004x *q = i2c_get_clientdata(t);
+ u8 buf[] = {t->addr << 1, adr, dat};
+ int err = qm1d1c004x_w(fe, 0xFE, buf, 3);
+
+ q->reg[adr] = dat;
+ return err;
+}
+
+enum qm1d1c004x_agc {
+ QM1D1C004X_AGC_AUTO,
+ QM1D1C004X_AGC_MANUAL,
+};
+
+int qm1d1c004x_set_agc(struct dvb_frontend *fe, enum qm1d1c004x_agc agc)
+{
+ u8 dat = (agc == QM1D1C004X_AGC_AUTO) ? 0xff : 0x00,
+ pskmsrst = 0x01;
+ int err = qm1d1c004x_w(fe, 0x0a, &dat, 1);
+
+ if (err)
+ return err;
+ dat = 0xb0 | (agc == QM1D1C004X_AGC_AUTO ? 1 : 0);
+ err = qm1d1c004x_w(fe, 0x10, &dat, 1);
+ if (err)
+ return err;
+ dat = (agc == QM1D1C004X_AGC_AUTO) ? 0x40 : 0x00;
+ return (err = qm1d1c004x_w(fe, 0x11, &dat, 1)) ?
+ err : qm1d1c004x_w(fe, 0x03, &pskmsrst, 1);
+}
+
+int qm1d1c004x_sleep(struct dvb_frontend *fe)
+{
+ u8 buf = 1,
+ *reg = ((struct qm1d1c004x *)fe->tuner_priv)->reg;
+
+ reg[0x01] &= (~(1 << 3)) & 0xff;
+ reg[0x01] |= 1 << 0;
+ reg[0x05] |= 1 << 3;
+ return qm1d1c004x_set_agc(fe, QM1D1C004X_AGC_MANUAL) ||
+ qm1d1c004x_w_tuner(fe, 0x05, reg[0x05]) ||
+ qm1d1c004x_w_tuner(fe, 0x01, reg[0x01]) ||
+ qm1d1c004x_w(fe, 0x17, &buf, 1);
+}
+
+int qm1d1c004x_wakeup(struct dvb_frontend *fe)
+{
+ u8 regs[][32] = {
+ { /* QM1D1C0042 Earthsoft PT3 */
+ 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,
+ }, { /* QM1D1C0045 untested! */
+ 0x58, 0x1C, 0xC0, 0x10, 0xBC, 0xC1, 0x15, 0x34, 0x06, 0x3e, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
+ 0x11, 0xFF, 0xF3, 0x00, 0x3E, 0x25, 0x5C, 0xD6, 0x55, 0x8F, 0x95, 0xF6, 0x36, 0xF2, 0x09, 0x00,
+ }, { /* QM1D1C0045_2 PLEX PX-BCUD */
+ 0x68, 0x1c, 0xc0, 0x10, 0xbc, 0xc1, 0x11, 0x33, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0xf3, 0x00, 0x3f, 0x25, 0x5c, 0xd6, 0x55, 0xcf, 0x95, 0xf6, 0x36, 0xf2, 0x09, 0x00,
+ }
+ },
+ *reg = ((struct qm1d1c004x *)i2c_get_clientdata(fe->tuner_priv))->reg,
+ dat = 0,
+ i;
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++) {
+ if (!qm1d1c004x_r(fe, 0, &dat))
+ return -EIO;
+ if (dat == regs[i][0])
+ break;
+ }
+ if (i == ARRAY_SIZE(regs))
+ return -ENOTSUPP;
+ memcpy(reg, regs[i], 32);
+ reg[0x01] |= 1 << 3;
+ reg[0x01] &= (~(1 << 0)) & 0xff;
+ reg[0x05] &= (~(1 << 3)) & 0xff;
+ dat = 0;
+ return qm1d1c004x_w(fe, 0x17, &dat, 1) ||
+ qm1d1c004x_w_tuner(fe, 0x01, reg[0x01]) ||
+ qm1d1c004x_w_tuner(fe, 0x05, reg[0x05]);
+}
+
+int qm1d1c004x_tune(struct dvb_frontend *fe)
+{
+ u32 fgap_tab[9][3] = {
+ {2151000, 1, 7}, {1950000, 1, 6}, {1800000, 1, 5},
+ {1600000, 1, 4}, {1450000, 1, 3}, {1250000, 1, 2},
+ {1200000, 0, 7}, { 975000, 0, 6}, { 950000, 0, 0}
+ };
+ u8 *reg = ((struct qm1d1c004x *)fe->tuner_priv)->reg;
+ u32 kHz = fe->dtv_property_cache.frequency - 500,
+ XtalkHz = 16000,
+ i = ((kHz + XtalkHz / 2) / XtalkHz) * XtalkHz;
+ s64 b = kHz - i;
+ u8 N = i / (4 * XtalkHz) - 3,
+ A = (i / XtalkHz) - 4 * (N + 1) - 5;
+ int sd = b < 0 ? (0x100000 / XtalkHz) * b + 0x400000 : (0x100000 / XtalkHz) * b,
+ err = qm1d1c004x_set_agc(fe, QM1D1C004X_AGC_MANUAL);
+
+ if (err)
+ return -EIO;
+
+ /* div2/vco_band */
+ for (i = 0; i < 8; i++)
+ if ((fgap_tab[i+1][0] <= kHz) && (kHz < fgap_tab[i][0]))
+ qm1d1c004x_w_tuner(fe, 0x02, (reg[0x02] & 0x0f) | fgap_tab[i][1] << 7 | fgap_tab[i][2] << 4);
+
+ reg[0x06] &= 0x40;
+ reg[0x06] |= N;
+ reg[0x07] &= 0xf0;
+ reg[0x07] |= A & 0x0f;
+
+ /* LPF */
+ reg[0x08] &= 0xf0;
+ reg[0x08] |= 0x09;
+ reg[0x13] &= 0x9f;
+ reg[0x13] |= 0x20;
+ err = qm1d1c004x_w_tuner(fe, 0x06, reg[0x06]) ||
+ qm1d1c004x_w_tuner(fe, 0x07, reg[0x07]) ||
+ qm1d1c004x_w_tuner(fe, 0x08, (reg[0x08] & 0xf0) | 2);
+ if (err)
+ return err;
+ reg[0x09] &= 0xc0;
+ reg[0x09] |= (sd >> 16) & 0x3f;
+ reg[0x0a] = (sd >> 8) & 0xff;
+ reg[0x0b] = (sd >> 0) & 0xff;
+ err = qm1d1c004x_w_tuner(fe, 0x09, reg[0x09]) ||
+ qm1d1c004x_w_tuner(fe, 0x0a, reg[0x0a]) ||
+ qm1d1c004x_w_tuner(fe, 0x0b, reg[0x0b]) ||
+ qm1d1c004x_w_tuner(fe, 0x0c, reg[0x0c] & 0x3f);
+ if (err)
+ return err;
+ msleep_interruptible(1);
+ err = qm1d1c004x_w_tuner(fe, 0x0c, reg[0x0c] | 0xc0) ||
+ qm1d1c004x_w_tuner(fe, 0x08, 0x09) ||
+ qm1d1c004x_w_tuner(fe, 0x13, reg[0x13]);
+ if (err)
+ return err;
+ for (i = 0; i < 500; i++) {
+ if (!qm1d1c004x_r(fe, 0x0d, ®[0x0d]))
+ return -EIO;
+ if (reg[0x0d] & 0x40) /* locked */
+ return qm1d1c004x_set_agc(fe, QM1D1C004X_AGC_AUTO);
+ msleep_interruptible(1);
+ }
+ return -ETIMEDOUT;
+}
+
+int qm1d1c004x_remove(struct i2c_client *t)
+{
+ kfree(i2c_get_clientdata(t));
+ return 0;
+}
+
+int qm1d1c004x_probe(struct i2c_client *t, const struct i2c_device_id *id)
+{
+ struct dvb_frontend *fe = t->dev.platform_data;
+ struct qm1d1c004x *q = kzalloc(sizeof(struct qm1d1c004x), GFP_KERNEL);
+ u8 d[] = {0x10, 0x15, 0x04};
+
+ if (!q)
+ return -ENOMEM;
+ i2c_set_clientdata(t, q);
+ fe->ops.tuner_ops.set_params = qm1d1c004x_tune;
+ fe->ops.tuner_ops.sleep = qm1d1c004x_sleep;
+ fe->ops.tuner_ops.init = qm1d1c004x_wakeup;
+ return qm1d1c004x_w(fe, 0x1e, d, 1) ||
+ qm1d1c004x_w(fe, 0x1c, d+1, 1) ||
+ qm1d1c004x_w(fe, 0x1f, d+2, 1);
+}
+
+static struct i2c_device_id qm1d1c004x_id[] = {
+ {QM1D1C004X_MODNAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, qm1d1c004x_id);
+
+static struct i2c_driver qm1d1c004x_driver = {
+ .driver.name = qm1d1c004x_id->name,
+ .probe = qm1d1c004x_probe,
+ .remove = qm1d1c004x_remove,
+ .id_table = qm1d1c004x_id,
+};
+module_i2c_driver(qm1d1c004x_driver);
+
+MODULE_AUTHOR("Budi Rachmanto, AreMa Inc. <knightrider(@)are.ma>");
+MODULE_DESCRIPTION("Earthsoft PT3 QM1D1C004X ISDB-S tuner driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/tuners/qm1d1c004x.h b/drivers/media/tuners/qm1d1c004x.h
new file mode 100644
index 0000000..2fdf9cb
--- /dev/null
+++ b/drivers/media/tuners/qm1d1c004x.h
@@ -0,0 +1,23 @@
+/*
+ Sharp VA4M6JC2103 QM1D1C004x ISDB-S tuner driver
+
+ Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+
+ CHIP VER. CARD
+ QM1D1C0042 0x48 Earthsoft PT3
+ QM1D1C0045 0x58
+ QM1D1C0045_2 0x68 PLEX PX-BCUD
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#ifndef QM1D1C004X_H
+#define QM1D1C004X_H
+
+#define QM1D1C004X_MODNAME "qm1d1c004x"
+
+#endif
+
--
2.7.4
next prev parent reply other threads:[~2016-03-31 19:42 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-03-31 19:15 [media 0/8] DVB driver for Earthsoft PT3, PLEX PX-Q3PE ISDB-S/T PCIE cards & PX-BCUD ISDB-S USB dongle info
2016-03-31 19:32 ` info
2016-03-31 19:42 ` [media 1/8] raise adapter number limit info
2016-03-31 19:42 ` [media 2/8] NXP tda2014x & Newport Media nm120/130/131 tuner (PXQ3PE) info
2016-04-01 4:08 ` kbuild test robot
2016-04-01 4:20 ` kbuild test robot
2016-03-31 19:42 ` [media 3/8] drop backstabbing drivers info
2016-03-31 19:42 ` [media 4/8] Toshiba TC905xx demodulator for PT3/PX-Q3PE/PX-BCUD info
2016-03-31 19:42 ` [media 5/8] MaxLinear MxL301RF ISDB-T tuner info
2016-03-31 19:42 ` info [this message]
2016-03-31 19:42 ` [media 7/8] PCIE bridge driver for PT3 & PX-Q3PE info
2016-04-01 3:47 ` kbuild test robot
2016-04-01 4:13 ` kbuild test robot
2016-04-01 14:27 ` kbuild test robot
2016-03-31 19:42 ` [media 8/8] Support for PLEX PX-BCUD (ISDB-S usb dongle) info
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=14864a3616de0610f2343924584c71f4f26909f4.1459450632.git.knightrider@are.ma \
--to=info@are.ma \
--cc=crope@iki.fi \
--cc=g.liakhovetski@gmx.de \
--cc=hdegoede@redhat.com \
--cc=knightrider@are.ma \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=m.chehab@samsung.com \
--cc=mchehab@osg.samsung.com \
--cc=mkrufky@linuxtv.org \
--cc=peter.senna@gmail.com \
--cc=sylvester.nawrocki@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.