All of lore.kernel.org
 help / color / mirror / Atom feed
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, &reg[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

  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.