162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * altera-ci.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  CI driver in conjunction with NetUp Dual DVB-T/C RF CI card
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2010,2011 NetUP Inc.
862306a36Sopenharmony_ci * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@netup.ru>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * currently cx23885 GPIO's used.
1362306a36Sopenharmony_ci * GPIO-0 ~INT in
1462306a36Sopenharmony_ci * GPIO-1 TMS out
1562306a36Sopenharmony_ci * GPIO-2 ~reset chips out
1662306a36Sopenharmony_ci * GPIO-3 to GPIO-10 data/addr for CA in/out
1762306a36Sopenharmony_ci * GPIO-11 ~CS out
1862306a36Sopenharmony_ci * GPIO-12 AD_RG out
1962306a36Sopenharmony_ci * GPIO-13 ~WR out
2062306a36Sopenharmony_ci * GPIO-14 ~RD out
2162306a36Sopenharmony_ci * GPIO-15 ~RDY in
2262306a36Sopenharmony_ci * GPIO-16 TCK out
2362306a36Sopenharmony_ci * GPIO-17 TDO in
2462306a36Sopenharmony_ci * GPIO-18 TDI out
2562306a36Sopenharmony_ci */
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci *  Bit definitions for MC417_RWD and MC417_OEN registers
2862306a36Sopenharmony_ci * bits 31-16
2962306a36Sopenharmony_ci * +-----------+
3062306a36Sopenharmony_ci * | Reserved  |
3162306a36Sopenharmony_ci * +-----------+
3262306a36Sopenharmony_ci *   bit 15  bit 14  bit 13 bit 12  bit 11  bit 10  bit 9   bit 8
3362306a36Sopenharmony_ci * +-------+-------+-------+-------+-------+-------+-------+-------+
3462306a36Sopenharmony_ci * |  TDI  |  TDO  |  TCK  |  RDY# |  #RD  |  #WR  | AD_RG |  #CS  |
3562306a36Sopenharmony_ci * +-------+-------+-------+-------+-------+-------+-------+-------+
3662306a36Sopenharmony_ci *  bit 7   bit 6   bit 5   bit 4   bit 3   bit 2   bit 1   bit 0
3762306a36Sopenharmony_ci * +-------+-------+-------+-------+-------+-------+-------+-------+
3862306a36Sopenharmony_ci * |  DATA7|  DATA6|  DATA5|  DATA4|  DATA3|  DATA2|  DATA1|  DATA0|
3962306a36Sopenharmony_ci * +-------+-------+-------+-------+-------+-------+-------+-------+
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#include <media/dvb_demux.h>
4562306a36Sopenharmony_ci#include <media/dvb_frontend.h>
4662306a36Sopenharmony_ci#include "altera-ci.h"
4762306a36Sopenharmony_ci#include <media/dvb_ca_en50221.h>
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* FPGA regs */
5062306a36Sopenharmony_ci#define NETUP_CI_INT_CTRL	0x00
5162306a36Sopenharmony_ci#define NETUP_CI_BUSCTRL2	0x01
5262306a36Sopenharmony_ci#define NETUP_CI_ADDR0		0x04
5362306a36Sopenharmony_ci#define NETUP_CI_ADDR1		0x05
5462306a36Sopenharmony_ci#define NETUP_CI_DATA		0x06
5562306a36Sopenharmony_ci#define NETUP_CI_BUSCTRL	0x07
5662306a36Sopenharmony_ci#define NETUP_CI_PID_ADDR0	0x08
5762306a36Sopenharmony_ci#define NETUP_CI_PID_ADDR1	0x09
5862306a36Sopenharmony_ci#define NETUP_CI_PID_DATA	0x0a
5962306a36Sopenharmony_ci#define NETUP_CI_TSA_DIV	0x0c
6062306a36Sopenharmony_ci#define NETUP_CI_TSB_DIV	0x0d
6162306a36Sopenharmony_ci#define NETUP_CI_REVISION	0x0f
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* const for ci op */
6462306a36Sopenharmony_ci#define NETUP_CI_FLG_CTL	1
6562306a36Sopenharmony_ci#define NETUP_CI_FLG_RD		1
6662306a36Sopenharmony_ci#define NETUP_CI_FLG_AD		1
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic unsigned int ci_dbg;
6962306a36Sopenharmony_cimodule_param(ci_dbg, int, 0644);
7062306a36Sopenharmony_ciMODULE_PARM_DESC(ci_dbg, "Enable CI debugging");
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic unsigned int pid_dbg;
7362306a36Sopenharmony_cimodule_param(pid_dbg, int, 0644);
7462306a36Sopenharmony_ciMODULE_PARM_DESC(pid_dbg, "Enable PID filtering debugging");
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciMODULE_DESCRIPTION("altera FPGA CI module");
7762306a36Sopenharmony_ciMODULE_AUTHOR("Igor M. Liplianin  <liplianin@netup.ru>");
7862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci#define ci_dbg_print(fmt, args...) \
8162306a36Sopenharmony_ci	do { \
8262306a36Sopenharmony_ci		if (ci_dbg) \
8362306a36Sopenharmony_ci			printk(KERN_DEBUG pr_fmt("%s: " fmt), \
8462306a36Sopenharmony_ci			       __func__, ##args); \
8562306a36Sopenharmony_ci	} while (0)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define pid_dbg_print(fmt, args...) \
8862306a36Sopenharmony_ci	do { \
8962306a36Sopenharmony_ci		if (pid_dbg) \
9062306a36Sopenharmony_ci			printk(KERN_DEBUG pr_fmt("%s: " fmt), \
9162306a36Sopenharmony_ci			       __func__, ##args); \
9262306a36Sopenharmony_ci	} while (0)
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistruct altera_ci_state;
9562306a36Sopenharmony_cistruct netup_hw_pid_filter;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistruct fpga_internal {
9862306a36Sopenharmony_ci	void *dev;
9962306a36Sopenharmony_ci	struct mutex fpga_mutex;/* two CI's on the same fpga */
10062306a36Sopenharmony_ci	struct netup_hw_pid_filter *pid_filt[2];
10162306a36Sopenharmony_ci	struct altera_ci_state *state[2];
10262306a36Sopenharmony_ci	struct work_struct work;
10362306a36Sopenharmony_ci	int (*fpga_rw) (void *dev, int flag, int data, int rw);
10462306a36Sopenharmony_ci	int cis_used;
10562306a36Sopenharmony_ci	int filts_used;
10662306a36Sopenharmony_ci	int strt_wrk;
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/* stores all private variables for communication with CI */
11062306a36Sopenharmony_cistruct altera_ci_state {
11162306a36Sopenharmony_ci	struct fpga_internal *internal;
11262306a36Sopenharmony_ci	struct dvb_ca_en50221 ca;
11362306a36Sopenharmony_ci	int status;
11462306a36Sopenharmony_ci	int nr;
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/* stores all private variables for hardware pid filtering */
11862306a36Sopenharmony_cistruct netup_hw_pid_filter {
11962306a36Sopenharmony_ci	struct fpga_internal *internal;
12062306a36Sopenharmony_ci	struct dvb_demux *demux;
12162306a36Sopenharmony_ci	/* save old functions */
12262306a36Sopenharmony_ci	int (*start_feed)(struct dvb_demux_feed *feed);
12362306a36Sopenharmony_ci	int (*stop_feed)(struct dvb_demux_feed *feed);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	int status;
12662306a36Sopenharmony_ci	int nr;
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/* internal params node */
13062306a36Sopenharmony_cistruct fpga_inode {
13162306a36Sopenharmony_ci	/* pointer for internal params, one for each pair of CI's */
13262306a36Sopenharmony_ci	struct fpga_internal		*internal;
13362306a36Sopenharmony_ci	struct fpga_inode		*next_inode;
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/* first internal params */
13762306a36Sopenharmony_cistatic struct fpga_inode *fpga_first_inode;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* find chip by dev */
14062306a36Sopenharmony_cistatic struct fpga_inode *find_inode(void *dev)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct fpga_inode *temp_chip = fpga_first_inode;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (temp_chip == NULL)
14562306a36Sopenharmony_ci		return temp_chip;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/*
14862306a36Sopenharmony_ci	 Search for the last fpga CI chip or
14962306a36Sopenharmony_ci	 find it by dev */
15062306a36Sopenharmony_ci	while ((temp_chip != NULL) &&
15162306a36Sopenharmony_ci				(temp_chip->internal->dev != dev))
15262306a36Sopenharmony_ci		temp_chip = temp_chip->next_inode;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return temp_chip;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci/* check demux */
15762306a36Sopenharmony_cistatic struct fpga_internal *check_filter(struct fpga_internal *temp_int,
15862306a36Sopenharmony_ci						void *demux_dev, int filt_nr)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	if (temp_int == NULL)
16162306a36Sopenharmony_ci		return NULL;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if ((temp_int->pid_filt[filt_nr]) == NULL)
16462306a36Sopenharmony_ci		return NULL;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (temp_int->pid_filt[filt_nr]->demux == demux_dev)
16762306a36Sopenharmony_ci		return temp_int;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return NULL;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci/* find chip by demux */
17362306a36Sopenharmony_cistatic struct fpga_inode *find_dinode(void *demux_dev)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct fpga_inode *temp_chip = fpga_first_inode;
17662306a36Sopenharmony_ci	struct fpga_internal *temp_int;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/*
17962306a36Sopenharmony_ci	 * Search of the last fpga CI chip or
18062306a36Sopenharmony_ci	 * find it by demux
18162306a36Sopenharmony_ci	 */
18262306a36Sopenharmony_ci	while (temp_chip != NULL) {
18362306a36Sopenharmony_ci		if (temp_chip->internal != NULL) {
18462306a36Sopenharmony_ci			temp_int = temp_chip->internal;
18562306a36Sopenharmony_ci			if (check_filter(temp_int, demux_dev, 0))
18662306a36Sopenharmony_ci				break;
18762306a36Sopenharmony_ci			if (check_filter(temp_int, demux_dev, 1))
18862306a36Sopenharmony_ci				break;
18962306a36Sopenharmony_ci		}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		temp_chip = temp_chip->next_inode;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return temp_chip;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/* deallocating chip */
19862306a36Sopenharmony_cistatic void remove_inode(struct fpga_internal *internal)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct fpga_inode *prev_node = fpga_first_inode;
20162306a36Sopenharmony_ci	struct fpga_inode *del_node = find_inode(internal->dev);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (del_node != NULL) {
20462306a36Sopenharmony_ci		if (del_node == fpga_first_inode) {
20562306a36Sopenharmony_ci			fpga_first_inode = del_node->next_inode;
20662306a36Sopenharmony_ci		} else {
20762306a36Sopenharmony_ci			while (prev_node->next_inode != del_node)
20862306a36Sopenharmony_ci				prev_node = prev_node->next_inode;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci			if (del_node->next_inode == NULL)
21162306a36Sopenharmony_ci				prev_node->next_inode = NULL;
21262306a36Sopenharmony_ci			else
21362306a36Sopenharmony_ci				prev_node->next_inode =
21462306a36Sopenharmony_ci					prev_node->next_inode->next_inode;
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		kfree(del_node);
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/* allocating new chip */
22262306a36Sopenharmony_cistatic struct fpga_inode *append_internal(struct fpga_internal *internal)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct fpga_inode *new_node = fpga_first_inode;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (new_node == NULL) {
22762306a36Sopenharmony_ci		new_node = kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
22862306a36Sopenharmony_ci		fpga_first_inode = new_node;
22962306a36Sopenharmony_ci	} else {
23062306a36Sopenharmony_ci		while (new_node->next_inode != NULL)
23162306a36Sopenharmony_ci			new_node = new_node->next_inode;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		new_node->next_inode =
23462306a36Sopenharmony_ci				kmalloc(sizeof(struct fpga_inode), GFP_KERNEL);
23562306a36Sopenharmony_ci		if (new_node->next_inode != NULL)
23662306a36Sopenharmony_ci			new_node = new_node->next_inode;
23762306a36Sopenharmony_ci		else
23862306a36Sopenharmony_ci			new_node = NULL;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (new_node != NULL) {
24262306a36Sopenharmony_ci		new_node->internal = internal;
24362306a36Sopenharmony_ci		new_node->next_inode = NULL;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return new_node;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic int netup_fpga_op_rw(struct fpga_internal *inter, int addr,
25062306a36Sopenharmony_ci							u8 val, u8 read)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	inter->fpga_rw(inter->dev, NETUP_CI_FLG_AD, addr, 0);
25362306a36Sopenharmony_ci	return inter->fpga_rw(inter->dev, 0, val, read);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/* flag - mem/io, read - read/write */
25762306a36Sopenharmony_cistatic int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
25862306a36Sopenharmony_ci				u8 flag, u8 read, int addr, u8 val)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	struct altera_ci_state *state = en50221->data;
26262306a36Sopenharmony_ci	struct fpga_internal *inter = state->internal;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	u8 store;
26562306a36Sopenharmony_ci	int mem = 0;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (0 != slot)
26862306a36Sopenharmony_ci		return -EINVAL;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_ADDR0, ((addr << 1) & 0xfe), 0);
27362306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_ADDR1, ((addr >> 7) & 0x7f), 0);
27462306a36Sopenharmony_ci	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	store &= 0x0f;
27762306a36Sopenharmony_ci	store |= ((state->nr << 7) | (flag << 6));
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, store, 0);
28062306a36Sopenharmony_ci	mem = netup_fpga_op_rw(inter, NETUP_CI_DATA, val, read);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__,
28562306a36Sopenharmony_ci			(read) ? "read" : "write", addr,
28662306a36Sopenharmony_ci			(flag == NETUP_CI_FLG_CTL) ? "ctl" : "mem",
28762306a36Sopenharmony_ci			(read) ? mem : val);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return mem;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
29362306a36Sopenharmony_ci					int slot, int addr)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	return altera_ci_op_cam(en50221, slot, 0, NETUP_CI_FLG_RD, addr, 0);
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
29962306a36Sopenharmony_ci					 int slot, int addr, u8 data)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	return altera_ci_op_cam(en50221, slot, 0, 0, addr, data);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221,
30562306a36Sopenharmony_ci				  int slot, u8 addr)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL,
30862306a36Sopenharmony_ci						NETUP_CI_FLG_RD, addr, 0);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
31262306a36Sopenharmony_ci				   u8 addr, u8 data)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, 0, addr, data);
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct altera_ci_state *state = en50221->data;
32062306a36Sopenharmony_ci	struct fpga_internal *inter = state->internal;
32162306a36Sopenharmony_ci	/* reasonable timeout for CI reset is 10 seconds */
32262306a36Sopenharmony_ci	unsigned long t_out = jiffies + msecs_to_jiffies(9999);
32362306a36Sopenharmony_ci	int ret;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (0 != slot)
32862306a36Sopenharmony_ci		return -EINVAL;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
33362306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
33462306a36Sopenharmony_ci				(ret & 0xcf) | (1 << (5 - state->nr)), 0);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	for (;;) {
33962306a36Sopenharmony_ci		msleep(50);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		mutex_lock(&inter->fpga_mutex);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
34462306a36Sopenharmony_ci						0, NETUP_CI_FLG_RD);
34562306a36Sopenharmony_ci		mutex_unlock(&inter->fpga_mutex);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		if ((ret & (1 << (5 - state->nr))) == 0)
34862306a36Sopenharmony_ci			break;
34962306a36Sopenharmony_ci		if (time_after(jiffies, t_out))
35062306a36Sopenharmony_ci			break;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	ci_dbg_print("%s: %d msecs\n", __func__,
35562306a36Sopenharmony_ci		jiffies_to_msecs(jiffies + msecs_to_jiffies(9999) - t_out));
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return 0;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	/* not implemented */
36362306a36Sopenharmony_ci	return 0;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct altera_ci_state *state = en50221->data;
36962306a36Sopenharmony_ci	struct fpga_internal *inter = state->internal;
37062306a36Sopenharmony_ci	int ret;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (0 != slot)
37562306a36Sopenharmony_ci		return -EINVAL;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
38062306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL,
38162306a36Sopenharmony_ci				(ret & 0x0f) | (1 << (3 - state->nr)), 0);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return 0;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/* work handler */
38962306a36Sopenharmony_cistatic void netup_read_ci_status(struct work_struct *work)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct fpga_internal *inter =
39262306a36Sopenharmony_ci			container_of(work, struct fpga_internal, work);
39362306a36Sopenharmony_ci	int ret;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
39862306a36Sopenharmony_ci	/* ack' irq */
39962306a36Sopenharmony_ci	ret = netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0, NETUP_CI_FLG_RD);
40062306a36Sopenharmony_ci	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (inter->state[1] != NULL) {
40562306a36Sopenharmony_ci		inter->state[1]->status =
40662306a36Sopenharmony_ci				((ret & 1) == 0 ?
40762306a36Sopenharmony_ci				DVB_CA_EN50221_POLL_CAM_PRESENT |
40862306a36Sopenharmony_ci				DVB_CA_EN50221_POLL_CAM_READY : 0);
40962306a36Sopenharmony_ci		ci_dbg_print("%s: setting CI[1] status = 0x%x\n",
41062306a36Sopenharmony_ci				__func__, inter->state[1]->status);
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (inter->state[0] != NULL) {
41462306a36Sopenharmony_ci		inter->state[0]->status =
41562306a36Sopenharmony_ci				((ret & 2) == 0 ?
41662306a36Sopenharmony_ci				DVB_CA_EN50221_POLL_CAM_PRESENT |
41762306a36Sopenharmony_ci				DVB_CA_EN50221_POLL_CAM_READY : 0);
41862306a36Sopenharmony_ci		ci_dbg_print("%s: setting CI[0] status = 0x%x\n",
41962306a36Sopenharmony_ci				__func__, inter->state[0]->status);
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci/* CI irq handler */
42462306a36Sopenharmony_ciint altera_ci_irq(void *dev)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct fpga_inode *temp_int = NULL;
42762306a36Sopenharmony_ci	struct fpga_internal *inter = NULL;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (dev != NULL) {
43262306a36Sopenharmony_ci		temp_int = find_inode(dev);
43362306a36Sopenharmony_ci		if (temp_int != NULL) {
43462306a36Sopenharmony_ci			inter = temp_int->internal;
43562306a36Sopenharmony_ci			schedule_work(&inter->work);
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	return 1;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ciEXPORT_SYMBOL(altera_ci_irq);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
44462306a36Sopenharmony_ci				      int slot, int open)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct altera_ci_state *state = en50221->data;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (0 != slot)
44962306a36Sopenharmony_ci		return -EINVAL;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	return state->status;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic void altera_hw_filt_release(void *main_dev, int filt_nr)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct fpga_inode *temp_int = find_inode(main_dev);
45762306a36Sopenharmony_ci	struct netup_hw_pid_filter *pid_filt = NULL;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (temp_int != NULL) {
46262306a36Sopenharmony_ci		pid_filt = temp_int->internal->pid_filt[filt_nr - 1];
46362306a36Sopenharmony_ci		/* stored old feed controls */
46462306a36Sopenharmony_ci		pid_filt->demux->start_feed = pid_filt->start_feed;
46562306a36Sopenharmony_ci		pid_filt->demux->stop_feed = pid_filt->stop_feed;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		if (((--(temp_int->internal->filts_used)) <= 0) &&
46862306a36Sopenharmony_ci			 ((temp_int->internal->cis_used) <= 0)) {
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci			ci_dbg_print("%s: Actually removing\n", __func__);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci			remove_inode(temp_int->internal);
47362306a36Sopenharmony_ci			kfree(pid_filt->internal);
47462306a36Sopenharmony_ci		}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		kfree(pid_filt);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_civoid altera_ci_release(void *dev, int ci_nr)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct fpga_inode *temp_int = find_inode(dev);
48562306a36Sopenharmony_ci	struct altera_ci_state *state = NULL;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (temp_int != NULL) {
49062306a36Sopenharmony_ci		state = temp_int->internal->state[ci_nr - 1];
49162306a36Sopenharmony_ci		altera_hw_filt_release(dev, ci_nr);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		if (((temp_int->internal->filts_used) <= 0) &&
49562306a36Sopenharmony_ci				((--(temp_int->internal->cis_used)) <= 0)) {
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci			ci_dbg_print("%s: Actually removing\n", __func__);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci			remove_inode(temp_int->internal);
50062306a36Sopenharmony_ci			kfree(state->internal);
50162306a36Sopenharmony_ci		}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		if (state != NULL) {
50462306a36Sopenharmony_ci			if (state->ca.data != NULL)
50562306a36Sopenharmony_ci				dvb_ca_en50221_release(&state->ca);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci			kfree(state);
50862306a36Sopenharmony_ci		}
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ciEXPORT_SYMBOL(altera_ci_release);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic void altera_pid_control(struct netup_hw_pid_filter *pid_filt,
51562306a36Sopenharmony_ci		u16 pid, int onoff)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct fpga_internal *inter = pid_filt->internal;
51862306a36Sopenharmony_ci	u8 store = 0;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* pid 0-0x1f always enabled, don't touch them */
52162306a36Sopenharmony_ci	if ((pid == 0x2000) || (pid < 0x20))
52262306a36Sopenharmony_ci		return;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, (pid >> 3) & 0xff, 0);
52762306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
52862306a36Sopenharmony_ci			((pid >> 11) & 0x03) | (pid_filt->nr << 2), 0);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	store = netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, 0, NETUP_CI_FLG_RD);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (onoff)/* 0 - on, 1 - off */
53362306a36Sopenharmony_ci		store |= (1 << (pid & 7));
53462306a36Sopenharmony_ci	else
53562306a36Sopenharmony_ci		store &= ~(1 << (pid & 7));
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	pid_dbg_print("%s: (%d) set pid: %5d 0x%04x '%s'\n", __func__,
54262306a36Sopenharmony_ci		pid_filt->nr, pid, pid, onoff ? "off" : "on");
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt,
54662306a36Sopenharmony_ci					int filt_nr, int onoff)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	struct fpga_internal *inter = pid_filt->internal;
54962306a36Sopenharmony_ci	u8 store = 0;
55062306a36Sopenharmony_ci	int i;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	pid_dbg_print("%s: pid_filt->nr[%d]  now %s\n", __func__, pid_filt->nr,
55362306a36Sopenharmony_ci			onoff ? "off" : "on");
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (onoff)/* 0 - on, 1 - off */
55662306a36Sopenharmony_ci		store = 0xff;/* ignore pid */
55762306a36Sopenharmony_ci	else
55862306a36Sopenharmony_ci		store = 0;/* enable pid */
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	for (i = 0; i < 1024; i++) {
56362306a36Sopenharmony_ci		netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, i & 0xff, 0);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci		netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1,
56662306a36Sopenharmony_ci				((i >> 8) & 0x03) | (pid_filt->nr << 2), 0);
56762306a36Sopenharmony_ci		/* pid 0-0x1f always enabled */
56862306a36Sopenharmony_ci		netup_fpga_op_rw(inter, NETUP_CI_PID_DATA,
56962306a36Sopenharmony_ci				(i > 3 ? store : 0), 0);
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic int altera_pid_feed_control(void *demux_dev, int filt_nr,
57662306a36Sopenharmony_ci		struct dvb_demux_feed *feed, int onoff)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct fpga_inode *temp_int = find_dinode(demux_dev);
57962306a36Sopenharmony_ci	struct fpga_internal *inter = temp_int->internal;
58062306a36Sopenharmony_ci	struct netup_hw_pid_filter *pid_filt = inter->pid_filt[filt_nr - 1];
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	altera_pid_control(pid_filt, feed->pid, onoff ? 0 : 1);
58362306a36Sopenharmony_ci	/* call old feed proc's */
58462306a36Sopenharmony_ci	if (onoff)
58562306a36Sopenharmony_ci		pid_filt->start_feed(feed);
58662306a36Sopenharmony_ci	else
58762306a36Sopenharmony_ci		pid_filt->stop_feed(feed);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (feed->pid == 0x2000)
59062306a36Sopenharmony_ci		altera_toggle_fullts_streaming(pid_filt, filt_nr,
59162306a36Sopenharmony_ci						onoff ? 0 : 1);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	return 0;
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic int altera_ci_start_feed(struct dvb_demux_feed *feed, int num)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	altera_pid_feed_control(feed->demux, num, feed, 1);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	return 0;
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	altera_pid_feed_control(feed->demux, num, feed, 0);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	return 0;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic int altera_ci_start_feed_1(struct dvb_demux_feed *feed)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	return altera_ci_start_feed(feed, 1);
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic int altera_ci_stop_feed_1(struct dvb_demux_feed *feed)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	return altera_ci_stop_feed(feed, 1);
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic int altera_ci_start_feed_2(struct dvb_demux_feed *feed)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	return altera_ci_start_feed(feed, 2);
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic int altera_ci_stop_feed_2(struct dvb_demux_feed *feed)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	return altera_ci_stop_feed(feed, 2);
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	struct netup_hw_pid_filter *pid_filt = NULL;
63362306a36Sopenharmony_ci	struct fpga_inode *temp_int = find_inode(config->dev);
63462306a36Sopenharmony_ci	struct fpga_internal *inter = NULL;
63562306a36Sopenharmony_ci	int ret = 0;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	pid_filt = kzalloc(sizeof(struct netup_hw_pid_filter), GFP_KERNEL);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (!pid_filt) {
64262306a36Sopenharmony_ci		ret = -ENOMEM;
64362306a36Sopenharmony_ci		goto err;
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (temp_int != NULL) {
64762306a36Sopenharmony_ci		inter = temp_int->internal;
64862306a36Sopenharmony_ci		(inter->filts_used)++;
64962306a36Sopenharmony_ci		ci_dbg_print("%s: Find Internal Structure!\n", __func__);
65062306a36Sopenharmony_ci	} else {
65162306a36Sopenharmony_ci		inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
65262306a36Sopenharmony_ci		if (!inter) {
65362306a36Sopenharmony_ci			ret = -ENOMEM;
65462306a36Sopenharmony_ci			goto err;
65562306a36Sopenharmony_ci		}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		temp_int = append_internal(inter);
65862306a36Sopenharmony_ci		if (!temp_int) {
65962306a36Sopenharmony_ci			ret = -ENOMEM;
66062306a36Sopenharmony_ci			goto err;
66162306a36Sopenharmony_ci		}
66262306a36Sopenharmony_ci		inter->filts_used = 1;
66362306a36Sopenharmony_ci		inter->dev = config->dev;
66462306a36Sopenharmony_ci		inter->fpga_rw = config->fpga_rw;
66562306a36Sopenharmony_ci		mutex_init(&inter->fpga_mutex);
66662306a36Sopenharmony_ci		inter->strt_wrk = 1;
66762306a36Sopenharmony_ci		ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	ci_dbg_print("%s: setting hw pid filter = %p for ci = %d\n", __func__,
67162306a36Sopenharmony_ci						pid_filt, hw_filt_nr - 1);
67262306a36Sopenharmony_ci	inter->pid_filt[hw_filt_nr - 1] = pid_filt;
67362306a36Sopenharmony_ci	pid_filt->demux = config->demux;
67462306a36Sopenharmony_ci	pid_filt->internal = inter;
67562306a36Sopenharmony_ci	pid_filt->nr = hw_filt_nr - 1;
67662306a36Sopenharmony_ci	/* store old feed controls */
67762306a36Sopenharmony_ci	pid_filt->start_feed = config->demux->start_feed;
67862306a36Sopenharmony_ci	pid_filt->stop_feed = config->demux->stop_feed;
67962306a36Sopenharmony_ci	/* replace with new feed controls */
68062306a36Sopenharmony_ci	if (hw_filt_nr == 1) {
68162306a36Sopenharmony_ci		pid_filt->demux->start_feed = altera_ci_start_feed_1;
68262306a36Sopenharmony_ci		pid_filt->demux->stop_feed = altera_ci_stop_feed_1;
68362306a36Sopenharmony_ci	} else if (hw_filt_nr == 2) {
68462306a36Sopenharmony_ci		pid_filt->demux->start_feed = altera_ci_start_feed_2;
68562306a36Sopenharmony_ci		pid_filt->demux->stop_feed = altera_ci_stop_feed_2;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	altera_toggle_fullts_streaming(pid_filt, 0, 1);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	return 0;
69162306a36Sopenharmony_cierr:
69262306a36Sopenharmony_ci	ci_dbg_print("%s: Can't init hardware filter: Error %d\n",
69362306a36Sopenharmony_ci		     __func__, ret);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	kfree(pid_filt);
69662306a36Sopenharmony_ci	kfree(inter);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	return ret;
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ciint altera_ci_init(struct altera_ci_config *config, int ci_nr)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	struct altera_ci_state *state;
70462306a36Sopenharmony_ci	struct fpga_inode *temp_int = find_inode(config->dev);
70562306a36Sopenharmony_ci	struct fpga_internal *inter = NULL;
70662306a36Sopenharmony_ci	int ret = 0;
70762306a36Sopenharmony_ci	u8 store = 0;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	state = kzalloc(sizeof(struct altera_ci_state), GFP_KERNEL);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	if (!state) {
71462306a36Sopenharmony_ci		ret = -ENOMEM;
71562306a36Sopenharmony_ci		goto err;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (temp_int != NULL) {
71962306a36Sopenharmony_ci		inter = temp_int->internal;
72062306a36Sopenharmony_ci		(inter->cis_used)++;
72162306a36Sopenharmony_ci		inter->fpga_rw = config->fpga_rw;
72262306a36Sopenharmony_ci		ci_dbg_print("%s: Find Internal Structure!\n", __func__);
72362306a36Sopenharmony_ci	} else {
72462306a36Sopenharmony_ci		inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL);
72562306a36Sopenharmony_ci		if (!inter) {
72662306a36Sopenharmony_ci			ret = -ENOMEM;
72762306a36Sopenharmony_ci			goto err;
72862306a36Sopenharmony_ci		}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		temp_int = append_internal(inter);
73162306a36Sopenharmony_ci		if (!temp_int) {
73262306a36Sopenharmony_ci			ret = -ENOMEM;
73362306a36Sopenharmony_ci			goto err;
73462306a36Sopenharmony_ci		}
73562306a36Sopenharmony_ci		inter->cis_used = 1;
73662306a36Sopenharmony_ci		inter->dev = config->dev;
73762306a36Sopenharmony_ci		inter->fpga_rw = config->fpga_rw;
73862306a36Sopenharmony_ci		mutex_init(&inter->fpga_mutex);
73962306a36Sopenharmony_ci		inter->strt_wrk = 1;
74062306a36Sopenharmony_ci		ci_dbg_print("%s: Create New Internal Structure!\n", __func__);
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	ci_dbg_print("%s: setting state = %p for ci = %d\n", __func__,
74462306a36Sopenharmony_ci						state, ci_nr - 1);
74562306a36Sopenharmony_ci	state->internal = inter;
74662306a36Sopenharmony_ci	state->nr = ci_nr - 1;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	state->ca.owner = THIS_MODULE;
74962306a36Sopenharmony_ci	state->ca.read_attribute_mem = altera_ci_read_attribute_mem;
75062306a36Sopenharmony_ci	state->ca.write_attribute_mem = altera_ci_write_attribute_mem;
75162306a36Sopenharmony_ci	state->ca.read_cam_control = altera_ci_read_cam_ctl;
75262306a36Sopenharmony_ci	state->ca.write_cam_control = altera_ci_write_cam_ctl;
75362306a36Sopenharmony_ci	state->ca.slot_reset = altera_ci_slot_reset;
75462306a36Sopenharmony_ci	state->ca.slot_shutdown = altera_ci_slot_shutdown;
75562306a36Sopenharmony_ci	state->ca.slot_ts_enable = altera_ci_slot_ts_ctl;
75662306a36Sopenharmony_ci	state->ca.poll_slot_status = altera_poll_ci_slot_status;
75762306a36Sopenharmony_ci	state->ca.data = state;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	ret = dvb_ca_en50221_init(config->adapter,
76062306a36Sopenharmony_ci				   &state->ca,
76162306a36Sopenharmony_ci				   /* flags */ 0,
76262306a36Sopenharmony_ci				   /* n_slots */ 1);
76362306a36Sopenharmony_ci	if (0 != ret)
76462306a36Sopenharmony_ci		goto err;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	inter->state[ci_nr - 1] = state;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	altera_hw_filt_init(config, ci_nr);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (inter->strt_wrk) {
77162306a36Sopenharmony_ci		INIT_WORK(&inter->work, netup_read_ci_status);
77262306a36Sopenharmony_ci		inter->strt_wrk = 0;
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	ci_dbg_print("%s: CI initialized!\n", __func__);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	/* Enable div */
78062306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_TSA_DIV, 0x0, 0);
78162306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_TSB_DIV, 0x0, 0);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/* enable TS out */
78462306a36Sopenharmony_ci	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
78562306a36Sopenharmony_ci	store |= (3 << 4);
78662306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	ret = netup_fpga_op_rw(inter, NETUP_CI_REVISION, 0, NETUP_CI_FLG_RD);
78962306a36Sopenharmony_ci	/* enable irq */
79062306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0x44, 0);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	ci_dbg_print("%s: NetUP CI Revision = 0x%x\n", __func__, ret);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	schedule_work(&inter->work);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	return 0;
79962306a36Sopenharmony_cierr:
80062306a36Sopenharmony_ci	ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	kfree(state);
80362306a36Sopenharmony_ci	kfree(inter);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	return ret;
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ciEXPORT_SYMBOL(altera_ci_init);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ciint altera_ci_tuner_reset(void *dev, int ci_nr)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	struct fpga_inode *temp_int = find_inode(dev);
81262306a36Sopenharmony_ci	struct fpga_internal *inter = NULL;
81362306a36Sopenharmony_ci	u8 store;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	ci_dbg_print("%s\n", __func__);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (temp_int == NULL)
81862306a36Sopenharmony_ci		return -1;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	if (temp_int->internal == NULL)
82162306a36Sopenharmony_ci		return -1;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	inter = temp_int->internal;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	mutex_lock(&inter->fpga_mutex);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD);
82862306a36Sopenharmony_ci	store &= ~(4 << (2 - ci_nr));
82962306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
83062306a36Sopenharmony_ci	msleep(100);
83162306a36Sopenharmony_ci	store |= (4 << (2 - ci_nr));
83262306a36Sopenharmony_ci	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	mutex_unlock(&inter->fpga_mutex);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	return 0;
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ciEXPORT_SYMBOL(altera_ci_tuner_reset);
839