aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/pci/netup_unidvb/netup_unidvb_ci.c')
-rw-r--r--drivers/media/pci/netup_unidvb/netup_unidvb_ci.c248
1 files changed, 248 insertions, 0 deletions
diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
new file mode 100644
index 000000000000..751b51b03593
--- /dev/null
+++ b/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
@@ -0,0 +1,248 @@
+/*
+ * netup_unidvb_ci.c
+ *
+ * DVB CAM support for NetUP Universal Dual DVB-CI
+ *
+ * Copyright (C) 2014 NetUP Inc.
+ * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
+ * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include "netup_unidvb.h"
+
+/* CI slot 0 base address */
+#define CAM0_CONFIG 0x0
+#define CAM0_IO 0x8000
+#define CAM0_MEM 0x10000
+#define CAM0_SZ 32
+/* CI slot 1 base address */
+#define CAM1_CONFIG 0x20000
+#define CAM1_IO 0x28000
+#define CAM1_MEM 0x30000
+#define CAM1_SZ 32
+/* ctrlstat registers */
+#define CAM_CTRLSTAT_READ_SET 0x4980
+#define CAM_CTRLSTAT_CLR 0x4982
+/* register bits */
+#define BIT_CAM_STCHG (1<<0)
+#define BIT_CAM_PRESENT (1<<1)
+#define BIT_CAM_RESET (1<<2)
+#define BIT_CAM_BYPASS (1<<3)
+#define BIT_CAM_READY (1<<4)
+#define BIT_CAM_ERROR (1<<5)
+#define BIT_CAM_OVERCURR (1<<6)
+/* BIT_CAM_BYPASS bit shift for SLOT 1 */
+#define CAM1_SHIFT 8
+
+irqreturn_t netup_ci_interrupt(struct netup_unidvb_dev *ndev)
+{
+ writew(0x101, ndev->bmmio0 + CAM_CTRLSTAT_CLR);
+ return IRQ_HANDLED;
+}
+
+static int netup_unidvb_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221,
+ int slot)
+{
+ struct netup_ci_state *state = en50221->data;
+ struct netup_unidvb_dev *dev = state->dev;
+ u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0;
+
+ dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT=0x%x\n",
+ __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
+ if (slot != 0)
+ return -EINVAL;
+ /* pass data to CAM module */
+ writew(BIT_CAM_BYPASS << shift, dev->bmmio0 + CAM_CTRLSTAT_CLR);
+ dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT=0x%x done\n",
+ __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
+ return 0;
+}
+
+static int netup_unidvb_ci_slot_shutdown(struct dvb_ca_en50221 *en50221,
+ int slot)
+{
+ struct netup_ci_state *state = en50221->data;
+ struct netup_unidvb_dev *dev = state->dev;
+
+ dev_dbg(&dev->pci_dev->dev, "%s()\n", __func__);
+ return 0;
+}
+
+static int netup_unidvb_ci_slot_reset(struct dvb_ca_en50221 *en50221,
+ int slot)
+{
+ struct netup_ci_state *state = en50221->data;
+ struct netup_unidvb_dev *dev = state->dev;
+ unsigned long timeout = 0;
+ u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0;
+ u16 ci_stat = 0;
+ int reset_counter = 3;
+
+ dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT_READ_SET=0x%x\n",
+ __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
+reset:
+ timeout = jiffies + msecs_to_jiffies(5000);
+ /* start reset */
+ writew(BIT_CAM_RESET << shift, dev->bmmio0 + CAM_CTRLSTAT_READ_SET);
+ dev_dbg(&dev->pci_dev->dev, "%s(): waiting for reset\n", __func__);
+ /* wait until reset done */
+ while (time_before(jiffies, timeout)) {
+ ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET);
+ if (ci_stat & (BIT_CAM_READY << shift))
+ break;
+ udelay(1000);
+ }
+ if (!(ci_stat & (BIT_CAM_READY << shift)) && reset_counter > 0) {
+ dev_dbg(&dev->pci_dev->dev,
+ "%s(): CAMP reset timeout! Will try again..\n",
+ __func__);
+ reset_counter--;
+ goto reset;
+ }
+ return 0;
+}
+
+static int netup_unidvb_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
+ int slot, int open)
+{
+ struct netup_ci_state *state = en50221->data;
+ struct netup_unidvb_dev *dev = state->dev;
+ u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0;
+ u16 ci_stat = 0;
+
+ dev_dbg(&dev->pci_dev->dev, "%s(): CAM_CTRLSTAT_READ_SET=0x%x\n",
+ __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
+ ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET);
+ if (ci_stat & (BIT_CAM_READY << shift)) {
+ state->status = DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+ } else if (ci_stat & (BIT_CAM_PRESENT << shift)) {
+ state->status = DVB_CA_EN50221_POLL_CAM_PRESENT;
+ } else {
+ state->status = 0;
+ }
+ return state->status;
+}
+
+static int netup_unidvb_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr)
+{
+ struct netup_ci_state *state = en50221->data;
+ struct netup_unidvb_dev *dev = state->dev;
+ u8 val = state->membase8_config[addr];
+
+ dev_dbg(&dev->pci_dev->dev,
+ "%s(): addr=0x%x val=0x%x\n", __func__, addr, val);
+ return val;
+}
+
+static int netup_unidvb_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
+ int slot, int addr, u8 data)
+{
+ struct netup_ci_state *state = en50221->data;
+ struct netup_unidvb_dev *dev = state->dev;
+
+ dev_dbg(&dev->pci_dev->dev,
+ "%s(): addr=0x%x data=0x%x\n", __func__, addr, data);
+ state->membase8_config[addr] = data;
+ return 0;
+}
+
+static int netup_unidvb_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221,
+ int slot, u8 addr)
+{
+ struct netup_ci_state *state = en50221->data;
+ struct netup_unidvb_dev *dev = state->dev;
+ u8 val = state->membase8_io[addr];
+
+ dev_dbg(&dev->pci_dev->dev,
+ "%s(): addr=0x%x val=0x%x\n", __func__, addr, val);
+ return val;
+}
+
+static int netup_unidvb_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221,
+ int slot, u8 addr, u8 data)
+{
+ struct netup_ci_state *state = en50221->data;
+ struct netup_unidvb_dev *dev = state->dev;
+
+ dev_dbg(&dev->pci_dev->dev,
+ "%s(): addr=0x%x data=0x%x\n", __func__, addr, data);
+ state->membase8_io[addr] = data;
+ return 0;
+}
+
+int netup_unidvb_ci_register(struct netup_unidvb_dev *dev,
+ int num, struct pci_dev *pci_dev)
+{
+ int result;
+ struct netup_ci_state *state;
+
+ if (num < 0 || num > 1) {
+ dev_err(&pci_dev->dev, "%s(): invalid CI adapter %d\n",
+ __func__, num);
+ return -EINVAL;
+ }
+ state = &dev->ci[num];
+ state->nr = num;
+ state->membase8_config = dev->bmmio1 +
+ ((num == 0) ? CAM0_CONFIG : CAM1_CONFIG);
+ state->membase8_io = dev->bmmio1 +
+ ((num == 0) ? CAM0_IO : CAM1_IO);
+ state->dev = dev;
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = netup_unidvb_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = netup_unidvb_ci_write_attribute_mem;
+ state->ca.read_cam_control = netup_unidvb_ci_read_cam_ctl;
+ state->ca.write_cam_control = netup_unidvb_ci_write_cam_ctl;
+ state->ca.slot_reset = netup_unidvb_ci_slot_reset;
+ state->ca.slot_shutdown = netup_unidvb_ci_slot_shutdown;
+ state->ca.slot_ts_enable = netup_unidvb_ci_slot_ts_ctl;
+ state->ca.poll_slot_status = netup_unidvb_poll_ci_slot_status;
+ state->ca.data = state;
+ result = dvb_ca_en50221_init(&dev->frontends[num].adapter,
+ &state->ca, 0, 1);
+ if (result < 0) {
+ dev_err(&pci_dev->dev,
+ "%s(): dvb_ca_en50221_init result %d\n",
+ __func__, result);
+ return result;
+ }
+ writew(NETUP_UNIDVB_IRQ_CI, (u16 *)(dev->bmmio0 + REG_IMASK_SET));
+ dev_info(&pci_dev->dev,
+ "%s(): CI adapter %d init done\n", __func__, num);
+ return 0;
+}
+
+void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num)
+{
+ struct netup_ci_state *state;
+
+ dev_dbg(&dev->pci_dev->dev, "%s()\n", __func__);
+ if (num < 0 || num > 1) {
+ dev_err(&dev->pci_dev->dev, "%s(): invalid CI adapter %d\n",
+ __func__, num);
+ return;
+ }
+ state = &dev->ci[num];
+ dvb_ca_en50221_release(&state->ca);
+}
+