* [media 1/7] raise adapter number limit
2016-02-15 6:08 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
@ 2016-02-15 6:08 ` info
2016-02-15 11:25 ` Peter Senna Tschudin
2016-02-15 6:08 ` [media 2/7] add NXP tda2014x & Newport Media nm120/130/131 tuners info
` (5 subsequent siblings)
6 siblings, 1 reply; 39+ messages in thread
From: info @ 2016-02-15 6:08 UTC (permalink / raw)
To: linux-media
Cc: Буди Романто, AreMa Inc,
linux-kernel, crope, m.chehab, mchehab, hdegoede,
laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski,
peter.senna
From: Буди Романто, AreMa Inc <knightrider@are.ma>
The current limit is too low for latest cards with 8+ tuners on a single slot, change to 64.
Signed-off-by: Буди Романто, AreMa Inc <knightrider@are.ma>
---
drivers/media/dvb-core/dvbdev.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/dvb-core/dvbdev.h b/drivers/media/dvb-core/dvbdev.h
index 4aff7bd..950decd 100644
--- a/drivers/media/dvb-core/dvbdev.h
+++ b/drivers/media/dvb-core/dvbdev.h
@@ -34,7 +34,7 @@
#if defined(CONFIG_DVB_MAX_ADAPTERS) && CONFIG_DVB_MAX_ADAPTERS > 0
#define DVB_MAX_ADAPTERS CONFIG_DVB_MAX_ADAPTERS
#else
- #define DVB_MAX_ADAPTERS 8
+ #define DVB_MAX_ADAPTERS 64
#endif
#define DVB_UNSET (-1)
--
2.3.10
^ permalink raw reply related [flat|nested] 39+ messages in thread* Re: [media 1/7] raise adapter number limit
2016-02-15 6:08 ` [media 1/7] raise adapter number limit info
@ 2016-02-15 11:25 ` Peter Senna Tschudin
0 siblings, 0 replies; 39+ messages in thread
From: Peter Senna Tschudin @ 2016-02-15 11:25 UTC (permalink / raw)
To: info
Cc: linux-media,
Буди Романто, AreMa Inc,
linux-kernel@vger.kernel.org, Antti Palosaari,
Mauro Carvalho Chehab, mchehab, Hans De Goede, Laurent Pinchart,
mkrufky, Sylwester Nawrocki, g.liakhovetski
On Mon, Feb 15, 2016 at 7:08 AM, <info@are.ma> wrote:
> From: Буди Романто, AreMa Inc <knightrider@are.ma>
>
> The current limit is too low for latest cards with 8+ tuners on a single slot, change to 64.
>
> Signed-off-by: Буди Романто, AreMa Inc <knightrider@are.ma>
I think that here goes only your name, not <name>, <company>.
^ permalink raw reply [flat|nested] 39+ messages in thread
* [media 2/7] add NXP tda2014x & Newport Media nm120/130/131 tuners
2016-02-15 6:08 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
2016-02-15 6:08 ` [media 1/7] raise adapter number limit info
@ 2016-02-15 6:08 ` info
2016-02-15 6:08 ` [media 3/7] drop backstabbing drivers info
` (4 subsequent siblings)
6 siblings, 0 replies; 39+ messages in thread
From: info @ 2016-02-15 6:08 UTC (permalink / raw)
To: linux-media
Cc: Буди Романто, AreMa Inc,
linux-kernel, crope, m.chehab, mchehab, hdegoede,
laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski,
peter.senna
From: Буди Романто, AreMa Inc <knightrider@are.ma>
Signed-off-by: Буди Романто, AreMa Inc <knightrider@are.ma>
---
drivers/media/tuners/Kconfig | 14 ++
drivers/media/tuners/Makefile | 2 +
drivers/media/tuners/nm131.c | 272 ++++++++++++++++++++++++++++++
drivers/media/tuners/nm131.h | 13 ++
drivers/media/tuners/tda2014x.c | 356 ++++++++++++++++++++++++++++++++++++++++
drivers/media/tuners/tda2014x.h | 13 ++
6 files changed, 670 insertions(+)
create mode 100644 drivers/media/tuners/nm131.c
create mode 100644 drivers/media/tuners/nm131.h
create mode 100644 drivers/media/tuners/tda2014x.c
create mode 100644 drivers/media/tuners/tda2014x.h
diff --git a/drivers/media/tuners/Kconfig b/drivers/media/tuners/Kconfig
index 05998f0..b044231 100644
--- a/drivers/media/tuners/Kconfig
+++ b/drivers/media/tuners/Kconfig
@@ -277,4 +277,18 @@ config MEDIA_TUNER_QM1D1C0042
default m if !MEDIA_SUBDRV_AUTOSELECT
help
Sharp QM1D1C0042 trellis coded 8PSK tuner driver.
+
+config MEDIA_TUNER_NM131
+ tristate "Newport Media tuners NM131, NM130 and NM120"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ Newport Media NM131, NM130 and NM120 tuner driver.
+
+config MEDIA_TUNER_TDA2014X
+ tristate "NXP Semiconductors TDA2014x tuner"
+ depends on MEDIA_SUPPORT && I2C
+ default m if !MEDIA_SUBDRV_AUTOSELECT
+ help
+ NXP Semiconductor TDA2014x tuner driver.
endmenu
diff --git a/drivers/media/tuners/Makefile b/drivers/media/tuners/Makefile
index 06a9ab6..f527a51 100644
--- a/drivers/media/tuners/Makefile
+++ b/drivers/media/tuners/Makefile
@@ -41,6 +41,8 @@ obj-$(CONFIG_MEDIA_TUNER_R820T) += r820t.o
obj-$(CONFIG_MEDIA_TUNER_MXL301RF) += mxl301rf.o
obj-$(CONFIG_MEDIA_TUNER_QM1D1C0042) += qm1d1c0042.o
obj-$(CONFIG_MEDIA_TUNER_M88RS6000T) += m88rs6000t.o
+obj-$(CONFIG_MEDIA_TUNER_NM131) += nm131.o
+obj-$(CONFIG_MEDIA_TUNER_TDA2014X) += tda2014x.o
ccflags-y += -I$(srctree)/drivers/media/dvb-core
ccflags-y += -I$(srctree)/drivers/media/dvb-frontends
diff --git a/drivers/media/tuners/nm131.c b/drivers/media/tuners/nm131.c
new file mode 100644
index 0000000..ae53544
--- /dev/null
+++ b/drivers/media/tuners/nm131.c
@@ -0,0 +1,272 @@
+/*
+ * Driver for Newport Media tuners NMI131, NMI130 and NMI120
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+ */
+
+#include <linux/pci.h>
+#include "dvb_frontend.h"
+#include "nm131.h"
+
+struct nm131 {
+ struct i2c_adapter *i2c;
+};
+
+bool nm131_w(struct dvb_frontend *fe, u16 slvadr, u32 val, u32 sz)
+{
+ struct nm131 *t = fe->tuner_priv;
+ u8 buf[] = {0xFE, 0xCE, slvadr >> 8, slvadr & 0xFF, 0, 0, 0, 0};
+ struct i2c_msg msg[] = {
+ {.addr = fe->id, .flags = 0, .buf = buf, .len = sz + 4,},
+ };
+
+ *(u32 *)(buf + 4) = slvadr == 0x36 ? val & 0x7F : val;
+ return i2c_transfer(t->i2c, msg, 1) == 1;
+}
+
+bool nm131_w8(struct dvb_frontend *fe, u8 slvadr, u8 dat)
+{
+ struct nm131 *t = fe->tuner_priv;
+ u8 buf[] = {slvadr, dat};
+ struct i2c_msg msg[] = {
+ {.addr = fe->id, .flags = 0, .buf = buf, .len = 2,},
+ };
+ return i2c_transfer(t->i2c, msg, 1) == 1;
+}
+
+bool nm131_r(struct dvb_frontend *fe, u16 slvadr, u8 *dat, u32 sz)
+{
+ struct nm131 *t = fe->tuner_priv;
+ u8 rcmd[] = {0xFE, 0xCF},
+ buf[sz];
+ struct i2c_msg msg[] = {
+ {.addr = fe->id, .flags = 0, .buf = rcmd, .len = 2,},
+ {.addr = fe->id, .flags = I2C_M_RD, .buf = buf, .len = sz,},
+ };
+ bool ret = nm131_w(fe, slvadr, 0, 0) && i2c_transfer(t->i2c, msg, 2) == 2;
+
+ memcpy(dat, buf, sz);
+ return ret;
+}
+
+int nm131_tune(struct dvb_frontend *fe)
+{
+ struct vhf_filter_cutoff_codes_t {
+ u32 Hz;
+ u8 val8_0x08,
+ val8_0x09;
+ } const vhf_filter_cutoff_codes[] = {
+ {45000000, 167, 58}, {55000000, 151, 57}, {65000000, 100, 54}, {75000000, 83, 53}, {85000000, 82, 53},
+ {95000000, 65, 52}, {105000000, 64, 52}, {115000000, 64, 52}, {125000000, 0, 0}
+ };
+ const u8 v45[] = {0, 1, 2, 3, 4, 6, 9, 12},
+ ACI_audio_lut = 0,
+ aci_lut = 1;
+ const u32 lo_freq_lut[] = {0, 0, 434000000, 237000000, 214000000, 118000000, 79000000, 53000000},
+ *v11,
+ adec_ddfs_fq = 126217,
+ ddfs_lut = 0;
+ u8 rf_reg_0x05 = 0x87,
+ v15,
+ val;
+ int i;
+ u32 tune_rf = fe->dtv_property_cache.frequency,
+ clk_off_f = tune_rf,
+ xo = 24000,
+ lofreq,
+ rf,
+ v14,
+ v32;
+ bool done = false;
+
+ if (!(nm131_w8(fe, 1, 0x50) &&
+ nm131_w8(fe, 0x47, 0x30) &&
+ nm131_w8(fe, 0x25, 0) &&
+ nm131_w8(fe, 0x20, 0) &&
+ nm131_w8(fe, 0x23, 0x4D)))
+ return -EIO;
+ while (1) {
+ rf = clk_off_f;
+ val = rf > 120400000 ? 0x85 : 5;
+ if (rf_reg_0x05 != val) {
+ nm131_w(fe, 0x05, val, 1);
+ rf_reg_0x05 = val;
+ }
+ lofreq = rf;
+ v11 = &lo_freq_lut[6];
+ val = 6;
+ if (lofreq > 53000000) {
+ do {
+ if (*v11 >= lofreq)
+ break;
+ --val;
+ --v11;
+ } while (val != 1);
+ } else
+ val = 7;
+ i = 0;
+ do {
+ if (lofreq > vhf_filter_cutoff_codes[i].Hz && lofreq <= vhf_filter_cutoff_codes[i + 1].Hz)
+ break;
+ ++i;
+ } while (i != 8);
+ nm131_w(fe, 8, vhf_filter_cutoff_codes[i].val8_0x08, 1);
+ nm131_w(fe, 9, vhf_filter_cutoff_codes[i].val8_0x09, 1);
+ v14 = lofreq / 1000 * 8 * v45[val];
+ nm131_r(fe, 0x21, &v15, 1);
+ v15 &= 3;
+ xo = v15 == 2 ? xo * 2 : v15 == 3 ? xo >> 1 : xo;
+ v32 = v14 / xo;
+ if (!((v14 % xo * (0x80000000 / xo) >> 12) & 0x7FFFF) || done)
+ break;
+ clk_off_f += 1000;
+ done = true;
+ }
+ xo = (v14 % xo * (0x80000000 / xo) >> 12) & 0x7FFFF;
+ clk_off_f = v14;
+ v14 /= 216000;
+ if (v14 > 31)
+ v14 = 31;
+ if (v14 < 16)
+ v14 = 16;
+ nm131_w(fe, 1, (u16)v32 >> 1, 1);
+ nm131_w(fe, 2, (v32 & 1) | 2 * xo, 1);
+ nm131_w(fe, 3, xo >> 7, 1);
+ nm131_w(fe, 4, (xo >> 15) | 16 * v14, 1);
+ nm131_r(fe, 0x1D, &v15, 1);
+ nm131_w(fe, 0x1D, 32 * val | (v15 & 0x1F), 1);
+ if (lofreq < 300000000) {
+ nm131_w(fe, 0x25, 0x78, 1);
+ nm131_w(fe, 0x27, 0x7F, 1);
+ nm131_w(fe, 0x29, 0x7F, 1);
+ nm131_w(fe, 0x2E, 0x12, 1);
+ } else {
+ nm131_w(fe, 0x25, 0xF4, 1);
+ nm131_w(fe, 0x27, 0xEF, 1);
+ nm131_w(fe, 0x29, 0x4F, 1);
+ nm131_w(fe, 0x2E, 0x34, 1);
+ }
+ nm131_w(fe, 0x36, lofreq < 150000000 ? 0x54 : 0x7C, 1);
+ nm131_w(fe, 0x37, lofreq < 155000000 ? 0x84 : lofreq < 300000000 ? 0x9C : 0x84, 1);
+ clk_off_f = (clk_off_f << 9) / v14 - 110592000;
+
+ nm131_w(fe, 0x164, tune_rf < 300000000 ? 0x600 : 0x500, 4);
+ rf = clk_off_f / 6750 + 16384;
+ nm131_w(fe, 0x230, (adec_ddfs_fq << 15) / rf | 0x80000, 4);
+ nm131_w(fe, 0x250, ACI_audio_lut, 4);
+ nm131_w(fe, 0x27C, 0x1010, 4);
+ nm131_w(fe, 0x1BC, (ddfs_lut << 14) / rf, 4);
+ nm131_r(fe, 0x21C, (u8 *)(&v32), 4);
+ nm131_w(fe, 0x21C, (((v32 & 0xFFC00000) | 524288000 / ((clk_off_f >> 14) + 6750)) & 0xC7BFFFFE) | 0x8000000, 4);
+ nm131_r(fe, 0x234, (u8 *)(&v32), 4);
+ nm131_w(fe, 0x234, v32 & 0xCFC00000, 4);
+ nm131_w(fe, 0x210, ((864 * (clk_off_f >> 5) - 1308983296) / 216000 & 0xFFFFFFF0) | 3, 4);
+ nm131_r(fe, 0x104, (u8 *)(&v32), 4);
+ v32 = ((((clk_off_f > 3686396 ? 2 : clk_off_f >= 1843193 ? 1 : 0) << 16) | (((((((v32 & 0x87FFFFC0) | 0x10000011)
+ & 0xFFFF87FF) | (aci_lut << 11)) & 0xFFFF7FFF) | 0x8000) & 0xFFF0FFFF)) & 0xFC0FFFFF) | 0xA00000;
+ nm131_w(fe, 0x104, v32, 4);
+ v32 = (v32 & 0xFFFFFFEF) | 0x20000020;
+ nm131_w(fe, 0x104, v32, 4);
+ nm131_r(fe, 0x328, (u8 *)(&v14), 4);
+ if (v14) {
+ v32 &= 0xFFFFFFDF;
+ nm131_w(fe, 0x104, v32, 4);
+ nm131_w(fe, 0x104, v32 | 0x20, 4);
+ }
+ return nm131_w8(fe, 0x23, 0x4C) &&
+ nm131_w8(fe, 1, 0x50) &&
+ nm131_w8(fe, 0x71, 1) &&
+ nm131_w8(fe, 0x72, 0x24) ?
+ 0 : -EIO;
+}
+
+static struct dvb_tuner_ops nm131_ops = {
+ .set_params = nm131_tune,
+};
+
+int nm131_remove(struct i2c_client *c)
+{
+ kfree(i2c_get_clientdata(c));
+ return 0;
+}
+
+int nm131_probe(struct i2c_client *c, const struct i2c_device_id *id)
+{
+ struct tnr_rf_reg_t {
+ u8 slvadr;
+ u8 val;
+ } const
+ tnr_rf_defaults_lut[] = {
+ {6, 72}, {7, 64}, {10, 235}, {11, 17}, {12, 16}, {13, 136},
+ {16, 4}, {17, 48}, {18, 48}, {21, 170}, {22, 3}, {23, 128},
+ {24, 103}, {25, 212}, {26, 68}, {28, 16}, {29, 238}, {30, 153},
+ {33, 197}, {34, 145}, {36, 1}, {43, 145}, {45, 1}, {47, 128},
+ {49, 0}, {51, 0}, {56, 0}, {57, 47}, {58, 0}, {59, 0}
+ },
+ nm120_rf_defaults_lut[] = {
+ {14, 69}, {27, 14}, {35, 255}, {38, 130}, {40, 0},
+ {48, 223}, {50, 223}, {52, 104}, {53, 24}
+ };
+ struct tnr_bb_reg_t {
+ u16 slvadr;
+ u32 val;
+ } const
+ tnr_bb_defaults_lut[2] = {
+ {356, 2048}, {448, 764156359}
+ };
+ u8 i;
+ struct nm131 *t = kzalloc(sizeof(struct nm131), GFP_KERNEL);
+ struct dvb_frontend *fe = c->dev.platform_data;
+
+ if (!t)
+ return -ENOMEM;
+ t->i2c = c->adapter;
+ fe->tuner_priv = t;
+ memcpy(&fe->ops.tuner_ops, &nm131_ops, sizeof(struct dvb_tuner_ops));
+ i2c_set_clientdata(c, t);
+
+ nm131_w8(fe, 0xB0, 0xA0) &&
+ nm131_w8(fe, 0xB2, 0x3D) &&
+ nm131_w8(fe, 0xB3, 0x25) &&
+ nm131_w8(fe, 0xB4, 0x8B) &&
+ nm131_w8(fe, 0xB5, 0x4B) &&
+ nm131_w8(fe, 0xB6, 0x3F) &&
+ nm131_w8(fe, 0xB7, 0xFF) &&
+ nm131_w8(fe, 0xB8, 0xC0) &&
+ nm131_w8(fe, 3, 0) &&
+ nm131_w8(fe, 0x1D, 0) &&
+ nm131_w8(fe, 0x1F, 0) &&
+ (nm131_w8(fe, 0xE, 0x77), nm131_w8(fe, 0xF, 0x13), nm131_w8(fe, 0x75, 2));
+
+ for (i = 0; i < ARRAY_SIZE(tnr_rf_defaults_lut); i++)
+ nm131_w(fe, tnr_rf_defaults_lut[i].slvadr, tnr_rf_defaults_lut[i].val, 1);
+ nm131_r(fe, 0x36, &i, 1);
+ nm131_w(fe, 0x36, i & 0x7F, 1); /* no LDO bypass */
+ nm131_w(fe, tnr_bb_defaults_lut[0].slvadr, tnr_bb_defaults_lut[0].val, 4);
+ nm131_w(fe, tnr_bb_defaults_lut[1].slvadr, tnr_bb_defaults_lut[1].val, 4);
+ for (i = 0; i < ARRAY_SIZE(nm120_rf_defaults_lut); i++)
+ nm131_w(fe, nm120_rf_defaults_lut[i].slvadr, nm120_rf_defaults_lut[i].val, 1);
+ nm131_w(fe, 0xA, 0xFB, 1); /* ltgain */
+ return 0;
+}
+
+static struct i2c_device_id nm131_id[] = {
+ {NM131_MODNAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, nm131_id);
+
+static struct i2c_driver nm131_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = nm131_id->name,
+ },
+ .probe = nm131_probe,
+ .remove = nm131_remove,
+ .id_table = nm131_id,
+};
+module_i2c_driver(nm131_driver);
+
+MODULE_AUTHOR("Budi Rachmanto, AreMa Inc. <info@are.ma>");
+MODULE_DESCRIPTION("Driver for Newport Media tuners NMI131, NMI130 and NMI120");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/nm131.h b/drivers/media/tuners/nm131.h
new file mode 100644
index 0000000..21f3627
--- /dev/null
+++ b/drivers/media/tuners/nm131.h
@@ -0,0 +1,13 @@
+/*
+ * Driver for Newport Media tuners NMI131, NMI130 and NMI120
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+ */
+
+#ifndef NM131_H
+#define NM131_H
+
+#define NM131_MODNAME "nm131"
+
+#endif
+
diff --git a/drivers/media/tuners/tda2014x.c b/drivers/media/tuners/tda2014x.c
new file mode 100644
index 0000000..b70c929
--- /dev/null
+++ b/drivers/media/tuners/tda2014x.c
@@ -0,0 +1,356 @@
+/*
+ * Driver for NXP Semiconductors tuner TDA2014x
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+ */
+
+#include <linux/pci.h>
+#include "dvb_frontend.h"
+#include "tda2014x.h"
+
+struct tda2014x {
+ struct i2c_adapter *i2c;
+};
+
+int tda2014x_r(struct dvb_frontend *fe, u8 slvadr)
+{
+ struct tda2014x *t = fe->tuner_priv;
+ u8 buf[] = {0xFE, 0xA8, slvadr},
+ rcmd[] = {0xFE, 0xA9},
+ ret = 0;
+ struct i2c_msg msg[] = {
+ {.addr = fe->id, .flags = 0, .buf = buf, .len = 3,},
+ {.addr = fe->id, .flags = 0, .buf = rcmd, .len = 2,},
+ {.addr = fe->id, .flags = I2C_M_RD, .buf = &ret, .len = 1,},
+ };
+ return i2c_transfer(t->i2c, msg, 3) == 3 ? ret : -EIO;
+}
+
+bool tda2014x_r8(struct dvb_frontend *fe, u16 slvadr, u8 start_bit, u8 nbits, u8 *rdat)
+{
+ u8 mask = nbits > 7 ? 0xFF : ((1 << nbits) - 1) << start_bit;
+ int val = tda2014x_r(fe, slvadr);
+
+ if (val < 0)
+ return false;
+ *rdat = (val & mask) >> start_bit;
+ return true;
+}
+
+bool tda2014x_w8(struct dvb_frontend *fe, u8 slvadr, u8 dat)
+{
+ struct tda2014x *t = fe->tuner_priv;
+ u8 buf[] = {slvadr, dat};
+ struct i2c_msg msg[] = {
+ {.addr = fe->id, .flags = 0, .buf = buf, .len = 2,},
+ };
+ return i2c_transfer(t->i2c, msg, 1) == 1;
+}
+
+bool tda2014x_w16(struct dvb_frontend *fe, u16 slvadr, u8 start_bit, u8 nbits, u8 nbytes, bool rmw, u8 access, u16 wdat)
+{
+ struct tda2014x *t = fe->tuner_priv;
+ u16 mask = nbits > 15 ? 0xFFFF : ((1 << nbits) - 1) << start_bit,
+ val = mask & (wdat << start_bit);
+ u8 *wval = (u8 *)&val,
+ i;
+
+ for (i = 0, nbytes = !nbytes ? 1 : nbytes > 2 ? 2 : nbytes; access & 2 && nbytes; i++, nbytes--) {
+ u8 buf[] = {0xFE, 0xA8, slvadr + i, 0};
+ int ret = tda2014x_r(fe, slvadr + i);
+ struct i2c_msg msg[] = {
+ {.addr = fe->id, .flags = 0, .buf = buf, .len = 4,},
+ };
+
+ if (ret < 0)
+ return false;
+ if (rmw)
+ wval[nbytes - 1] |= ~(mask >> 8 * i) & ret;
+ buf[3] = wval[nbytes - 1];
+ if (i2c_transfer(t->i2c, msg, 1) != 1)
+ return false;
+ }
+ return true;
+}
+
+int tda2014x_tune(struct dvb_frontend *fe)
+{
+ bool bDoublerEnable[] = {false, true, true, true, true},
+ bDcc1Enable[] = {false, true, true, true, true},
+ bDcc2Enable[] = {false, true, true, true, true},
+ bPpfEnable[] = {false, true, true, true, true},
+ bDiv1ConfigInDivideBy3[] = {false, true, false, true, false},
+ bDiv2ConfigInDivideBy3[] = {false, true, true, false, false},
+ bSelectDivideBy4Or5Or6Or7Path[] = {false, true, true, true, true},
+ bSelectDivideBy8Path[] = {true, false, false, false, false},
+ bInputMuxEnable;
+ u64 kHz = fe->dtv_property_cache.frequency,
+ ResLsb,
+ kint,
+ Nint,
+ v15;
+ enum {
+ TDA2014X_LNA_GAIN_7dB = 0x0,
+ TDA2014X_LNA_GAIN_10dB = 0x1,
+ TDA2014X_LNA_GAIN_13dB = 0x2, /* default */
+ TDA2014X_LNA_GAIN_18dB = 0x3,
+ TDA2014X_LNA_GAIN_NEGATIVE_11dB = 0x4,
+
+ TDA2014X_LPT_GAIN_NEGATIVE_8dB = 0x0,
+ TDA2014X_LPT_GAIN_NEGATIVE_10dB = 0x1, /* default */
+ TDA2014X_LPT_GAIN_NEGATIVE_14dB = 0x2,
+ TDA2014X_LPT_GAIN_NEGATIVE_16dB = 0x3,
+
+ TDA2014X_AMPOUT_15DB = 0x0,
+ TDA2014X_AMPOUT_18DB = 0x1,
+ TDA2014X_AMPOUT_18DB_b = 0x2,
+ TDA2014X_AMPOUT_21DB = 0x3, /* default */
+ TDA2014X_AMPOUT_24DB = 0x6, /* ok too */
+ TDA2014X_AMPOUT_27DB = 0x7,
+ TDA2014X_AMPOUT_30DB = 0xE,
+ TDA2014X_AMPOUT_33DB = 0xF,
+ };
+ u8 val,
+ R,
+ Premain,
+ i = kHz <= 1075000 ? 0 : kHz <= 1228000 ? 1 : kHz <= 1433000 ? 2 : kHz <= 1720000 ? 3 : 4,
+ lna = TDA2014X_LNA_GAIN_13dB,
+ gain = (lna == TDA2014X_LNA_GAIN_18dB) | ((lna & 3) << 4) | (TDA2014X_LPT_GAIN_NEGATIVE_10dB << 1),
+ ampout = TDA2014X_AMPOUT_21DB;
+
+ if (!(tda2014x_w8(fe, 0xA, 0) &&
+ tda2014x_w8(fe, 0x10, 0xB0) &&
+ tda2014x_w8(fe, 0x11, 2) &&
+ tda2014x_w8(fe, 3, 1) &&
+
+ /* GetLoConfig */
+ tda2014x_r8(fe, 0x25, 3, 1, &val)))
+ return -EIO;
+ bInputMuxEnable = val;
+
+ /* SetLoConfig */
+ tda2014x_w16(fe, 0x22, 0, 8, 0, 0, 6,
+ (bDoublerEnable[i] << 7) | (bDcc1Enable[i] << 6) | (bDcc2Enable[i] << 5) | 0b11110 | bPpfEnable[i]) &&
+ tda2014x_r8(fe, 0x23, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x23, 0, 8, 0, 0, 6, (bDiv1ConfigInDivideBy3[i] << 7) | (bDiv2ConfigInDivideBy3[i] << 5) |
+ (bSelectDivideBy4Or5Or6Or7Path[i] << 3) | (bSelectDivideBy8Path[i] << 2) | (val & 0b1010011)) &&
+ tda2014x_w16(fe, 0x25, 3, 1, 0, 1, 6, bInputMuxEnable);
+
+ ResLsb = (8 - i) * kHz * 39; /* Xtal 27 MHz */
+ kint = ResLsb;
+ v15 = ResLsb / 0x100000;
+ R = 1;
+ Premain = 1;
+ Nint = v15 * R >> Premain;
+ if (Nint < 131) {
+ Premain = 0;
+ Nint = v15 * R >> Premain;
+ if (Nint > 251) {
+ R = 3;
+ Premain = 2;
+ goto LABEL_36;
+ }
+ if (Nint < 131) {
+ R = 3;
+ Premain = 1;
+ goto LABEL_36;
+ }
+ } else if (Nint > 251) {
+ Premain = 2;
+ Nint = v15 * R >> Premain;
+ if (Nint > 251) {
+ R = 3;
+ Premain = 2;
+ }
+LABEL_36:
+ Nint = v15 * R >> Premain;
+ if (Nint < 131 || Nint > 251)
+ return -ERANGE;
+ }
+ switch (100 * R >> Premain) {
+ case 25:
+ kint = ResLsb / 4;
+ break;
+ case 50:
+ kint = ResLsb / 2;
+ break;
+ case 75:
+ kint = ResLsb / 2 + ResLsb / 4;
+ break;
+ case 100:
+ break;
+ case 150:
+ kint = ResLsb / 2 + ResLsb;
+ break;
+ default:
+ return -ERANGE;
+ }
+ return !(tda2014x_w16(fe, 3, 6, 2, 0, 1, 6, R - 1) &&
+
+ /* SetPllDividerConfig */
+ tda2014x_w16(fe, 0x1A, 5, 1, 0, 1, 6, Premain == 1 ? 0 : 1) &&
+ tda2014x_w16(fe, 0x1E, 0, 8, 0, 0, 6, kint / 0x100000) &&
+ tda2014x_w16(fe, 0x1F, 0, 16, 2, 0, 6, kint % 0x100000) &&
+
+ /* ProgramVcoChannelChange */
+ tda2014x_r8(fe, 0x12, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x12, 0, 8, 0, 0, 6, (val & 0x7F) | 0x40) &&
+ tda2014x_w16(fe, 0x13, 0, 2, 0, 1, 6, 2) &&
+ tda2014x_w16(fe, 0x13, 7, 1, 0, 1, 6, 0) &&
+ tda2014x_w16(fe, 0x13, 5, 1, 0, 1, 6, 1) &&
+ tda2014x_w16(fe, 0x13, 5, 1, 0, 1, 6, 1) &&
+ tda2014x_w16(fe, 0x13, 7, 1, 0, 1, 6, 0) &&
+ tda2014x_w16(fe, 0x13, 4, 1, 0, 1, 6, 1) &&
+ ((tda2014x_r8(fe, 0x15, 4, 1, &val) && val == 1) ||
+ (tda2014x_r8(fe, 0x15, 4, 1, &val) && val == 1)) &&
+ tda2014x_w16(fe, 0x13, 4, 1, 0, 1, 6, 0) &&
+ tda2014x_r8(fe, 0x12, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x12, 0, 8, 0, 0, 6, val & 0x7F) &&
+
+ /* SetFilterBandwidth */
+ tda2014x_w16(fe, 0xA, 0, 4, 0, 1, 6, 0xA) &&
+ tda2014x_w16(fe, 0xB, 1, 7, 0, 1, 6, 0x7C) &&
+
+ /* SetGainConfig */
+ tda2014x_r8(fe, 6, 0, 8, &val) &&
+ tda2014x_w16(fe, 6, 0, 8, 0, 0, 6, (val & 0x48) | 0x80 | gain) &&
+ tda2014x_r8(fe, 9, 0, 8, &val) &&
+ tda2014x_w16(fe, 9, 0, 8, 0, 0, 6, 0b10110000 | (val & 3)) &&
+ tda2014x_w16(fe, 0xA, 5, 3, 0, 1, 6, 3) &&
+ tda2014x_w16(fe, 0xC, 4, 4, 0, 1, 6, ampout) &&
+
+ tda2014x_w8(fe, 0xA, 0xFF) &&
+ tda2014x_w8(fe, 0x10, 0xB2) &&
+ tda2014x_w8(fe, 0x11, 0) &&
+ tda2014x_w8(fe, 3, 1)) * -EIO;
+}
+
+static struct dvb_tuner_ops tda2014x_ops = {
+ .set_params = tda2014x_tune,
+};
+
+int tda2014x_remove(struct i2c_client *c)
+{
+ kfree(i2c_get_clientdata(c));
+ return 0;
+}
+
+int tda2014x_probe(struct i2c_client *c, const struct i2c_device_id *id)
+{
+ u8 val = 0;
+ struct tda2014x *t = kzalloc(sizeof(struct tda2014x), GFP_KERNEL);
+ struct dvb_frontend *fe = c->dev.platform_data;
+
+ if (!t)
+ return -ENOMEM;
+ t->i2c = c->adapter;
+ fe->tuner_priv = t;
+ memcpy(&fe->ops.tuner_ops, &tda2014x_ops, sizeof(struct dvb_tuner_ops));
+ i2c_set_clientdata(c, t);
+ fe->dtv_property_cache.frequency = 1318000;
+
+ tda2014x_w8(fe, 0x13, 0) &&
+ tda2014x_w8(fe, 0x15, 0) &&
+ tda2014x_w8(fe, 0x17, 0) &&
+ tda2014x_w8(fe, 0x1C, 0) &&
+ tda2014x_w8(fe, 0x1D, 0) &&
+ tda2014x_w8(fe, 0x1F, 0) &&
+ (tda2014x_w8(fe, 7, 0x31), tda2014x_w8(fe, 8, 0x77), tda2014x_w8(fe, 4, 2)) &&
+
+ /* SetPowerMode */
+ tda2014x_r8(fe, 2, 0, 8, &val) &&
+ tda2014x_w16(fe, 2, 0, 8, 0, 0, 6, val | 0x81) &&
+ tda2014x_r8(fe, 6, 0, 8, &val) &&
+ tda2014x_w16(fe, 6, 0, 8, 0, 0, 6, (val | 0x39) & 0x7F) &&
+ tda2014x_r8(fe, 7, 0, 8, &val) &&
+ tda2014x_w16(fe, 7, 0, 8, 0, 0, 6, val | 0xAE) &&
+ tda2014x_r8(fe, 0xF, 0, 8, &val) &&
+ tda2014x_w16(fe, 0xF, 0, 8, 0, 0, 6, val | 0x80) &&
+ tda2014x_r8(fe, 0x18, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x18, 0, 8, 0, 0, 6, val & 0x7F) &&
+ tda2014x_r8(fe, 0x1A, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x1A, 0, 8, 0, 0, 6, val | 0xC0) &&
+ tda2014x_w16(fe, 0x22, 0, 8, 0, 0, 6, 0xFF) &&
+ tda2014x_r8(fe, 0x23, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x23, 0, 8, 0, 0, 6, val & 0xFE) &&
+ tda2014x_r8(fe, 0x25, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x25, 0, 8, 0, 0, 6, val | 8) &&
+ tda2014x_r8(fe, 0x27, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x27, 0, 8, 0, 0, 6, (val | 0xC0) & 0xDF) &&
+ tda2014x_r8(fe, 0x24, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x24, 0, 8, 0, 0, 6, (val | 4) & 0xCF) &&
+ tda2014x_r8(fe, 0xD, 0, 8, &val) &&
+ tda2014x_w16(fe, 0xD, 0, 8, 0, 0, 6, val & 0xDF) &&
+ tda2014x_r8(fe, 9, 0, 8, &val) &&
+ tda2014x_w16(fe, 9, 0, 8, 0, 0, 6, (val | 0xB0) & 0xB1) &&
+ tda2014x_r8(fe, 0xA, 0, 8, &val) &&
+ tda2014x_w16(fe, 0xA, 0, 8, 0, 0, 6, (val | 0x6F) & 0x7F) &&
+ tda2014x_r8(fe, 0xB, 0, 8, &val) &&
+ tda2014x_w16(fe, 0xB, 0, 8, 0, 0, 6, (val | 0x7A) & 0x7B) &&
+ tda2014x_w16(fe, 0xC, 0, 8, 0, 0, 6, 0) &&
+ tda2014x_w16(fe, 0x19, 0, 8, 0, 0, 6, 0xFA) &&
+ tda2014x_r8(fe, 0x1B, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x1B, 0, 8, 0, 0, 6, val & 0x7F) &&
+ tda2014x_r8(fe, 0x21, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x21, 0, 8, 0, 0, 6, val | 0x40) &&
+ tda2014x_r8(fe, 0x10, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x10, 0, 8, 0, 0, 6, (val | 0x90) & 0xBF) &&
+ tda2014x_r8(fe, 0x14, 0, 8, &val) &&
+ tda2014x_w16(fe, 0x14, 0, 8, 0, 0, 6, (val | 0x20) & 0xEF) &&
+
+ /* ProgramPllPor */
+ tda2014x_w16(fe, 0x1A, 6, 1, 0, 1, 6, 1) &&
+ tda2014x_w16(fe, 0x18, 0, 1, 0, 1, 6, 1) &&
+ tda2014x_w16(fe, 0x18, 7, 1, 0, 1, 6, 1) &&
+ tda2014x_w16(fe, 0x1B, 7, 1, 0, 1, 6, 1) &&
+ tda2014x_w16(fe, 0x18, 0, 1, 0, 1, 6, 0) &&
+
+ /* ProgramVcoPor */
+ tda2014x_r8(fe, 0xF, 0, 8, &val) &&
+ (val = (val & 0x1F) | 0x80, tda2014x_w16(fe, 0xF, 0, 8, 0, 0, 6, val)) &&
+ tda2014x_r8(fe, 0x13, 0, 8, &val) &&
+ (val = (val & 0xFFFFFFCF) | 0x20, tda2014x_w16(fe, 0x13, 0, 8, 0, 0, 6, val)) &&
+ tda2014x_r8(fe, 0x12, 0, 8, &val) &&
+ (val |= 0xC0, tda2014x_w16(fe, 0x12, 0, 8, 0, 0, 6, val)) &&
+ tda2014x_w16(fe, 0x10, 5, 1, 0, 1, 6, 1) &&
+ tda2014x_w16(fe, 0x10, 5, 1, 0, 1, 6, 1) &&
+ tda2014x_w16(fe, 0xF, 5, 1, 0, 1, 6, 1) &&
+ tda2014x_r8(fe, 0x11, 4, 1, &val) &&
+ (val || tda2014x_r8(fe, 0x11, 4, 1, &val)) &&
+ (val || tda2014x_r8(fe, 0x11, 4, 1, &val)) &&
+ val &&
+ tda2014x_r8(fe, 0x10, 0, 4, &val) &&
+ tda2014x_w16(fe, 0xF, 0, 4, 0, 1, 6, val) &&
+ tda2014x_w16(fe, 0xF, 6, 1, 0, 1, 6, 1) &&
+ tda2014x_w16(fe, 0xF, 5, 1, 0, 1, 6, 0) &&
+ tda2014x_r8(fe, 0x12, 0, 8, &val) &&
+ (val &= 0x7F, tda2014x_w16(fe, 0x12, 0, 8, 0, 0, 6, val)) &&
+ tda2014x_w16(fe, 0xD, 5, 2, 0, 1, 6, 1) &&
+
+ /* EnableLoopThrough */
+ tda2014x_r8(fe, 6, 0, 8, &val) &&
+ tda2014x_w16(fe, 6, 0, 8, 0, 0, 6, (val & 0xF7) | 8);
+
+ return tda2014x_tune(fe);
+}
+
+static struct i2c_device_id tda2014x_id[] = {
+ {TDA2014X_MODNAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, tda2014x_id);
+
+static struct i2c_driver tda2014x_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = tda2014x_id->name,
+ },
+ .probe = tda2014x_probe,
+ .remove = tda2014x_remove,
+ .id_table = tda2014x_id,
+};
+module_i2c_driver(tda2014x_driver);
+
+MODULE_AUTHOR("Budi Rachmanto, AreMa Inc. <info@are.ma>");
+MODULE_DESCRIPTION("Driver for NXP Semiconductors tuner TDA2014x");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/tda2014x.h b/drivers/media/tuners/tda2014x.h
new file mode 100644
index 0000000..36f8198
--- /dev/null
+++ b/drivers/media/tuners/tda2014x.h
@@ -0,0 +1,13 @@
+/*
+ * Driver for NXP Semiconductors tuner TDA2014x
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+ */
+
+#ifndef TDA2014X_H
+#define TDA2014X_H
+
+#define TDA2014X_MODNAME "tda2014x"
+
+#endif
+
--
2.3.10
^ permalink raw reply related [flat|nested] 39+ messages in thread* [media 3/7] drop backstabbing drivers
2016-02-15 6:08 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
2016-02-15 6:08 ` [media 1/7] raise adapter number limit info
2016-02-15 6:08 ` [media 2/7] add NXP tda2014x & Newport Media nm120/130/131 tuners info
@ 2016-02-15 6:08 ` info
2016-02-15 6:08 ` [media 4/7] Toshiba TC90522XBG quad demodulator (2ch OFDM + 2ch 8PSK) used by both PT3 & PX-Q3PE info
` (3 subsequent siblings)
6 siblings, 0 replies; 39+ messages in thread
From: info @ 2016-02-15 6:08 UTC (permalink / raw)
To: linux-media
Cc: Буди Романто, AreMa Inc,
linux-kernel, crope, m.chehab, mchehab, hdegoede,
laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski,
peter.senna
From: Буди Романто, AreMa Inc <knightrider@are.ma>
Signed-off-by: Буди Романто, AreMa Inc <knightrider@are.ma>
---
drivers/media/dvb-frontends/tc90522.c | 840 --------------------------------
drivers/media/dvb-frontends/tc90522.h | 42 --
drivers/media/pci/pt3/Kconfig | 10 -
drivers/media/pci/pt3/Makefile | 8 -
drivers/media/pci/pt3/pt3.c | 873 ----------------------------------
drivers/media/pci/pt3/pt3.h | 186 --------
drivers/media/pci/pt3/pt3_dma.c | 225 ---------
drivers/media/pci/pt3/pt3_i2c.c | 240 ----------
drivers/media/tuners/mxl301rf.c | 349 --------------
drivers/media/tuners/mxl301rf.h | 26 -
drivers/media/tuners/qm1d1c0042.c | 448 -----------------
drivers/media/tuners/qm1d1c0042.h | 37 --
12 files changed, 3284 deletions(-)
delete mode 100644 drivers/media/dvb-frontends/tc90522.c
delete mode 100644 drivers/media/dvb-frontends/tc90522.h
delete mode 100644 drivers/media/pci/pt3/Kconfig
delete mode 100644 drivers/media/pci/pt3/Makefile
delete mode 100644 drivers/media/pci/pt3/pt3.c
delete mode 100644 drivers/media/pci/pt3/pt3.h
delete mode 100644 drivers/media/pci/pt3/pt3_dma.c
delete mode 100644 drivers/media/pci/pt3/pt3_i2c.c
delete mode 100644 drivers/media/tuners/mxl301rf.c
delete mode 100644 drivers/media/tuners/mxl301rf.h
delete mode 100644 drivers/media/tuners/qm1d1c0042.c
delete mode 100644 drivers/media/tuners/qm1d1c0042.h
diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c
deleted file mode 100644
index 31cd325..0000000
--- a/drivers/media/dvb-frontends/tc90522.c
+++ /dev/null
@@ -1,840 +0,0 @@
-/*
- * Toshiba TC90522 Demodulator
- *
- * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * 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.
- *
- *
- * 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.
- */
-
-/*
- * NOTICE:
- * This driver is incomplete and lacks init/config of the chips,
- * as the necessary info is not disclosed.
- * It assumes that users of this driver (such as a PCI bridge of
- * DTV receiver cards) properly init and configure the chip
- * via I2C *before* calling this driver's init() function.
- *
- * 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 <linux/dvb/frontend.h>
-#include "dvb_math.h"
-#include "tc90522.h"
-
-#define TC90522_I2C_THRU_REG 0xfe
-
-#define TC90522_MODULE_IDX(addr) (((u8)(addr) & 0x02U) >> 1)
-
-struct tc90522_state {
- struct tc90522_config cfg;
- struct dvb_frontend fe;
- struct i2c_client *i2c_client;
- struct i2c_adapter tuner_i2c;
-
- bool lna;
-};
-
-struct reg_val {
- u8 reg;
- u8 val;
-};
-
-static int
-reg_write(struct tc90522_state *state, const struct reg_val *regs, int num)
-{
- int i, ret;
- struct i2c_msg msg;
-
- ret = 0;
- msg.addr = state->i2c_client->addr;
- msg.flags = 0;
- msg.len = 2;
- for (i = 0; i < num; i++) {
- msg.buf = (u8 *)®s[i];
- ret = i2c_transfer(state->i2c_client->adapter, &msg, 1);
- if (ret == 0)
- ret = -EIO;
- if (ret < 0)
- return ret;
- }
- return 0;
-}
-
-static int reg_read(struct tc90522_state *state, u8 reg, u8 *val, u8 len)
-{
- struct i2c_msg msgs[2] = {
- {
- .addr = state->i2c_client->addr,
- .flags = 0,
- .buf = ®,
- .len = 1,
- },
- {
- .addr = state->i2c_client->addr,
- .flags = I2C_M_RD,
- .buf = val,
- .len = len,
- },
- };
- int ret;
-
- ret = i2c_transfer(state->i2c_client->adapter, msgs, ARRAY_SIZE(msgs));
- if (ret == ARRAY_SIZE(msgs))
- ret = 0;
- else if (ret >= 0)
- ret = -EIO;
- return ret;
-}
-
-static struct tc90522_state *cfg_to_state(struct tc90522_config *c)
-{
- return container_of(c, struct tc90522_state, cfg);
-}
-
-
-static int tc90522s_set_tsid(struct dvb_frontend *fe)
-{
- struct reg_val set_tsid[] = {
- { 0x8f, 00 },
- { 0x90, 00 }
- };
-
- set_tsid[0].val = (fe->dtv_property_cache.stream_id & 0xff00) >> 8;
- set_tsid[1].val = fe->dtv_property_cache.stream_id & 0xff;
- return reg_write(fe->demodulator_priv, set_tsid, ARRAY_SIZE(set_tsid));
-}
-
-static int tc90522t_set_layers(struct dvb_frontend *fe)
-{
- struct reg_val rv;
- u8 laysel;
-
- laysel = ~fe->dtv_property_cache.isdbt_layer_enabled & 0x07;
- laysel = (laysel & 0x01) << 2 | (laysel & 0x02) | (laysel & 0x04) >> 2;
- rv.reg = 0x71;
- rv.val = laysel;
- return reg_write(fe->demodulator_priv, &rv, 1);
-}
-
-/* frontend ops */
-
-static int tc90522s_read_status(struct dvb_frontend *fe, enum fe_status *status)
-{
- struct tc90522_state *state;
- int ret;
- u8 reg;
-
- state = fe->demodulator_priv;
- ret = reg_read(state, 0xc3, ®, 1);
- if (ret < 0)
- return ret;
-
- *status = 0;
- if (reg & 0x80) /* input level under min ? */
- return 0;
- *status |= FE_HAS_SIGNAL;
-
- if (reg & 0x60) /* carrier? */
- return 0;
- *status |= FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC;
-
- if (reg & 0x10)
- return 0;
- if (reg_read(state, 0xc5, ®, 1) < 0 || !(reg & 0x03))
- return 0;
- *status |= FE_HAS_LOCK;
- return 0;
-}
-
-static int tc90522t_read_status(struct dvb_frontend *fe, enum fe_status *status)
-{
- struct tc90522_state *state;
- int ret;
- u8 reg;
-
- state = fe->demodulator_priv;
- ret = reg_read(state, 0x96, ®, 1);
- if (ret < 0)
- return ret;
-
- *status = 0;
- if (reg & 0xe0) {
- *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI
- | FE_HAS_SYNC | FE_HAS_LOCK;
- return 0;
- }
-
- ret = reg_read(state, 0x80, ®, 1);
- if (ret < 0)
- return ret;
-
- if (reg & 0xf0)
- return 0;
- *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER;
-
- if (reg & 0x0c)
- return 0;
- *status |= FE_HAS_SYNC | FE_HAS_VITERBI;
-
- if (reg & 0x02)
- return 0;
- *status |= FE_HAS_LOCK;
- return 0;
-}
-
-static const enum fe_code_rate fec_conv_sat[] = {
- FEC_NONE, /* unused */
- FEC_1_2, /* for BPSK */
- FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, /* for QPSK */
- FEC_2_3, /* for 8PSK. (trellis code) */
-};
-
-static int tc90522s_get_frontend(struct dvb_frontend *fe,
- struct dtv_frontend_properties *c)
-{
- struct tc90522_state *state;
- struct dtv_fe_stats *stats;
- int ret, i;
- int layers;
- u8 val[10];
- u32 cndat;
-
- state = fe->demodulator_priv;
- c->delivery_system = SYS_ISDBS;
- c->symbol_rate = 28860000;
-
- layers = 0;
- ret = reg_read(state, 0xe6, val, 5);
- if (ret == 0) {
- u8 v;
-
- c->stream_id = val[0] << 8 | val[1];
-
- /* high/single layer */
- v = (val[2] & 0x70) >> 4;
- c->modulation = (v == 7) ? PSK_8 : QPSK;
- c->fec_inner = fec_conv_sat[v];
- c->layer[0].fec = c->fec_inner;
- c->layer[0].modulation = c->modulation;
- c->layer[0].segment_count = val[3] & 0x3f; /* slots */
-
- /* low layer */
- v = (val[2] & 0x07);
- c->layer[1].fec = fec_conv_sat[v];
- if (v == 0) /* no low layer */
- c->layer[1].segment_count = 0;
- else
- c->layer[1].segment_count = val[4] & 0x3f; /* slots */
- /*
- * actually, BPSK if v==1, but not defined in
- * enum fe_modulation
- */
- c->layer[1].modulation = QPSK;
- layers = (v > 0) ? 2 : 1;
- }
-
- /* statistics */
-
- stats = &c->strength;
- stats->len = 0;
- /* let the connected tuner set RSSI property cache */
- if (fe->ops.tuner_ops.get_rf_strength) {
- u16 dummy;
-
- fe->ops.tuner_ops.get_rf_strength(fe, &dummy);
- }
-
- stats = &c->cnr;
- stats->len = 1;
- stats->stat[0].scale = FE_SCALE_NOT_AVAILABLE;
- cndat = 0;
- ret = reg_read(state, 0xbc, val, 2);
- if (ret == 0)
- cndat = val[0] << 8 | val[1];
- if (cndat >= 3000) {
- u32 p, p4;
- s64 cn;
-
- cndat -= 3000; /* cndat: 4.12 fixed point float */
- /*
- * cnr[mdB] = -1634.6 * P^5 + 14341 * P^4 - 50259 * P^3
- * + 88977 * P^2 - 89565 * P + 58857
- * (P = sqrt(cndat) / 64)
- */
- /* p := sqrt(cndat) << 8 = P << 14, 2.14 fixed point float */
- /* cn = cnr << 3 */
- p = int_sqrt(cndat << 16);
- p4 = cndat * cndat;
- cn = div64_s64(-16346LL * p4 * p, 10) >> 35;
- cn += (14341LL * p4) >> 21;
- cn -= (50259LL * cndat * p) >> 23;
- cn += (88977LL * cndat) >> 9;
- cn -= (89565LL * p) >> 11;
- cn += 58857 << 3;
- stats->stat[0].svalue = cn >> 3;
- stats->stat[0].scale = FE_SCALE_DECIBEL;
- }
-
- /* per-layer post viterbi BER (or PER? config dependent?) */
- stats = &c->post_bit_error;
- memset(stats, 0, sizeof(*stats));
- stats->len = layers;
- ret = reg_read(state, 0xeb, val, 10);
- if (ret < 0)
- for (i = 0; i < layers; i++)
- stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE;
- else {
- for (i = 0; i < layers; i++) {
- stats->stat[i].scale = FE_SCALE_COUNTER;
- stats->stat[i].uvalue = val[i * 5] << 16
- | val[i * 5 + 1] << 8 | val[i * 5 + 2];
- }
- }
- stats = &c->post_bit_count;
- memset(stats, 0, sizeof(*stats));
- stats->len = layers;
- if (ret < 0)
- for (i = 0; i < layers; i++)
- stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE;
- else {
- for (i = 0; i < layers; i++) {
- stats->stat[i].scale = FE_SCALE_COUNTER;
- stats->stat[i].uvalue =
- val[i * 5 + 3] << 8 | val[i * 5 + 4];
- stats->stat[i].uvalue *= 204 * 8;
- }
- }
-
- return 0;
-}
-
-
-static const enum fe_transmit_mode tm_conv[] = {
- TRANSMISSION_MODE_2K,
- TRANSMISSION_MODE_4K,
- TRANSMISSION_MODE_8K,
- 0
-};
-
-static const enum fe_code_rate fec_conv_ter[] = {
- FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, 0, 0, 0
-};
-
-static const enum fe_modulation mod_conv[] = {
- DQPSK, QPSK, QAM_16, QAM_64, 0, 0, 0, 0
-};
-
-static int tc90522t_get_frontend(struct dvb_frontend *fe,
- struct dtv_frontend_properties *c)
-{
- struct tc90522_state *state;
- struct dtv_fe_stats *stats;
- int ret, i;
- int layers;
- u8 val[15], mode;
- u32 cndat;
-
- state = fe->demodulator_priv;
- c->delivery_system = SYS_ISDBT;
- c->bandwidth_hz = 6000000;
- mode = 1;
- ret = reg_read(state, 0xb0, val, 1);
- if (ret == 0) {
- mode = (val[0] & 0xc0) >> 2;
- c->transmission_mode = tm_conv[mode];
- c->guard_interval = (val[0] & 0x30) >> 4;
- }
-
- ret = reg_read(state, 0xb2, val, 6);
- layers = 0;
- if (ret == 0) {
- u8 v;
-
- c->isdbt_partial_reception = val[0] & 0x01;
- c->isdbt_sb_mode = (val[0] & 0xc0) == 0x40;
-
- /* layer A */
- v = (val[2] & 0x78) >> 3;
- if (v == 0x0f)
- c->layer[0].segment_count = 0;
- else {
- layers++;
- c->layer[0].segment_count = v;
- c->layer[0].fec = fec_conv_ter[(val[1] & 0x1c) >> 2];
- c->layer[0].modulation = mod_conv[(val[1] & 0xe0) >> 5];
- v = (val[1] & 0x03) << 1 | (val[2] & 0x80) >> 7;
- c->layer[0].interleaving = v;
- }
-
- /* layer B */
- v = (val[3] & 0x03) << 1 | (val[4] & 0xc0) >> 6;
- if (v == 0x0f)
- c->layer[1].segment_count = 0;
- else {
- layers++;
- c->layer[1].segment_count = v;
- c->layer[1].fec = fec_conv_ter[(val[3] & 0xe0) >> 5];
- c->layer[1].modulation = mod_conv[(val[2] & 0x07)];
- c->layer[1].interleaving = (val[3] & 0x1c) >> 2;
- }
-
- /* layer C */
- v = (val[5] & 0x1e) >> 1;
- if (v == 0x0f)
- c->layer[2].segment_count = 0;
- else {
- layers++;
- c->layer[2].segment_count = v;
- c->layer[2].fec = fec_conv_ter[(val[4] & 0x07)];
- c->layer[2].modulation = mod_conv[(val[4] & 0x38) >> 3];
- c->layer[2].interleaving = (val[5] & 0xe0) >> 5;
- }
- }
-
- /* statistics */
-
- stats = &c->strength;
- stats->len = 0;
- /* let the connected tuner set RSSI property cache */
- if (fe->ops.tuner_ops.get_rf_strength) {
- u16 dummy;
-
- fe->ops.tuner_ops.get_rf_strength(fe, &dummy);
- }
-
- stats = &c->cnr;
- stats->len = 1;
- stats->stat[0].scale = FE_SCALE_NOT_AVAILABLE;
- cndat = 0;
- ret = reg_read(state, 0x8b, val, 3);
- if (ret == 0)
- cndat = val[0] << 16 | val[1] << 8 | val[2];
- if (cndat != 0) {
- u32 p, tmp;
- s64 cn;
-
- /*
- * cnr[mdB] = 0.024 P^4 - 1.6 P^3 + 39.8 P^2 + 549.1 P + 3096.5
- * (P = 10log10(5505024/cndat))
- */
- /* cn = cnr << 3 (61.3 fixed point float */
- /* p = 10log10(5505024/cndat) << 24 (8.24 fixed point float)*/
- p = intlog10(5505024) - intlog10(cndat);
- p *= 10;
-
- cn = 24772;
- cn += div64_s64(43827LL * p, 10) >> 24;
- tmp = p >> 8;
- cn += div64_s64(3184LL * tmp * tmp, 10) >> 32;
- tmp = p >> 13;
- cn -= div64_s64(128LL * tmp * tmp * tmp, 10) >> 33;
- tmp = p >> 18;
- cn += div64_s64(192LL * tmp * tmp * tmp * tmp, 1000) >> 24;
-
- stats->stat[0].svalue = cn >> 3;
- stats->stat[0].scale = FE_SCALE_DECIBEL;
- }
-
- /* per-layer post viterbi BER (or PER? config dependent?) */
- stats = &c->post_bit_error;
- memset(stats, 0, sizeof(*stats));
- stats->len = layers;
- ret = reg_read(state, 0x9d, val, 15);
- if (ret < 0)
- for (i = 0; i < layers; i++)
- stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE;
- else {
- for (i = 0; i < layers; i++) {
- stats->stat[i].scale = FE_SCALE_COUNTER;
- stats->stat[i].uvalue = val[i * 3] << 16
- | val[i * 3 + 1] << 8 | val[i * 3 + 2];
- }
- }
- stats = &c->post_bit_count;
- memset(stats, 0, sizeof(*stats));
- stats->len = layers;
- if (ret < 0)
- for (i = 0; i < layers; i++)
- stats->stat[i].scale = FE_SCALE_NOT_AVAILABLE;
- else {
- for (i = 0; i < layers; i++) {
- stats->stat[i].scale = FE_SCALE_COUNTER;
- stats->stat[i].uvalue =
- val[9 + i * 2] << 8 | val[9 + i * 2 + 1];
- stats->stat[i].uvalue *= 204 * 8;
- }
- }
-
- return 0;
-}
-
-static const struct reg_val reset_sat = { 0x03, 0x01 };
-static const struct reg_val reset_ter = { 0x01, 0x40 };
-
-static int tc90522_set_frontend(struct dvb_frontend *fe)
-{
- struct tc90522_state *state;
- int ret;
-
- state = fe->demodulator_priv;
-
- if (fe->ops.tuner_ops.set_params)
- ret = fe->ops.tuner_ops.set_params(fe);
- else
- ret = -ENODEV;
- if (ret < 0)
- goto failed;
-
- if (fe->ops.delsys[0] == SYS_ISDBS) {
- ret = tc90522s_set_tsid(fe);
- if (ret < 0)
- goto failed;
- ret = reg_write(state, &reset_sat, 1);
- } else {
- ret = tc90522t_set_layers(fe);
- if (ret < 0)
- goto failed;
- ret = reg_write(state, &reset_ter, 1);
- }
- if (ret < 0)
- goto failed;
-
- return 0;
-
-failed:
- dev_warn(&state->tuner_i2c.dev, "(%s) failed. [adap%d-fe%d]\n",
- __func__, fe->dvb->num, fe->id);
- return ret;
-}
-
-static int tc90522_get_tune_settings(struct dvb_frontend *fe,
- struct dvb_frontend_tune_settings *settings)
-{
- if (fe->ops.delsys[0] == SYS_ISDBS) {
- settings->min_delay_ms = 250;
- settings->step_size = 1000;
- settings->max_drift = settings->step_size * 2;
- } else {
- settings->min_delay_ms = 400;
- settings->step_size = 142857;
- settings->max_drift = settings->step_size;
- }
- return 0;
-}
-
-static int tc90522_set_if_agc(struct dvb_frontend *fe, bool on)
-{
- struct reg_val agc_sat[] = {
- { 0x0a, 0x00 },
- { 0x10, 0x30 },
- { 0x11, 0x00 },
- { 0x03, 0x01 },
- };
- struct reg_val agc_ter[] = {
- { 0x25, 0x00 },
- { 0x23, 0x4c },
- { 0x01, 0x40 },
- };
- struct tc90522_state *state;
- struct reg_val *rv;
- int num;
-
- state = fe->demodulator_priv;
- if (fe->ops.delsys[0] == SYS_ISDBS) {
- agc_sat[0].val = on ? 0xff : 0x00;
- agc_sat[1].val |= 0x80;
- agc_sat[1].val |= on ? 0x01 : 0x00;
- agc_sat[2].val |= on ? 0x40 : 0x00;
- rv = agc_sat;
- num = ARRAY_SIZE(agc_sat);
- } else {
- agc_ter[0].val = on ? 0x40 : 0x00;
- agc_ter[1].val |= on ? 0x00 : 0x01;
- rv = agc_ter;
- num = ARRAY_SIZE(agc_ter);
- }
- return reg_write(state, rv, num);
-}
-
-static const struct reg_val sleep_sat = { 0x17, 0x01 };
-static const struct reg_val sleep_ter = { 0x03, 0x90 };
-
-static int tc90522_sleep(struct dvb_frontend *fe)
-{
- struct tc90522_state *state;
- int ret;
-
- state = fe->demodulator_priv;
- if (fe->ops.delsys[0] == SYS_ISDBS)
- ret = reg_write(state, &sleep_sat, 1);
- else {
- ret = reg_write(state, &sleep_ter, 1);
- if (ret == 0 && fe->ops.set_lna &&
- fe->dtv_property_cache.lna == LNA_AUTO) {
- fe->dtv_property_cache.lna = 0;
- ret = fe->ops.set_lna(fe);
- fe->dtv_property_cache.lna = LNA_AUTO;
- }
- }
- if (ret < 0)
- dev_warn(&state->tuner_i2c.dev,
- "(%s) failed. [adap%d-fe%d]\n",
- __func__, fe->dvb->num, fe->id);
- return ret;
-}
-
-static const struct reg_val wakeup_sat = { 0x17, 0x00 };
-static const struct reg_val wakeup_ter = { 0x03, 0x80 };
-
-static int tc90522_init(struct dvb_frontend *fe)
-{
- struct tc90522_state *state;
- int ret;
-
- /*
- * Because the init sequence is not public,
- * the parent device/driver should have init'ed the device before.
- * just wake up the device here.
- */
-
- state = fe->demodulator_priv;
- if (fe->ops.delsys[0] == SYS_ISDBS)
- ret = reg_write(state, &wakeup_sat, 1);
- else {
- ret = reg_write(state, &wakeup_ter, 1);
- if (ret == 0 && fe->ops.set_lna &&
- fe->dtv_property_cache.lna == LNA_AUTO) {
- fe->dtv_property_cache.lna = 1;
- ret = fe->ops.set_lna(fe);
- fe->dtv_property_cache.lna = LNA_AUTO;
- }
- }
- if (ret < 0) {
- dev_warn(&state->tuner_i2c.dev,
- "(%s) failed. [adap%d-fe%d]\n",
- __func__, fe->dvb->num, fe->id);
- return ret;
- }
-
- /* prefer 'all-layers' to 'none' as a default */
- if (fe->dtv_property_cache.isdbt_layer_enabled == 0)
- fe->dtv_property_cache.isdbt_layer_enabled = 7;
- return tc90522_set_if_agc(fe, true);
-}
-
-
-/*
- * tuner I2C adapter functions
- */
-
-static int
-tc90522_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
-{
- struct tc90522_state *state;
- struct i2c_msg *new_msgs;
- int i, j;
- int ret, rd_num;
- u8 wbuf[256];
- u8 *p, *bufend;
-
- if (num <= 0)
- return -EINVAL;
-
- rd_num = 0;
- for (i = 0; i < num; i++)
- if (msgs[i].flags & I2C_M_RD)
- rd_num++;
- new_msgs = kmalloc(sizeof(*new_msgs) * (num + rd_num), GFP_KERNEL);
- if (!new_msgs)
- return -ENOMEM;
-
- state = i2c_get_adapdata(adap);
- p = wbuf;
- bufend = wbuf + sizeof(wbuf);
- for (i = 0, j = 0; i < num; i++, j++) {
- new_msgs[j].addr = state->i2c_client->addr;
- new_msgs[j].flags = msgs[i].flags;
-
- if (msgs[i].flags & I2C_M_RD) {
- new_msgs[j].flags &= ~I2C_M_RD;
- if (p + 2 > bufend)
- break;
- p[0] = TC90522_I2C_THRU_REG;
- p[1] = msgs[i].addr << 1 | 0x01;
- new_msgs[j].buf = p;
- new_msgs[j].len = 2;
- p += 2;
- j++;
- new_msgs[j].addr = state->i2c_client->addr;
- new_msgs[j].flags = msgs[i].flags;
- new_msgs[j].buf = msgs[i].buf;
- new_msgs[j].len = msgs[i].len;
- continue;
- }
-
- if (p + msgs[i].len + 2 > bufend)
- break;
- p[0] = TC90522_I2C_THRU_REG;
- p[1] = msgs[i].addr << 1;
- memcpy(p + 2, msgs[i].buf, msgs[i].len);
- new_msgs[j].buf = p;
- new_msgs[j].len = msgs[i].len + 2;
- p += new_msgs[j].len;
- }
-
- if (i < num)
- ret = -ENOMEM;
- else
- ret = i2c_transfer(state->i2c_client->adapter, new_msgs, j);
- if (ret >= 0 && ret < j)
- ret = -EIO;
- kfree(new_msgs);
- return (ret == j) ? num : ret;
-}
-
-static u32 tc90522_functionality(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C;
-}
-
-static const struct i2c_algorithm tc90522_tuner_i2c_algo = {
- .master_xfer = &tc90522_master_xfer,
- .functionality = &tc90522_functionality,
-};
-
-
-/*
- * I2C driver functions
- */
-
-static const struct dvb_frontend_ops tc90522_ops_sat = {
- .delsys = { SYS_ISDBS },
- .info = {
- .name = "Toshiba TC90522 ISDB-S module",
- .frequency_min = 950000,
- .frequency_max = 2150000,
- .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO |
- FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
- },
-
- .init = tc90522_init,
- .sleep = tc90522_sleep,
- .set_frontend = tc90522_set_frontend,
- .get_tune_settings = tc90522_get_tune_settings,
-
- .get_frontend = tc90522s_get_frontend,
- .read_status = tc90522s_read_status,
-};
-
-static const struct dvb_frontend_ops tc90522_ops_ter = {
- .delsys = { SYS_ISDBT },
- .info = {
- .name = "Toshiba TC90522 ISDB-T module",
- .frequency_min = 470000000,
- .frequency_max = 770000000,
- .frequency_stepsize = 142857,
- .caps = FE_CAN_INVERSION_AUTO |
- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 |
- FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_RECOVER |
- FE_CAN_HIERARCHY_AUTO,
- },
-
- .init = tc90522_init,
- .sleep = tc90522_sleep,
- .set_frontend = tc90522_set_frontend,
- .get_tune_settings = tc90522_get_tune_settings,
-
- .get_frontend = tc90522t_get_frontend,
- .read_status = tc90522t_read_status,
-};
-
-
-static int tc90522_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct tc90522_state *state;
- struct tc90522_config *cfg;
- const struct dvb_frontend_ops *ops;
- struct i2c_adapter *adap;
- int ret;
-
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
- state->i2c_client = client;
-
- cfg = client->dev.platform_data;
- memcpy(&state->cfg, cfg, sizeof(state->cfg));
- cfg->fe = state->cfg.fe = &state->fe;
- ops = id->driver_data == 0 ? &tc90522_ops_sat : &tc90522_ops_ter;
- memcpy(&state->fe.ops, ops, sizeof(*ops));
- state->fe.demodulator_priv = state;
-
- adap = &state->tuner_i2c;
- adap->owner = THIS_MODULE;
- adap->algo = &tc90522_tuner_i2c_algo;
- adap->dev.parent = &client->dev;
- strlcpy(adap->name, "tc90522_sub", sizeof(adap->name));
- i2c_set_adapdata(adap, state);
- ret = i2c_add_adapter(adap);
- if (ret < 0)
- goto err;
- cfg->tuner_i2c = state->cfg.tuner_i2c = adap;
-
- i2c_set_clientdata(client, &state->cfg);
- dev_info(&client->dev, "Toshiba TC90522 attached.\n");
- return 0;
-
-err:
- kfree(state);
- return ret;
-}
-
-static int tc90522_remove(struct i2c_client *client)
-{
- struct tc90522_state *state;
-
- state = cfg_to_state(i2c_get_clientdata(client));
- i2c_del_adapter(&state->tuner_i2c);
- kfree(state);
- return 0;
-}
-
-
-static const struct i2c_device_id tc90522_id[] = {
- { TC90522_I2C_DEV_SAT, 0 },
- { TC90522_I2C_DEV_TER, 1 },
- {}
-};
-MODULE_DEVICE_TABLE(i2c, tc90522_id);
-
-static struct i2c_driver tc90522_driver = {
- .driver = {
- .name = "tc90522",
- },
- .probe = tc90522_probe,
- .remove = tc90522_remove,
- .id_table = tc90522_id,
-};
-
-module_i2c_driver(tc90522_driver);
-
-MODULE_DESCRIPTION("Toshiba TC90522 frontend");
-MODULE_AUTHOR("Akihiro TSUKADA");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb-frontends/tc90522.h b/drivers/media/dvb-frontends/tc90522.h
deleted file mode 100644
index b1cbddf..0000000
--- a/drivers/media/dvb-frontends/tc90522.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Toshiba TC90522 Demodulator
- *
- * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * 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.
- *
- *
- * 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.
- */
-
-/*
- * The demod has 4 input (2xISDB-T and 2xISDB-S),
- * and provides independent sub modules for each input.
- * As the sub modules work in parallel and have the separate i2c addr's,
- * this driver treats each sub module as one demod device.
- */
-
-#ifndef TC90522_H
-#define TC90522_H
-
-#include <linux/i2c.h>
-#include "dvb_frontend.h"
-
-/* I2C device types */
-#define TC90522_I2C_DEV_SAT "tc90522sat"
-#define TC90522_I2C_DEV_TER "tc90522ter"
-
-struct tc90522_config {
- /* [OUT] frontend returned by driver */
- struct dvb_frontend *fe;
-
- /* [OUT] tuner I2C adapter returned by driver */
- struct i2c_adapter *tuner_i2c;
-};
-
-#endif /* TC90522_H */
diff --git a/drivers/media/pci/pt3/Kconfig b/drivers/media/pci/pt3/Kconfig
deleted file mode 100644
index 16c208a..0000000
--- a/drivers/media/pci/pt3/Kconfig
+++ /dev/null
@@ -1,10 +0,0 @@
-config DVB_PT3
- tristate "Earthsoft PT3 cards"
- depends on DVB_CORE && PCI && I2C
- select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT
- select MEDIA_TUNER_QM1D1C0042 if MEDIA_SUBDRV_AUTOSELECT
- select MEDIA_TUNER_MXL301RF if MEDIA_SUBDRV_AUTOSELECT
- help
- Support for Earthsoft PT3 PCIe cards.
-
- Say Y or M if you own such a device and want to use it.
diff --git a/drivers/media/pci/pt3/Makefile b/drivers/media/pci/pt3/Makefile
deleted file mode 100644
index 396f146..0000000
--- a/drivers/media/pci/pt3/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-
-earth-pt3-objs += pt3.o pt3_i2c.o pt3_dma.o
-
-obj-$(CONFIG_DVB_PT3) += earth-pt3.o
-
-ccflags-y += -Idrivers/media/dvb-core
-ccflags-y += -Idrivers/media/dvb-frontends
-ccflags-y += -Idrivers/media/tuners
diff --git a/drivers/media/pci/pt3/pt3.c b/drivers/media/pci/pt3/pt3.c
deleted file mode 100644
index 0d2e2b2..0000000
--- a/drivers/media/pci/pt3/pt3.c
+++ /dev/null
@@ -1,873 +0,0 @@
-/*
- * Earthsoft PT3 driver
- *
- * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * 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.
- *
- *
- * 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 <linux/freezer.h>
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/mutex.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/string.h>
-
-#include "dmxdev.h"
-#include "dvbdev.h"
-#include "dvb_demux.h"
-#include "dvb_frontend.h"
-
-#include "pt3.h"
-
-DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
-
-static bool one_adapter;
-module_param(one_adapter, bool, 0444);
-MODULE_PARM_DESC(one_adapter, "Place FE's together under one adapter.");
-
-static int num_bufs = 4;
-module_param(num_bufs, int, 0444);
-MODULE_PARM_DESC(num_bufs, "Number of DMA buffer (188KiB) per FE.");
-
-
-static const struct i2c_algorithm pt3_i2c_algo = {
- .master_xfer = &pt3_i2c_master_xfer,
- .functionality = &pt3_i2c_functionality,
-};
-
-static const struct pt3_adap_config adap_conf[PT3_NUM_FE] = {
- {
- .demod_info = {
- I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x11),
- },
- .tuner_info = {
- I2C_BOARD_INFO("qm1d1c0042", 0x63),
- },
- .tuner_cfg.qm1d1c0042 = {
- .lpf = 1,
- },
- .init_freq = 1049480 - 300,
- },
- {
- .demod_info = {
- I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x10),
- },
- .tuner_info = {
- I2C_BOARD_INFO("mxl301rf", 0x62),
- },
- .init_freq = 515142857,
- },
- {
- .demod_info = {
- I2C_BOARD_INFO(TC90522_I2C_DEV_SAT, 0x13),
- },
- .tuner_info = {
- I2C_BOARD_INFO("qm1d1c0042", 0x60),
- },
- .tuner_cfg.qm1d1c0042 = {
- .lpf = 1,
- },
- .init_freq = 1049480 + 300,
- },
- {
- .demod_info = {
- I2C_BOARD_INFO(TC90522_I2C_DEV_TER, 0x12),
- },
- .tuner_info = {
- I2C_BOARD_INFO("mxl301rf", 0x61),
- },
- .init_freq = 521142857,
- },
-};
-
-
-struct reg_val {
- u8 reg;
- u8 val;
-};
-
-static int
-pt3_demod_write(struct pt3_adapter *adap, const struct reg_val *data, int num)
-{
- struct i2c_msg msg;
- int i, ret;
-
- ret = 0;
- msg.addr = adap->i2c_demod->addr;
- msg.flags = 0;
- msg.len = 2;
- for (i = 0; i < num; i++) {
- msg.buf = (u8 *)&data[i];
- ret = i2c_transfer(adap->i2c_demod->adapter, &msg, 1);
- if (ret == 0)
- ret = -EREMOTE;
- if (ret < 0)
- return ret;
- }
- return 0;
-}
-
-static inline void pt3_lnb_ctrl(struct pt3_board *pt3, bool on)
-{
- iowrite32((on ? 0x0f : 0x0c), pt3->regs[0] + REG_SYSTEM_W);
-}
-
-static inline struct pt3_adapter *pt3_find_adapter(struct dvb_frontend *fe)
-{
- struct pt3_board *pt3;
- int i;
-
- if (one_adapter) {
- pt3 = fe->dvb->priv;
- for (i = 0; i < PT3_NUM_FE; i++)
- if (pt3->adaps[i]->fe == fe)
- return pt3->adaps[i];
- }
- return container_of(fe->dvb, struct pt3_adapter, dvb_adap);
-}
-
-/*
- * all 4 tuners in PT3 are packaged in a can module (Sharp VA4M6JC2103).
- * it seems that they share the power lines and Amp power line and
- * adaps[3] controls those powers.
- */
-static int
-pt3_set_tuner_power(struct pt3_board *pt3, bool tuner_on, bool amp_on)
-{
- struct reg_val rv = { 0x1e, 0x99 };
-
- if (tuner_on)
- rv.val |= 0x40;
- if (amp_on)
- rv.val |= 0x04;
- return pt3_demod_write(pt3->adaps[PT3_NUM_FE - 1], &rv, 1);
-}
-
-static int pt3_set_lna(struct dvb_frontend *fe)
-{
- struct pt3_adapter *adap;
- struct pt3_board *pt3;
- u32 val;
- int ret;
-
- /* LNA is shared btw. 2 TERR-tuners */
-
- adap = pt3_find_adapter(fe);
- val = fe->dtv_property_cache.lna;
- if (val == LNA_AUTO || val == adap->cur_lna)
- return 0;
-
- pt3 = adap->dvb_adap.priv;
- if (mutex_lock_interruptible(&pt3->lock))
- return -ERESTARTSYS;
- if (val)
- pt3->lna_on_cnt++;
- else
- pt3->lna_on_cnt--;
-
- if (val && pt3->lna_on_cnt <= 1) {
- pt3->lna_on_cnt = 1;
- ret = pt3_set_tuner_power(pt3, true, true);
- } else if (!val && pt3->lna_on_cnt <= 0) {
- pt3->lna_on_cnt = 0;
- ret = pt3_set_tuner_power(pt3, true, false);
- } else
- ret = 0;
- mutex_unlock(&pt3->lock);
- adap->cur_lna = (val != 0);
- return ret;
-}
-
-static int pt3_set_voltage(struct dvb_frontend *fe, enum fe_sec_voltage volt)
-{
- struct pt3_adapter *adap;
- struct pt3_board *pt3;
- bool on;
-
- /* LNB power is shared btw. 2 SAT-tuners */
-
- adap = pt3_find_adapter(fe);
- on = (volt != SEC_VOLTAGE_OFF);
- if (on == adap->cur_lnb)
- return 0;
- adap->cur_lnb = on;
- pt3 = adap->dvb_adap.priv;
- if (mutex_lock_interruptible(&pt3->lock))
- return -ERESTARTSYS;
- if (on)
- pt3->lnb_on_cnt++;
- else
- pt3->lnb_on_cnt--;
-
- if (on && pt3->lnb_on_cnt <= 1) {
- pt3->lnb_on_cnt = 1;
- pt3_lnb_ctrl(pt3, true);
- } else if (!on && pt3->lnb_on_cnt <= 0) {
- pt3->lnb_on_cnt = 0;
- pt3_lnb_ctrl(pt3, false);
- }
- mutex_unlock(&pt3->lock);
- return 0;
-}
-
-/* register values used in pt3_fe_init() */
-
-static const struct reg_val init0_sat[] = {
- { 0x03, 0x01 },
- { 0x1e, 0x10 },
-};
-static const struct reg_val init0_ter[] = {
- { 0x01, 0x40 },
- { 0x1c, 0x10 },
-};
-static const struct reg_val cfg_sat[] = {
- { 0x1c, 0x15 },
- { 0x1f, 0x04 },
-};
-static const struct reg_val cfg_ter[] = {
- { 0x1d, 0x01 },
-};
-
-/*
- * pt3_fe_init: initialize demod sub modules and ISDB-T tuners all at once.
- *
- * As for demod IC (TC90522) and ISDB-T tuners (MxL301RF),
- * the i2c sequences for init'ing them are not public and hidden in a ROM,
- * and include the board specific configurations as well.
- * They are stored in a lump and cannot be taken out / accessed separately,
- * thus cannot be moved to the FE/tuner driver.
- */
-static int pt3_fe_init(struct pt3_board *pt3)
-{
- int i, ret;
- struct dvb_frontend *fe;
-
- pt3_i2c_reset(pt3);
- ret = pt3_init_all_demods(pt3);
- if (ret < 0) {
- dev_warn(&pt3->pdev->dev, "Failed to init demod chips\n");
- return ret;
- }
-
- /* additional config? */
- for (i = 0; i < PT3_NUM_FE; i++) {
- fe = pt3->adaps[i]->fe;
-
- if (fe->ops.delsys[0] == SYS_ISDBS)
- ret = pt3_demod_write(pt3->adaps[i],
- init0_sat, ARRAY_SIZE(init0_sat));
- else
- ret = pt3_demod_write(pt3->adaps[i],
- init0_ter, ARRAY_SIZE(init0_ter));
- if (ret < 0) {
- dev_warn(&pt3->pdev->dev,
- "demod[%d] failed in init sequence0\n", i);
- return ret;
- }
- ret = fe->ops.init(fe);
- if (ret < 0)
- return ret;
- }
-
- usleep_range(2000, 4000);
- ret = pt3_set_tuner_power(pt3, true, false);
- if (ret < 0) {
- dev_warn(&pt3->pdev->dev, "Failed to control tuner module\n");
- return ret;
- }
-
- /* output pin configuration */
- for (i = 0; i < PT3_NUM_FE; i++) {
- fe = pt3->adaps[i]->fe;
- if (fe->ops.delsys[0] == SYS_ISDBS)
- ret = pt3_demod_write(pt3->adaps[i],
- cfg_sat, ARRAY_SIZE(cfg_sat));
- else
- ret = pt3_demod_write(pt3->adaps[i],
- cfg_ter, ARRAY_SIZE(cfg_ter));
- if (ret < 0) {
- dev_warn(&pt3->pdev->dev,
- "demod[%d] failed in init sequence1\n", i);
- return ret;
- }
- }
- usleep_range(4000, 6000);
-
- for (i = 0; i < PT3_NUM_FE; i++) {
- fe = pt3->adaps[i]->fe;
- if (fe->ops.delsys[0] != SYS_ISDBS)
- continue;
- /* init and wake-up ISDB-S tuners */
- ret = fe->ops.tuner_ops.init(fe);
- if (ret < 0) {
- dev_warn(&pt3->pdev->dev,
- "Failed to init SAT-tuner[%d]\n", i);
- return ret;
- }
- }
- ret = pt3_init_all_mxl301rf(pt3);
- if (ret < 0) {
- dev_warn(&pt3->pdev->dev, "Failed to init TERR-tuners\n");
- return ret;
- }
-
- ret = pt3_set_tuner_power(pt3, true, true);
- if (ret < 0) {
- dev_warn(&pt3->pdev->dev, "Failed to control tuner module\n");
- return ret;
- }
-
- /* Wake up all tuners and make an initial tuning,
- * in order to avoid interference among the tuners in the module,
- * according to the doc from the manufacturer.
- */
- for (i = 0; i < PT3_NUM_FE; i++) {
- fe = pt3->adaps[i]->fe;
- ret = 0;
- if (fe->ops.delsys[0] == SYS_ISDBT)
- ret = fe->ops.tuner_ops.init(fe);
- /* set only when called from pt3_probe(), not resume() */
- if (ret == 0 && fe->dtv_property_cache.frequency == 0) {
- fe->dtv_property_cache.frequency =
- adap_conf[i].init_freq;
- ret = fe->ops.tuner_ops.set_params(fe);
- }
- if (ret < 0) {
- dev_warn(&pt3->pdev->dev,
- "Failed in initial tuning of tuner[%d]\n", i);
- return ret;
- }
- }
-
- /* and sleep again, waiting to be opened by users. */
- for (i = 0; i < PT3_NUM_FE; i++) {
- fe = pt3->adaps[i]->fe;
- if (fe->ops.tuner_ops.sleep)
- ret = fe->ops.tuner_ops.sleep(fe);
- if (ret < 0)
- break;
- if (fe->ops.sleep)
- ret = fe->ops.sleep(fe);
- if (ret < 0)
- break;
- if (fe->ops.delsys[0] == SYS_ISDBS)
- fe->ops.set_voltage = &pt3_set_voltage;
- else
- fe->ops.set_lna = &pt3_set_lna;
- }
- if (i < PT3_NUM_FE) {
- dev_warn(&pt3->pdev->dev, "FE[%d] failed to standby\n", i);
- return ret;
- }
- return 0;
-}
-
-
-static int pt3_attach_fe(struct pt3_board *pt3, int i)
-{
- struct i2c_board_info info;
- struct tc90522_config cfg;
- struct i2c_client *cl;
- struct dvb_adapter *dvb_adap;
- int ret;
-
- info = adap_conf[i].demod_info;
- cfg = adap_conf[i].demod_cfg;
- cfg.tuner_i2c = NULL;
- info.platform_data = &cfg;
-
- ret = -ENODEV;
- request_module("tc90522");
- cl = i2c_new_device(&pt3->i2c_adap, &info);
- if (!cl || !cl->dev.driver)
- return -ENODEV;
- pt3->adaps[i]->i2c_demod = cl;
- if (!try_module_get(cl->dev.driver->owner))
- goto err_demod_i2c_unregister_device;
-
- if (!strncmp(cl->name, TC90522_I2C_DEV_SAT, sizeof(cl->name))) {
- struct qm1d1c0042_config tcfg;
-
- tcfg = adap_conf[i].tuner_cfg.qm1d1c0042;
- tcfg.fe = cfg.fe;
- info = adap_conf[i].tuner_info;
- info.platform_data = &tcfg;
- request_module("qm1d1c0042");
- cl = i2c_new_device(cfg.tuner_i2c, &info);
- } else {
- struct mxl301rf_config tcfg;
-
- tcfg = adap_conf[i].tuner_cfg.mxl301rf;
- tcfg.fe = cfg.fe;
- info = adap_conf[i].tuner_info;
- info.platform_data = &tcfg;
- request_module("mxl301rf");
- cl = i2c_new_device(cfg.tuner_i2c, &info);
- }
- if (!cl || !cl->dev.driver)
- goto err_demod_module_put;
- pt3->adaps[i]->i2c_tuner = cl;
- if (!try_module_get(cl->dev.driver->owner))
- goto err_tuner_i2c_unregister_device;
-
- dvb_adap = &pt3->adaps[one_adapter ? 0 : i]->dvb_adap;
- ret = dvb_register_frontend(dvb_adap, cfg.fe);
- if (ret < 0)
- goto err_tuner_module_put;
- pt3->adaps[i]->fe = cfg.fe;
- return 0;
-
-err_tuner_module_put:
- module_put(pt3->adaps[i]->i2c_tuner->dev.driver->owner);
-err_tuner_i2c_unregister_device:
- i2c_unregister_device(pt3->adaps[i]->i2c_tuner);
-err_demod_module_put:
- module_put(pt3->adaps[i]->i2c_demod->dev.driver->owner);
-err_demod_i2c_unregister_device:
- i2c_unregister_device(pt3->adaps[i]->i2c_demod);
-
- return ret;
-}
-
-
-static int pt3_fetch_thread(void *data)
-{
- struct pt3_adapter *adap = data;
- ktime_t delay;
- bool was_frozen;
-
-#define PT3_INITIAL_BUF_DROPS 4
-#define PT3_FETCH_DELAY 10
-#define PT3_FETCH_DELAY_DELTA 2
-
- pt3_init_dmabuf(adap);
- adap->num_discard = PT3_INITIAL_BUF_DROPS;
-
- dev_dbg(adap->dvb_adap.device, "PT3: [%s] started\n",
- adap->thread->comm);
- set_freezable();
- while (!kthread_freezable_should_stop(&was_frozen)) {
- if (was_frozen)
- adap->num_discard = PT3_INITIAL_BUF_DROPS;
-
- pt3_proc_dma(adap);
-
- delay = ktime_set(0, PT3_FETCH_DELAY * NSEC_PER_MSEC);
- set_current_state(TASK_UNINTERRUPTIBLE);
- freezable_schedule_hrtimeout_range(&delay,
- PT3_FETCH_DELAY_DELTA * NSEC_PER_MSEC,
- HRTIMER_MODE_REL);
- }
- dev_dbg(adap->dvb_adap.device, "PT3: [%s] exited\n",
- adap->thread->comm);
- adap->thread = NULL;
- return 0;
-}
-
-static int pt3_start_streaming(struct pt3_adapter *adap)
-{
- struct task_struct *thread;
-
- /* start fetching thread */
- thread = kthread_run(pt3_fetch_thread, adap, "pt3-ad%i-dmx%i",
- adap->dvb_adap.num, adap->dmxdev.dvbdev->id);
- if (IS_ERR(thread)) {
- int ret = PTR_ERR(thread);
-
- dev_warn(adap->dvb_adap.device,
- "PT3 (adap:%d, dmx:%d): failed to start kthread\n",
- adap->dvb_adap.num, adap->dmxdev.dvbdev->id);
- return ret;
- }
- adap->thread = thread;
-
- return pt3_start_dma(adap);
-}
-
-static int pt3_stop_streaming(struct pt3_adapter *adap)
-{
- int ret;
-
- ret = pt3_stop_dma(adap);
- if (ret)
- dev_warn(adap->dvb_adap.device,
- "PT3: failed to stop streaming of adap:%d/FE:%d\n",
- adap->dvb_adap.num, adap->fe->id);
-
- /* kill the fetching thread */
- ret = kthread_stop(adap->thread);
- return ret;
-}
-
-static int pt3_start_feed(struct dvb_demux_feed *feed)
-{
- struct pt3_adapter *adap;
-
- if (signal_pending(current))
- return -EINTR;
-
- adap = container_of(feed->demux, struct pt3_adapter, demux);
- adap->num_feeds++;
- if (adap->thread)
- return 0;
- if (adap->num_feeds != 1) {
- dev_warn(adap->dvb_adap.device,
- "%s: unmatched start/stop_feed in adap:%i/dmx:%i\n",
- __func__, adap->dvb_adap.num, adap->dmxdev.dvbdev->id);
- adap->num_feeds = 1;
- }
-
- return pt3_start_streaming(adap);
-
-}
-
-static int pt3_stop_feed(struct dvb_demux_feed *feed)
-{
- struct pt3_adapter *adap;
-
- adap = container_of(feed->demux, struct pt3_adapter, demux);
-
- adap->num_feeds--;
- if (adap->num_feeds > 0 || !adap->thread)
- return 0;
- adap->num_feeds = 0;
-
- return pt3_stop_streaming(adap);
-}
-
-
-static int pt3_alloc_adapter(struct pt3_board *pt3, int index)
-{
- int ret;
- struct pt3_adapter *adap;
- struct dvb_adapter *da;
-
- adap = kzalloc(sizeof(*adap), GFP_KERNEL);
- if (!adap)
- return -ENOMEM;
-
- pt3->adaps[index] = adap;
- adap->adap_idx = index;
-
- if (index == 0 || !one_adapter) {
- ret = dvb_register_adapter(&adap->dvb_adap, "PT3 DVB",
- THIS_MODULE, &pt3->pdev->dev, adapter_nr);
- if (ret < 0) {
- dev_err(&pt3->pdev->dev,
- "failed to register adapter dev\n");
- goto err_mem;
- }
- da = &adap->dvb_adap;
- } else
- da = &pt3->adaps[0]->dvb_adap;
-
- adap->dvb_adap.priv = pt3;
- adap->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
- adap->demux.priv = adap;
- adap->demux.feednum = 256;
- adap->demux.filternum = 256;
- adap->demux.start_feed = pt3_start_feed;
- adap->demux.stop_feed = pt3_stop_feed;
- ret = dvb_dmx_init(&adap->demux);
- if (ret < 0) {
- dev_err(&pt3->pdev->dev, "failed to init dmx dev\n");
- goto err_adap;
- }
-
- adap->dmxdev.filternum = 256;
- adap->dmxdev.demux = &adap->demux.dmx;
- ret = dvb_dmxdev_init(&adap->dmxdev, da);
- if (ret < 0) {
- dev_err(&pt3->pdev->dev, "failed to init dmxdev\n");
- goto err_demux;
- }
-
- ret = pt3_alloc_dmabuf(adap);
- if (ret) {
- dev_err(&pt3->pdev->dev, "failed to alloc DMA buffers\n");
- goto err_dmabuf;
- }
-
- return 0;
-
-err_dmabuf:
- pt3_free_dmabuf(adap);
- dvb_dmxdev_release(&adap->dmxdev);
-err_demux:
- dvb_dmx_release(&adap->demux);
-err_adap:
- if (index == 0 || !one_adapter)
- dvb_unregister_adapter(da);
-err_mem:
- kfree(adap);
- pt3->adaps[index] = NULL;
- return ret;
-}
-
-static void pt3_cleanup_adapter(struct pt3_board *pt3, int index)
-{
- struct pt3_adapter *adap;
- struct dmx_demux *dmx;
-
- adap = pt3->adaps[index];
- if (adap == NULL)
- return;
-
- /* stop demux kthread */
- if (adap->thread)
- pt3_stop_streaming(adap);
-
- dmx = &adap->demux.dmx;
- dmx->close(dmx);
- if (adap->fe) {
- adap->fe->callback = NULL;
- if (adap->fe->frontend_priv)
- dvb_unregister_frontend(adap->fe);
- if (adap->i2c_tuner) {
- module_put(adap->i2c_tuner->dev.driver->owner);
- i2c_unregister_device(adap->i2c_tuner);
- }
- if (adap->i2c_demod) {
- module_put(adap->i2c_demod->dev.driver->owner);
- i2c_unregister_device(adap->i2c_demod);
- }
- }
- pt3_free_dmabuf(adap);
- dvb_dmxdev_release(&adap->dmxdev);
- dvb_dmx_release(&adap->demux);
- if (index == 0 || !one_adapter)
- dvb_unregister_adapter(&adap->dvb_adap);
- kfree(adap);
- pt3->adaps[index] = NULL;
-}
-
-#ifdef CONFIG_PM_SLEEP
-
-static int pt3_suspend(struct device *dev)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct pt3_board *pt3 = pci_get_drvdata(pdev);
- int i;
- struct pt3_adapter *adap;
-
- for (i = 0; i < PT3_NUM_FE; i++) {
- adap = pt3->adaps[i];
- if (adap->num_feeds > 0)
- pt3_stop_dma(adap);
- dvb_frontend_suspend(adap->fe);
- pt3_free_dmabuf(adap);
- }
-
- pt3_lnb_ctrl(pt3, false);
- pt3_set_tuner_power(pt3, false, false);
- return 0;
-}
-
-static int pt3_resume(struct device *dev)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct pt3_board *pt3 = pci_get_drvdata(pdev);
- int i, ret;
- struct pt3_adapter *adap;
-
- ret = pt3_fe_init(pt3);
- if (ret)
- return ret;
-
- if (pt3->lna_on_cnt > 0)
- pt3_set_tuner_power(pt3, true, true);
- if (pt3->lnb_on_cnt > 0)
- pt3_lnb_ctrl(pt3, true);
-
- for (i = 0; i < PT3_NUM_FE; i++) {
- adap = pt3->adaps[i];
- dvb_frontend_resume(adap->fe);
- ret = pt3_alloc_dmabuf(adap);
- if (ret) {
- dev_err(&pt3->pdev->dev, "failed to alloc DMA bufs\n");
- continue;
- }
- if (adap->num_feeds > 0)
- pt3_start_dma(adap);
- }
-
- return 0;
-}
-
-#endif /* CONFIG_PM_SLEEP */
-
-
-static void pt3_remove(struct pci_dev *pdev)
-{
- struct pt3_board *pt3;
- int i;
-
- pt3 = pci_get_drvdata(pdev);
- for (i = PT3_NUM_FE - 1; i >= 0; i--)
- pt3_cleanup_adapter(pt3, i);
- i2c_del_adapter(&pt3->i2c_adap);
- kfree(pt3->i2c_buf);
- pci_iounmap(pt3->pdev, pt3->regs[0]);
- pci_iounmap(pt3->pdev, pt3->regs[1]);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
- kfree(pt3);
-}
-
-static int pt3_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
-{
- u8 rev;
- u32 ver;
- int i, ret;
- struct pt3_board *pt3;
- struct i2c_adapter *i2c;
-
- if (pci_read_config_byte(pdev, PCI_REVISION_ID, &rev) || rev != 1)
- return -ENODEV;
-
- ret = pci_enable_device(pdev);
- if (ret < 0)
- return -ENODEV;
- pci_set_master(pdev);
-
- ret = pci_request_regions(pdev, DRV_NAME);
- if (ret < 0)
- goto err_disable_device;
-
- ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
- if (ret == 0)
- dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
- else {
- ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
- if (ret == 0)
- dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
- else {
- dev_err(&pdev->dev, "Failed to set DMA mask\n");
- goto err_release_regions;
- }
- dev_info(&pdev->dev, "Use 32bit DMA\n");
- }
-
- pt3 = kzalloc(sizeof(*pt3), GFP_KERNEL);
- if (!pt3) {
- ret = -ENOMEM;
- goto err_release_regions;
- }
- pci_set_drvdata(pdev, pt3);
- pt3->pdev = pdev;
- mutex_init(&pt3->lock);
- pt3->regs[0] = pci_ioremap_bar(pdev, 0);
- pt3->regs[1] = pci_ioremap_bar(pdev, 2);
- if (pt3->regs[0] == NULL || pt3->regs[1] == NULL) {
- dev_err(&pdev->dev, "Failed to ioremap\n");
- ret = -ENOMEM;
- goto err_kfree;
- }
-
- ver = ioread32(pt3->regs[0] + REG_VERSION);
- if ((ver >> 16) != 0x0301) {
- dev_warn(&pdev->dev, "PT%d, I/F-ver.:%d not supported\n",
- ver >> 24, (ver & 0x00ff0000) >> 16);
- ret = -ENODEV;
- goto err_iounmap;
- }
-
- pt3->num_bufs = clamp_val(num_bufs, MIN_DATA_BUFS, MAX_DATA_BUFS);
-
- pt3->i2c_buf = kmalloc(sizeof(*pt3->i2c_buf), GFP_KERNEL);
- if (pt3->i2c_buf == NULL) {
- ret = -ENOMEM;
- goto err_iounmap;
- }
- i2c = &pt3->i2c_adap;
- i2c->owner = THIS_MODULE;
- i2c->algo = &pt3_i2c_algo;
- i2c->algo_data = NULL;
- i2c->dev.parent = &pdev->dev;
- strlcpy(i2c->name, DRV_NAME, sizeof(i2c->name));
- i2c_set_adapdata(i2c, pt3);
- ret = i2c_add_adapter(i2c);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to add i2c adapter\n");
- goto err_i2cbuf;
- }
-
- for (i = 0; i < PT3_NUM_FE; i++) {
- ret = pt3_alloc_adapter(pt3, i);
- if (ret < 0)
- break;
-
- ret = pt3_attach_fe(pt3, i);
- if (ret < 0)
- break;
- }
- if (i < PT3_NUM_FE) {
- dev_err(&pdev->dev, "Failed to create FE%d\n", i);
- goto err_cleanup_adapters;
- }
-
- ret = pt3_fe_init(pt3);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to init frontends\n");
- i = PT3_NUM_FE - 1;
- goto err_cleanup_adapters;
- }
-
- dev_info(&pdev->dev,
- "successfully init'ed PT%d (fw:0x%02x, I/F:0x%02x)\n",
- ver >> 24, (ver >> 8) & 0xff, (ver >> 16) & 0xff);
- return 0;
-
-err_cleanup_adapters:
- while (i >= 0)
- pt3_cleanup_adapter(pt3, i--);
- i2c_del_adapter(i2c);
-err_i2cbuf:
- kfree(pt3->i2c_buf);
-err_iounmap:
- if (pt3->regs[0])
- pci_iounmap(pdev, pt3->regs[0]);
- if (pt3->regs[1])
- pci_iounmap(pdev, pt3->regs[1]);
-err_kfree:
- kfree(pt3);
-err_release_regions:
- pci_release_regions(pdev);
-err_disable_device:
- pci_disable_device(pdev);
- return ret;
-
-}
-
-static const struct pci_device_id pt3_id_table[] = {
- { PCI_DEVICE_SUB(0x1172, 0x4c15, 0xee8d, 0x0368) },
- { },
-};
-MODULE_DEVICE_TABLE(pci, pt3_id_table);
-
-static SIMPLE_DEV_PM_OPS(pt3_pm_ops, pt3_suspend, pt3_resume);
-
-static struct pci_driver pt3_driver = {
- .name = DRV_NAME,
- .probe = pt3_probe,
- .remove = pt3_remove,
- .id_table = pt3_id_table,
-
- .driver.pm = &pt3_pm_ops,
-};
-
-module_pci_driver(pt3_driver);
-
-MODULE_DESCRIPTION("Earthsoft PT3 Driver");
-MODULE_AUTHOR("Akihiro TSUKADA");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/pt3/pt3.h b/drivers/media/pci/pt3/pt3.h
deleted file mode 100644
index 1b3f2ad..0000000
--- a/drivers/media/pci/pt3/pt3.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Earthsoft PT3 driver
- *
- * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * 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.
- *
- *
- * 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 PT3_H
-#define PT3_H
-
-#include <linux/atomic.h>
-#include <linux/types.h>
-
-#include "dvb_demux.h"
-#include "dvb_frontend.h"
-#include "dmxdev.h"
-
-#include "tc90522.h"
-#include "mxl301rf.h"
-#include "qm1d1c0042.h"
-
-#define DRV_NAME KBUILD_MODNAME
-
-#define PT3_NUM_FE 4
-
-/*
- * register index of the FPGA chip
- */
-#define REG_VERSION 0x00
-#define REG_BUS 0x04
-#define REG_SYSTEM_W 0x08
-#define REG_SYSTEM_R 0x0c
-#define REG_I2C_W 0x10
-#define REG_I2C_R 0x14
-#define REG_RAM_W 0x18
-#define REG_RAM_R 0x1c
-#define REG_DMA_BASE 0x40 /* regs for FE[i] = REG_DMA_BASE + 0x18 * i */
-#define OFST_DMA_DESC_L 0x00
-#define OFST_DMA_DESC_H 0x04
-#define OFST_DMA_CTL 0x08
-#define OFST_TS_CTL 0x0c
-#define OFST_STATUS 0x10
-#define OFST_TS_ERR 0x14
-
-/*
- * internal buffer for I2C
- */
-#define PT3_I2C_MAX 4091
-struct pt3_i2cbuf {
- u8 data[PT3_I2C_MAX];
- u8 tmp;
- u32 num_cmds;
-};
-
-/*
- * DMA things
- */
-#define TS_PACKET_SZ 188
-/* DMA transfers must not cross 4GiB, so use one page / transfer */
-#define DATA_XFER_SZ 4096
-#define DATA_BUF_XFERS 47
-/* (num_bufs * DATA_BUF_SZ) % TS_PACKET_SZ must be 0 */
-#define DATA_BUF_SZ (DATA_BUF_XFERS * DATA_XFER_SZ)
-#define MAX_DATA_BUFS 16
-#define MIN_DATA_BUFS 2
-
-#define DESCS_IN_PAGE (PAGE_SIZE / sizeof(struct xfer_desc))
-#define MAX_NUM_XFERS (MAX_DATA_BUFS * DATA_BUF_XFERS)
-#define MAX_DESC_BUFS DIV_ROUND_UP(MAX_NUM_XFERS, DESCS_IN_PAGE)
-
-/* DMA transfer description.
- * device is passed a pointer to this struct, dma-reads it,
- * and gets the DMA buffer ring for storing TS data.
- */
-struct xfer_desc {
- u32 addr_l; /* bus address of target data buffer */
- u32 addr_h;
- u32 size;
- u32 next_l; /* bus adddress of the next xfer_desc */
- u32 next_h;
-};
-
-/* A DMA mapping of a page containing xfer_desc's */
-struct xfer_desc_buffer {
- dma_addr_t b_addr;
- struct xfer_desc *descs; /* PAGE_SIZE (xfer_desc[DESCS_IN_PAGE]) */
-};
-
-/* A DMA mapping of a data buffer */
-struct dma_data_buffer {
- dma_addr_t b_addr;
- u8 *data; /* size: u8[PAGE_SIZE] */
-};
-
-/*
- * device things
- */
-struct pt3_adap_config {
- struct i2c_board_info demod_info;
- struct tc90522_config demod_cfg;
-
- struct i2c_board_info tuner_info;
- union tuner_config {
- struct qm1d1c0042_config qm1d1c0042;
- struct mxl301rf_config mxl301rf;
- } tuner_cfg;
- u32 init_freq;
-};
-
-struct pt3_adapter {
- struct dvb_adapter dvb_adap; /* dvb_adap.priv => struct pt3_board */
- int adap_idx;
-
- struct dvb_demux demux;
- struct dmxdev dmxdev;
- struct dvb_frontend *fe;
- struct i2c_client *i2c_demod;
- struct i2c_client *i2c_tuner;
-
- /* data fetch thread */
- struct task_struct *thread;
- int num_feeds;
-
- bool cur_lna;
- bool cur_lnb; /* current LNB power status (on/off) */
-
- /* items below are for DMA */
- struct dma_data_buffer buffer[MAX_DATA_BUFS];
- int buf_idx;
- int buf_ofs;
- int num_bufs; /* == pt3_board->num_bufs */
- int num_discard; /* how many access units to discard initially */
-
- struct xfer_desc_buffer desc_buf[MAX_DESC_BUFS];
- int num_desc_bufs; /* == num_bufs * DATA_BUF_XFERS / DESCS_IN_PAGE */
-};
-
-
-struct pt3_board {
- struct pci_dev *pdev;
- void __iomem *regs[2];
- /* regs[0]: registers, regs[1]: internal memory, used for I2C */
-
- struct mutex lock;
-
- /* LNB power shared among sat-FEs */
- int lnb_on_cnt; /* LNB power on count */
-
- /* LNA shared among terr-FEs */
- int lna_on_cnt; /* booster enabled count */
-
- int num_bufs; /* number of DMA buffers allocated/mapped per FE */
-
- struct i2c_adapter i2c_adap;
- struct pt3_i2cbuf *i2c_buf;
-
- struct pt3_adapter *adaps[PT3_NUM_FE];
-};
-
-
-/*
- * prototypes
- */
-extern int pt3_alloc_dmabuf(struct pt3_adapter *adap);
-extern void pt3_init_dmabuf(struct pt3_adapter *adap);
-extern void pt3_free_dmabuf(struct pt3_adapter *adap);
-extern int pt3_start_dma(struct pt3_adapter *adap);
-extern int pt3_stop_dma(struct pt3_adapter *adap);
-extern int pt3_proc_dma(struct pt3_adapter *adap);
-
-extern int pt3_i2c_master_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num);
-extern u32 pt3_i2c_functionality(struct i2c_adapter *adap);
-extern void pt3_i2c_reset(struct pt3_board *pt3);
-extern int pt3_init_all_demods(struct pt3_board *pt3);
-extern int pt3_init_all_mxl301rf(struct pt3_board *pt3);
-#endif /* PT3_H */
diff --git a/drivers/media/pci/pt3/pt3_dma.c b/drivers/media/pci/pt3/pt3_dma.c
deleted file mode 100644
index f0ce904..0000000
--- a/drivers/media/pci/pt3/pt3_dma.c
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Earthsoft PT3 driver
- *
- * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * 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.
- *
- *
- * 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 <linux/dma-mapping.h>
-#include <linux/kernel.h>
-#include <linux/pci.h>
-
-#include "pt3.h"
-
-#define PT3_ACCESS_UNIT (TS_PACKET_SZ * 128)
-#define PT3_BUF_CANARY (0x74)
-
-static u32 get_dma_base(int idx)
-{
- int i;
-
- i = (idx == 1 || idx == 2) ? 3 - idx : idx;
- return REG_DMA_BASE + 0x18 * i;
-}
-
-int pt3_stop_dma(struct pt3_adapter *adap)
-{
- struct pt3_board *pt3 = adap->dvb_adap.priv;
- u32 base;
- u32 stat;
- int retry;
-
- base = get_dma_base(adap->adap_idx);
- stat = ioread32(pt3->regs[0] + base + OFST_STATUS);
- if (!(stat & 0x01))
- return 0;
-
- iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL);
- for (retry = 0; retry < 5; retry++) {
- stat = ioread32(pt3->regs[0] + base + OFST_STATUS);
- if (!(stat & 0x01))
- return 0;
- msleep(50);
- }
- return -EIO;
-}
-
-int pt3_start_dma(struct pt3_adapter *adap)
-{
- struct pt3_board *pt3 = adap->dvb_adap.priv;
- u32 base = get_dma_base(adap->adap_idx);
-
- iowrite32(0x02, pt3->regs[0] + base + OFST_DMA_CTL);
- iowrite32(lower_32_bits(adap->desc_buf[0].b_addr),
- pt3->regs[0] + base + OFST_DMA_DESC_L);
- iowrite32(upper_32_bits(adap->desc_buf[0].b_addr),
- pt3->regs[0] + base + OFST_DMA_DESC_H);
- iowrite32(0x01, pt3->regs[0] + base + OFST_DMA_CTL);
- return 0;
-}
-
-
-static u8 *next_unit(struct pt3_adapter *adap, int *idx, int *ofs)
-{
- *ofs += PT3_ACCESS_UNIT;
- if (*ofs >= DATA_BUF_SZ) {
- *ofs -= DATA_BUF_SZ;
- (*idx)++;
- if (*idx == adap->num_bufs)
- *idx = 0;
- }
- return &adap->buffer[*idx].data[*ofs];
-}
-
-int pt3_proc_dma(struct pt3_adapter *adap)
-{
- int idx, ofs;
-
- idx = adap->buf_idx;
- ofs = adap->buf_ofs;
-
- if (adap->buffer[idx].data[ofs] == PT3_BUF_CANARY)
- return 0;
-
- while (*next_unit(adap, &idx, &ofs) != PT3_BUF_CANARY) {
- u8 *p;
-
- p = &adap->buffer[adap->buf_idx].data[adap->buf_ofs];
- if (adap->num_discard > 0)
- adap->num_discard--;
- else if (adap->buf_ofs + PT3_ACCESS_UNIT > DATA_BUF_SZ) {
- dvb_dmx_swfilter_packets(&adap->demux, p,
- (DATA_BUF_SZ - adap->buf_ofs) / TS_PACKET_SZ);
- dvb_dmx_swfilter_packets(&adap->demux,
- adap->buffer[idx].data, ofs / TS_PACKET_SZ);
- } else
- dvb_dmx_swfilter_packets(&adap->demux, p,
- PT3_ACCESS_UNIT / TS_PACKET_SZ);
-
- *p = PT3_BUF_CANARY;
- adap->buf_idx = idx;
- adap->buf_ofs = ofs;
- }
- return 0;
-}
-
-void pt3_init_dmabuf(struct pt3_adapter *adap)
-{
- int idx, ofs;
- u8 *p;
-
- idx = 0;
- ofs = 0;
- p = adap->buffer[0].data;
- /* mark the whole buffers as "not written yet" */
- while (idx < adap->num_bufs) {
- p[ofs] = PT3_BUF_CANARY;
- ofs += PT3_ACCESS_UNIT;
- if (ofs >= DATA_BUF_SZ) {
- ofs -= DATA_BUF_SZ;
- idx++;
- p = adap->buffer[idx].data;
- }
- }
- adap->buf_idx = 0;
- adap->buf_ofs = 0;
-}
-
-void pt3_free_dmabuf(struct pt3_adapter *adap)
-{
- struct pt3_board *pt3;
- int i;
-
- pt3 = adap->dvb_adap.priv;
- for (i = 0; i < adap->num_bufs; i++)
- dma_free_coherent(&pt3->pdev->dev, DATA_BUF_SZ,
- adap->buffer[i].data, adap->buffer[i].b_addr);
- adap->num_bufs = 0;
-
- for (i = 0; i < adap->num_desc_bufs; i++)
- dma_free_coherent(&pt3->pdev->dev, PAGE_SIZE,
- adap->desc_buf[i].descs, adap->desc_buf[i].b_addr);
- adap->num_desc_bufs = 0;
-}
-
-
-int pt3_alloc_dmabuf(struct pt3_adapter *adap)
-{
- struct pt3_board *pt3;
- void *p;
- int i, j;
- int idx, ofs;
- int num_desc_bufs;
- dma_addr_t data_addr, desc_addr;
- struct xfer_desc *d;
-
- pt3 = adap->dvb_adap.priv;
- adap->num_bufs = 0;
- adap->num_desc_bufs = 0;
- for (i = 0; i < pt3->num_bufs; i++) {
- p = dma_alloc_coherent(&pt3->pdev->dev, DATA_BUF_SZ,
- &adap->buffer[i].b_addr, GFP_KERNEL);
- if (p == NULL)
- goto failed;
- adap->buffer[i].data = p;
- adap->num_bufs++;
- }
- pt3_init_dmabuf(adap);
-
- /* build circular-linked pointers (xfer_desc) to the data buffers*/
- idx = 0;
- ofs = 0;
- num_desc_bufs =
- DIV_ROUND_UP(adap->num_bufs * DATA_BUF_XFERS, DESCS_IN_PAGE);
- for (i = 0; i < num_desc_bufs; i++) {
- p = dma_alloc_coherent(&pt3->pdev->dev, PAGE_SIZE,
- &desc_addr, GFP_KERNEL);
- if (p == NULL)
- goto failed;
- adap->num_desc_bufs++;
- adap->desc_buf[i].descs = p;
- adap->desc_buf[i].b_addr = desc_addr;
-
- if (i > 0) {
- d = &adap->desc_buf[i - 1].descs[DESCS_IN_PAGE - 1];
- d->next_l = lower_32_bits(desc_addr);
- d->next_h = upper_32_bits(desc_addr);
- }
- for (j = 0; j < DESCS_IN_PAGE; j++) {
- data_addr = adap->buffer[idx].b_addr + ofs;
- d = &adap->desc_buf[i].descs[j];
- d->addr_l = lower_32_bits(data_addr);
- d->addr_h = upper_32_bits(data_addr);
- d->size = DATA_XFER_SZ;
-
- desc_addr += sizeof(struct xfer_desc);
- d->next_l = lower_32_bits(desc_addr);
- d->next_h = upper_32_bits(desc_addr);
-
- ofs += DATA_XFER_SZ;
- if (ofs >= DATA_BUF_SZ) {
- ofs -= DATA_BUF_SZ;
- idx++;
- if (idx >= adap->num_bufs) {
- desc_addr = adap->desc_buf[0].b_addr;
- d->next_l = lower_32_bits(desc_addr);
- d->next_h = upper_32_bits(desc_addr);
- return 0;
- }
- }
- }
- }
- return 0;
-
-failed:
- pt3_free_dmabuf(adap);
- return -ENOMEM;
-}
diff --git a/drivers/media/pci/pt3/pt3_i2c.c b/drivers/media/pci/pt3/pt3_i2c.c
deleted file mode 100644
index ec6a8a2..0000000
--- a/drivers/media/pci/pt3/pt3_i2c.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Earthsoft PT3 driver
- *
- * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * 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.
- *
- *
- * 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 <linux/delay.h>
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <linux/io.h>
-#include <linux/pci.h>
-
-#include "pt3.h"
-
-#define PT3_I2C_BASE 2048
-#define PT3_CMD_ADDR_NORMAL 0
-#define PT3_CMD_ADDR_INIT_DEMOD 4096
-#define PT3_CMD_ADDR_INIT_TUNER (4096 + 2042)
-
-/* masks for I2C status register */
-#define STAT_SEQ_RUNNING 0x1
-#define STAT_SEQ_ERROR 0x6
-#define STAT_NO_SEQ 0x8
-
-#define PT3_I2C_RUN (1 << 16)
-#define PT3_I2C_RESET (1 << 17)
-
-enum ctl_cmd {
- I_END,
- I_ADDRESS,
- I_CLOCK_L,
- I_CLOCK_H,
- I_DATA_L,
- I_DATA_H,
- I_RESET,
- I_SLEEP,
- I_DATA_L_NOP = 0x08,
- I_DATA_H_NOP = 0x0c,
- I_DATA_H_READ = 0x0d,
- I_DATA_H_ACK0 = 0x0e,
- I_DATA_H_ACK1 = 0x0f,
-};
-
-
-static void cmdbuf_add(struct pt3_i2cbuf *cbuf, enum ctl_cmd cmd)
-{
- int buf_idx;
-
- if ((cbuf->num_cmds % 2) == 0)
- cbuf->tmp = cmd;
- else {
- cbuf->tmp |= cmd << 4;
- buf_idx = cbuf->num_cmds / 2;
- if (buf_idx < ARRAY_SIZE(cbuf->data))
- cbuf->data[buf_idx] = cbuf->tmp;
- }
- cbuf->num_cmds++;
-}
-
-static void put_end(struct pt3_i2cbuf *cbuf)
-{
- cmdbuf_add(cbuf, I_END);
- if (cbuf->num_cmds % 2)
- cmdbuf_add(cbuf, I_END);
-}
-
-static void put_start(struct pt3_i2cbuf *cbuf)
-{
- cmdbuf_add(cbuf, I_DATA_H);
- cmdbuf_add(cbuf, I_CLOCK_H);
- cmdbuf_add(cbuf, I_DATA_L);
- cmdbuf_add(cbuf, I_CLOCK_L);
-}
-
-static void put_byte_write(struct pt3_i2cbuf *cbuf, u8 val)
-{
- u8 mask;
-
- mask = 0x80;
- for (mask = 0x80; mask > 0; mask >>= 1)
- cmdbuf_add(cbuf, (val & mask) ? I_DATA_H_NOP : I_DATA_L_NOP);
- cmdbuf_add(cbuf, I_DATA_H_ACK0);
-}
-
-static void put_byte_read(struct pt3_i2cbuf *cbuf, u32 size)
-{
- int i, j;
-
- for (i = 0; i < size; i++) {
- for (j = 0; j < 8; j++)
- cmdbuf_add(cbuf, I_DATA_H_READ);
- cmdbuf_add(cbuf, (i == size - 1) ? I_DATA_H_NOP : I_DATA_L_NOP);
- }
-}
-
-static void put_stop(struct pt3_i2cbuf *cbuf)
-{
- cmdbuf_add(cbuf, I_DATA_L);
- cmdbuf_add(cbuf, I_CLOCK_H);
- cmdbuf_add(cbuf, I_DATA_H);
-}
-
-
-/* translates msgs to internal commands for bit-banging */
-static void translate(struct pt3_i2cbuf *cbuf, struct i2c_msg *msgs, int num)
-{
- int i, j;
- bool rd;
-
- cbuf->num_cmds = 0;
- for (i = 0; i < num; i++) {
- rd = !!(msgs[i].flags & I2C_M_RD);
- put_start(cbuf);
- put_byte_write(cbuf, msgs[i].addr << 1 | rd);
- if (rd)
- put_byte_read(cbuf, msgs[i].len);
- else
- for (j = 0; j < msgs[i].len; j++)
- put_byte_write(cbuf, msgs[i].buf[j]);
- }
- if (num > 0) {
- put_stop(cbuf);
- put_end(cbuf);
- }
-}
-
-static int wait_i2c_result(struct pt3_board *pt3, u32 *result, int max_wait)
-{
- int i;
- u32 v;
-
- for (i = 0; i < max_wait; i++) {
- v = ioread32(pt3->regs[0] + REG_I2C_R);
- if (!(v & STAT_SEQ_RUNNING))
- break;
- usleep_range(500, 750);
- }
- if (i >= max_wait)
- return -EIO;
- if (result)
- *result = v;
- return 0;
-}
-
-/* send [pre-]translated i2c msgs stored at addr */
-static int send_i2c_cmd(struct pt3_board *pt3, u32 addr)
-{
- u32 ret;
-
- /* make sure that previous transactions had finished */
- if (wait_i2c_result(pt3, NULL, 50)) {
- dev_warn(&pt3->pdev->dev, "(%s) prev. transaction stalled\n",
- __func__);
- return -EIO;
- }
-
- iowrite32(PT3_I2C_RUN | addr, pt3->regs[0] + REG_I2C_W);
- usleep_range(200, 300);
- /* wait for the current transaction to finish */
- if (wait_i2c_result(pt3, &ret, 500) || (ret & STAT_SEQ_ERROR)) {
- dev_warn(&pt3->pdev->dev, "(%s) failed.\n", __func__);
- return -EIO;
- }
- return 0;
-}
-
-
-/* init commands for each demod are combined into one transaction
- * and hidden in ROM with the address PT3_CMD_ADDR_INIT_DEMOD.
- */
-int pt3_init_all_demods(struct pt3_board *pt3)
-{
- ioread32(pt3->regs[0] + REG_I2C_R);
- return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_DEMOD);
-}
-
-/* init commands for two ISDB-T tuners are hidden in ROM. */
-int pt3_init_all_mxl301rf(struct pt3_board *pt3)
-{
- usleep_range(1000, 2000);
- return send_i2c_cmd(pt3, PT3_CMD_ADDR_INIT_TUNER);
-}
-
-void pt3_i2c_reset(struct pt3_board *pt3)
-{
- iowrite32(PT3_I2C_RESET, pt3->regs[0] + REG_I2C_W);
-}
-
-/*
- * I2C algorithm
- */
-int
-pt3_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
-{
- struct pt3_board *pt3;
- struct pt3_i2cbuf *cbuf;
- int i;
- void __iomem *p;
-
- pt3 = i2c_get_adapdata(adap);
- cbuf = pt3->i2c_buf;
-
- for (i = 0; i < num; i++)
- if (msgs[i].flags & I2C_M_RECV_LEN) {
- dev_warn(&pt3->pdev->dev,
- "(%s) I2C_M_RECV_LEN not supported.\n",
- __func__);
- return -EINVAL;
- }
-
- translate(cbuf, msgs, num);
- memcpy_toio(pt3->regs[1] + PT3_I2C_BASE + PT3_CMD_ADDR_NORMAL / 2,
- cbuf->data, cbuf->num_cmds);
-
- if (send_i2c_cmd(pt3, PT3_CMD_ADDR_NORMAL) < 0)
- return -EIO;
-
- p = pt3->regs[1] + PT3_I2C_BASE;
- for (i = 0; i < num; i++)
- if ((msgs[i].flags & I2C_M_RD) && msgs[i].len > 0) {
- memcpy_fromio(msgs[i].buf, p, msgs[i].len);
- p += msgs[i].len;
- }
-
- return num;
-}
-
-u32 pt3_i2c_functionality(struct i2c_adapter *adap)
-{
- return I2C_FUNC_I2C;
-}
diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c
deleted file mode 100644
index 1575a5d..0000000
--- a/drivers/media/tuners/mxl301rf.c
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * MaxLinear MxL301RF OFDM tuner driver
- *
- * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * 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.
- *
- *
- * 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.
- */
-
-/*
- * NOTICE:
- * This driver is incomplete and lacks init/config of the chips,
- * as the necessary info is not disclosed.
- * Other features like get_if_frequency() are missing as well.
- * It assumes that users of this driver (such as a PCI bridge of
- * DTV receiver cards) properly init and configure the chip
- * via I2C *before* calling this driver's init() function.
- *
- * 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 "mxl301rf.h"
-
-struct mxl301rf_state {
- struct mxl301rf_config cfg;
- struct i2c_client *i2c;
-};
-
-static struct mxl301rf_state *cfg_to_state(struct mxl301rf_config *c)
-{
- return container_of(c, struct mxl301rf_state, cfg);
-}
-
-static int raw_write(struct mxl301rf_state *state, const u8 *buf, int len)
-{
- int ret;
-
- ret = i2c_master_send(state->i2c, buf, len);
- if (ret >= 0 && ret < len)
- ret = -EIO;
- return (ret == len) ? 0 : ret;
-}
-
-static int reg_write(struct mxl301rf_state *state, u8 reg, u8 val)
-{
- u8 buf[2] = { reg, val };
-
- return raw_write(state, buf, 2);
-}
-
-static int reg_read(struct mxl301rf_state *state, u8 reg, u8 *val)
-{
- u8 wbuf[2] = { 0xfb, reg };
- int ret;
-
- ret = raw_write(state, wbuf, sizeof(wbuf));
- if (ret == 0)
- ret = i2c_master_recv(state->i2c, val, 1);
- if (ret >= 0 && ret < 1)
- ret = -EIO;
- return (ret == 1) ? 0 : ret;
-}
-
-/* tuner_ops */
-
-/* get RSSI and update propery cache, set to *out in % */
-static int mxl301rf_get_rf_strength(struct dvb_frontend *fe, u16 *out)
-{
- struct mxl301rf_state *state;
- int ret;
- u8 rf_in1, rf_in2, rf_off1, rf_off2;
- u16 rf_in, rf_off;
- s64 level;
- struct dtv_fe_stats *rssi;
-
- rssi = &fe->dtv_property_cache.strength;
- rssi->len = 1;
- rssi->stat[0].scale = FE_SCALE_NOT_AVAILABLE;
- *out = 0;
-
- state = fe->tuner_priv;
- ret = reg_write(state, 0x14, 0x01);
- if (ret < 0)
- return ret;
- usleep_range(1000, 2000);
-
- ret = reg_read(state, 0x18, &rf_in1);
- if (ret == 0)
- ret = reg_read(state, 0x19, &rf_in2);
- if (ret == 0)
- ret = reg_read(state, 0xd6, &rf_off1);
- if (ret == 0)
- ret = reg_read(state, 0xd7, &rf_off2);
- if (ret != 0)
- return ret;
-
- rf_in = (rf_in2 & 0x07) << 8 | rf_in1;
- rf_off = (rf_off2 & 0x0f) << 5 | (rf_off1 >> 3);
- level = rf_in - rf_off - (113 << 3); /* x8 dBm */
- level = level * 1000 / 8;
- rssi->stat[0].svalue = level;
- rssi->stat[0].scale = FE_SCALE_DECIBEL;
- /* *out = (level - min) * 100 / (max - min) */
- *out = (rf_in - rf_off + (1 << 9) - 1) * 100 / ((5 << 9) - 2);
- return 0;
-}
-
-/* spur shift parameters */
-struct shf {
- u32 freq; /* Channel center frequency */
- u32 ofst_th; /* Offset frequency threshold */
- u8 shf_val; /* Spur shift value */
- u8 shf_dir; /* Spur shift direction */
-};
-
-static const struct shf shf_tab[] = {
- { 64500, 500, 0x92, 0x07 },
- { 191500, 300, 0xe2, 0x07 },
- { 205500, 500, 0x2c, 0x04 },
- { 212500, 500, 0x1e, 0x04 },
- { 226500, 500, 0xd4, 0x07 },
- { 99143, 500, 0x9c, 0x07 },
- { 173143, 500, 0xd4, 0x07 },
- { 191143, 300, 0xd4, 0x07 },
- { 207143, 500, 0xce, 0x07 },
- { 225143, 500, 0xce, 0x07 },
- { 243143, 500, 0xd4, 0x07 },
- { 261143, 500, 0xd4, 0x07 },
- { 291143, 500, 0xd4, 0x07 },
- { 339143, 500, 0x2c, 0x04 },
- { 117143, 500, 0x7a, 0x07 },
- { 135143, 300, 0x7a, 0x07 },
- { 153143, 500, 0x01, 0x07 }
-};
-
-struct reg_val {
- u8 reg;
- u8 val;
-} __attribute__ ((__packed__));
-
-static const struct reg_val set_idac[] = {
- { 0x0d, 0x00 },
- { 0x0c, 0x67 },
- { 0x6f, 0x89 },
- { 0x70, 0x0c },
- { 0x6f, 0x8a },
- { 0x70, 0x0e },
- { 0x6f, 0x8b },
- { 0x70, 0x1c },
-};
-
-static int mxl301rf_set_params(struct dvb_frontend *fe)
-{
- struct reg_val tune0[] = {
- { 0x13, 0x00 }, /* abort tuning */
- { 0x3b, 0xc0 },
- { 0x3b, 0x80 },
- { 0x10, 0x95 }, /* BW */
- { 0x1a, 0x05 },
- { 0x61, 0x00 }, /* spur shift value (placeholder) */
- { 0x62, 0xa0 } /* spur shift direction (placeholder) */
- };
-
- struct reg_val tune1[] = {
- { 0x11, 0x40 }, /* RF frequency L (placeholder) */
- { 0x12, 0x0e }, /* RF frequency H (placeholder) */
- { 0x13, 0x01 } /* start tune */
- };
-
- struct mxl301rf_state *state;
- u32 freq;
- u16 f;
- u32 tmp, div;
- int i, ret;
-
- state = fe->tuner_priv;
- freq = fe->dtv_property_cache.frequency;
-
- /* spur shift function (for analog) */
- for (i = 0; i < ARRAY_SIZE(shf_tab); i++) {
- if (freq >= (shf_tab[i].freq - shf_tab[i].ofst_th) * 1000 &&
- freq <= (shf_tab[i].freq + shf_tab[i].ofst_th) * 1000) {
- tune0[5].val = shf_tab[i].shf_val;
- tune0[6].val = 0xa0 | shf_tab[i].shf_dir;
- break;
- }
- }
- ret = raw_write(state, (u8 *) tune0, sizeof(tune0));
- if (ret < 0)
- goto failed;
- usleep_range(3000, 4000);
-
- /* convert freq to 10.6 fixed point float [MHz] */
- f = freq / 1000000;
- tmp = freq % 1000000;
- div = 1000000;
- for (i = 0; i < 6; i++) {
- f <<= 1;
- div >>= 1;
- if (tmp > div) {
- tmp -= div;
- f |= 1;
- }
- }
- if (tmp > 7812)
- f++;
- tune1[0].val = f & 0xff;
- tune1[1].val = f >> 8;
- ret = raw_write(state, (u8 *) tune1, sizeof(tune1));
- if (ret < 0)
- goto failed;
- msleep(31);
-
- ret = reg_write(state, 0x1a, 0x0d);
- if (ret < 0)
- goto failed;
- ret = raw_write(state, (u8 *) set_idac, sizeof(set_idac));
- if (ret < 0)
- goto failed;
- return 0;
-
-failed:
- dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
- __func__, fe->dvb->num, fe->id);
- return ret;
-}
-
-static const struct reg_val standby_data[] = {
- { 0x01, 0x00 },
- { 0x13, 0x00 }
-};
-
-static int mxl301rf_sleep(struct dvb_frontend *fe)
-{
- struct mxl301rf_state *state;
- int ret;
-
- state = fe->tuner_priv;
- ret = raw_write(state, (u8 *)standby_data, sizeof(standby_data));
- if (ret < 0)
- dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
- __func__, fe->dvb->num, fe->id);
- return ret;
-}
-
-
-/* init sequence is not public.
- * the parent must have init'ed the device.
- * just wake up here.
- */
-static int mxl301rf_init(struct dvb_frontend *fe)
-{
- struct mxl301rf_state *state;
- int ret;
-
- state = fe->tuner_priv;
-
- ret = reg_write(state, 0x01, 0x01);
- if (ret < 0) {
- dev_warn(&state->i2c->dev, "(%s) failed. [adap%d-fe%d]\n",
- __func__, fe->dvb->num, fe->id);
- return ret;
- }
- return 0;
-}
-
-/* I2C driver functions */
-
-static const struct dvb_tuner_ops mxl301rf_ops = {
- .info = {
- .name = "MaxLinear MxL301RF",
-
- .frequency_min = 93000000,
- .frequency_max = 803142857,
- },
-
- .init = mxl301rf_init,
- .sleep = mxl301rf_sleep,
-
- .set_params = mxl301rf_set_params,
- .get_rf_strength = mxl301rf_get_rf_strength,
-};
-
-
-static int mxl301rf_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct mxl301rf_state *state;
- struct mxl301rf_config *cfg;
- struct dvb_frontend *fe;
-
- state = kzalloc(sizeof(*state), GFP_KERNEL);
- if (!state)
- return -ENOMEM;
-
- state->i2c = client;
- cfg = client->dev.platform_data;
-
- memcpy(&state->cfg, cfg, sizeof(state->cfg));
- fe = cfg->fe;
- fe->tuner_priv = state;
- memcpy(&fe->ops.tuner_ops, &mxl301rf_ops, sizeof(mxl301rf_ops));
-
- i2c_set_clientdata(client, &state->cfg);
- dev_info(&client->dev, "MaxLinear MxL301RF attached.\n");
- return 0;
-}
-
-static int mxl301rf_remove(struct i2c_client *client)
-{
- struct mxl301rf_state *state;
-
- state = cfg_to_state(i2c_get_clientdata(client));
- state->cfg.fe->tuner_priv = NULL;
- kfree(state);
- return 0;
-}
-
-
-static const struct i2c_device_id mxl301rf_id[] = {
- {"mxl301rf", 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, mxl301rf_id);
-
-static struct i2c_driver mxl301rf_driver = {
- .driver = {
- .name = "mxl301rf",
- },
- .probe = mxl301rf_probe,
- .remove = mxl301rf_remove,
- .id_table = mxl301rf_id,
-};
-
-module_i2c_driver(mxl301rf_driver);
-
-MODULE_DESCRIPTION("MaxLinear MXL301RF tuner");
-MODULE_AUTHOR("Akihiro TSUKADA");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/mxl301rf.h b/drivers/media/tuners/mxl301rf.h
deleted file mode 100644
index 19e6840..0000000
--- a/drivers/media/tuners/mxl301rf.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * MaxLinear MxL301RF OFDM tuner driver
- *
- * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * 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.
- *
- *
- * 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 MXL301RF_H
-#define MXL301RF_H
-
-#include "dvb_frontend.h"
-
-struct mxl301rf_config {
- struct dvb_frontend *fe;
-};
-
-#endif /* MXL301RF_H */
diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c
deleted file mode 100644
index 18bc745..0000000
--- a/drivers/media/tuners/qm1d1c0042.c
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- * Sharp QM1D1C0042 8PSK tuner driver
- *
- * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * 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.
- *
- *
- * 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.
- */
-
-/*
- * 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 "qm1d1c0042.h"
-
-#define QM1D1C0042_NUM_REGS 0x20
-
-static const u8 reg_initval[QM1D1C0042_NUM_REGS] = {
- 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,
-};
-
-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)
-{
- u8 wbuf[2] = { reg, val };
- int ret;
-
- ret = i2c_master_send(state->i2c, wbuf, sizeof(wbuf));
- if (ret >= 0 && ret < sizeof(wbuf))
- ret = -EIO;
- return (ret == sizeof(wbuf)) ? 0 : ret;
-}
-
-static int reg_read(struct qm1d1c0042_state *state, u8 reg, u8 *val)
-{
- struct i2c_msg msgs[2] = {
- {
- .addr = state->i2c->addr,
- .flags = 0,
- .buf = ®,
- .len = 1,
- },
- {
- .addr = state->i2c->addr,
- .flags = I2C_M_RD,
- .buf = val,
- .len = 1,
- },
- };
- int ret;
-
- 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;
-}
-
-
-static int qm1d1c0042_set_srch_mode(struct qm1d1c0042_state *state, bool fast)
-{
- if (fast)
- state->regs[0x03] |= 0x01; /* set fast search mode */
- else
- state->regs[0x03] &= ~0x01 & 0xff;
-
- return reg_write(state, 0x03, state->regs[0x03]);
-}
-
-static int qm1d1c0042_wakeup(struct qm1d1c0042_state *state)
-{
- 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]);
-
- 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;
-}
-
-/* tuner_ops */
-
-static int qm1d1c0042_set_config(struct dvb_frontend *fe, void *priv_cfg)
-{
- struct qm1d1c0042_state *state;
- struct qm1d1c0042_config *cfg;
-
- state = fe->tuner_priv;
- cfg = priv_cfg;
-
- if (cfg->fe)
- state->cfg.fe = cfg->fe;
-
- 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;
-
- state->cfg.lpf = cfg->lpf;
- state->cfg.fast_srch = cfg->fast_srch;
-
- if (cfg->lpf_wait != QM1D1C0042_CFG_WAIT_DFLT)
- state->cfg.lpf_wait = cfg->lpf_wait;
- else
- state->cfg.lpf_wait = default_cfg.lpf_wait;
-
- 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;
-
- 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;
-}
-
-/* divisor, vco_band parameters */
-/* {maxfreq, param1(band?), param2(div?) */
-static const u32 conv_table[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 }
-};
-
-static int qm1d1c0042_set_params(struct dvb_frontend *fe)
-{
- 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;
- }
- 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);
-
- 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)
- 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;
-}
-
-static int qm1d1c0042_sleep(struct dvb_frontend *fe)
-{
- 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;
-}
-
-static int qm1d1c0042_init(struct dvb_frontend *fe)
-{
- struct qm1d1c0042_state *state;
- u8 val;
- int i, ret;
-
- state = fe->tuner_priv;
- memcpy(state->regs, reg_initval, sizeof(reg_initval));
-
- 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;
-
- /* check ID */
- ret = reg_read(state, 0x00, &val);
- if (ret < 0 || val != 0x48)
- goto failed;
- usleep_range(2000, 3000);
-
- 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;
- }
-
- 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;
-}
-
-/* I2C driver functions */
-
-static const struct dvb_tuner_ops qm1d1c0042_ops = {
- .info = {
- .name = "Sharp QM1D1C0042",
-
- .frequency_min = 950000,
- .frequency_max = 2150000,
- },
-
- .init = qm1d1c0042_init,
- .sleep = qm1d1c0042_sleep,
- .set_config = qm1d1c0042_set_config,
- .set_params = qm1d1c0042_set_params,
-};
-
-
-static int qm1d1c0042_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- 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;
-
- 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");
- return 0;
-}
-
-static int qm1d1c0042_remove(struct i2c_client *client)
-{
- struct qm1d1c0042_state *state;
-
- state = cfg_to_state(i2c_get_clientdata(client));
- state->cfg.fe->tuner_priv = NULL;
- kfree(state);
- return 0;
-}
-
-
-static const struct i2c_device_id qm1d1c0042_id[] = {
- {"qm1d1c0042", 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, qm1d1c0042_id);
-
-static struct i2c_driver qm1d1c0042_driver = {
- .driver = {
- .name = "qm1d1c0042",
- },
- .probe = qm1d1c0042_probe,
- .remove = qm1d1c0042_remove,
- .id_table = qm1d1c0042_id,
-};
-
-module_i2c_driver(qm1d1c0042_driver);
-
-MODULE_DESCRIPTION("Sharp QM1D1C0042 tuner");
-MODULE_AUTHOR("Akihiro TSUKADA");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/tuners/qm1d1c0042.h b/drivers/media/tuners/qm1d1c0042.h
deleted file mode 100644
index 4f5c188..0000000
--- a/drivers/media/tuners/qm1d1c0042.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Sharp QM1D1C0042 8PSK tuner driver
- *
- * Copyright (C) 2014 Akihiro Tsukada <tskd08@gmail.com>
- *
- * 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.
- *
- *
- * 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
-
-#include "dvb_frontend.h"
-
-
-struct qm1d1c0042_config {
- struct dvb_frontend *fe;
-
- u32 xtal_freq; /* [kHz] */ /* currently ignored */
- bool lpf; /* enable LPF */
- bool fast_srch; /* enable fast search mode, no LPF */
- u32 lpf_wait; /* wait in tuning with LPF enabled. [ms] */
- u32 fast_srch_wait; /* with fast-search mode, no LPF. [ms] */
- u32 normal_srch_wait; /* with no LPF/fast-search mode. [ms] */
-};
-/* special values indicating to use the default in qm1d1c0042_config */
-#define QM1D1C0042_CFG_XTAL_DFLT 0
-#define QM1D1C0042_CFG_WAIT_DFLT 0
-
-#endif /* QM1D1C0042_H */
--
2.3.10
^ permalink raw reply related [flat|nested] 39+ messages in thread* [media 4/7] Toshiba TC90522XBG quad demodulator (2ch OFDM + 2ch 8PSK) used by both PT3 & PX-Q3PE
2016-02-15 6:08 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
` (2 preceding siblings ...)
2016-02-15 6:08 ` [media 3/7] drop backstabbing drivers info
@ 2016-02-15 6:08 ` info
2016-02-15 6:08 ` [media 5/7] MaxLinear MxL301RF ISDB-T tuner info
` (2 subsequent siblings)
6 siblings, 0 replies; 39+ messages in thread
From: info @ 2016-02-15 6:08 UTC (permalink / raw)
To: linux-media
Cc: Буди Романто, AreMa Inc,
linux-kernel, crope, m.chehab, mchehab, hdegoede,
laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski,
peter.senna
From: Буди Романто, AreMa Inc <knightrider@are.ma>
Signed-off-by: Буди Романто, AreMa Inc <knightrider@are.ma>
---
drivers/media/dvb-frontends/tc90522.c | 283 ++++++++++++++++++++++++++++++++++
drivers/media/dvb-frontends/tc90522.h | 18 +++
2 files changed, 301 insertions(+)
create mode 100644 drivers/media/dvb-frontends/tc90522.c
create mode 100644 drivers/media/dvb-frontends/tc90522.h
diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c
new file mode 100644
index 0000000..97d18ba
--- /dev/null
+++ b/drivers/media/dvb-frontends/tc90522.c
@@ -0,0 +1,283 @@
+/*
+ * Toshiba TC90522XBG 2ch OFDM(ISDB-T) + 2ch 8PSK(ISDB-S) demodulator
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+ *
+ * Supported cards:
+ * Earthsoft PT3
+ * PLEX PX-Q3PE
+ *
+ * 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 <linux/pci.h>
+#include "dvb_math.h"
+#include "dvb_frontend.h"
+#include "tc90522.h"
+
+struct tc90522 {
+ struct i2c_adapter *i2c;
+ fe_status_t stat;
+};
+
+bool tc90522_r(struct dvb_frontend *fe, u8 slvadr, u8 *buf, u8 len)
+{
+ struct tc90522 *d = fe->demodulator_priv;
+ struct i2c_msg msg[] = {
+ {.addr = fe->id, .flags = 0, .buf = &slvadr, .len = 1,},
+ {.addr = fe->id, .flags = I2C_M_RD, .buf = buf, .len = len,},
+ };
+ return i2c_transfer(d->i2c, msg, 2) == 2;
+}
+
+bool tc90522_w(struct dvb_frontend *fe, u8 slvadr, u8 dat)
+{
+ struct tc90522 *d = fe->demodulator_priv;
+ u8 buf[] = {slvadr, dat};
+ struct i2c_msg msg[] = {
+ {.addr = fe->id, .flags = 0, .buf = buf, .len = 2,},
+ };
+ return i2c_transfer(d->i2c, msg, 1) == 1;
+}
+
+u64 tc90522_n2int(const u8 *data, u8 n) /* convert n_bytes data from stream (network byte order) to integer */
+{ /* can't use <arpa/inet.h>'s ntoh*() as sometimes n = 3,5,... */
+ u32 i, val = 0;
+
+ for (i = 0; i < n; i++) {
+ val <<= 8;
+ val |= data[i];
+ }
+ return val;
+}
+
+int tc90522_cn_raw(struct dvb_frontend *fe, u16 *raw) /* for DVBv3 compatibility */
+{
+ u8 buf[3],
+ len = fe->dtv_property_cache.delivery_system == SYS_ISDBS ? 2 : 3,
+ adr = fe->dtv_property_cache.delivery_system == SYS_ISDBS ? 0xbc : 0x8b;
+ bool ok = tc90522_r(fe, adr, buf, len);
+ int cn = tc90522_n2int(buf, len);
+
+ if (!ok)
+ return -EIO;
+ *raw = cn;
+ return cn;
+}
+
+int tc90522_status(struct dvb_frontend *fe, fe_status_t *stat)
+{
+ struct tc90522 *d = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u16 v16;
+ s64 raw = tc90522_cn_raw(fe, &v16),
+ x,
+ y;
+
+ s64 cn_s(void) /* @ .0001 dB */
+ {
+ raw -= 3000;
+ if (raw < 0)
+ raw = 0;
+ x = int_sqrt(raw << 20);
+ y = 16346ll * x - (143410ll << 16);
+ y = ((x * y) >> 16) + (502590ll << 16);
+ y = ((x * y) >> 16) - (889770ll << 16);
+ y = ((x * y) >> 16) + (895650ll << 16);
+ y = (588570ll << 16) - ((x * y) >> 16);
+ return y < 0 ? 0 : y >> 16;
+ }
+
+ s64 cn_t(void) /* @ .0001 dB */
+ {
+ if (!raw)
+ return 0;
+ x = (1130911733ll - 10ll * intlog10(raw)) >> 2;
+ y = (x >> 2) - (x >> 6) + (x >> 8) + (x >> 9) - (x >> 10) + (x >> 11) + (x >> 12) - (16ll << 22);
+ y = ((x * y) >> 22) + (398ll << 22);
+ y = ((x * y) >> 22) + (5491ll << 22);
+ y = ((x * y) >> 22) + (30965ll << 22);
+ return y >> 22;
+ }
+
+ c->cnr.len = 1;
+ c->cnr.stat[0].svalue = fe->dtv_property_cache.delivery_system == SYS_ISDBS ? cn_s() : cn_t();
+ c->cnr.stat[0].scale = FE_SCALE_DECIBEL;
+ *stat = d->stat;
+ return d->stat;
+}
+
+int tc90522_get_frontend_algo(struct dvb_frontend *fe)
+{
+ return DVBFE_ALGO_HW;
+}
+
+int tc90522_tune(struct dvb_frontend *fe, bool retune, u32 mode_flags, u32 *delay, fe_status_t *stat)
+{
+ u32 fno2kHz(u32 fno)
+ {
+ u32 chno;
+
+ if (fno < 12) {
+ chno = 1 + 2 * fno; /* BS */
+ return 1049480 + 38360 * fno;
+ } else if (fno < 24) {
+ fno -= 12;
+ chno = 2 + 2 * fno; /* CS110 right */
+ return 1613000 + 40000 * fno;
+ }
+ fno -= 24;
+ chno = 1 + 2 * fno; /* CS110 left */
+ return 1593000 + 40000 * fno;
+ }
+
+ void s_kHz(u32 *f)
+ {
+ *f = *f > 3000000 ? fno2kHz(14) : /* CNN etc. */
+ *f >= 1049480 ? *f :
+ *f > 48 ? fno2kHz(4) : /* BS11 etc. */
+ fno2kHz(*f - 1);
+ }
+
+ u32 fno2Hz(u32 fno)
+ {
+ return (fno > 112 ? 557 : 93 + 6 * fno + (fno < 12 ? 0 : fno < 17 ? 2 : fno < 63 ? 0 : 2)) * 1000000 + 142857;
+ }
+
+ void t_Hz(u32 *f)
+ {
+ *f = *f >= 90000000 ? *f : /* real_freq Hz */
+ *f > 255 ? fno2Hz(77) : /* NHK */
+ *f > 127 ? fno2Hz(*f - 128) : /* freqno (IO#) */
+ *f > 63 ? (*f -= 64, /* CATV */
+ *f > 22 ? fno2Hz(*f - 1) : /* C23-C62 */
+ *f > 12 ? fno2Hz(*f - 10) : /* C13-C22 */
+ fno2Hz(77)) :
+ *f > 62 ? fno2Hz(77) :
+ *f > 12 ? fno2Hz(*f + 50) : /* 13-62 */
+ *f > 3 ? fno2Hz(*f + 9) : /* 4-12 */
+ *f ? fno2Hz(*f - 1) : /* 1-3 */
+ fno2Hz(77);
+ }
+ struct tc90522 *d = fe->demodulator_priv;
+ u16 stream_id = fe->dtv_property_cache.stream_id,
+ i = 0xFFFF;
+ u8 data[16];
+
+ if (!retune) /* once is enough */
+ return 0;
+ d->stat = 0;
+ if (fe->dtv_property_cache.delivery_system == SYS_ISDBT)
+ goto ISDBT;
+
+ s_kHz(&fe->dtv_property_cache.frequency);
+ if (fe->ops.tuner_ops.set_params(fe))
+ return -EIO;
+ while (i--) {
+ if ((tc90522_r(fe, 0xC3, data, 1), !(data[0] & 0x10)) && /* locked */
+ (tc90522_r(fe, 0xCE, data, 2), *(u16 *)data != 0) && /* valid TSID */
+ tc90522_r(fe, 0xC3, data, 1) &&
+ tc90522_r(fe, 0xCE, data, 16))
+ break;
+ msleep_interruptible(1);
+ }
+ if (!i)
+ return -ETIMEDOUT;
+ for (i = 0; i < 8; i++) {
+ u16 tsid = tc90522_n2int(data + i*2, 2);
+
+ if ((tsid == stream_id || stream_id == i) &&
+ tc90522_w(fe, 0x8F, tsid >> 8) &&
+ tc90522_w(fe, 0x90, tsid & 0xFF) &&
+ tc90522_r(fe, 0xE6, data, 2) &&
+ tc90522_n2int(data, 2) == tsid)
+ goto LOCK;
+ }
+ goto ERR;
+ISDBT:
+ t_Hz(&fe->dtv_property_cache.frequency);
+ if (fe->ops.tuner_ops.set_params(fe))
+ return -EIO;
+ while (i--) {
+ bool retryov,
+ lock0,
+ lock1;
+ if (!tc90522_r(fe, 0x80, data, 1) || !tc90522_r(fe, 0xB0, data + 1, 1))
+ break;
+ retryov = data[0] & 0b10000000 ? true : false;
+ lock0 = data[0] & 0b00001000 ? false : true;
+ lock1 = data[1] & 0b00001000 ? true : false;
+ if (lock0 && lock1) {
+LOCK:
+ d->stat = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
+ *stat = d->stat;
+ return 0;
+ }
+ if (retryov)
+ break;
+ msleep_interruptible(1);
+ }
+ERR:
+ *stat = d->stat;
+ return -ETIMEDOUT;
+}
+
+static struct dvb_frontend_ops tc90522_ops = {
+ .info = {
+ .name = TC90522_MODNAME,
+ .caps = FE_CAN_INVERSION_AUTO | FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO | FE_CAN_MULTISTREAM |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO,
+ .frequency_min = 1,
+ .frequency_max = 770000000,
+ },
+ .get_frontend_algo = tc90522_get_frontend_algo,
+ .read_snr = tc90522_cn_raw,
+ .read_status = tc90522_status,
+ .tune = tc90522_tune,
+};
+
+int tc90522_probe(struct i2c_client *c, const struct i2c_device_id *id)
+{
+ struct tc90522 *d = kzalloc(sizeof(struct tc90522), GFP_KERNEL);
+ struct dvb_frontend *fe = c->dev.platform_data;
+
+ if (!d)
+ return -ENOMEM;
+ d->i2c = c->adapter;
+ fe->demodulator_priv = d;
+ i2c_set_clientdata(c, d);
+ memcpy(&fe->ops, &tc90522_ops, sizeof(struct dvb_frontend_ops));
+ fe->ops.delsys[0] = fe->dtv_property_cache.delivery_system;
+ return 0;
+}
+
+int tc90522_remove(struct i2c_client *c)
+{
+ kfree(i2c_get_clientdata(c));
+ return 0;
+}
+
+static struct i2c_device_id tc90522_id[] = {
+ {TC90522_MODNAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, tc90522_id);
+
+static struct i2c_driver tc90522_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = tc90522_id->name,
+ },
+ .probe = tc90522_probe,
+ .remove = tc90522_remove,
+ .id_table = tc90522_id,
+};
+module_i2c_driver(tc90522_driver);
+
+MODULE_AUTHOR("Budi Rachmanto, AreMa Inc. <knightrider(@)are.ma>");
+MODULE_DESCRIPTION("Toshiba TC90522 8PSK(ISDB-S)/OFDM(ISDB-T) quad demodulator");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/dvb-frontends/tc90522.h b/drivers/media/dvb-frontends/tc90522.h
new file mode 100644
index 0000000..b6ee014
--- /dev/null
+++ b/drivers/media/dvb-frontends/tc90522.h
@@ -0,0 +1,18 @@
+/*
+ * Toshiba TC90522XBG 2ch OFDM(ISDB-T) + 2ch 8PSK(ISDB-S) demodulator
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+ *
+ * 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 TC90522_H
+#define TC90522_H
+
+#define TC90522_MODNAME "tc90522"
+
+#endif
+
--
2.3.10
^ permalink raw reply related [flat|nested] 39+ messages in thread* [media 5/7] MaxLinear MxL301RF ISDB-T tuner
2016-02-15 6:08 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
` (3 preceding siblings ...)
2016-02-15 6:08 ` [media 4/7] Toshiba TC90522XBG quad demodulator (2ch OFDM + 2ch 8PSK) used by both PT3 & PX-Q3PE info
@ 2016-02-15 6:08 ` info
2016-02-15 6:08 ` [media 6/7] Sharp QM1D1C0042 ISDB-S tuner info
2016-02-15 6:08 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE info
6 siblings, 0 replies; 39+ messages in thread
From: info @ 2016-02-15 6:08 UTC (permalink / raw)
To: linux-media
Cc: Буди Романто, AreMa Inc,
linux-kernel, crope, m.chehab, mchehab, hdegoede,
laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski,
peter.senna
From: Буди Романто, AreMa Inc <knightrider@are.ma>
Signed-off-by: Буди Романто, AreMa Inc <knightrider@are.ma>
---
drivers/media/tuners/mxl301rf.c | 251 ++++++++++++++++++++++++++++++++++++++++
drivers/media/tuners/mxl301rf.h | 23 ++++
2 files changed, 274 insertions(+)
create mode 100644 drivers/media/tuners/mxl301rf.c
create mode 100644 drivers/media/tuners/mxl301rf.h
diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c
new file mode 100644
index 0000000..2f1f1eb
--- /dev/null
+++ b/drivers/media/tuners/mxl301rf.c
@@ -0,0 +1,251 @@
+/*
+ * Sharp VA4M6JC2103 - Earthsoft PT3 ISDB-T tuner MaxLinear CMOS Hybrid TV MxL301RF
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+ *
+ * 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 "mxl301rf.h"
+
+struct mxl301rf {
+ struct i2c_adapter *i2c;
+ u8 adr_tuner;
+};
+
+int mxl301rf_w(struct dvb_frontend *fe, u8 slvadr, const u8 *dat, int len)
+{
+ u8 buf[len + 1];
+ struct mxl301rf *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 mxl301rf_w_tuner(struct dvb_frontend *fe, const u8 *dat, int len)
+{
+ u8 buf[len + 1];
+
+ buf[0] = ((struct mxl301rf *)fe->tuner_priv)->adr_tuner << 1;
+ memcpy(buf + 1, dat, len);
+ return mxl301rf_w(fe, 0xFE, buf, len + 1);
+}
+
+void mxl301rf_r(struct dvb_frontend *fe, u8 regadr, u8 *dat)
+{
+ struct mxl301rf *t = fe->tuner_priv;
+ u8 wbuf[] = {0xFB, regadr},
+ rbuf[] = {0xFE, (t->adr_tuner << 1) | 1, 0};
+ struct i2c_msg msg[] = {
+ {.addr = fe->id, .flags = 0, .buf = rbuf, .len = 2,},
+ {.addr = fe->id, .flags = I2C_M_RD, .buf = rbuf + 2, .len = 1,},
+ };
+ mxl301rf_w_tuner(fe, wbuf, sizeof(wbuf));
+ *dat = t->adr_tuner && (i2c_transfer(t->i2c, msg, 2) == 2) ? rbuf[2] : 0;
+}
+
+enum mxl301rf_agc {
+ MXL301RF_AGC_AUTO,
+ MXL301RF_AGC_MANUAL,
+};
+
+int mxl301rf_set_agc(struct dvb_frontend *fe, enum mxl301rf_agc agc)
+{
+ u8 dat = agc == MXL301RF_AGC_AUTO ? 0x40 : 0x00,
+ imsrst = 0x01 << 6;
+ int err = mxl301rf_w(fe, 0x25, &dat, 1);
+
+ dat = 0x4c | (agc == MXL301RF_AGC_AUTO ? 0 : 1);
+ return err ||
+ mxl301rf_w(fe, 0x23, &dat, 1) ||
+ mxl301rf_w(fe, 0x01, &imsrst, 1);
+}
+
+int mxl301rf_sleep(struct dvb_frontend *fe)
+{
+ u8 buf = (1 << 7) | (1 << 4),
+ dat[] = {0x01, 0x00, 0x13, 0x00};
+ int err = mxl301rf_set_agc(fe, MXL301RF_AGC_MANUAL);
+
+ if (err)
+ return err;
+ mxl301rf_w_tuner(fe, dat, sizeof(dat));
+ return mxl301rf_w(fe, 0x03, &buf, 1);
+}
+
+int mxl301rf_tune(struct dvb_frontend *fe)
+{
+ struct shf_dvbt {
+ u32 freq, /* Channel center frequency @ kHz */
+ freq_th; /* Offset frequency threshold @ kHz */
+ u8 shf_val, /* Spur shift value */
+ shf_dir; /* Spur shift direction */
+ } shf_dvbt_tab[] = {
+ { 64500, 500, 0x92, 0x07},
+ {191500, 300, 0xe2, 0x07},
+ {205500, 500, 0x2c, 0x04},
+ {212500, 500, 0x1e, 0x04},
+ {226500, 500, 0xd4, 0x07},
+ { 99143, 500, 0x9c, 0x07},
+ {173143, 500, 0xd4, 0x07},
+ {191143, 300, 0xd4, 0x07},
+ {207143, 500, 0xce, 0x07},
+ {225143, 500, 0xce, 0x07},
+ {243143, 500, 0xd4, 0x07},
+ {261143, 500, 0xd4, 0x07},
+ {291143, 500, 0xd4, 0x07},
+ {339143, 500, 0x2c, 0x04},
+ {117143, 500, 0x7a, 0x07},
+ {135143, 300, 0x7a, 0x07},
+ {153143, 500, 0x01, 0x07}
+ };
+ u8 rf_dat[] = {
+ 0x13, 0x00, /* abort tune */
+ 0x3b, 0xc0,
+ 0x3b, 0x80,
+ 0x10, 0x95, /* BW */
+ 0x1a, 0x05,
+ 0x61, 0x00,
+ 0x62, 0xa0,
+ 0x11, 0x40, /* 2 bytes to store RF */
+ 0x12, 0x0e, /* 2 bytes to store RF */
+ 0x13, 0x01 /* start tune */
+ };
+ const u8 idac[] = {
+ 0x0d, 0x00,
+ 0x0c, 0x67,
+ 0x6f, 0x89,
+ 0x70, 0x0c,
+ 0x6f, 0x8a,
+ 0x70, 0x0e,
+ 0x6f, 0x8b,
+ 0x70, 0x10+12,
+ };
+ u8 dat[20];
+ int err = mxl301rf_set_agc(fe, MXL301RF_AGC_MANUAL);
+ u32 freq = fe->dtv_property_cache.frequency,
+ kHz = 1000,
+ MHz = 1000000,
+ dig_rf = freq / MHz,
+ tmp = freq % MHz,
+ i,
+ fdiv = 1000000;
+ bool rfsynth_locked,
+ refsynth_locked;
+ unsigned long timeout;
+
+ if (err)
+ return err;
+ for (i = 0; i < 6; i++) {
+ dig_rf <<= 1;
+ fdiv /= 2;
+ if (tmp > fdiv) {
+ tmp -= fdiv;
+ dig_rf++;
+ }
+ }
+ if (tmp > 7812)
+ dig_rf++;
+ rf_dat[2 * 7 + 1] = (u8)(dig_rf);
+ rf_dat[2 * 8 + 1] = (u8)(dig_rf >> 8);
+ for (i = 0; i < ARRAY_SIZE(shf_dvbt_tab); i++) {
+ if ((freq >= (shf_dvbt_tab[i].freq - shf_dvbt_tab[i].freq_th) * kHz) &&
+ (freq <= (shf_dvbt_tab[i].freq + shf_dvbt_tab[i].freq_th) * kHz)) {
+ rf_dat[2 * 5 + 1] = shf_dvbt_tab[i].shf_val;
+ rf_dat[2 * 6 + 1] = 0xa0 | shf_dvbt_tab[i].shf_dir;
+ break;
+ }
+ }
+ memcpy(dat, rf_dat, sizeof(rf_dat));
+
+ mxl301rf_w_tuner(fe, dat, 14);
+ msleep_interruptible(1);
+ mxl301rf_w_tuner(fe, dat + 14, 6);
+ msleep_interruptible(1);
+ dat[0] = 0x1a;
+ dat[1] = 0x0d;
+ mxl301rf_w_tuner(fe, dat, 2);
+ mxl301rf_w_tuner(fe, idac, sizeof(idac));
+ timeout = jiffies + msecs_to_jiffies(100);
+ while (time_before(jiffies, timeout)) {
+ mxl301rf_r(fe, 0x16, dat);
+ rfsynth_locked = (*dat & 0x0c) == 0x0c;
+ mxl301rf_r(fe, 0x16, dat);
+ refsynth_locked = (*dat & 0x03) == 0x03;
+ if (rfsynth_locked && refsynth_locked)
+ return mxl301rf_set_agc(fe, MXL301RF_AGC_AUTO);
+ msleep_interruptible(1);
+ }
+ return -ETIMEDOUT;
+}
+
+int mxl301rf_wakeup(struct dvb_frontend *fe)
+{
+ u8 buf = (1 << 7) | (0 << 4),
+ dat[2] = {0x01, 0x01};
+ int err = mxl301rf_w(fe, 0x03, &buf, 1);
+
+ if (err)
+ return err;
+ mxl301rf_w_tuner(fe, dat, sizeof(dat));
+ return 0;
+}
+
+static struct dvb_tuner_ops mxl301rf_ops = {
+ .set_params = mxl301rf_tune,
+ .sleep = mxl301rf_sleep,
+ .init = mxl301rf_wakeup,
+};
+
+int mxl301rf_remove(struct i2c_client *c)
+{
+ kfree(i2c_get_clientdata(c));
+ return 0;
+}
+
+int mxl301rf_probe(struct i2c_client *c, const struct i2c_device_id *id)
+{
+ struct dvb_frontend *fe = c->dev.platform_data;
+ struct mxl301rf *t = kzalloc(sizeof(struct mxl301rf), GFP_KERNEL);
+ u8 d[] = {0x10, 0x01};
+
+ if (!t)
+ return -ENOMEM;
+ t->i2c = c->adapter;
+ t->adr_tuner = c->addr;
+ fe->tuner_priv = t;
+ memcpy(&fe->ops.tuner_ops, &mxl301rf_ops, sizeof(struct dvb_tuner_ops));
+ i2c_set_clientdata(c, t);
+ return mxl301rf_w(fe, 0x1c, d, 1) ||
+ mxl301rf_w(fe, 0x1d, d+1, 1);
+}
+
+static struct i2c_device_id mxl301rf_id[] = {
+ {MXL301RF_MODNAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mxl301rf_id);
+
+static struct i2c_driver mxl301rf_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = mxl301rf_id->name,
+ },
+ .probe = mxl301rf_probe,
+ .remove = mxl301rf_remove,
+ .id_table = mxl301rf_id,
+};
+module_i2c_driver(mxl301rf_driver);
+
+MODULE_AUTHOR("Budi Rachmanto, AreMa Inc. <knightrider(@)are.ma>");
+MODULE_DESCRIPTION("Earthsoft PT3 MxL301RF MaxLinear CMOS Hybrid TV ISDB-T tuner driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/tuners/mxl301rf.h b/drivers/media/tuners/mxl301rf.h
new file mode 100644
index 0000000..32a31b0
--- /dev/null
+++ b/drivers/media/tuners/mxl301rf.h
@@ -0,0 +1,23 @@
+/*
+ * Sharp VA4M6JC2103 - Earthsoft PT3 ISDB-T tuner MaxLinear CMOS Hybrid TV MxL301RF
+ *
+ * Copyright (C) 2014 Budi Rachmanto, AreMa Inc. <info@are.ma>
+ *
+ * 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 MXL301RF_H
+#define MXL301RF_H
+
+#define MXL301RF_MODNAME "mxl301rf"
+
+#endif
+
--
2.3.10
^ permalink raw reply related [flat|nested] 39+ messages in thread* [media 6/7] Sharp QM1D1C0042 ISDB-S tuner
2016-02-15 6:08 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
` (4 preceding siblings ...)
2016-02-15 6:08 ` [media 5/7] MaxLinear MxL301RF ISDB-T tuner info
@ 2016-02-15 6:08 ` info
2016-02-15 6:08 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE info
6 siblings, 0 replies; 39+ messages in thread
From: info @ 2016-02-15 6:08 UTC (permalink / raw)
To: linux-media
Cc: Буди Романто, AreMa Inc,
linux-kernel, crope, m.chehab, mchehab, hdegoede,
laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski,
peter.senna
From: Буди Романто, AreMa Inc <knightrider@are.ma>
Signed-off-by: Буди Романто, AreMa Inc <knightrider@are.ma>
---
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@are.ma>
+ *
+ * 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@are.ma>
+ *
+ * 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
^ permalink raw reply related [flat|nested] 39+ messages in thread* [media 7/7] PCI bridge driver for PT3 & PXQ3PE
2016-02-15 6:08 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
` (5 preceding siblings ...)
2016-02-15 6:08 ` [media 6/7] Sharp QM1D1C0042 ISDB-S tuner info
@ 2016-02-15 6:08 ` info
2016-02-15 6:23 ` kbuild test robot
` (3 more replies)
6 siblings, 4 replies; 39+ messages in thread
From: info @ 2016-02-15 6:08 UTC (permalink / raw)
To: linux-media
Cc: Буди Романто, AreMa Inc,
linux-kernel, crope, m.chehab, mchehab, hdegoede,
laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski,
peter.senna
From: Буди Романто, AreMa Inc <knightrider@are.ma>
Signed-off-by: Буди Романто, AreMa Inc <knightrider@are.ma>
---
drivers/media/pci/Kconfig | 2 +-
drivers/media/pci/Makefile | 2 +-
drivers/media/pci/ptx/Kconfig | 21 ++
drivers/media/pci/ptx/Makefile | 8 +
drivers/media/pci/ptx/pt3_pci.c | 509 +++++++++++++++++++++++++++++++
drivers/media/pci/ptx/ptx_common.c | 215 +++++++++++++
drivers/media/pci/ptx/ptx_common.h | 68 +++++
drivers/media/pci/ptx/pxq3pe_pci.c | 607 +++++++++++++++++++++++++++++++++++++
8 files changed, 1430 insertions(+), 2 deletions(-)
create mode 100644 drivers/media/pci/ptx/Kconfig
create mode 100644 drivers/media/pci/ptx/Makefile
create mode 100644 drivers/media/pci/ptx/pt3_pci.c
create mode 100644 drivers/media/pci/ptx/ptx_common.c
create mode 100644 drivers/media/pci/ptx/ptx_common.h
create mode 100644 drivers/media/pci/ptx/pxq3pe_pci.c
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index 48a611b..9d63ad6 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -44,7 +44,7 @@ source "drivers/media/pci/b2c2/Kconfig"
source "drivers/media/pci/pluto2/Kconfig"
source "drivers/media/pci/dm1105/Kconfig"
source "drivers/media/pci/pt1/Kconfig"
-source "drivers/media/pci/pt3/Kconfig"
+source "drivers/media/pci/ptx/Kconfig"
source "drivers/media/pci/mantis/Kconfig"
source "drivers/media/pci/ngene/Kconfig"
source "drivers/media/pci/ddbridge/Kconfig"
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
index 5f8aacb..984e37c 100644
--- a/drivers/media/pci/Makefile
+++ b/drivers/media/pci/Makefile
@@ -7,7 +7,7 @@ obj-y += ttpci/ \
pluto2/ \
dm1105/ \
pt1/ \
- pt3/ \
+ ptx/ \
mantis/ \
ngene/ \
ddbridge/ \
diff --git a/drivers/media/pci/ptx/Kconfig b/drivers/media/pci/ptx/Kconfig
new file mode 100644
index 0000000..792acfe
--- /dev/null
+++ b/drivers/media/pci/ptx/Kconfig
@@ -0,0 +1,21 @@
+config DVB_PT3
+ tristate "Earthsoft PT3 cards"
+ depends on DVB_CORE && PCI && I2C
+ select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_QM1D1C0042 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MXL301RF if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for Earthsoft PT3 ISDB-S/T PCIe cards.
+
+ Say Y or M if you own such a device and want to use it.
+
+config DVB_PXQ3PE
+ tristate "PLEX PX-Q3PE cards"
+ depends on DVB_CORE && PCI && I2C
+ select DVB_TC90522 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_QM1D1C0042 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_MXL301RF if MEDIA_SUBDRV_AUTOSELECT
+ help
+ Support for PLEX PX-Q3PE ISDB-S/T PCIe cards.
+
+ Say Y or M if you own such a device and want to use it.
diff --git a/drivers/media/pci/ptx/Makefile b/drivers/media/pci/ptx/Makefile
new file mode 100644
index 0000000..b10ad8a
--- /dev/null
+++ b/drivers/media/pci/ptx/Makefile
@@ -0,0 +1,8 @@
+pt3-objs := pt3_pci.o ptx_common.o
+pxq3pe-objs := pxq3pe_pci.o ptx_common.o
+
+obj-$(CONFIG_DVB_PT3) += pt3.o
+obj-$(CONFIG_DVB_PXQ3PE) += pxq3pe.o
+
+ccflags-y += -Idrivers/media/dvb-core -Idrivers/media/dvb-frontends -Idrivers/media/tuners
+
diff --git a/drivers/media/pci/ptx/pt3_pci.c b/drivers/media/pci/ptx/pt3_pci.c
new file mode 100644
index 0000000..3af9e48
--- /dev/null
+++ b/drivers/media/pci/ptx/pt3_pci.c
@@ -0,0 +1,509 @@
+/*
+ * DVB driver for Earthsoft PT3 ISDB-S/T PCIE bridge Altera Cyclone IV FPGA EP4CGX15BF14C8N
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+ *
+ * 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 <linux/pci.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include "dvb_frontend.h"
+#include "tc90522.h"
+#include "qm1d1c0042.h"
+#include "mxl301rf.h"
+#include "ptx_common.h"
+
+MODULE_AUTHOR("Budi Rachmanto, AreMa Inc. <knightrider(@)are.ma>");
+MODULE_DESCRIPTION("Earthsoft PT3 DVB Driver");
+MODULE_LICENSE("GPL");
+
+static struct pci_device_id pt3_id[] = {
+ {PCI_DEVICE(0x1172, 0x4c15)},
+ {},
+};
+MODULE_DEVICE_TABLE(pci, pt3_id);
+
+enum ePT3 {
+ PT3_REG_VERSION = 0x00, /* R Version */
+ PT3_REG_BUS = 0x04, /* R Bus */
+ PT3_REG_SYS_W = 0x08, /* W System */
+ PT3_REG_SYS_R = 0x0c, /* R System */
+ PT3_REG_I2C_W = 0x10, /* W I2C */
+ PT3_REG_I2C_R = 0x14, /* R I2C */
+ PT3_REG_RAM_W = 0x18, /* W RAM */
+ PT3_REG_RAM_R = 0x1c, /* R RAM */
+ PT3_DMA_BASE = 0x40, /* + 0x18*idx */
+ PT3_DMA_OFFSET = 0x18,
+ PT3_DMA_DESC_L = 0x00, /* W DMA descriptor */
+ PT3_DMA_DESC_H = 0x04, /* W DMA descriptor */
+ PT3_DMA_CTL = 0x08, /* W DMA */
+ PT3_TS_CTL = 0x0c, /* W TS */
+ PT3_STATUS = 0x10, /* R DMA/FIFO/TS */
+ PT3_TS_ERR = 0x14, /* R TS */
+
+ PT3_I2C_DATA_OFFSET = 0x800,
+ PT3_I2C_START_ADDR = 0x17fa,
+
+ PT3_PWR_OFF = 0x00,
+ PT3_PWR_AMP_ON = 0x04,
+ PT3_PWR_TUNER_ON = 0x40,
+};
+
+struct pt3_card {
+ void __iomem *bar_reg,
+ *bar_mem;
+};
+
+struct pt3_dma {
+ dma_addr_t adr;
+ u8 *dat;
+ u32 sz,
+ pos;
+};
+
+struct pt3_adap {
+ u32 ts_pos,
+ ts_count,
+ desc_count;
+ void __iomem *dma_base;
+ struct pt3_dma *ts_info,
+ *desc_info;
+};
+
+int pt3_i2c_flush(struct pt3_card *c, u32 start_addr)
+{
+ u32 i2c_wait(void)
+ {
+ while (1) {
+ u32 val = readl(c->bar_reg + PT3_REG_I2C_R);
+
+ if (!(val & 1)) /* sequence stopped */
+ return val;
+ msleep_interruptible(1);
+ }
+ }
+ i2c_wait();
+ writel(1 << 16 | start_addr, c->bar_reg + PT3_REG_I2C_W); /* 0x00010000 start sequence */
+ return i2c_wait() & 0b0110 ? -EIO : 0; /* ACK status */
+}
+
+int pt3_i2c_xfr(struct i2c_adapter *i2c, struct i2c_msg *msg, int sz)
+{
+ enum pt3_i2c_cmd {
+ I_END,
+ I_ADDRESS,
+ I_CLOCK_L,
+ I_CLOCK_H,
+ I_DATA_L,
+ I_DATA_H,
+ I_RESET,
+ I_SLEEP,
+ I_DATA_L_NOP = 0x08,
+ I_DATA_H_NOP = 0x0c,
+ I_DATA_H_READ = 0x0d,
+ I_DATA_H_ACK0 = 0x0e,
+ };
+ struct ptx_card *card = i2c_get_adapdata(i2c);
+ struct pt3_card *c = card->priv;
+ u32 offset = 0;
+ u8 buf;
+ bool filled = false;
+
+ void i2c_shoot(u8 dat)
+ {
+ if (filled) {
+ buf |= dat << 4;
+ writeb(buf, c->bar_mem + PT3_I2C_DATA_OFFSET + offset);
+ offset++;
+ } else
+ buf = dat;
+ filled ^= true;
+ }
+
+ void i2c_w(const u8 *dat, u32 size)
+ {
+ u32 i, j;
+
+ for (i = 0; i < size; i++) {
+ for (j = 0; j < 8; j++)
+ i2c_shoot((dat[i] >> (7 - j)) & 1 ? I_DATA_H_NOP : I_DATA_L_NOP);
+ i2c_shoot(I_DATA_H_ACK0);
+ }
+ }
+
+ void i2c_r(u32 size)
+ {
+ u32 i, j;
+
+ for (i = 0; i < size; i++) {
+ for (j = 0; j < 8; j++)
+ i2c_shoot(I_DATA_H_READ);
+ if (i == (size - 1))
+ i2c_shoot(I_DATA_H_NOP);
+ else
+ i2c_shoot(I_DATA_L_NOP);
+ }
+ }
+ int i, j;
+
+ if (sz < 1 || sz > 3 || !msg || msg[0].flags) /* always write first */
+ return -ENOTSUPP;
+ mutex_lock(&card->lock);
+ for (i = 0; i < sz; i++) {
+ u8 byte = (msg[i].addr << 1) | (msg[i].flags & 1);
+
+ /* start */
+ i2c_shoot(I_DATA_H);
+ i2c_shoot(I_CLOCK_H);
+ i2c_shoot(I_DATA_L);
+ i2c_shoot(I_CLOCK_L);
+ i2c_w(&byte, 1);
+ if (msg[i].flags == I2C_M_RD)
+ i2c_r(msg[i].len);
+ else
+ i2c_w(msg[i].buf, msg[i].len);
+ }
+
+ /* stop */
+ i2c_shoot(I_DATA_L);
+ i2c_shoot(I_CLOCK_H);
+ i2c_shoot(I_DATA_H);
+ i2c_shoot(I_END);
+ if (filled)
+ i2c_shoot(I_END);
+ if (pt3_i2c_flush(c, 0))
+ sz = -EIO;
+ else
+ for (i = 1; i < sz; i++)
+ if (msg[i].flags == I2C_M_RD)
+ for (j = 0; j < msg[i].len; j++)
+ msg[i].buf[j] = readb(c->bar_mem + PT3_I2C_DATA_OFFSET + j);
+ mutex_unlock(&card->lock);
+ return sz;
+}
+
+static const struct i2c_algorithm pt3_i2c_algo = {
+ .functionality = ptx_i2c_func,
+ .master_xfer = pt3_i2c_xfr,
+};
+
+void pt3_lnb(struct ptx_card *card, bool lnb)
+{
+ struct pt3_card *c = card->priv;
+
+ writel(lnb ? 0b1111 : 0b1100, c->bar_reg + PT3_REG_SYS_W);
+}
+
+int pt3_thread(void *dat)
+{
+ struct ptx_adap *adap = dat;
+ struct pt3_adap *p = adap->priv;
+ struct pt3_dma *ts;
+ u32 i,
+ prev;
+ size_t csize,
+ remain = 0;
+
+ set_freezable();
+ while (!kthread_should_stop()) {
+ try_to_freeze();
+ while (p->ts_info[p->ts_pos].sz > remain) {
+ remain = p->ts_info[p->ts_pos].sz;
+ mutex_lock(&adap->lock);
+ while (remain > 0) {
+ for (i = 0; i < 20; i++) {
+ struct pt3_dma *ts;
+ u32 next = p->ts_pos + 1;
+
+ if (next >= p->ts_count)
+ next = 0;
+ ts = &p->ts_info[next];
+ if (ts->dat[ts->pos] == PTX_TS_SYNC)
+ break;
+ msleep_interruptible(30);
+ }
+ if (i == 20)
+ break;
+ prev = p->ts_pos - 1;
+ if (prev < 0 || p->ts_count <= prev)
+ prev = p->ts_count - 1;
+ ts = &p->ts_info[p->ts_pos];
+ while (remain > 0) {
+ csize = (remain < (ts->sz - ts->pos)) ?
+ remain : (ts->sz - ts->pos);
+ dvb_dmx_swfilter_raw(&adap->demux, &ts->dat[ts->pos], csize);
+ remain -= csize;
+ ts->pos += csize;
+ if (ts->pos < ts->sz)
+ continue;
+ ts->pos = 0;
+ ts->dat[ts->pos] = PTX_TS_NOT_SYNC;
+ p->ts_pos++;
+ if (p->ts_pos >= p->ts_count)
+ p->ts_pos = 0;
+ break;
+ }
+ }
+ mutex_unlock(&adap->lock);
+ }
+ if (p->ts_info[p->ts_pos].sz < remain)
+ msleep_interruptible(1);
+ }
+ return 0;
+}
+
+void pt3_dma_run(struct ptx_adap *adap, bool ON)
+{
+ struct pt3_adap *p = adap->priv;
+ void __iomem *base = p->dma_base;
+ u64 start_addr = p->desc_info->adr,
+ i;
+
+ if (ON) {
+ writel(1 << 18, base + PT3_TS_CTL); /* reset error count */
+ for (i = 0; i < p->ts_count; i++) {
+ struct pt3_dma *ts = &p->ts_info[i];
+
+ memset(ts->dat, 0, ts->sz);
+ ts->pos = 0;
+ *ts->dat = PTX_TS_NOT_SYNC;
+ }
+ p->ts_pos = 0;
+ writel(1 << 1, base + PT3_DMA_CTL); /* stop DMA */
+ writel(start_addr & 0xffffffff, base + PT3_DMA_DESC_L);
+ writel(start_addr >> 32, base + PT3_DMA_DESC_H);
+ writel(1 << 0, base + PT3_DMA_CTL); /* start DMA */
+ } else {
+ writel(1 << 1, base + PT3_DMA_CTL); /* stop DMA */
+ while (1) {
+ if (!(readl(base + PT3_STATUS) & 1))
+ break;
+ msleep_interruptible(1);
+ }
+ }
+}
+
+int pt3_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct ptx_adap *adap = container_of(feed->demux, struct ptx_adap, demux);
+
+ if (adap->kthread) {
+ pt3_dma_run(adap, false);
+ kthread_stop(adap->kthread);
+ adap->kthread = NULL;
+ }
+ return 0;
+}
+
+int pt3_start_feed(struct dvb_demux_feed *feed)
+{
+ int err = 0;
+ struct ptx_adap *adap = container_of(feed->demux, struct ptx_adap, demux);
+
+ if (!adap->kthread) {
+ adap->kthread = kthread_run(pt3_thread, adap, "%s_%d", adap->card->name, adap->fe.id);
+ if (IS_ERR(adap->kthread)) {
+ err = PTR_ERR(adap->kthread);
+ adap->kthread = NULL;
+ } else
+ pt3_dma_run(adap, true);
+ }
+ return err;
+}
+
+void pt3_dma_free(struct ptx_adap *adap)
+{
+ struct pt3_adap *p = adap->priv;
+ struct pt3_dma *page;
+ u32 i;
+
+ if (p->ts_info) {
+ for (i = 0; i < p->ts_count; i++) {
+ page = &p->ts_info[i];
+ if (page->dat)
+ pci_free_consistent(adap->card->pdev, page->sz, page->dat, page->adr);
+ }
+ kfree(p->ts_info);
+ }
+ if (p->desc_info) {
+ for (i = 0; i < p->desc_count; i++) {
+ page = &p->desc_info[i];
+ if (page->dat)
+ pci_free_consistent(adap->card->pdev, page->sz, page->dat, page->adr);
+ }
+ kfree(p->desc_info);
+ }
+}
+
+int pt3_power(struct dvb_frontend *fe, u8 pwr)
+{
+ struct ptx_adap *adap = container_of(fe, struct ptx_adap, fe);
+ u8 buf[] = {0x1e, pwr | 0b10011001};
+ struct i2c_msg msg[] = {
+ {.addr = fe->id, .flags = 0, .buf = buf, .len = 2,},
+ };
+
+ return i2c_transfer(&adap->card->i2c, msg, 1) == 1 ? 0 : -EIO;
+}
+
+void pt3_remove(struct pci_dev *pdev)
+{
+ struct ptx_card *card = pci_get_drvdata(pdev);
+ struct pt3_card *c = card->priv;
+ struct ptx_adap *adap = card->adap;
+ int i;
+
+ if (!card)
+ return;
+ for (i = 0; i < card->adapn; i++, adap++) {
+ pt3_dma_run(adap, false);
+ pt3_dma_free(adap);
+ ptx_sleep(&adap->fe);
+ pt3_power(&adap->fe, PT3_PWR_OFF);
+ }
+ ptx_unregister_adap_fe(card);
+ if (c->bar_reg) {
+ writel(1 << 17, c->bar_reg + PT3_REG_I2C_W); /* i2c_reset */
+ iounmap(c->bar_reg);
+ }
+ if (c->bar_mem)
+ iounmap(c->bar_mem);
+}
+
+int pt3_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct dma_desc {
+ u64 page_addr;
+ u32 page_size;
+ u64 next_desc;
+ } __packed;
+ enum {
+ DMA_MAX_DESCS = 204,
+ DMA_PAGE_SIZE = DMA_MAX_DESCS * sizeof(struct dma_desc),
+ DMA_BLOCK_COUNT = 17,
+ DMA_BLOCK_SIZE = DMA_PAGE_SIZE * 47,
+ DMA_TS_BUF_SIZE = DMA_BLOCK_SIZE * DMA_BLOCK_COUNT,
+ };
+ struct ptx_subdev_info pt3_subdev_info[] = {
+ {SYS_ISDBS, 0b00010001, TC90522_MODNAME, 0x63, QM1D1C0042_MODNAME},
+ {SYS_ISDBS, 0b00010011, TC90522_MODNAME, 0x60, QM1D1C0042_MODNAME},
+ {SYS_ISDBT, 0b00010000, TC90522_MODNAME, 0x62, MXL301RF_MODNAME},
+ {SYS_ISDBT, 0b00010010, TC90522_MODNAME, 0x61, MXL301RF_MODNAME},
+ };
+ struct ptx_card *card = ptx_alloc(pdev, KBUILD_MODNAME, ARRAY_SIZE(pt3_subdev_info),
+ sizeof(struct pt3_card), sizeof(struct pt3_adap), pt3_lnb);
+ struct pt3_card *c = card->priv;
+ struct ptx_adap *adap;
+
+ bool dma_create(void)
+ {
+ struct pt3_adap *p = adap->priv;
+ struct pt3_dma *descinfo;
+ struct dma_desc *prev = NULL,
+ *curr;
+ u32 i,
+ j,
+ desc_remain = 0,
+ desc_info_pos = 0;
+ u64 desc_addr;
+
+ p->ts_count = DMA_BLOCK_COUNT;
+ p->ts_info = kcalloc(p->ts_count, sizeof(struct pt3_dma), GFP_KERNEL);
+ p->desc_count = 1 + (DMA_TS_BUF_SIZE / DMA_PAGE_SIZE - 1) / DMA_MAX_DESCS;
+ p->desc_info = kcalloc(p->desc_count, sizeof(struct pt3_dma), GFP_KERNEL);
+ if (!p->ts_info || !p->desc_info)
+ return false;
+ for (i = 0; i < p->ts_count; i++) {
+ p->ts_info[i].sz = DMA_BLOCK_SIZE;
+ p->ts_info[i].pos = 0;
+ p->ts_info[i].dat = pci_alloc_consistent(adap->card->pdev, p->ts_info[i].sz, &p->ts_info[i].adr);
+ if (!p->ts_info[i].dat)
+ return false;
+ }
+ for (i = 0; i < p->desc_count; i++) {
+ p->desc_info[i].sz = DMA_PAGE_SIZE;
+ p->desc_info[i].pos = 0;
+ p->desc_info[i].dat = pci_alloc_consistent(adap->card->pdev, p->desc_info[i].sz, &p->desc_info[i].adr);
+ if (!p->desc_info[i].dat)
+ return false;
+ }
+ for (i = 0; i < p->ts_count; i++)
+ for (j = 0; j < p->ts_info[i].sz / DMA_PAGE_SIZE; j++) {
+ if (desc_remain < sizeof(struct dma_desc)) {
+ descinfo = &p->desc_info[desc_info_pos];
+ descinfo->pos = 0;
+ curr = (struct dma_desc *)&descinfo->dat[descinfo->pos];
+ desc_addr = descinfo->adr;
+ desc_remain = descinfo->sz;
+ desc_info_pos++;
+ }
+ if (prev)
+ prev->next_desc = desc_addr | 0b10;
+ curr->page_addr = 0b111 | (p->ts_info[i].adr + DMA_PAGE_SIZE * j);
+ curr->page_size = 0b111 | DMA_PAGE_SIZE;
+ curr->next_desc = 0b10;
+
+ prev = curr;
+ descinfo->pos += sizeof(struct dma_desc);
+ curr = (struct dma_desc *)&descinfo->dat[descinfo->pos];
+ desc_addr += sizeof(struct dma_desc);
+ desc_remain -= sizeof(struct dma_desc);
+ }
+ prev->next_desc = p->desc_info->adr | 0b10;
+ return true;
+ }
+
+ bool i2c_is_clean(void)
+ {
+ return (readl(c->bar_reg + PT3_REG_I2C_R) >> 3) & 1;
+ }
+ u8 i;
+ int err = !card || pci_read_config_byte(pdev, PCI_CLASS_REVISION, &i);
+
+ if (err)
+ return ptx_abort(pdev, pt3_remove, err, "PCI/DMA/memory error");
+ if (i != 1)
+ return ptx_abort(pdev, pt3_remove, -EINVAL, "Revision 0x%X is not supported", i);
+ pci_set_master(pdev);
+ c->bar_reg = pci_ioremap_bar(pdev, 0);
+ c->bar_mem = pci_ioremap_bar(pdev, 2);
+ if (!c->bar_reg || !c->bar_mem)
+ return ptx_abort(pdev, pt3_remove, -EIO, "Failed pci_ioremap_bar");
+ err = readl(c->bar_reg + PT3_REG_VERSION);
+ i = ((err >> 24) & 0xFF);
+ if (i != 3)
+ return ptx_abort(pdev, pt3_remove, -EIO, "ID=0x%X, not a PT3", i);
+ i = ((err >> 8) & 0xFF);
+ if (i != 4)
+ return ptx_abort(pdev, pt3_remove, -EIO, "FPGA version 0x%X is not supported", i);
+ if (ptx_i2c_add_adapter(card, &pt3_i2c_algo) || (!i2c_is_clean() && pt3_i2c_flush(c, 0)))
+ return ptx_abort(pdev, pt3_remove, err, "Cannot add I2C");
+ for (i = 0, adap = card->adap; i < card->adapn; i++, adap++) {
+ struct dvb_frontend *fe = &adap->fe;
+ struct pt3_adap *p = adap->priv;
+
+ fe->id = pt3_subdev_info[i].demod_addr;
+ p->dma_base = c->bar_reg + PT3_DMA_BASE + PT3_DMA_OFFSET * i;
+ if (!dma_create())
+ return ptx_abort(pdev, pt3_remove, -ENOMEM, "Failed dma_create");
+ }
+ err = ptx_register_adap_fe(card, pt3_subdev_info, pt3_start_feed, pt3_stop_feed) ||
+ pt3_power(&card->adap[i - 1].fe, PT3_PWR_TUNER_ON) ||
+ pt3_i2c_flush(c, PT3_I2C_START_ADDR) ||
+ pt3_power(&card->adap[i - 1].fe, PT3_PWR_TUNER_ON | PT3_PWR_AMP_ON);
+ return err ? ptx_abort(pdev, pt3_remove, err, "Unable to register DVB adapter/frontend") : 0;
+}
+
+static struct pci_driver pt3_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = pt3_id,
+ .probe = pt3_probe,
+ .remove = pt3_remove,
+};
+module_pci_driver(pt3_driver);
+
diff --git a/drivers/media/pci/ptx/ptx_common.c b/drivers/media/pci/ptx/ptx_common.c
new file mode 100644
index 0000000..2d98d27
--- /dev/null
+++ b/drivers/media/pci/ptx/ptx_common.c
@@ -0,0 +1,215 @@
+/*
+ * Common procedures for PT3 & PX-Q3PE DVB driver
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+ */
+
+#include <linux/pci.h>
+#include "dvb_frontend.h"
+#include "ptx_common.h"
+
+void ptx_lnb(struct ptx_card *card)
+{
+ int i;
+ bool lnb = false;
+
+ for (i = 0; i < card->adapn; i++)
+ if (card->adap[i].fe.dtv_property_cache.delivery_system == SYS_ISDBS && card->adap[i].ON) {
+ lnb = true;
+ break;
+ }
+ if (card->lnbON != lnb) {
+ card->lnb(card, lnb);
+ card->lnbON = lnb;
+ }
+}
+
+int ptx_sleep(struct dvb_frontend *fe)
+{
+ struct ptx_adap *adap = container_of(fe, struct ptx_adap, fe);
+
+ adap->ON = false;
+ ptx_lnb(adap->card);
+ return adap->fe_sleep ? adap->fe_sleep(fe) : 0;
+}
+
+int ptx_wakeup(struct dvb_frontend *fe)
+{
+ struct ptx_adap *adap = container_of(fe, struct ptx_adap, fe);
+
+ adap->ON = true;
+ ptx_lnb(adap->card);
+ return adap->fe_wakeup ? adap->fe_wakeup(fe) : 0;
+}
+
+struct ptx_card *ptx_alloc(struct pci_dev *pdev, u8 *name, u8 adapn, u32 sz_card_priv, u32 sz_adap_priv,
+ void (*lnb)(struct ptx_card *, bool))
+{
+ u8 i;
+ struct ptx_card *card = kzalloc(sizeof(struct ptx_card) + sz_card_priv + adapn *
+ (sizeof(struct ptx_adap) + sz_adap_priv), GFP_KERNEL);
+ if (!card)
+ return NULL;
+ card->priv = sz_card_priv ? &card[1] : NULL;
+ card->adap = (struct ptx_adap *)((u8 *)&card[1] + sz_card_priv);
+ card->pdev = pdev;
+ card->adapn = adapn;
+ card->name = name;
+ card->lnbON = true;
+ card->lnb = lnb;
+ for (i = 0; i < card->adapn; i++) {
+ struct ptx_adap *p = &card->adap[i];
+
+ p->card = card;
+ p->priv = sz_adap_priv ? (u8 *)&card->adap[card->adapn] + i * sz_adap_priv : NULL;
+ }
+ if (pci_enable_device(pdev) ||
+ pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) ||
+ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) ||
+ pci_request_regions(pdev, name)) {
+ kfree(card);
+ return NULL;
+ }
+ pci_set_drvdata(pdev, card);
+ return card;
+}
+
+int ptx_i2c_add_adapter(struct ptx_card *card, const struct i2c_algorithm *algo)
+{
+ struct i2c_adapter *i2c = &card->i2c;
+
+ i2c->algo = algo;
+ i2c->dev.parent = &card->pdev->dev;
+ strcpy(i2c->name, card->name);
+ i2c_set_adapdata(i2c, card);
+ mutex_init(&card->lock);
+ return i2c_add_adapter(i2c);
+}
+
+void ptx_unregister_subdev(struct i2c_client *c)
+{
+ if (!c)
+ return;
+ if (c->dev.driver)
+ module_put(c->dev.driver->owner);
+ i2c_unregister_device(c);
+}
+
+struct i2c_client *ptx_register_subdev(struct i2c_adapter *i2c, void *dat, u16 adr, char *type)
+{
+ struct i2c_client *c;
+ struct i2c_board_info info = {
+ .platform_data = dat,
+ .addr = adr,
+ };
+
+ strlcpy(info.type, type, I2C_NAME_SIZE);
+ request_module("%s", info.type);
+ c = i2c_new_device(i2c, &info);
+ if (c) {
+ if (c->dev.driver && try_module_get(c->dev.driver->owner))
+ return c;
+ i2c_unregister_device(c);
+ }
+ return NULL;
+}
+
+void ptx_unregister_adap_fe(struct ptx_card *card)
+{
+ int i = card->adapn - 1;
+ struct ptx_adap *adap = card->adap + i;
+
+ for (; i >= 0; i--, adap--) {
+ if (adap->fe.frontend_priv)
+ dvb_unregister_frontend(&adap->fe);
+ if (adap->fe.ops.release)
+ adap->fe.ops.release(&adap->fe);
+ ptx_unregister_subdev(adap->tuner);
+ ptx_unregister_subdev(adap->demod);
+ if (adap->demux.dmx.close)
+ adap->demux.dmx.close(&adap->demux.dmx);
+ if (adap->dmxdev.filter)
+ dvb_dmxdev_release(&adap->dmxdev);
+ if (adap->demux.cnt_storage)
+ dvb_dmx_release(&adap->demux);
+ if (adap->dvb.name)
+ dvb_unregister_adapter(&adap->dvb);
+ }
+ i2c_del_adapter(&card->i2c);
+ pci_release_regions(card->pdev);
+ pci_set_drvdata(card->pdev, NULL);
+ pci_disable_device(card->pdev);
+ kfree(card);
+}
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adap_no);
+int ptx_register_adap_fe(struct ptx_card *card, const struct ptx_subdev_info *info,
+ int (*start)(struct dvb_demux_feed *), int (*stop)(struct dvb_demux_feed *))
+{
+ struct ptx_adap *adap;
+ u8 i;
+ int err;
+
+ for (i = 0, adap = card->adap; i < card->adapn; i++, adap++) {
+ struct dvb_adapter *dvb = &adap->dvb;
+ struct dvb_demux *demux = &adap->demux;
+ struct dmxdev *dmxdev = &adap->dmxdev;
+ struct dvb_frontend *fe = &adap->fe;
+
+ if (dvb_register_adapter(dvb, card->name, THIS_MODULE, &card->pdev->dev, adap_no) < 0)
+ return -ENFILE;
+ demux->feednum = 1;
+ demux->filternum = 1;
+ demux->start_feed = start;
+ demux->stop_feed = stop;
+ if (dvb_dmx_init(demux) < 0)
+ return -ENOMEM;
+ dmxdev->filternum = 1;
+ dmxdev->demux = &demux->dmx;
+ err = dvb_dmxdev_init(dmxdev, dvb);
+ if (err)
+ return err;
+ fe->dtv_property_cache.delivery_system = info[i].type;
+ fe->dvb = &adap->dvb;
+ adap->demod = ptx_register_subdev(&card->i2c, &adap->fe, info[i].demod_addr, info[i].demod_name);
+ adap->tuner = ptx_register_subdev(&card->i2c, &adap->fe, info[i].tuner_addr, info[i].tuner_name);
+ if (!adap->demod || !adap->tuner)
+ return -ENFILE;
+ adap->fe_sleep = adap->fe.ops.sleep;
+ adap->fe_wakeup = adap->fe.ops.init;
+ adap->fe.ops.sleep = ptx_sleep;
+ adap->fe.ops.init = ptx_wakeup;
+ adap->fe.dvb = &adap->dvb;
+ if (dvb_register_frontend(&adap->dvb, &adap->fe))
+ return -EIO;
+ ptx_sleep(&adap->fe);
+ mutex_init(&adap->lock);
+ }
+ return 0;
+}
+
+int ptx_abort(struct pci_dev *pdev, void remover(struct pci_dev *), int err, char *fmt, ...)
+{
+ va_list ap;
+ char *s = NULL;
+ int slen;
+
+ va_start(ap, fmt);
+ slen = vsnprintf(s, 0, fmt, ap) + 1;
+ s = vzalloc(slen);
+ if (s) {
+ vsnprintf(s, slen, fmt, ap);
+ dev_err(&pdev->dev, "%s", s);
+ vfree(s);
+ }
+ va_end(ap);
+ remover(pdev);
+ return err;
+}
+
+u32 ptx_i2c_func(struct i2c_adapter *i2c)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_NOSTART;
+}
+
+
diff --git a/drivers/media/pci/ptx/ptx_common.h b/drivers/media/pci/ptx/ptx_common.h
new file mode 100644
index 0000000..e5edba41
--- /dev/null
+++ b/drivers/media/pci/ptx/ptx_common.h
@@ -0,0 +1,68 @@
+/*
+ * Defs & procs for PT3 & PX-Q3PE DVB driver
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+ *
+ * This program is distributed in 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 PTX_COMMON_H
+#define PTX_COMMON_H
+
+#include "dvb_demux.h"
+#include "dmxdev.h"
+
+enum ePTX {
+ PTX_TS_SYNC = 0x47,
+ PTX_TS_NOT_SYNC = 0x74,
+};
+
+struct ptx_subdev_info {
+ fe_delivery_system_t type;
+ u8 demod_addr, *demod_name,
+ tuner_addr, *tuner_name;
+};
+
+struct ptx_card {
+ struct ptx_adap *adap;
+ struct mutex lock;
+ struct i2c_adapter i2c;
+ struct pci_dev *pdev;
+ u8 *name,
+ adapn;
+ bool lnbON;
+ void *priv,
+ (*lnb)(struct ptx_card *card, bool lnb);
+};
+
+struct ptx_adap {
+ struct ptx_card *card;
+ struct mutex lock;
+ bool ON;
+ struct dvb_frontend fe;
+ struct dvb_adapter dvb;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct i2c_client *demod,
+ *tuner;
+ struct task_struct *kthread;
+ void *priv;
+ int (*fe_sleep)(struct dvb_frontend *),
+ (*fe_wakeup)(struct dvb_frontend *);
+};
+
+struct ptx_card *ptx_alloc(struct pci_dev *pdev, u8 *name, u8 adapn, u32 sz_card_priv, u32 sz_adap_priv,
+ void (*lnb)(struct ptx_card *, bool));
+int ptx_sleep(struct dvb_frontend *fe);
+int ptx_wakeup(struct dvb_frontend *fe);
+int ptx_i2c_add_adapter(struct ptx_card *card, const struct i2c_algorithm *algo);
+void ptx_unregister_adap_fe(struct ptx_card *card);
+int ptx_register_adap_fe(struct ptx_card *card, const struct ptx_subdev_info *info,
+ int (*start)(struct dvb_demux_feed *), int (*stop)(struct dvb_demux_feed *));
+int ptx_abort(struct pci_dev *pdev, void remover(struct pci_dev *), int err, char *fmt, ...);
+u32 ptx_i2c_func(struct i2c_adapter *i2c);
+
+#endif
diff --git a/drivers/media/pci/ptx/pxq3pe_pci.c b/drivers/media/pci/ptx/pxq3pe_pci.c
new file mode 100644
index 0000000..5bf0648
--- /dev/null
+++ b/drivers/media/pci/ptx/pxq3pe_pci.c
@@ -0,0 +1,607 @@
+/*
+ * DVB driver for PLEX PX-Q3PE ISDB-S/T PCIE receiver
+ *
+ * Copyright (C) Budi Rachmanto, AreMa Inc. <info@are.ma>
+ *
+ * Main components:
+ * ASIE5606X8 - controller
+ * TC90522 - 2ch OFDM ISDB-T + 2ch 8PSK ISDB-S demodulator
+ * TDA20142 - ISDB-S tuner
+ * NM120 - ISDB-T tuner
+ */
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include "dvb_frontend.h"
+#include "ptx_common.h"
+#include "tc90522.h"
+#include "tda2014x.h"
+#include "nm131.h"
+
+#define MOD_AUTH "Budi Rachmanto, AreMa Inc. <knightrider(@)are.ma>"
+MODULE_AUTHOR(MOD_AUTH);
+MODULE_DESCRIPTION("PLEX PX-Q3PE Driver");
+MODULE_LICENSE("GPL");
+
+static char *auth = MOD_AUTH;
+static int ni,
+ nx,
+ idx[8] = {0},
+ xor[4] = {0};
+module_param(auth, charp, 0);
+module_param_array(idx, int, &ni, 0);
+module_param_array(xor, int, &nx, 0);
+
+static struct pci_device_id pxq3pe_id_table[] = {
+ {0x188B, 0x5220, 0x0B06, 0x0002, 0, 0, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(pci, pxq3pe_id_table);
+
+enum ePXQ3PE {
+ PKT_BYTES = 188,
+ PKT_NUM = 312,
+ PKT_BUFSZ = PKT_BYTES * PKT_NUM,
+
+ PXQ3PE_IRQ_STAT = 0x808,
+ PXQ3PE_IRQ_CLEAR = 0x80C,
+ PXQ3PE_IRQ_ACTIVE = 0x814,
+ PXQ3PE_IRQ_DISABLE = 0x818,
+ PXQ3PE_IRQ_ENABLE = 0x81C,
+
+ PXQ3PE_MODE_GPIO = 0,
+ PXQ3PE_MODE_TUNER = 1,
+ PXQ3PE_MODE_STAT = 2,
+
+ PXQ3PE_I2C_ADR_GPIO = 0x4A,
+ PXQ3PE_I2C_CTL_STAT = 0x940,
+ PXQ3PE_I2C_ADR = 0x944,
+ PXQ3PE_I2C_SW_CTL = 0x948,
+ PXQ3PE_I2C_FIFO_STAT = 0x950,
+ PXQ3PE_I2C_FIFO_DATA = 0x960,
+
+ PXQ3PE_DMA_OFFSET_PORT = 0x140,
+ PXQ3PE_DMA_TSMODE = 0xA00,
+ PXQ3PE_DMA_MGMT = 0xAE0,
+ PXQ3PE_DMA_OFFSET_CH = 0x10,
+ PXQ3PE_DMA_ADR_LO = 0xAC0,
+ PXQ3PE_DMA_ADR_HI = 0xAC4,
+ PXQ3PE_DMA_XFR_STAT = 0xAC8,
+ PXQ3PE_DMA_CTL = 0xACC,
+
+ PXQ3PE_MAX_LOOP = 0xFFFF,
+};
+
+struct pxq3pe_card {
+ void __iomem *bar;
+ struct {
+ dma_addr_t adr;
+ u8 *dat;
+ u32 sz;
+ bool ON[2];
+ } dma;
+};
+
+struct pxq3pe_adap {
+ u8 tBuf[PKT_BUFSZ],
+ *sBuf;
+ u32 tBufIdx,
+ sBufSize,
+ sBufStart,
+ sBufStop,
+ sBufByteCnt;
+};
+
+bool pxq3pe_w(struct ptx_card *card, u8 slvadr, u8 regadr, u8 *wdat, u8 bytelen, u8 mode)
+{
+ struct pxq3pe_card *c = card->priv;
+ void __iomem *bar = c->bar;
+ int i,
+ j,
+ k;
+ u8 i2cCtlByte,
+ i2cFifoWSz;
+
+ if ((readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F) != 0x10 || readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F00)
+ return false;
+ writel(0, bar + PXQ3PE_I2C_CTL_STAT);
+ switch (mode) {
+ case PXQ3PE_MODE_GPIO:
+ i2cCtlByte = 0xC0;
+ break;
+ case PXQ3PE_MODE_TUNER:
+ slvadr = 2 * slvadr + 0x20;
+ regadr = 0;
+ i2cCtlByte = 0x80;
+ break;
+ case PXQ3PE_MODE_STAT:
+ slvadr = 2 * slvadr + 0x20;
+ regadr = 0;
+ i2cCtlByte = 0x84;
+ break;
+ default:
+ return false;
+ }
+ writel((slvadr << 8) + regadr, bar + PXQ3PE_I2C_ADR);
+ for (i = 0; i < 16 && i < bytelen; i += 4) {
+ udelay(10);
+ writel(*((u32 *)(wdat + i)), bar + PXQ3PE_I2C_FIFO_DATA);
+ }
+ writew((bytelen << 8) + i2cCtlByte, bar + PXQ3PE_I2C_CTL_STAT);
+ for (j = 0; j < PXQ3PE_MAX_LOOP; j++) {
+ if (i < bytelen) {
+ i2cFifoWSz = readb(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F;
+ for (k = 0; bytelen > 16 && k < PXQ3PE_MAX_LOOP && i2cFifoWSz < bytelen - 16; k++) {
+ i2cFifoWSz = readb(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F;
+ udelay(10);
+ }
+ if (i2cFifoWSz & 3)
+ continue;
+ if (i2cFifoWSz) {
+ for (k = i; k < bytelen && k - i < i2cFifoWSz; k += 4)
+ writel(*((u32 *)(wdat + k)), bar + PXQ3PE_I2C_FIFO_DATA);
+ i = k;
+ }
+ }
+ udelay(10);
+ if (readl(bar + PXQ3PE_I2C_CTL_STAT) & 0x400000)
+ break;
+ }
+ return j < PXQ3PE_MAX_LOOP ? !(readl(bar + PXQ3PE_I2C_CTL_STAT) & 0x280000) : false;
+}
+
+bool pxq3pe_r(struct ptx_card *card, u8 slvadr, u8 regadr, u8 *rdat, u8 bytelen, u8 mode)
+{
+ struct pxq3pe_card *c = card->priv;
+ void __iomem *bar = c->bar;
+ u8 i2cCtlByte,
+ i2cStat,
+ i2cFifoRSz,
+ i2cByteCnt;
+ int i = 0,
+ j,
+ idx;
+ bool ret = false;
+
+ if ((readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F) != 0x10 || readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F00)
+ return false;
+ writel(0, bar + PXQ3PE_I2C_CTL_STAT);
+ switch (mode) {
+ case PXQ3PE_MODE_GPIO:
+ i2cCtlByte = 0xE0;
+ break;
+ case PXQ3PE_MODE_TUNER:
+ slvadr = 2 * slvadr + 0x20;
+ regadr = 0;
+ i2cCtlByte = 0xA0;
+ break;
+ default:
+ return false;
+ }
+ writel((slvadr << 8) + regadr, bar + PXQ3PE_I2C_ADR);
+ writew(i2cCtlByte + (bytelen << 8), bar + PXQ3PE_I2C_CTL_STAT);
+ i2cByteCnt = bytelen;
+ j = 0;
+ while (j < PXQ3PE_MAX_LOOP) {
+ udelay(10);
+ i2cStat = (readl(bar + PXQ3PE_I2C_CTL_STAT) & 0xFF0000) >> 16;
+ if (i2cStat & 0x80) {
+ if (i2cStat & 0x28)
+ break;
+ ret = true;
+ }
+ i2cFifoRSz = (readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F00) >> 8;
+ if (i2cFifoRSz & 3) {
+ ++j;
+ continue;
+ }
+ for (idx = i; i2cFifoRSz && idx < i2cByteCnt && idx - i < i2cFifoRSz; idx += 4)
+ *(u32 *)(rdat + idx) = readl(bar + PXQ3PE_I2C_FIFO_DATA);
+ i = idx;
+ if (i < bytelen) {
+ if (i2cFifoRSz)
+ i2cByteCnt -= i2cFifoRSz;
+ else
+ ++j;
+ continue;
+ }
+ i2cStat = (readl(bar + PXQ3PE_I2C_CTL_STAT) & 0xFF0000) >> 16;
+ if (i2cStat & 0x80) {
+ if (i2cStat & 0x28)
+ break;
+ ret = true;
+ break;
+ }
+ ++j;
+ }
+ return !(readl(bar + PXQ3PE_I2C_FIFO_STAT) & 0x1F00) && ret;
+}
+
+int pxq3pe_xfr(struct i2c_adapter *i2c, struct i2c_msg *msg, int sz)
+{
+ struct ptx_card *card = i2c_get_adapdata(i2c);
+ u8 i;
+ bool ret = true;
+
+ if (!i2c || !card || !msg)
+ return -EINVAL;
+ for (i = 0; i < sz && ret; i++, msg++) {
+ u8 slvadr = msg->addr,
+ regadr = msg->len ? *msg->buf : 0,
+ mode = slvadr == PXQ3PE_I2C_ADR_GPIO ? PXQ3PE_MODE_GPIO
+ : sz > 1 && i == sz - 2 ? PXQ3PE_MODE_STAT
+ : PXQ3PE_MODE_TUNER;
+
+ mutex_lock(&card->lock);
+ if (msg->flags & I2C_M_RD) {
+ u8 buf[sz];
+
+ ret = pxq3pe_r(card, slvadr, regadr, buf, msg->len, mode);
+ memcpy(msg->buf, buf, msg->len);
+ } else
+ ret = pxq3pe_w(card, slvadr, regadr, msg->buf, msg->len, mode);
+ mutex_unlock(&card->lock);
+ }
+ return i;
+}
+
+bool pxq3pe_w_gpio2(struct ptx_card *card, u8 dat, u8 mask)
+{
+ u8 val;
+
+ return pxq3pe_r(card, PXQ3PE_I2C_ADR_GPIO, 0xB, &val, 1, PXQ3PE_MODE_GPIO) &&
+ (val = (mask & dat) | (val & ~mask), pxq3pe_w(card, PXQ3PE_I2C_ADR_GPIO, 0xB, &val, 1, PXQ3PE_MODE_GPIO));
+}
+
+void pxq3pe_w_gpio1(struct ptx_card *card, u8 val, u8 mask)
+{
+ struct pxq3pe_card *c = card->priv;
+
+ mask <<= 3;
+ writeb((readb(c->bar + 0x890) & ~mask) | ((val << 3) & mask), c->bar + 0x890);
+}
+
+void pxq3pe_w_gpio0(struct ptx_card *card, u8 val, u8 mask)
+{
+ struct pxq3pe_card *c = card->priv;
+
+ writeb((-(mask & 1) & 4 & -(val & 1)) | (readb(c->bar + 0x890) & ~(-(mask & 1) & 4)), c->bar + 0x890);
+ writeb((mask & val) | (readb(c->bar + 0x894) & ~mask), c->bar + 0x894);
+}
+
+void pxq3pe_power(struct ptx_card *card, bool ON)
+{
+ if (ON) {
+ pxq3pe_w_gpio0(card, 1, 1);
+ pxq3pe_w_gpio0(card, 0, 1);
+ pxq3pe_w_gpio0(card, 1, 1);
+ pxq3pe_w_gpio1(card, 1, 1);
+ pxq3pe_w_gpio1(card, 0, 1);
+ pxq3pe_w_gpio2(card, 2, 2);
+ pxq3pe_w_gpio2(card, 0, 2);
+ pxq3pe_w_gpio2(card, 2, 2);
+ pxq3pe_w_gpio2(card, 4, 4);
+ pxq3pe_w_gpio2(card, 0, 4);
+ pxq3pe_w_gpio2(card, 4, 4);
+ } else {
+ pxq3pe_w_gpio0(card, 0, 1);
+ pxq3pe_w_gpio0(card, 1, 1);
+ pxq3pe_w_gpio1(card, 1, 1);
+ }
+}
+
+irqreturn_t pxq3pe_irq(int irq, void *ctx)
+{
+ struct ptx_card *card = ctx;
+ struct pxq3pe_card *c = card->priv;
+ void __iomem *bar = c->bar;
+ u32 dmamgmt,
+ i,
+ intstat = readl(bar + PXQ3PE_IRQ_STAT);
+ bool ch = intstat & 0b0101 ? 0 : 1,
+ port = intstat & 0b0011 ? 0 : 1;
+ u8 *tbuf = c->dma.dat + PKT_BUFSZ * (port * 2 + ch);
+
+ void pxq3pe_dma_put_stream(struct pxq3pe_adap *p)
+ {
+ u8 *src = p->tBuf;
+ u32 len = p->tBufIdx,
+ savesz = len <= p->sBufSize - p->sBufStop ? len : p->sBufSize - p->sBufStop,
+ remain = len - savesz;
+
+ memcpy(&p->sBuf[p->sBufStop], src, savesz);
+ if (remain)
+ memcpy(p->sBuf, &src[savesz], remain);
+ p->sBufStop = (p->sBufStop + len) % p->sBufSize;
+ if (p->sBufByteCnt == p->sBufSize)
+ p->sBufStart = p->sBufStop;
+ else {
+ if (p->sBufSize >= p->sBufByteCnt + len)
+ p->sBufByteCnt += len;
+ else {
+ p->sBufStart = p->sBufStop;
+ p->sBufByteCnt = p->sBufSize;
+ }
+ }
+ }
+
+ if (!(intstat & 0b1111))
+ return IRQ_HANDLED;
+ writel(intstat, bar + PXQ3PE_IRQ_CLEAR);
+ dmamgmt = readl(bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT);
+ if ((readl(bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_OFFSET_CH * ch + PXQ3PE_DMA_XFR_STAT) & 0x3FFFFF) == PKT_BUFSZ)
+ for (i = 0; i < PKT_BUFSZ; i += PKT_BYTES) {
+ u8 i2cadr = !port * 4 + (tbuf[i] == 0xC7 ? 0 : tbuf[i] == 0x47 ?
+ 1 : tbuf[i] == 0x07 ? 2 : tbuf[i] == 0x87 ? 3 : card->adapn);
+ struct ptx_adap *adap = &card->adap[i2cadr];
+ struct pxq3pe_adap *p = adap->priv;
+
+ if (i2cadr < card->adapn && adap->ON) {
+ tbuf[i] = PTX_TS_SYNC;
+ memcpy(&p->tBuf[p->tBufIdx], &tbuf[i], PKT_BYTES);
+ p->tBufIdx += PKT_BYTES;
+ if (p->tBufIdx >= PKT_BUFSZ) {
+ pxq3pe_dma_put_stream(p);
+ p->tBufIdx = 0;
+ }
+ }
+ }
+ if (c->dma.ON[port])
+ writel(dmamgmt | (2 << (ch * 16)), bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT);
+ return IRQ_HANDLED;
+}
+
+int pxq3pe_thread(void *dat)
+{
+ struct ptx_adap *adap = dat;
+ struct pxq3pe_adap *p = adap->priv;
+
+ set_freezable();
+ while (!kthread_should_stop()) {
+ u8 *rbuf = &p->sBuf[p->sBufStart];
+ int i = 0,
+ j = 0,
+ k,
+ sz = p->sBufSize - p->sBufStart;
+
+ try_to_freeze();
+ if (!p->sBufByteCnt) {
+ msleep_interruptible(0);
+ continue;
+ }
+ if (sz > p->sBufByteCnt)
+ sz = p->sBufByteCnt;
+ while (j < sz / PKT_BYTES) {
+ j++;
+ i += 4;
+ while (i < j * PKT_BYTES)
+ for (k = 0; k < 8; k++, i++)
+ rbuf[i] ^= xor[idx[k]];
+ }
+ dvb_dmx_swfilter_raw(&adap->demux, rbuf, sz);
+ p->sBufStart = (p->sBufStart + sz) % p->sBufSize;
+ p->sBufByteCnt -= sz;
+ }
+ return 0;
+}
+
+void pxq3pe_dma_stop(struct ptx_adap *adap)
+{
+ struct ptx_card *card = adap->card;
+ struct pxq3pe_card *c = card->priv;
+ u8 i2cadr = adap->fe.id,
+ i;
+ bool port = !(i2cadr & 4);
+
+ for (i = 0; i < card->adapn; i++)
+ if (!c->dma.ON[port] || (i2cadr != i && (i & 4) == (i2cadr & 4) && c->dma.ON[port]))
+ return;
+
+ i = readb(c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT);
+ if ((i & 0b1100) == 4)
+ writeb(i & 0xFD, c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT);
+ writeb(0b0011 << (port * 2), c->bar + PXQ3PE_IRQ_DISABLE);
+ c->dma.ON[port] = false;
+}
+
+bool pxq3pe_dma_start(struct ptx_adap *adap)
+{
+ struct ptx_card *card = adap->card;
+ struct pxq3pe_card *c = card->priv;
+ struct pxq3pe_adap *p = adap->priv;
+ u8 i2cadr = adap->fe.id,
+ i;
+ bool port = !(i2cadr & 4);
+ u32 val = 0b0011 << (port * 2);
+
+ p->sBufByteCnt = 0;
+ p->sBufStop = 0;
+ p->sBufStart = 0;
+ if (c->dma.ON[port])
+ return true;
+
+ /* SetTSMode */
+ i = readb(c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_TSMODE);
+ if ((i & 0x80) == 0)
+ writeb(i | 0x80, c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_TSMODE);
+
+ /* irq_enable */
+ writel(val, c->bar + PXQ3PE_IRQ_ENABLE);
+ if (val != (readl(c->bar + PXQ3PE_IRQ_ACTIVE) & val))
+ return false;
+
+ /* cfg_dma */
+ for (i = 0; i < 2; i++) {
+ val = readl(c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT);
+ writel(c->dma.adr + PKT_BUFSZ * (port * 2 + i),
+ c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_OFFSET_CH * i + PXQ3PE_DMA_ADR_LO);
+ writel(0, c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_OFFSET_CH * i + PXQ3PE_DMA_ADR_HI);
+ writel(0x11C0E520, c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_OFFSET_CH * i + PXQ3PE_DMA_CTL);
+ writel(val | 3 << (i * 16),
+ c->bar + PXQ3PE_DMA_OFFSET_PORT * port + PXQ3PE_DMA_MGMT);
+ }
+ c->dma.ON[port] = true;
+ return true;
+}
+
+int pxq3pe_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct ptx_adap *adap = container_of(feed->demux, struct ptx_adap, demux);
+
+ pxq3pe_dma_stop(adap);
+ kthread_stop(adap->kthread);
+ return 0;
+}
+
+int pxq3pe_start_feed(struct dvb_demux_feed *feed)
+{
+ struct ptx_adap *adap = container_of(feed->demux, struct ptx_adap, demux);
+
+ if (!pxq3pe_dma_start(adap))
+ return -EIO;
+ adap->kthread = kthread_run(pxq3pe_thread, adap, "%s_%d", adap->card->name, adap->fe.id);
+ return IS_ERR(adap->kthread) ? PTR_ERR(adap->kthread) : 0;
+}
+
+void pxq3pe_lnb(struct ptx_card *card, bool lnb)
+{
+ pxq3pe_w_gpio2(card, lnb ? 0x20 : 0, 0x20);
+}
+
+void pxq3pe_remove(struct pci_dev *pdev)
+{
+ struct ptx_card *card = pci_get_drvdata(pdev);
+ struct pxq3pe_card *c = card->priv;
+ u8 regctl = 0,
+ i;
+
+ if (!card)
+ return;
+ for (i = 0; i < card->adapn; i++) {
+ struct ptx_adap *adap = &card->adap[i];
+
+ pxq3pe_dma_stop(adap);
+ ptx_sleep(&adap->fe);
+ }
+ pxq3pe_w(card, PXQ3PE_I2C_ADR_GPIO, 0x80, ®ctl, 1, PXQ3PE_MODE_GPIO);
+ pxq3pe_power(card, false);
+
+ /* dma_hw_unmap */
+ free_irq(pdev->irq, card);
+ if (c->dma.dat && dma_ops->free)
+ dma_ops->free(&pdev->dev, c->dma.sz, c->dma.dat, c->dma.adr, NULL);
+ for (i = 0; i < card->adapn; i++) {
+ struct ptx_adap *adap = &card->adap[i];
+ struct pxq3pe_adap *p = adap->priv;
+
+ vfree(p->sBuf);
+ }
+ if (c->bar)
+ pci_iounmap(pdev, c->bar);
+ ptx_unregister_adap_fe(card);
+}
+
+static const struct i2c_algorithm pxq3pe_algo = {
+ .functionality = ptx_i2c_func,
+ .master_xfer = pxq3pe_xfr,
+};
+
+static int pxq3pe_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+{
+ struct ptx_subdev_info pxq3pe_subdev_info[] = {
+ {SYS_ISDBT, 0x10, TC90522_MODNAME, 0x20, NM131_MODNAME},
+ {SYS_ISDBS, 0x11, TC90522_MODNAME, 0x21, TDA2014X_MODNAME},
+ {SYS_ISDBT, 0x12, TC90522_MODNAME, 0x22, NM131_MODNAME},
+ {SYS_ISDBS, 0x13, TC90522_MODNAME, 0x23, TDA2014X_MODNAME},
+ {SYS_ISDBT, 0x14, TC90522_MODNAME, 0x24, NM131_MODNAME},
+ {SYS_ISDBS, 0x15, TC90522_MODNAME, 0x25, TDA2014X_MODNAME},
+ {SYS_ISDBT, 0x16, TC90522_MODNAME, 0x26, NM131_MODNAME},
+ {SYS_ISDBS, 0x17, TC90522_MODNAME, 0x27, TDA2014X_MODNAME},
+ };
+ struct ptx_card *card = ptx_alloc(pdev, KBUILD_MODNAME, ARRAY_SIZE(pxq3pe_subdev_info),
+ sizeof(struct pxq3pe_card), sizeof(struct pxq3pe_adap), pxq3pe_lnb);
+ struct pxq3pe_card *c = card->priv;
+ struct device *dev = &pdev->dev;
+ u8 regctl = 0xA0,
+ i;
+ u16 cfg;
+ int err = !card || pci_read_config_word(pdev, PCI_COMMAND, &cfg);
+
+ if (err)
+ return ptx_abort(pdev, pxq3pe_remove, err, "Memory/PCI/DMA error, card=%p", card);
+ if (!(cfg & PCI_COMMAND_MASTER)) {
+ pci_set_master(pdev);
+ pci_read_config_word(pdev, PCI_COMMAND, &cfg);
+ if (!(cfg & PCI_COMMAND_MASTER))
+ return ptx_abort(pdev, pxq3pe_remove, -EIO, "Bus Mastering is disabled");
+ }
+ c->bar = pci_iomap(pdev, 0, 0);
+ if (!c->bar)
+ return ptx_abort(pdev, pxq3pe_remove, -EIO, "I/O map failed");
+ if (ptx_i2c_add_adapter(card, &pxq3pe_algo))
+ return ptx_abort(pdev, pxq3pe_remove, -EIO, "Cannot add I2C");
+
+ for (i = 0; i < card->adapn; i++) {
+ struct ptx_adap *adap = &card->adap[i];
+ struct pxq3pe_adap *p = adap->priv;
+
+ adap->fe.id = i;
+ p->sBufSize = PKT_BYTES * 100 << 9;
+ p->sBuf = vzalloc(p->sBufSize);
+ if (!p->sBuf)
+ return ptx_abort(pdev, pxq3pe_remove, -ENOMEM, "No memory for stream buffer");
+ }
+
+ /* dma_map */
+ c->dma.sz = PKT_BUFSZ * 4;
+ if (request_irq(pdev->irq, pxq3pe_irq, IRQF_SHARED, KBUILD_MODNAME, card))
+ return ptx_abort(pdev, pxq3pe_remove, -EIO, "IRQ failed");
+ if (dev->dma_mask && *dev->dma_mask && dma_ops->alloc) {
+ u32 gfp = dev->coherent_dma_mask ? 0x21 - (dev->coherent_dma_mask >= 0x1000000) : 0x20;
+
+ if ((!dev->coherent_dma_mask || dev->coherent_dma_mask <= 0xFFFFFFFF) && !(gfp & 1))
+ gfp |= 4;
+ c->dma.dat = dma_ops->alloc(dev, c->dma.sz, &c->dma.adr, gfp, NULL);
+ }
+ if (!c->dma.dat)
+ return ptx_abort(pdev, pxq3pe_remove, -EIO, "DMA mapping failed");
+
+ /* hw_init */
+ writeb(readb(c->bar + 0x880) & 0xC0, c->bar + 0x880);
+ writel(0x3200C8, c->bar + 0x904);
+ writel(0x90, c->bar + 0x900);
+ writel(0x10000, c->bar + 0x880);
+ writel(0x0080, c->bar + PXQ3PE_DMA_TSMODE);
+ writel(0x0080, c->bar + PXQ3PE_DMA_TSMODE + PXQ3PE_DMA_OFFSET_PORT);
+ writel(0x0000, c->bar + 0x888);
+ writel(0x00CF, c->bar + 0x894);
+ writel(0x8000, c->bar + 0x88C);
+ writel(0x1004, c->bar + 0x890);
+ writel(0x0090, c->bar + 0x900);
+ writel(0x3200C8, c->bar + 0x904);
+ pxq3pe_w_gpio0(card, 8, 0xFF);
+ pxq3pe_w_gpio1(card, 0, 2);
+ pxq3pe_w_gpio1(card, 1, 1);
+ pxq3pe_w_gpio0(card, 1, 1);
+ pxq3pe_w_gpio0(card, 0, 1);
+ pxq3pe_w_gpio0(card, 1, 1);
+ for (i = 0; i < 16; i++)
+ if (!pxq3pe_w(card, PXQ3PE_I2C_ADR_GPIO, 0x10 + i, auth + i, 1, PXQ3PE_MODE_GPIO))
+ break;
+ if (i < 16 || !pxq3pe_w(card, PXQ3PE_I2C_ADR_GPIO, 5, ®ctl, 1, PXQ3PE_MODE_GPIO))
+ return ptx_abort(pdev, pxq3pe_remove, -EIO, "hw_init failed");
+ pxq3pe_power(card, true);
+ err = ptx_register_adap_fe(card, pxq3pe_subdev_info, pxq3pe_start_feed, pxq3pe_stop_feed);
+ return err ? ptx_abort(pdev, pxq3pe_remove, err, "Unable to register DVB adapter & frontend") : 0;
+}
+
+static struct pci_driver pxq3pe_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = pxq3pe_id_table,
+ .probe = pxq3pe_probe,
+ .remove = pxq3pe_remove,
+};
+module_pci_driver(pxq3pe_driver);
+
--
2.3.10
^ permalink raw reply related [flat|nested] 39+ messages in thread* Re: [media 7/7] PCI bridge driver for PT3 & PXQ3PE
2016-02-15 6:08 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE info
@ 2016-02-15 6:23 ` kbuild test robot
2016-02-15 6:47 ` kbuild test robot
` (2 subsequent siblings)
3 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2016-02-15 6:23 UTC (permalink / raw)
To: info
Cc: kbuild-all, linux-media,
Буди Романто, AreMa Inc,
linux-kernel, crope, m.chehab, mchehab, hdegoede,
laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski,
peter.senna
[-- Attachment #1: Type: text/plain, Size: 7081 bytes --]
Hi Буди,
[auto build test ERROR on linuxtv-media/master]
[cannot apply to v4.5-rc4 next-20160212]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/info-are-ma/Driver-bundle-for-PT3-PX-Q3PE/20160215-141501
base: git://linuxtv.org/media_tree.git master
config: x86_64-randconfig-x006-201607 (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All errors (new ones prefixed by >>):
>> drivers/media/dvb-frontends/tc90522.c:23:2: error: unknown type name 'fe_status_t'
fe_status_t stat;
^
drivers/media/dvb-frontends/tc90522.c:71:45: error: unknown type name 'fe_status_t'
int tc90522_status(struct dvb_frontend *fe, fe_status_t *stat)
^
drivers/media/dvb-frontends/tc90522.c:118:84: error: unknown type name 'fe_status_t'
int tc90522_tune(struct dvb_frontend *fe, bool retune, u32 mode_flags, u32 *delay, fe_status_t *stat)
^
>> drivers/media/dvb-frontends/tc90522.c:238:17: error: 'tc90522_status' undeclared here (not in a function)
.read_status = tc90522_status,
^
>> drivers/media/dvb-frontends/tc90522.c:239:11: error: 'tc90522_tune' undeclared here (not in a function)
.tune = tc90522_tune,
^
vim +/fe_status_t +23 drivers/media/dvb-frontends/tc90522.c
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 17 #include "dvb_math.h"
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 18 #include "dvb_frontend.h"
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 19 #include "tc90522.h"
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 20
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 21 struct tc90522 {
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 22 struct i2c_adapter *i2c;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 @23 fe_status_t stat;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 24 };
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 25
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 26 bool tc90522_r(struct dvb_frontend *fe, u8 slvadr, u8 *buf, u8 len)
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 27 {
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 28 struct tc90522 *d = fe->demodulator_priv;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 29 struct i2c_msg msg[] = {
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 30 {.addr = fe->id, .flags = 0, .buf = &slvadr, .len = 1,},
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 31 {.addr = fe->id, .flags = I2C_M_RD, .buf = buf, .len = len,},
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 32 };
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 33 return i2c_transfer(d->i2c, msg, 2) == 2;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 34 }
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 35
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 36 bool tc90522_w(struct dvb_frontend *fe, u8 slvadr, u8 dat)
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 37 {
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 38 struct tc90522 *d = fe->demodulator_priv;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 39 u8 buf[] = {slvadr, dat};
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 40 struct i2c_msg msg[] = {
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 41 {.addr = fe->id, .flags = 0, .buf = buf, .len = 2,},
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 42 };
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 43 return i2c_transfer(d->i2c, msg, 1) == 1;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 44 }
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 45
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 46 u64 tc90522_n2int(const u8 *data, u8 n) /* convert n_bytes data from stream (network byte order) to integer */
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 47 { /* can't use <arpa/inet.h>'s ntoh*() as sometimes n = 3,5,... */
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 48 u32 i, val = 0;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 49
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 50 for (i = 0; i < n; i++) {
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 51 val <<= 8;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 52 val |= data[i];
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 53 }
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 54 return val;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 55 }
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 56
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 57 int tc90522_cn_raw(struct dvb_frontend *fe, u16 *raw) /* for DVBv3 compatibility */
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 58 {
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 59 u8 buf[3],
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 60 len = fe->dtv_property_cache.delivery_system == SYS_ISDBS ? 2 : 3,
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 61 adr = fe->dtv_property_cache.delivery_system == SYS_ISDBS ? 0xbc : 0x8b;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 62 bool ok = tc90522_r(fe, adr, buf, len);
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 63 int cn = tc90522_n2int(buf, len);
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 64
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 65 if (!ok)
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 66 return -EIO;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 67 *raw = cn;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 68 return cn;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 69 }
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 70
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 @71 int tc90522_status(struct dvb_frontend *fe, fe_status_t *stat)
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 72 {
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 73 struct tc90522 *d = fe->demodulator_priv;
fc7fad5bd Буди Романто, AreMa Inc 2016-02-15 74 struct dtv_frontend_properties *c = &fe->dtv_property_cache;
:::::: The code at line 23 was first introduced by commit
:::::: fc7fad5bdf7260bcb1bb53da404eb436af2269c0 Toshiba TC90522XBG quad demodulator (2ch OFDM + 2ch 8PSK) used by both PT3 & PX-Q3PE
:::::: TO: Буди Романто, AreMa Inc <knightrider@are.ma>
:::::: CC: 0day robot <fengguang.wu@intel.com>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 23937 bytes --]
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [media 7/7] PCI bridge driver for PT3 & PXQ3PE
2016-02-15 6:08 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE info
2016-02-15 6:23 ` kbuild test robot
@ 2016-02-15 6:47 ` kbuild test robot
2016-02-15 7:34 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot
2016-02-15 7:34 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE kbuild test robot
3 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2016-02-15 6:47 UTC (permalink / raw)
To: info
Cc: kbuild-all, linux-media,
Буди Романто, AreMa Inc,
linux-kernel, crope, m.chehab, mchehab, hdegoede,
laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski,
peter.senna
[-- Attachment #1: Type: text/plain, Size: 3296 bytes --]
Hi Буди,
[auto build test WARNING on linuxtv-media/master]
[cannot apply to v4.5-rc4 next-20160212]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/info-are-ma/Driver-bundle-for-PT3-PX-Q3PE/20160215-141501
base: git://linuxtv.org/media_tree.git master
config: s390-allyesconfig (attached as .config)
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=s390
All warnings (new ones prefixed by >>):
drivers/media/tuners/mxl301rf.c: In function 'mxl301rf_w':
>> drivers/media/tuners/mxl301rf.c:31:1: warning: 'mxl301rf_w' uses dynamic stack allocation
}
^
drivers/media/tuners/mxl301rf.c: In function 'mxl301rf_w_tuner':
>> drivers/media/tuners/mxl301rf.c:40:1: warning: 'mxl301rf_w_tuner' uses dynamic stack allocation
}
^
--
drivers/media/tuners/qm1d1c0042.c: In function 'qm1d1c0042_w':
>> drivers/media/tuners/qm1d1c0042.c:46:1: warning: 'qm1d1c0042_w' uses dynamic stack allocation
}
^
vim +/mxl301rf_w +31 drivers/media/tuners/mxl301rf.c
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 25 {.addr = fe->id, .flags = 0, .buf = buf, .len = len + 1,},
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 26 };
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 27
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 28 buf[0] = slvadr;
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 29 memcpy(buf + 1, dat, len);
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 30 return i2c_transfer(t->i2c, msg, 1) == 1 ? 0 : -EIO;
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 @31 }
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 32
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 33 int mxl301rf_w_tuner(struct dvb_frontend *fe, const u8 *dat, int len)
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 34 {
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 35 u8 buf[len + 1];
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 36
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 37 buf[0] = ((struct mxl301rf *)fe->tuner_priv)->adr_tuner << 1;
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 38 memcpy(buf + 1, dat, len);
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 39 return mxl301rf_w(fe, 0xFE, buf, len + 1);
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 @40 }
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 41
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 42 void mxl301rf_r(struct dvb_frontend *fe, u8 regadr, u8 *dat)
3f15bcc3 Буди Романто, AreMa Inc 2016-02-15 43 {
:::::: The code at line 31 was first introduced by commit
:::::: 3f15bcc3ee23377c702f9635ca3e5908d147eb80 MaxLinear MxL301RF ISDB-T tuner
:::::: TO: Буди Романто, AreMa Inc <knightrider@are.ma>
:::::: CC: 0day robot <fengguang.wu@intel.com>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 40160 bytes --]
^ permalink raw reply [flat|nested] 39+ messages in thread* [PATCH] fix platform_no_drv_owner.cocci warnings
2016-02-15 6:08 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE info
2016-02-15 6:23 ` kbuild test robot
2016-02-15 6:47 ` kbuild test robot
@ 2016-02-15 7:34 ` kbuild test robot
2016-02-15 7:34 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE kbuild test robot
3 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2016-02-15 7:34 UTC (permalink / raw)
To: info
Cc: kbuild-all, linux-media,
Буди Романто, AreMa Inc,
linux-kernel, crope, m.chehab, mchehab, hdegoede,
laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski,
peter.senna
drivers/media/dvb-frontends/tc90522.c:271:3-8: No need to set .owner here. The core will do it.
Remove .owner field if calls are used which set it automatically
Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci
CC: Буди Романто, AreMa Inc <knightrider@are.ma>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
tc90522.c | 1 -
1 file changed, 1 deletion(-)
--- a/drivers/media/dvb-frontends/tc90522.c
+++ b/drivers/media/dvb-frontends/tc90522.c
@@ -268,7 +268,6 @@ MODULE_DEVICE_TABLE(i2c, tc90522_id);
static struct i2c_driver tc90522_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = tc90522_id->name,
},
.probe = tc90522_probe,
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [media 7/7] PCI bridge driver for PT3 & PXQ3PE
2016-02-15 6:08 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE info
` (2 preceding siblings ...)
2016-02-15 7:34 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot
@ 2016-02-15 7:34 ` kbuild test robot
3 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2016-02-15 7:34 UTC (permalink / raw)
To: info
Cc: kbuild-all, linux-media,
Буди Романто, AreMa Inc,
linux-kernel, crope, m.chehab, mchehab, hdegoede,
laurent.pinchart, mkrufky, sylvester.nawrocki, g.liakhovetski,
peter.senna
[-- Attachment #1: Type: text/plain, Size: 3131 bytes --]
Hi Буди,
[auto build test ERROR on linuxtv-media/master]
[cannot apply to v4.5-rc4 next-20160212]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
url: https://github.com/0day-ci/linux/commits/info-are-ma/Driver-bundle-for-PT3-PX-Q3PE/20160215-141501
base: git://linuxtv.org/media_tree.git master
config: i386-allmodconfig (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All error/warnings (new ones prefixed by >>):
In file included from drivers/media/pci/ptx/pt3_pci.c:19:0:
>> drivers/media/pci/ptx/ptx_common.h:24:2: error: unknown type name 'fe_delivery_system_t'
fe_delivery_system_t type;
^
--
In file included from drivers/media/pci/ptx/ptx_common.c:9:0:
>> drivers/media/pci/ptx/ptx_common.h:24:2: error: unknown type name 'fe_delivery_system_t'
fe_delivery_system_t type;
^
drivers/media/pci/ptx/ptx_common.c: In function 'ptx_abort':
>> drivers/media/pci/ptx/ptx_common.c:199:6: error: implicit declaration of function 'vzalloc' [-Werror=implicit-function-declaration]
s = vzalloc(slen);
^
>> drivers/media/pci/ptx/ptx_common.c:199:4: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
s = vzalloc(slen);
^
>> drivers/media/pci/ptx/ptx_common.c:203:3: error: implicit declaration of function 'vfree' [-Werror=implicit-function-declaration]
vfree(s);
^
cc1: some warnings being treated as errors
--
In file included from drivers/media/pci/ptx/pxq3pe_pci.c:18:0:
>> drivers/media/pci/ptx/ptx_common.h:24:2: error: unknown type name 'fe_delivery_system_t'
fe_delivery_system_t type;
^
drivers/media/pci/ptx/pxq3pe_pci.c: In function 'pxq3pe_remove':
>> drivers/media/pci/ptx/pxq3pe_pci.c:499:3: error: implicit declaration of function 'vfree' [-Werror=implicit-function-declaration]
vfree(p->sBuf);
^
drivers/media/pci/ptx/pxq3pe_pci.c: In function 'pxq3pe_probe':
>> drivers/media/pci/ptx/pxq3pe_pci.c:552:14: error: implicit declaration of function 'vzalloc' [-Werror=implicit-function-declaration]
p->sBuf = vzalloc(p->sBufSize);
^
>> drivers/media/pci/ptx/pxq3pe_pci.c:552:12: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
p->sBuf = vzalloc(p->sBufSize);
^
cc1: some warnings being treated as errors
coccinelle warnings: (new ones prefixed by >>)
>> drivers/media/dvb-frontends/tc90522.c:271:3-8: No need to set .owner here. The core will do it.
Please review and possibly fold the followup patch.
vim +/fe_delivery_system_t +24 drivers/media/pci/ptx/ptx_common.h
18 enum ePTX {
19 PTX_TS_SYNC = 0x47,
20 PTX_TS_NOT_SYNC = 0x74,
21 };
22
23 struct ptx_subdev_info {
> 24 fe_delivery_system_t type;
25 u8 demod_addr, *demod_name,
26 tuner_addr, *tuner_name;
27 };
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 53467 bytes --]
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH] ALSA SoC MAX98927 driver - Initial release
@ 2016-11-23 4:57 ` Ryan Lee
2016-11-23 18:13 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot
0 siblings, 1 reply; 39+ messages in thread
From: Ryan Lee @ 2016-11-23 4:57 UTC (permalink / raw)
To: lgirdwood, broonie, robh+dt, mark.rutland, perex, tiwai, arnd,
michael, oder_chiou, yesanishhere, jacob, Damien.Horsley,
bardliao, kuninori.morimoto.gx, petr, lars, nh6z, ryans.lee,
alsa-devel, devicetree, linux-kernel
Cc: Ryan Lee
Signed-off-by: Ryan Lee <ryans.lee@maximintegrated.com>
---
.../devicetree/bindings/sound/max98927.txt | 32 +
sound/soc/codecs/Kconfig | 5 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/max98927.c | 954 +++++++++++++++
sound/soc/codecs/max98927.h | 1253 ++++++++++++++++++++
5 files changed, 2246 insertions(+)
create mode 100755 Documentation/devicetree/bindings/sound/max98927.txt
mode change 100644 => 100755 sound/soc/codecs/Kconfig
mode change 100644 => 100755 sound/soc/codecs/Makefile
create mode 100755 sound/soc/codecs/max98927.c
create mode 100755 sound/soc/codecs/max98927.h
diff --git a/Documentation/devicetree/bindings/sound/max98927.txt b/Documentation/devicetree/bindings/sound/max98927.txt
new file mode 100755
index 0000000..ddcd332
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/max98927.txt
@@ -0,0 +1,32 @@
+max98927 audio CODEC
+
+This device supports I2C.
+
+Required properties:
+
+ - compatible : "maxim,max98927"
+
+ - vmon-slot-no : slot number used to send voltage information
+ or in inteleave mode this will be used as
+ interleave slot.
+
+ - imon-slot-no : slot number used to send current information
+
+ - interleave-mode : When using two MAX98927 in a system it is
+ possible to create ADC data that that will
+ overflow the frame size. Digital Audio Interleave
+ mode provides a means to output VMON and IMON data
+ from two devices on a single DOUT line when running
+ smaller frames sizes such as 32 BCLKS per LRCLK or
+ 48 BCLKS per LRCLK.
+
+ - reg : the I2C address of the device for I2C
+
+Example:
+
+codec: max98927@3a {
+ compatible = "maxim,max98927";
+ vmon-slot-no = <1>;
+ imon-slot-no = <0>;
+ reg = <0x3a>;
+};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
old mode 100644
new mode 100755
index c67667b..45f21ca
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -86,6 +86,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX9867 if I2C
select SND_SOC_MAX98925 if I2C
select SND_SOC_MAX98926 if I2C
+ select SND_SOC_MAX98927 if I2C
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9860 if I2C
select SND_SOC_MAX9768 if I2C
@@ -573,6 +574,10 @@ config SND_SOC_MAX98925
config SND_SOC_MAX98926
tristate
+config SND_SOC_MAX98927
+ tristate "Maxim Integrated MAX98927 Speaker Amplifier"
+ depends on I2C
+
config SND_SOC_MAX9850
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
old mode 100644
new mode 100755
index 958cd49..1f5fe2c
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -82,6 +82,7 @@ snd-soc-max98371-objs := max98371.o
snd-soc-max9867-objs := max9867.o
snd-soc-max98925-objs := max98925.o
snd-soc-max98926-objs := max98926.o
+snd-soc-max98927-objs := max98927.o
snd-soc-max9850-objs := max9850.o
snd-soc-max9860-objs := max9860.o
snd-soc-mc13783-objs := mc13783.o
@@ -306,6 +307,7 @@ obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o
obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o
obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o
+obj-$(CONFIG_SND_SOC_MAX98927) += snd-soc-max98927.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
new file mode 100755
index 0000000..d85c84f
--- /dev/null
+++ b/sound/soc/codecs/max98927.c
@@ -0,0 +1,954 @@
+/*
+ * max98927.c -- MAX98927 ALSA Soc Audio driver
+ *
+ * Copyright 2013-15 Maxim Integrated Products
+ * Author: Ryan Lee <ryans.lee@maximintegrated.com>
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/tlv.h>
+#include "max98927.h"
+
+static struct reg_default max98927_reg_map[] = {
+ {0x0014, 0x78},
+ {0x0015, 0xFF},
+ {0x0043, 0x04},
+ {0x0017, 0x55},
+ /* For mono driver we are just enabling one channel*/
+ {MAX98927_PCM_Rx_Enables_A, 0x03},
+ {MAX98927_PCM_Tx_HiZ_Control_A, 0xfc},
+ {MAX98927_PCM_Tx_HiZ_Control_B, 0xff},
+ {MAX98927_PCM_Tx_Channel_Sources_A, 0x01},
+ {MAX98927_PCM_Tx_Channel_Sources_B, 0x01},
+ {MAX98927_Measurement_DSP_Config, 0xf7},
+ {0x0025, 0x80},
+ {0x0026, 0x01},
+ {0x0035, 0x40},
+ {0x0036, 0x40},
+ {0x0037, 0x02},
+ {0x0039, 0x01},
+ {0x003c, 0x44},
+ {0x003d, 0x04},
+ {0x0040, 0x10},
+ {0x0042, 0x3f},
+ {0x0044, 0x00},
+ {0x0045, 0x24},
+ {0x007f, 0x06},
+ {0x0087, 0x1c},
+ {0x0089, 0x03},
+ {0x009f, 0x01},
+};
+
+void max98927_wrapper_write(struct max98927_priv *max98927,
+ unsigned int reg, unsigned int val)
+{
+ if (max98927->regmap)
+ regmap_write(max98927->regmap, reg, val);
+ if (max98927->sub_regmap)
+ regmap_write(max98927->sub_regmap, reg, val);
+}
+
+void max98927_wrap_update_bits(struct max98927_priv *max98927,
+ unsigned int reg, unsigned int mask, unsigned int val)
+{
+ if (max98927->regmap)
+ regmap_update_bits(max98927->regmap, reg, mask, val);
+ if (max98927->sub_regmap)
+ regmap_update_bits(max98927->sub_regmap, reg, mask, val);
+}
+
+static int max98927_reg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol, unsigned int reg,
+ unsigned int mask, unsigned int shift)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ int data;
+
+ regmap_read(max98927->regmap, reg, &data);
+ ucontrol->value.integer.value[0] =
+ (data & mask) >> shift;
+ return 0;
+}
+
+static int max98927_reg_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol, unsigned int reg,
+ unsigned int mask, unsigned int shift)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ max98927_wrap_update_bits(max98927, reg, mask, sel << shift);
+ dev_dbg(codec->dev, "%s: register 0x%02X, value 0x%02X\n",
+ __func__, reg, sel);
+ return 0;
+}
+
+static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ unsigned int invert = 0;
+
+ dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode,
+ MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask,
+ MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_SLAVE);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ max98927->master = true;
+ max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode,
+ MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask,
+ MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_MASTER);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode,
+ MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask,
+ MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_HYBRID);
+ default:
+ dev_err(codec->dev, "DAI clock mode unsupported");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98927_PCM_Mode_Config_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(codec->dev, "DAI invert mode unsupported");
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ max98927->iface |= SND_SOC_DAIFMT_I2S;
+ max98927_wrap_update_bits(max98927,
+ MAX98927_PCM_Mode_Config,
+ max98927->iface, max98927->iface);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ max98927->iface |= SND_SOC_DAIFMT_LEFT_J;
+ max98927_wrap_update_bits(max98927,
+ MAX98927_PCM_Mode_Config,
+ max98927->iface, max98927->iface);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* pcm channel configuration */
+ if (max98927->iface & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) {
+ max98927_wrapper_write(max98927,
+ MAX98927_PCM_Rx_Enables_A,
+ MAX98927_PCM_Rx_Enables_A_PCM_RX_CH0_EN|
+ MAX98927_PCM_Rx_Enables_A_PCM_RX_CH1_EN);
+ max98927_wrapper_write(max98927,
+ MAX98927_PCM_Tx_Enables_A,
+ MAX98927_PCM_Tx_Enables_A_PCM_TX_CH0_EN|
+ MAX98927_PCM_Tx_Enables_A_PCM_TX_CH1_EN);
+ }
+ max98927_wrap_update_bits(max98927, MAX98927_PCM_Mode_Config,
+ MAX98927_PCM_Mode_Config_PCM_BCLKEDGE, invert);
+ return 0;
+}
+
+/* codec MCLK rate in master mode */
+static const int rate_table[] = {
+ 5644800, 6000000, 6144000, 6500000,
+ 9600000, 11289600, 12000000, 12288000,
+ 13000000, 19200000,
+};
+
+static int max98927_set_clock(struct max98927_priv *max98927,
+ struct snd_pcm_hw_params *params)
+{
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98927->ch_size;
+ int reg = MAX98927_PCM_Clock_setup;
+ int mask = MAX98927_PCM_Clock_setup_PCM_BSEL_Mask;
+ int value;
+
+ if (max98927->master) {
+ int i;
+ /* match rate to closest value */
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i] >= max98927->sysclk)
+ break;
+ }
+ if (i == ARRAY_SIZE(rate_table)) {
+ pr_err("%s couldn't get the MCLK to match codec\n",
+ __func__);
+ return -EINVAL;
+ }
+ max98927_wrap_update_bits(max98927, MAX98927_PCM_Master_Mode,
+ MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_Mask,
+ i << MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_SHIFT);
+ }
+
+ switch (blr_clk_ratio) {
+ case 32:
+ value = 2;
+ break;
+ case 48:
+ value = 3;
+ break;
+ case 64:
+ value = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ max98927_wrap_update_bits(max98927,
+ reg, mask, value);
+ return 0;
+}
+
+static int max98927_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ int sampling_rate = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ max98927_wrap_update_bits(max98927,
+ MAX98927_PCM_Mode_Config,
+ MAX98927_PCM_Mode_Config_PCM_CHANSZ_16,
+ MAX98927_PCM_Mode_Config_PCM_CHANSZ_16);
+ max98927->ch_size = 16;
+ break;
+ case 24:
+ max98927_wrap_update_bits(max98927,
+ MAX98927_PCM_Mode_Config,
+ MAX98927_PCM_Mode_Config_PCM_CHANSZ_24,
+ MAX98927_PCM_Mode_Config_PCM_CHANSZ_24);
+ max98927->ch_size = 24;
+ break;
+ case 32:
+ max98927_wrap_update_bits(max98927,
+ MAX98927_PCM_Mode_Config,
+ MAX98927_PCM_Mode_Config_PCM_CHANSZ_32,
+ MAX98927_PCM_Mode_Config_PCM_CHANSZ_32);
+ max98927->ch_size = 32;
+ break;
+ default:
+ pr_err("%s: format unsupported %d",
+ __func__, params_format(params));
+ goto err;
+ }
+ dev_dbg(codec->dev, "%s: format supported %d",
+ __func__, params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate |=
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_8000;
+ break;
+ case 11025:
+ sampling_rate |=
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_11025;
+ break;
+ case 12000:
+ sampling_rate |=
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_12000;
+ break;
+ case 16000:
+ sampling_rate |=
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_16000;
+ break;
+ case 22050:
+ sampling_rate |=
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_22050;
+ break;
+ case 24000:
+ sampling_rate |=
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_24000;
+ break;
+ case 32000:
+ sampling_rate |=
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_32000;
+ break;
+ case 44100:
+ sampling_rate |=
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_44100;
+ break;
+ case 48000:
+ sampling_rate |=
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_48000;
+ break;
+ default:
+ pr_err("%s rate %d not supported\n",
+ __func__, params_rate(params));
+ goto err;
+ }
+ /* set DAI_SR to correct LRCLK frequency */
+ max98927_wrap_update_bits(max98927, MAX98927_PCM_Sample_rate_setup_1,
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_Mask, sampling_rate);
+ max98927_wrap_update_bits(max98927, MAX98927_PCM_Sample_rate_setup_2,
+ MAX98927_PCM_Sample_rate_setup_2_SPK_SR_Mask, sampling_rate<<4);
+ max98927_wrap_update_bits(max98927, MAX98927_PCM_Sample_rate_setup_2,
+ MAX98927_PCM_Sample_rate_setup_2_IVADC_SR_Mask, sampling_rate);
+ return max98927_set_clock(max98927, params);
+err:
+ return -EINVAL;
+}
+
+#define MAX98927_RATES SNDRV_PCM_RATE_8000_48000
+
+#define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98927_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+
+ max98927->sysclk = freq;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98927_dai_ops = {
+ .set_sysclk = max98927_dai_set_sysclk,
+ .set_fmt = max98927_dai_set_fmt,
+ .hw_params = max98927_dai_hw_params,
+};
+
+static void max98927_handle_pdata(struct snd_soc_codec *codec)
+{
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ struct max98927_reg_default *regInfo;
+ int cfg_size = 0;
+ int x;
+
+ if (max98927->regcfg != NULL)
+ cfg_size = max98927->regcfg_sz / sizeof(uint32_t);
+
+ if (cfg_size <= 0) {
+ dev_dbg(codec->dev,
+ "Register configuration is not required.\n");
+ return;
+ }
+
+ /* direct configuration from device tree */
+ for (x = 0; x < cfg_size; x += 3) {
+ regInfo = (struct max98927_reg_default *)&max98927->regcfg[x];
+ dev_info(codec->dev, "CH:%d, reg:0x%02x, value:0x%02x\n",
+ be32_to_cpu(regInfo->ch),
+ be32_to_cpu(regInfo->reg),
+ be32_to_cpu(regInfo->def));
+ if (be32_to_cpu(regInfo->ch) == PRI_MAX98927
+ && max98927->regmap)
+ regmap_write(max98927->regmap,
+ be32_to_cpu(regInfo->reg),
+ be32_to_cpu(regInfo->def));
+ else if (be32_to_cpu(regInfo->ch) == SEC_MAX98927
+ && max98927->sub_regmap)
+ regmap_write(max98927->sub_regmap,
+ be32_to_cpu(regInfo->reg),
+ be32_to_cpu(regInfo->def));
+ }
+}
+
+static int max98927_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ max98927_wrap_update_bits(max98927,
+ MAX98927_AMP_enables, 1, 1);
+ /* enable the v and i for vi feedback */
+ max98927_wrap_update_bits(max98927,
+ MAX98927_Measurement_enables,
+ MAX98927_Measurement_enables_IVADC_V_EN,
+ MAX98927_Measurement_enables_IVADC_V_EN);
+ max98927_wrap_update_bits(max98927,
+ MAX98927_Measurement_enables,
+ MAX98927_Measurement_enables_IVADC_I_EN,
+ MAX98927_Measurement_enables_IVADC_I_EN);
+ max98927_wrap_update_bits(max98927,
+ MAX98927_Global_Enable, 1, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ max98927_wrap_update_bits(max98927,
+ MAX98927_Global_Enable, 1, 0);
+ max98927_wrap_update_bits(max98927,
+ MAX98927_AMP_enables, 1, 0);
+ /* disable the v and i for vi feedback */
+ max98927_wrap_update_bits(max98927,
+ MAX98927_Measurement_enables,
+ MAX98927_Measurement_enables_IVADC_V_EN,
+ 0);
+ max98927_wrap_update_bits(max98927,
+ MAX98927_Measurement_enables,
+ MAX98927_Measurement_enables_IVADC_I_EN,
+ 0);
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", MAX98927_AMP_enables,
+ 0, 0, max98927_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static DECLARE_TLV_DB_SCALE(max98927_spk_tlv, 300, 300, 0);
+static DECLARE_TLV_DB_SCALE(max98927_digital_tlv, -1600, 25, 0);
+
+static int max98927_spk_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = max98927->spk_gain;
+ dev_dbg(codec->dev, "%s: spk_gain setting returned %d\n", __func__,
+ (int) ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int max98927_spk_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ if (sel < ((1 << MAX98927_Speaker_Gain_Width) - 1)) {
+ max98927_wrap_update_bits(max98927, MAX98927_Speaker_Gain,
+ MAX98927_Speaker_Gain_SPK_PCM_GAIN_Mask, sel);
+ max98927->spk_gain = sel;
+ }
+ return 0;
+}
+
+static int max98927_digital_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = max98927->digital_gain;
+ dev_dbg(codec->dev, "%s: spk_gain setting returned %d\n", __func__,
+ (int) ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int max98927_digital_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sel = ucontrol->value.integer.value[0];
+
+ if (sel < ((1 << MAX98927_AMP_VOL_WIDTH) - 1)) {
+ max98927_wrap_update_bits(max98927, MAX98927_AMP_volume_control,
+ MAX98927_AMP_volume_control_AMP_VOL_Mask, sel);
+ max98927->digital_gain = sel;
+ }
+ return 0;
+}
+
+static int max98927_boost_voltage_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_get(kcontrol, ucontrol, MAX98927_Boost_Control_0,
+ MAX98927_Boost_Control_0_BST_VOUT_Mask, 0);
+}
+
+static int max98927_boost_voltage_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_put(kcontrol, ucontrol, MAX98927_Boost_Control_0,
+ MAX98927_Boost_Control_0_BST_VOUT_Mask, 0);
+}
+
+static int max98927_amp_vol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_get(kcontrol, ucontrol, MAX98927_Boost_Control_0,
+ MAX98927_Boost_Control_0_BST_VOUT_Mask,
+ MAX98927_AMP_VOL_LOCATION_SHIFT);
+}
+
+static int max98927_amp_dsp_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_put(kcontrol, ucontrol, MAX98927_Brownout_enables,
+ MAX98927_Brownout_enables_AMP_DSP_EN, MAX98927_BDE_DSP_SHIFT);
+}
+
+static int max98927_amp_dsp_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_get(kcontrol, ucontrol, MAX98927_Brownout_enables,
+ MAX98927_Brownout_enables_AMP_DSP_EN, MAX98927_BDE_DSP_SHIFT);
+}
+
+static int max98927_ramp_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_put(kcontrol, ucontrol, MAX98927_AMP_DSP_Config,
+ MAX98927_AMP_DSP_Config_AMP_VOL_RMP_BYPASS,
+ MAX98927_SPK_RMP_EN_SHIFT);
+}
+static int max98927_ramp_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_get(kcontrol, ucontrol, MAX98927_AMP_DSP_Config,
+ MAX98927_AMP_DSP_Config_AMP_VOL_RMP_BYPASS,
+ MAX98927_SPK_RMP_EN_SHIFT);
+}
+
+static int max98927_dre_en_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_put(kcontrol, ucontrol, MAX98927_DRE_Control,
+ MAX98927_DRE_Control_DRE_EN, 0);
+}
+static int max98927_dre_en_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_get(kcontrol, ucontrol, MAX98927_DRE_Control,
+ MAX98927_DRE_Control_DRE_EN, 0);
+}
+static int max98927_amp_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_put(kcontrol, ucontrol,
+ MAX98927_AMP_volume_control,
+ MAX98927_AMP_volume_control_AMP_VOL_SEL,
+ MAX98927_AMP_VOL_LOCATION_SHIFT);
+}
+static int max98927_spk_src_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_get(kcontrol, ucontrol,
+ MAX98927_Speaker_source_select,
+ MAX98927_Speaker_source_select_SPK_SOURCE_Mask, 0);
+}
+
+static int max98927_spk_src_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_put(kcontrol, ucontrol,
+ MAX98927_Speaker_source_select,
+ MAX98927_Speaker_source_select_SPK_SOURCE_Mask, 0);
+}
+
+static int max98927_mono_out_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_get(kcontrol, ucontrol,
+ MAX98927_PCM_to_speaker_monomix_A,
+ MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CFG_Mask,
+ MAX98927_PCM_to_speaker_monomix_A_SHIFT);
+}
+
+static int max98927_mono_out_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return max98927_reg_put(kcontrol, ucontrol,
+ MAX98927_PCM_to_speaker_monomix_A,
+ MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CFG_Mask,
+ MAX98927_PCM_to_speaker_monomix_A_SHIFT);
+}
+
+static bool max98927_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0001 ... 0x0028:
+ case 0x002B ... 0x004E:
+ case 0x0051 ... 0x0055:
+ case 0x005A ... 0x0061:
+ case 0x0072 ... 0x0087:
+ case 0x00FF:
+ case 0x0100:
+ case 0x01FF:
+ return true;
+ }
+ return false;
+};
+
+static const char * const max98927_boost_voltage_text[] = {
+ "6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V",
+ "7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V",
+ "8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V",
+ "9.5V", "9.625V", "9.75V", "9.875V", "10V"
+};
+
+static const char * const max98927_speaker_source_text[] = {
+ "i2s", "reserved", "tone", "pdm"
+};
+
+static const char * const max98927_monomix_output_text[] = {
+ "ch_0", "ch_1", "ch_1_2_div"
+};
+
+static const struct soc_enum max98927_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98927_monomix_output_text),
+ max98927_monomix_output_text),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98927_speaker_source_text),
+ max98927_speaker_source_text),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max98927_boost_voltage_text),
+ max98927_boost_voltage_text),
+};
+
+static const struct snd_kcontrol_new max98927_snd_controls[] = {
+ SOC_SINGLE_EXT_TLV("Speaker Volume", MAX98927_Speaker_Gain,
+ 0, (1<<MAX98927_Speaker_Gain_Width)-1, 0,
+ max98927_spk_gain_get, max98927_spk_gain_put,
+ max98927_spk_tlv),
+ SOC_SINGLE_EXT_TLV("Digital Gain", MAX98927_AMP_volume_control,
+ 0, (1<<MAX98927_AMP_VOL_WIDTH)-1, 0,
+ max98927_digital_gain_get, max98927_digital_gain_put,
+ max98927_digital_tlv),
+ SOC_SINGLE_EXT("Amp DSP Enable", MAX98927_Brownout_enables,
+ MAX98927_BDE_DSP_SHIFT, 1, 0,
+ max98927_amp_dsp_get, max98927_amp_dsp_put),
+ SOC_SINGLE_EXT("Ramp Switch", MAX98927_AMP_DSP_Config,
+ MAX98927_SPK_RMP_EN_SHIFT, 1, 1,
+ max98927_ramp_switch_get, max98927_ramp_switch_put),
+ SOC_SINGLE_EXT("DRE EN", MAX98927_DRE_Control,
+ MAX98927_DRE_Control_DRE_SHIFT, 1, 0,
+ max98927_dre_en_get, max98927_dre_en_put),
+ SOC_SINGLE_EXT("Amp Volume Location", MAX98927_AMP_volume_control,
+ MAX98927_AMP_VOL_LOCATION_SHIFT, 1, 0,
+ max98927_amp_vol_get, max98927_amp_vol_put),
+
+ SOC_ENUM_EXT("Boost Output Voltage", max98927_enum[2],
+ max98927_boost_voltage_get, max98927_boost_voltage_put),
+ SOC_ENUM_EXT("Speaker Source", max98927_enum[1],
+ max98927_spk_src_get, max98927_spk_src_put),
+ SOC_ENUM_EXT("Monomix Output", max98927_enum[0],
+ max98927_mono_out_get, max98927_mono_out_put),
+};
+
+static const struct snd_soc_dapm_route max98927_audio_map[] = {
+ {"BE_OUT", NULL, "Amp Enable"},
+};
+
+static struct snd_soc_dai_driver max98927_dai[] = {
+ {
+ .name = "max98927-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98927_RATES,
+ .formats = MAX98927_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98927_RATES,
+ .formats = MAX98927_FORMATS,
+ },
+ .ops = &max98927_dai_ops,
+ }
+};
+
+static int max98927_probe(struct snd_soc_codec *codec)
+{
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0, reg = 0, i;
+
+ max98927->codec = codec;
+ codec->control_data = max98927->regmap;
+ codec->cache_bypass = 1;
+
+ /* Software Reset */
+ max98927_wrapper_write(max98927,
+ MAX98927_Software_Reset, MAX98927_Software_Reset_RST);
+
+ /* Check Revision ID for the primary MAX98927*/
+ ret = regmap_read(max98927->regmap, MAX98927_REV_ID, ®);
+ if (ret < 0)
+ dev_err(codec->dev,
+ "Failed to read: 0x%02X\n", MAX98927_REV_ID);
+ else
+ dev_info(codec->dev,
+ "MAX98927 revisionID: 0x%02X\n", reg);
+
+ /* Check Revision ID for the secondary MAX98927*/
+ if (max98927->sub_regmap) {
+ ret = regmap_read(max98927->sub_regmap, MAX98927_REV_ID, ®);
+ if (ret < 0)
+ dev_err(codec->dev,
+ "Failed to read: 0x%02X from secodnary device\n"
+ , MAX98927_REV_ID);
+ else
+ dev_info(codec->dev,
+ "Secondary device revisionID: 0x%02X\n", reg);
+ }
+
+ /* Register initialization */
+ for (i = 0; i < sizeof(max98927_reg_map)/
+ sizeof(max98927_reg_map[0]); i++)
+ max98927_wrapper_write(max98927,
+ max98927_reg_map[i].reg,
+ max98927_reg_map[i].def);
+
+ if (max98927->regmap)
+ regmap_write(max98927->regmap,
+ MAX98927_PCM_Tx_Channel_Sources_A,
+ (max98927->i_l_slot
+ <<MAX98927_PCM_Tx_Ch_Sources_A_I_SHIFT|
+ max98927->v_l_slot)&0xFF);
+ if (max98927->sub_regmap)
+ regmap_write(max98927->sub_regmap,
+ MAX98927_PCM_Tx_Channel_Sources_A,
+ (max98927->i_r_slot
+ <<MAX98927_PCM_Tx_Ch_Sources_A_I_SHIFT|
+ max98927->v_r_slot)&0xFF);
+
+ /* Set interleave mode */
+ if (max98927->interleave_mode)
+ max98927_wrap_update_bits(max98927,
+ MAX98927_PCM_Tx_Channel_Sources_B,
+ MAX98927_PCM_Tx_Channel_Src_INTERLEAVE_Mask,
+ MAX98927_PCM_Tx_Channel_Src_INTERLEAVE_Mask);
+
+ max98927_handle_pdata(codec);
+
+ return ret;
+}
+
+static const struct snd_soc_codec_driver soc_codec_dev_max98927 = {
+ .probe = max98927_probe,
+ .dapm_routes = max98927_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98927_audio_map),
+ .dapm_widgets = max98927_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98927_dapm_widgets),
+ .controls = max98927_snd_controls,
+ .num_controls = ARRAY_SIZE(max98927_snd_controls),
+};
+
+static const struct regmap_config max98927_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98927_REV_ID,
+ .reg_defaults = max98927_reg_map,
+ .num_reg_defaults = ARRAY_SIZE(max98927_reg_map),
+ .readable_reg = max98927_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static struct i2c_board_info max98927_i2c_sub_board[] = {
+ {
+ I2C_BOARD_INFO("max98927_sub", 0x39),
+ }
+};
+
+static struct i2c_driver max98927_i2c_sub_driver = {
+ .driver = {
+ .name = "max98927_sub",
+ .owner = THIS_MODULE,
+ },
+};
+
+struct i2c_client *max98927_add_sub_device(int bus_id, int slave_addr)
+{
+ struct i2c_client *i2c = NULL;
+ struct i2c_adapter *adapter;
+
+ max98927_i2c_sub_board[0].addr = slave_addr;
+
+ adapter = i2c_get_adapter(bus_id);
+ if (adapter) {
+ i2c = i2c_new_device(adapter, max98927_i2c_sub_board);
+ if (i2c)
+ i2c->dev.driver = &max98927_i2c_sub_driver.driver;
+ }
+
+ return i2c;
+}
+
+int probe_common(struct i2c_client *i2c, struct max98927_priv *max98927)
+{
+ int ret = 0, value;
+
+ if (!of_property_read_u32(i2c->dev.of_node, "vmon-l-slot", &value))
+ max98927->v_l_slot = value & 0xF;
+ else
+ max98927->v_l_slot = 0;
+ if (!of_property_read_u32(i2c->dev.of_node, "imon-l-slot", &value))
+ max98927->i_l_slot = value & 0xF;
+ else
+ max98927->i_l_slot = 1;
+ if (!of_property_read_u32(i2c->dev.of_node, "vmon-r-slot", &value))
+ max98927->v_r_slot = value & 0xF;
+ else
+ max98927->v_r_slot = 2;
+ if (!of_property_read_u32(i2c->dev.of_node, "imon-r-slot", &value))
+ max98927->i_r_slot = value & 0xF;
+ else
+ max98927->i_r_slot = 3;
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98927,
+ max98927_dai, ARRAY_SIZE(max98927_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev,
+ "Failed to register codec: %d\n", ret);
+ return ret;
+}
+
+static int max98927_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+
+ int ret = 0, value;
+ struct max98927_priv *max98927 = NULL;
+
+ max98927 = devm_kzalloc(&i2c->dev,
+ sizeof(*max98927), GFP_KERNEL);
+
+ if (!max98927) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ i2c_set_clientdata(i2c, max98927);
+
+ /* update interleave mode info */
+ if (!of_property_read_u32(i2c->dev.of_node,
+ "interleave_mode", &value)) {
+ if (value > 0)
+ max98927->interleave_mode = 1;
+ else
+ max98927->interleave_mode = 0;
+ } else
+ max98927->interleave_mode = 0;
+
+ /* update direct configuration info */
+ max98927->regcfg = of_get_property(i2c->dev.of_node,
+ "maxim,regcfg", &max98927->regcfg_sz);
+
+ /* check for secondary MAX98927 */
+ ret = of_property_read_u32(i2c->dev.of_node,
+ "maxim,sub_reg", &max98927->sub_reg);
+ if (ret) {
+ dev_err(&i2c->dev, "Sub-device slave address was not found.\n");
+ max98927->sub_reg = -1;
+ }
+ ret = of_property_read_u32(i2c->dev.of_node,
+ "maxim,sub_bus", &max98927->sub_bus);
+ if (ret) {
+ dev_err(&i2c->dev, "Sub-device bus information was not found.\n");
+ max98927->sub_bus = i2c->adapter->nr;
+ }
+
+ /* regmap initialization for primary device */
+ max98927->regmap
+ = devm_regmap_init_i2c(i2c, &max98927_regmap);
+ if (IS_ERR(max98927->regmap)) {
+ ret = PTR_ERR(max98927->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ goto err;
+ }
+
+ /* regmap initialization for secondary device */
+ if (max98927->sub_reg > 0) {
+ max98927->sub_i2c = max98927_add_sub_device(max98927->sub_bus,
+ max98927->sub_reg);
+ if (IS_ERR(max98927->sub_i2c)) {
+ dev_err(&max98927->sub_i2c->dev,
+ "Second MAX98927 was not found\n");
+ ret = PTR_ERR(max98927->regmap);
+ goto err;
+ } else {
+ max98927->sub_regmap = regmap_init_i2c(
+ max98927->sub_i2c, &max98927_regmap);
+ if (IS_ERR(max98927->sub_regmap)) {
+ ret = PTR_ERR(max98927->sub_regmap);
+ dev_err(&max98927->sub_i2c->dev,
+ "Failed to allocate sub_regmap: %d\n",
+ ret);
+ goto err;
+ }
+ }
+ }
+
+ /* codec registeration */
+ ret = probe_common(i2c, max98927);
+
+ return ret;
+
+err:
+ if (max98927)
+ devm_kfree(&i2c->dev, max98927);
+ return ret;
+}
+
+static int max98927_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id max98927_i2c_id[] = {
+ { "max98927", 0},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98927_i2c_id);
+
+static const struct of_device_id max98927_of_match[] = {
+ { .compatible = "maxim,max98927", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98927_of_match);
+
+static struct i2c_driver max98927_i2c_driver = {
+ .driver = {
+ .name = "max98927",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(max98927_of_match),
+ .pm = NULL,
+ },
+ .probe = max98927_i2c_probe,
+ .remove = max98927_i2c_remove,
+ .id_table = max98927_i2c_id,
+};
+
+module_i2c_driver(max98927_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98927 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h
new file mode 100755
index 0000000..2305185
--- /dev/null
+++ b/sound/soc/codecs/max98927.h
@@ -0,0 +1,1253 @@
+/*
+ * max98927.c -- MAX98927 ALSA Soc Audio driver
+ *
+ * Copyright 2008-11 Wolfson Microelectronics PLC.
+ * Author: Ryan Lee <ryans.lee@maximintegrated.com>
+ *
+ * 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.
+ *
+ */
+#ifndef __MAX98927_REGISTERDEFS_H
+#define __MAX98927_REGISTERDEFS_H
+#ifdef CONFIG_SND_SOC_MAXIM_DSM
+#include <sound/maxim_dsm.h>
+#endif /* CONFIG_SND_SOC_MAXIM_DSM */
+
+enum {
+ PRI_MAX98927 = 0,
+ SEC_MAX98927 = 1,
+ MAX_DEV_ID_MAX98927,
+} MAX98927deviceID;
+
+enum {
+ /*Interrupt Raw 1 (Address 0x0001)*/
+ MAX98927_Interrupt_Raw_1 = 0x0001,
+ MAX98927_Interrupt_Raw_1_BDE_ACTIVE_END_RAW = (0x1 << 0),
+ MAX98927_Interrupt_Raw_1_BDE_ACTIVE_BGN_RAW = (0x1 << 1),
+ MAX98927_Interrupt_Raw_1_BDE_LEVEL_CHANGE_RAW = (0x1 << 2),
+ MAX98927_Interrupt_Raw_1_BDE_L8_RAW = (0x1 << 3),
+ MAX98927_Interrupt_Raw_1_THERMWARN_END_RAW = (0x1 << 4),
+ MAX98927_Interrupt_Raw_1_THERMWARN_START_RAW = (0x1 << 5),
+ MAX98927_Interrupt_Raw_1_THERMSHDN_END_RAW = (0x1 << 6),
+ MAX98927_Interrupt_Raw_1_THERMSHDN_START_RAW = (0x1 << 7),
+
+ /* Interrupt Raw 2 (Address 0x0002)*/
+ MAX98927_Interrupt_Raw_2 = 0x0002,
+ MAX98927_Interrupt_Raw_2_WATCHDOGWARN_RAW = (0x1 << 0),
+ MAX98927_Interrupt_Raw_2_WATCHDOGFAIL_RAW = (0x1 << 1),
+ MAX98927_Interrupt_Raw_2_BOOSTCURRLIM_RAW = (0x1 << 2),
+ MAX98927_Interrupt_Raw_2_CLKSTOP_RAW = (0x1 << 3),
+ MAX98927_Interrupt_Raw_2_CLKSTART_RAW = (0x1 << 4),
+ MAX98927_Interrupt_Raw_2_MEASADC_END_RAW = (0x1 << 5),
+ MAX98927_Interrupt_Raw_2_PWRDN_DONE_RAW = (0x1 << 6),
+ MAX98927_Interrupt_Raw_2_PWRUP_DONE_RAW = (0x1 << 7),
+
+ /* Interrupt Raw 3 (Address 0x0003)*/
+ MAX98927_Interrupt_Raw_3 = 0x0003,
+ MAX98927_Interrupt_Raw_3_PWRUP_FAIL_RAW = (0x1 << 0),
+ MAX98927_Interrupt_Raw_3_AUTH_DONE_RAW = (0x1 << 1),
+ MAX98927_Interrupt_Raw_3_SPK_OVC_RAW = (0x1 << 2),
+ MAX98927_Interrupt_Raw_3_BST_UVLO_RAW = (0x1 << 3),
+
+ /* Interrupt State 1 (Address 0x0004)*/
+ MAX98927_Interrupt_State_1 = 0x0004,
+ MAX98927_Interrupt_State_1_BDE_ACTIVE_END_STATE = (0x1 << 0),
+ MAX98927_Interrupt_State_1_BDE_ACTIVE_BGN_STATE = (0x1 << 1),
+ MAX98927_Interrupt_State_1_BDE_LEVEL_CHANGE_STATE = (0x1 << 2),
+ MAX98927_Interrupt_State_1_BDE_L8_STATE = (0x1 << 3),
+ MAX98927_Interrupt_State_1_THERMWARN_END_STATE = (0x1 << 4),
+ MAX98927_Interrupt_State_1_THERMWARN_START_STATE = (0x1 << 5),
+ MAX98927_Interrupt_State_1_THERMSHDN_END_STATE = (0x1 << 6),
+ MAX98927_Interrupt_State_1_THERMSHDN_START_STATE = (0x1 << 7),
+
+ /* Interrupt State 2 (Address 0x0005)*/
+ MAX98927_Interrupt_State_2 = 0x0005,
+ MAX98927_Interrupt_State_2_WATCHDOGWARN_STATE = (0x1 << 0),
+ MAX98927_Interrupt_State_2_WATCHDOGFAIL_STATE = (0x1 << 1),
+ MAX98927_Interrupt_State_2_BOOSTCURRLIM_STATE = (0x1 << 2),
+ MAX98927_Interrupt_State_2_CLKSTOP_STATE = (0x1 << 3),
+ MAX98927_Interrupt_State_2_CLKSTART_STATE = (0x1 << 4),
+ MAX98927_Interrupt_State_2_MEASADC_END_STATE = (0x1 << 5),
+ MAX98927_Interrupt_State_2_PWRDN_DONE_STATE = (0x1 << 6),
+ MAX98927_Interrupt_State_2_PWRUP_DONE_STATE = (0x1 << 7),
+
+ /* Interrupt State 3 (Address 0x0006)*/
+ MAX98927_Interrupt_State_3 = 0x0006,
+ MAX98927_Interrupt_State_3_PWRUP_FAIL_STATE = (0x1 << 0),
+ MAX98927_Interrupt_State_3_AUTH_DONE_STATE = (0x1 << 1),
+ MAX98927_Interrupt_State_3_SPK_OVC_STATE = (0x1 << 2),
+ MAX98927_Interrupt_State_3_BST_UVLO_STATE = (0x1 << 3),
+
+ /* Interrupt Flag 1 (Address 0x0007)*/
+ MAX98927_Interrupt_Flag_1 = 0x0007,
+ MAX98927_Interrupt_Flag_1_BDE_ACTIVE_END_FLAG = (0x1 << 0),
+ MAX98927_Interrupt_Flag_1_BDE_ACTIVE_BGN_FLAG = (0x1 << 1),
+ MAX98927_Interrupt_Flag_1_BDE_LEVEL_CHANGE_FLAG = (0x1 << 2),
+ MAX98927_Interrupt_Flag_1_BDE_L8_FLAG = (0x1 << 3),
+ MAX98927_Interrupt_Flag_1_THERMWARN_END_FLAG = (0x1 << 4),
+ MAX98927_Interrupt_Flag_1_THERMWARN_START_FLAG = (0x1 << 5),
+ MAX98927_Interrupt_Flag_1_THERMSHDN_END_FLAG = (0x1 << 6),
+ MAX98927_Interrupt_Flag_1_THERMSHDN_START_FLAG = (0x1 << 7),
+
+ /* Interrupt Flag 2 (Address 0x0008)*/
+ MAX98927_Interrupt_Flag_2 = 0x0008,
+ MAX98927_Interrupt_Flag_2_WATCHDOGWARN_FLAG = (0x1 << 0),
+ MAX98927_Interrupt_Flag_2_WATCHDOGFAIL_FLAG = (0x1 << 1),
+ MAX98927_Interrupt_Flag_2_BOOSTCURRLIM_FLAG = (0x1 << 2),
+ MAX98927_Interrupt_Flag_2_CLKSTOP_FLAG = (0x1 << 3),
+ MAX98927_Interrupt_Flag_2_CLKSTART_FLAG = (0x1 << 4),
+ MAX98927_Interrupt_Flag_2_MEASADC_END_FLAG = (0x1 << 5),
+ MAX98927_Interrupt_Flag_2_PWRDN_DONE_FLAG = (0x1 << 6),
+ MAX98927_Interrupt_Flag_2_PWRUP_DONE_FLAG = (0x1 << 7),
+
+ /* Interrupt Flag 3 (Address 0x0009)*/
+ MAX98927_Interrupt_Flag_3 = 0x0009,
+ MAX98927_Interrupt_Flag_3_PWRUP_FAIL_FLAG = (0x1 << 0),
+ MAX98927_Interrupt_Flag_3_AUTH_DONE_FLAG = (0x1 << 1),
+ MAX98927_Interrupt_Flag_3_SPK_OVC_FLAG = (0x1 << 2),
+ MAX98927_Interrupt_Flag_3_BST_UVLO_FLAG = (0x1 << 3),
+
+ /* Interrupt Enable 1 (Address 0x000a)*/
+ MAX98927_Interrupt_Enable_1 = 0x000a,
+ MAX98927_Interrupt_Enable_1_BDE_ACTIVE_END_EN = (0x1 << 0),
+ MAX98927_Interrupt_Enable_1_BDE_ACTIVE_BGN_EN = (0x1 << 1),
+ MAX98927_Interrupt_Enable_1_BDE_LEVEL_CHANGE_EN = (0x1 << 2),
+ MAX98927_Interrupt_Enable_1_BDE_L8_EN = (0x1 << 3),
+ MAX98927_Interrupt_Enable_1_THERMWARN_END_EN = (0x1 << 4),
+ MAX98927_Interrupt_Enable_1_THERMWARN_START_EN = (0x1 << 5),
+ MAX98927_Interrupt_Enable_1_THERMSHDN_END_EN = (0x1 << 6),
+ MAX98927_Interrupt_Enable_1_THERMSHDN_START_EN = (0x1 << 7),
+
+ /* Interrupt Enable 2 (Address 0x000b)*/
+ MAX98927_Interrupt_Enable_2 = 0x000b,
+ MAX98927_Interrupt_Enable_2_WATCHDOGWARN_EN = (0x1 << 0),
+ MAX98927_Interrupt_Enable_2_WATCHDOGFAIL_EN = (0x1 << 1),
+ MAX98927_Interrupt_Enable_2_BOOSTCURRLIM_EN = (0x1 << 2),
+ MAX98927_Interrupt_Enable_2_CLKSTOP_EN = (0x1 << 3),
+ MAX98927_Interrupt_Enable_2_CLKSTART_EN = (0x1 << 4),
+ MAX98927_Interrupt_Enable_2_MEASADC_END_EN = (0x1 << 5),
+ MAX98927_Interrupt_Enable_2_PWRDN_DONE_EN = (0x1 << 6),
+ MAX98927_Interrupt_Enable_2_PWRUP_DONE_EN = (0x1 << 7),
+
+ /* Interrupt Enable 3 (Address 0x000c)*/
+ MAX98927_Interrupt_Enable_3 = 0x000c,
+ MAX98927_Interrupt_Enable_3_PWRUP_FAIL_EN = (0x1 << 0),
+ MAX98927_Interrupt_Enable_3_AUTH_DONE_EN = (0x1 << 1),
+ MAX98927_Interrupt_Enable_3_SPK_OVC_EN = (0x1 << 2),
+ MAX98927_Interrupt_Enable_3_BST_UVLO_EN = (0x1 << 3),
+
+ /* Interrupt Flag Clear 1 (Address 0x000d)*/
+ MAX98927_Interrupt_Flag_Clear_1 = 0x000d,
+ MAX98927_Interrupt_Flag_Clear_1_BDE_ACTIVE_END_CLR = (0x1 << 0),
+ MAX98927_Interrupt_Flag_Clear_1_BDE_ACTIVE_BGN_CLR = (0x1 << 1),
+ MAX98927_Interrupt_Flag_Clear_1_BDE_LEVEL_CHANGE_CLR = (0x1 << 2),
+ MAX98927_Interrupt_Flag_Clear_1_BDE_L8_CLR = (0x1 << 3),
+ MAX98927_Interrupt_Flag_Clear_1_THERMWARN_END_CLR = (0x1 << 4),
+ MAX98927_Interrupt_Flag_Clear_1_THERMWARN_START_CLR = (0x1 << 5),
+ MAX98927_Interrupt_Flag_Clear_1_THERMSHDN_END_CLR = (0x1 << 6),
+ MAX98927_Interrupt_Flag_Clear_1_THERMSHDN_START_CLR = (0x1 << 7),
+
+ /* Interrupt Flag Clear 2 (Address 0x000e)*/
+ MAX98927_Interrupt_Flag_Clear_2 = 0x000e,
+ MAX98927_Interrupt_Flag_Clear_2_WATCHDOGWARN_CLR = (0x1 << 0),
+ MAX98927_Interrupt_Flag_Clear_2_WATCHDOGFAIL_CLR = (0x1 << 1),
+ MAX98927_Interrupt_Flag_Clear_2_BOOSTCURRLIM_CLR = (0x1 << 2),
+ MAX98927_Interrupt_Flag_Clear_2_CLKSTOP_CLR = (0x1 << 3),
+ MAX98927_Interrupt_Flag_Clear_2_CLKSTART_CLR = (0x1 << 4),
+ MAX98927_Interrupt_Flag_Clear_2_MEASADC_END_CLR = (0x1 << 5),
+ MAX98927_Interrupt_Flag_Clear_2_PWRDN_DONE_CLR = (0x1 << 6),
+ MAX98927_Interrupt_Flag_Clear_2_PWRUP_DONE_CLR = (0x1 << 7),
+
+ /* Interrupt Flag Clear 3 (Address 0x000f)*/
+ MAX98927_Interrupt_Flag_Clear_3 = 0x000f,
+ MAX98927_Interrupt_Flag_Clear_3_PWRUP_FAIL_CLR = (0x1 << 0),
+ MAX98927_Interrupt_Flag_Clear_3_AUTH_DONE_CLR = (0x1 << 1),
+ MAX98927_Interrupt_Flag_Clear_3_SPK_OVC_CLR = (0x1 << 2),
+ MAX98927_Interrupt_Flag_Clear_3_BST_UVLO_CLR = (0x1 << 3),
+
+ /* IRQ Control (Address 0x0010)*/
+ MAX98927_IRQ_Control = 0x0010,
+ MAX98927_IRQ_Control_IRQ_EN = (0x1 << 0),
+ MAX98927_IRQ_Control_IRQ_POL = (0x1 << 1),
+ MAX98927_IRQ_Control_IRQ_MODE = (0x1 << 2),
+
+ /* Clock monitor enable (Address 0x0011)*/
+ MAX98927_Clock_monitor_enable = 0x0011,
+ MAX98927_Clock_monitor_enable_CMON_ENA = (0x1 << 0),
+ MAX98927_Clock_monitor_enable_CMON_AUTORESTART_ENA = (0x1 << 1),
+
+ /* Watchdog Control (Address 0x0012)*/
+ MAX98927_Watchdog_Control = 0x0012,
+ MAX98927_Watchdog_Control_WDT_ENA = (0x1 << 0),
+ MAX98927_Watchdog_Control_WDT_MODE = (0x1 << 1),
+ MAX98927_Watchdog_Control_WDT_TO_SEL_Mask = (0x3 << 2),
+ MAX98927_Watchdog_Control_WDT_TO_SEL_5 = (0x0 << 2),
+ MAX98927_Watchdog_Control_WDT_TO_SEL_10 = (0x1 << 2),
+ MAX98927_Watchdog_Control_WDT_TO_SEL_35 = (0x2 << 2),
+ MAX98927_Watchdog_Control_WDT_TO_SEL_50 = (0x3 << 2),
+ MAX98927_Watchdog_Control_WDT_HW_SOURCE = (0x1 << 4),
+
+ /* Watchdog SW Reset (Address 0x0013)*/
+ MAX98927_Watchdog_SW_Reset = 0x0013,
+ MAX98927_Watchdog_SW_Reset_WDT_SW_RST_Mask = (0xff << 0),
+
+ /* Meas ADC Thermal Warning Threshhold (Address 0x0014)*/
+ MAX98927_Meas_ADC_TW_Threshhold = 0x0014,
+ MAX98927_Meas_ADC_TW_Threshhold_MEAS_ADC_WARN_THRESH_Mask
+ = (0xff << 0),
+
+ /* Meas ADC Thermal Shutdown Threshhold (Address 0x0015)*/
+ MAX98927_Meas_ADC_TS_Threshhold = 0x0015,
+ MAX98927_Meas_ADC_TS_Threshhold_MEAS_ADC_SHDN_THRESH_Mask
+ = (0xff << 0),
+
+ /* Meas ADC Thermal Hysteresis (Address 0x0016)*/
+ MAX98927_Meas_ADC_Thermal_Hysteresis = 0x0016,
+ MAX98927_Meas_ADC_TH_MEAS_ADC_THERM_HYST_Mask = (0x1f << 0),
+
+ /* Pin Config (Address 0x0017)*/
+ MAX98927_Pin_Config = 0x0017,
+ MAX98927_Pin_Config_DOUT_DRV_Mask = (0x3 << 0),
+ MAX98927_Pin_Config_DOUT_DRV_01 = (0x0 << 0),
+ MAX98927_Pin_Config_DOUT_DRV_11 = (0x2 << 0),
+ MAX98927_Pin_Config_BCLK_DRV_Mask = (0x3 << 2),
+ MAX98927_Pin_Config_BCLK_DRV_01 = (0x0 << 2),
+ MAX98927_Pin_Config_BCLK_DRV_11 = (0x2 << 2),
+ MAX98927_Pin_Config_LRCLK_DRV_Mask = (0x3 << 4),
+ MAX98927_Pin_Config_LRCLK_DRV_01 = (0x0 << 4),
+ MAX98927_Pin_Config_LRCLK_DRV_11 = (0x2 << 4),
+ MAX98927_Pin_Config_ICC_DRV_Mask = (0x3 << 6),
+ MAX98927_Pin_Config_ICC_DRV_01 = (0x0 << 6),
+ MAX98927_Pin_Config_ICC_DRV_11 = (0x2 << 6),
+
+ /* PCM Rx Enables A (Address 0x0018)*/
+ MAX98927_PCM_Rx_Enables_A = 0x0018,
+ MAX98927_PCM_Rx_Enables_A_PCM_RX_CH0_EN = (0x1 << 0),
+ MAX98927_PCM_Rx_Enables_A_PCM_RX_CH1_EN = (0x1 << 1),
+ MAX98927_PCM_Rx_Enables_A_PCM_RX_CH2_EN = (0x1 << 2),
+ MAX98927_PCM_Rx_Enables_A_PCM_RX_CH3_EN = (0x1 << 3),
+ MAX98927_PCM_Rx_Enables_A_PCM_RX_CH4_EN = (0x1 << 4),
+ MAX98927_PCM_Rx_Enables_A_PCM_RX_CH5_EN = (0x1 << 5),
+ MAX98927_PCM_Rx_Enables_A_PCM_RX_CH6_EN = (0x1 << 6),
+ MAX98927_PCM_Rx_Enables_A_PCM_RX_CH7_EN = (0x1 << 7),
+
+ /* PCM Rx Enables B (Address 0x0019)*/
+ MAX98927_PCM_Rx_Enables_B = 0x0019,
+ MAX98927_PCM_Rx_Enables_B_PCM_RX_CH8_EN = (0x1 << 0),
+ MAX98927_PCM_Rx_Enables_B_PCM_RX_CH9_EN = (0x1 << 1),
+ MAX98927_PCM_Rx_Enables_B_PCM_RX_CH10_EN = (0x1 << 2),
+ MAX98927_PCM_Rx_Enables_B_PCM_RX_CH11_EN = (0x1 << 3),
+ MAX98927_PCM_Rx_Enables_B_PCM_RX_CH12_EN = (0x1 << 4),
+ MAX98927_PCM_Rx_Enables_B_PCM_RX_CH13_EN = (0x1 << 5),
+ MAX98927_PCM_Rx_Enables_B_PCM_RX_CH14_EN = (0x1 << 6),
+ MAX98927_PCM_Rx_Enables_B_PCM_RX_CH15_EN = (0x1 << 7),
+
+ /* PCM Tx Enables A (Address 0x001a)*/
+ MAX98927_PCM_Tx_Enables_A = 0x001a,
+ MAX98927_PCM_Tx_Enables_A_PCM_TX_CH0_EN = (0x1 << 0),
+ MAX98927_PCM_Tx_Enables_A_PCM_TX_CH1_EN = (0x1 << 1),
+ MAX98927_PCM_Tx_Enables_A_PCM_TX_CH2_EN = (0x1 << 2),
+ MAX98927_PCM_Tx_Enables_A_PCM_TX_CH3_EN = (0x1 << 3),
+ MAX98927_PCM_Tx_Enables_A_PCM_TX_CH4_EN = (0x1 << 4),
+ MAX98927_PCM_Tx_Enables_A_PCM_TX_CH5_EN = (0x1 << 5),
+ MAX98927_PCM_Tx_Enables_A_PCM_TX_CH6_EN = (0x1 << 6),
+ MAX98927_PCM_Tx_Enables_A_PCM_TX_CH7_EN = (0x1 << 7),
+
+ /* PCM Tx Enables B (Address 0x001b)*/
+ MAX98927_PCM_Tx_Enables_B = 0x001b,
+ MAX98927_PCM_Tx_Enables_B_PCM_TX_CH8_EN = (0x1 << 0),
+ MAX98927_PCM_Tx_Enables_B_PCM_TX_CH9_EN = (0x1 << 1),
+ MAX98927_PCM_Tx_Enables_B_PCM_TX_CH10_EN = (0x1 << 2),
+ MAX98927_PCM_Tx_Enables_B_PCM_TX_CH11_EN = (0x1 << 3),
+ MAX98927_PCM_Tx_Enables_B_PCM_TX_CH12_EN = (0x1 << 4),
+ MAX98927_PCM_Tx_Enables_B_PCM_TX_CH13_EN = (0x1 << 5),
+ MAX98927_PCM_Tx_Enables_B_PCM_TX_CH14_EN = (0x1 << 6),
+ MAX98927_PCM_Tx_Enables_B_PCM_TX_CH15_EN = (0x1 << 7),
+
+ /* PCM Tx HiZ Control A (Address 0x001c)*/
+ MAX98927_PCM_Tx_HiZ_Control_A = 0x001c,
+ MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH0_HIZ = (0x1 << 0),
+ MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH1_HIZ = (0x1 << 1),
+ MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH2_HIZ = (0x1 << 2),
+ MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH3_HIZ = (0x1 << 3),
+ MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH4_HIZ = (0x1 << 4),
+ MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH5_HIZ = (0x1 << 5),
+ MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH6_HIZ = (0x1 << 6),
+ MAX98927_PCM_Tx_HiZ_Control_A_PCM_TX_CH7_HIZ = (0x1 << 7),
+
+ /* PCM Tx HiZ Control B (Address 0x001d)*/
+ MAX98927_PCM_Tx_HiZ_Control_B = 0x001d,
+ MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH8_HIZ = (0x1 << 0),
+ MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH9_HIZ = (0x1 << 1),
+ MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH10_HIZ = (0x1 << 2),
+ MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH11_HIZ = (0x1 << 3),
+ MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH12_HIZ = (0x1 << 4),
+ MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH13_HIZ = (0x1 << 5),
+ MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH14_HIZ = (0x1 << 6),
+ MAX98927_PCM_Tx_HiZ_Control_B_PCM_TX_CH15_HIZ = (0x1 << 7),
+
+ /* PCM Tx Channel Sources A (Address 0x001e)*/
+ MAX98927_PCM_Tx_Channel_Sources_A = 0x001e,
+ MAX98927_PCM_Tx_Channel_Sources_A_PCM_IVADC_V_DEST_Mask = (0xf << 0),
+ MAX98927_PCM_Tx_Channel_Sources_A_PCM_IVADC_I_DEST_Mask = (0xf << 4),
+
+ /* PCM Tx Channel Sources B (Address 0x001f)*/
+ MAX98927_PCM_Tx_Channel_Sources_B = 0x001f,
+ MAX98927_PCM_Tx_Channel_Sources_B_PCM_AMP_DSP_DEST_Mask = (0xf << 0),
+ MAX98927_PCM_Tx_Channel_Src_INTERLEAVE_Mask = (0x1 << 5),
+
+ /* PCM Mode Config (Address 0x0020)*/
+ MAX98927_PCM_Mode_Config = 0x0020,
+ MAX98927_PCM_Mode_Config_PCM_TX_EXTRA_HIZ = (0x1 << 0),
+ MAX98927_PCM_Mode_Config_PCM_CHANSEL = (0x1 << 1),
+ MAX98927_PCM_Mode_Config_PCM_BCLKEDGE = (0x1 << 2),
+ MAX98927_PCM_Mode_Config_PCM_FORMAT_Mask = (0x7 << 3),
+ MAX98927_PCM_Mode_Config_PCM_FORMAT_I2S = (0x0 << 3),
+ MAX98927_PCM_Mode_Config_PCM_FORMAT_LEFT = (0x1 << 3),
+ MAX98927_PCM_Mode_Config_PCM_FORMAT_TDM_0 = (0x3 << 3),
+ MAX98927_PCM_Mode_Config_PCM_FORMAT_TDM_1 = (0x4 << 3),
+ MAX98927_PCM_Mode_Config_PCM_FORMAT_TDM_2 = (0x5 << 3),
+ MAX98927_PCM_Mode_Config_PCM_FORMAT_ = (0x6 << 3),
+ MAX98927_PCM_Mode_Config_PCM_CHANSZ_Mask = (0x3 << 6),
+ MAX98927_PCM_Mode_Config_PCM_CHANSZ_16 = (0x1 << 6),
+ MAX98927_PCM_Mode_Config_PCM_CHANSZ_24 = (0x2 << 6),
+ MAX98927_PCM_Mode_Config_PCM_CHANSZ_32 = (0x3 << 6),
+
+ /* PCM Master Mode (Address 0x0021)*/
+ MAX98927_PCM_Master_Mode = 0x0021,
+ MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_Mask = (0x3 << 0),
+ MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_SLAVE = (0x0 << 0),
+ MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_MASTER = (0x3 << 0),
+ MAX98927_PCM_Master_Mode_PCM_MSTR_MODE_HYBRID = (0x1 << 0),
+ MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_Mask = (0xf << 2),
+ MAX98927_PCM_Master_Mode_PCM_CLK_SOURCE = (0x1 << 6),
+
+ /* PCM Clock setup (Address 0x0022)*/
+ MAX98927_PCM_Clock_setup = 0x0022,
+ MAX98927_PCM_Clock_setup_PCM_BSEL_Mask = (0xf << 0),
+ MAX98927_PCM_Clock_setup_PCM_MSEL_Mask = (0xf << 4),
+
+ /* PCM Sample rate setup 1 (Address 0x0023)*/
+ MAX98927_PCM_Sample_rate_setup_1 = 0x0023,
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_Mask = (0xf << 0),
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_8000 = (0x0 << 0),
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_11025 = (0x1 << 0),
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_12000 = (0x2 << 0),
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_16000 = (0x3 << 0),
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_22050 = (0x4 << 0),
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_24000 = (0x5 << 0),
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_32000 = (0x6 << 0),
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_44100 = (0x7 << 0),
+ MAX98927_PCM_Sample_rate_setup_1_DIG_IF_SR_48000 = (0x8 << 0),
+
+ /* PCM Sample rate setup 1 (Address 0x0024)*/
+ MAX98927_PCM_Sample_rate_setup_2 = 0x0024,
+ MAX98927_PCM_Sample_rate_setup_2_IVADC_SR_Mask = (0xf << 0),
+ MAX98927_PCM_Sample_rate_setup_2_SPK_SR_Mask = (0xf << 4),
+ MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0001 = (0x0 << 4),
+ MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0011 = (0x2 << 4),
+ MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0101 = (0x4 << 4),
+ MAX98927_PCM_Sample_rate_setup_2_SPK_SR_0111 = (0x6 << 4),
+ MAX98927_PCM_Sample_rate_setup_2_SPK_SR_1001 = (0x8 << 4),
+ MAX98927_PCM_Sample_rate_setup_2_SPK_SR_1011 = (0xa << 4),
+ MAX98927_PCM_Sample_rate_setup_2_SPK_SR_1101 = (0xc << 4),
+ MAX98927_PCM_Sample_rate_setup_2_SPK_SR_ = (0xf << 4),
+
+ /* PCM to speaker monomix A (Address 0x0025)*/
+ MAX98927_PCM_to_speaker_monomix_A = 0x0025,
+ MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CH0_SOURCE_Mask = (0xf << 0),
+ MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CFG_Mask = (0x3 << 6),
+ MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CFG_1 = (0x0 << 6),
+ MAX98927_PCM_to_spkmonomix_A_DMONOMIX_CFG_3 = (0x0 << 6),
+
+ /* PCM to speaker monomix B (Address 0x0026)*/
+ MAX98927_PCM_to_spkmonomix_B = 0x0026,
+ MAX98927_PCM_to_spkmonomix_B_DMONOMIX_CH1_SOURCE_Mask = (0xf << 0),
+
+ /* ICC RX Enables A (Address 0x0027)*/
+ MAX98927_ICC_RX_Enables_A = 0x0027,
+ MAX98927_ICC_RX_Enables_A_ICC_RX_CH0_EN = (0x1 << 0),
+ MAX98927_ICC_RX_Enables_A_ICC_RX_CH1_EN = (0x1 << 1),
+ MAX98927_ICC_RX_Enables_A_ICC_RX_CH2_EN = (0x1 << 2),
+ MAX98927_ICC_RX_Enables_A_ICC_RX_CH3_EN = (0x1 << 3),
+ MAX98927_ICC_RX_Enables_A_ICC_RX_CH4_EN = (0x1 << 4),
+ MAX98927_ICC_RX_Enables_A_ICC_RX_CH5_EN = (0x1 << 5),
+ MAX98927_ICC_RX_Enables_A_ICC_RX_CH6_EN = (0x1 << 6),
+ MAX98927_ICC_RX_Enables_A_ICC_RX_CH7_EN = (0x1 << 7),
+
+ /* ICC RX Enables B (Address 0x0028)*/
+ MAX98927_ICC_RX_Enables_B = 0x0028,
+ MAX98927_ICC_RX_Enables_B_ICC_RX_CH8_EN = (0x1 << 0),
+ MAX98927_ICC_RX_Enables_B_ICC_RX_CH9_EN = (0x1 << 1),
+ MAX98927_ICC_RX_Enables_B_ICC_RX_CH10_EN = (0x1 << 2),
+ MAX98927_ICC_RX_Enables_B_ICC_RX_CH11_EN = (0x1 << 3),
+ MAX98927_ICC_RX_Enables_B_ICC_RX_CH12_EN = (0x1 << 4),
+ MAX98927_ICC_RX_Enables_B_ICC_RX_CH13_EN = (0x1 << 5),
+ MAX98927_ICC_RX_Enables_B_ICC_RX_CH14_EN = (0x1 << 6),
+ MAX98927_ICC_RX_Enables_B_ICC_RX_CH15_EN = (0x1 << 7),
+
+ /* ICC TX Enables A (Address 0x002b)*/
+ MAX98927_ICC_TX_Enables_A = 0x002b,
+ MAX98927_ICC_TX_Enables_A_ICC_TX_CH0_EN = (0x1 << 0),
+ MAX98927_ICC_TX_Enables_A_ICC_TX_CH1_EN = (0x1 << 1),
+ MAX98927_ICC_TX_Enables_A_ICC_TX_CH2_EN = (0x1 << 2),
+ MAX98927_ICC_TX_Enables_A_ICC_TX_CH3_EN = (0x1 << 3),
+ MAX98927_ICC_TX_Enables_A_ICC_TX_CH4_EN = (0x1 << 4),
+ MAX98927_ICC_TX_Enables_A_ICC_TX_CH5_EN = (0x1 << 5),
+ MAX98927_ICC_TX_Enables_A_ICC_TX_CH6_EN = (0x1 << 6),
+ MAX98927_ICC_TX_Enables_A_ICC_TX_CH7_EN = (0x1 << 7),
+
+ /* ICC TX Enables B (Address 0x002c)*/
+ MAX98927_ICC_TX_Enables_B = 0x002c,
+ MAX98927_ICC_TX_Enables_B_ICC_TX_CH8_EN = (0x1 << 0),
+ MAX98927_ICC_TX_Enables_B_ICC_TX_CH9_EN = (0x1 << 1),
+ MAX98927_ICC_TX_Enables_B_ICC_TX_CH10_EN = (0x1 << 2),
+ MAX98927_ICC_TX_Enables_B_ICC_TX_CH11_EN = (0x1 << 3),
+ MAX98927_ICC_TX_Enables_B_ICC_TX_CH12_EN = (0x1 << 4),
+ MAX98927_ICC_TX_Enables_B_ICC_TX_CH13_EN = (0x1 << 5),
+ MAX98927_ICC_TX_Enables_B_ICC_TX_CH14_EN = (0x1 << 6),
+ MAX98927_ICC_TX_Enables_B_ICC_TX_CH15_EN = (0x1 << 7),
+
+ /* ICC Data Order Select (Address 0x002d)*/
+ MAX98927_ICC_Data_Order_Select = 0x002d,
+ MAX98927_ICC_Data_Order_Select_ICC_DRIVE_MODE = (0x1 << 3),
+
+ /* ICC HiZ Manual Mode (Address 0x002e)*/
+ MAX98927_ICC_HiZ_Manual_Mode = 0x002e,
+ MAX98927_ICC_HiZ_Manual_Mode_ICC_TX_HIZ_MANUAL = (0x1 << 0),
+ MAX98927_ICC_HiZ_Manual_Mode_ICC_TX_EXTRA_HIZ = (0x1 << 1),
+
+ /* ICC TX HiZ Enables A (Address 0x002f)*/
+ MAX98927_ICC_TX_HiZ_Enables_A = 0x002f,
+ MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH0_HIZ = (0x1 << 0),
+ MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH1_HIZ = (0x1 << 1),
+ MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH2_HIZ = (0x1 << 2),
+ MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH3_HIZ = (0x1 << 3),
+ MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH4_HIZ = (0x1 << 4),
+ MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH5_HIZ = (0x1 << 5),
+ MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH6_HIZ = (0x1 << 6),
+ MAX98927_ICC_TX_HiZ_Enables_A_ICC_TX_CH7_HIZ = (0x1 << 7),
+
+ /* ICC TX HiZ Enables B (Address 0x0030)*/
+ MAX98927_ICC_TX_HiZ_Enables_B = 0x0030,
+ MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH8_HIZ = (0x1 << 0),
+ MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH9_HIZ = (0x1 << 1),
+ MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH10_HIZ = (0x1 << 2),
+ MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH11_HIZ = (0x1 << 3),
+ MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH12_HIZ = (0x1 << 4),
+ MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH13_HIZ = (0x1 << 5),
+ MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH14_HIZ = (0x1 << 6),
+ MAX98927_ICC_TX_HiZ_Enables_B_ICC_TX_CH15_HIZ = (0x1 << 7),
+
+ /* ICC Link Enables (Address 0x0031)*/
+ MAX98927_ICC_Link_Enables = 0x0031,
+ MAX98927_ICC_Link_Enables_ICC_LINK_EN = (0x1 << 1),
+
+ /* PDM Tx Enables (Address 0x0032)*/
+ MAX98927_PDM_Tx_Enables = 0x0032,
+ MAX98927_PDM_Tx_Enables_PDM_TX_EN = (0x1 << 0),
+ MAX98927_PDM_Tx_Enables_PDM_TX_CLK_DIV2 = (0x1 << 1),
+
+ /* PDM Tx HiZ Control (Address 0x0033)*/
+ MAX98927_PDM_Tx_HiZ_Control = 0x0033,
+ MAX98927_PDM_Tx_HiZ_Control_PDM_TX_HIZ = (0x1 << 0),
+
+ /* PDM Tx Control (Address 0x0034)*/
+ MAX98927_PDM_Tx_Control = 0x0034,
+ MAX98927_PDM_Tx_Control_PDM_TX_CH0_SOURCE = (0x1 << 0),
+ MAX98927_PDM_Tx_Control_PDM_TX_CH1_SOURCE = (0x1 << 1),
+
+ /* PDM Rx Enable (Address 0x0034)*/
+ MAX98927_PDM_Rx_Enable = 0x0035,
+ MAX98927_PDM_Rx_Enable_PDM_RX_EN = (0x1 << 0),
+ MAX98927_PDM_Rx_Enable_PDM_DSP_EN = (0x1 << 1),
+ MAX98927_PDM_Rx_Enable_PDM_DITH_EN = (0x1 << 2),
+ MAX98927_PDM_Rx_Enable_PDM_RX_CH_SEL = (0x1 << 3),
+ MAX98927_PDM_Rx_Enable_PDM_FIFO_RDY_LVL_Mask = (0xf << 4),
+
+ /* AMP volume control (Address 0x0036)*/
+ MAX98927_AMP_volume_control = 0x0036,
+ MAX98927_AMP_volume_control_AMP_VOL_Mask = (0x7f << 0),
+ MAX98927_AMP_volume_control_AMP_VOL_SEL = (0x1 << 7),
+
+ /* AMP DSP Config (Address 0x0037)*/
+ MAX98927_AMP_DSP_Config = 0x0037,
+ MAX98927_AMP_DSP_Config_AMP_DCBLK_EN = (0x1 << 0),
+ MAX98927_AMP_DSP_Config_AMP_DITH_EN = (0x1 << 1),
+ MAX98927_AMP_DSP_Config_DAC_HALF_REF_CURRENT = (0x1 << 2),
+ MAX98927_AMP_DSP_Config_DAC_DOUBLE_RFB = (0x1 << 3),
+ MAX98927_AMP_DSP_Config_AMP_VOL_RMP_BYPASS = (0x1 << 4),
+ MAX98927_AMP_DSP_Config_DAC_INVERT = (0x1 << 5),
+
+ /* Tone Generator and DC Config (Address 0x0038)*/
+ MAX98927_Tone_Generator_and_DC_Config = 0x0038,
+ MAX98927_Tone_Generator_and_DC_Config_TONE_CONFIG_Mask = (0xf << 0),
+
+ /* DRE Control (Address 0x0039)*/
+ MAX98927_DRE_Control = 0x0039,
+ MAX98927_DRE_Control_DRE_EN = (0x1 << 0),
+
+ /* AMP enables (Address 0x003a)*/
+ MAX98927_AMP_enables = 0x003a,
+ MAX98927_AMP_enables_SPK_EN = (0x1 << 0),
+
+ /* Speaker source select (Address 0x003b)*/
+ MAX98927_Speaker_source_select = 0x003b,
+ MAX98927_Speaker_source_select_SPK_SOURCE_Mask = (0x3 << 0),
+ MAX98927_Speaker_source_select_SPK_SOURCE_01 = (0x0 << 0),
+ MAX98927_Speaker_source_select_SPK_SOURCE_11 = (0x2 << 0),
+
+ /* Speaker Gain (Address 0x003c)*/
+ MAX98927_Speaker_Gain = 0x003c,
+ MAX98927_Speaker_Gain_SPK_PCM_GAIN_Mask = (0x7 << 0),
+ MAX98927_Speaker_Gain_SPK_PCM_GAIN_001 = (0x0 << 0),
+ MAX98927_Speaker_Gain_SPK_PCM_GAIN_011 = (0x2 << 0),
+ MAX98927_Speaker_Gain_SPK_PCM_GAIN_101 = (0x4 << 0),
+ MAX98927_Speaker_Gain_SPK_PCM_GAIN_111 = (0x6 << 0),
+ MAX98927_Speaker_Gain_SPK_PDM_GAIN_Mask = (0x7 << 4),
+ MAX98927_Speaker_Gain_SPK_PDM_GAIN_001 = (0x0 << 4),
+ MAX98927_Speaker_Gain_SPK_PDM_GAIN_011 = (0x2 << 4),
+ MAX98927_Speaker_Gain_SPK_PDM_GAIN_101 = (0x4 << 4),
+ MAX98927_Speaker_Gain_SPK_PDM_GAIN_111 = (0x6 << 4),
+
+ /* SSM Configuration (Address 0x003d)*/
+ MAX98927_SSM_Configuration = 0x003d,
+ MAX98927_SSM_Configuration_SSM_MOD_INDEX_Mask = (0x7 << 0),
+ MAX98927_SSM_Configuration_SSM_MOD_INDEX_001 = (0x0 << 0),
+ MAX98927_SSM_Configuration_SSM_MOD_INDEX_011 = (0x2 << 0),
+ MAX98927_SSM_Configuration_SSM_MOD_INDEX_101 = (0x4 << 0),
+ MAX98927_SSM_Configuration_SSM_MOD_INDEX_ = (0x6 << 0),
+ MAX98927_SSM_Configuration_SPK_FSW_SEL = (0x1 << 3),
+ MAX98927_SSM_Configuration_SSM_ENA = (0x1 << 7),
+
+ /* Measurement enables (Address 0x003e)*/
+ MAX98927_Measurement_enables = 0x003e,
+ MAX98927_Measurement_enables_IVADC_V_EN = (0x1 << 0),
+ MAX98927_Measurement_enables_IVADC_I_EN = (0x1 << 1),
+
+ /* Measurement DSP Config (Address 0x003f)*/
+ MAX98927_Measurement_DSP_Config = 0x003f,
+ MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_EN = (0x1 << 0),
+ MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_EN = (0x1 << 1),
+ MAX98927_Measurement_DSP_Config_MEAS_DITH_EN = (0x1 << 2),
+ MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_Mask = (0x3 << 4),
+ MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_01 = (0x0 << 4),
+ MAX98927_Measurement_DSP_Config_MEAS_V_DCBLK_11 = (0x2 << 4),
+ MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_Mask = (0x3 << 6),
+ MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_01 = (0x0 << 6),
+ MAX98927_Measurement_DSP_Config_MEAS_I_DCBLK_11 = (0x2 << 6),
+
+ /* Boost Control 0 (Address 0x0040)*/
+ MAX98927_Boost_Control_0 = 0x0040,
+ MAX98927_Boost_Control_0_BST_VOUT_Mask = (0x1f << 0),
+ MAX98927_Boost_Control_0_EXT_PVDD_EN = (0x1 << 7),
+
+ /* Boost Control 3 (Address 0x0041)*/
+ MAX98927_Boost_Control_3 = 0x0041,
+ MAX98927_Boost_Control_3_BST_SKIPLOAD_Mask = (0x3 << 0),
+ MAX98927_Boost_Control_3_BST_SKIPLOAD_01 = (0x0 << 0),
+ MAX98927_Boost_Control_3_BST_SKIPLOAD_11 = (0x2 << 0),
+ MAX98927_Boost_Control_3_BST_PHASE_Mask = (0x7 << 2),
+ MAX98927_Boost_Control_3_BST_PHASE_001 = (0x0 << 2),
+ MAX98927_Boost_Control_3_BST_PHASE_011 = (0x2 << 2),
+ MAX98927_Boost_Control_3_BST_PHASE_ = (0x1 << 2),
+ MAX98927_Boost_Control_3_BST_SLOWSTART = (0x1 << 5),
+
+ /* Boost Control 1 (Address 0x0042)*/
+ MAX98927_Boost_Control_1 = 0x0042,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Boost_Control_1_BST_ILIM_Mask = (0x3f << 0),
+
+ /* Meas ADC Config (Address 0x0043)*/
+ MAX98927_Meas_ADC_Config = 0x0043,
+ MAX98927_Meas_ADC_Config_MEAS_ADC_CH0_EN = (0x1 << 0),
+ MAX98927_Meas_ADC_Config_MEAS_ADC_CH1_EN = (0x1 << 1),
+ MAX98927_Meas_ADC_Config_MEAS_ADC_CH2_EN = (0x1 << 2),
+
+ /* Meas ADC Base Divide MSByte (Address 0x0044)*/
+ MAX98927_Meas_ADC_Base_Divide_MSByte = 0x0044,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Meas_ADC_Base_Divide_MSByte_MEAS_ADC_BASE_DIV_Mask
+ = (0xff << 0),
+
+ /* Meas ADC Base Divide LSByte (Address 0x0045)*/
+ MAX98927_Meas_ADC_Base_Divide_LSByte = 0x0045,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Meas_ADC_Base_Divide_LSByte_MEAS_ADC_BASE_DIV_Mask
+ = (0xff << 0),
+
+ /* Meas ADC Chan 0 Divide (Address 0x0046)*/
+ MAX98927_Meas_ADC_Chan_0_Divide = 0x0046,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Meas_ADC_Chan_0_Divide_MEAS_ADC_CH0_DIV_Mask = (0xff << 0),
+
+ /* Meas ADC Chan 1 Divide (Address 0x0047)*/
+ MAX98927_Meas_ADC_Chan_1_Divide = 0x0047,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Meas_ADC_Chan_1_Divide_MEAS_ADC_CH1_DIV_Mask = (0xff << 0),
+
+ /* Meas ADC Chan 2 Divide (Address 0x0048)*/
+ MAX98927_Meas_ADC_Chan_2_Divide = 0x0048,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Meas_ADC_Chan_2_Divide_MEAS_ADC_CH2_DIV_Mask = (0xff << 0),
+
+ /* Meas ADC Chan 0 Filt Config (Address 0x0049)*/
+ MAX98927_Meas_ADC_Chan_0_Filt_Config = 0x0049,
+ MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_Mask
+ = (0x7 << 0),
+ MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_001
+ = (0x0 << 0),
+ MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_011
+ = (0x2 << 0),
+ MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_AVG_101
+ = (0x4 << 0),
+ MAX98927_Meas_ADC_Chan_0_Filt_Config_MEAS_ADC_CH0_FILT_EN
+ = (0x1 << 3),
+
+ /* Meas ADC Chan 1 Filt Config (Address 0x004a)*/
+ MAX98927_Meas_ADC_Chan_1_Filt_Config = 0x004a,
+ MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_Mask
+ = (0x7 << 0),
+ MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_001
+ = (0x0 << 0),
+ MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_011
+ = (0x2 << 0),
+ MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_AVG_101
+ = (0x4 << 0),
+ MAX98927_Meas_ADC_Chan_1_Filt_Config_MEAS_ADC_CH1_FILT_EN
+ = (0x1 << 3),
+
+ /* Meas ADC Chan 2 Filt Config (Address 0x004b)*/
+ MAX98927_Meas_ADC_Chan_2_Filt_Config = 0x004b,
+ MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_Mask
+ = (0x7 << 0),
+ MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_001
+ = (0x0 << 0),
+ MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_011
+ = (0x2 << 0),
+ MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_AVG_101
+ = (0x4 << 0),
+ MAX98927_Meas_ADC_Chan_2_Filt_Config_MEAS_ADC_CH2_FILT_EN
+ = (0x1 << 3),
+
+ /* Meas ADC Chan 0 Readback (Address 0x004c)*/
+ MAX98927_Meas_ADC_Chan_0_Readback = 0x004c,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Meas_ADC_Chan_0_Readback_MEAS_ADC_CH0_DATA_Mask
+ = (0xff << 0),
+
+ /* Meas ADC Chan 1 Readback (Address 0x004d)*/
+ MAX98927_Meas_ADC_Chan_1_Readback = 0x004d,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Meas_ADC_Chan_1_Readback_MEAS_ADC_CH1_DATA_Mask
+ = (0xff << 0),
+
+ /* Meas ADC Chan 2 Readback (Address 0x004e)*/
+ MAX98927_Meas_ADC_Chan_2_Readback = 0x004e,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Meas_ADC_Chan_2_Readback_MEAS_ADC_CH2_DATA_Mask
+ = (0xff << 0),
+
+ /* Brownout status (Address 0x0051)*/
+ MAX98927_Brownout_status = 0x0051,
+ MAX98927_Brownout_status_BDE_STATE_Mask = (0xf << 0),
+
+ /* Brownout enables (Address 0x0052)*/
+ MAX98927_Brownout_enables = 0x0052,
+ MAX98927_Brownout_enables_BDE_EN = (0x1 << 0),
+ MAX98927_Brownout_enables_BDE_AMP_EN = (0x1 << 1),
+ MAX98927_Brownout_enables_AMP_DSP_EN = (0x1 << 2),
+
+ /* Brownout level infinite hold (Address 0x0053)*/
+ MAX98927_Brownout_level_infinite_hold = 0x0053,
+ MAX98927_Brownout_level_infinite_hold_BDE_L8_INF_HLD = (0x1 << 1),
+
+ /* Brownout level infinite hold clear (Address 0x0054)*/
+ MAX98927_Brownout_level_infinite_hold_clear = 0x0054,
+ MAX98927_Brownout_level_infinite_hold_clear_BDE_L8_HLD_RLS
+ = (0x1 << 1),
+
+ /* Brownout level hold (Address 0x0055)*/
+ MAX98927_Brownout_level_hold = 0x0055,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout_level_hold_BDE_HLD_Mask = (0xff << 0),
+
+ /* Brownout level 1 threshold (Address 0x0056)*/
+ MAX98927_Brownout__level_1_threshold = 0x0056,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_1_threshold_BDE_L1_VTHRESH_Mask = (0xff << 0),
+
+ /* Brownout level 2 threshold (Address 0x0057)*/
+ MAX98927_Brownout__level_2_threshold = 0x0057,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_2_threshold_BDE_L2_VTHRESH_Mask = (0xff << 0),
+
+ /* Brownout level 3 threshold (Address 0x0058)*/
+ MAX98927_Brownout__level_3_threshold = 0x0058,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_3_threshold_BDE_L3_VTHRESH_Mask = (0xff << 0),
+
+ /* Brownout level 4 threshold (Address 0x0059)*/
+ MAX98927_Brownout__level_4_threshold = 0x0059,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_4_threshold_BDE_L4_VTHRESH_Mask = (0xff << 0),
+
+ /* Brownout level 5 threshold (Address 0x005a)*/
+ MAX98927_Brownout__level_5_threshold = 0x005a,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_5_threshold_BDE_L5_VTHRESH_Mask = (0xff << 0),
+
+ /* Brownout level 6 threshold (Address 0x005b)*/
+ MAX98927_Brownout__level_6_threshold = 0x005b,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_6_threshold_BDE_L6_VTHRESH_Mask = (0xff << 0),
+
+ /* Brownout level 7 threshold (Address 0x005c)*/
+ MAX98927_Brownout__level_7_threshold = 0x005c,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_7_threshold_BDE_L7_VTHRESH_Mask = (0xff << 0),
+
+ /* Brownout level 8 threshold (Address 0x005d)*/
+ MAX98927_Brownout__level_8_threshold = 0x005d,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_8_threshold_BDE_L8_VTHRESH_Mask = (0xff << 0),
+
+ /* Brownout threshold hysterysis (Address 0x005e)*/
+ MAX98927_Brownout_threshold_hysterysis = 0x005e,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout_threshold_hysterysis_BDE_VTHRESH_HYST_Mask
+ = (0xff << 0),
+ /* Brownout AMP limiter attack/release (Address 0x005f)*/
+ MAX98927_Brownout_AMP_limiter_attack_release = 0x005f,
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_Mask
+ = (0xf << 0),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0001
+ = (0x0 << 0),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0011
+ = (0x2 << 0),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0101
+ = (0x4 << 0),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_0111
+ = (0x6 << 0),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1001
+ = (0x8 << 0),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1011
+ = (0xa << 0),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1101
+ = (0xc << 0),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_RLS_1111
+ = (0xe << 0),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_Mask
+ = (0xf << 4),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0001
+ = (0x0 << 4),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0011
+ = (0x2 << 4),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0101
+ = (0x4 << 4),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_0111
+ = (0x6 << 4),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1001
+ = (0x8 << 4),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1011
+ = (0xa << 4),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1101
+ = (0xc << 4),
+ MAX98927_Brownout_AMP_limiter_attack_release_AMP_LIM_ATK_1111
+ = (0xe << 4),
+
+ /* Brownout AMP gain attack/release (Address 0x0060)*/
+ MAX98927_Brownout_AMP_gain_attack_release = 0x0060,
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_Mask
+ = (0xf << 0),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0001
+ = (0x0 << 0),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0011
+ = (0x2 << 0),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0101
+ = (0x4 << 0),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_0111
+ = (0x6 << 0),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1001
+ = (0x8 << 0),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1011
+ = (0xa << 0),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1101
+ = (0xc << 0),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_RLS_1111
+ = (0xe << 0),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_Mask
+ = (0xf << 4),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0001
+ = (0x0 << 4),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0011
+ = (0x2 << 4),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0101
+ = (0x4 << 4),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_0111
+ = (0x6 << 4),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1001
+ = (0x8 << 4),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1011
+ = (0xa << 4),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1101
+ = (0xc << 4),
+ MAX98927_Brownout_AMP_gain_attack_release_AMP_GAIN_ATK_1111
+ = (0xe << 4),
+
+ /* Brownout AMP1 clip mode (Address 0x0061)*/
+ MAX98927_Brownout_AMP1_clip_mode = 0x0061,
+ MAX98927_Brownout_AMP1_clip_mode_AMP_CLIP_MODE = (0x1 << 0),
+
+ /* Brownout level 1 current limit (Address 0x0062)*/
+ MAX98927_Brownout__level_1_current_limit = 0x0062,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_1_current_limit_BDE_L1_ILIM_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 1 amp 1 control 1 (Address 0x0063)*/
+ MAX98927_Brownout__level_1_amp_1_control_1 = 0x0063,
+ MAX98927_Brownout__level_1_amp_1_control_1_BDE_L1_AMP1_LIM_Mask
+ = (0xf << 0),
+
+ /* Brownout level 1 amp 1 control 2 (Address 0x0064)*/
+ MAX98927_Brownout__level_1_amp_1_control_2 = 0x0064,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_1_amp_1_control_2_BDE_L1_AMP1_CLIP_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 1 amp 1 control 3 (Address 0x0065)*/
+ MAX98927_Brownout__level_1_amp_1_control_3 = 0x0065,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_1_amp_1_control_3_BDE_L1_AMP1_GAIN_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 2 current limit (Address 0x0066)*/
+ MAX98927_Brownout__level_2_current_limit = 0x0066,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_2_current_limit_BDE_L2_ILIM_Mask = (0x3f << 0),
+
+ /* Brownout level 2 amp 1 control 1 (Address 0x0067)*/
+ MAX98927_Brownout__level_2_amp_1_control_1 = 0x0067,
+ MAX98927_Brownout__level_2_amp_1_control_1_BDE_L2_AMP1_LIM_Mask
+ = (0xf << 0),
+
+ /* Brownout level 2 amp 1 control 2 (Address 0x0068)*/
+ MAX98927_Brownout__level_2_amp_1_control_2 = 0x0068,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_2_amp_1_control_2_BDE_L2_AMP1_CLIP_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 2 amp 1 control 3 (Address 0x0069)*/
+ MAX98927_Brownout__level_2_amp_1_control_3 = 0x0069,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_2_amp_1_control_3_BDE_L2_AMP1_GAIN_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 3 current limit (Address 0x006a)*/
+ MAX98927_Brownout__level_3_current_limit = 0x006a,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_3_current_limit_BDE_L3_ILIM_Mask = (0x3f << 0),
+
+ /* Brownout level 3 amp 1 control 1 (Address 0x006b)*/
+ MAX98927_Brownout__level_3_amp_1_control_1 = 0x006b,
+ MAX98927_Brownout__level_3_amp_1_control_1_BDE_L3_AMP1_LIM_Mask
+ = (0xf << 0),
+
+ /* Brownout level 3 amp 1 control 2 (Address 0x006c)*/
+ MAX98927_Brownout__level_3_amp_1_control_2 = 0x006c,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_3_amp_1_control_2_BDE_L3_AMP1_CLIP_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 3 amp 1 control 3 (Address 0x006d)*/
+ MAX98927_Brownout__level_3_amp_1_control_3 = 0x006d,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_3_amp_1_control_3_BDE_L3_AMP1_GAIN_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 4 current limit (Address 0x006e)*/
+ MAX98927_Brownout__level_4_current_limit = 0x006e,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_4_current_limit_BDE_L4_ILIM_Mask = (0x3f << 0),
+
+ /* Brownout level 4 amp 1 control 1 (Address 0x006f)*/
+ MAX98927_Brownout__level_4_amp_1_control_1 = 0x006f,
+ MAX98927_Brownout__level_4_amp_1_control_1_BDE_L4_AMP1_LIM_Mask
+ = (0xf << 0),
+
+ /* Brownout level 4 amp 1 control 2 (Address 0x0070)*/
+ MAX98927_Brownout__level_4_amp_1_control_2 = 0x0070,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_4_amp_1_control_2_BDE_L4_AMP1_CLIP_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 4 amp 1 control 3 (Address 0x0071)*/
+ MAX98927_Brownout__level_4_amp_1_control_3 = 0x0071,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_4_amp_1_control_3_BDE_L4_AMP1_GAIN_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 5 current limit (Address 0x0072)*/
+ MAX98927_Brownout__level_5_current_limit = 0x0072,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_5_current_limit_BDE_L5_ILIM_Mask = (0x3f << 0),
+
+ /* Brownout level 5 amp 1 control 1 (Address 0x0073)*/
+ MAX98927_Brownout__level_5_amp_1_control_1 = 0x0073,
+ MAX98927_Brownout__level_5_amp_1_control_1_BDE_L5_AMP1_LIM_Mask
+ = (0xf << 0),
+
+ /* Brownout level 5 amp 1 control 2 (Address 0x0074)*/
+ MAX98927_Brownout__level_5_amp_1_control_2 = 0x0074,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_5_amp_1_control_2_BDE_L5_AMP1_CLIP_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 5 amp 1 control 3 (Address 0x0075)*/
+ MAX98927_Brownout__level_5_amp_1_control_3 = 0x0075,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_5_amp_1_control_3_BDE_L5_AMP1_GAIN_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 6 current limit (Address 0x0076)*/
+ MAX98927_Brownout__level_6_current_limit = 0x0076,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_6_current_limit_BDE_L6_ILIM_Mask = (0x3f << 0),
+
+ /* Brownout level 6 amp 1 control 1 (Address 0x0077)*/
+ MAX98927_Brownout__level_6_amp_1_control_1 = 0x0077,
+ MAX98927_Brownout__level_6_amp_1_control_1_BDE_L6_AMP1_LIM_Mask
+ = (0xf << 0),
+
+ /* Brownout level 6 amp 1 control 2 (Address 0x0078)*/
+ MAX98927_Brownout__level_6_amp_1_control_2 = 0x0078,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_6_amp_1_control_2_BDE_L6_AMP1_CLIP_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 6 amp 1 control 3 (Address 0x0079)*/
+ MAX98927_Brownout__level_6_amp_1_control_3 = 0x0079,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_6_amp_1_control_3_BDE_L6_AMP1_GAIN_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 7 current limit (Address 0x007a)*/
+ MAX98927_Brownout__level_7_current_limit = 0x007a,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_7_current_limit_BDE_L7_ILIM_Mask = (0x3f << 0),
+
+ /* Brownout level 7 amp 1 control 1 (Address 0x007b)*/
+ MAX98927_Brownout__level_7_amp_1_control_1 = 0x007b,
+ MAX98927_Brownout__level_7_amp_1_control_1_BDE_L7_AMP1_LIM_Mask
+ = (0xf << 0),
+
+ /* Brownout level 7 amp 1 control 2 (Address 0x007c)*/
+ MAX98927_Brownout__level_7_amp_1_control_2 = 0x007c,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_7_amp_1_control_2_BDE_L7_AMP1_CLIP_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 7 amp 1 control 3 (Address 0x007d)*/
+ MAX98927_Brownout__level_7_amp_1_control_3 = 0x007d,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_7_amp_1_control_3_BDE_L7_AMP1_GAIN_Mask
+ = (0x3f << 0),
+
+ /* Brownout level 8 current limit (Address 0x007e)*/
+ MAX98927_Brownout__level_8_current_limit = 0x007e,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_8_current_limit_BDE_L8_ILIM_Mask = (0x3f << 0),
+
+ /* Brownout level 8 amp 1 control 1 (Address 0x007f)*/
+ MAX98927_Brownout__level_8_amp_1_control_1 = 0x007f,
+ MAX98927_Brownout__level_8_amp_1_control_1_BDE_L8_AMP1_LIM_Mask
+ = (0xf << 0),
+
+ /* Brownout level 8 amp 1 control 2 (Address 0x0080)*/
+ MAX98927_Brownout__lvl_8_amp_1_control_2 = 0x0080,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__lvl_8_amp_1_control_2_BDE_L8_AMP1_CLIP_Mask
+ = (0x3f << 0),
+ MAX98927_Brownout__lvl_8_amp_1_control_2_BDE_L8_AMP1_MUTE
+ = (0x1 << 7),
+
+ /* Brownout level 8 amp 1 control 3 (Address 0x0081)*/
+ MAX98927_Brownout__level_8_amp_1_control_3 = 0x0081,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Brownout__level_8_amp_1_control_3_BDE_L8_AMP1_GAIN_Mask
+ = (0x3f << 0),
+
+ /* Env Tracker Vout Headroom (Address 0x0082)*/
+ MAX98927_Env_Tracker_Vout_Headroom = 0x0082,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Env_Tracker_Vout_Head_ENV_TRACKER_BST_VOUT_HEADROOM_Mask
+ = (0x1f << 0),
+
+ /* Env Tracker Boost Vout Delay (Address 0x0083)*/
+ MAX98927_Env_Tracker_Boost_Vout_Delay = 0x0083,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Env_Tracker_Boost_V_Delay_ENV_TRACKER_BST_VOUT_DELAY_Mask
+ = (0x1f << 0),
+ MAX98927_Env_Tracker_Boost_Vout_Delay_ENV_TRACKER_BDE_MODE
+ = (0x1 << 7),
+
+ /* Env Tracker Release Rate (Address 0x0084)*/
+ MAX98927_Env_Tracker_Release_Rate = 0x0084,
+ MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_Mask
+ = (0x7 << 0),
+ MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_001
+ = (0x0 << 0),
+ MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_011
+ = (0x2 << 0),
+ MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_101
+ = (0x4 << 0),
+ MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_111
+ = (0x6 << 0),
+ MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_PEAK_DET_LPF_BYP_EN
+ = (0x1 << 3),
+ MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_SCALE_Mask
+ = (0x3 << 4),
+ MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_SCALE_01
+ = (0x0 << 4),
+ MAX98927_Env_Tracker_Release_Rate_ENV_TRACKER_RLS_RATE_SCALE_11
+ = (0x2 << 4),
+
+ /* Env Tracker Hold Rate (Address 0x0085)*/
+ MAX98927_Env_Tracker_Hold_Rate = 0x0085,
+ MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_Mask
+ = (0x7 << 0),
+ MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_001
+ = (0x0 << 0),
+ MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_011
+ = (0x2 << 0),
+ MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_101
+ = (0x4 << 0),
+ MAX98927_Env_Tracker_Hold_Rate_ENV_TRACKER_HOLD_RATE_111
+ = (0x6 << 0),
+
+ /* Env Tracker Control (Address 0x0086)*/
+ MAX98927_Env_Tracker_Control = 0x0086,
+ MAX98927_Env_Tracker_Control_ENV_TRACKER_EN = (0x1 << 0),
+
+ /* Env Tracker Boost Vout ReadBack (Address 0x0087)*/
+ MAX98927_Env_Tracker__Boost_Vout_ReadBack = 0x0087,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Env_Tracker__Boost_Vout_RB_ENV_TRACKER_BST_VOUT_RD_Mask
+ = (0x1f << 0),
+
+ /* Advanced Settings (Address 0x0089)*/
+ MAX98927_Advanced_Settings = 0x0089,
+ MAX98927_Advanced_Settings_DAC_HALF_FIR = (0x1 << 0),
+ MAX98927_Advanced_Settings_PDM_MOD_SEL = (0x1 << 1),
+ MAX98927_Advanced_Settings_ISOCH_EN = (0x1 << 2),
+
+ /* DAC Test 1 (Address 0x009f)*/
+ MAX98927_DAC_Test_1 = 0x009f,
+ MAX98927_DAC_Test_1_DAC_PCM_TIMING = (0x1 << 0),
+ MAX98927_DAC_Test_1_DAC_HALFI_AMP = (0x1 << 1),
+ MAX98927_DAC_Test_1_DAC_LONG_HOLD = (0x1 << 3),
+ MAX98927_DAC_Test_1_DAC_DISABLE_CHOP = (0x1 << 4),
+ MAX98927_DAC_Test_1_DAC_TM = (0x1 << 5),
+ MAX98927_DAC_Test_1_DAC_INVERT_DACCLK = (0x1 << 6),
+
+ /* Authentication key 0 (Address 0x00ea)*/
+ MAX98927_Authentication_key_0 = 0x00ea,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_key_0_AUTH_KEY_Mask = (0xff << 0),
+
+ /* Authentication key 1 (Address 0x00eb)*/
+ MAX98927_Authentication_key_1 = 0x00eb,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_key_1_AUTH_KEY_Mask = (0xff << 0),
+
+ /* Authentication key 2 (Address 0x00ec)*/
+ MAX98927_Authentication_key_2 = 0x00ec,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_key_2_AUTH_KEY_Mask = (0xff << 0),
+
+ /* Authentication key 3 (Address 0x00ed)*/
+ MAX98927_Authentication_key_3 = 0x00ed,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_key_3_AUTH_KEY_Mask = (0xff << 0),
+
+ /* Authentication enable (Address 0x00ee)*/
+ MAX98927_Authentication_enable = 0x00ee,
+ MAX98927_Authentication_enable_AUTH_EN = (0x1 << 0),
+
+ /* Authentication result 0 (Address 0x00ef)*/
+ MAX98927_Authentication_result_0 = 0x00ef,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_0_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 1 (Address 0x00f0)*/
+ MAX98927_Authentication_result_1 = 0x00f0,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_1_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 2 (Address 0x00f1)*/
+ MAX98927_Authentication_result_2 = 0x00f1,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_2_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 3 (Address 0x00f2)*/
+ MAX98927_Authentication_result_3 = 0x00f2,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_3_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 4 (Address 0x00f3)*/
+ MAX98927_Authentication_result_4 = 0x00f3,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_4_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 5 (Address 0x00f4)*/
+ MAX98927_Authentication_result_5 = 0x00f4,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_5_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 6 (Address 0x00f5)*/
+ MAX98927_Authentication_result_6 = 0x00f5,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_6_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 7 (Address 0x00f6)*/
+ MAX98927_Authentication_result_7 = 0x00f6,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_7_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 8 (Address 0x00f7)*/
+ MAX98927_Authentication_result_8 = 0x00f7,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_8_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 9 (Address 0x00f8)*/
+ MAX98927_Authentication_result_9 = 0x00f8,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_9_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 10 (Address 0x00f9)*/
+ MAX98927_Authentication_result_10 = 0x00f9,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_10_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 11 (Address 0x00fa)*/
+ MAX98927_Authentication_result_11 = 0x00fa,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_11_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 12 (Address 0x00fb)*/
+ MAX98927_Authentication_result_12 = 0x00fb,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_12_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 13 (Address 0x00fc)*/
+ MAX98927_Authentication_result_13 = 0x00fc,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_13_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 14 (Address 0x00fd)*/
+ MAX98927_Authentication_result_14 = 0x00fd,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_14_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Authentication result 15 (Address 0x00fe)*/
+ MAX98927_Authentication_result_15 = 0x00fe,
+ /*#BYHAND width >= 5:*/
+ MAX98927_Authentication_result_15_AUTH_RESULT_Mask = (0xff << 0),
+
+ /* Global Enable (Address 0x00ff)*/
+ MAX98927_Global_Enable = 0x00ff,
+ MAX98927_Global_Enable_EN = (0x1 << 0),
+ /* Software Reset (Address 0x0100)*/
+ MAX98927_Software_Reset = 0x0100,
+ MAX98927_Software_Reset_RST = (0x1 << 0),
+
+ /* REV ID (Address 0x01ff)*/
+ MAX98927_REV_ID = 0x01ff,
+ /*#BYHAND width >= 5:*/
+ MAX98927_REV_ID_REV_ID_Mask = (0xff << 0),
+} MAX98927Registers;
+
+struct max98927_reg_default {
+ unsigned int ch;
+ unsigned int reg;
+ unsigned int def;
+};
+struct max98927_priv {
+ struct regmap *regmap;
+ struct regmap *sub_regmap;
+ struct snd_soc_codec *codec;
+ struct max98927_pdata *pdata;
+ const uint32_t *regcfg;
+ uint32_t regcfg_sz;
+ unsigned int spk_gain;
+ unsigned int sysclk;
+ unsigned int v_l_slot;
+ unsigned int i_l_slot;
+ unsigned int v_r_slot;
+ unsigned int i_r_slot;
+ bool interleave_mode;
+ unsigned int ch_size;
+ unsigned int rate;
+ unsigned int iface;
+ unsigned int master;
+ unsigned int thres_hyste;
+ unsigned int level5_hold;
+ unsigned int level6_hold;
+ unsigned int level7_hold;
+ unsigned int level8_hold;
+ unsigned int amp_limit;
+ unsigned int amp_limit_rel;
+ unsigned int amp1_level;
+ unsigned int amp2_level;
+ unsigned int amp3_level;
+ unsigned int amp1_level8;
+ unsigned int amp2_level8;
+ unsigned int amp3_level8;
+ unsigned int amp1_level7;
+ unsigned int amp2_level7;
+ unsigned int amp3_level7;
+ unsigned int amp1_level6;
+ unsigned int amp2_level6;
+ unsigned int amp3_level6;
+ unsigned int amp1_level5;
+ unsigned int amp2_level5;
+ unsigned int amp3_level5;
+ unsigned int digital_gain;
+ unsigned int pdm_gain;
+ unsigned int level_hold;
+ struct i2c_client *sub_i2c;
+ int sub_reg;
+ int sub_bus;
+};
+
+#define MAX98927_GLOBAL_SHIFT 0
+#define M98927_DAI_MSEL_SHIFT 4
+#define M98927_DAI_BSEL_SHIFT 0
+#define M98927_DAI_BSEL_32 (2 << M98927_DAI_BSEL_SHIFT)
+#define M98927_DAI_BSEL_48 (3 << M98927_DAI_BSEL_SHIFT)
+#define M98927_DAI_BSEL_64 (4 << M98927_DAI_BSEL_SHIFT)
+#define M98927_DAI_MSEL_32 (2 << M98927_DAI_MSEL_SHIFT)
+#define M98927_DAI_MSEL_48 (3 << M98927_DAI_MSEL_SHIFT)
+#define M98927_DAI_MSEL_64 (4 << M98927_DAI_MSEL_SHIFT)
+#define MAX98927_Speaker_Gain_Width 3
+#define MAX98927_SPK_RMP_EN_SHIFT 4
+#define MAX98927_PDM_GAIN_SHIFT 4
+#define MAX98927_pdm_Gain_Width 3
+#define MAX98927_AMP_VOL_WIDTH 7
+#define MAX98927_AMP_VOL_LOCATION_SHIFT 7
+#define MAX98927_PDM_Rx_Enable_PDM_CH_SHIFT 3
+#define MAX98927_PCM_to_speaker_monomix_A_SHIFT 6
+#define MAX98927_PCM_Sample_rate_setup_2_DIG_IF_SR_48000 (0x8 << 4)
+#define MAX98927_PCM_FORMAT_DSP_A (0x3 << 3)
+#define MAX98927_DRE_Control_DRE_SHIFT 0x1
+#define MAX98927_PCM_Master_Mode_PCM_MCLK_RATE_SHIFT (2)
+#define MAX98927_Brownout_AMP_limiter_attack_release_shift 4
+#define MAX98927_BDE_DSP_SHIFT 2
+#define MAX98927_Speaker_Gain_SPK_PDM_GAIN_SHIFT (4)
+#define MAX98927_BDE_AMP_SHIFT (1)
+#define MAX98927_PCM_Tx_Ch_Sources_A_I_SHIFT (4)
+#endif
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH] fix platform_no_drv_owner.cocci warnings
2016-11-23 4:57 ` Ryan Lee
@ 2016-11-23 18:13 ` kbuild test robot
0 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2016-11-23 18:13 UTC (permalink / raw)
To: Ryan Lee
Cc: kbuild-all, lgirdwood, broonie, robh+dt, mark.rutland, perex,
tiwai, arnd, michael, oder_chiou, yesanishhere, jacob,
Damien.Horsley, bardliao, kuninori.morimoto.gx, petr, lars, nh6z,
ryans.lee, alsa-devel, devicetree, linux-kernel
sound/soc/codecs/max98927.c:941:3-8: No need to set .owner here. The core will do it.
Remove .owner field if calls are used which set it automatically
Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci
CC: Ryan Lee <RyanS.Lee@maximintegrated.com>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
max98927.c | 1 -
1 file changed, 1 deletion(-)
--- a/sound/soc/codecs/max98927.c
+++ b/sound/soc/codecs/max98927.c
@@ -938,7 +938,6 @@ MODULE_DEVICE_TABLE(of, max98927_of_matc
static struct i2c_driver max98927_i2c_driver = {
.driver = {
.name = "max98927",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(max98927_of_match),
.pm = NULL,
},
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v2 3/6] Add Advantech iManager HWmon driver
@ 2016-01-10 9:11 ` richard.dorsch
2016-01-10 10:25 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot
0 siblings, 1 reply; 39+ messages in thread
From: richard.dorsch @ 2016-01-10 9:11 UTC (permalink / raw)
To: linux-kernel
Cc: lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones,
jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch
From: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
Signed-off-by: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
---
Documentation/hwmon/imanager | 59 ++
drivers/hwmon/Kconfig | 12 +
drivers/hwmon/Makefile | 2 +
drivers/hwmon/imanager-ec-hwmon.c | 606 +++++++++++++++++++++
drivers/hwmon/imanager-hwmon.c | 1058 ++++++++++++++++++++++++++++++++++++
include/linux/mfd/imanager/hwmon.h | 120 ++++
6 files changed, 1857 insertions(+)
create mode 100644 Documentation/hwmon/imanager
create mode 100644 drivers/hwmon/imanager-ec-hwmon.c
create mode 100644 drivers/hwmon/imanager-hwmon.c
create mode 100644 include/linux/mfd/imanager/hwmon.h
diff --git a/Documentation/hwmon/imanager b/Documentation/hwmon/imanager
new file mode 100644
index 0000000..0705cf9
--- /dev/null
+++ b/Documentation/hwmon/imanager
@@ -0,0 +1,59 @@
+Kernel driver imanager_hwmon
+============================
+
+This platform driver provides support for iManager Hardware Monitoring
+and FAN control.
+
+This driver depends on imanager (mfd).
+
+Description
+-----------
+
+This driver provides support for the Advantech iManager Hardware Monitoring EC.
+
+The Advantech iManager supports up to 3 fan rotation speed sensors,
+3 temperature monitoring sources and up to 5 voltage sensors, VID, alarms and
+a automatic fan regulation strategy (as well as manual fan control mode).
+
+Temperatures are measured in degrees Celsius and measurement resolution is
+1 degC. An Alarm is triggered when the temperature gets higher than the high
+limit; it stays on until the temperature falls below the high limit.
+
+Fan rotation speeds are reported in RPM (rotations per minute). An alarm is
+triggered if the rotation speed has dropped below a programmable limit. No fan
+speed divider support available.
+
+Voltage sensors (also known as IN sensors) report their values in millivolts.
+An alarm is triggered if the voltage has crossed a programmable minimum
+or maximum limit.
+
+The driver supports automatic fan control mode known as Thermal Cruise.
+In this mode, the firmware attempts to keep the measured temperature in a
+predefined temperature range. If the temperature goes out of range, fan
+is driven slower/faster to reach the predefined range again.
+
+The mode works for fan1-fan3.
+
+sysfs attributes
+----------------
+
+pwm[1-3] - this file stores PWM duty cycle or DC value (fan speed) in range:
+ 0 (lowest speed) to 255 (full)
+
+pwm[1-3]_enable - this file controls mode of fan/temperature control:
+ * 0 Fan control disabled (fans set to maximum speed)
+ * 1 Manual mode, write to pwm[1-3] any value 0-255
+ * 2 "Fan Speed Cruise" mode
+
+pwm[1-3]_mode - controls if output is PWM or DC level
+ * 0 DC output
+ * 1 PWM output
+
+Speed Cruise mode (2)
+---------------------
+
+This mode tries to keep the fan speed constant within min/max speed.
+
+fan[1-3]_min - Minimum fan speed
+fan[1-3]_max - Maximum fan speed
+
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 80a73bf..776bb8a 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -613,6 +613,18 @@ config SENSORS_CORETEMP
sensor inside your CPU. Most of the family 6 CPUs
are supported. Check Documentation/hwmon/coretemp for details.
+config SENSORS_IMANAGER
+ tristate "Advantech iManager Hardware Monitoring"
+ depends on MFD_IMANAGER
+ select HWMON_VID
+ help
+ This enables support for Advantech iManager hardware monitoring
+ of some Advantech SOM, MIO, AIMB, and PCM modules/boards.
+ Requires mfd-core and imanager-core to function properly.
+
+ This driver can also be built as a module. If so, the module
+ will be called imanager_hwmon.
+
config SENSORS_IT87
tristate "ITE IT87xx and compatibles"
depends on !PPC
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 12a3239..53752bc 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -76,6 +76,8 @@ obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o
obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o
obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o
+imanager_hwmon-objs := imanager-hwmon.o imanager-ec-hwmon.o
+obj-$(CONFIG_SENSORS_IMANAGER) += imanager_hwmon.o
obj-$(CONFIG_SENSORS_INA209) += ina209.o
obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
diff --git a/drivers/hwmon/imanager-ec-hwmon.c b/drivers/hwmon/imanager-ec-hwmon.c
new file mode 100644
index 0000000..1920835
--- /dev/null
+++ b/drivers/hwmon/imanager-ec-hwmon.c
@@ -0,0 +1,606 @@
+/*
+ * Advantech iManager Hardware Monitoring core
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/byteorder/generic.h>
+#include <linux/swab.h>
+#include <linux/mfd/imanager/ec.h>
+#include <linux/mfd/imanager/hwmon.h>
+
+#define HWM_STATUS_UNDEFINED_ITEM 2UL
+#define HWM_STATUS_UNDEFINED_DID 3UL
+#define HWM_STATUS_UNDEFINED_HWPIN 4UL
+
+/*
+ * FAN defs
+ */
+
+struct fan_dev_config {
+ u8 did,
+ hwpin,
+ tachoid,
+ status,
+ control,
+ temp_max,
+ temp_min,
+ temp_stop,
+ pwm_max,
+ pwm_min;
+ u16 rpm_max;
+ u16 rpm_min;
+ u8 debounce; /* debounce time, not used */
+ u8 temp; /* Current Thermal Zone Temperature */
+ u16 rpm_target; /* RPM Target Speed, not used */
+};
+
+struct fan_status {
+ u32 sysctl : 1, /* System Control flag */
+ tacho : 1, /* FAN tacho source defined */
+ pulse : 1, /* FAN pulse type defined */
+ thermal : 1, /* Thermal zone init */
+ i2clink : 1, /* I2C protocol fail flag (thermal sensor) */
+ dnc : 1, /* don't care */
+ mode : 2; /* FAN Control mode */
+};
+
+struct fan_alert_limit {
+ u16 fan0_min,
+ fan0_max,
+ fan1_min,
+ fan1_max,
+ fan2_min,
+ fan2_max;
+};
+
+struct fan_alert_flag {
+ u32 fan0_min_alarm : 1,
+ fan0_max_alarm : 1,
+ fan1_min_alarm : 1,
+ fan1_max_alarm : 1,
+ fan2_min_alarm : 1,
+ fan2_max_alarm : 1,
+ dnc : 2; /* don't care */
+};
+
+/*----------------------------------------------------*
+ * FAN Control bit field *
+ * enable: 0:Disabled, 1:Enabled *
+ * type: 0:PWM, 1:RPM *
+ * pulse: 0:Undefined 1:2 Pulse 2:4 Pulse *
+ * tacho: 1:CPU FAN, 2:SYS1 FAN, 3:SYS2 FAN *
+ * mode: 0:Off, 1:Full, 2:Manual, 3:Auto *
+ *- 7 6 ---- 5 4 --- 3 2 ----- 1 -------- 0 -------*
+ * MODE | TACHO | PULSE | TYPE | ENABLE *
+ *----------------------------------------------------*/
+struct fan_ctrl {
+ u32 enable : 1, /* SmartFAN control on/off */
+ type : 1, /* FAN control type [0, 1] PWM/RPM */
+ pulse : 2, /* FAN pulse [0..2] */
+ tacho : 2, /* FAN Tacho Input [1..3] */
+ mode : 2; /* off/full/manual/auto */
+};
+
+enum fan_dev_ctrl {
+ CTRL_STATE = 3,
+ OPMODE,
+ IDSENSOR,
+ ACTIVE,
+ CTRL_MODE,
+};
+
+enum fan_limit {
+ LIMIT_PWM,
+ LIMIT_RPM,
+ LIMIT_TEMP,
+};
+
+static const char * const fan_temp_label[] = {
+ "Temp CPU",
+ "Temp SYS1",
+ "Temp SYS2",
+ NULL,
+};
+
+static const struct imanager_hwmon_device *hwmon;
+
+static inline int hwm_get_adc_value(u8 did)
+{
+ return imanager_read_word(EC_CMD_HWP_RD, did);
+}
+
+static inline int hwm_get_rpm_value(u8 did)
+{
+ return imanager_read_word(EC_CMD_HWP_RD, did);
+}
+
+static inline int hwm_get_pwm_value(u8 did)
+{
+ return imanager_read_byte(EC_CMD_HWP_RD, did);
+}
+
+static inline int hwm_set_pwm_value(u8 did, u8 val)
+{
+ return imanager_write_byte(EC_CMD_HWP_WR, did, val);
+}
+
+static int hwm_read_fan_config(int num, struct fan_dev_config *cfg)
+{
+ int ret;
+ struct ec_message msg = {
+ .rlen = 0xff, /* use alternative message body */
+ .wlen = 0,
+ };
+ struct fan_dev_config *_cfg = (struct fan_dev_config *)&msg.u.data;
+
+ if (WARN_ON(!cfg))
+ return -EINVAL;
+
+ memset(cfg, 0, sizeof(struct fan_dev_config));
+
+ ret = imanager_msg_read(EC_CMD_FAN_CTL_RD, num, &msg);
+ if (ret)
+ return ret;
+
+ if (!_cfg->did) {
+ pr_err("Invalid FAN%d device ID - possible firmware bug\n",
+ num);
+ return -ENODEV;
+ }
+
+ memcpy(cfg, &msg.u.data, sizeof(struct fan_dev_config));
+
+ return 0;
+}
+
+static int hwm_write_fan_config(int fnum, struct fan_dev_config *cfg)
+{
+ int ret;
+ struct ec_message msg = {
+ .rlen = 0,
+ .wlen = sizeof(struct fan_dev_config),
+ };
+
+ if (!cfg->did)
+ return -ENODEV;
+
+ msg.data = (u8 *)cfg;
+
+ ret = imanager_msg_write(EC_CMD_FAN_CTL_WR, fnum, &msg);
+ if (ret < 0)
+ return ret;
+
+ switch (ret) {
+ case 0:
+ break;
+ case HWM_STATUS_UNDEFINED_ITEM:
+ case HWM_STATUS_UNDEFINED_DID:
+ case HWM_STATUS_UNDEFINED_HWPIN:
+ return -EFAULT;
+ default:
+ pr_err("Unknown error status of fan%d (%d)\n", fnum, ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static inline void hwm_set_temp_limit(struct fan_dev_config *cfg,
+ const struct hwm_fan_temp_limit *temp)
+{
+ cfg->temp_stop = temp->stop;
+ cfg->temp_min = temp->min;
+ cfg->temp_max = temp->max;
+}
+
+static inline void hwm_set_pwm_limit(struct fan_dev_config *cfg,
+ const struct hwm_fan_limit *pwm)
+{
+ cfg->pwm_min = pwm->min;
+ cfg->pwm_max = pwm->max;
+}
+
+static inline void hwm_set_rpm_limit(struct fan_dev_config *cfg,
+ const struct hwm_fan_limit *rpm)
+{
+ cfg->rpm_min = swab16(rpm->min);
+ cfg->rpm_max = swab16(rpm->max);
+}
+
+static inline void hwm_set_limit(struct fan_dev_config *cfg,
+ const struct hwm_sensors_limit *limit)
+{
+ hwm_set_temp_limit(cfg, &limit->temp);
+ hwm_set_pwm_limit(cfg, &limit->pwm);
+ hwm_set_rpm_limit(cfg, &limit->rpm);
+}
+
+static int hwm_core_get_fan_alert_flag(struct fan_alert_flag *flag)
+{
+ int ret;
+ u8 *value = (u8 *)flag;
+
+ ret = imanager_acpiram_read_byte(EC_ACPIRAM_FAN_ALERT);
+ if (ret < 0)
+ return ret;
+
+ *value = ret;
+
+ return 0;
+}
+
+static int hwm_core_get_fan_alert_limit(int fnum,
+ struct hwm_smartfan *fan)
+{
+ int ret;
+ struct fan_alert_limit limit;
+ struct fan_alert_flag flag;
+
+ ret = imanager_acpiram_read_block(EC_ACPIRAM_FAN_SPEED_LIMIT,
+ (u8 *)&limit, sizeof(limit));
+ if (ret < 0)
+ return ret;
+
+ ret = hwm_core_get_fan_alert_flag(&flag);
+ if (ret < 0)
+ return ret;
+
+ switch (fnum) {
+ case FAN_CPU:
+ fan->alert.min = swab16(limit.fan0_min);
+ fan->alert.max = swab16(limit.fan0_max);
+ fan->alert.min_alarm = flag.fan0_min_alarm;
+ fan->alert.max_alarm = flag.fan0_max_alarm;
+ break;
+ case FAN_SYS1:
+ fan->alert.min = swab16(limit.fan1_min);
+ fan->alert.max = swab16(limit.fan1_max);
+ fan->alert.min_alarm = flag.fan1_min_alarm;
+ fan->alert.max_alarm = flag.fan1_max_alarm;
+ break;
+ case FAN_SYS2:
+ fan->alert.min = swab16(limit.fan2_min);
+ fan->alert.max = swab16(limit.fan2_max);
+ fan->alert.min_alarm = flag.fan2_min_alarm;
+ fan->alert.max_alarm = flag.fan2_max_alarm;
+ break;
+ default:
+ pr_err("Unknown FAN ID %d\n", fnum);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hwm_core_fan_set_alert_limit(int fnum,
+ struct hwm_fan_alert *alert)
+{
+ int ret;
+ struct fan_alert_limit limit;
+
+ ret = imanager_acpiram_read_block(EC_ACPIRAM_FAN_SPEED_LIMIT,
+ (u8 *)&limit, sizeof(limit));
+ if (ret < 0)
+ return ret;
+
+ switch (fnum) {
+ case FAN_CPU:
+ limit.fan0_min = swab16(alert->min);
+ limit.fan0_max = swab16(alert->max);
+ break;
+ case FAN_SYS1:
+ limit.fan1_min = swab16(alert->min);
+ limit.fan1_max = swab16(alert->max);
+ break;
+ case FAN_SYS2:
+ limit.fan2_min = swab16(alert->min);
+ limit.fan2_max = swab16(alert->max);
+ break;
+ default:
+ pr_err("Unknown FAN ID %d\n", fnum);
+ return -EINVAL;
+ }
+
+ return imanager_acpiram_write_block(EC_ACPIRAM_FAN_SPEED_LIMIT,
+ (u8 *)&limit, sizeof(limit));
+}
+
+/* HWM CORE API */
+
+const char *hwm_core_adc_get_label(int num)
+{
+ if (WARN_ON(num >= hwmon->adc.num))
+ return NULL;
+
+ return hwmon->adc.attr[num].label;
+}
+
+const char *hwm_core_fan_get_label(int num)
+{
+ if (WARN_ON(num >= hwmon->fan.num))
+ return NULL;
+
+ return hwmon->fan.attr[num].label;
+}
+
+const char *hwm_core_fan_get_temp_label(int num)
+{
+ if (WARN_ON(num >= hwmon->fan.num))
+ return NULL;
+
+ return fan_temp_label[num];
+}
+
+int hwm_core_adc_is_available(int num)
+{
+ if (num >= EC_HWM_MAX_ADC)
+ return -EINVAL;
+
+ return hwmon->adc.attr[num].did ? 0 : -ENODEV;
+}
+
+int hwm_core_adc_get_value(int num, struct hwm_voltage *volt)
+{
+ int ret;
+
+ volt->valid = false;
+
+ ret = hwm_core_adc_is_available(num);
+ if (ret < 0)
+ return ret;
+
+ ret = hwm_get_adc_value(hwmon->adc.attr[num].did);
+ if (ret < 0)
+ return ret;
+
+ volt->value = ret * hwmon->adc.attr[num].scale;
+ volt->valid = true;
+
+ return 0;
+}
+
+int hwm_core_fan_get_ctrl(int num, struct hwm_smartfan *fan)
+{
+ int ret;
+ struct fan_dev_config cfg;
+ struct fan_ctrl *ctrl = (struct fan_ctrl *)&cfg.control;
+
+ if (WARN_ON((num >= HWM_MAX_FAN) || !fan))
+ return -EINVAL;
+
+ memset(fan, 0, sizeof(struct hwm_smartfan));
+
+ ret = hwm_read_fan_config(num, &cfg);
+ if (ret < 0)
+ return ret;
+
+ fan->pulse = ctrl->pulse;
+ fan->type = ctrl->type;
+
+ /*
+ * It seems that fan->mode does not always report the correct
+ * FAN mode so the only way of reporting the current FAN mode
+ * is to read back ctrl->mode.
+ */
+ fan->mode = ctrl->mode;
+
+ ret = hwm_get_rpm_value(cfg.tachoid);
+ if (ret < 0) {
+ pr_err("Failed to read FAN speed\n");
+ return ret;
+ }
+
+ fan->speed = ret;
+
+ ret = hwm_get_pwm_value(hwmon->fan.attr[num].did);
+ if (ret < 0) {
+ pr_err("Failed to read FAN%d PWM\n", num);
+ return ret;
+ }
+
+ fan->pwm = ret;
+
+ fan->alarm = (fan->pwm && !fan->speed) ? 1 : 0;
+
+ fan->limit.temp.min = cfg.temp_min;
+ fan->limit.temp.max = cfg.temp_max;
+ fan->limit.temp.stop = cfg.temp_stop;
+ fan->limit.pwm.min = cfg.pwm_min;
+ fan->limit.pwm.max = cfg.pwm_max;
+ fan->limit.rpm.min = swab16(cfg.rpm_min);
+ fan->limit.rpm.max = swab16(cfg.rpm_max);
+
+ ret = hwm_core_get_fan_alert_limit(num, fan);
+ if (ret)
+ return ret;
+
+ fan->valid = true;
+
+ return 0;
+}
+
+int hwm_core_fan_set_ctrl(int num, int fmode, int ftype, int pwm, int pulse,
+ struct hwm_sensors_limit *limit,
+ struct hwm_fan_alert *alert)
+{
+ int ret;
+ struct fan_dev_config cfg;
+ struct fan_ctrl *ctrl = (struct fan_ctrl *)&cfg.control;
+ struct hwm_sensors_limit _limit = { {0, 0, 0}, {0, 0}, {0, 0} };
+
+ if (WARN_ON(num >= HWM_MAX_FAN))
+ return -EINVAL;
+
+ ret = hwm_read_fan_config(num, &cfg);
+ if (ret < 0) {
+ pr_err("Failed while reading FAN %s config\n",
+ hwmon->fan.attr[num].label);
+ return ret;
+ }
+
+ if (!limit)
+ limit = &_limit;
+
+ switch (fmode) {
+ case MODE_OFF:
+ ctrl->type = CTRL_PWM;
+ ctrl->mode = MODE_OFF;
+ break;
+ case MODE_FULL:
+ ctrl->type = CTRL_PWM;
+ ctrl->mode = MODE_FULL;
+ break;
+ case MODE_MANUAL:
+ ctrl->type = CTRL_PWM;
+ ctrl->mode = MODE_MANUAL;
+ ret = hwm_set_pwm_value(hwmon->fan.attr[num].did, pwm);
+ if (ret < 0)
+ return ret;
+ break;
+ case MODE_AUTO:
+ switch (ftype) {
+ case CTRL_PWM:
+ limit->rpm.min = 0;
+ limit->rpm.max = 0;
+ ctrl->type = CTRL_PWM;
+ break;
+ case CTRL_RPM:
+ limit->pwm.min = 0;
+ limit->pwm.max = 0;
+ ctrl->type = CTRL_RPM;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ctrl->mode = MODE_AUTO;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ hwm_set_limit(&cfg, limit);
+
+ ctrl->pulse = (pulse && (pulse < 3)) ? pulse : 0;
+ ctrl->enable = 1;
+
+ ret = hwm_write_fan_config(num, &cfg);
+ if (ret < 0)
+ return ret;
+
+ if (alert)
+ return hwm_core_fan_set_alert_limit(num, alert);
+
+ return 0;
+}
+
+int hwm_core_fan_is_available(int num)
+{
+ if (WARN_ON(num >= HWM_MAX_FAN))
+ return -EINVAL;
+
+ return hwmon->fan.active & (1 << num) &&
+ hwmon->fan.attr[num].did ? 0 : -ENODEV;
+}
+
+static int hwm_core_fan_set_limit(int num, int fan_limit,
+ struct hwm_sensors_limit *limit)
+{
+ struct fan_dev_config cfg;
+ int ret;
+
+ if (WARN_ON(num >= HWM_MAX_FAN))
+ return -EINVAL;
+
+ ret = hwm_read_fan_config(num, &cfg);
+ if (ret < 0) {
+ pr_err("Failed while reading FAN %s config\n",
+ hwmon->fan.attr[num].label);
+ return ret;
+ }
+
+ switch (fan_limit) {
+ case LIMIT_PWM:
+ hwm_set_pwm_limit(&cfg, &limit->pwm);
+ break;
+ case LIMIT_RPM:
+ hwm_set_rpm_limit(&cfg, &limit->rpm);
+ break;
+ case LIMIT_TEMP:
+ hwm_set_temp_limit(&cfg, &limit->temp);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return hwm_write_fan_config(num, &cfg);
+}
+
+int hwm_core_fan_set_rpm_limit(int num, int min, int max)
+{
+ struct hwm_sensors_limit limit = {
+ .rpm = {
+ .min = min,
+ .max = max,
+ },
+ };
+
+ return hwm_core_fan_set_limit(num, LIMIT_RPM, &limit);
+}
+
+int hwm_core_fan_set_pwm_limit(int num, int min, int max)
+{
+ struct hwm_sensors_limit limit = {
+ .pwm = {
+ .min = min,
+ .max = max,
+ },
+ };
+
+ return hwm_core_fan_set_limit(num, LIMIT_PWM, &limit);
+}
+
+int hwm_core_fan_set_temp_limit(int num, int stop, int min, int max)
+{
+ struct hwm_sensors_limit limit = {
+ .temp = {
+ .stop = stop,
+ .min = min,
+ .max = max,
+ },
+ };
+
+ return hwm_core_fan_set_limit(num, LIMIT_TEMP, &limit);
+}
+
+int hwm_core_adc_get_max_count(void)
+{
+ return hwmon->adc.num;
+}
+
+int hwm_core_fan_get_max_count(void)
+{
+ return hwmon->fan.num;
+}
+
+int hwm_core_init(void)
+{
+ hwmon = imanager_get_hwmon_device();
+ if (!hwmon)
+ return -ENODEV;
+
+ return 0;
+}
+
diff --git a/drivers/hwmon/imanager-hwmon.c b/drivers/hwmon/imanager-hwmon.c
new file mode 100644
index 0000000..b8ad562
--- /dev/null
+++ b/drivers/hwmon/imanager-hwmon.c
@@ -0,0 +1,1058 @@
+/*
+ * Advantech iManager Hardware Monitoring driver
+ * Derived from nct6775 driver
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/mfd/imanager/core.h>
+#include <linux/mfd/imanager/hwmon.h>
+
+struct imanager_hwmon_data {
+ struct imanager_device_data *idev;
+ bool valid; /* if set, below values are valid */
+ struct hwm_data hwm;
+ int adc_num;
+ int fan_num;
+ unsigned long samples;
+ unsigned long last_updated;
+ const struct attribute_group *groups[3];
+};
+
+static inline u32 in_from_reg(u16 val)
+{
+ return clamp_val(DIV_ROUND_CLOSEST(val * SCALE_IN, 1000), 0, 65535);
+}
+
+static inline u16 in_to_reg(u32 val)
+{
+ return clamp_val(DIV_ROUND_CLOSEST(val * 1000, SCALE_IN), 0, 65535);
+}
+
+static struct imanager_hwmon_data *
+imanager_hwmon_update_device(struct device *dev)
+{
+ struct imanager_hwmon_data *data = dev_get_drvdata(dev);
+ int i;
+
+ mutex_lock(&data->idev->lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ /* Measured voltages */
+ for (i = 0; i < data->adc_num; i++)
+ hwm_core_adc_get_value(i, &data->hwm.volt[i]);
+
+ /* Measured fan speeds */
+ for (i = 0; i < data->fan_num; i++)
+ hwm_core_fan_get_ctrl(i, &data->hwm.fan[i]);
+
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+ mutex_unlock(&data->idev->lock);
+
+ return data;
+}
+
+static ssize_t
+show_in(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_voltage *adc = &data->hwm.volt[index];
+
+ return sprintf(buf, "%u\n", in_from_reg(adc->value));
+}
+
+static ssize_t
+show_in_min(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_voltage *adc = &data->hwm.volt[index];
+
+ return sprintf(buf, "%u\n", in_from_reg(adc->min));
+}
+
+static ssize_t
+show_in_max(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_voltage *adc = &data->hwm.volt[index];
+
+ return sprintf(buf, "%u\n", in_from_reg(adc->max));
+}
+
+static ssize_t
+store_in_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_voltage *adc = &data->hwm.volt[index];
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->idev->lock);
+
+ adc->min = in_to_reg(val);
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+store_in_max(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_voltage *adc = &data->hwm.volt[index];
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->idev->lock);
+
+ adc->max = in_to_reg(val);
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+show_in_alarm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_voltage *adc = &data->hwm.volt[index];
+ int val = 0;
+
+ if (adc->valid)
+ val = (adc->value < adc->min) || (adc->value > adc->max);
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t
+show_in_average(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_voltage *adc = &data->hwm.volt[index];
+
+ if (adc->average)
+ adc->average =
+ DIV_ROUND_CLOSEST(adc->average * data->samples +
+ adc->value, ++data->samples);
+ else {
+ adc->average = adc->value;
+ data->samples = 1;
+ }
+
+ return sprintf(buf, "%u\n", in_from_reg(adc->average));
+}
+
+static ssize_t
+show_in_lowest(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_voltage *adc = &data->hwm.volt[index];
+
+ if (!adc->lowest)
+ adc->lowest = adc->highest = adc->value;
+ else if (adc->value < adc->lowest)
+ adc->lowest = adc->value;
+
+ return sprintf(buf, "%u\n", in_from_reg(adc->lowest));
+}
+
+static ssize_t
+show_in_highest(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_voltage *adc = &data->hwm.volt[index];
+
+ if (!adc->highest)
+ adc->highest = adc->value;
+ else if (adc->value > adc->highest)
+ adc->highest = adc->value;
+
+ return sprintf(buf, "%u\n", in_from_reg(adc->highest));
+}
+
+static ssize_t
+store_in_reset_history(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_voltage *adc = &data->hwm.volt[index];
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&data->idev->lock);
+
+ if (val == 1) {
+ adc->lowest = 0;
+ adc->highest = 0;
+ } else {
+ count = -EINVAL;
+ }
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+show_temp(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_smartfan *fan = &data->hwm.fan[index - 1];
+
+ return sprintf(buf, "%u\n", fan->temp * 1000);
+}
+
+static ssize_t
+show_fan_in(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_smartfan *fan = &data->hwm.fan[index - 1];
+
+ return sprintf(buf, "%u\n", fan->valid ? fan->speed : 0);
+}
+
+static inline int is_alarm(const struct hwm_smartfan *fan)
+{
+ /*
+ * Do not set ALARM flag if FAN is in speed cruise mode (3)
+ * as this mode automatically turns on the FAN
+ * Set ALARM flag when pwm is set but speed is 0 as this
+ * could be a defective FAN or no FAN is present
+ */
+ return (!fan->valid ||
+ ((fan->mode == MODE_AUTO) && fan->alarm) ||
+ (fan->speed > fan->limit.rpm.max));
+}
+
+static ssize_t
+show_fan_alarm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_smartfan *fan = &data->hwm.fan[index - 1];
+
+ return sprintf(buf, "%u\n", fan->valid ? is_alarm(fan) : 0);
+}
+
+static ssize_t
+show_fan_min(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_fan_limit *rpm = &data->hwm.fan[index - 1].limit.rpm;
+
+ return sprintf(buf, "%u\n", rpm->min);
+}
+
+static ssize_t
+show_fan_max(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_fan_limit *rpm = &data->hwm.fan[index - 1].limit.rpm;
+
+ return sprintf(buf, "%u\n", rpm->max);
+}
+
+static ssize_t
+store_fan_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_smartfan *fan = &data->hwm.fan[index - 1];
+ struct hwm_fan_limit *rpm = &fan->limit.rpm;
+ unsigned long val = 0;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ /* do not apply value if not in 'fan cruise mode' */
+ if (fan->mode != MODE_AUTO)
+ return -EINVAL;
+
+ mutex_lock(&data->idev->lock);
+
+ hwm_core_fan_set_rpm_limit(index - 1, val, rpm->max);
+ hwm_core_fan_get_ctrl(index - 1, fan); /* update */
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+store_fan_max(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_smartfan *fan = &data->hwm.fan[index - 1];
+ struct hwm_fan_limit *rpm = &fan->limit.rpm;
+ unsigned long val = 0;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ /* do not apply value if not in 'fan cruise mode' */
+ if (fan->mode != MODE_AUTO)
+ return -EINVAL;
+
+ mutex_lock(&data->idev->lock);
+
+ hwm_core_fan_set_rpm_limit(index - 1, rpm->min, val);
+ hwm_core_fan_get_ctrl(index - 1, fan);
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ u32 val = DIV_ROUND_CLOSEST(data->hwm.fan[index - 1].pwm * 255, 100);
+
+ return sprintf(buf, "%u\n", val);
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_smartfan *fan = &data->hwm.fan[index - 1];
+ unsigned long val = 0;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = DIV_ROUND_CLOSEST(val * 100, 255);
+
+ mutex_lock(&data->idev->lock);
+
+ switch (fan->mode) {
+ case MODE_MANUAL:
+ hwm_core_fan_set_ctrl(index - 1, MODE_MANUAL, CTRL_PWM,
+ val, 0, NULL, NULL);
+ break;
+ case MODE_AUTO:
+ if (fan->type == CTRL_RPM)
+ break;
+ hwm_core_fan_set_ctrl(index - 1, MODE_AUTO, CTRL_PWM,
+ val, 0, &fan->limit, &fan->alert);
+ break;
+ }
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+show_pwm_min(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%u\n", data->hwm.fan[index - 1].limit.pwm.min);
+}
+
+static ssize_t
+show_pwm_max(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%u\n", data->hwm.fan[index - 1].limit.pwm.max);
+}
+
+static ssize_t
+show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index - 1;
+ struct hwm_smartfan *fan = &data->hwm.fan[nr];
+
+ if (fan->mode == MODE_OFF)
+ return -EINVAL;
+
+ return sprintf(buf, "%u\n", fan->mode - 1);
+}
+
+static ssize_t
+store_pwm_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index - 1;
+ struct hwm_smartfan *fan = &data->hwm.fan[nr];
+ unsigned long mode = 0;
+ int err;
+
+ err = kstrtoul(buf, 10, &mode);
+ if (err < 0)
+ return err;
+
+ if (mode > MODE_AUTO)
+ return -EINVAL;
+
+ mutex_lock(&data->idev->lock);
+
+ switch (mode) {
+ case 0:
+ if (mode != 0)
+ hwm_core_fan_set_ctrl(nr, MODE_FULL, CTRL_PWM, 100,
+ fan->pulse, NULL, NULL);
+ break;
+ case 1:
+ if (mode != 1)
+ hwm_core_fan_set_ctrl(nr, MODE_MANUAL, CTRL_PWM, 0,
+ fan->pulse, NULL, NULL);
+ break;
+ case 2:
+ if (mode != 2)
+ hwm_core_fan_set_ctrl(nr, MODE_AUTO, fan->type, 0,
+ fan->pulse, &fan->limit, NULL);
+ break;
+ }
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_smartfan *fan = &data->hwm.fan[index - 1];
+
+ if (fan->mode == MODE_OFF)
+ return -EINVAL;
+
+ return sprintf(buf, "%u\n", fan->type == CTRL_PWM ? 1 : 0);
+}
+
+static ssize_t
+store_pwm_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index - 1;
+ struct hwm_smartfan *fan = &data->hwm.fan[nr];
+ unsigned long val = 0;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ if (fan->mode != MODE_AUTO)
+ return -EINVAL;
+
+ mutex_lock(&data->idev->lock);
+
+ hwm_core_fan_set_ctrl(nr, fan->mode, val ? CTRL_RPM : CTRL_PWM,
+ fan->pwm, fan->pulse, &fan->limit, &fan->alert);
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+show_temp_min(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index - 1;
+ int val = data->hwm.fan[nr].limit.temp.min;
+
+ return sprintf(buf, "%d\n", val * 1000);
+}
+
+static ssize_t
+show_temp_max(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index - 1;
+ int val = data->hwm.fan[nr].limit.temp.max;
+
+ return sprintf(buf, "%u\n", val * 1000);
+}
+
+static ssize_t
+show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index - 1;
+ struct hwm_smartfan *fan = &data->hwm.fan[nr];
+ struct hwm_fan_temp_limit *temp = &fan->limit.temp;
+
+ return sprintf(buf, "%u\n", (fan->temp && (fan->temp >= temp->max)));
+}
+
+static ssize_t
+store_temp_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index - 1;
+ struct hwm_smartfan *fan = &data->hwm.fan[nr];
+ struct hwm_fan_temp_limit *temp = &fan->limit.temp;
+ long val = 0;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = DIV_ROUND_CLOSEST(val, 1000);
+
+ if (val > 100)
+ return -EINVAL;
+
+ /* do not apply value if not in 'fan cruise mode' */
+ if (fan->mode != MODE_AUTO)
+ return -EINVAL;
+
+ /* The EC imanager provides three different temperature limit values
+ * (stop, min, and max) where stop indicates a minimum temp value
+ * (threshold) from which the FAN will turn off. We are setting
+ * temp_stop to the same value as temp_min.
+ */
+
+ mutex_lock(&data->idev->lock);
+
+ hwm_core_fan_set_temp_limit(nr, val, val, temp->max);
+ hwm_core_fan_get_ctrl(nr, fan);
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+store_temp_max(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int nr = to_sensor_dev_attr(attr)->index - 1;
+ struct hwm_smartfan *fan = &data->hwm.fan[nr];
+ struct hwm_fan_temp_limit *temp = &fan->limit.temp;
+ long val = 0;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = DIV_ROUND_CLOSEST(val, 1000);
+
+ if (val > 100)
+ return -EINVAL;
+
+ /* do not apply value if not in 'fan cruise mode' */
+ if (fan->mode != MODE_AUTO)
+ return -EINVAL;
+
+ mutex_lock(&data->idev->lock);
+
+ hwm_core_fan_set_temp_limit(nr, temp->stop, temp->min, val);
+ hwm_core_fan_get_ctrl(nr, fan);
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+store_pwm_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_fan_limit *pwm = &data->hwm.fan[index - 1].limit.pwm;
+ long val = 0;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = DIV_ROUND_CLOSEST(val * 100, 255);
+
+ if (val > 100)
+ return -EINVAL;
+
+ mutex_lock(&data->idev->lock);
+
+ hwm_core_fan_set_pwm_limit(index - 1, val, pwm->max);
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+store_pwm_max(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct imanager_hwmon_data *data = imanager_hwmon_update_device(dev);
+ int index = to_sensor_dev_attr(attr)->index;
+ struct hwm_fan_limit *pwm = &data->hwm.fan[index - 1].limit.pwm;
+ long val = 0;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+
+ val = DIV_ROUND_CLOSEST(val * 100, 255);
+
+ if (val > 100)
+ return -EINVAL;
+
+ mutex_lock(&data->idev->lock);
+
+ hwm_core_fan_set_pwm_limit(index - 1, pwm->min, val);
+
+ mutex_unlock(&data->idev->lock);
+
+ return count;
+}
+
+static ssize_t
+show_in_label(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%s\n", hwm_core_adc_get_label(index));
+}
+
+static ssize_t
+show_temp_label(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%s\n", hwm_core_fan_get_temp_label(index - 1));
+}
+
+static ssize_t
+show_fan_label(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int index = to_sensor_dev_attr(attr)->index;
+
+ return sprintf(buf, "%s\n", hwm_core_fan_get_label(index - 1));
+}
+
+/*
+ * Sysfs callback functions
+ */
+
+static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_in_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
+static SENSOR_DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min,
+ store_in_min, 0);
+static SENSOR_DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max,
+ store_in_max, 0);
+static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_in_alarm, NULL, 0);
+
+static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_in_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min,
+ store_in_min, 1);
+static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max,
+ store_in_max, 1);
+static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_in_alarm, NULL, 1);
+
+static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_in_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min,
+ store_in_min, 2);
+static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max,
+ store_in_max, 2);
+static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_in_alarm, NULL, 2);
+
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, show_temp_min,
+ store_temp_min, 1);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp_max,
+ store_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 1);
+
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, show_temp_min,
+ store_temp_min, 2);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max,
+ store_temp_max, 2);
+static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 2);
+
+static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO | S_IWUSR, show_temp_min,
+ store_temp_min, 3);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max,
+ store_temp_max, 3);
+static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 3);
+
+static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, show_fan_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
+ store_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan1_max, S_IWUSR | S_IRUGO, show_fan_max,
+ store_fan_max, 1);
+static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 1);
+
+static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, show_fan_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
+ store_fan_min, 2);
+static SENSOR_DEVICE_ATTR(fan2_max, S_IWUSR | S_IRUGO, show_fan_max,
+ store_fan_max, 2);
+static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 2);
+
+static SENSOR_DEVICE_ATTR(fan3_label, S_IRUGO, show_fan_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min,
+ store_fan_min, 3);
+static SENSOR_DEVICE_ATTR(fan3_max, S_IWUSR | S_IRUGO, show_fan_max,
+ store_fan_max, 3);
+static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 3);
+
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm1_min, S_IWUSR | S_IRUGO, show_pwm_min,
+ store_pwm_min, 1);
+static SENSOR_DEVICE_ATTR(pwm1_max, S_IWUSR | S_IRUGO, show_pwm_max,
+ store_pwm_max, 1);
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 1);
+static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 1);
+
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm2_min, S_IWUSR | S_IRUGO, show_pwm_min,
+ store_pwm_min, 2);
+static SENSOR_DEVICE_ATTR(pwm2_max, S_IWUSR | S_IRUGO, show_pwm_max,
+ store_pwm_max, 2);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 2);
+static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 2);
+
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3);
+static SENSOR_DEVICE_ATTR(pwm3_min, S_IWUSR | S_IRUGO, show_pwm_min,
+ store_pwm_min, 3);
+static SENSOR_DEVICE_ATTR(pwm3_max, S_IWUSR | S_IRUGO, show_pwm_max,
+ store_pwm_max, 3);
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 3);
+static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 3);
+
+static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, show_in, NULL, 4);
+static SENSOR_DEVICE_ATTR(curr1_min, S_IWUSR | S_IRUGO, show_in_min,
+ store_in_min, 4);
+static SENSOR_DEVICE_ATTR(curr1_max, S_IWUSR | S_IRUGO, show_in_max,
+ store_in_max, 4);
+static SENSOR_DEVICE_ATTR(curr1_alarm, S_IRUGO, show_in_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(curr1_average, S_IRUGO, show_in_average, NULL, 4);
+static SENSOR_DEVICE_ATTR(curr1_lowest, S_IRUGO, show_in_lowest, NULL, 4);
+static SENSOR_DEVICE_ATTR(curr1_highest, S_IRUGO, show_in_highest, NULL, 4);
+static SENSOR_DEVICE_ATTR(curr1_reset_history, S_IWUSR, NULL,
+ store_in_reset_history, 4);
+
+static SENSOR_DEVICE_ATTR(cpu0_vid, S_IRUGO, show_in, NULL, 3);
+
+static struct attribute *imanager_in_attributes[] = {
+ &sensor_dev_attr_in0_label.dev_attr.attr,
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in1_label.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_in2_label.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t
+imanager_in_is_visible(struct kobject *kobj, struct attribute *attr, int index)
+{
+ int max_count = hwm_core_adc_get_max_count();
+
+ if (max_count >= 3)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group imanager_group_in = {
+ .attrs = imanager_in_attributes,
+ .is_visible = imanager_in_is_visible,
+};
+
+static struct attribute *imanager_other_attributes[] = {
+ &sensor_dev_attr_curr1_input.dev_attr.attr,
+ &sensor_dev_attr_curr1_min.dev_attr.attr,
+ &sensor_dev_attr_curr1_max.dev_attr.attr,
+ &sensor_dev_attr_curr1_alarm.dev_attr.attr,
+ &sensor_dev_attr_curr1_average.dev_attr.attr,
+ &sensor_dev_attr_curr1_lowest.dev_attr.attr,
+ &sensor_dev_attr_curr1_highest.dev_attr.attr,
+ &sensor_dev_attr_curr1_reset_history.dev_attr.attr,
+
+ &sensor_dev_attr_cpu0_vid.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t
+imanager_other_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ int max_count = hwm_core_adc_get_max_count();
+
+ /*
+ * There are either 3 or 5 VINs
+ * vin3 is current monitoring
+ * vin4 is CPU VID
+ */
+ if (max_count > 3)
+ return attr->mode;
+
+ return 0;
+}
+
+static const struct attribute_group imanager_group_other = {
+ .attrs = imanager_other_attributes,
+ .is_visible = imanager_other_is_visible,
+};
+
+static struct attribute *imanager_fan_attributes[] = {
+ &sensor_dev_attr_fan1_label.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_max.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_fan2_label.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_max.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_fan3_label.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_max.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp3_label.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_min.dev_attr.attr,
+ &sensor_dev_attr_pwm1_max.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_min.dev_attr.attr,
+ &sensor_dev_attr_pwm2_max.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_mode.dev_attr.attr,
+
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_min.dev_attr.attr,
+ &sensor_dev_attr_pwm3_max.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3_mode.dev_attr.attr,
+
+ NULL
+};
+
+static umode_t
+imanager_fan_is_visible(struct kobject *kobj, struct attribute *attr, int index)
+{
+ int err;
+
+ if ((index >= 0) && (index <= 14)) { /* fan */
+ err = hwm_core_fan_is_available(index / 5);
+ if (err < 0)
+ return 0;
+ } else if ((index >= 15) && (index <= 29)) { /* temp */
+ err = hwm_core_fan_is_available((index - 15) / 5);
+ if (err < 0)
+ return 0;
+ } else if ((index >= 30) && (index <= 34)) { /* pwm */
+ err = hwm_core_fan_is_available((index - 30) / 5);
+ if (err < 0)
+ return 0;
+ }
+
+ return attr->mode;
+}
+
+static const struct attribute_group imanager_group_fan = {
+ .attrs = imanager_fan_attributes,
+ .is_visible = imanager_fan_is_visible,
+};
+
+/*
+ * Module stuff
+ */
+static int imanager_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imanager_device_data *idev = dev_get_drvdata(dev->parent);
+ struct imanager_hwmon_data *data;
+ struct device *hwmon_dev;
+ int err, i, num_attr_groups = 0;
+
+ if (!idev) {
+ dev_err(dev, "Invalid platform data\n");
+ return -EINVAL;
+ }
+
+ err = hwm_core_init();
+ if (err) {
+ dev_err(dev, "Hwmon core init failed\n");
+ return -EIO;
+ }
+
+ data = devm_kzalloc(dev, sizeof(struct imanager_hwmon_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->idev = idev;
+ platform_set_drvdata(pdev, data);
+
+ data->adc_num = hwm_core_adc_get_max_count();
+ data->fan_num = hwm_core_fan_get_max_count();
+
+ for (i = 0; i < data->fan_num; i++) {
+ /* set active fan to automatic speed control */
+ hwm_core_fan_set_ctrl(i, MODE_AUTO, CTRL_RPM, 0, 0,
+ NULL, NULL);
+ hwm_core_fan_get_ctrl(i, &data->hwm.fan[i]);
+ }
+
+ data->groups[num_attr_groups++] = &imanager_group_in;
+
+ if (data->adc_num > 3)
+ data->groups[num_attr_groups++] = &imanager_group_other;
+
+ if (data->fan_num)
+ data->groups[num_attr_groups++] = &imanager_group_fan;
+
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev,
+ "imanager_hwmon", data, data->groups);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct platform_driver imanager_hwmon_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "imanager_hwmon",
+ },
+ .probe = imanager_hwmon_probe,
+};
+
+module_platform_driver(imanager_hwmon_driver);
+
+MODULE_DESCRIPTION("Advantech iManager HWmon Driver");
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imanager_hwmon");
diff --git a/include/linux/mfd/imanager/hwmon.h b/include/linux/mfd/imanager/hwmon.h
new file mode 100644
index 0000000..2a7e191
--- /dev/null
+++ b/include/linux/mfd/imanager/hwmon.h
@@ -0,0 +1,120 @@
+/*
+ * Advantech iManager Hardware Monitoring core
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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.
+ */
+
+#ifndef __HWMON_H__
+#define __HWMON_H__
+
+#include <linux/types.h>
+
+#define HWM_MAX_ADC 5
+#define HWM_MAX_FAN 3
+
+/* Voltage computation (10-bit ADC, 0..3V input) */
+#define SCALE_IN 2933 /* (3000mV / (2^10 - 1)) * 1000 */
+
+/* Default Voltage Sensors */
+struct hwm_voltage {
+ bool valid; /* if set, below values are valid */
+
+ int value;
+ int min;
+ int max;
+ int average;
+ int lowest;
+ int highest;
+
+};
+
+struct hwm_fan_temp_limit {
+ int stop;
+ int min;
+ int max;
+};
+
+struct hwm_fan_limit {
+ int min;
+ int max;
+};
+
+struct hwm_fan_alert {
+ int min;
+ int max;
+ int min_alarm;
+ int max_alarm;
+};
+
+struct hwm_sensors_limit {
+ struct hwm_fan_temp_limit temp;
+ struct hwm_fan_limit pwm;
+ struct hwm_fan_limit rpm;
+};
+
+struct hwm_smartfan {
+ bool valid; /* if set, below values are valid */
+
+ int mode;
+ int type;
+ int pwm;
+ int speed;
+ int pulse;
+ int alarm;
+ int temp;
+
+ struct hwm_sensors_limit limit;
+ struct hwm_fan_alert alert;
+};
+
+struct hwm_data {
+ struct hwm_voltage volt[HWM_MAX_ADC];
+ struct hwm_smartfan fan[HWM_MAX_FAN];
+};
+
+enum fan_unit {
+ FAN_CPU,
+ FAN_SYS1,
+ FAN_SYS2,
+};
+
+enum fan_ctrl_type {
+ CTRL_PWM,
+ CTRL_RPM,
+};
+
+enum fan_mode {
+ MODE_OFF,
+ MODE_FULL,
+ MODE_MANUAL,
+ MODE_AUTO,
+};
+
+int hwm_core_init(void);
+
+int hwm_core_adc_is_available(int num);
+int hwm_core_adc_get_max_count(void);
+int hwm_core_adc_get_value(int num, struct hwm_voltage *volt);
+const char *hwm_core_adc_get_label(int num);
+
+int hwm_core_fan_is_available(int num);
+int hwm_core_fan_get_max_count(void);
+int hwm_core_fan_get_ctrl(int num, struct hwm_smartfan *fan);
+int hwm_core_fan_set_ctrl(int num, int fmode, int ftype, int pwm, int pulse,
+ struct hwm_sensors_limit *limit,
+ struct hwm_fan_alert *alert);
+
+int hwm_core_fan_set_rpm_limit(int num, int min, int max);
+int hwm_core_fan_set_pwm_limit(int num, int min, int max);
+int hwm_core_fan_set_temp_limit(int num, int stop, int min, int max);
+
+const char *hwm_core_fan_get_label(int num);
+const char *hwm_core_fan_get_temp_label(int num);
+
+#endif
--
2.6.4
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH] fix platform_no_drv_owner.cocci warnings
2016-01-10 9:11 ` richard.dorsch
@ 2016-01-10 10:25 ` kbuild test robot
0 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2016-01-10 10:25 UTC (permalink / raw)
To: richard.dorsch
Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog,
linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga,
Richard Vidal-Dorsch
drivers/hwmon/imanager-hwmon.c:1047:3-8: No need to set .owner here. The core will do it.
Remove .owner field if calls are used which set it automatically
Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci
CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
imanager-hwmon.c | 1 -
1 file changed, 1 deletion(-)
--- a/drivers/hwmon/imanager-hwmon.c
+++ b/drivers/hwmon/imanager-hwmon.c
@@ -1044,7 +1044,6 @@ static int imanager_hwmon_probe(struct p
static struct platform_driver imanager_hwmon_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "imanager_hwmon",
},
.probe = imanager_hwmon_probe,
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v2 1/6] Add Advantech iManager MFD core driver
@ 2016-01-10 9:10 ` richard.dorsch
2016-01-10 10:11 ` [PATCH] fix platform_no_drv_owner.cocci warnings kbuild test robot
0 siblings, 1 reply; 39+ messages in thread
From: richard.dorsch @ 2016-01-10 9:10 UTC (permalink / raw)
To: linux-kernel
Cc: lm-sensors, linux-i2c, linux-watchdog, linux-gpio, lee.jones,
jdelvare, linux, wim, jo.sunga, Richard Vidal-Dorsch
From: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
This patch adds Advantech iManager Embedded Controller MFD core driver.
This mfd core dirver provides an interface to GPIO, I2C, HWmon,
Watchdog, and Backlight/Brightness control.
Signed-off-by: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
---
Documentation/devicetree/bindings/mfd/imanager.txt | 33 +
MAINTAINERS | 13 +
drivers/mfd/Kconfig | 20 +
drivers/mfd/Makefile | 2 +
drivers/mfd/imanager-core.c | 288 +++++
drivers/mfd/imanager-ec.c | 1345 ++++++++++++++++++++
include/linux/mfd/imanager/core.h | 31 +
include/linux/mfd/imanager/ec.h | 210 +++
8 files changed, 1942 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/imanager.txt
create mode 100644 drivers/mfd/imanager-core.c
create mode 100644 drivers/mfd/imanager-ec.c
create mode 100644 include/linux/mfd/imanager/core.h
create mode 100644 include/linux/mfd/imanager/ec.h
diff --git a/Documentation/devicetree/bindings/mfd/imanager.txt b/Documentation/devicetree/bindings/mfd/imanager.txt
new file mode 100644
index 0000000..bf58a96
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/imanager.txt
@@ -0,0 +1,33 @@
+Note
+====
+
+This is a set of platform drivers which provide support for multiple
+embedded features such as GPIO, I2C/SMBus, Hardware Monitoring, Watchdog,
+and Backlight/Brightness control. Those features are available on Advantech
+Embedded boards such as SOM, MIO, AIMB, and PCM.
+Datasheets of each product line can be downloaded from www.advantech.com
+
+Author:
+ Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+
+
+Kernel driver imanager
+======================
+
+This driver provides a communication layer to the Advantech iManager EC
+firmware which is factory loaded onto ITE IT8518/28 chips. The type of
+communication is message based. Clients (gpio, i2c, hwmon etc. drivers)
+request data from Advantech iManager (polling, not interrupt driven). If
+a response is been received within a time frame, the data is been extracted
+from the message and then passed to the caller (clients).
+
+ Supported chips:
+ * Advantech EC based on ITE IT8518
+ Prefix: imanager
+ Addresses: 0x029e/0x029f
+ Datasheet: Available from ITE upon request
+ * Advantech EC based on ITE IT8528
+ Prefix: imanager
+ Addresses: 0x0299/0x29a
+ Datasheet: Available from ITE upon request
+
diff --git a/MAINTAINERS b/MAINTAINERS
index 233f834..8d25fdc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5391,6 +5391,19 @@ M: Stanislaw Gruszka <stf_xl@wp.pl>
S: Maintained
F: drivers/usb/atm/ueagle-atm.c
+IMANAGER ADVANTECH EC DRIVER
+M: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+S: Maintained
+F: Documentation/devicetree/bindings/mfd/imanager.txt
+F: Documentation/hwmon/imanager
+F: Documentation/i2c/busses/i2c-imanager
+F: drivers/mfd/imanager-core.c
+F: drivers/gpio/imanager-gpio.c
+F: drivers/hwmon/imanager-hwmon.c
+F: drivers/i2c/busses/imanager-i2c.c
+F: drivers/video/backlight/imanager-bl.c
+F: drivers/watchdog/imanager-wdt.c
+
INA209 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
L: lm-sensors@lm-sensors.org
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 4d92df6..4dc7c13 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -322,6 +322,26 @@ config MFD_INTEL_QUARK_I2C_GPIO
their respective IO driver.
The GPIO exports a total amount of 8 interrupt-capable GPIOs.
+config MFD_IMANAGER
+ tristate "Advantech iManager Embedded Controller"
+ depends on PCI
+ depends on X86
+ select MFD_CORE
+ help
+ This is the core driver for Advantech iManager EC as found on some
+ Advantech SOM, MIO, AIMB, and PCM modules/boards. The EC may provide
+ functions like GPIO, I2C interface, HW monitoring, Watchdog, and
+ backlight/brightness control.
+
+ The following Advantech boards are supported:
+ * All SOM modules newer than SOM-5788
+ * MIO-5250/5251/5270/5271 and newer
+ * PCM-9388
+ * AIMB-274/231
+
+ This driver can also be built as a module. If so, the module
+ will be called imanager.
+
config LPC_ICH
tristate "Intel ICH LPC"
depends on PCI
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index a8b76b8..c9c63d9 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -142,6 +142,8 @@ obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
+imanager-objs := imanager-core.o imanager-ec.o
+obj-$(CONFIG_MFD_IMANAGER) += imanager.o
obj-$(CONFIG_MFD_KEMPLD) += kempld-core.o
obj-$(CONFIG_MFD_INTEL_QUARK_I2C_GPIO) += intel_quark_i2c_gpio.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
diff --git a/drivers/mfd/imanager-core.c b/drivers/mfd/imanager-core.c
new file mode 100644
index 0000000..9697c144
--- /dev/null
+++ b/drivers/mfd/imanager-core.c
@@ -0,0 +1,288 @@
+/*
+ * Advantech iManager MFD core driver
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/imanager/core.h>
+#include <linux/mfd/imanager/ec.h>
+
+static struct platform_device *pdev;
+
+enum imanager_cells {
+ IMANAGER_BL,
+ IMANAGER_GPIO,
+ IMANAGER_HWMON,
+ IMANAGER_I2C,
+ IMANAGER_WDT,
+};
+
+static const char * const chip_names[] = {
+ "it8516",
+ "it8518",
+ "it8528",
+ NULL
+};
+
+static struct resource imanager_ioresource = {
+ .start = IT8516_DAT_PORT,
+ .end = IT8518_DAT_PORT,
+ .flags = IORESOURCE_IO,
+};
+
+/*
+ * Devices which are part of the iManager and are available via firmware.
+ */
+static struct mfd_cell imanager_devs[] = {
+ [IMANAGER_BL] = {
+ .name = "imanager_backlight",
+ },
+ [IMANAGER_GPIO] = {
+ .name = "imanager_gpio",
+ },
+ [IMANAGER_HWMON] = {
+ .name = "imanager_hwmon",
+ },
+ [IMANAGER_I2C] = {
+ .name = "imanager_i2c",
+ },
+ [IMANAGER_WDT] = {
+ .name = "imanager_wdt",
+ },
+};
+
+const char *project_type_to_str(int type)
+{
+ const char *version_type;
+
+ switch (type) {
+ case 'V':
+ version_type = "Release";
+ break;
+ case 'X':
+ version_type = "Engineering Sample";
+ break;
+ case 'A' ... 'U':
+ version_type = "Custom";
+ break;
+ default:
+ version_type = "Unknown";
+ break;
+ }
+
+ return version_type;
+}
+
+static ssize_t imanager_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imanager_platform_data *pdata = dev_get_platdata(dev);
+ const struct ec_info *info = &pdata->dev->info;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", info->pcb_name);
+}
+
+static ssize_t imanager_kversion_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imanager_platform_data *pdata = dev_get_platdata(dev);
+ const struct ec_info *info = &pdata->dev->info;
+
+ return scnprintf(buf, PAGE_SIZE, "%d.%d\n",
+ info->version.kernel_major,
+ info->version.kernel_minor);
+}
+
+static ssize_t imanager_fwversion_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imanager_platform_data *pdata = dev_get_platdata(dev);
+ const struct ec_info *info = &pdata->dev->info;
+
+ return scnprintf(buf, PAGE_SIZE, "%d.%d\n",
+ info->version.firmware_major,
+ info->version.firmware_minor);
+}
+
+static ssize_t imanager_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imanager_platform_data *pdata = dev_get_platdata(dev);
+ const struct ec_info *info = &pdata->dev->info;
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n",
+ project_type_to_str(info->version.type));
+}
+
+static ssize_t imanager_chip_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct imanager_platform_data *pdata = dev_get_platdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", pdata->chip_name);
+}
+
+static DEVICE_ATTR(imanager_name, S_IRUGO, imanager_name_show, NULL);
+static DEVICE_ATTR(imanager_kversion, S_IRUGO, imanager_kversion_show, NULL);
+static DEVICE_ATTR(imanager_fwversion, S_IRUGO, imanager_fwversion_show, NULL);
+static DEVICE_ATTR(imanager_type, S_IRUGO, imanager_type_show, NULL);
+static DEVICE_ATTR(imanager_chip_name, S_IRUGO, imanager_chip_name_show, NULL);
+
+static struct attribute *imanager_core_attributes[] = {
+ &dev_attr_imanager_name.attr,
+ &dev_attr_imanager_kversion.attr,
+ &dev_attr_imanager_fwversion.attr,
+ &dev_attr_imanager_type.attr,
+ &dev_attr_imanager_chip_name.attr,
+ NULL
+};
+
+static const struct attribute_group imanager_core_attr_group = {
+ .attrs = imanager_core_attributes,
+};
+
+static int imanager_platform_create(void)
+{
+ struct device *dev;
+ struct imanager_platform_data platdata;
+ int err;
+
+ pdev = platform_device_alloc("imanager-core", -1);
+ if (!pdev)
+ return -ENOMEM;
+
+ dev = &pdev->dev;
+
+ err = platform_device_add_data(pdev, &platdata, sizeof(platdata));
+ if (err)
+ goto exit_device_put;
+
+ err = platform_device_add_resources(pdev, &imanager_ioresource, 1);
+ if (err)
+ goto exit_device_put;
+
+ err = platform_device_add(pdev);
+ if (err)
+ goto exit_device_put;
+
+ err = mfd_add_devices(dev, pdev->id, imanager_devs,
+ ARRAY_SIZE(imanager_devs), NULL, -1, NULL);
+ if (err)
+ goto exit_device_unregister;
+
+ return 0;
+
+exit_device_unregister:
+ platform_device_unregister(pdev);
+exit_device_put:
+ platform_device_put(pdev);
+
+ return err;
+}
+
+static int imanager_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct imanager_platform_data *pdata = dev_get_platdata(dev);
+ struct imanager_device_data *imanager;
+ int ret;
+
+ if (!pdev)
+ return -EINVAL;
+
+ imanager = devm_kzalloc(dev, sizeof(*imanager), GFP_KERNEL);
+ if (!imanager)
+ return -ENOMEM;
+
+ imanager->dev = dev;
+ mutex_init(&imanager->lock);
+
+ platform_set_drvdata(pdev, imanager);
+
+ pdata->dev = imanager_get_ec_device();
+ pdata->chip_name = chip_names[pdata->dev->id];
+
+ dev_info(dev, "Found Advantech iManager %s - %s %d.%d/%d.%d (%s)\n",
+ pdata->chip_name,
+ pdata->dev->info.pcb_name,
+ pdata->dev->info.version.kernel_major,
+ pdata->dev->info.version.kernel_minor,
+ pdata->dev->info.version.firmware_major,
+ pdata->dev->info.version.firmware_minor,
+ project_type_to_str(pdata->dev->info.version.type));
+
+ ret = sysfs_create_group(&dev->kobj, &imanager_core_attr_group);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int imanager_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ sysfs_remove_group(&dev->kobj, &imanager_core_attr_group);
+
+ mfd_remove_devices(dev);
+
+ return 0;
+}
+
+static struct platform_driver imanager_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "imanager-core",
+ },
+ .probe = imanager_probe,
+ .remove = imanager_remove,
+};
+
+static int __init imanager_init(void)
+{
+ int ret;
+
+ ret = imanager_ec_probe();
+ if (ret < 0)
+ return ret;
+
+ ret = imanager_platform_create();
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&imanager_driver);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void __exit imanager_exit(void)
+{
+ if (pdev)
+ platform_device_unregister(pdev);
+
+ platform_driver_unregister(&imanager_driver);
+}
+
+module_init(imanager_init);
+module_exit(imanager_exit);
+
+MODULE_DESCRIPTION("Advantech iManager Core Driver");
+MODULE_AUTHOR("Richard Vidal-Dorsch <richard.dorsch at advantech.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imanager-core");
diff --git a/drivers/mfd/imanager-ec.c b/drivers/mfd/imanager-ec.c
new file mode 100644
index 0000000..be554ba
--- /dev/null
+++ b/drivers/mfd/imanager-ec.c
@@ -0,0 +1,1345 @@
+/*
+ * Advantech iManager Core - Firmware Interface
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/byteorder/generic.h>
+#include <linux/module.h>
+#include <linux/swab.h>
+#include <linux/mfd/imanager/ec.h>
+
+/**
+ * This is the delay time between two EC transactions.
+ * Values lower than 200us are not encouraged and may
+ * cause I/O errors
+ */
+#define EC_MICRO_DELAY 200
+#define EC_MAX_RETRY 1000
+
+#define EC_MSG_OFFSET_CMD 0UL
+#define EC_MSG_OFFSET_STATUS 1UL
+#define EC_MSG_OFFSET_PARAM 2UL
+#define EC_MSG_OFFSET_DATA(N) (3UL + N)
+#define EC_MSG_OFFSET_PAYLOAD(N) (7UL + N)
+
+/* The Device ID registers - 16 bit */
+#define DEVID_REG_MSB 0x20
+#define DEVID_REG_LSB 0x21
+
+/*
+ * IT8528 based firmware require a read/write command offset.
+ */
+#define EC_CMD_OFFSET_READ 0xA0UL
+#define EC_CMD_OFFSET_WRITE 0x50UL
+
+#define EC_STATUS_SUCCESS BIT(0)
+#define EC_STATUS_CMD_COMPLETE BIT(7)
+
+#define PCB_NAME_MAX_SIZE 8UL
+#define EC_I2C_BLOCK_SIZE 32
+#define EC_MAX_DID 32UL
+
+#define DID_LABEL_SIZE 24UL
+#define DID_DESC_SIZE 32UL
+
+#define EC_KERNEL_MINOR(x) (LOBYTE16(x))
+#define EC_KERNEL_MAJOR(x) ({ \
+ typeof(x) __x = (HIBYTE16(x)); \
+ ((__x >> 4) * 10 + (__x & 0x000f)); })
+#define EC_FIRMWARE_MINOR(x) (LOBYTE16(x))
+#define EC_FIRMWARE_MAJOR(x) EC_KERNEL_MAJOR(x)
+#define EC_PROJECT_CODE(x) ((char)(LOBYTE16(x)))
+
+enum ec_device_type {
+ ADC = 1,
+ DAC,
+ GPIO,
+ IRQ,
+ PWM,
+ SMB,
+ TACH
+};
+
+enum ec_device_id {
+ /* GPIO */
+ ALTGPIO0 = 0x10,
+ ALTGPIO1,
+ ALTGPIO2,
+ ALTGPIO3,
+ ALTGPIO4,
+ ALTGPIO5,
+ ALTGPIO6,
+ ALTGPIO7,
+ /* Button (GPIO) */
+ BUTTON0,
+ BUTTON1,
+ BUTTON2,
+ BUTTON3,
+ BUTTON4,
+ BUTTON5,
+ BUTTON6,
+ BUTTON7,
+ /* FAN */
+ CPUFAN_2P,
+ CPUFAN_4P,
+ SYSFAN1_2P,
+ SYSFAN1_4P,
+ SYSFAN2_2P,
+ SYSFAN2_4P,
+ /* Brightness Control */
+ BRIGHTNESS,
+ /* System Speaker */
+ PCBEEP,
+ /* SMBus */
+ SMBOEM0,
+ SMBOEM1,
+ SMBOEM2,
+ SMBEEPROM,
+ SMBTHERMAL0,
+ SMBTHERMAL1,
+ SMBSECEEP,
+ I2COEM,
+ /* Speaker */
+ SPEAKER = 0x30,
+ /* SMBus */
+ SMBEEP2K = 0x38,
+ OEMEEP,
+ OEMEEP2K,
+ PECI,
+ SMBOEM3,
+ SMLINK,
+ SMBSLV,
+ /* LED */
+ POWERLED = 0x40,
+ BATLEDG,
+ OEMLED0,
+ OEMLED1,
+ OEMLED2,
+ BATLEDR,
+ /* Smart Battery */
+ SMARTBAT1 = 0x48,
+ SMARTBAT2,
+ /* ADC */
+ CMOSBAT = 0x50,
+ CMOSBATx2,
+ CMOSBATx10,
+ LIBAT,
+ LIBATx2,
+ LIBATx10,
+ ADC5VS0,
+ ADC5VS0x2,
+ ADC5VS0x10,
+ ADC5VS5,
+ ADC5VS5x2,
+ ADC5VS5x10,
+ ADC33VS0,
+ ADC33VS0x2,
+ ADC33VS0x10,
+ ADC33VS5,
+ ADC33VS5x2, /* 0x60 */
+ ADC33VS5x10,
+ ADC12VS0,
+ ADC12VS0x2,
+ ADC12VS0x10,
+ VCOREA,
+ VCOREAx2,
+ VCOREAx10,
+ VCOREB,
+ VCOREBx2,
+ VCOREBx10,
+ ADCDC,
+ ADCDCx2,
+ ADCDCx10,
+ VSTBY,
+ VSTBYx2,
+ VSTBYx10, /* 0x70 */
+ VAUX,
+ VAUXx2,
+ VAUXx10,
+ CURRENT,
+ /* Watchdog */
+ WDIRQ = 0x78,
+ WDNMI,
+ /* FAN Tacho */
+ TACHO0 = 0x80,
+ TACHO1,
+ TACHO2,
+ /* Brightness/Backlight Control */
+ BRIGHTNESS2 = 0x88,
+ BACKLIGHT1,
+ BACKLIGHT2
+};
+
+enum ec_dynamic_table_type {
+ EC_DYN_DID,
+ EC_DYN_HWP,
+ EC_DYN_POL
+};
+
+struct ec_devtbl {
+ int id;
+ int type;
+ int scale;
+ const char label[DID_LABEL_SIZE];
+ const char description[DID_DESC_SIZE];
+};
+
+struct ec_dyn_devtbl {
+ int did; /* Device ID */
+ int hwp; /* Hardware Pin */
+ int pol; /* Polarity */
+ const struct ec_devtbl *devtbl; /* Device table Entry */
+};
+
+struct imanager_data {
+ int (*read)(int cmd);
+ int (*write)(int cmd, int value);
+
+ struct ec_dyn_devtbl dyn[EC_MAX_DID];
+
+ struct imanager_ec_device dev;
+ struct imanager_hwmon_device sensors;
+ struct imanager_gpio_device gpio;
+ struct imanager_i2c_device i2c;
+ struct imanager_watchdog_device wdt;
+ struct imanager_backlight_device blc;
+};
+
+struct ec_version_raw {
+ u16 kernel,
+ chipid,
+ project_code,
+ firmware;
+};
+
+enum ec_ram_type {
+ EC_RAM_ACPI = 1,
+ EC_RAM_HW,
+ EC_RAM_EXT
+};
+
+static struct imanager_data ec;
+
+static const struct ec_devtbl devtbl[] = {
+ { ALTGPIO0, GPIO, -1, "gpio0" },
+ { ALTGPIO1, GPIO, -1, "gpio1" },
+ { ALTGPIO2, GPIO, -1, "gpio2" },
+ { ALTGPIO3, GPIO, -1, "gpio3" },
+ { ALTGPIO4, GPIO, -1, "gpio4" },
+ { ALTGPIO5, GPIO, -1, "gpio5" },
+ { ALTGPIO6, GPIO, -1, "gpio6" },
+ { ALTGPIO7, GPIO, -1, "gpio7" },
+ { BUTTON0, GPIO, -1, "button0" },
+ { BUTTON1, GPIO, -1, "button1" },
+ { BUTTON2, GPIO, -1, "button2" },
+ { BUTTON3, GPIO, -1, "button3" },
+ { BUTTON4, GPIO, -1, "button4" },
+ { BUTTON5, GPIO, -1, "button4" },
+ { BUTTON6, GPIO, -1, "button4" },
+ { BUTTON7, GPIO, -1, "button4" },
+ { CPUFAN_2P, PWM, 2, "FAN CPU" },
+ { CPUFAN_4P, PWM, 4, "FAN CPU" },
+ { SYSFAN1_2P, PWM, 2, "FAN SYS1" },
+ { SYSFAN1_4P, PWM, 4, "FAN SYS1" },
+ { SYSFAN2_2P, PWM, 2, "FAN SYS2" },
+ { SYSFAN2_4P, PWM, 4, "FAN SYS2" },
+ { BRIGHTNESS, PWM, -1, "Brightness1" },
+ { PCBEEP, PWM, -1, "Beep" },
+ { SMBOEM0, SMB, -1, "SMB1" },
+ { SMBOEM1, SMB, -1, "SMB2" },
+ { SMBOEM2, SMB, -1, "SMB3" },
+ { SMBEEPROM, SMB, -1, "SMBEEP" },
+ { SMBTHERMAL0, SMB, -1, "SMBTHERM0" },
+ { SMBTHERMAL1, SMB, -1, "SMBTHERM1" },
+ { SMBSECEEP, SMB, -1, "SMBSECEEP" },
+ { I2COEM, SMB, -1, "I2COEM" },
+ { SPEAKER, DAC, -1, "Speaker" },
+ { SMBEEP2K, SMB, -1, "SMBEEP2K" },
+ { OEMEEP, SMB, -1, "OEMEEP" },
+ { OEMEEP2K, SMB, -1, "OEMEEP2K" },
+ { PECI, SMB, -1, "SMB_PECI" },
+ { SMBOEM3, SMB, -1, "SMBOEM3" },
+ { SMLINK, SMB, -1, "SMLINK" },
+ { SMBSLV, SMB, -1, "SMBSLV" },
+ { POWERLED, GPIO, -1, "Power LED" },
+ { BATLEDG, GPIO, -1, "BATLEDG" },
+ { OEMLED0, GPIO, -1, "OEMLED0" },
+ { OEMLED1, GPIO, -1, "OEMLED1" },
+ { OEMLED2, GPIO, -1, "OEMLED2" },
+ { BATLEDR, GPIO, -1, "OEMLEDR" },
+ { SMARTBAT1, SMB, -1, "SmartBat1" },
+ { SMARTBAT2, SMB, -1, "SmartBat2" },
+ { CMOSBAT, ADC, 1, "VBat" },
+ { CMOSBATx2, ADC, 2, "VBat" },
+ { CMOSBATx10, ADC, 10, "VBat" },
+ { LIBAT, ADC, 1, "VBat2" },
+ { LIBATx2, ADC, 2, "VBat2" },
+ { LIBATx10, ADC, 10, "VBat2" },
+ { ADC5VS0, ADC, 1, "+5V" },
+ { ADC5VS0x2, ADC, 2, "+5V" },
+ { ADC5VS0x10, ADC, 10, "+5V" },
+ { ADC5VS5, ADC, 1, "+5V" },
+ { ADC5VS5x2, ADC, 2, "+5V" },
+ { ADC5VS5x10, ADC, 10, "+5V" },
+ { ADC33VS0, ADC, 1, "+3.3V" },
+ { ADC33VS0x2, ADC, 2, "+3.3V" },
+ { ADC33VS0x10, ADC, 10, "+3.3V" },
+ { ADC33VS5, ADC, 1, "+3.3V" },
+ { ADC33VS5x2, ADC, 2, "+3.3V" },
+ { ADC33VS5x10, ADC, 10, "+3.3V" },
+ { ADC12VS0, ADC, 1, "+12V" },
+ { ADC12VS0x2, ADC, 2, "+12V" },
+ { ADC12VS0x10, ADC, 10, "+12V" },
+ { VCOREA, ADC, 1, "VCore" },
+ { VCOREAx2, ADC, 2, "VCore" },
+ { VCOREAx10, ADC, 10, "VCore" },
+ { VCOREB, ADC, 1, "VCore2" },
+ { VCOREBx2, ADC, 2, "VCore2" },
+ { VCOREBx10, ADC, 10, "VCore2" },
+ { ADCDC, ADC, 1, "ADCDC" },
+ { ADCDCx2, ADC, 2, "ADCDCx2" },
+ { ADCDCx10, ADC, 10, "ADCDCx10" },
+ { VSTBY, ADC, 1, "Vsb" },
+ { VSTBYx2, ADC, 2, "Vsb" },
+ { VSTBYx10, ADC, 10, "Vsb" },
+ { VAUX, ADC, 1, "VAUX" },
+ { VAUXx2, ADC, 2, "VAUX" },
+ { VAUXx10, ADC, 10, "VAUX" },
+ { CURRENT, ADC, 1, "Imon" },
+ { WDIRQ, IRQ, -1, "WDIRQ" },
+ { WDNMI, GPIO, -1, "WDNMI" },
+ { TACHO0, TACH, -1, "Tacho1" },
+ { TACHO1, TACH, -1, "Tacho2" },
+ { TACHO2, TACH, -1, "Tacho3" },
+ { BRIGHTNESS2, PWM, -1, "Brightness2" },
+ { BACKLIGHT1, GPIO, -1, "Backlight1" },
+ { BACKLIGHT2, GPIO, -1, "Backlight2" },
+ { 0, 0, 0, "" }
+};
+
+/**
+ * EC I/O
+ */
+
+static inline void imanager_delay(void)
+{
+ udelay(EC_MICRO_DELAY);
+}
+
+static int wait_ibf_cleared(void)
+{
+ int i = 0;
+
+ do {
+ if (!(inb(IT8516_CMD_PORT) & BIT(1)))
+ return 0;
+ imanager_delay();
+ } while (i++ < EC_MAX_RETRY);
+
+ return -ETIME;
+}
+
+static int wait_obf_set(void)
+{
+ int i = 0;
+
+ do {
+ if (inb(IT8516_CMD_PORT) & BIT(0))
+ return 0;
+ imanager_delay();
+ } while (i++ < EC_MAX_RETRY);
+
+ return -ETIME;
+}
+
+static inline int ec_inb(int addr, int reg)
+{
+ outb(reg, addr);
+ return inb(addr + 1);
+}
+
+static inline void ec_outb(int addr, int reg, int val)
+{
+ outb(reg, addr);
+ outb(val, addr + 1);
+}
+
+static inline int ec_io28_inb(int addr, int reg)
+{
+ int ret;
+
+ ret = wait_ibf_cleared();
+ if (ret)
+ return ret;
+
+ /* clear data to prevent lock */
+ inb(addr - 1);
+
+ outb(reg, addr);
+
+ ret = wait_obf_set();
+ if (ret)
+ return ret;
+
+ return inb(addr - 1);
+}
+
+static inline int ec_io28_outb(int addr, int reg, int val)
+{
+ int ret;
+
+ ret = wait_ibf_cleared();
+ if (ret)
+ return ret;
+
+ outb(reg, addr);
+
+ ret = wait_ibf_cleared();
+ if (ret)
+ return ret;
+
+ outb(val, addr - 1);
+
+ return 0;
+}
+
+static int ec_io18_read(int cmd)
+{
+ return ec_inb(IT8518_CMD_PORT, cmd);
+}
+
+static int ec_io18_write(int cmd, int value)
+{
+ ec_outb(IT8518_CMD_PORT, cmd, value);
+
+ return 0;
+}
+
+static int ec_io28_read(int cmd)
+{
+ return ec_io28_inb(IT8516_CMD_PORT, cmd + EC_CMD_OFFSET_READ);
+}
+
+static int ec_io28_write(int cmd, int value)
+{
+ return ec_io28_outb(IT8516_CMD_PORT, cmd + EC_CMD_OFFSET_WRITE, value);
+}
+
+/* Prevent FW lock */
+static void ec_clear_ports(void)
+{
+ inb(IT8516_DAT_PORT);
+ inb(IT8518_DAT_PORT);
+}
+
+static inline u16 ec_read_chipid(u16 addr)
+{
+ return (ec_inb(addr, DEVID_REG_MSB) << 8 |
+ ec_inb(addr, DEVID_REG_LSB));
+}
+
+static int ec_wait_cmd_clear(void)
+{
+ int i = 0;
+
+ do {
+ if (!ec.read(0))
+ return 0;
+ imanager_delay();
+ } while (i++ < EC_MAX_RETRY);
+
+ pr_err("No respons from EC (timeout)\n");
+
+ return -ETIME;
+}
+
+static int ec_read_ram(u8 bank, u8 offset, u8 len, u8 *buf, u8 bufsz)
+{
+ int i;
+ int ret;
+
+ if (WARN_ON(!buf))
+ return -EINVAL;
+
+ ret = ec_wait_cmd_clear();
+ if (ret)
+ return ret;
+
+ ec.write(EC_MSG_OFFSET_PARAM, bank);
+ ec.write(EC_MSG_OFFSET_DATA(0), offset);
+ ec.write(EC_MSG_OFFSET_DATA(0x2C), len);
+ ec.write(EC_MSG_OFFSET_CMD, EC_CMD_RAM_RD);
+
+ ret = ec_wait_cmd_clear();
+ if (ret)
+ return ret;
+
+ ret = ec.read(EC_MSG_OFFSET_STATUS);
+ if (ret != EC_STATUS_SUCCESS)
+ return -EIO;
+
+ for (i = 0; (i < len) && (len < EC_MSG_SIZE) && (len <= bufsz); i++)
+ buf[i] = ec.read(EC_MSG_OFFSET_DATA(i + 1));
+
+ return 0;
+}
+
+static int ec_write_ram(u8 bank, u8 offset, u8 len, u8 *buf)
+{
+ int i;
+ int ret;
+
+ if (WARN_ON(!buf))
+ return -EINVAL;
+
+ ret = ec_wait_cmd_clear();
+ if (ret)
+ return ret;
+
+ ec.write(EC_MSG_OFFSET_PARAM, bank);
+ ec.write(EC_MSG_OFFSET_DATA(0), offset);
+ ec.write(EC_MSG_OFFSET_DATA(0x2C), len);
+
+ for (i = 0; (i < len) && (len < EC_MSG_SIZE); i++)
+ ec.write(EC_MSG_OFFSET_DATA(i + 1), buf[i]);
+
+ ec.write(EC_MSG_OFFSET_CMD, EC_CMD_RAM_WR);
+
+ ret = ec_wait_cmd_clear();
+ if (ret)
+ return ret;
+
+ ret = ec.read(EC_MSG_OFFSET_STATUS);
+ if (ret != EC_STATUS_SUCCESS)
+ return -EIO;
+
+ return 0;
+}
+
+static int ec_read_dynamic_devtbl(struct imanager_data *ec)
+{
+ u32 i, j;
+ int ret;
+ struct ec_message did = {
+ .rlen = EC_MAX_DID,
+ .wlen = 0,
+ };
+ struct ec_message hwp = {
+ .rlen = EC_MAX_DID,
+ .wlen = 0,
+ };
+ struct ec_message pol = {
+ .rlen = EC_MAX_DID,
+ .wlen = 0,
+ };
+ struct ec_dyn_devtbl *dyn;
+
+ memset(ec->dyn, 0, sizeof(ec->dyn));
+
+ ret = imanager_msg_read(EC_CMD_DYN_TBL_RD, EC_DYN_DID, &did);
+ if (ret)
+ return -EIO;
+
+ ret = imanager_msg_read(EC_CMD_DYN_TBL_RD, EC_DYN_HWP, &hwp);
+ if (ret)
+ return -EIO;
+
+ ret = imanager_msg_read(EC_CMD_DYN_TBL_RD, EC_DYN_POL, &pol);
+ if (ret)
+ return -EIO;
+
+ for (i = 0; (i < EC_MAX_DID) && did.u.data[i]; i++) {
+ dyn = &ec->dyn[i];
+ for (j = 0; j < ARRAY_SIZE(devtbl); j++) {
+ if (devtbl[j].id == did.u.data[i]) {
+ dyn->did = did.u.data[i];
+ dyn->hwp = hwp.u.data[i];
+ dyn->pol = pol.u.data[i];
+ dyn->devtbl = &devtbl[j];
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int ec_read_buffer(u8 *data, int rlen)
+{
+ int ret, i, j;
+ int pages = rlen % EC_I2C_BLOCK_SIZE;
+ int rem = rlen / EC_I2C_BLOCK_SIZE;
+
+ /* pre-condition: rlen <= 256 */
+
+ ret = ec_wait_cmd_clear();
+ if (ret)
+ return ret;
+
+ for (i = 0; i < pages; i++) {
+ ec.write(EC_MSG_OFFSET_PARAM, i);
+ ec.write(EC_MSG_OFFSET_CMD, EC_CMD_BUF_RD);
+
+ ret = ec_wait_cmd_clear();
+ if (ret)
+ return ret;
+
+ ret = ec.read(EC_MSG_OFFSET_STATUS);
+ if (ret != EC_STATUS_SUCCESS)
+ return -EIO;
+
+ for (j = 0; j < EC_I2C_BLOCK_SIZE; j++)
+ data[i * EC_I2C_BLOCK_SIZE + j] =
+ ec.read(EC_MSG_OFFSET_DATA(j));
+ }
+
+ if (rem) {
+ ec.write(EC_MSG_OFFSET_PARAM, pages);
+ ec.write(EC_MSG_OFFSET_CMD, EC_CMD_BUF_RD);
+
+ ret = ec_wait_cmd_clear();
+ if (ret)
+ return ret;
+
+ ret = ec.read(EC_MSG_OFFSET_STATUS);
+ if (ret != EC_STATUS_SUCCESS)
+ return -EIO;
+
+ for (j = 0; j < rem; j++)
+ data[pages * EC_I2C_BLOCK_SIZE + j] =
+ ec.read(EC_MSG_OFFSET_DATA(j));
+ }
+
+ return 0;
+}
+
+static int
+imanager_msg_trans(u8 cmd, u8 param, struct ec_message *msg, bool payload)
+{
+ int ret, i, len;
+ u32 offset;
+
+ ret = ec_wait_cmd_clear();
+ if (ret)
+ return ret;
+
+ ec.write(EC_MSG_OFFSET_PARAM, param);
+
+ if (msg && msg->wlen) {
+ if (!msg->data) {
+ for (i = 0; i < msg->wlen; i++)
+ ec.write(EC_MSG_OFFSET_DATA(i),
+ msg->u.data[i]);
+ } else {
+ for (i = 0; i < msg->wlen; i++)
+ ec.write(EC_MSG_OFFSET_DATA(i), msg->data[i]);
+ ec.write(EC_MSG_OFFSET_DATA(0x2c), msg->wlen);
+ }
+ }
+
+ ec.write(EC_MSG_OFFSET_CMD, cmd);
+ ret = ec_wait_cmd_clear();
+ if (ret)
+ return ret;
+
+ /* GPIO and I2C have different success return values */
+ ret = ec.read(EC_MSG_OFFSET_STATUS);
+ if ((ret != EC_STATUS_SUCCESS) && !(ret & EC_STATUS_CMD_COMPLETE))
+ return -EIO;
+ /*
+ * EC I2C may return an error code which we need to hand-off
+ * to the caller
+ */
+ else if (ret & 0x07e)
+ return ret;
+
+ if (msg && msg->data) {
+ ret = ec_read_buffer(msg->data, msg->rlen);
+ if (ret < 0)
+ return ret;
+ } else if (msg && msg->rlen) {
+ if (msg->rlen == 0xff)
+ /* Use alternate message body for hwmon */
+ len = ec.read(EC_MSG_OFFSET_DATA(0x2C));
+ else
+ len = (msg->rlen > EC_MSG_SIZE ? EC_MSG_SIZE :
+ msg->rlen);
+ offset = payload ? EC_MSG_OFFSET_PAYLOAD(0) :
+ EC_MSG_OFFSET_DATA(0);
+ for (i = 0; i < len; i++)
+ msg->u.data[i] = ec.read(offset + i);
+ }
+
+ return 0;
+}
+
+int imanager_msg_read(u8 cmd, u8 param, struct ec_message *msg)
+{
+ return imanager_msg_trans(cmd, param, msg, false);
+}
+EXPORT_SYMBOL_GPL(imanager_msg_read);
+
+int imanager_msg_write(u8 cmd, u8 param, struct ec_message *msg)
+{
+ return imanager_msg_trans(cmd, param, msg, true);
+}
+EXPORT_SYMBOL_GPL(imanager_msg_write);
+
+int imanager_read_byte(u8 cmd, u8 param)
+{
+ int ret;
+ struct ec_message msg = {
+ .rlen = 1,
+ .wlen = 0,
+ };
+
+ ret = imanager_msg_read(cmd, param, &msg);
+ if (ret)
+ return ret;
+
+ return msg.u.data[0];
+}
+EXPORT_SYMBOL_GPL(imanager_read_byte);
+
+int imanager_read_word(u8 cmd, u8 param)
+{
+ int ret;
+ struct ec_message msg = {
+ .rlen = 2,
+ .wlen = 0,
+ };
+
+ ret = imanager_msg_read(cmd, param, &msg);
+ if (ret)
+ return ret;
+
+ return (msg.u.data[0] << 8 | msg.u.data[1]);
+}
+EXPORT_SYMBOL_GPL(imanager_read_word);
+
+int imanager_write_byte(u8 cmd, u8 param, u8 byte)
+{
+ struct ec_message msg = {
+ .rlen = 0,
+ .wlen = 1,
+ .u = {
+ .data = { byte, 0 },
+ },
+ };
+
+ return imanager_msg_write(cmd, param, &msg);
+}
+EXPORT_SYMBOL_GPL(imanager_write_byte);
+
+int imanager_write_word(u8 cmd, u8 param, u16 word)
+{
+ struct ec_message msg = {
+ .rlen = 0,
+ .wlen = 2,
+ .u = {
+ .data = { HIBYTE16(word), LOBYTE16(word), 0 },
+ },
+ };
+
+ return imanager_msg_write(cmd, param, &msg);
+}
+EXPORT_SYMBOL_GPL(imanager_write_word);
+
+static int ec_hwram_read_byte(u8 offset)
+{
+ int ret;
+ u8 val;
+
+ ret = ec_read_ram(EC_RAM_HW, offset, sizeof(val), &val, sizeof(val));
+ if (ret < 0) {
+ pr_err("Failed to read from HWRAM @ 0x%02X\n", offset);
+ return ret;
+ }
+
+ return val;
+}
+
+int imanager_acpiram_read_byte(u8 offset)
+{
+ int ret;
+ u8 value;
+
+ ret = ec_read_ram(EC_RAM_ACPI, offset, sizeof(value), (u8 *)&value,
+ sizeof(value));
+ if (ret < 0) {
+ pr_err("Failed to read from ACPI RAM @ 0x%02X\n", offset);
+ return ret;
+ }
+
+ return value;
+}
+EXPORT_SYMBOL_GPL(imanager_acpiram_read_byte);
+
+int imanager_acpiram_write_byte(u8 offset, u8 value)
+{
+ int ret;
+
+ ret = ec_write_ram(EC_RAM_ACPI, offset, sizeof(value), &value);
+ if (ret) {
+ pr_err("Failed to write to ACPI RAM @ 0x%02X\n", offset);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imanager_acpiram_write_byte);
+
+int imanager_acpiram_read_block(u8 offset, u8 *buf, u8 len)
+{
+ int ret;
+
+ ret = ec_read_ram(EC_RAM_ACPI, offset, len, buf, len);
+ if (ret < 0) {
+ pr_err("Failed to read from ACPI RAM @ 0x%02X\n", offset);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imanager_acpiram_read_block);
+
+int imanager_acpiram_write_block(u8 offset, u8 *buf, u8 len)
+{
+ int ret;
+
+ ret = ec_write_ram(EC_RAM_ACPI, offset, len, buf);
+ if (ret) {
+ pr_err("Failed to write to ACPI RAM @ 0x%02X\n", offset);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imanager_acpiram_write_block);
+
+int imanager_wait_proc_complete(u8 offset, int cond)
+{
+ int ret, i;
+
+ for (i = 0; i < EC_MAX_RETRY; i++) {
+ ret = ec_hwram_read_byte(offset);
+ if (ret < 0)
+ return ret;
+
+ if (ret == cond)
+ return 0;
+
+ imanager_delay();
+ }
+
+ return -EIO;
+}
+EXPORT_SYMBOL_GPL(imanager_wait_proc_complete);
+
+static inline void ec_get_dev_attr(struct ec_dev_attr *attr,
+ const struct ec_dyn_devtbl *tbl)
+{
+ attr->did = tbl->did;
+ attr->hwp = tbl->hwp;
+ attr->pol = tbl->pol;
+ attr->scale = tbl->devtbl->scale;
+ attr->label = tbl->devtbl->label;
+}
+
+static int ec_get_dev_gpio(struct imanager_data *ec)
+{
+ size_t i;
+ struct ec_dyn_devtbl *dyn;
+ struct imanager_gpio_device *gpio = &ec->gpio;
+
+ for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) {
+ dyn = &ec->dyn[i];
+ if (dyn->did && (dyn->devtbl->type == GPIO)) {
+ switch (dyn->did) {
+ case ALTGPIO0:
+ ec_get_dev_attr(&gpio->attr[0], dyn);
+ gpio->num++;
+ break;
+ case ALTGPIO1:
+ ec_get_dev_attr(&gpio->attr[1], dyn);
+ gpio->num++;
+ break;
+ case ALTGPIO2:
+ ec_get_dev_attr(&gpio->attr[2], dyn);
+ gpio->num++;
+ break;
+ case ALTGPIO3:
+ ec_get_dev_attr(&gpio->attr[3], dyn);
+ gpio->num++;
+ break;
+ case ALTGPIO4:
+ ec_get_dev_attr(&gpio->attr[4], dyn);
+ gpio->num++;
+ break;
+ case ALTGPIO5:
+ ec_get_dev_attr(&gpio->attr[5], dyn);
+ gpio->num++;
+ break;
+ case ALTGPIO6:
+ ec_get_dev_attr(&gpio->attr[6], dyn);
+ gpio->num++;
+ break;
+ case ALTGPIO7:
+ ec_get_dev_attr(&gpio->attr[7], dyn);
+ gpio->num++;
+ break;
+ case BUTTON0:
+ case BUTTON1:
+ case BUTTON2:
+ case BUTTON3:
+ case BUTTON4:
+ case BUTTON5:
+ case BUTTON6:
+ case BUTTON7:
+ case POWERLED:
+ case BATLEDG:
+ case OEMLED0:
+ case OEMLED1:
+ case OEMLED2:
+ case BATLEDR:
+ case WDNMI:
+ case BACKLIGHT1:
+ case BACKLIGHT2:
+ break;
+ default:
+ pr_err("DID 0x%02X not handled\n", dyn->did);
+ return -EINVAL;
+ }
+ }
+ }
+
+ gpio->info = &ec->dev.info;
+
+ return 0;
+}
+
+static int ec_get_dev_adc(struct imanager_data *ec)
+{
+ size_t i;
+ struct ec_dyn_devtbl *dyn;
+ struct dev_adc *adc = &ec->sensors.adc;
+
+ for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) {
+ dyn = &ec->dyn[i];
+ if (dyn->did && (dyn->devtbl->type == ADC)) {
+ switch (dyn->did) {
+ case ADC12VS0:
+ case ADC12VS0x2:
+ case ADC12VS0x10:
+ ec_get_dev_attr(&adc->attr[0], dyn);
+ adc->num++;
+ break;
+ case ADC5VS5:
+ case ADC5VS5x2:
+ case ADC5VS5x10:
+ ec_get_dev_attr(&adc->attr[1], dyn);
+ adc->num++;
+ break;
+ case CMOSBAT:
+ case CMOSBATx2:
+ case CMOSBATx10:
+ ec_get_dev_attr(&adc->attr[2], dyn);
+ adc->num++;
+ break;
+ case VCOREA:
+ case ADC5VS0:
+ case ADC5VS0x2:
+ case ADC5VS0x10:
+ ec_get_dev_attr(&adc->attr[3], dyn);
+ adc->num++;
+ break;
+ case CURRENT:
+ case ADC33VS0:
+ case ADC33VS0x2:
+ case ADC33VS0x10:
+ ec_get_dev_attr(&adc->attr[4], dyn);
+ adc->num++;
+ break;
+ default:
+ pr_err("DID 0x%02X not handled\n", dyn->did);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int ec_get_dev_fan(struct imanager_data *ec)
+{
+ size_t i;
+ struct ec_dyn_devtbl *dyn;
+ struct dev_fan *fan = &ec->sensors.fan;
+
+ for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) {
+ dyn = &ec->dyn[i];
+ if (dyn->did && ((dyn->devtbl->type == TACH) ||
+ (dyn->devtbl->type == PWM))) {
+ switch (dyn->did) {
+ case CPUFAN_2P:
+ case CPUFAN_4P:
+ ec_get_dev_attr(&fan->attr[0], dyn);
+ fan->num++;
+ fan->active |= 1 << 0;
+ break;
+ case SYSFAN1_2P:
+ case SYSFAN1_4P:
+ ec_get_dev_attr(&fan->attr[1], dyn);
+ fan->num++;
+ fan->active |= 1 << 1;
+ break;
+ case SYSFAN2_2P:
+ case SYSFAN2_4P:
+ ec_get_dev_attr(&fan->attr[2], dyn);
+ fan->num++;
+ fan->active |= 1 << 2;
+ break;
+ case TACHO0:
+ case TACHO1:
+ case TACHO2:
+ case BRIGHTNESS:
+ case BRIGHTNESS2:
+ case PCBEEP:
+ break;
+ default:
+ pr_err("DID 0x%02X not handled\n", dyn->did);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int ec_get_dev_hwmon(struct imanager_data *ec)
+{
+ int ret;
+
+ ret = ec_get_dev_adc(ec);
+ if (ret < 0)
+ return ret;
+
+ ret = ec_get_dev_fan(ec);
+ if (ret < 0)
+ return ret;
+
+ ec->sensors.ecdev = &ec->dev;
+
+ return 0;
+}
+
+static int ec_get_dev_i2c(struct imanager_data *ec)
+{
+ size_t i;
+ struct ec_dyn_devtbl *dyn;
+ struct imanager_i2c_device *i2c = &ec->i2c;
+
+ for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) {
+ dyn = &ec->dyn[i];
+ if (dyn->did && (dyn->devtbl->type == SMB)) {
+ switch (dyn->did) {
+ case SMBEEPROM:
+ ec_get_dev_attr(&i2c->attr[0], dyn);
+ i2c->eeprom = &i2c->attr[0];
+ i2c->num++;
+ break;
+ case I2COEM:
+ ec_get_dev_attr(&i2c->attr[1], dyn);
+ i2c->i2coem = &i2c->attr[1];
+ i2c->num++;
+ break;
+ case SMBOEM0:
+ case SMBOEM1:
+ case SMBOEM2:
+ case SMBTHERMAL0:
+ case SMBTHERMAL1:
+ case SMBSECEEP:
+ case SMBEEP2K:
+ case OEMEEP:
+ case OEMEEP2K:
+ case PECI:
+ case SMBOEM3:
+ case SMLINK:
+ case SMBSLV:
+ case SMARTBAT1:
+ case SMARTBAT2:
+ break;
+ default:
+ pr_err("DID 0x%02X not handled\n", dyn->did);
+ return -EINVAL;
+ }
+ }
+ }
+
+ i2c->ecdev = &ec->dev;
+
+ return 0;
+}
+
+static int ec_get_dev_blc(struct imanager_data *ec)
+{
+ size_t i;
+ struct ec_dyn_devtbl *dyn;
+ struct imanager_backlight_device *blc = &ec->blc;
+
+ for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) {
+ dyn = &ec->dyn[i];
+ if (dyn->did && (dyn->devtbl->type == PWM)) {
+ switch (dyn->did) {
+ case BRIGHTNESS:
+ ec_get_dev_attr(&blc->attr[0], dyn);
+ blc->brightness[0] = EC_ACPIRAM_BRIGHTNESS1;
+ blc->num++;
+ break;
+ case BRIGHTNESS2:
+ ec_get_dev_attr(&blc->attr[1], dyn);
+ blc->brightness[1] = EC_ACPIRAM_BRIGHTNESS2;
+ blc->num++;
+ break;
+ case CPUFAN_2P:
+ case CPUFAN_4P:
+ case SYSFAN1_2P:
+ case SYSFAN1_4P:
+ case SYSFAN2_2P:
+ case SYSFAN2_4P:
+ case PCBEEP:
+ case TACHO0:
+ case TACHO1:
+ case TACHO2:
+ break;
+ default:
+ pr_err("DID 0x%02X not handled\n", dyn->did);
+ return -EINVAL;
+ }
+ }
+ }
+
+ blc->info = &ec->dev.info;
+
+ return 0;
+}
+
+static int ec_get_dev_wdt(struct imanager_data *ec)
+{
+ size_t i;
+ struct ec_dyn_devtbl *dyn;
+ struct imanager_watchdog_device *wdt = &ec->wdt;
+
+ for (i = 0; i < ARRAY_SIZE(ec->dyn); i++) {
+ dyn = &ec->dyn[i];
+ if (dyn->did && (dyn->devtbl->type == IRQ)) {
+ switch (dyn->did) {
+ case WDIRQ:
+ ec_get_dev_attr(&wdt->attr[0], dyn);
+ wdt->irq = &wdt->attr[0];
+ wdt->num++;
+ break;
+ case WDNMI:
+ ec_get_dev_attr(&wdt->attr[1], dyn);
+ wdt->nmi = &wdt->attr[1];
+ wdt->num++;
+ break;
+ default:
+ pr_err("DID 0x%02X not handled\n", dyn->did);
+ return -EINVAL;
+ }
+ }
+ }
+
+ wdt->info = &ec->dev.info;
+
+ return 0;
+}
+
+const struct imanager_ec_device *imanager_get_ec_device(void)
+{
+ return &ec.dev;
+};
+EXPORT_SYMBOL_GPL(imanager_get_ec_device);
+
+const struct imanager_hwmon_device *imanager_get_hwmon_device(void)
+{
+ return &ec.sensors;
+}
+EXPORT_SYMBOL_GPL(imanager_get_hwmon_device);
+
+const struct imanager_gpio_device *imanager_get_gpio_device(void)
+{
+ return &ec.gpio;
+}
+EXPORT_SYMBOL_GPL(imanager_get_gpio_device);
+
+const struct imanager_i2c_device *imanager_get_i2c_device(void)
+{
+ return &ec.i2c;
+}
+EXPORT_SYMBOL_GPL(imanager_get_i2c_device);
+
+const struct imanager_backlight_device *imanager_get_backlight_device(void)
+{
+ return &ec.blc;
+}
+EXPORT_SYMBOL_GPL(imanager_get_backlight_device);
+
+const struct imanager_watchdog_device *imanager_get_watchdog_device(void)
+{
+ return &ec.wdt;
+}
+EXPORT_SYMBOL_GPL(imanager_get_watchdog_device);
+
+static int ec_get_version(struct ec_version *version)
+{
+ int ret;
+ u16 raw;
+ struct ec_version_raw ver;
+
+ if (WARN_ON(!version))
+ return -EINVAL;
+
+ ret = ec_read_ram(EC_RAM_ACPI, EC_ACPIRAM_FW_RELEASE_RD, sizeof(ver),
+ (u8 *)&ver, sizeof(ver));
+ if (ret < 0)
+ return ret;
+
+ raw = swab16(ver.kernel);
+ version->kernel_major = EC_KERNEL_MAJOR(raw);
+ version->kernel_minor = EC_KERNEL_MINOR(raw);
+
+ raw = swab16(ver.firmware);
+ version->firmware_major = EC_FIRMWARE_MAJOR(raw);
+ version->firmware_minor = EC_FIRMWARE_MINOR(raw);
+
+ raw = swab16(ver.project_code);
+ version->type = EC_PROJECT_CODE(raw);
+
+ return 0;
+}
+
+static int ec_get_pcb_name(struct ec_info *info)
+{
+ int ret;
+ struct ec_message msg = {
+ .rlen = ARRAY_SIZE(info->pcb_name),
+ .wlen = 0,
+ };
+
+ if (WARN_ON(!info))
+ return -EINVAL;
+
+ ret = imanager_msg_read(EC_CMD_FW_INFO_RD, 0, &msg);
+ if (ret)
+ return ret;
+
+ /*
+ * Sadly, the string is not Null-terminated so we will need to read a
+ * fixed amount of chars. There is, apparently, no exact definition
+ * of board name (SOM6867 vs. MIO-5271).
+ */
+ memset(info->pcb_name, 0, ARRAY_SIZE(info->pcb_name));
+ strncpy(info->pcb_name, (const char *)msg.u.data, PCB_NAME_MAX_SIZE);
+
+ if (strchr(info->pcb_name, '-') == NULL)
+ info->pcb_name[PCB_NAME_MAX_SIZE - 1] = '\0';
+
+ return 0;
+}
+
+static int ec_get_fw_info(struct ec_info *info)
+{
+ int ret;
+
+ if (WARN_ON(!info))
+ return -EINVAL;
+
+ ret = ec_get_version(&info->version);
+ if (ret)
+ return ret;
+
+ return ec_get_pcb_name(info);
+}
+
+static int ec_init(void)
+{
+ int ret;
+
+ ec_clear_ports();
+
+ ret = ec_read_dynamic_devtbl(&ec);
+ if (ret)
+ return ret;
+
+ ret = ec_get_fw_info(&ec.dev.info);
+ if (ret < 0)
+ return ret;
+
+ ret = ec_get_dev_gpio(&ec);
+ if (ret < 0)
+ return ret;
+
+ ret = ec_get_dev_hwmon(&ec);
+ if (ret < 0)
+ return ret;
+
+ ret = ec_get_dev_i2c(&ec);
+ if (ret < 0)
+ return ret;
+
+ ret = ec_get_dev_blc(&ec);
+ if (ret < 0)
+ return ret;
+
+ ret = ec_get_dev_wdt(&ec);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int imanager_ec_probe(void)
+{
+ int chipid = ec_read_chipid(EC_BASE_ADDR);
+
+ memset((void *)&ec, 0, sizeof(ec));
+
+ switch (chipid) {
+ case EC_DEVID_IT8516:
+ pr_err("EC IT8516 not supported\n");
+ ec.dev.id = IT8516;
+ return -ENODEV;
+ case EC_DEVID_IT8518:
+ ec.read = ec_io18_read;
+ ec.write = ec_io18_write;
+ ec.dev.id = IT8518;
+ break;
+ case EC_DEVID_IT8528:
+ ec.read = ec_io28_read;
+ ec.write = ec_io28_write;
+ ec.dev.id = IT8528;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ ec.dev.addr = EC_BASE_ADDR;
+
+ return ec_init();
+}
+
diff --git a/include/linux/mfd/imanager/core.h b/include/linux/mfd/imanager/core.h
new file mode 100644
index 0000000..05c77fa
--- /dev/null
+++ b/include/linux/mfd/imanager/core.h
@@ -0,0 +1,31 @@
+/*
+ * Advantech iManager MFD core
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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.
+ */
+
+#ifndef __CORE_H__
+#define __CORE_H__
+
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/mfd/imanager/ec.h>
+
+struct imanager_platform_data {
+ const struct imanager_ec_device *dev;
+ const char *chip_name;
+};
+
+struct imanager_device_data {
+ struct device *dev;
+ struct mutex lock;
+};
+
+#endif
diff --git a/include/linux/mfd/imanager/ec.h b/include/linux/mfd/imanager/ec.h
new file mode 100644
index 0000000..783f268
--- /dev/null
+++ b/include/linux/mfd/imanager/ec.h
@@ -0,0 +1,210 @@
+/*
+ * Advantech iManager core - firmware interface
+ *
+ * Copyright (C) 2016 Advantech Co., Ltd., Irvine, CA, USA
+ * Author: Richard Vidal-Dorsch <richard.dorsch@advantech.com>
+ *
+ * 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.
+ */
+
+#ifndef __EC_H__
+#define __EC_H__
+
+#include <linux/types.h>
+
+#define EC_DEVID_IT8516 0x8516
+#define EC_DEVID_IT8518 0x8518
+#define EC_DEVID_IT8528 0x8528
+
+#define EC_BASE_ADDR 0x029C
+
+#define IT8516_CMD_PORT 0x029A /* it8528 */
+#define IT8516_DAT_PORT 0x0299
+
+#define IT8518_CMD_PORT 0x029E /* it8518 */
+#define IT8518_DAT_PORT 0x029F
+
+#define EC_GPIO_MAX_NUM 8
+#define EC_HWM_MAX_ADC 5
+#define EC_HWM_MAX_FAN 3
+#define EC_BLC_MAX_NUM 2
+#define EC_SMB_MAX_NUM 4
+#define EC_WDT_MAX_NUM 2
+
+#define PCB_NAME_SIZE 32
+#define EC_PAYLOAD_SIZE 40
+#define EC_MSG_SIZE sizeof(struct ec_smb_message)
+
+#define LOBYTE16(x) (x & 0x00FF)
+#define HIBYTE16(x) (LOBYTE16(x >> 8))
+#define LOADDR16(x) LOBYTE16(x)
+#define HIADDR16(x) (x >= 0xF000 ? LOBYTE16(x >> 8) : 0)
+
+/*
+ * iManager commands
+ */
+#define EC_CMD_HWP_RD 0x11UL
+#define EC_CMD_HWP_WR 0x12UL
+#define EC_CMD_GPIO_DIR_RD 0x30UL
+#define EC_CMD_GPIO_DIR_WR 0x31UL
+#define EC_CMD_PWM_FREQ_RD 0x36UL
+#define EC_CMD_PWM_FREQ_WR 0x32UL
+#define EC_CMD_PWM_POL_RD 0x37UL
+#define EC_CMD_PWM_POL_WR 0x33UL
+#define EC_CMD_SMB_FREQ_RD 0x34UL
+#define EC_CMD_SMB_FREQ_WR 0x35UL
+#define EC_CMD_FAN_CTL_RD 0x40UL
+#define EC_CMD_FAN_CTL_WR 0x41UL
+#define EC_CMD_THZ_RD 0x42UL
+#define EC_CMD_DYN_TBL_RD 0x20UL
+#define EC_CMD_FW_INFO_RD 0xF0UL
+#define EC_CMD_BUF_CLR 0xC0UL
+#define EC_CMD_BUF_RD 0xC1UL
+#define EC_CMD_BUF_WR 0xC2UL
+#define EC_CMD_RAM_RD 0x1EUL
+#define EC_CMD_RAM_WR 0x1FUL
+#define EC_CMD_I2C_RW 0x0EUL
+#define EC_CMD_I2C_WR 0x0FUL
+#define EC_CMD_WDT_CTRL 0x28UL
+
+/*
+ * ACPI and HW RAM offsets
+ */
+#define EC_ACPIRAM_FAN_ALERT 0x6FUL
+#define EC_ACPIRAM_FAN_SPEED_LIMIT 0x76UL
+#define EC_ACPIRAM_BRIGHTNESS1 0x50UL
+#define EC_ACPIRAM_BRIGHTNESS2 0x52UL
+#define EC_ACPIRAM_BLC_CTRL 0x99UL
+#define EC_ACPIRAM_FW_RELEASE_RD 0xF8UL
+
+enum chips { IT8516, IT8518, IT8528 };
+
+struct ec_message_header {
+ u8 addr_low; /* SMB low-byte address or data low-byte */
+ /* of byte-/word-transaction */
+ u8 addr_high; /* SMB high-byte address or data high-byte */
+ /* of word-transaction */
+ u8 rlen; /* SMB read length */
+ u8 wlen; /* SMB write length */
+ u8 cmd; /* SMB command */
+};
+
+struct ec_smb_message {
+ struct ec_message_header hdr;
+ u8 data[EC_PAYLOAD_SIZE];
+};
+
+struct ec_message {
+ u8 rlen; /* EC message read length */
+ u8 wlen; /* EC message write length */
+ union {
+ struct ec_smb_message smb;
+ u8 data[EC_MSG_SIZE];
+ } u;
+
+ u8 *data;
+};
+
+struct ec_version {
+ u32 kernel_major;
+ u32 kernel_minor;
+ u32 firmware_major;
+ u32 firmware_minor;
+ u32 type;
+};
+
+struct ec_info {
+ struct ec_version version;
+ char pcb_name[PCB_NAME_SIZE];
+};
+
+struct imanager_ec_device {
+ int id; /* enum chip */
+ u16 addr;
+ struct ec_info info;
+};
+
+struct ec_dev_attr {
+ int did; /* Device ID */
+ int hwp; /* Hardware Pin number */
+ int pol; /* Polarity */
+ int scale; /* Scaling factor */
+ const char *label;
+};
+
+struct imanager_gpio_device {
+ u32 num;
+ struct ec_dev_attr attr[EC_GPIO_MAX_NUM];
+ struct ec_info *info;
+};
+
+struct dev_adc {
+ u32 num;
+ struct ec_dev_attr attr[EC_HWM_MAX_ADC];
+};
+
+struct dev_fan {
+ u32 num;
+ u32 active;
+ struct ec_dev_attr attr[EC_HWM_MAX_FAN];
+};
+
+struct imanager_hwmon_device {
+ struct dev_adc adc;
+ struct dev_fan fan;
+ struct imanager_ec_device *ecdev;
+};
+
+struct imanager_i2c_device {
+ u32 num;
+ struct ec_dev_attr attr[EC_SMB_MAX_NUM];
+ struct ec_dev_attr *eeprom;
+ struct ec_dev_attr *i2coem;
+ struct imanager_ec_device *ecdev;
+};
+
+struct imanager_backlight_device {
+ u32 num;
+ struct ec_dev_attr attr[EC_BLC_MAX_NUM];
+ u8 brightness[EC_BLC_MAX_NUM];
+ struct ec_info *info;
+};
+
+struct imanager_watchdog_device {
+ u32 num;
+ struct ec_dev_attr attr[EC_WDT_MAX_NUM];
+ struct ec_dev_attr *irq;
+ struct ec_dev_attr *nmi;
+ struct ec_info *info;
+};
+
+/* Must be called first, obviously */
+int imanager_ec_probe(void);
+
+int imanager_msg_write(u8 cmd, u8 param, struct ec_message *msg);
+int imanager_msg_read(u8 cmd, u8 param, struct ec_message *msg);
+
+int imanager_read_byte(u8 cmd, u8 param);
+int imanager_read_word(u8 cmd, u8 param);
+
+int imanager_write_byte(u8 cmd, u8 param, u8 byte);
+int imanager_write_word(u8 cmd, u8 param, u16 word);
+
+int imanager_acpiram_read_byte(u8 offset);
+int imanager_acpiram_write_byte(u8 offset, u8 value);
+int imanager_acpiram_read_block(u8 offset, u8 *buf, u8 len);
+int imanager_acpiram_write_block(u8 offset, u8 *buf, u8 len);
+
+int imanager_wait_proc_complete(u8 offset, int cond);
+
+const struct imanager_ec_device *imanager_get_ec_device(void);
+const struct imanager_hwmon_device *imanager_get_hwmon_device(void);
+const struct imanager_gpio_device *imanager_get_gpio_device(void);
+const struct imanager_i2c_device *imanager_get_i2c_device(void);
+const struct imanager_watchdog_device *imanager_get_watchdog_device(void);
+const struct imanager_backlight_device *imanager_get_backlight_device(void);
+
+#endif
--
2.6.4
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH] fix platform_no_drv_owner.cocci warnings
2016-01-10 9:10 ` richard.dorsch
@ 2016-01-10 10:11 ` kbuild test robot
0 siblings, 0 replies; 39+ messages in thread
From: kbuild test robot @ 2016-01-10 10:11 UTC (permalink / raw)
To: richard.dorsch
Cc: kbuild-all, linux-kernel, lm-sensors, linux-i2c, linux-watchdog,
linux-gpio, lee.jones, jdelvare, linux, wim, jo.sunga,
Richard Vidal-Dorsch
drivers/mfd/imanager-core.c:248:3-8: No need to set .owner here. The core will do it.
Remove .owner field if calls are used which set it automatically
Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci
CC: Richard Vidal-Dorsch <richard.dorsch@gmail.com>
Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
---
imanager-core.c | 1 -
1 file changed, 1 deletion(-)
--- a/drivers/mfd/imanager-core.c
+++ b/drivers/mfd/imanager-core.c
@@ -245,7 +245,6 @@ static int imanager_remove(struct platfo
static struct platform_driver imanager_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "imanager-core",
},
.probe = imanager_probe,
^ permalink raw reply [flat|nested] 39+ messages in thread