[media 6/7] Sharp QM1D1C0042 ISDB-S tuner

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Буди Романто, AreMa Inc <knightrider@xxxxxx>

Signed-off-by: Буди Романто, AreMa Inc <knightrider@xxxxxx>
---
 drivers/media/tuners/qm1d1c0042.c | 246 ++++++++++++++++++++++++++++++++++++++
 drivers/media/tuners/qm1d1c0042.h |  23 ++++
 2 files changed, 269 insertions(+)
 create mode 100644 drivers/media/tuners/qm1d1c0042.c
 create mode 100644 drivers/media/tuners/qm1d1c0042.h

diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c
new file mode 100644
index 0000000..19754d2
--- /dev/null
+++ b/drivers/media/tuners/qm1d1c0042.c
@@ -0,0 +1,246 @@
+/*
+ * Sharp VA4M6JC2103 - Earthsoft PT3 ISDB-S tuner driver QM1D1C0042
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@xxxxxx>
+ *
+ * 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 "qm1d1c0042.h"
+
+struct qm1d1c0042 {
+	struct i2c_adapter *i2c;
+	u8 adr_tuner, reg[32];
+};
+
+bool qm1d1c0042_r(struct dvb_frontend *fe, u8 slvadr, u8 *dat)
+{
+	struct qm1d1c0042 *t = fe->tuner_priv;
+	u8		buf[]	= {0xFE, t->adr_tuner << 1, slvadr, 0xFE, (t->adr_tuner << 1) | 1, 0};
+	struct i2c_msg	msg[]	= {
+		{.addr = fe->id,	.flags = 0,		.buf = buf,	.len = 3,},
+		{.addr = fe->id,	.flags = 0,		.buf = buf + 3,	.len = 2,},
+		{.addr = fe->id,	.flags = I2C_M_RD,	.buf = buf + 5,	.len = 1,},
+	};
+	bool	ret = i2c_transfer(t->i2c, msg, 3) == 3;
+
+	*dat = buf[5];
+	return ret;
+}
+
+int qm1d1c0042_w(struct dvb_frontend *fe, u8 slvadr, u8 *dat, int len)
+{
+	u8 buf[len + 1];
+	struct qm1d1c0042 *t = fe->tuner_priv;
+	struct i2c_msg msg[] = {
+		{.addr = fe->id,	.flags = 0,	.buf = buf,	.len = len + 1,},
+	};
+
+	buf[0] = slvadr;
+	memcpy(buf + 1, dat, len);
+	return i2c_transfer(t->i2c, msg, 1) == 1 ? 0 : -EIO;
+}
+
+int qm1d1c0042_w_tuner(struct dvb_frontend *fe, u8 adr, u8 dat)
+{
+	struct qm1d1c0042 *t = fe->tuner_priv;
+	u8 buf[] = {t->adr_tuner << 1, adr, dat};
+	int err = qm1d1c0042_w(fe, 0xFE, buf, 3);
+
+	t->reg[adr] = dat;
+	return err;
+}
+
+enum qm1d1c0042_agc {
+	QM1D1C0042_AGC_AUTO,
+	QM1D1C0042_AGC_MANUAL,
+};
+
+int qm1d1c0042_set_agc(struct dvb_frontend *fe, enum qm1d1c0042_agc agc)
+{
+	u8	dat		= (agc == QM1D1C0042_AGC_AUTO) ? 0xff : 0x00,
+		pskmsrst	= 0x01;
+	int	err		= qm1d1c0042_w(fe, 0x0a, &dat, 1);
+
+	if (err)
+		return err;
+	dat = 0xb0 | (agc == QM1D1C0042_AGC_AUTO ? 1 : 0);
+	err = qm1d1c0042_w(fe, 0x10, &dat, 1);
+	if (err)
+		return err;
+	dat = (agc == QM1D1C0042_AGC_AUTO) ? 0x40 : 0x00;
+	return	(err = qm1d1c0042_w(fe, 0x11, &dat, 1)) ?
+		err : qm1d1c0042_w(fe, 0x03, &pskmsrst, 1);
+}
+
+int qm1d1c0042_sleep(struct dvb_frontend *fe)
+{
+	struct qm1d1c0042 *t = fe->tuner_priv;
+	u8 buf = 1;
+
+	t->reg[0x01] &= (~(1 << 3)) & 0xff;
+	t->reg[0x01] |= 1 << 0;
+	t->reg[0x05] |= 1 << 3;
+	return	qm1d1c0042_set_agc(fe, QM1D1C0042_AGC_MANUAL)	||
+		qm1d1c0042_w_tuner(fe, 0x05, t->reg[0x05])	||
+		qm1d1c0042_w_tuner(fe, 0x01, t->reg[0x01])	||
+		qm1d1c0042_w(fe, 0x17, &buf, 1);
+}
+
+int qm1d1c0042_wakeup(struct dvb_frontend *fe)
+{
+	struct qm1d1c0042 *t = fe->tuner_priv;
+	u8 buf = 0;
+
+	t->reg[0x01] |= 1 << 3;
+	t->reg[0x01] &= (~(1 << 0)) & 0xff;
+	t->reg[0x05] &= (~(1 << 3)) & 0xff;
+	return	qm1d1c0042_w(fe, 0x17, &buf, 1)			||
+		qm1d1c0042_w_tuner(fe, 0x01, t->reg[0x01])	||
+		qm1d1c0042_w_tuner(fe, 0x05, t->reg[0x05]);
+}
+
+int qm1d1c0042_tune(struct dvb_frontend *fe)
+{
+	u32 freq2ch(u32 kHz)
+	{
+		u32 freq = kHz / 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 (diff2 < min)
+			return ch2 + 24;
+		if (min == diff1)
+			return ch1 + 12;
+		return ch0;
+	}
+
+	struct qm1d1c0042	*t	= fe->tuner_priv;
+	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}
+	};
+	u32	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},
+	};
+	int	err	= qm1d1c0042_set_agc(fe, QM1D1C0042_AGC_MANUAL);
+	u32	kHz	= fe->dtv_property_cache.frequency,
+		ch	= freq2ch(kHz),
+		N	= sd_tab[ch][1],
+		A	= sd_tab[ch][2],
+		sd	= 0,
+		i;
+
+	if (err || ch >= 24)
+		return -EIO;
+	t->reg[0x08] &= 0xf0;
+	t->reg[0x08] |= 0x09;
+	t->reg[0x13] &= 0x9f;
+	t->reg[0x13] |= 0x20;
+	for (i = 0; i < 8; i++)
+		if ((fgap_tab[i+1][0] <= kHz - 500) && (kHz - 500 < fgap_tab[i][0]))
+			qm1d1c0042_w_tuner(fe, 0x02, (t->reg[0x02] & 0x0f) | fgap_tab[i][1] << 7 | fgap_tab[i][2] << 4);
+	sd = sd_tab[ch][0];
+	t->reg[0x06] &= 0x40;
+	t->reg[0x06] |= N;
+	t->reg[0x07] &= 0xf0;
+	t->reg[0x07] |= A & 0x0f;
+	err =	qm1d1c0042_w_tuner(fe, 0x06, t->reg[0x06])	||
+		qm1d1c0042_w_tuner(fe, 0x07, t->reg[0x07])	||
+		qm1d1c0042_w_tuner(fe, 0x08, (t->reg[0x08] & 0xf0) | 2);
+	if (err)
+		return err;
+	t->reg[0x09] &= 0xc0;
+	t->reg[0x09] |= (sd >> 16) & 0x3f;
+	t->reg[0x0a] = (sd >> 8) & 0xff;
+	t->reg[0x0b] = (sd >> 0) & 0xff;
+	err =	qm1d1c0042_w_tuner(fe, 0x09, t->reg[0x09])	||
+		qm1d1c0042_w_tuner(fe, 0x0a, t->reg[0x0a])	||
+		qm1d1c0042_w_tuner(fe, 0x0b, t->reg[0x0b])	||
+		qm1d1c0042_w_tuner(fe, 0x0c, t->reg[0x0c] & 0x3f);
+	if (err)
+		return err;
+	msleep_interruptible(1);
+	err =	qm1d1c0042_w_tuner(fe, 0x0c, t->reg[0x0c] | 0xc0)	||
+		qm1d1c0042_w_tuner(fe, 0x08, 0x09)			||
+		qm1d1c0042_w_tuner(fe, 0x13, t->reg[0x13]);
+	if (err)
+		return err;
+	for (i = 0; i < 500; i++) {
+		if (!qm1d1c0042_r(fe, 0x0d, &t->reg[0x0d]))
+			return -EIO;
+		if (t->reg[0x0d] & 0x40)	/* locked */
+			return qm1d1c0042_set_agc(fe, QM1D1C0042_AGC_AUTO);
+		msleep_interruptible(1);
+	}
+	return -ETIMEDOUT;
+}
+
+static struct dvb_tuner_ops qm1d1c0042_ops = {
+	.set_params	= qm1d1c0042_tune,
+	.sleep		= qm1d1c0042_sleep,
+	.init		= qm1d1c0042_wakeup,
+};
+
+int qm1d1c0042_remove(struct i2c_client *c)
+{
+	kfree(i2c_get_clientdata(c));
+	return 0;
+}
+
+int qm1d1c0042_probe(struct i2c_client *c, const struct i2c_device_id *id)
+{
+	u8 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,
+	};
+	u8	d[] = {0x10, 0x15, 0x04};
+	struct dvb_frontend	*fe	= c->dev.platform_data;
+	struct qm1d1c0042	*t	= kzalloc(sizeof(struct qm1d1c0042), GFP_KERNEL);
+
+	if (!t)
+		return -ENOMEM;
+	fe->tuner_priv	= t;
+	t->i2c		= c->adapter;
+	t->adr_tuner	= c->addr;
+	memcpy(&fe->ops.tuner_ops, &qm1d1c0042_ops, sizeof(struct dvb_tuner_ops));
+	memcpy(t->reg, reg_rw, sizeof(reg_rw));
+	i2c_set_clientdata(c, t);
+	return	qm1d1c0042_w(fe, 0x1e, d,   1)	||
+		qm1d1c0042_w(fe, 0x1c, d+1, 1)	||
+		qm1d1c0042_w(fe, 0x1f, d+2, 1);
+}
+
+static struct i2c_device_id qm1d1c0042_id[] = {
+	{QM1D1C0042_MODNAME, 0},
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, qm1d1c0042_id);
+
+static struct i2c_driver qm1d1c0042_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= qm1d1c0042_id->name,
+	},
+	.probe		= qm1d1c0042_probe,
+	.remove		= qm1d1c0042_remove,
+	.id_table	= qm1d1c0042_id,
+};
+module_i2c_driver(qm1d1c0042_driver);
+
+MODULE_AUTHOR("Budi Rachmanto, AreMa Inc. <knightrider(@)are.ma>");
+MODULE_DESCRIPTION("Earthsoft PT3 QM1D1C0042 ISDB-S tuner driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/tuners/qm1d1c0042.h b/drivers/media/tuners/qm1d1c0042.h
new file mode 100644
index 0000000..b342b82
--- /dev/null
+++ b/drivers/media/tuners/qm1d1c0042.h
@@ -0,0 +1,23 @@
+/*
+ * Sharp VA4M6JC2103 - Earthsoft PT3 ISDB-S tuner driver QM1D1C0042
+ *
+ * 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef QM1D1C0042_H
+#define QM1D1C0042_H
+
+#define QM1D1C0042_MODNAME "qm1d1c0042"
+
+#endif
+
-- 
2.3.10

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



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux