All of lore.kernel.org
 help / color / mirror / Atom feed
From: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
To: Kozlov Sergey <serjk@netup.ru>
Cc: linux-media@vger.kernel.org
Subject: Re: [PATCH 5/5] [media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver
Date: Thu, 5 Mar 2015 08:59:30 -0300	[thread overview]
Message-ID: <20150305085930.357ca5b0@recife.lan> (raw)
In-Reply-To: <20150203082419.CA9AC1BC32CD@debian>

Em Mon, 02 Feb 2015 12:22:32 +0300
Kozlov Sergey <serjk@netup.ru> escreveu:

> 
> NetUP Dual Universal CI PCIe board driver.
> The board has
>     - two CI slots
>     - two I2C adapters
>     - SPI master bus for accessing flash memory containing
>       FPGA firmware
> 
> Signed-off-by: Kozlov Sergey <serjk@netup.ru>
> ---
>  MAINTAINERS                                 |    9 +
>  drivers/media/pci/Kconfig                   |    1 +
>  drivers/media/pci/Makefile                  |    3 +-
>  drivers/media/pci/netup/Kconfig             |   12 +
>  drivers/media/pci/netup/Makefile            |    9 +
>  drivers/media/pci/netup/netup_unidvb.h      |  232 +++++++
>  drivers/media/pci/netup/netup_unidvb_ci.c   |  248 ++++++++
>  drivers/media/pci/netup/netup_unidvb_core.c |  919 +++++++++++++++++++++++++++
>  drivers/media/pci/netup/netup_unidvb_i2c.c  |  350 ++++++++++
>  drivers/media/pci/netup/netup_unidvb_spi.c  |  272 ++++++++
>  10 files changed, 2054 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/media/pci/netup/Kconfig
>  create mode 100644 drivers/media/pci/netup/Makefile
>  create mode 100644 drivers/media/pci/netup/netup_unidvb.h
>  create mode 100644 drivers/media/pci/netup/netup_unidvb_ci.c
>  create mode 100644 drivers/media/pci/netup/netup_unidvb_core.c
>  create mode 100644 drivers/media/pci/netup/netup_unidvb_i2c.c
>  create mode 100644 drivers/media/pci/netup/netup_unidvb_spi.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a52001c..e2febd3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6572,6 +6572,15 @@ F:	include/net/netrom.h
>  F:	include/uapi/linux/netrom.h
>  F:	net/netrom/
>  
> +NETUP VIDEO4LINUX DRIVER

I would call it:

MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices


> +M:	Sergey Kozlov <serjk@netup.ru>
> +L:	linux-media@vger.kernel.org
> +W:	http://linuxtv.org/
> +W:	http://netup.tv/
> +T:	git git://linuxtv.org/media_tree.git
> +S:	Supported
> +F:	drivers/media/pci/netup/*

Just drivers/media/pci/netup directory doesn't seem to be a good
idea, as NetUp might have, in the future, other devices built using
different chipsets.

So, I would prefer if you use the name of the current NetUp platform
here (and on the driver's name). Perhaps unidvb or netup_unidvb?

> +
>  NETWORK BLOCK DEVICE (NBD)
>  M:	Paul Clements <Paul.Clements@steeleye.com>
>  S:	Maintained
> diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
> index 218144a..f1ec01e 100644
> --- a/drivers/media/pci/Kconfig
> +++ b/drivers/media/pci/Kconfig
> @@ -47,6 +47,7 @@ source "drivers/media/pci/mantis/Kconfig"
>  source "drivers/media/pci/ngene/Kconfig"
>  source "drivers/media/pci/ddbridge/Kconfig"
>  source "drivers/media/pci/smipcie/Kconfig"
> +source "drivers/media/pci/netup/Kconfig"
>  endif
>  
>  endif #MEDIA_PCI_SUPPORT
> diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
> index 0baf0d2..92be70e 100644
> --- a/drivers/media/pci/Makefile
> +++ b/drivers/media/pci/Makefile
> @@ -12,7 +12,8 @@ obj-y        +=	ttpci/		\
>  		ngene/		\
>  		ddbridge/	\
>  		saa7146/	\
> -		smipcie/
> +		smipcie/	\
> +		netup/
>  
>  obj-$(CONFIG_VIDEO_IVTV) += ivtv/
>  obj-$(CONFIG_VIDEO_ZORAN) += zoran/
> diff --git a/drivers/media/pci/netup/Kconfig b/drivers/media/pci/netup/Kconfig
> new file mode 100644
> index 0000000..3ad3cc3
> --- /dev/null
> +++ b/drivers/media/pci/netup/Kconfig
> @@ -0,0 +1,12 @@
> +config DVB_NETUP_UNIDVB
> +	tristate "NetUP Universal DVB card support"
> +	depends on DVB_CORE && VIDEO_DEV && PCI && I2C && SPI_MASTER
> +    select VIDEOBUF_DVB
> +    select VIDEOBUF_VMALLOC

Videobuf version 1 is deprecated. We're not adding new drivers to it,
as the goal is to remove it from the Kernel.

Please use, instead, Videobuf2.

> +	select DVB_HORUS3A if MEDIA_SUBDRV_AUTOSELECT
> +	select DVB_ASCOT2E if MEDIA_SUBDRV_AUTOSELECT
> +	select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT
> +	select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT
> +	---help---
> +	  Support for NetUP PCI express Universal DVB card.
> +
> diff --git a/drivers/media/pci/netup/Makefile b/drivers/media/pci/netup/Makefile
> new file mode 100644
> index 0000000..ee6ae05
> --- /dev/null
> +++ b/drivers/media/pci/netup/Makefile
> @@ -0,0 +1,9 @@
> +netup-unidvb-objs += netup_unidvb_core.o
> +netup-unidvb-objs += netup_unidvb_i2c.o
> +netup-unidvb-objs += netup_unidvb_ci.o
> +netup-unidvb-objs += netup_unidvb_spi.o
> +
> +obj-$(CONFIG_DVB_NETUP_UNIDVB) += netup-unidvb.o
> +
> +ccflags-y += -Idrivers/media/dvb-core
> +ccflags-y += -Idrivers/media/dvb-frontends
> diff --git a/drivers/media/pci/netup/netup_unidvb.h b/drivers/media/pci/netup/netup_unidvb.h
> new file mode 100644
> index 0000000..4d0e1eb
> --- /dev/null
> +++ b/drivers/media/pci/netup/netup_unidvb.h
> @@ -0,0 +1,232 @@
> +/*
> + * netup_unidvb.h
> + *
> + * Data type definitions 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/pci.h>
> +#include <linux/i2c.h>
> +#include <linux/workqueue.h>
> +#include <media/v4l2-common.h>
> +#include <media/v4l2-device.h>
> +#include <media/videobuf-dma-sg.h>
> +#include <media/videobuf-dvb.h>
> +#include <dvb_ca_en50221.h>
> +
> +#define NETUP_UNIDVB_NAME	"netup_unidvb"
> +#define NETUP_UNIDVB_VERSION	"0.0.1"
> +#define NETUP_VENDOR_ID		0x1b55
> +#define NETUP_PCI_DEV_REVISION  0x2
> +
> +/* Avalon-MM PCI-E registers */
> +#define	AVL_PCIE_IENR		0x50
> +#define AVL_PCIE_ISR		0x40
> +#define AVL_IRQ_ENABLE		0x80
> +#define AVL_IRQ_ASSERTED	0x80
> +
> +/* IRQ-related regisers */
> +#define REG_ISR			0x4890
> +#define REG_ISR_MASKED		0x4892
> +#define REG_IMASK_SET		0x4894
> +#define REG_IMASK_CLEAR		0x4896
> +
> +#define NETUP_UNIDVB_IRQ_SPI	(1 << 0)
> +#define NETUP_UNIDVB_IRQ_I2C0	(1 << 1)
> +#define NETUP_UNIDVB_IRQ_I2C1	(1 << 2)
> +#define NETUP_UNIDVB_IRQ_FRA0	(1 << 4)	/* FrontendA IRQ0 */
> +#define NETUP_UNIDVB_IRQ_FRA1	(1 << 5)	/* FrontendA IRQ1 */
> +#define NETUP_UNIDVB_IRQ_FRB0	(1 << 6)	/* FrontendB IRQ0 */
> +#define NETUP_UNIDVB_IRQ_FRB1	(1 << 7)	/* FrontendB IRQ1 */
> +#define NETUP_UNIDVB_IRQ_DMA1	(1 << 8)	/* DMA1 */
> +#define NETUP_UNIDVB_IRQ_DMA2	(1 << 9)	/* DMA2 */
> +#define NETUP_UNIDVB_IRQ_CI	(1 << 10)	/* CI MAIN MODULE */
> +#define NETUP_UNIDVB_IRQ_CAM0	(1 << 11)	/* CI CAM0 */
> +#define NETUP_UNIDVB_IRQ_CAM1	(1 << 12)	/* CI CAM1 */
> +
> +/* GPIO registers */
> +#define GPIO_REG_IO		0x4880
> +#define GPIO_REG_IO_TOGGLE	0x4882
> +#define GPIO_REG_IO_SET		0x4884
> +#define GPIO_REG_IO_CLEAR	0x4886
> +/* GPIO pins */
> +#define GPIO_FEA_RESET		(1 << 0)
> +#define GPIO_FEB_RESET		(1 << 1)
> +#define GPIO_RFA_CTL		(1 << 2)
> +#define GPIO_RFB_CTL		(1 << 3)
> +#define GPIO_FEA_TU_RESET	(1 << 4)
> +#define GPIO_FEB_TU_RESET	(1 << 5)
> +
> +/* DMA section */
> +#define NETUP_DMA0_ADDR		0x4900
> +#define NETUP_DMA1_ADDR		0x4940
> +/* DMA part. 8 blocks for 120Mbps per port is ok */
> +#define NETUP_DMA_BLOCKS_COUNT	8
> +#define NETUP_DMA_PACKETS_COUNT	128
> +/* DMA block status */
> +#define NETUP_DMA_UNK		1
> +#define NETUP_DMA_DONE		2
> +#define BIT_DMA_RUN		1
> +#define BIT_DMA_ERROR		2
> +#define BIT_DMA_IRQ		0x200
> +
> +struct netup_dma_regs {
> +	/* see BIT_DMA_* defines;
> +	 * [24-31] - dma_current_length;
> +	 * [16-23] - dma_current_block_cnt */
> +	u32	ctrlstat_set;
> +	u32	ctrlstat_clear;
> +	/* dma start address, low */
> +	u32	start_addr_lo;
> +	/* dma start address, high */
> +	u32	start_addr_hi;
> +	/* [0-7] dma_packet_size (188);
> +	 * [16-23] packets count in block (128);
> +	 * [24-31] dma_length - blocks count in dma */
> +	u32	size;
> +	/* 8ns intervals;
> +	 * for example 3sec => timeout = 375 000 000 */
> +	u32	timeout;
> +	/* dma current address, low */
> +	u32	curr_addr_lo;
> +	/* dma current address, high */
> +	u32	curr_addr_hi;
> +	u32	stat_pkt_received;
> +	u32	stat_pkt_accepted;
> +	u32	stat_pkt_overruns;
> +	u32	stat_pkt_underruns;
> +	u32	stat_fifo_overruns;
> +} __packed __aligned(1);
> +
> +struct netup_unidvb_buffer {
> +	struct videobuf_buffer	vb;
> +	u32			size;
> +};
> +
> +struct netup_dma {
> +	u8			num;
> +	spinlock_t		lock;
> +	struct netup_unidvb_dev	*ndev;
> +	struct netup_dma_regs	*regs;
> +	u32			ring_buffer_size;
> +	u8			*addr_virt;
> +	dma_addr_t		addr_phys;
> +	u64			addr_last;
> +	u32			high_addr;
> +	u32			data_offset;
> +	u32			data_size;
> +	struct list_head	free_buffers;
> +	struct work_struct	work;
> +	struct timer_list	timeout;
> +};
> +
> +/* I2C bus section */
> +#define NETUP_I2C_BUS0_ADDR		0x4800
> +#define NETUP_I2C_BUS1_ADDR		0x4840
> +#define NETUP_I2C_TIMEOUT		HZ
> +
> +struct netup_i2c_fifo_regs {
> +	union {
> +		u8	data8;
> +		u16	data16;
> +		u32	data32;
> +	};
> +	u8		padding[4];
> +	u16		stat_ctrl;
> +} __packed __aligned(1);
> +
> +struct netup_i2c_regs {
> +	u16				clkdiv;
> +	u16				twi_ctrl0_stat;
> +	u16				twi_addr_ctrl1;
> +	u16				length;
> +	u8				padding1[8];
> +	struct netup_i2c_fifo_regs	tx_fifo;
> +	u8				padding2[6];
> +	struct netup_i2c_fifo_regs	rx_fifo;
> +} __packed __aligned(1);

The above structures are harware/firmware oriented, right?

You need to specify if the integers are either big endian or
little endian, declaring them using __be32/__le32 and so on.

Those macros will produce a warning if you try to access the
fields without the proper endiannes conversion routines:
be32_to_cpu() / le32_to_cpu(), with is a good thing, as it will
warrant that your board would work on a system using a different
endiannes than x86.

> +
> +enum netup_i2c_state {
> +	STATE_DONE,
> +	STATE_WAIT,
> +	STATE_WANT_READ,
> +	STATE_WANT_WRITE,
> +	STATE_ERROR
> +};
> +
> +struct netup_i2c {
> +	spinlock_t			lock;
> +	wait_queue_head_t		wq;
> +	struct i2c_adapter		adap;
> +	struct netup_unidvb_dev		*dev;
> +	struct netup_i2c_regs		*regs;
> +	struct i2c_msg			*msg;
> +	enum netup_i2c_state		state;
> +	u32				xmit_size;
> +};
> +
> +/* CI section */
> +struct netup_ci_state {
> +	struct dvb_ca_en50221		ca;
> +	u8 __iomem			*membase8_config;
> +	u8 __iomem			*membase8_io;
> +	struct netup_unidvb_dev		*dev;
> +	int status;
> +	int nr;
> +};
> +
> +#define CAM_CTRLSTAT_READ_SET		0x4980
> +#define CAM_CTRLSTAT_CLR		0x4982
> +
> +/* SPI flash section */
> +struct netup_spi;
> +
> +/* main device context */
> +
> +struct netup_unidvb_dev {
> +	struct pci_dev			*pci_dev;
> +	int				pci_bus;
> +	int				pci_slot;
> +	int				pci_func;
> +	int				board_num;
> +	int				old_fw;
> +	/* MMIO */
> +	u32 __iomem			*lmmio0;
> +	u8 __iomem			*bmmio0;
> +	u32 __iomem			*lmmio1;
> +	u8 __iomem			*bmmio1;
> +	struct videobuf_dvb_frontends	frontends[2];
> +	/* I2C buses */
> +	struct netup_i2c	i2c[2];
> +	/* DMA */
> +	struct workqueue_struct	*wq;
> +	struct netup_dma	dma[2];
> +	/* CI slots */
> +	struct netup_ci_state	ci[2];
> +	/* SPI */
> +	struct netup_spi	*spi;
> +};
> +
> +int netup_i2c_register(struct netup_unidvb_dev *ndev);
> +void netup_i2c_unregister(struct netup_unidvb_dev *ndev);
> +irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c);
> +irqreturn_t netup_spi_interrupt(struct netup_spi *spi);
> +irqreturn_t netup_dma_interrupt(struct netup_dma *dma);
> +int netup_unidvb_ci_register(
> +	struct netup_unidvb_dev *dev, int num, struct pci_dev *pci_dev);
> +void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num);
> +int netup_spi_init(struct netup_unidvb_dev *ndev);
> +void netup_spi_release(struct netup_unidvb_dev *ndev);
> diff --git a/drivers/media/pci/netup/netup_unidvb_ci.c b/drivers/media/pci/netup/netup_unidvb_ci.c
> new file mode 100644
> index 0000000..3610634
> --- /dev/null
> +++ b/drivers/media/pci/netup/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"
> +
> +static int ci_debug;
> +
> +#define dprintk(args...) \
> +do { \
> +	if (ci_debug) \
> +		dev_dbg(&dev->pci_dev->dev, args); \
> +} while (0)
> +
> +module_param(ci_debug, int, 0644);

