summaryrefslogtreecommitdiff
path: root/drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-09-06 04:21:14 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2015-09-06 04:21:14 +0300
commit9cfcc658da9693f65e7224e8329e40ada2f3c699 (patch)
tree44fb518eee069733f3f895177899815e7c89e5b0 /drivers/media/pci/netup_unidvb/netup_unidvb_ci.c
parente3a98ac47698bf1c1e4e6fae72afc9866953fce5 (diff)
parent50ef28a6ac216fd8b796257a3768fef8f57b917d (diff)
downloadlinux-9cfcc658da9693f65e7224e8329e40ada2f3c699.tar.xz
Merge tag 'media/v4.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - new DVB frontend drivers: ascot2e, cxd2841er, horus3a, lnbh25 - new HDMI capture driver: tc358743 - new driver for NetUP DVB new boards (netup_unidvb) - IR support for DVBSky cards (smipcie-ir) - Coda driver has gain macroblock tiling support - Renesas R-Car gains JPEG codec driver - new DVB platform driver for STi boards: c8sectpfe - added documentation for the media core kABI to device-drivers DocBook - lots of driver fixups, cleanups and improvements * tag 'media/v4.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (297 commits) [media] c8sectpfe: Remove select on undefined LIBELF_32 [media] i2c: fix platform_no_drv_owner.cocci warnings [media] cx231xx: Use wake_up_interruptible() instead of wake_up_interruptible_nr() [media] tc358743: only queue subdev notifications if devnode is set [media] tc358743: add missing Kconfig dependency/select [media] c8sectpfe: Use %pad to print 'dma_addr_t' [media] DocBook media: Fix typo "the the" in xml files [media] tc358743: make reset gpio optional [media] tc358743: set direction of reset gpio using devm_gpiod_get [media] dvbdev: document most of the functions/data structs [media] dvb_frontend.h: document the struct dvb_frontend [media] dvb-frontend.h: document struct dtv_frontend_properties [media] dvb-frontend.h: document struct dvb_frontend_ops [media] dvb: Use DVBFE_ALGO_HW where applicable [media] dvb_frontend.h: document struct analog_demod_ops [media] dvb_frontend.h: Document struct dvb_tuner_ops [media] Docbook: Document struct analog_parameters [media] dvb_frontend.h: get rid of dvbfe_modcod [media] add documentation for struct dvb_tuner_info [media] dvb_frontend: document dvb_frontend_tune_settings ...
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);
+}
+