* [media 7/7] PCI bridge driver for PT3 & PXQ3PE
[not found] <cover.1455566803.git.knightrider@are.ma>
@ 2016-02-15 20:07 ` info
0 siblings, 0 replies; 10+ messages in thread
From: info @ 2016-02-15 20:07 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 | 73 +++++
drivers/media/pci/ptx/pxq3pe_pci.c | 605 +++++++++++++++++++++++++++++++++++++
8 files changed, 1433 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..f00c03c
--- /dev/null
+++ b/drivers/media/pci/ptx/ptx_common.h
@@ -0,0 +1,73 @@
+/*
+ * 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 <linux/vmalloc.h>
+#include "dvb_demux.h"
+#include "dmxdev.h"
+
+enum ePTX {
+ PTX_MODE_GPIO = 0,
+ PTX_MODE_TUNER = 1,
+ PTX_MODE_STAT = 2,
+
+ PTX_TS_SYNC = 0x47,
+ PTX_TS_NOT_SYNC = 0x74,
+};
+
+struct ptx_subdev_info {
+ enum fe_delivery_system 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..eec3c46
--- /dev/null
+++ b/drivers/media/pci/ptx/pxq3pe_pci.c
@@ -0,0 +1,605 @@
+/*
+ * 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_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 PTX_MODE_GPIO:
+ i2cCtlByte = 0xC0;
+ break;
+ case PTX_MODE_TUNER:
+ slvadr = 2 * slvadr + 0x20;
+ regadr = 0;
+ i2cCtlByte = 0x80;
+ break;
+ case PTX_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(1000);
+ 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(1000);
+ }
+ 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 PTX_MODE_GPIO:
+ i2cCtlByte = 0xE0;
+ break;
+ case PTX_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 ? PTX_MODE_GPIO
+ : sz > 1 && i == sz - 2 ? PTX_MODE_STAT
+ : PTX_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, PTX_MODE_GPIO) &&
+ (val = (mask & dat) | (val & ~mask), pxq3pe_w(card, PXQ3PE_I2C_ADR_GPIO, 0xB, &val, 1, PTX_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 dma_map_ops *ops = get_dma_ops(&pdev->dev);
+ 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, PTX_MODE_GPIO);
+ pxq3pe_power(card, false);
+
+ /* dma_hw_unmap */
+ free_irq(pdev->irq, card);
+ if (c->dma.dat && ops && ops->free)
+ 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;
+ struct dma_map_ops *ops = get_dma_ops(dev);
+ u8 regctl = 0xA0,
+ i;
+ u16 cfg;
+ int err = !card || pci_read_config_word(pdev, PCI_COMMAND, &cfg);
+
+ if (err || !ops)
+ 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 && 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 = 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, PTX_MODE_GPIO))
+ break;
+ if (i < 16 || !pxq3pe_w(card, PXQ3PE_I2C_ADR_GPIO, 5, ®ctl, 1, PTX_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] 10+ messages in thread
* Re: [media 7/7] PCI bridge driver for PT3 & PXQ3PE
2016-02-15 12:57 ` kbuild test robot
@ 2016-02-16 2:07 ` AreMa Inc.
0 siblings, 0 replies; 10+ messages in thread
From: AreMa Inc. @ 2016-02-16 2:07 UTC (permalink / raw)
To: kbuild test robot
Cc: kbuild-all, linux-media, linux-kernel, Antti Palosaari,
Mauro Carvalho Chehab, Mauro Carvalho Chehab, Hans De Goede,
Laurent Pinchart, Michael Krufky, Sylwester Nawrocki,
Guennadi Liakhovetski, Peter Senna Tschudin
2016-02-15 21:57 GMT+09:00 kbuild test robot <lkp@intel.com>:
> Hi Буди,
>
> [auto build test ERROR on linuxtv-media/master]
> [cannot apply to v4.5-rc4 next-20160215]
> [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-173307
> base: git://linuxtv.org/media_tree.git master
> config: x86_64-allyesconfig (attached as .config)
> reproduce:
> # save the attached .config to linux build tree
> make ARCH=x86_64
We tried to reproduce. It compiles successfully. Nothing wrong.
Your attached .config is corrupted, it doesn't match the kernel
mentioned (v4.5-rc4 next-20160215).
Remaking .config solved the problem. Voila!
^ permalink raw reply [flat|nested] 10+ messages in thread
* [media 7/7] PCI bridge driver for PT3 & PXQ3PE
2016-02-15 17:24 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
@ 2016-02-15 17:10 ` info
0 siblings, 0 replies; 10+ messages in thread
From: info @ 2016-02-15 17:10 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 | 69 +++++
drivers/media/pci/ptx/pxq3pe_pci.c | 609 +++++++++++++++++++++++++++++++++++++
8 files changed, 1433 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..4a51e30
--- /dev/null
+++ b/drivers/media/pci/ptx/ptx_common.h
@@ -0,0 +1,69 @@
+/*
+ * 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 <linux/vmalloc.h>
+#include "dvb_demux.h"
+#include "dmxdev.h"
+
+enum ePTX {
+ PTX_TS_SYNC = 0x47,
+ PTX_TS_NOT_SYNC = 0x74,
+};
+
+struct ptx_subdev_info {
+ enum fe_delivery_system 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..0f0d892
--- /dev/null
+++ b/drivers/media/pci/ptx/pxq3pe_pci.c
@@ -0,0 +1,609 @@
+/*
+ * 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(1000);
+ 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(1000);
+ }
+ 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 dma_map_ops *ops = get_dma_ops(&pdev->dev);
+ 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 && ops && ops->free)
+ 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;
+ struct dma_map_ops *ops = get_dma_ops(dev);
+ u8 regctl = 0xA0,
+ i;
+ u16 cfg;
+ int err = !card || pci_read_config_word(pdev, PCI_COMMAND, &cfg);
+
+ if (err || !ops)
+ 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 && 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 = 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] 10+ messages in thread
* Re: [media 7/7] PCI bridge driver for PT3 & PXQ3PE
2016-02-15 9:27 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE info
2016-02-15 11:51 ` kbuild test robot
@ 2016-02-15 12:57 ` kbuild test robot
2016-02-16 2:07 ` AreMa Inc.
1 sibling, 1 reply; 10+ messages in thread
From: kbuild test robot @ 2016-02-15 12:57 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: 3915 bytes --]
Hi Буди,
[auto build test ERROR on linuxtv-media/master]
[cannot apply to v4.5-rc4 next-20160215]
[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-173307
base: git://linuxtv.org/media_tree.git master
config: x86_64-allyesconfig (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All error/warnings (new ones prefixed by >>):
In file included from include/linux/module.h:18:0,
from drivers/media/dvb-core/dvb_frontend.h:35,
from drivers/media/pci/ptx/ptx_common.c:8:
>> include/linux/moduleparam.h:13:29: error: 'KBUILD_MODNAME' undeclared here (not in a function)
#define MODULE_PARAM_PREFIX KBUILD_MODNAME "."
^
include/linux/moduleparam.h:220:43: note: in definition of macro '__module_param_call'
static const char __param_str_##name[] = prefix #name; \
^
>> include/linux/moduleparam.h:448:22: note: in expansion of macro 'MODULE_PARAM_PREFIX'
__module_param_call(MODULE_PARAM_PREFIX, name, \
^
>> include/linux/moduleparam.h:429:2: note: in expansion of macro 'module_param_array_named'
module_param_array_named(name, name, type, nump, perm)
^
>> drivers/media/dvb-core/dvbdev.h:55:2: note: in expansion of macro 'module_param_array'
module_param_array(adapter_nr, short, NULL, 0444); \
^
>> drivers/media/pci/ptx/ptx_common.c:145:1: note: in expansion of macro 'DVB_DEFINE_MOD_OPT_ADAPTER_NR'
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adap_no);
^
>> include/linux/moduleparam.h:13:44: error: expected ',' or ';' before string constant
#define MODULE_PARAM_PREFIX KBUILD_MODNAME "."
^
include/linux/moduleparam.h:220:43: note: in definition of macro '__module_param_call'
static const char __param_str_##name[] = prefix #name; \
^
>> include/linux/moduleparam.h:448:22: note: in expansion of macro 'MODULE_PARAM_PREFIX'
__module_param_call(MODULE_PARAM_PREFIX, name, \
^
>> include/linux/moduleparam.h:429:2: note: in expansion of macro 'module_param_array_named'
module_param_array_named(name, name, type, nump, perm)
^
>> drivers/media/dvb-core/dvbdev.h:55:2: note: in expansion of macro 'module_param_array'
module_param_array(adapter_nr, short, NULL, 0444); \
^
>> drivers/media/pci/ptx/ptx_common.c:145:1: note: in expansion of macro 'DVB_DEFINE_MOD_OPT_ADAPTER_NR'
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adap_no);
^
vim +/KBUILD_MODNAME +13 include/linux/moduleparam.h
^1da177e Linus Torvalds 2005-04-16 7
^1da177e Linus Torvalds 2005-04-16 8 /* You can override this manually, but generally this should match the
^1da177e Linus Torvalds 2005-04-16 9 module name. */
^1da177e Linus Torvalds 2005-04-16 10 #ifdef MODULE
^1da177e Linus Torvalds 2005-04-16 11 #define MODULE_PARAM_PREFIX /* empty */
^1da177e Linus Torvalds 2005-04-16 12 #else
367cb704 Sam Ravnborg 2006-01-06 @13 #define MODULE_PARAM_PREFIX KBUILD_MODNAME "."
^1da177e Linus Torvalds 2005-04-16 14 #endif
^1da177e Linus Torvalds 2005-04-16 15
730b69d2 Rusty Russell 2008-10-22 16 /* Chosen so that structs with an unsigned long line up. */
:::::: The code at line 13 was first introduced by commit
:::::: 367cb704212cd0c9273ba2b1e62523139210563b kbuild: un-stringnify KBUILD_MODNAME
:::::: TO: Sam Ravnborg <sam@mars.ravnborg.org>
:::::: CC: Sam Ravnborg <sam@mars.ravnborg.org>
---
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: 52455 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [media 7/7] PCI bridge driver for PT3 & PXQ3PE
2016-02-15 9:27 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE info
@ 2016-02-15 11:51 ` kbuild test robot
2016-02-15 12:57 ` kbuild test robot
1 sibling, 0 replies; 10+ messages in thread
From: kbuild test robot @ 2016-02-15 11:51 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: 1979 bytes --]
Hi Буди,
[auto build test ERROR on linuxtv-media/master]
[cannot apply to v4.5-rc4 next-20160215]
[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-173307
base: git://linuxtv.org/media_tree.git master
config: microblaze-allmodconfig (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=microblaze
All errors (new ones prefixed by >>):
drivers/media/pci/ptx/pxq3pe_pci.c: In function 'pxq3pe_remove':
>> drivers/media/pci/ptx/pxq3pe_pci.c:493:20: error: 'dma_ops' undeclared (first use in this function)
if (c->dma.dat && dma_ops->free)
^
drivers/media/pci/ptx/pxq3pe_pci.c:493:20: note: each undeclared identifier is reported only once for each function it appears in
drivers/media/pci/ptx/pxq3pe_pci.c: In function 'pxq3pe_probe':
drivers/media/pci/ptx/pxq3pe_pci.c:561:41: error: 'dma_ops' undeclared (first use in this function)
if (dev->dma_mask && *dev->dma_mask && dma_ops->alloc) {
^
vim +/dma_ops +493 drivers/media/pci/ptx/pxq3pe_pci.c
487 }
488 pxq3pe_w(card, PXQ3PE_I2C_ADR_GPIO, 0x80, ®ctl, 1, PXQ3PE_MODE_GPIO);
489 pxq3pe_power(card, false);
490
491 /* dma_hw_unmap */
492 free_irq(pdev->irq, card);
> 493 if (c->dma.dat && dma_ops->free)
494 dma_ops->free(&pdev->dev, c->dma.sz, c->dma.dat, c->dma.adr, NULL);
495 for (i = 0; i < card->adapn; i++) {
496 struct ptx_adap *adap = &card->adap[i];
---
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: 43717 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread
* [media 7/7] PCI bridge driver for PT3 & PXQ3PE
2016-02-15 9:27 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
@ 2016-02-15 9:27 ` info
2016-02-15 11:51 ` kbuild test robot
2016-02-15 12:57 ` kbuild test robot
0 siblings, 2 replies; 10+ messages in thread
From: info @ 2016-02-15 9:27 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 | 69 +++++
drivers/media/pci/ptx/pxq3pe_pci.c | 607 +++++++++++++++++++++++++++++++++++++
8 files changed, 1431 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..4a51e30
--- /dev/null
+++ b/drivers/media/pci/ptx/ptx_common.h
@@ -0,0 +1,69 @@
+/*
+ * 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 <linux/vmalloc.h>
+#include "dvb_demux.h"
+#include "dmxdev.h"
+
+enum ePTX {
+ PTX_TS_SYNC = 0x47,
+ PTX_TS_NOT_SYNC = 0x74,
+};
+
+struct ptx_subdev_info {
+ enum fe_delivery_system 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..3836e90
--- /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, 0, 3, 1, 0, 2, 1, 2},
+ xor[4] = {0x2F, 0x46, 0x56, 0xE3};
+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(1000);
+ 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(1000);
+ }
+ 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] 10+ 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 ` kbuild test robot
2 siblings, 0 replies; 10+ 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] 10+ 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 ` kbuild test robot
2 siblings, 0 replies; 10+ 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] 10+ 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 ` kbuild test robot
2 siblings, 0 replies; 10+ 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] 10+ 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
@ 2016-02-15 6:08 ` info
2016-02-15 6:23 ` kbuild test robot
` (2 more replies)
0 siblings, 3 replies; 10+ 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] 10+ messages in thread
end of thread, other threads:[~2016-02-16 2:07 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <cover.1455566803.git.knightrider@are.ma>
2016-02-15 20:07 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE info
2016-02-15 17:24 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
2016-02-15 17:10 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE info
-- strict thread matches above, loose matches on Subject: below --
2016-02-15 9:27 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
2016-02-15 9:27 ` [media 7/7] PCI bridge driver for PT3 & PXQ3PE info
2016-02-15 11:51 ` kbuild test robot
2016-02-15 12:57 ` kbuild test robot
2016-02-16 2:07 ` AreMa Inc.
2016-02-15 6:08 [media 0/7] Driver bundle for PT3 & PX-Q3PE info
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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).