This is not very nice, as, in order to enable debug, both this parameter
and dynamic printk should be enabled via /sys/kernel/debug/dynamic_debug.

Just use dev_dbg() directly.

> +
> +/* CI */
> +#define CAM0_CONFIG		0x0
> +#define CAM0_IO			0x8000
> +#define CAM0_MEM		0x10000
> +#define CAM0_SZ			32
> +
> +#define CAM1_CONFIG		0x20000
> +#define CAM1_IO			0x28000
> +#define CAM1_MEM		0x30000
> +#define CAM1_SZ			32
> +
> +#define CAM_CTRLSTAT_READ_SET	0x4980
> +#define CAM_CTRLSTAT_CLR	0x4982
> +
> +#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)
> +
> +#define CAM1_SHIFT 8
> +
> +
> +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;
> +
> +	dprintk("%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);
> +	dprintk("%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;
> +
> +	dprintk("%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;
> +
> +	dprintk("%s(): CAM_CTRLSTAT_READ_SET=0x%x\n",
> +		__func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET));
> +reset:
> +	timeout = jiffies + 5*HZ;

Please use msecs_to_jiffies instead.

> +	/* start reset */
> +	writew(BIT_CAM_RESET << shift, dev->bmmio0 + CAM_CTRLSTAT_READ_SET);
> +	dprintk("%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) {
> +		dprintk("%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;
> +
> +	dprintk("%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];
> +
> +	dprintk("%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;
> +
> +	dprintk("%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];
> +
> +	dprintk("%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;
> +
> +	dprintk("%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;
> +
> +	dprintk("%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);
> +}
> +
> diff --git a/drivers/media/pci/netup/netup_unidvb_core.c b/drivers/media/pci/netup/netup_unidvb_core.c
> new file mode 100644
> index 0000000..2670ca0
> --- /dev/null
> +++ b/drivers/media/pci/netup/netup_unidvb_core.c
> @@ -0,0 +1,919 @@
> +/*
> + * netup_unidvb_core.c
> + *
> + * Main module 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 <linux/list.h>
> +#include <media/videobuf-vmalloc.h>
> +
> +#include "netup_unidvb.h"
> +#include "cxd2841er.h"
> +#include "horus3a.h"
> +#include "ascot2e.h"
> +#include "lnbh25.h"
> +
> +MODULE_DESCRIPTION("Driver for NetUP Dual Universal DVB CI PCIe card");
> +MODULE_AUTHOR("info@netup.ru");
> +MODULE_VERSION(NETUP_UNIDVB_VERSION);
> +MODULE_LICENSE("GPL");
> +
> +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
> +
> +#define DRIVER_NAME	"netup_unidvb"
> +
> +static int debug;
> +module_param(debug, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
> +static int spi_enable;
> +module_param(spi_enable, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
> +
> +#define dprintk(args...) \
> +	do { \
> +		if (debug) \
> +			dev_dbg(&ndev->pci_dev->dev, args); \
> +	} while (0)

This is not very nice, as, in order to enable debug, both this parameter
and dynamic printk should be enabled via /sys/kernel/debug/dynamic_debug.

Just use dev_dbg() directly.

> +
> +static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc);
> +
> +static struct cxd2841er_config demod_config = {
> +	.i2c_addr = 0xc8
> +};
> +
> +static struct horus3a_config horus3a_conf = {
> +	.i2c_address = 0xc0,
> +	.xtal_freq_mhz = 16,
> +	.set_tuner_callback = netup_unidvb_tuner_ctrl
> +};
> +
> +static struct ascot2e_config ascot2e_conf = {
> +	.i2c_address = 0xc2,
> +	.set_tuner_callback = netup_unidvb_tuner_ctrl
> +};
> +
> +static struct lnbh25_config lnbh25_conf = {
> +	.i2c_address = 0x10,
> +	.data2_config = LNBH25_TEN | LNBH25_EXTM
> +};
> +
> +static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc)
> +{
> +	u8 reg, mask;
> +	struct netup_dma *dma = priv;
> +	struct netup_unidvb_dev *ndev;
> +
> +	if (!priv)
> +		return -EINVAL;
> +	dprintk("%s(): num %d is_dvb_tc %d\n",
> +		__func__, dma->num, is_dvb_tc);
> +	ndev = dma->ndev;
> +	reg = readb(ndev->bmmio0 + GPIO_REG_IO);
> +	mask = (dma->num == 0) ? GPIO_RFA_CTL : GPIO_RFB_CTL;
> +	if (!is_dvb_tc)
> +		reg |= mask;
> +	else
> +		reg &= ~mask;
> +	writeb(reg, ndev->bmmio0 + GPIO_REG_IO);
> +	return 0;
> +}
> +
> +static void netup_unidvb_dev_enable(struct netup_unidvb_dev *ndev)
> +{
> +	u16 gpio_reg;
> +
> +	/* enable PCI-E interrupts */
> +	writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR);
> +	/* unreset frontends bits[0:1] */
> +	writeb(0x00, ndev->bmmio0 + GPIO_REG_IO);
> +	msleep(100);
> +	gpio_reg =
> +		GPIO_FEA_RESET | GPIO_FEB_RESET |
> +		GPIO_FEA_TU_RESET | GPIO_FEB_TU_RESET |
> +		GPIO_RFA_CTL | GPIO_RFB_CTL;
> +	writeb(gpio_reg, ndev->bmmio0 + GPIO_REG_IO);
> +	dev_dbg(&ndev->pci_dev->dev,
> +		"%s(): AVL_PCIE_IENR 0x%x GPIO_REG_IO 0x%x\n",
> +		__func__, readl(ndev->bmmio0 + AVL_PCIE_IENR),
> +		(int)readb(ndev->bmmio0 + GPIO_REG_IO));
> +
> +}
> +
> +static void netup_unidvb_dma_enable(struct netup_dma *dma, int enable)
> +{
> +	u32 irq_mask = (dma->num == 0 ?
> +		NETUP_UNIDVB_IRQ_DMA1 : NETUP_UNIDVB_IRQ_DMA2);
> +
> +	dev_dbg(&dma->ndev->pci_dev->dev,
> +		"%s(): DMA%d enable %d\n", __func__, dma->num, enable);
> +	if (enable) {
> +		writel(BIT_DMA_RUN, &dma->regs->ctrlstat_set);
> +		writew(irq_mask,
> +			(u16 *)(dma->ndev->bmmio0 + REG_IMASK_SET));
> +	} else {
> +		writel(BIT_DMA_RUN, &dma->regs->ctrlstat_clear);
> +		writew(irq_mask,
> +			(u16 *)(dma->ndev->bmmio0 + REG_IMASK_CLEAR));
> +	}
> +}
> +
> +irqreturn_t netup_dma_interrupt(struct netup_dma *dma)
> +{
> +	u64 addr_curr;
> +	u32 size;
> +	unsigned long flags;
> +	struct device *dev = &dma->ndev->pci_dev->dev;
> +
> +	spin_lock_irqsave(&dma->lock, flags);
> +	addr_curr = ((u64)readl(&dma->regs->curr_addr_hi) << 32) |
> +		(u64)readl(&dma->regs->curr_addr_lo) | dma->high_addr;
> +	/* clear IRQ */
> +	writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear);
> +	/* sanity check */
> +	if (addr_curr < dma->addr_phys ||
> +			addr_curr > dma->addr_phys +  dma->ring_buffer_size) {
> +		if (addr_curr != 0) {
> +			dev_err(dev, "%s(): DMA current address (0x%llx) not in range 0x%llx - 0x%llx\n",
> +				__func__,
> +				addr_curr, (u64)dma->addr_phys,
> +				(u64)(dma->addr_phys + dma->ring_buffer_size));
> +		}
> +		goto irq_handled;
> +	}
> +	size = (addr_curr >= dma->addr_last) ?
> +		(u32)(addr_curr - dma->addr_last) :
> +		(u32)(dma->ring_buffer_size - (dma->addr_last - addr_curr));
> +	if (dma->data_size != 0) {
> +		printk_ratelimited("%s(): lost interrupt, data size %d\n",
> +			__func__, dma->data_size);
> +		dma->data_size += size;
> +	}
> +	if (dma->data_size == 0 || dma->data_size > dma->ring_buffer_size) {
> +		dma->data_size = size;
> +		dma->data_offset = (u32)(dma->addr_last - dma->addr_phys);
> +	}
> +	dma->addr_last = addr_curr;
> +	queue_work(dma->ndev->wq, &dma->work);
> +irq_handled:
> +	spin_unlock_irqrestore(&dma->lock, flags);
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t netup_unidvb_isr(int irq, void *dev_id)
> +{
> +	struct pci_dev *pci_dev = (struct pci_dev *)dev_id;
> +	struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev);
> +	u32 reg40, reg_isr;
> +	irqreturn_t iret = IRQ_NONE;
> +
> +	/* disable interrupts */
> +	writel(0, ndev->bmmio0 + AVL_PCIE_IENR);
> +	/* check IRQ source */
> +	reg40 = readl(ndev->bmmio0 + AVL_PCIE_ISR);
> +	if ((reg40 & AVL_IRQ_ASSERTED) != 0) {
> +		/* IRQ is being signaled */
> +		reg_isr = readw(ndev->bmmio0 + REG_ISR);
> +		if (reg_isr & NETUP_UNIDVB_IRQ_I2C0) {
> +			iret = netup_i2c_interrupt(&ndev->i2c[0]);
> +		} else if (reg_isr & NETUP_UNIDVB_IRQ_I2C1) {
> +			iret = netup_i2c_interrupt(&ndev->i2c[1]);
> +		} else if (reg_isr & NETUP_UNIDVB_IRQ_SPI) {
> +			iret = netup_spi_interrupt(ndev->spi);
> +		} else if (reg_isr & NETUP_UNIDVB_IRQ_DMA1) {
> +			iret = netup_dma_interrupt(&ndev->dma[0]);
> +		} else if (reg_isr & NETUP_UNIDVB_IRQ_DMA2) {
> +			iret = netup_dma_interrupt(&ndev->dma[1]);
> +		} else if (reg_isr & NETUP_UNIDVB_IRQ_CI) {
> +			writew(0x101, ndev->bmmio0 + CAM_CTRLSTAT_CLR);
> +			iret = IRQ_HANDLED;
> +		} else {
> +			dev_err(&pci_dev->dev,
> +				"%s(): unknown interrupt 0x%x\n",
> +				__func__, reg_isr);
> +		}
> +	}
> +	/* re-enable interrupts */
> +	writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR);
> +	return iret;
> +}
> +
> +static int netup_unidvb_buf_setup(struct videobuf_queue *q,
> +		unsigned int *count, unsigned int *size)
> +{
> +	struct netup_dma *dma = q->priv_data;
> +	struct netup_unidvb_dev *ndev = dma->ndev;
> +
> +	dprintk("%s()\n", __func__);
> +	*count = NETUP_DMA_BLOCKS_COUNT * 10;
> +	*size = NETUP_DMA_PACKETS_COUNT * 188;
> +	return 0;
> +}
> +
> +static int netup_unidvb_buf_prepare(struct videobuf_queue *q,
> +		struct videobuf_buffer *vb, enum v4l2_field field)
> +{
> +	struct netup_dma *dma = q->priv_data;
> +	struct device *dev = &dma->ndev->pci_dev->dev;
> +	struct netup_unidvb_buffer *buf = (struct  netup_unidvb_buffer *)vb;
> +
> +	dev_dbg(dev, "%s(): %p\n", __func__, buf);
> +	if (buf->vb.baddr != 0 &&
> +			buf->vb.size < NETUP_DMA_PACKETS_COUNT * 188) {
> +		dev_err(dev, "%s(): invalid buffer size %d\n",
> +			__func__, (unsigned int)buf->vb.size);
> +		return -EINVAL;
> +	}
> +	if (buf->vb.state == VIDEOBUF_NEEDS_INIT) {
> +		buf->vb.width = 188;
> +		buf->vb.height = NETUP_DMA_PACKETS_COUNT;
> +		buf->vb.size = NETUP_DMA_PACKETS_COUNT * 188;
> +		buf->vb.field = field;
> +		if (videobuf_iolock(q, &buf->vb, NULL)) {
> +			dev_err(dev, "%s(): unable to lock buffer\n",
> +				__func__);
> +			return -EINVAL;
> +		}
> +	}
> +	buf->vb.state = VIDEOBUF_PREPARED;
> +	buf->size = 0;
> +	return 0;
> +}
> +
> +static void netup_unidvb_buf_queue(struct videobuf_queue *q,
> +		struct videobuf_buffer *vb)
> +{
> +	struct netup_dma *dma = q->priv_data;
> +	struct device *dev = &dma->ndev->pci_dev->dev;
> +	struct netup_unidvb_buffer *buf = (struct  netup_unidvb_buffer *)vb;
> +
> +	dev_dbg(dev, "%s(): %p\n", __func__, buf);
> +	buf->vb.state = VIDEOBUF_ACTIVE;
> +	buf->size = 0;
> +	list_add_tail(&buf->vb.queue, &dma->free_buffers);
> +	mod_timer(&dma->timeout, jiffies + HZ);
> +}
> +
> +static void netup_unidvb_buf_release(struct videobuf_queue *q,
> +		struct videobuf_buffer *vb)
> +{
> +	struct netup_dma *dma = q->priv_data;
> +	struct device *dev = &dma->ndev->pci_dev->dev;
> +	struct netup_unidvb_buffer *buf = (struct  netup_unidvb_buffer *)vb;
> +
> +	dev_dbg(dev, "%s(): %p\n", __func__, buf);
> +	videobuf_waiton(q, &buf->vb, 0, 0);
> +	videobuf_vmalloc_free(&buf->vb);
> +	buf->vb.state = VIDEOBUF_NEEDS_INIT;
> +}
> +
> +static struct videobuf_queue_ops dvb_qops = {
> +	.buf_setup	= netup_unidvb_buf_setup,
> +	.buf_prepare	= netup_unidvb_buf_prepare,
> +	.buf_queue	= netup_unidvb_buf_queue,
> +	.buf_release	= netup_unidvb_buf_release,
> +};
> +
> +static int netup_unidvb_dvb_init(
> +		struct netup_unidvb_dev *ndev, int num)
> +{
> +	struct videobuf_dvb_frontend *fe0, *fe1, *fe2;
> +
> +	if (num < 0 || num > 1) {
> +		dprintk("%s(): unable to init DVB bus %d\n",
> +			__func__, num);
> +		return -ENODEV;
> +	}
> +	mutex_init(&ndev->frontends[num].lock);
> +	INIT_LIST_HEAD(&ndev->frontends[num].felist);
> +	if (videobuf_dvb_alloc_frontend(
> +			&ndev->frontends[num], 1) == NULL ||
> +		videobuf_dvb_alloc_frontend(
> +			&ndev->frontends[num], 2) == NULL ||
> +		videobuf_dvb_alloc_frontend(
> +			&ndev->frontends[num], 3) == NULL) {
> +		dprintk("%s(): unabto to alllocate videobuf_dvb_frontend\n",
> +			__func__);
> +		return -ENOMEM;
> +	}
> +	fe0 = videobuf_dvb_get_frontend(&ndev->frontends[num], 1);
> +	fe1 = videobuf_dvb_get_frontend(&ndev->frontends[num], 2);
> +	fe2 = videobuf_dvb_get_frontend(&ndev->frontends[num], 3);
> +	if (fe0 == NULL || fe1 == NULL || fe2 == NULL) {
> +		dprintk("%s(): frontends has not been allocated\n",
> +			__func__);
> +		return -EINVAL;
> +	}
> +	fe0->dvb.name = "netup_fe0";
> +	fe1->dvb.name = "netup_fe1";
> +	fe2->dvb.name = "netup_fe2";
> +	videobuf_queue_vmalloc_init(&fe0->dvb.dvbq, &dvb_qops,
> +		&ndev->pci_dev->dev, &ndev->dma[num].lock,
> +		V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP,
> +		sizeof(struct netup_unidvb_buffer), &ndev->dma[num], NULL);
> +	videobuf_queue_vmalloc_init(&fe1->dvb.dvbq, &dvb_qops,
> +		&ndev->pci_dev->dev, &ndev->dma[num].lock,
> +		V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP,
> +		sizeof(struct netup_unidvb_buffer), &ndev->dma[num], NULL);
> +	videobuf_queue_vmalloc_init(&fe2->dvb.dvbq, &dvb_qops,
> +		&ndev->pci_dev->dev, &ndev->dma[num].lock,
> +		V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP,
> +		sizeof(struct netup_unidvb_buffer), &ndev->dma[num], NULL);
> +	fe0->dvb.frontend = dvb_attach(
> +		cxd2841er_attach_s, &demod_config, &ndev->i2c[num].adap);
> +	if (fe0->dvb.frontend == NULL) {
> +		dprintk("%s(): unable to attach DVB-S/S2 frontend\n",
> +			__func__);
> +		goto frontend_detach;
> +	}
> +	horus3a_conf.set_tuner_priv = &ndev->dma[num];
> +	if (!dvb_attach(horus3a_attach, fe0->dvb.frontend,
> +			&horus3a_conf, &ndev->i2c[num].adap)) {
> +		dprintk("%s(): unable to attach DVB-S/S2 tuner frontend\n",
> +			__func__);
> +		goto frontend_detach;
> +	}
> +	if (!dvb_attach(lnbh25_attach, fe0->dvb.frontend,
> +			&lnbh25_conf, &ndev->i2c[num].adap)) {
> +		dprintk("%s(): unable to attach SEC frontend\n",
> +			__func__);
> +		goto frontend_detach;
> +	}
> +	/* DVB-T/T2 frontend */
> +	fe1->dvb.frontend = dvb_attach(
> +		cxd2841er_attach_t, &demod_config, &ndev->i2c[num].adap);
> +	if (fe1->dvb.frontend == NULL) {
> +		dprintk("%s(): unable to attach DVB-T frontend\n",
> +			__func__);
> +		goto frontend_detach;
> +	}
> +	fe1->dvb.frontend->id = 1;
> +	ascot2e_conf.set_tuner_priv = &ndev->dma[num];
> +	if (!dvb_attach(ascot2e_attach, fe1->dvb.frontend,
> +			&ascot2e_conf, &ndev->i2c[num].adap)) {
> +		dprintk("%s(): unable to attach DVB-T tuner frontend\n",
> +			__func__);
> +		goto frontend_detach;
> +	}
> +	/* DVB-C/C2 frontend */
> +	fe2->dvb.frontend = dvb_attach(
> +		cxd2841er_attach_c, &demod_config, &ndev->i2c[num].adap);
> +	if (fe2->dvb.frontend == NULL) {
> +		dprintk("%s(): unable to attach DVB-C frontend\n",
> +			__func__);
> +		goto frontend_detach;
> +	}
> +	fe2->dvb.frontend->id = 2;
> +	if (!dvb_attach(ascot2e_attach, fe2->dvb.frontend,
> +			&ascot2e_conf, &ndev->i2c[num].adap)) {
> +		dprintk("%s(): unable to attach DVB-T/C tuner frontend\n",
> +			__func__);
> +		goto frontend_detach;
> +	}
> +
> +	if (videobuf_dvb_register_bus(
> +			&ndev->frontends[num], THIS_MODULE, NULL,
> +			&ndev->pci_dev->dev, adapter_nr, 1)) {
> +		dprintk("%s(): unable to register DVB bus %d\n",
> +			__func__, num);
> +		goto frontend_detach;
> +	}
> +	dev_info(&ndev->pci_dev->dev, "DVB init done, num=%d\n", num);
> +	return 0;
> +frontend_detach:
> +	videobuf_dvb_dealloc_frontends(&ndev->frontends[num]);
> +	return -EINVAL;
> +}
> +
> +static void netup_unidvb_dvb_fini(struct netup_unidvb_dev *ndev, int num)
> +{
> +	if (num < 0 || num > 1) {
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): unable to unregister DVB bus %d\n",
> +			__func__, num);
> +		return;
> +	}
> +	videobuf_dvb_unregister_bus(&ndev->frontends[num]);
> +	dev_info(&ndev->pci_dev->dev,
> +		"%s(): DVB bus %d unregistered\n", __func__, num);
> +}
> +
> +static int netup_unidvb_dvb_setup(struct netup_unidvb_dev *ndev)
> +{
> +	int res;
> +
> +	res = netup_unidvb_dvb_init(ndev, 0);
> +	if (res)
> +		return res;
> +	res = netup_unidvb_dvb_init(ndev, 1);
> +	if (res) {
> +		netup_unidvb_dvb_fini(ndev, 0);
> +		return res;
> +	}
> +	return 0;
> +}
> +
> +static int netup_unidvb_ring_copy(
> +		struct netup_dma *dma, struct netup_unidvb_buffer *buf)
> +{
> +	u32 copy_bytes, ring_bytes, buff_bytes = buf->vb.size - buf->size;
> +	u8 *p = videobuf_to_vmalloc(&buf->vb) + buf->size;
> +	struct netup_unidvb_dev *ndev = dma->ndev;
> +
> +	if (p == NULL) {
> +		dprintk("%s(): buffer is NULL\n", __func__);
> +		return -EINVAL;
> +	}
> +	if (dma->data_offset + dma->data_size > dma->ring_buffer_size) {
> +		ring_bytes = dma->ring_buffer_size - dma->data_offset;
> +		copy_bytes = (ring_bytes > buff_bytes) ?
> +			buff_bytes : ring_bytes;
> +		memcpy_fromio(p, dma->addr_virt + dma->data_offset, copy_bytes);
> +		p += copy_bytes;
> +		buf->size += copy_bytes;
> +		buff_bytes -= copy_bytes;
> +		dma->data_size -= copy_bytes;
> +		dma->data_offset += copy_bytes;
> +		if (dma->data_offset == dma->ring_buffer_size)
> +			dma->data_offset = 0;
> +	}
> +	if (buff_bytes > 0) {
> +		ring_bytes = dma->data_size;
> +		copy_bytes = (ring_bytes > buff_bytes) ?
> +				buff_bytes : ring_bytes;
> +		memcpy_fromio(p, dma->addr_virt + dma->data_offset, copy_bytes);
> +		buf->size += copy_bytes;
> +		dma->data_size -= copy_bytes;
> +		dma->data_offset += copy_bytes;
> +		if (dma->data_offset == dma->ring_buffer_size)
> +			dma->data_offset = 0;
> +	}
> +	return 0;
> +}
> +
> +static void netup_unidvb_dma_worker(struct work_struct *work)
> +{
> +	struct netup_dma *dma = container_of(work, struct netup_dma, work);
> +	struct netup_unidvb_dev *ndev = dma->ndev;
> +	struct netup_unidvb_buffer *buf;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dma->lock, flags);
> +	if (dma->data_size == 0) {
> +		dprintk("%s(): data_size == 0\n", __func__);
> +		goto work_done;
> +	}
> +	while (dma->data_size > 0) {
> +		if (list_empty(&dma->free_buffers)) {
> +			dprintk("%s(): no free buffers\n", __func__);
> +			goto work_done;
> +		}
> +		buf = list_first_entry(&dma->free_buffers,
> +			struct netup_unidvb_buffer, vb.queue);
> +		if (buf->vb.size != NETUP_DMA_PACKETS_COUNT * 188) {
> +			dprintk("%s(): invalid buffer size %d\n",
> +				__func__, (unsigned int)buf->vb.size);
> +			goto work_done;
> +		}
> +		if (buf->size >= buf->vb.size) {
> +			dprintk("%s(): buffer overflow, size %d\n",
> +				__func__, buf->size);
> +			goto work_done;
> +		}
> +		if (netup_unidvb_ring_copy(dma, buf))
> +			goto work_done;
> +		if (buf->size == buf->vb.size) {
> +			list_del(&buf->vb.queue);
> +			do_gettimeofday(&buf->vb.ts);
> +			buf->vb.state = VIDEOBUF_DONE;
> +			wake_up(&buf->vb.done);
> +		}
> +	}
> +work_done:
> +	dma->data_size = 0;
> +	spin_unlock_irqrestore(&dma->lock, flags);
> +}
> +
> +static void netup_unidvb_dma_timeout(unsigned long data)
> +{
> +	struct netup_dma *dma = (struct netup_dma *)data;
> +	struct netup_unidvb_dev *ndev = dma->ndev;
> +	struct netup_unidvb_buffer *buf;
> +	unsigned long flags;
> +
> +	dprintk("%s()\n", __func__);
> +	spin_lock_irqsave(&dma->lock, flags);
> +	while (!list_empty(&dma->free_buffers)) {
> +		buf = list_first_entry(&dma->free_buffers,
> +			struct netup_unidvb_buffer, vb.queue);
> +		list_del(&buf->vb.queue);
> +		buf->vb.state = VIDEOBUF_ERROR;
> +		wake_up(&buf->vb.done);
> +	}
> +	spin_unlock_irqrestore(&dma->lock, flags);
> +}
> +
> +static int netup_unidvb_dma_init(struct netup_unidvb_dev *ndev, int num)
> +{
> +	struct netup_dma *dma;
> +	struct device *dev = &ndev->pci_dev->dev;
> +
> +	if (num < 0 || num > 1) {
> +		dev_err(dev, "%s(): unable to register DMA%d\n",
> +			__func__, num);
> +		return -ENODEV;
> +	}
> +	dma = &ndev->dma[num];
> +	dev_info(dev, "%s(): starting DMA%d\n", __func__, num);
> +	dma->num = num;
> +	dma->ndev = ndev;
> +	spin_lock_init(&dma->lock);
> +	INIT_WORK(&dma->work, netup_unidvb_dma_worker);
> +	INIT_LIST_HEAD(&dma->free_buffers);
> +	dma->timeout.function = netup_unidvb_dma_timeout;
> +	dma->timeout.data = (unsigned long)dma;
> +	init_timer(&dma->timeout);
> +	dma->ring_buffer_size =
> +		NETUP_DMA_BLOCKS_COUNT * NETUP_DMA_PACKETS_COUNT * 188;
> +	dma->addr_virt = dma_alloc_coherent(
> +		&ndev->pci_dev->dev, dma->ring_buffer_size,
> +		&dma->addr_phys, GFP_KERNEL);
> +	if (!dma->addr_virt) {
> +		dev_err(dev, "%s(): unable to allocate DMA%d buffer\n",
> +			__func__, num);
> +		return -ENOMEM;
> +	}
> +	dev_info(dev, "%s(): DMA%d buffer virt/phys 0x%p/0x%llx size %d\n",
> +		__func__, num, dma->addr_virt,
> +		(unsigned long long)dma->addr_phys,
> +		dma->ring_buffer_size);
> +	memset_io(dma->addr_virt, 0, dma->ring_buffer_size);
> +	dma->addr_last = dma->addr_phys;
> +	dma->high_addr = (u32)(dma->addr_phys & 0xC0000000);
> +	dma->regs = (struct netup_dma_regs *)(num == 0 ?
> +		ndev->bmmio0 + NETUP_DMA0_ADDR :
> +		ndev->bmmio0 + NETUP_DMA1_ADDR);
> +	writel((NETUP_DMA_BLOCKS_COUNT << 24) |
> +		(NETUP_DMA_PACKETS_COUNT << 8) | 188, &dma->regs->size);
> +	writel((u32)(dma->addr_phys & 0x3FFFFFFF), &dma->regs->start_addr_lo);
> +	writel((u32)((u64)dma->addr_phys >> 32), &dma->regs->start_addr_hi);
> +	writel(375000000, &dma->regs->timeout);
> +	msleep(1000);
> +	writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear);
> +	writel(dma->high_addr, ndev->bmmio0 + 0x1000);
> +	return 0;
> +}
> +
> +static void netup_unidvb_dma_fini(struct netup_unidvb_dev *ndev, int num)
> +{
> +	struct netup_dma *dma;
> +
> +	if (num < 0 || num > 1)
> +		return;
> +	dprintk("%s(): num %d\n", __func__, num);
> +	dma = &ndev->dma[num];
> +	netup_unidvb_dma_enable(dma, 0);
> +	msleep(50);
> +	cancel_work_sync(&dma->work);
> +	del_timer(&dma->timeout);
> +	dma_free_coherent(
> +		&ndev->pci_dev->dev, dma->ring_buffer_size,
> +		dma->addr_virt, dma->addr_phys);
> +}
> +
> +static int netup_unidvb_dma_setup(struct netup_unidvb_dev *ndev)
> +{
> +	int res;
> +
> +	res = netup_unidvb_dma_init(ndev, 0);
> +	if (res)
> +		return res;
> +	res = netup_unidvb_dma_init(ndev, 1);
> +	if (res) {
> +		netup_unidvb_dma_fini(ndev, 0);
> +		return res;
> +	}
> +	netup_unidvb_dma_enable(&ndev->dma[0], 1);
> +	netup_unidvb_dma_enable(&ndev->dma[1], 1);
> +	return 0;
> +}
> +
> +static int netup_unidvb_ci_setup(
> +	struct netup_unidvb_dev *ndev, struct pci_dev *pci_dev)
> +{
> +	int res;
> +
> +	writew(NETUP_UNIDVB_IRQ_CI, ndev->bmmio0 + REG_IMASK_SET);
> +	res = netup_unidvb_ci_register(ndev, 0, pci_dev);
> +	if (res)
> +		return res;
> +	res = netup_unidvb_ci_register(ndev, 1, pci_dev);
> +	if (res)
> +		netup_unidvb_ci_unregister(ndev, 0);
> +	return res;
> +}
> +
> +static int netup_unidvb_request_mmio(struct pci_dev *pci_dev)
> +{
> +	if (!request_mem_region(pci_resource_start(pci_dev, 0),
> +			pci_resource_len(pci_dev, 0), DRIVER_NAME)) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to request MMIO bar 0 at 0x%llx\n",
> +			__func__,
> +			(unsigned long long)pci_resource_start(pci_dev, 0));
> +		return -EBUSY;
> +	}
> +	if (!request_mem_region(pci_resource_start(pci_dev, 1),
> +			pci_resource_len(pci_dev, 1), DRIVER_NAME)) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to request MMIO bar 1 at 0x%llx\n",
> +			__func__,
> +			(unsigned long long)pci_resource_start(pci_dev, 1));
> +		release_mem_region(pci_resource_start(pci_dev, 0),
> +			pci_resource_len(pci_dev, 0));
> +		return -EBUSY;
> +	}
> +	return 0;
> +}
> +
> +static int netup_unidvb_request_modules(struct device *dev)
> +{
> +	static const char * const modules[] = {
> +		"lnbh25", "ascot2e", "horus3a", "cxd2841er", NULL
> +	};
> +	const char * const *curr_mod = modules;
> +	int err;
> +
> +	while (*curr_mod != NULL) {
> +		err = request_module(*curr_mod);
> +		if (err) {
> +			dev_warn(dev, "request_module(%s) failed: %d\n",
> +				*curr_mod, err);
> +		}
> +		++curr_mod;
> +	}
> +	return 0;
> +}
> +
> +static int netup_unidvb_initdev(
> +		struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
> +{
> +	u8 board_revision;
> +	u16 board_vendor;
> +	struct netup_unidvb_dev *ndev;
> +	int old_firmware = 0;
> +
> +	netup_unidvb_request_modules(&pci_dev->dev);
> +
> +	/* Check card revision */
> +	if (pci_dev->revision != NETUP_PCI_DEV_REVISION) {
> +		dev_err(&pci_dev->dev,
> +			"netup_unidvb: expected card revision %d, got %d\n",
> +			NETUP_PCI_DEV_REVISION, pci_dev->revision);
> +		dev_err(&pci_dev->dev,
> +			"Please upgrade firmware!\n");
> +		dev_err(&pci_dev->dev,
> +			"Instructions on http://www.netup.tv\n");
> +		old_firmware = 1;
> +		spi_enable = 1;
> +	}
> +
> +	/* allocate device context */
> +	ndev = kzalloc(sizeof(*ndev), GFP_KERNEL);
> +
> +	if (!ndev)
> +		goto dev_alloc_err;
> +	memset(ndev, 0, sizeof(*ndev));
> +	ndev->old_fw = old_firmware;
> +	ndev->wq = create_singlethread_workqueue(DRIVER_NAME);
> +	if (!ndev->wq) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to create workqueue\n", __func__);
> +		goto wq_create_err;
> +	}
> +	ndev->pci_dev = pci_dev;
> +	ndev->pci_bus = pci_dev->bus->number;
> +	ndev->pci_slot = PCI_SLOT(pci_dev->devfn);
> +	ndev->pci_func = PCI_FUNC(pci_dev->devfn);
> +	ndev->board_num = ndev->pci_bus*10 + ndev->pci_slot;
> +	pci_set_drvdata(pci_dev, ndev);
> +	/* PCI init */
> +	dev_info(&pci_dev->dev, "%s(): PCI device (%d). Bus:0x%x Slot:0x%x\n",
> +		__func__, ndev->board_num, ndev->pci_bus, ndev->pci_slot);
> +
> +	if (pci_enable_device(pci_dev)) {
> +		dev_err(&pci_dev->dev, "%s(): pci_enable_device failed\n",
> +			__func__);
> +		goto pci_enable_err;
> +	}
> +	/* read PCI info */
> +	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &board_revision);
> +	pci_read_config_word(pci_dev, PCI_VENDOR_ID, &board_vendor);
> +	if (board_vendor != NETUP_VENDOR_ID) {
> +		dev_err(&pci_dev->dev, "%s(): unknown board vendor 0x%x",
> +			__func__, board_vendor);
> +		goto pci_detect_err;
> +	}
> +	dev_info(&pci_dev->dev,
> +		"%s(): board vendor 0x%x, revision 0x%x\n",
> +		__func__, board_vendor, board_revision);
> +	pci_set_master(pci_dev);
> +	if (!pci_dma_supported(pci_dev, 0xffffffff)) {
> +		dev_err(&pci_dev->dev, "%s(): 32bit PCI DMA is not supported\n",
> +			__func__);
> +		goto pci_detect_err;
> +	}
> +	dev_info(&pci_dev->dev, "%s(): using 32bit PCI DMA\n", __func__);
> +	/* Clear "no snoop" and "relaxed ordering" bits, use default MRRS. */
> +	pcie_capability_clear_and_set_word(pci_dev, PCI_EXP_DEVCTL,
> +		PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_RELAX_EN |
> +		PCI_EXP_DEVCTL_NOSNOOP_EN, 0);
> +	/* Adjust PCIe completion timeout. */
> +	pcie_capability_clear_and_set_word(
> +		pci_dev, PCI_EXP_DEVCTL2, 0xf, 0x2);
> +
> +	if (netup_unidvb_request_mmio(pci_dev)) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to request MMIO regions\n", __func__);
> +		goto pci_detect_err;
> +	}
> +	ndev->lmmio0 = ioremap(pci_resource_start(pci_dev, 0),
> +		pci_resource_len(pci_dev, 0));
> +	if (!ndev->lmmio0) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to remap MMIO bar 0\n", __func__);
> +		goto pci_bar0_error;
> +	}
> +	ndev->lmmio1 = ioremap(pci_resource_start(pci_dev, 1),
> +		pci_resource_len(pci_dev, 1));
> +	if (!ndev->lmmio1) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): unable to remap MMIO bar 1\n", __func__);
> +		goto pci_bar1_error;
> +	}
> +	ndev->bmmio0 = (u8 __iomem *)ndev->lmmio0;
> +	ndev->bmmio1 = (u8 __iomem *)ndev->lmmio1;
> +	dev_info(&pci_dev->dev,
> +		"%s(): PCI MMIO at 0x%p (%d); 0x%p (%d); IRQ %d",
> +		__func__,
> +		ndev->lmmio0, (u32)pci_resource_len(pci_dev, 0),
> +		ndev->lmmio1, (u32)pci_resource_len(pci_dev, 1),
> +		pci_dev->irq);
> +	if (request_irq(pci_dev->irq, netup_unidvb_isr,
> +			IRQF_SHARED | IRQF_DISABLED,
> +			"netup_unidvb", pci_dev) < 0) {
> +		dev_err(&pci_dev->dev,
> +			"%s(): can't get IRQ %d\n", __func__, pci_dev->irq);
> +		goto irq_request_err;
> +	}
> +	netup_unidvb_dev_enable(ndev);
> +	if (spi_enable && netup_spi_init(ndev)) {
> +		dev_warn(&pci_dev->dev,
> +			"netup_unidvb: SPI flash setup failed\n");
> +		goto spi_setup_err;
> +	}
> +	if (old_firmware) {
> +		dev_err(&pci_dev->dev,
> +			"netup_unidvb: card initialization was incomplete\n");
> +		return 0;
> +	}
> +	if (netup_i2c_register(ndev)) {
> +		dev_err(&pci_dev->dev, "netup_unidvb: I2C setup failed\n");
> +		goto i2c_setup_err;
> +	}
> +	/* enable I2C IRQs */
> +	writew(NETUP_UNIDVB_IRQ_I2C0 | NETUP_UNIDVB_IRQ_I2C1,
> +		ndev->bmmio0 + REG_IMASK_SET);
> +	usleep_range(5000, 10000);
> +	if (netup_unidvb_dvb_setup(ndev)) {
> +		dev_err(&pci_dev->dev, "netup_unidvb: DVB setup failed\n");
> +		goto dvb_setup_err;
> +	}
> +	if (netup_unidvb_ci_setup(ndev, pci_dev)) {
> +		dev_err(&pci_dev->dev, "netup_unidvb: CI setup failed\n");
> +		goto ci_setup_err;
> +	}
> +	if (netup_unidvb_dma_setup(ndev)) {
> +		dev_err(&pci_dev->dev, "netup_unidvb: DMA setup failed\n");
> +		goto dma_setup_err;
> +	}
> +	dev_info(&pci_dev->dev, "netup_unidvb: device has been initialized\n");
> +	return 0;
> +dma_setup_err:
> +	netup_unidvb_ci_unregister(ndev, 0);
> +	netup_unidvb_ci_unregister(ndev, 1);
> +ci_setup_err:
> +	netup_unidvb_dvb_fini(ndev, 0);
> +	netup_unidvb_dvb_fini(ndev, 1);
> +dvb_setup_err:
> +	netup_i2c_unregister(ndev);
> +i2c_setup_err:
> +	if (ndev->spi)
> +		netup_spi_release(ndev);
> +spi_setup_err:
> +	free_irq(pci_dev->irq, pci_dev);
> +irq_request_err:
> +	iounmap(ndev->lmmio1);
> +pci_bar1_error:
> +	iounmap(ndev->lmmio0);
> +pci_bar0_error:
> +	release_mem_region(
> +		pci_resource_start(pci_dev, 0),
> +		pci_resource_len(pci_dev, 0));
> +	release_mem_region(
> +		pci_resource_start(pci_dev, 1),
> +		pci_resource_len(pci_dev, 1));
> +pci_detect_err:
> +	pci_disable_device(pci_dev);
> +pci_enable_err:
> +	pci_set_drvdata(pci_dev, NULL);
> +	destroy_workqueue(ndev->wq);
> +wq_create_err:
> +	kfree(ndev);
> +dev_alloc_err:
> +	dev_err(&pci_dev->dev,
> +		"%s(): failed to initizalize device\n", __func__);
> +	return -EIO;
> +}
> +
> +static void netup_unidvb_finidev(struct pci_dev *pci_dev)
> +{
> +	struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev);
> +
> +	dev_info(&pci_dev->dev, "%s(): trying to stop device\n", __func__);
> +	if (!ndev->old_fw) {
> +		netup_unidvb_dma_fini(ndev, 0);
> +		netup_unidvb_dma_fini(ndev, 1);
> +		netup_unidvb_ci_unregister(ndev, 0);
> +		netup_unidvb_ci_unregister(ndev, 1);
> +		netup_unidvb_dvb_fini(ndev, 0);
> +		netup_unidvb_dvb_fini(ndev, 1);
> +		netup_i2c_unregister(ndev);
> +	}
> +	if (ndev->spi)
> +		netup_spi_release(ndev);
> +	writew(0xffff, ndev->bmmio0 + REG_IMASK_CLEAR);
> +	free_irq(pci_dev->irq, pci_dev);
> +	iounmap(ndev->lmmio0);
> +	iounmap(ndev->lmmio1);
> +	release_mem_region(
> +		pci_resource_start(pci_dev, 0),
> +		pci_resource_len(pci_dev, 0));
> +	release_mem_region(
> +		pci_resource_start(pci_dev, 1),
> +		pci_resource_len(pci_dev, 1));
> +	pci_disable_device(pci_dev);
> +	pci_set_drvdata(pci_dev, NULL);
> +	destroy_workqueue(ndev->wq);
> +	kfree(ndev);
> +	dev_info(&pci_dev->dev,
> +		"%s(): device has been successfully stopped\n", __func__);
> +}
> +
> +
> +static struct pci_device_id netup_unidvb_pci_tbl[] = {
> +	{ PCI_DEVICE(0x1b55, 0x18f6) },
> +	{ 0, }
> +};
> +MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl);
> +
> +static struct pci_driver netup_unidvb_pci_driver = {
> +	.name     = "netup_unidvb",
> +	.id_table = netup_unidvb_pci_tbl,
> +	.probe    = netup_unidvb_initdev,
> +	.remove   = netup_unidvb_finidev,
> +	.suspend  = NULL,
> +	.resume   = NULL,
> +};
> +
> +static int __init netup_unidvb_init(void)
> +{
> +	return pci_register_driver(&netup_unidvb_pci_driver);
> +}
> +
> +static void __exit netup_unidvb_fini(void)
> +{
> +	pci_unregister_driver(&netup_unidvb_pci_driver);
> +}
> +
> +module_init(netup_unidvb_init);
> +module_exit(netup_unidvb_fini);
> diff --git a/drivers/media/pci/netup/netup_unidvb_i2c.c b/drivers/media/pci/netup/netup_unidvb_i2c.c
> new file mode 100644
> index 0000000..7ebe652
> --- /dev/null
> +++ b/drivers/media/pci/netup/netup_unidvb_i2c.c
> @@ -0,0 +1,350 @@
> +/*
> + * netup_unidvb_i2c.c
> + *
> + * Internal I2C bus driver 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/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/init.h>
> +#include <linux/delay.h>
> +#include "netup_unidvb.h"
> +
> +static int i2c_debug;
> +module_param(i2c_debug, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
> +MODULE_PARM_DESC(i2c_debug, "Debug NetUP I2C (default:no)");
> +
> +#define dprintk(args...) \
> +do { \
> +	if (i2c_debug) \
> +		dev_dbg(i2c->adap.dev.parent, args); \
> +} while (0)

This is not very nice, as, in order to enable debug, both this parameter
and dynamic printk should be enabled via /sys/kernel/debug/dynamic_debug.

Just use dev_dbg() directly.


> +
> +/* twi_ctrl0_stat reg bits */
> +#define TWI_IRQEN_COMPL	0x1
> +#define TWI_IRQEN_ANACK 0x2
> +#define TWI_IRQEN_DNACK 0x4
> +#define TWI_IRQ_COMPL	(TWI_IRQEN_COMPL << 8)
> +#define TWI_IRQ_ANACK	(TWI_IRQEN_ANACK << 8)
> +#define TWI_IRQ_DNACK	(TWI_IRQEN_DNACK << 8)
> +#define TWI_IRQ_TX	0x800
> +#define TWI_IRQ_RX	0x1000
> +#define TWI_IRQEN	(TWI_IRQEN_COMPL | TWI_IRQEN_ANACK | TWI_IRQEN_DNACK)
> +/* twi_addr_ctrl1 reg bits*/
> +#define TWI_TRANSFER	0x100
> +#define TWI_NOSTOP	0x200
> +#define TWI_SOFT_RESET	0x2000
> +/* twi_clkdiv reg value */
> +#define TWI_CLKDIV	156
> +/* fifo_stat_ctrl reg bits */
> +#define FIFO_IRQEN	0x8000
> +#define FIFO_RESET	0x4000
> +
> +#define FIFO_SIZE	16
> +
> +irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c)
> +{
> +	u16 reg, tmp;
> +	unsigned long flags;
> +	irqreturn_t iret = IRQ_HANDLED;
> +
> +	spin_lock_irqsave(&i2c->lock, flags);
> +	reg = readw(&i2c->regs->twi_ctrl0_stat);
> +	writew(reg & ~TWI_IRQEN, &i2c->regs->twi_ctrl0_stat);
> +	dprintk("%s(): twi_ctrl0_state 0x%x\n", __func__, reg);
> +	if ((reg & TWI_IRQEN_COMPL) != 0 && (reg & TWI_IRQ_COMPL)) {
> +		dprintk("%s(): TWI_IRQEN_COMPL\n", __func__);
> +		i2c->state = STATE_DONE;
> +		goto irq_ok;
> +	}
> +	if ((reg & TWI_IRQEN_ANACK) != 0 && (reg & TWI_IRQ_ANACK)) {
> +		dprintk("%s(): TWI_IRQEN_ANACK\n", __func__);
> +		i2c->state = STATE_ERROR;
> +		goto irq_ok;
> +	}
> +	if ((reg & TWI_IRQEN_DNACK) != 0 && (reg & TWI_IRQ_DNACK)) {
> +		dprintk("%s(): TWI_IRQEN_DNACK\n", __func__);
> +		i2c->state = STATE_ERROR;
> +		goto irq_ok;
> +	}
> +	if ((reg & TWI_IRQ_RX) != 0) {
> +		tmp = readw(&i2c->regs->rx_fifo.stat_ctrl);
> +		writew(tmp & ~FIFO_IRQEN, &i2c->regs->rx_fifo.stat_ctrl);
> +		i2c->state = STATE_WANT_READ;
> +		dprintk("%s(): want read\n", __func__);
> +		goto irq_ok;
> +	}
> +	if ((reg & TWI_IRQ_TX) != 0) {
> +		tmp = readw(&i2c->regs->tx_fifo.stat_ctrl);
> +		writew(tmp & ~FIFO_IRQEN, &i2c->regs->tx_fifo.stat_ctrl);
> +		i2c->state = STATE_WANT_WRITE;
> +		dprintk("%s(): want write\n", __func__);
> +		goto irq_ok;
> +	}
> +	dev_warn(&i2c->adap.dev, "%s(): not mine interrupt\n", __func__);
> +	iret = IRQ_NONE;
> +irq_ok:
> +	spin_unlock_irqrestore(&i2c->lock, flags);
> +	if (iret == IRQ_HANDLED)
> +		wake_up(&i2c->wq);
> +	return iret;
> +}
> +
> +static void netup_i2c_reset(struct netup_i2c *i2c)
> +{
> +	dprintk("%s()\n", __func__);
> +	i2c->state = STATE_DONE;
> +	writew(TWI_SOFT_RESET, &i2c->regs->twi_addr_ctrl1);
> +	writew(TWI_CLKDIV, &i2c->regs->clkdiv);
> +	writew(FIFO_RESET, &i2c->regs->tx_fifo.stat_ctrl);
> +	writew(FIFO_RESET, &i2c->regs->rx_fifo.stat_ctrl);
> +	writew(0x800, &i2c->regs->tx_fifo.stat_ctrl);
> +	writew(0x800, &i2c->regs->rx_fifo.stat_ctrl);
> +}
> +
> +static void netup_i2c_fifo_tx(struct netup_i2c *i2c)
> +{
> +	u8 data;
> +	u32 fifo_space = FIFO_SIZE -
> +		(readw(&i2c->regs->tx_fifo.stat_ctrl) & 0x3f);
> +	u32 msg_length = i2c->msg->len - i2c->xmit_size;
> +
> +	msg_length = (msg_length < fifo_space ? msg_length : fifo_space);
> +	while (msg_length--) {
> +		data = i2c->msg->buf[i2c->xmit_size++];
> +		writeb(data, &i2c->regs->tx_fifo.data8);
> +		dprintk("%s(): write 0x%02x\n", __func__, data);
> +	}
> +	if (i2c->xmit_size < i2c->msg->len) {
> +		dprintk("%s(): TX IRQ enabled\n", __func__);
> +		writew(readw(&i2c->regs->tx_fifo.stat_ctrl) | FIFO_IRQEN,
> +			&i2c->regs->tx_fifo.stat_ctrl);
> +	}
> +}
> +
> +static void netup_i2c_fifo_rx(struct netup_i2c *i2c)
> +{
> +	u8 data;
> +	u32 fifo_size = readw(&i2c->regs->rx_fifo.stat_ctrl) & 0x3f;
> +
> +	dprintk("%s(): RX fifo size %d\n", __func__, fifo_size);
> +	while (fifo_size--) {
> +		data = readb(&i2c->regs->rx_fifo.data8);
> +		if ((i2c->msg->flags & I2C_M_RD) != 0 &&
> +					i2c->xmit_size < i2c->msg->len) {
> +			i2c->msg->buf[i2c->xmit_size++] = data;
> +			dprintk("%s(): read 0x%02x\n", __func__, data);
> +		}
> +	}
> +	if (i2c->xmit_size < i2c->msg->len) {
> +		dprintk("%s(): RX IRQ enabled\n", __func__);
> +		writew(readw(&i2c->regs->rx_fifo.stat_ctrl) | FIFO_IRQEN,
> +			&i2c->regs->rx_fifo.stat_ctrl);
> +	}
> +}
> +
> +static void netup_i2c_start_xfer(struct netup_i2c *i2c)
> +{
> +	u16 rdflag = ((i2c->msg->flags & I2C_M_RD) ? 1 : 0);
> +	u16 reg = readw(&i2c->regs->twi_ctrl0_stat);
> +
> +	writew(TWI_IRQEN | reg, &i2c->regs->twi_ctrl0_stat);
> +	writew(i2c->msg->len, &i2c->regs->length);
> +	writew(TWI_TRANSFER | (i2c->msg->addr << 1) | rdflag,
> +		&i2c->regs->twi_addr_ctrl1);
> +	dprintk("%s(): length %d twi_addr_ctrl1 0x%x twi_ctrl0_stat 0x%x\n",
> +		__func__,
> +		readw(&i2c->regs->length),
> +		readw(&i2c->regs->twi_addr_ctrl1),
> +		readw(&i2c->regs->twi_ctrl0_stat));
> +	i2c->state = STATE_WAIT;
> +	i2c->xmit_size = 0;
> +	if (!rdflag)
> +		netup_i2c_fifo_tx(i2c);
> +	else
> +		writew(FIFO_IRQEN | readw(&i2c->regs->rx_fifo.stat_ctrl),
> +			&i2c->regs->rx_fifo.stat_ctrl);
> +}
> +
> +static int netup_i2c_xfer(
> +	struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
> +{
> +	unsigned long flags;
> +	int i, trans_done, res = num;
> +	struct netup_i2c *i2c = i2c_get_adapdata(adap);
> +	u16 reg;
> +
> +	if (num <= 0) {
> +		dprintk("%s(): num == %d\n", __func__, num);
> +		return -EINVAL;
> +	}
> +	spin_lock_irqsave(&i2c->lock, flags);
> +	if (i2c->state != STATE_DONE) {
> +		dprintk("%s(): i2c->state == %d, resetting I2C\n",
> +			__func__, i2c->state);
> +		netup_i2c_reset(i2c);
> +	}
> +	dprintk("%s() num %d\n", __func__, num);
> +	for (i = 0; i < num; i++) {
> +		i2c->msg = &msgs[i];
> +		netup_i2c_start_xfer(i2c);
> +		trans_done = 0;
> +		while (!trans_done) {
> +			spin_unlock_irqrestore(&i2c->lock, flags);
> +			if (wait_event_timeout(
> +					i2c->wq,
> +					i2c->state != STATE_WAIT,
> +					NETUP_I2C_TIMEOUT)) {
> +				spin_lock_irqsave(&i2c->lock, flags);
> +				switch (i2c->state) {
> +				case STATE_WANT_READ:
> +					netup_i2c_fifo_rx(i2c);
> +					break;
> +				case STATE_WANT_WRITE:
> +					netup_i2c_fifo_tx(i2c);
> +					break;
> +				case STATE_DONE:
> +					if ((i2c->msg->flags & I2C_M_RD) != 0 &&
> +						i2c->xmit_size != i2c->msg->len)
> +						netup_i2c_fifo_rx(i2c);
> +					dprintk("%s(): msg %d OK\n",
> +						__func__, i);
> +					trans_done = 1;
> +					break;
> +				case STATE_ERROR:
> +					res = -EIO;
> +					dprintk("%s(): error state\n",
> +						__func__);
> +					goto done;
> +				default:
> +					dprintk("%s(): invalid state %d\n",
> +						__func__, i2c->state);
> +					res = -EINVAL;
> +					goto done;
> +				}
> +				if (!trans_done) {
> +					i2c->state = STATE_WAIT;
> +					reg = readw(
> +						&i2c->regs->twi_ctrl0_stat);
> +					writew(TWI_IRQEN | reg,
> +						&i2c->regs->twi_ctrl0_stat);
> +				}
> +				spin_unlock_irqrestore(&i2c->lock, flags);
> +			} else {
> +				spin_lock_irqsave(&i2c->lock, flags);
> +				dprintk("%s(): wait timeout\n", __func__);
> +				res = -ETIMEDOUT;
> +				goto done;
> +			}
> +			spin_lock_irqsave(&i2c->lock, flags);
> +		}
> +	}
> +done:	spin_unlock_irqrestore(&i2c->lock, flags);
> +	dprintk("%s(): result %d\n", __func__, res);
> +	return res;
> +}
> +
> +static u32 netup_i2c_func(struct i2c_adapter *adap)
> +{
> +	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm netup_i2c_algorithm = {
> +	.master_xfer	= netup_i2c_xfer,
> +	.functionality	= netup_i2c_func,
> +};
> +
> +static struct i2c_adapter netup_i2c_adapter = {
> +	.owner		= THIS_MODULE,
> +	.name		= NETUP_UNIDVB_NAME,
> +	.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD,
> +	.algo		= &netup_i2c_algorithm,
> +};
> +
> +static int netup_i2c_init(struct netup_unidvb_dev *ndev, int bus_num)
> +{
> +	int ret;
> +	struct netup_i2c *i2c;
> +
> +	if (bus_num < 0 || bus_num > 1) {
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): invalid bus_num %d\n", __func__, bus_num);
> +		return -EINVAL;
> +	}
> +	i2c = &ndev->i2c[bus_num];
> +	spin_lock_init(&i2c->lock);
> +	init_waitqueue_head(&i2c->wq);
> +	i2c->regs = (struct netup_i2c_regs *)(ndev->bmmio0 +
> +		(bus_num == 0 ? NETUP_I2C_BUS0_ADDR : NETUP_I2C_BUS1_ADDR));
> +	netup_i2c_reset(i2c);
> +	i2c->adap = netup_i2c_adapter;
> +	i2c->adap.dev.parent = &ndev->pci_dev->dev;
> +	i2c_set_adapdata(&i2c->adap, i2c);
> +	ret = i2c_add_adapter(&i2c->adap);
> +	if (ret) {
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): failed to add I2C adapter\n", __func__);
> +		return ret;
> +	}
> +	dev_info(&ndev->pci_dev->dev,
> +		"%s(): registered I2C bus %d at 0x%x\n",
> +		__func__,
> +		bus_num, (bus_num == 0 ?
> +			NETUP_I2C_BUS0_ADDR :
> +			NETUP_I2C_BUS1_ADDR));
> +	return 0;
> +}
> +
> +static void netup_i2c_remove(struct netup_unidvb_dev *ndev, int bus_num)
> +{
> +	struct netup_i2c *i2c;
> +
> +	if (bus_num < 0 || bus_num > 1) {
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): invalid bus number %d\n", __func__, bus_num);
> +		return;
> +	}
> +	i2c = &ndev->i2c[bus_num];
> +	netup_i2c_reset(i2c);
> +	msleep(20);

