18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * altera-ci.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  CI driver in conjunction with NetUp Dual DVB-T/C RF CI card
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2010,2011 NetUP Inc.
88c2ecf20Sopenharmony_ci * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * currently cx23885 GPIO's used.
138c2ecf20Sopenharmony_ci * GPIO-0 ~INT in
148c2ecf20Sopenharmony_ci * GPIO-1 TMS out
158c2ecf20Sopenharmony_ci * GPIO-2 ~reset chips out
168c2ecf20Sopenharmony_ci * GPIO-3 to GPIO-10 data/addr for CA in/out
178c2ecf20Sopenharmony_ci * GPIO-11 ~CS out
188c2ecf20Sopenharmony_ci * GPIO-12 AD_RG out
198c2ecf20Sopenharmony_ci * GPIO-13 ~WR out
208c2ecf20Sopenharmony_ci * GPIO-14 ~RD out
218c2ecf20Sopenharmony_ci * GPIO-15 ~RDY in
228c2ecf20Sopenharmony_ci * GPIO-16 TCK out
238c2ecf20Sopenharmony_ci * GPIO-17 TDO in
248c2ecf20Sopenharmony_ci * GPIO-18 TDI out
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci *  Bit definitions for MC417_RWD and MC417_OEN registers
288c2ecf20Sopenharmony_ci * bits 31-16
298c2ecf20Sopenharmony_ci * +-----------+
308c2ecf20Sopenharmony_ci * | Reserved  |
318c2ecf20Sopenharmony_ci * +-----------+
328c2ecf20Sopenharmony_ci *   bit 15  bit 14  bit 13 bit 12  bit 11  bit 10  bit 9   bit 8
338c2ecf20Sopenharmony_ci * +-------+-------+-------+-------+-------+-------+-------+-------+
348c2ecf20Sopenharmony_ci * |  TDI  |  TDO  |  TCK  |  RDY# |  #RD  |  #WR  | AD_RG |  #CS  |
358c2ecf20Sopenharmony_ci * +-------+-------+-------+-------+-------+-------+-------+-------+
368c2ecf20Sopenharmony_ci *  bit 7   bit 6   bit 5   bit 4   bit 3   bit 2   bit 1   bit 0
378c2ecf20Sopenharmony_ci * +-------+-------+-------+-------+-------+-------+-------+-------+
388c2ecf20Sopenharmony_ci * |  DATA7|  DATA6|  DATA5|  DATA4|  DATA3|  DATA2|  DATA1|  DATA0|
398c2ecf20Sopenharmony_ci * +-------+-------+-------+-------+-------+-------+-------+-------+
408c2ecf20Sopenharmony_ci */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#include <media/dvb_demux.h>
458c2ecf20Sopenharmony_ci#include <media/dvb_frontend.h>
468c2ecf20Sopenharmony_ci#include "altera-ci.h"
478c2ecf20Sopenharmony_ci#include <media/dvb_ca_en50221.h>
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* FPGA regs */
508c2ecf20Sopenharmony_ci#define NETUP_CI_INT_CTRL	0x00
518c2ecf20Sopenharmony_ci#define NETUP_CI_BUSCTRL2	0x01
528c2ecf20Sopenharmony_ci#define NETUP_CI_ADDR0		0x04
538c2ecf20Sopenharmony_ci#define NETUP_CI_ADDR1		0x05
548c2ecf20Sopenharmony_ci#define NETUP_CI_DATA		0x06
558c2ecf20Sopenharmony_ci#define NETUP_CI_BUSCTRL	0x07
568c2ecf20Sopenharmony_ci#define NETUP_CI_PID_ADDR0	0x08
578c2ecf20Sopenharmony_ci#define NETUP_CI_PID_ADDR1	0x09
588c2ecf20Sopenharmony_ci#define NETUP_CI_PID_DATA	0x0a
598c2ecf20Sopenharmony_ci#define NETUP_CI_TSA_DIV	0x0c
608c2ecf20Sopenharmony_ci#define NETUP_CI_TSB_DIV	0x0d
618c2ecf20Sopenharmony_ci#define NETUP_CI_REVISION	0x0f
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* const for ci op */
648c2ecf20Sopenharmony_ci#define NETUP_CI_FLG_CTL	1
658c2ecf20Sopenharmony_ci#define NETUP_CI_FLG_RD		1
668c2ecf20Sopenharmony_ci#define NETUP_CI_FLG_AD		1
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic unsigned int ci_dbg;
698c2ecf20Sopenharmony_cimodule_param(ci_dbg, int, 0644);
708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ci_dbg, "Enable CI debugging");
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic unsigned int pid_dbg;
738c2ecf20Sopenharmony_cimodule_param(pid_dbg, int, 0644);
748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pid_dbg, "Enable PID filtering debugging");
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("altera FPGA CI module");
778c2ecf20Sopenharmony_ciMODULE_AUTHOR("Igor M. Liplianin  <liplianin@netup.ru>");
788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define ci_dbg_print(fmt, args...) \
818c2ecf20Sopenharmony_ci	do { \
828c2ecf20Sopenharmony_ci		if (ci_dbg) \
838c2ecf20Sopenharmony_ci			printk(KERN_DEBUG pr_fmt("%s: " fmt), \
848c2ecf20Sopenharmony_ci			       __func__, ##args); \
858c2ecf20Sopenharmony_ci	} while (0)
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#define pid_dbg_print(fmt, args...) \
888c2ecf20Sopenharmony_ci	do { \
898c2ecf20Sopenharmony_ci		if (pid_dbg) \
908c2ecf20Sopenharmony_ci			printk(KERN_DEBUG pr_fmt("%s: " fmt), \
918c2ecf20Sopenharmony_ci			       __func__, ##args); \
928c2ecf20Sopenharmony_ci	} while (0)
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistruct altera_ci_state;
958c2ecf20Sopenharmony_cistruct netup_hw_pid_filter;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistruct fpga_internal {
988c2ecf20Sopenharmony_ci	void *dev;
998c2ecf20Sopenharmony_ci	struct mutex fpga_mutex;/* two CI's on the same fpga */
1008c2ecf20Sopenharmony_ci	struct netup_hw_pid_filter *pid_filt[2];
1018c2ecf20Sopenharmony_ci	struct altera_ci_state *state[2];
1028c2ecf20Sopenharmony_ci	struct work_struct work;
1038c2ecf20Sopenharmony_ci	int (*fpga_rw) (void *dev, int flag, int data, int rw);
1048c2ecf20Sopenharmony_ci	int cis_used;
1058c2ecf20Sopenharmony_ci	int filts_used;
1068c2ecf20Sopenharmony_ci	int strt_wrk;
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/* stores all private variables for communication with CI */
1108c2ecf20Sopenharmony_cistruct altera_ci_state {
1118c2ecf20Sopenharmony_ci	struct fpga_internal *internal;
1128c2ecf20Sopenharmony_ci	struct dvb_ca_en50221 ca;
1138c2ecf20Sopenharmony_ci	int status;
1148c2ecf20Sopenharmony_ci	int nr;
1158c2ecf20Sopenharmony_ci};
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/* stores all private variables for hardware pid filtering */
1188c2ecf20Sopenharmony_cistruct netup_hw_pid_filter {
1198c2ecf20Sopenharmony_ci	struct fpga_internal *internal;
1208c2ecf20Sopenharmony_ci	struct dvb_demux *demux;
1218c2ecf20Sopenharmony_ci	/* save old functions */
1228c2ecf20Sopenharmony_ci	int (*start_feed)(struct dvb_demux_feed *feed);
1238c2ecf20Sopenharmony_ci	int (*stop_feed)(struct dvb_demux_feed *feed);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	int status;
1268c2ecf20Sopenharmony_ci	int nr;
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/* internal params node */
1308c2ecf20Sopenharmony_cistruct fpga_inode {
1318c2ecf20Sopenharmony_ci	/* pointer for internal params, one for each pair of CI's */
1328c2ecf20Sopenharmony_ci	struct fpga_internal		*internal;
1338c2ecf20Sopenharmony_ci	struct fpga_inode		*next_inode;
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/* first internal params */
1378c2ecf20Sopenharmony_cistatic struct fpga_inode *fpga_first_inode;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/* find chip by dev */
1408c2ecf20Sopenharmony_cistatic struct fpga_inode *find_inode(void *dev)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct fpga_inode *temp_chip = fpga_first_inode;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (temp_chip == NULL)
1458c2ecf20Sopenharmony_ci		return temp_chip;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/*
1488c2ecf20Sopenharmony_ci	 Search for the last fpga CI chip or
1498c2ecf20Sopenharmony_ci	 find it by dev */
1508c2ecf20Sopenharmony_ci	while ((temp_chip != NULL) &&
1518c2ecf20Sopenharmony_ci				(temp_chip->internal->dev != dev))
1528c2ecf20Sopenharmony_ci		temp_chip = temp_chip->next_inode;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return temp_chip;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci/* check demux */
1578c2ecf20Sopenharmony_cistatic struct fpga_internal *check_filter(struct fpga_internal *temp_int,
1588c2ecf20Sopenharmony_ci						void *demux_dev, int filt_nr)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	if (temp_int == NULL)
1618c2ecf20Sopenharmony_ci		return NULL;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if ((temp_int->pid_filt[filt_nr]) == NULL)
1648c2ecf20Sopenharmony_ci		return NULL;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (temp_int->pid_filt[filt_nr]->demux == demux_dev)
1678c2ecf20Sopenharmony_ci		return temp_int;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return NULL;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/* find chip by demux */
1738c2ecf20Sopenharmony_cistatic struct fpga_inode *find_dinode(void *demux_dev)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct fpga_inode *temp_chip = fpga_first_inode;
1768c2ecf20Sopenharmony_ci	struct fpga_internal *temp_int;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/*
1798c2ecf20Sopenharmony_ci	 * Search of the last fpga CI chip or
1808c2ecf20Sopenharmony_ci	 * find it by demux
1818c2ecf20Sopenharmony_ci	 */
1828c2ecf20Sopenharmony_ci	while (temp_chip != NULL) {
1838c2ecf20Sopenharmony_ci		if (temp_chip->internal != NULL) {
1848c2ecf20Sopenharmony_ci			temp_int = temp_chip->internal;
1858c2ecf20Sopenharmony_ci			if (check_filter(temp_int, demux_dev, 0))
1868c2ecf20Sopenharmony_ci				break;
1878c2ecf20Sopenharmony_ci			if (check_filter(temp_int, demux_dev, 1))
1888c2ecf20Sopenharmony_ci				break;
1898c2ecf20Sopenharmony_ci		}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci		temp_chip = temp_chip->next_inode;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return temp_chip;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/* deallocating chip */
1988c2ecf20Sopenharmony_cistatic void remove_inode(struct fpga_internal *internal)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct fpga_inode *prev_node = fpga_first_inode;
2018c2ecf20Sopenharmony_ci	struct fpga_inode *del_node = find_inode(internal->dev);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (del_node != NULL) {
2048c2ecf20Sopenharmony_ci		if (del_node == fpga_first_inode) {
2058c2ecf20Sopenharmony_ci			fpga_first_inode = del_node->next_inode;
2068c2ecf20Sopenharmony_ci		} else {
2078c2ecf20Sopenharmony_ci			while (prev_node->next_inode != del_node)
2088c2ecf20Sopenharmony_ci				prev_node = prev_node->next_inode;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci			if (del_node->next_inode == NULL)
2118c2ecf20Sopenharmony_ci				prev_node->next_inode = NULL;
2128c2ecf20Sopenharmony_ci			else
2138c2ecf20Sopenharmony_ci				prev_node->next_inode =
2148c2ecf20Sopenharmony_ci					prev_node->next_inode->next_inode;
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		kfree(del_node);
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci/* allocating new chip */
2228c2ecf20Sopenharmony_cistatic struct fpga_inode *append_internal(struct fpga_internal *internal)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct fpga_inode *new_node = fpga_first_inode;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (new_node == NULL) {
2278c2ecf20Sopenharmony_ci		new_node = kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
2288c2ecf20Sopenharmony_ci		fpga_first_inode = new_node;
2298c2ecf20Sopenharmony_ci	} else {
2308c2ecf20Sopenharmony_ci		while (new_node->next_inode != NULL)
2318c2ecf20Sopenharmony_ci			new_node = new_node->next_inode;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci		new_node->next_inode =
2348c2ecf20Sopenharmony_ci				kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
2358c2ecf20Sopenharmony_ci		if (new_node->next_inode != NULL)
2368c2ecf20Sopenharmony_ci			new_node = new_node->next_inode;
2378c2ecf20Sopenharmony_ci		else
2388c2ecf20Sopenharmony_ci			new_node = NULL;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (new_node != NULL) {
2428c2ecf20Sopenharmony_ci		new_node->internal = internal;
2438c2ecf20Sopenharmony_ci		new_node->next_inode = NULL;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return new_node;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int netup_fpga_op_rw(struct fpga_internal *inter, int addr,
2508c2ecf20Sopenharmony_ci							u8 val, u8 read)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	inter->fpga_rw(inter->dev, NETUP_CI_FLG_AD, addr, 0);
2538c2ecf20Sopenharmony_ci	return inter->fpga_rw(inter->dev, 0, val, read);
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci/* flag - mem/io, read - read/write */
2578c2ecf20Sopenharmony_cistatic int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
2588c2ecf20Sopenharmony_ci				u8 flag, u8 read, int addr, u8 val)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	struct altera_ci_state *state = en50221->data;
2628c2ecf20Sopenharmony_ci	struct fpga_internal *inter = state->internal;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	u8 store;
2658c2ecf20Sopenharmony_ci	int mem = 0;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (0 != slot)
2688c2ecf20Sopenharmony_ci		return -EINVAL;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_ADDR0, ((addr << 1) & 0xfe), 0);
2738c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_ADDR1, ((addr >> 7) & 0x7f), 0);
2748c2ecf20Sopenharmony_ci	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	store &= 0x0f;
2778c2ecf20Sopenharmony_ci	store |= ((state->nr << 7) | (flag << 6));
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, store, 0);
2808c2ecf20Sopenharmony_ci	mem = netup_fpga_op_rw(inter, NETUP_CI_DATA, val, read);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__,
2858c2ecf20Sopenharmony_ci			(read) ? "read" : "write", addr,
2868c2ecf20Sopenharmony_ci			(flag == NETUP_CI_FLG_CTL) ? "ctl" : "mem",
2878c2ecf20Sopenharmony_ci			(read) ? mem : val);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	return mem;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
2938c2ecf20Sopenharmony_ci					int slot, int addr)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	return altera_ci_op_cam(en50221, slot, 0, NETUP_CI_FLG_RD, addr, 0);
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
2998c2ecf20Sopenharmony_ci					 int slot, int addr, u8 data)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	return altera_ci_op_cam(en50221, slot, 0, 0, addr, data);
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221,
3058c2ecf20Sopenharmony_ci				  int slot, u8 addr)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL,
3088c2ecf20Sopenharmony_ci						NETUP_CI_FLG_RD, addr, 0);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
3128c2ecf20Sopenharmony_ci				   u8 addr, u8 data)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, 0, addr, data);
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	struct altera_ci_state *state = en50221->data;
3208c2ecf20Sopenharmony_ci	struct fpga_internal *inter = state->internal;
3218c2ecf20Sopenharmony_ci	/* reasonable timeout for CI reset is 10 seconds */
3228c2ecf20Sopenharmony_ci	unsigned long t_out = jiffies + msecs_to_jiffies(9999);
3238c2ecf20Sopenharmony_ci	int ret;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (0 != slot)
3288c2ecf20Sopenharmony_ci		return -EINVAL;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
3338c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
3348c2ecf20Sopenharmony_ci				(ret & 0xcf) | (1 << (5 - state->nr)), 0);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	for (;;) {
3398c2ecf20Sopenharmony_ci		msleep(50);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		mutex_lock(&inter->fpga_mutex);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
3448c2ecf20Sopenharmony_ci						0, NETUP_CI_FLG_RD);
3458c2ecf20Sopenharmony_ci		mutex_unlock(&inter->fpga_mutex);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		if ((ret & (1 << (5 - state->nr))) == 0)
3488c2ecf20Sopenharmony_ci			break;
3498c2ecf20Sopenharmony_ci		if (time_after(jiffies, t_out))
3508c2ecf20Sopenharmony_ci			break;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	ci_dbg_print("%s: %d msecs\n", __func__,
3558c2ecf20Sopenharmony_ci		jiffies_to_msecs(jiffies + msecs_to_jiffies(9999) - t_out));
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return 0;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	/* not implemented */
3638c2ecf20Sopenharmony_ci	return 0;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	struct altera_ci_state *state = en50221->data;
3698c2ecf20Sopenharmony_ci	struct fpga_internal *inter = state->internal;
3708c2ecf20Sopenharmony_ci	int ret;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	if (0 != slot)
3758c2ecf20Sopenharmony_ci		return -EINVAL;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
3808c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
3818c2ecf20Sopenharmony_ci				(ret & 0x0f) | (1 << (3 - state->nr)), 0);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return 0;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci/* work handler */
3898c2ecf20Sopenharmony_cistatic void netup_read_ci_status(struct work_struct *work)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct fpga_internal *inter =
3928c2ecf20Sopenharmony_ci			container_of(work, struct fpga_internal, work);
3938c2ecf20Sopenharmony_ci	int ret;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
3988c2ecf20Sopenharmony_ci	/* ack' irq */
3998c2ecf20Sopenharmony_ci	ret = netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0, NETUP_CI_FLG_RD);
4008c2ecf20Sopenharmony_ci	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (inter->state[1] != NULL) {
4058c2ecf20Sopenharmony_ci		inter->state[1]->status =
4068c2ecf20Sopenharmony_ci				((ret & 1) == 0 ?
4078c2ecf20Sopenharmony_ci				DVB_CA_EN50221_POLL_CAM_PRESENT |
4088c2ecf20Sopenharmony_ci				DVB_CA_EN50221_POLL_CAM_READY : 0);
4098c2ecf20Sopenharmony_ci		ci_dbg_print("%s: setting CI[1] status = 0x%x\n",
4108c2ecf20Sopenharmony_ci				__func__, inter->state[1]->status);
4118c2ecf20Sopenharmony_ci	}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	if (inter->state[0] != NULL) {
4148c2ecf20Sopenharmony_ci		inter->state[0]->status =
4158c2ecf20Sopenharmony_ci				((ret & 2) == 0 ?
4168c2ecf20Sopenharmony_ci				DVB_CA_EN50221_POLL_CAM_PRESENT |
4178c2ecf20Sopenharmony_ci				DVB_CA_EN50221_POLL_CAM_READY : 0);
4188c2ecf20Sopenharmony_ci		ci_dbg_print("%s: setting CI[0] status = 0x%x\n",
4198c2ecf20Sopenharmony_ci				__func__, inter->state[0]->status);
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci/* CI irq handler */
4248c2ecf20Sopenharmony_ciint altera_ci_irq(void *dev)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	struct fpga_inode *temp_int = NULL;
4278c2ecf20Sopenharmony_ci	struct fpga_internal *inter = NULL;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (dev != NULL) {
4328c2ecf20Sopenharmony_ci		temp_int = find_inode(dev);
4338c2ecf20Sopenharmony_ci		if (temp_int != NULL) {
4348c2ecf20Sopenharmony_ci			inter = temp_int->internal;
4358c2ecf20Sopenharmony_ci			schedule_work(&inter->work);
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	return 1;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(altera_ci_irq);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
4448c2ecf20Sopenharmony_ci				      int slot, int open)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct altera_ci_state *state = en50221->data;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (0 != slot)
4498c2ecf20Sopenharmony_ci		return -EINVAL;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	return state->status;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistatic void altera_hw_filt_release(void *main_dev, int filt_nr)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct fpga_inode *temp_int = find_inode(main_dev);
4578c2ecf20Sopenharmony_ci	struct netup_hw_pid_filter *pid_filt = NULL;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (temp_int != NULL) {
4628c2ecf20Sopenharmony_ci		pid_filt = temp_int->internal->pid_filt[filt_nr - 1];
4638c2ecf20Sopenharmony_ci		/* stored old feed controls */
4648c2ecf20Sopenharmony_ci		pid_filt->demux->start_feed = pid_filt->start_feed;
4658c2ecf20Sopenharmony_ci		pid_filt->demux->stop_feed = pid_filt->stop_feed;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci		if (((--(temp_int->internal->filts_used)) <= 0) &&
4688c2ecf20Sopenharmony_ci			 ((temp_int->internal->cis_used) <= 0)) {
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci			ci_dbg_print("%s: Actually removing\n", __func__);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci			remove_inode(temp_int->internal);
4738c2ecf20Sopenharmony_ci			kfree(pid_filt->internal);
4748c2ecf20Sopenharmony_ci		}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		kfree(pid_filt);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_civoid altera_ci_release(void *dev, int ci_nr)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct fpga_inode *temp_int = find_inode(dev);
4858c2ecf20Sopenharmony_ci	struct altera_ci_state *state = NULL;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (temp_int != NULL) {
4908c2ecf20Sopenharmony_ci		state = temp_int->internal->state[ci_nr - 1];
4918c2ecf20Sopenharmony_ci		altera_hw_filt_release(dev, ci_nr);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		if (((temp_int->internal->filts_used) <= 0) &&
4958c2ecf20Sopenharmony_ci				((--(temp_int->internal->cis_used)) <= 0)) {
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci			ci_dbg_print("%s: Actually removing\n", __func__);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci			remove_inode(temp_int->internal);
5008c2ecf20Sopenharmony_ci			kfree(state->internal);
5018c2ecf20Sopenharmony_ci		}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci		if (state != NULL) {
5048c2ecf20Sopenharmony_ci			if (state->ca.data != NULL)
5058c2ecf20Sopenharmony_ci				dvb_ca_en50221_release(&state->ca);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci			kfree(state);
5088c2ecf20Sopenharmony_ci		}
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(altera_ci_release);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_cistatic void altera_pid_control(struct netup_hw_pid_filter *pid_filt,
5158c2ecf20Sopenharmony_ci		u16 pid, int onoff)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	struct fpga_internal *inter = pid_filt->internal;
5188c2ecf20Sopenharmony_ci	u8 store = 0;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	/* pid 0-0x1f always enabled, don't touch them */
5218c2ecf20Sopenharmony_ci	if ((pid == 0x2000) || (pid < 0x20))
5228c2ecf20Sopenharmony_ci		return;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, (pid >> 3) & 0xff, 0);
5278c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
5288c2ecf20Sopenharmony_ci			((pid >> 11) & 0x03) | (pid_filt->nr << 2), 0);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	store = netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, 0, NETUP_CI_FLG_RD);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	if (onoff)/* 0 - on, 1 - off */
5338c2ecf20Sopenharmony_ci		store |= (1 << (pid & 7));
5348c2ecf20Sopenharmony_ci	else
5358c2ecf20Sopenharmony_ci		store &= ~(1 << (pid & 7));
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	pid_dbg_print("%s: (%d) set pid: %5d 0x%04x '%s'\n", __func__,
5428c2ecf20Sopenharmony_ci		pid_filt->nr, pid, pid, onoff ? "off" : "on");
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt,
5468c2ecf20Sopenharmony_ci					int filt_nr, int onoff)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	struct fpga_internal *inter = pid_filt->internal;
5498c2ecf20Sopenharmony_ci	u8 store = 0;
5508c2ecf20Sopenharmony_ci	int i;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	pid_dbg_print("%s: pid_filt->nr[%d]  now %s\n", __func__, pid_filt->nr,
5538c2ecf20Sopenharmony_ci			onoff ? "off" : "on");
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	if (onoff)/* 0 - on, 1 - off */
5568c2ecf20Sopenharmony_ci		store = 0xff;/* ignore pid */
5578c2ecf20Sopenharmony_ci	else
5588c2ecf20Sopenharmony_ci		store = 0;/* enable pid */
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	for (i = 0; i < 1024; i++) {
5638c2ecf20Sopenharmony_ci		netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, i & 0xff, 0);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci		netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
5668c2ecf20Sopenharmony_ci				((i >> 8) & 0x03) | (pid_filt->nr << 2), 0);
5678c2ecf20Sopenharmony_ci		/* pid 0-0x1f always enabled */
5688c2ecf20Sopenharmony_ci		netup_fpga_op_rw(inter, NETUP_CI_PID_DATA,
5698c2ecf20Sopenharmony_ci				(i > 3 ? store : 0), 0);
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_cistatic int altera_pid_feed_control(void *demux_dev, int filt_nr,
5768c2ecf20Sopenharmony_ci		struct dvb_demux_feed *feed, int onoff)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	struct fpga_inode *temp_int = find_dinode(demux_dev);
5798c2ecf20Sopenharmony_ci	struct fpga_internal *inter = temp_int->internal;
5808c2ecf20Sopenharmony_ci	struct netup_hw_pid_filter *pid_filt = inter->pid_filt[filt_nr - 1];
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	altera_pid_control(pid_filt, feed->pid, onoff ? 0 : 1);
5838c2ecf20Sopenharmony_ci	/* call old feed proc's */
5848c2ecf20Sopenharmony_ci	if (onoff)
5858c2ecf20Sopenharmony_ci		pid_filt->start_feed(feed);
5868c2ecf20Sopenharmony_ci	else
5878c2ecf20Sopenharmony_ci		pid_filt->stop_feed(feed);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	if (feed->pid == 0x2000)
5908c2ecf20Sopenharmony_ci		altera_toggle_fullts_streaming(pid_filt, filt_nr,
5918c2ecf20Sopenharmony_ci						onoff ? 0 : 1);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	return 0;
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic int altera_ci_start_feed(struct dvb_demux_feed *feed, int num)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	altera_pid_feed_control(feed->demux, num, feed, 1);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	return 0;
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	altera_pid_feed_control(feed->demux, num, feed, 0);
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	return 0;
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_cistatic int altera_ci_start_feed_1(struct dvb_demux_feed *feed)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	return altera_ci_start_feed(feed, 1);
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic int altera_ci_stop_feed_1(struct dvb_demux_feed *feed)
6168c2ecf20Sopenharmony_ci{
6178c2ecf20Sopenharmony_ci	return altera_ci_stop_feed(feed, 1);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic int altera_ci_start_feed_2(struct dvb_demux_feed *feed)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	return altera_ci_start_feed(feed, 2);
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic int altera_ci_stop_feed_2(struct dvb_demux_feed *feed)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	return altera_ci_stop_feed(feed, 2);
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cistatic int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	struct netup_hw_pid_filter *pid_filt = NULL;
6338c2ecf20Sopenharmony_ci	struct fpga_inode *temp_int = find_inode(config->dev);
6348c2ecf20Sopenharmony_ci	struct fpga_internal *inter = NULL;
6358c2ecf20Sopenharmony_ci	int ret = 0;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	pid_filt = kzalloc(sizeof(struct netup_hw_pid_filter), GFP_KERNEL);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	if (!pid_filt) {
6428c2ecf20Sopenharmony_ci		ret = -ENOMEM;
6438c2ecf20Sopenharmony_ci		goto err;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	if (temp_int != NULL) {
6478c2ecf20Sopenharmony_ci		inter = temp_int->internal;
6488c2ecf20Sopenharmony_ci		(inter->filts_used)++;
6498c2ecf20Sopenharmony_ci		ci_dbg_print("%s: Find Internal Structure!\n", __func__);
6508c2ecf20Sopenharmony_ci	} else {
6518c2ecf20Sopenharmony_ci		inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
6528c2ecf20Sopenharmony_ci		if (!inter) {
6538c2ecf20Sopenharmony_ci			ret = -ENOMEM;
6548c2ecf20Sopenharmony_ci			goto err;
6558c2ecf20Sopenharmony_ci		}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci		temp_int = append_internal(inter);
6588c2ecf20Sopenharmony_ci		if (!temp_int) {
6598c2ecf20Sopenharmony_ci			ret = -ENOMEM;
6608c2ecf20Sopenharmony_ci			goto err;
6618c2ecf20Sopenharmony_ci		}
6628c2ecf20Sopenharmony_ci		inter->filts_used = 1;
6638c2ecf20Sopenharmony_ci		inter->dev = config->dev;
6648c2ecf20Sopenharmony_ci		inter->fpga_rw = config->fpga_rw;
6658c2ecf20Sopenharmony_ci		mutex_init(&inter->fpga_mutex);
6668c2ecf20Sopenharmony_ci		inter->strt_wrk = 1;
6678c2ecf20Sopenharmony_ci		ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	ci_dbg_print("%s: setting hw pid filter = %p for ci = %d\n", __func__,
6718c2ecf20Sopenharmony_ci						pid_filt, hw_filt_nr - 1);
6728c2ecf20Sopenharmony_ci	inter->pid_filt[hw_filt_nr - 1] = pid_filt;
6738c2ecf20Sopenharmony_ci	pid_filt->demux = config->demux;
6748c2ecf20Sopenharmony_ci	pid_filt->internal = inter;
6758c2ecf20Sopenharmony_ci	pid_filt->nr = hw_filt_nr - 1;
6768c2ecf20Sopenharmony_ci	/* store old feed controls */
6778c2ecf20Sopenharmony_ci	pid_filt->start_feed = config->demux->start_feed;
6788c2ecf20Sopenharmony_ci	pid_filt->stop_feed = config->demux->stop_feed;
6798c2ecf20Sopenharmony_ci	/* replace with new feed controls */
6808c2ecf20Sopenharmony_ci	if (hw_filt_nr == 1) {
6818c2ecf20Sopenharmony_ci		pid_filt->demux->start_feed = altera_ci_start_feed_1;
6828c2ecf20Sopenharmony_ci		pid_filt->demux->stop_feed = altera_ci_stop_feed_1;
6838c2ecf20Sopenharmony_ci	} else if (hw_filt_nr == 2) {
6848c2ecf20Sopenharmony_ci		pid_filt->demux->start_feed = altera_ci_start_feed_2;
6858c2ecf20Sopenharmony_ci		pid_filt->demux->stop_feed = altera_ci_stop_feed_2;
6868c2ecf20Sopenharmony_ci	}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	altera_toggle_fullts_streaming(pid_filt, 0, 1);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	return 0;
6918c2ecf20Sopenharmony_cierr:
6928c2ecf20Sopenharmony_ci	ci_dbg_print("%s: Can't init hardware filter: Error %d\n",
6938c2ecf20Sopenharmony_ci		     __func__, ret);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	kfree(pid_filt);
6968c2ecf20Sopenharmony_ci	kfree(inter);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	return ret;
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ciint altera_ci_init(struct altera_ci_config *config, int ci_nr)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	struct altera_ci_state *state;
7048c2ecf20Sopenharmony_ci	struct fpga_inode *temp_int = find_inode(config->dev);
7058c2ecf20Sopenharmony_ci	struct fpga_internal *inter = NULL;
7068c2ecf20Sopenharmony_ci	int ret = 0;
7078c2ecf20Sopenharmony_ci	u8 store = 0;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(struct altera_ci_state), GFP_KERNEL);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (!state) {
7148c2ecf20Sopenharmony_ci		ret = -ENOMEM;
7158c2ecf20Sopenharmony_ci		goto err;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (temp_int != NULL) {
7198c2ecf20Sopenharmony_ci		inter = temp_int->internal;
7208c2ecf20Sopenharmony_ci		(inter->cis_used)++;
7218c2ecf20Sopenharmony_ci		inter->fpga_rw = config->fpga_rw;
7228c2ecf20Sopenharmony_ci		ci_dbg_print("%s: Find Internal Structure!\n", __func__);
7238c2ecf20Sopenharmony_ci	} else {
7248c2ecf20Sopenharmony_ci		inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
7258c2ecf20Sopenharmony_ci		if (!inter) {
7268c2ecf20Sopenharmony_ci			ret = -ENOMEM;
7278c2ecf20Sopenharmony_ci			goto err;
7288c2ecf20Sopenharmony_ci		}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci		temp_int = append_internal(inter);
7318c2ecf20Sopenharmony_ci		if (!temp_int) {
7328c2ecf20Sopenharmony_ci			ret = -ENOMEM;
7338c2ecf20Sopenharmony_ci			goto err;
7348c2ecf20Sopenharmony_ci		}
7358c2ecf20Sopenharmony_ci		inter->cis_used = 1;
7368c2ecf20Sopenharmony_ci		inter->dev = config->dev;
7378c2ecf20Sopenharmony_ci		inter->fpga_rw = config->fpga_rw;
7388c2ecf20Sopenharmony_ci		mutex_init(&inter->fpga_mutex);
7398c2ecf20Sopenharmony_ci		inter->strt_wrk = 1;
7408c2ecf20Sopenharmony_ci		ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	ci_dbg_print("%s: setting state = %p for ci = %d\n", __func__,
7448c2ecf20Sopenharmony_ci						state, ci_nr - 1);
7458c2ecf20Sopenharmony_ci	state->internal = inter;
7468c2ecf20Sopenharmony_ci	state->nr = ci_nr - 1;
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	state->ca.owner = THIS_MODULE;
7498c2ecf20Sopenharmony_ci	state->ca.read_attribute_mem = altera_ci_read_attribute_mem;
7508c2ecf20Sopenharmony_ci	state->ca.write_attribute_mem = altera_ci_write_attribute_mem;
7518c2ecf20Sopenharmony_ci	state->ca.read_cam_control = altera_ci_read_cam_ctl;
7528c2ecf20Sopenharmony_ci	state->ca.write_cam_control = altera_ci_write_cam_ctl;
7538c2ecf20Sopenharmony_ci	state->ca.slot_reset = altera_ci_slot_reset;
7548c2ecf20Sopenharmony_ci	state->ca.slot_shutdown = altera_ci_slot_shutdown;
7558c2ecf20Sopenharmony_ci	state->ca.slot_ts_enable = altera_ci_slot_ts_ctl;
7568c2ecf20Sopenharmony_ci	state->ca.poll_slot_status = altera_poll_ci_slot_status;
7578c2ecf20Sopenharmony_ci	state->ca.data = state;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	ret = dvb_ca_en50221_init(config->adapter,
7608c2ecf20Sopenharmony_ci				   &state->ca,
7618c2ecf20Sopenharmony_ci				   /* flags */ 0,
7628c2ecf20Sopenharmony_ci				   /* n_slots */ 1);
7638c2ecf20Sopenharmony_ci	if (0 != ret)
7648c2ecf20Sopenharmony_ci		goto err;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	inter->state[ci_nr - 1] = state;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	altera_hw_filt_init(config, ci_nr);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	if (inter->strt_wrk) {
7718c2ecf20Sopenharmony_ci		INIT_WORK(&inter->work, netup_read_ci_status);
7728c2ecf20Sopenharmony_ci		inter->strt_wrk = 0;
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	ci_dbg_print("%s: CI initialized!\n", __func__);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	/* Enable div */
7808c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_TSA_DIV, 0x0, 0);
7818c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_TSB_DIV, 0x0, 0);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	/* enable TS out */
7848c2ecf20Sopenharmony_ci	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
7858c2ecf20Sopenharmony_ci	store |= (3 << 4);
7868c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	ret = netup_fpga_op_rw(inter, NETUP_CI_REVISION, 0, NETUP_CI_FLG_RD);
7898c2ecf20Sopenharmony_ci	/* enable irq */
7908c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0x44, 0);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	ci_dbg_print("%s: NetUP CI Revision = 0x%x\n", __func__, ret);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	schedule_work(&inter->work);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	return 0;
7998c2ecf20Sopenharmony_cierr:
8008c2ecf20Sopenharmony_ci	ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	kfree(state);
8038c2ecf20Sopenharmony_ci	kfree(inter);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	return ret;
8068c2ecf20Sopenharmony_ci}
8078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(altera_ci_init);
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ciint altera_ci_tuner_reset(void *dev, int ci_nr)
8108c2ecf20Sopenharmony_ci{
8118c2ecf20Sopenharmony_ci	struct fpga_inode *temp_int = find_inode(dev);
8128c2ecf20Sopenharmony_ci	struct fpga_internal *inter = NULL;
8138c2ecf20Sopenharmony_ci	u8 store;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	if (temp_int == NULL)
8188c2ecf20Sopenharmony_ci		return -1;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	if (temp_int->internal == NULL)
8218c2ecf20Sopenharmony_ci		return -1;
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	inter = temp_int->internal;
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
8288c2ecf20Sopenharmony_ci	store &= ~(4 << (2 - ci_nr));
8298c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
8308c2ecf20Sopenharmony_ci	msleep(100);
8318c2ecf20Sopenharmony_ci	store |= (4 << (2 - ci_nr));
8328c2ecf20Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	return 0;
8378c2ecf20Sopenharmony_ci}
8388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(altera_ci_tuner_reset);
839