This sleep here seems to be a hack... why this is needed? Is it due to some
race condition? If so, the best would be to use kref() and put the
i2c_del_adapter() code as the removal code, to be called after all users
of the i2c bus to release it.

> +	/* remove adapter */
> +	i2c_del_adapter(&i2c->adap);
> +	dev_info(&ndev->pci_dev->dev,
> +		"netup_i2c_remove: unregistered I2C bus %d\n", bus_num);
> +}
> +
> +int netup_i2c_register(struct netup_unidvb_dev *ndev)
> +{
> +	int ret;
> +
> +	ret = netup_i2c_init(ndev, 0);
> +	if (ret)
> +		return ret;
> +	ret = netup_i2c_init(ndev, 1);
> +	if (ret) {
> +		netup_i2c_remove(ndev, 0);
> +		return ret;
> +	}
> +	return 0;
> +}
> +
> +void netup_i2c_unregister(struct netup_unidvb_dev *ndev)
> +{
> +	netup_i2c_remove(ndev, 0);
> +	netup_i2c_remove(ndev, 1);
> +}
> +
> diff --git a/drivers/media/pci/netup/netup_unidvb_spi.c b/drivers/media/pci/netup/netup_unidvb_spi.c
> new file mode 100644
> index 0000000..d22158c
> --- /dev/null
> +++ b/drivers/media/pci/netup/netup_unidvb_spi.c
> @@ -0,0 +1,272 @@
> +/*
> + * netup_unidvb_spi.c
> + *
> + * Internal SPI driver 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 "netup_unidvb.h"
> +#include <linux/spi/spi.h>
> +#include <linux/spi/flash.h>
> +#include <linux/mtd/partitions.h>
> +#include <mtd/mtd-abi.h>
> +
> +#define NETUP_SPI_CTRL_IRQ	0x1000
> +#define NETUP_SPI_CTRL_IMASK	0x2000
> +#define NETUP_SPI_CTRL_START	0x8000
> +#define NETUP_SPI_CTRL_LAST_CS	0x4000
> +
> +#define NETUP_SPI_TIMEOUT	(6*HZ)
> +
> +static int spi_debug;
> +module_param(spi_debug, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
> +MODULE_PARM_DESC(i2c_debug, "Debug NetUP SPI bus (default:no)");
> +
> +#define dprintk(args...) \
> +	do { \
> +		if (spi_debug) \
> +			dev_dbg(&spi->master->dev, args); \
> +	} while (0)

This is not very nice, as, in order to enable debug, both this parameter
and dynamic printk should be enabled via /sys/kernel/debug/dynamic_debug.

Just use dev_dbg() directly.


> +
> +enum netup_spi_state {
> +	SPI_STATE_START,
> +	SPI_STATE_DONE,
> +};
> +
> +struct netup_spi_regs {
> +	u8	data[1024];
> +	u16	control_stat;
> +	u16	clock_divider;
> +} __packed __aligned(1);
> +
> +struct netup_spi {
> +	struct device			*dev;
> +	struct spi_master		*master;
> +	struct netup_spi_regs		*regs;
> +	u8 __iomem			*mmio;
> +	spinlock_t			lock;
> +	wait_queue_head_t		waitq;
> +	enum netup_spi_state		state;
> +};
> +
> +static char netup_spi_name[64] = "fpga";
> +
> +static struct mtd_partition netup_spi_flash_partitions = {
> +	.name = netup_spi_name,
> +	.size = 0x1000000, /* 16MB */
> +	.offset = 0,
> +	.mask_flags = MTD_CAP_ROM
> +};
> +
> +static struct flash_platform_data spi_flash_data = {
> +	.name = "netup0_m25p128",
> +	.parts = &netup_spi_flash_partitions,
> +	.nr_parts = 1,
> +};
> +
> +static struct spi_board_info netup_spi_board = {
> +	.modalias = "m25p128",
> +	.max_speed_hz = 11000000,
> +	.chip_select = 0,
> +	.mode = SPI_MODE_0,
> +	.platform_data = &spi_flash_data,
> +};
> +
> +irqreturn_t netup_spi_interrupt(struct netup_spi *spi)
> +{
> +	u16 reg;
> +	unsigned long flags;
> +
> +	if (!spi) {
> +		dprintk("%s(): SPI not initialized\n", __func__);
> +		return IRQ_NONE;
> +	}
> +	spin_lock_irqsave(&spi->lock, flags);
> +	reg = readw(&spi->regs->control_stat);
> +	if (!(reg & NETUP_SPI_CTRL_IRQ)) {
> +		spin_unlock_irqrestore(&spi->lock, flags);
> +		dprintk("%s(): not mine interrupt\n", __func__);
> +		return IRQ_NONE;
> +	}
> +	writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
> +	reg = readw(&spi->regs->control_stat);
> +	writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat);
> +	spi->state = SPI_STATE_DONE;
> +	wake_up(&spi->waitq);
> +	spin_unlock_irqrestore(&spi->lock, flags);
> +	dprintk("%s(): SPI interrupt handled\n", __func__);
> +	return IRQ_HANDLED;
> +}
> +
> +static void netup_spi_dump(struct netup_spi *spi, u8 *buf, u32 len, int tx)
> +{
> +	u32 i;
> +
> +	if (!spi_debug)
> +		return;
> +	dprintk("%s(): %s data len %d\n",
> +		__func__, (tx == 0 ? "RX" : "TX"), len);
> +	for (i = 0; i < len; i++)
> +		dprintk("%02x\n", *(buf + i));
> +}
> +
> +static int netup_spi_transfer(
> +		struct spi_master *master, struct spi_message *msg)
> +{
> +	struct netup_spi *spi = spi_master_get_devdata(master);
> +	struct spi_transfer *t;
> +	int result = 0;
> +	u32 tr_size;
> +
> +	/* reset CS */
> +	writew(NETUP_SPI_CTRL_LAST_CS, &spi->regs->control_stat);
> +	writew(0, &spi->regs->control_stat);
> +	list_for_each_entry(t, &msg->transfers, transfer_list) {
> +		tr_size = t->len;
> +		while (tr_size) {
> +			u32 frag_offset = t->len - tr_size;
> +			u32 frag_size = (tr_size > sizeof(spi->regs->data)) ?
> +					sizeof(spi->regs->data) : tr_size;
> +			int frag_last = 0;
> +
> +			if (list_is_last(&t->transfer_list,
> +					&msg->transfers) &&
> +					frag_offset + frag_size == t->len) {
> +				frag_last = 1;
> +			}
> +			if (t->tx_buf) {
> +				memcpy_toio(spi->regs->data,
> +					t->tx_buf + frag_offset,
> +					frag_size);
> +				netup_spi_dump(spi,
> +					(u8 *)t->tx_buf + frag_offset,
> +					frag_size, 1);
> +			} else {
> +				memset_io(spi->regs->data,
> +					0, frag_size);
> +			}
> +			spi->state = SPI_STATE_START;
> +			writew((frag_size & 0x3ff) |
> +				NETUP_SPI_CTRL_IMASK |
> +				NETUP_SPI_CTRL_START |
> +				(frag_last ? NETUP_SPI_CTRL_LAST_CS : 0),
> +				&spi->regs->control_stat);
> +			dprintk("%s(): control_stat 0x%04x\n",
> +				__func__, readw(&spi->regs->control_stat));
> +			wait_event_timeout(spi->waitq,
> +				spi->state != SPI_STATE_START,
> +				NETUP_SPI_TIMEOUT);
> +			if (spi->state == SPI_STATE_DONE) {
> +				if (t->rx_buf) {
> +					memcpy_fromio(t->rx_buf + frag_offset,
> +						spi->regs->data, frag_size);
> +					netup_spi_dump(spi,
> +						(u8 *)t->rx_buf + frag_offset,
> +						frag_size, 0);
> +				}
> +			} else {
> +				if (spi->state == SPI_STATE_START) {
> +					dprintk("%s(): transfer timeout\n",
> +						__func__);
> +				} else {
> +					dprintk("%s(): invalid state %d\n",
> +						__func__, spi->state);
> +				}
> +				result = -EIO;
> +				goto done;
> +			}
> +			tr_size -= frag_size;
> +			msg->actual_length += frag_size;
> +		}
> +	}
> +done:	msg->status = result;
> +	spi_finalize_current_message(master);
> +	return result;
> +}
> +
> +static int netup_spi_setup(struct spi_device *spi)
> +{
> +	return 0;
> +}
> +
> +int netup_spi_init(struct netup_unidvb_dev *ndev)
> +{
> +	struct spi_master *master;
> +	struct netup_spi *nspi;
> +
> +	master = spi_alloc_master(
> +		&ndev->pci_dev->dev, sizeof(struct netup_spi));
> +	if (!master) {
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): unable to alloc SPI master\n", __func__);
> +		return -EINVAL;
> +	}
> +	nspi = spi_master_get_devdata(master);
> +	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
> +	master->bus_num = -1;
> +	master->num_chipselect = 1;
> +	master->transfer_one_message = netup_spi_transfer;
> +	master->setup = netup_spi_setup;
> +	spin_lock_init(&nspi->lock);
> +	init_waitqueue_head(&nspi->waitq);
> +	nspi->master = master;
> +	nspi->regs = (struct netup_spi_regs *)(ndev->bmmio0 + 0x4000);
> +	writew(2, &nspi->regs->clock_divider);
> +	writew(NETUP_UNIDVB_IRQ_SPI, ndev->bmmio0 + REG_IMASK_SET);
> +	ndev->spi = nspi;
> +	if (spi_register_master(master)) {
> +		ndev->spi = NULL;
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): unable to register SPI bus\n", __func__);
> +		return -EINVAL;
> +	}
> +	snprintf(netup_spi_name,
> +		sizeof(netup_spi_name),
> +		"fpga_%02x:%02x.%01x",
> +		ndev->pci_bus,
> +		ndev->pci_slot,
> +		ndev->pci_func);
> +	if (!spi_new_device(master, &netup_spi_board)) {
> +		ndev->spi = NULL;
> +		dev_err(&ndev->pci_dev->dev,
> +			"%s(): unable to create SPI device\n", __func__);
> +		return -EINVAL;
> +	}
> +	dev_dbg(&ndev->pci_dev->dev, "%s(): SPI init OK\n", __func__);
> +	return 0;
> +}
> +
> +void netup_spi_release(struct netup_unidvb_dev *ndev)
> +{
> +	u16 reg;
> +	unsigned long flags;
> +	struct netup_spi *spi = ndev->spi;
> +
> +	if (!spi) {
> +		dprintk("%s(): SPI not initialized\n", __func__);
> +		return;
> +	}
> +	spin_lock_irqsave(&spi->lock, flags);
> +	reg = readw(&spi->regs->control_stat);
> +	writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
> +	reg = readw(&spi->regs->control_stat);
> +	writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat);
> +	spin_unlock_irqrestore(&spi->lock, flags);
> +	spi_unregister_master(spi->master);
> +	ndev->spi = NULL;
> +}
> +
> +

      reply	other threads:[~2015-03-05 11:59 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-02-02  9:22 [PATCH 5/5] [media] netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver Kozlov Sergey
2015-03-05 11:59 ` Mauro Carvalho Chehab [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20150305085930.357ca5b0@recife.lan \
    --to=mchehab@osg.samsung.com \
    --cc=linux-media@vger.kernel.org \
    --cc=serjk@netup.ru \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.