diff options
Diffstat (limited to 'drivers/media/pci/cx23885/altera-ci.c')
| -rw-r--r-- | drivers/media/pci/cx23885/altera-ci.c | 840 | 
1 files changed, 840 insertions, 0 deletions
diff --git a/drivers/media/pci/cx23885/altera-ci.c b/drivers/media/pci/cx23885/altera-ci.c new file mode 100644 index 00000000000..2926f7fadcc --- /dev/null +++ b/drivers/media/pci/cx23885/altera-ci.c @@ -0,0 +1,840 @@ +/* + * altera-ci.c + * + *  CI driver in conjunction with NetUp Dual DVB-T/C RF CI card + * + * Copyright (C) 2010,2011 NetUP Inc. + * Copyright (C) 2010,2011 Igor M. Liplianin <liplianin@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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * currently cx23885 GPIO's used. + * GPIO-0 ~INT in + * GPIO-1 TMS out + * GPIO-2 ~reset chips out + * GPIO-3 to GPIO-10 data/addr for CA in/out + * GPIO-11 ~CS out + * GPIO-12 AD_RG out + * GPIO-13 ~WR out + * GPIO-14 ~RD out + * GPIO-15 ~RDY in + * GPIO-16 TCK out + * GPIO-17 TDO in + * GPIO-18 TDI out + */ +/* + *  Bit definitions for MC417_RWD and MC417_OEN registers + * bits 31-16 + * +-----------+ + * | Reserved  | + * +-----------+ + *   bit 15  bit 14  bit 13 bit 12  bit 11  bit 10  bit 9   bit 8 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * |  TDI  |  TDO  |  TCK  |  RDY# |  #RD  |  #WR  | AD_RG |  #CS  | + * +-------+-------+-------+-------+-------+-------+-------+-------+ + *  bit 7   bit 6   bit 5   bit 4   bit 3   bit 2   bit 1   bit 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * |  DATA7|  DATA6|  DATA5|  DATA4|  DATA3|  DATA2|  DATA1|  DATA0| + * +-------+-------+-------+-------+-------+-------+-------+-------+ + */ +#include <media/videobuf-dma-sg.h> +#include <media/videobuf-dvb.h> +#include "altera-ci.h" +#include "dvb_ca_en50221.h" + +/* FPGA regs */ +#define NETUP_CI_INT_CTRL	0x00 +#define NETUP_CI_BUSCTRL2	0x01 +#define NETUP_CI_ADDR0		0x04 +#define NETUP_CI_ADDR1		0x05 +#define NETUP_CI_DATA		0x06 +#define NETUP_CI_BUSCTRL	0x07 +#define NETUP_CI_PID_ADDR0	0x08 +#define NETUP_CI_PID_ADDR1	0x09 +#define NETUP_CI_PID_DATA	0x0a +#define NETUP_CI_TSA_DIV	0x0c +#define NETUP_CI_TSB_DIV	0x0d +#define NETUP_CI_REVISION	0x0f + +/* const for ci op */ +#define NETUP_CI_FLG_CTL	1 +#define NETUP_CI_FLG_RD		1 +#define NETUP_CI_FLG_AD		1 + +static unsigned int ci_dbg; +module_param(ci_dbg, int, 0644); +MODULE_PARM_DESC(ci_dbg, "Enable CI debugging"); + +static unsigned int pid_dbg; +module_param(pid_dbg, int, 0644); +MODULE_PARM_DESC(pid_dbg, "Enable PID filtering debugging"); + +MODULE_DESCRIPTION("altera FPGA CI module"); +MODULE_AUTHOR("Igor M. Liplianin  <liplianin@netup.ru>"); +MODULE_LICENSE("GPL"); + +#define ci_dbg_print(args...) \ +	do { \ +		if (ci_dbg) \ +			printk(KERN_DEBUG args); \ +	} while (0) + +#define pid_dbg_print(args...) \ +	do { \ +		if (pid_dbg) \ +			printk(KERN_DEBUG args); \ +	} while (0) + +struct altera_ci_state; +struct netup_hw_pid_filter; + +struct fpga_internal { +	void *dev; +	struct mutex fpga_mutex;/* two CI's on the same fpga */ +	struct netup_hw_pid_filter *pid_filt[2]; +	struct altera_ci_state *state[2]; +	struct work_struct work; +	int (*fpga_rw) (void *dev, int flag, int data, int rw); +	int cis_used; +	int filts_used; +	int strt_wrk; +}; + +/* stores all private variables for communication with CI */ +struct altera_ci_state { +	struct fpga_internal *internal; +	struct dvb_ca_en50221 ca; +	int status; +	int nr; +}; + +/* stores all private variables for hardware pid filtering */ +struct netup_hw_pid_filter { +	struct fpga_internal *internal; +	struct dvb_demux *demux; +	/* save old functions */ +	int (*start_feed)(struct dvb_demux_feed *feed); +	int (*stop_feed)(struct dvb_demux_feed *feed); + +	int status; +	int nr; +}; + +/* internal params node */ +struct fpga_inode { +	/* pointer for internal params, one for each pair of CI's */ +	struct fpga_internal		*internal; +	struct fpga_inode		*next_inode; +}; + +/* first internal params */ +static struct fpga_inode *fpga_first_inode; + +/* find chip by dev */ +static struct fpga_inode *find_inode(void *dev) +{ +	struct fpga_inode *temp_chip = fpga_first_inode; + +	if (temp_chip == NULL) +		return temp_chip; + +	/* +	 Search for the last fpga CI chip or +	 find it by dev */ +	while ((temp_chip != NULL) && +				(temp_chip->internal->dev != dev)) +		temp_chip = temp_chip->next_inode; + +	return temp_chip; +} +/* check demux */ +static struct fpga_internal *check_filter(struct fpga_internal *temp_int, +						void *demux_dev, int filt_nr) +{ +	if (temp_int == NULL) +		return NULL; + +	if ((temp_int->pid_filt[filt_nr]) == NULL) +		return NULL; + +	if (temp_int->pid_filt[filt_nr]->demux == demux_dev) +		return temp_int; + +	return NULL; +} + +/* find chip by demux */ +static struct fpga_inode *find_dinode(void *demux_dev) +{ +	struct fpga_inode *temp_chip = fpga_first_inode; +	struct fpga_internal *temp_int; + +	/* +	 * Search of the last fpga CI chip or +	 * find it by demux +	 */ +	while (temp_chip != NULL) { +		if (temp_chip->internal != NULL) { +			temp_int = temp_chip->internal; +			if (check_filter(temp_int, demux_dev, 0)) +				break; +			if (check_filter(temp_int, demux_dev, 1)) +				break; +		} + +		temp_chip = temp_chip->next_inode; +	} + +	return temp_chip; +} + +/* deallocating chip */ +static void remove_inode(struct fpga_internal *internal) +{ +	struct fpga_inode *prev_node = fpga_first_inode; +	struct fpga_inode *del_node = find_inode(internal->dev); + +	if (del_node != NULL) { +		if (del_node == fpga_first_inode) { +			fpga_first_inode = del_node->next_inode; +		} else { +			while (prev_node->next_inode != del_node) +				prev_node = prev_node->next_inode; + +			if (del_node->next_inode == NULL) +				prev_node->next_inode = NULL; +			else +				prev_node->next_inode = +					prev_node->next_inode->next_inode; +		} + +		kfree(del_node); +	} +} + +/* allocating new chip */ +static struct fpga_inode *append_internal(struct fpga_internal *internal) +{ +	struct fpga_inode *new_node = fpga_first_inode; + +	if (new_node == NULL) { +		new_node = kmalloc(sizeof(struct fpga_inode), GFP_KERNEL); +		fpga_first_inode = new_node; +	} else { +		while (new_node->next_inode != NULL) +			new_node = new_node->next_inode; + +		new_node->next_inode = +				kmalloc(sizeof(struct fpga_inode), GFP_KERNEL); +		if (new_node->next_inode != NULL) +			new_node = new_node->next_inode; +		else +			new_node = NULL; +	} + +	if (new_node != NULL) { +		new_node->internal = internal; +		new_node->next_inode = NULL; +	} + +	return new_node; +} + +static int netup_fpga_op_rw(struct fpga_internal *inter, int addr, +							u8 val, u8 read) +{ +	inter->fpga_rw(inter->dev, NETUP_CI_FLG_AD, addr, 0); +	return inter->fpga_rw(inter->dev, 0, val, read); +} + +/* flag - mem/io, read - read/write */ +static int altera_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, +				u8 flag, u8 read, int addr, u8 val) +{ + +	struct altera_ci_state *state = en50221->data; +	struct fpga_internal *inter = state->internal; + +	u8 store; +	int mem = 0; + +	if (0 != slot) +		return -EINVAL; + +	mutex_lock(&inter->fpga_mutex); + +	netup_fpga_op_rw(inter, NETUP_CI_ADDR0, ((addr << 1) & 0xfe), 0); +	netup_fpga_op_rw(inter, NETUP_CI_ADDR1, ((addr >> 7) & 0x7f), 0); +	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); + +	store &= 0x0f; +	store |= ((state->nr << 7) | (flag << 6)); + +	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, store, 0); +	mem = netup_fpga_op_rw(inter, NETUP_CI_DATA, val, read); + +	mutex_unlock(&inter->fpga_mutex); + +	ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__, +			(read) ? "read" : "write", addr, +			(flag == NETUP_CI_FLG_CTL) ? "ctl" : "mem", +			(read) ? mem : val); + +	return mem; +} + +static int altera_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, +					int slot, int addr) +{ +	return altera_ci_op_cam(en50221, slot, 0, NETUP_CI_FLG_RD, addr, 0); +} + +static int altera_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, +					 int slot, int addr, u8 data) +{ +	return altera_ci_op_cam(en50221, slot, 0, 0, addr, data); +} + +static int altera_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, +				  int slot, u8 addr) +{ +	return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, +						NETUP_CI_FLG_RD, addr, 0); +} + +static int altera_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, +				   u8 addr, u8 data) +{ +	return altera_ci_op_cam(en50221, slot, NETUP_CI_FLG_CTL, 0, addr, data); +} + +static int altera_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) +{ +	struct altera_ci_state *state = en50221->data; +	struct fpga_internal *inter = state->internal; +	/* reasonable timeout for CI reset is 10 seconds */ +	unsigned long t_out = jiffies + msecs_to_jiffies(9999); +	int ret; + +	ci_dbg_print("%s\n", __func__); + +	if (0 != slot) +		return -EINVAL; + +	mutex_lock(&inter->fpga_mutex); + +	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); +	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, +				(ret & 0xcf) | (1 << (5 - state->nr)), 0); + +	mutex_unlock(&inter->fpga_mutex); + +	for (;;) { +		mdelay(50); + +		mutex_lock(&inter->fpga_mutex); + +		ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, +						0, NETUP_CI_FLG_RD); +		mutex_unlock(&inter->fpga_mutex); + +		if ((ret & (1 << (5 - state->nr))) == 0) +			break; +		if (time_after(jiffies, t_out)) +			break; +	} + + +	ci_dbg_print("%s: %d msecs\n", __func__, +		jiffies_to_msecs(jiffies + msecs_to_jiffies(9999) - t_out)); + +	return 0; +} + +static int altera_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) +{ +	/* not implemented */ +	return 0; +} + +static int altera_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot) +{ +	struct altera_ci_state *state = en50221->data; +	struct fpga_internal *inter = state->internal; +	int ret; + +	ci_dbg_print("%s\n", __func__); + +	if (0 != slot) +		return -EINVAL; + +	mutex_lock(&inter->fpga_mutex); + +	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); +	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, +				(ret & 0x0f) | (1 << (3 - state->nr)), 0); + +	mutex_unlock(&inter->fpga_mutex); + +	return 0; +} + +/* work handler */ +static void netup_read_ci_status(struct work_struct *work) +{ +	struct fpga_internal *inter = +			container_of(work, struct fpga_internal, work); +	int ret; + +	ci_dbg_print("%s\n", __func__); + +	mutex_lock(&inter->fpga_mutex); +	/* ack' irq */ +	ret = netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0, NETUP_CI_FLG_RD); +	ret = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL, 0, NETUP_CI_FLG_RD); + +	mutex_unlock(&inter->fpga_mutex); + +	if (inter->state[1] != NULL) { +		inter->state[1]->status = +				((ret & 1) == 0 ? +				DVB_CA_EN50221_POLL_CAM_PRESENT | +				DVB_CA_EN50221_POLL_CAM_READY : 0); +		ci_dbg_print("%s: setting CI[1] status = 0x%x\n", +				__func__, inter->state[1]->status); +	} + +	if (inter->state[0] != NULL) { +		inter->state[0]->status = +				((ret & 2) == 0 ? +				DVB_CA_EN50221_POLL_CAM_PRESENT | +				DVB_CA_EN50221_POLL_CAM_READY : 0); +		ci_dbg_print("%s: setting CI[0] status = 0x%x\n", +				__func__, inter->state[0]->status); +	} +} + +/* CI irq handler */ +int altera_ci_irq(void *dev) +{ +	struct fpga_inode *temp_int = NULL; +	struct fpga_internal *inter = NULL; + +	ci_dbg_print("%s\n", __func__); + +	if (dev != NULL) { +		temp_int = find_inode(dev); +		if (temp_int != NULL) { +			inter = temp_int->internal; +			schedule_work(&inter->work); +		} +	} + +	return 1; +} +EXPORT_SYMBOL(altera_ci_irq); + +static int altera_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, +				      int slot, int open) +{ +	struct altera_ci_state *state = en50221->data; + +	if (0 != slot) +		return -EINVAL; + +	return state->status; +} + +static void altera_hw_filt_release(void *main_dev, int filt_nr) +{ +	struct fpga_inode *temp_int = find_inode(main_dev); +	struct netup_hw_pid_filter *pid_filt = NULL; + +	ci_dbg_print("%s\n", __func__); + +	if (temp_int != NULL) { +		pid_filt = temp_int->internal->pid_filt[filt_nr - 1]; +		/* stored old feed controls */ +		pid_filt->demux->start_feed = pid_filt->start_feed; +		pid_filt->demux->stop_feed = pid_filt->stop_feed; + +		if (((--(temp_int->internal->filts_used)) <= 0) && +			 ((temp_int->internal->cis_used) <= 0)) { + +			ci_dbg_print("%s: Actually removing\n", __func__); + +			remove_inode(temp_int->internal); +			kfree(pid_filt->internal); +		} + +		kfree(pid_filt); + +	} + +} +EXPORT_SYMBOL(altera_hw_filt_release); + +void altera_ci_release(void *dev, int ci_nr) +{ +	struct fpga_inode *temp_int = find_inode(dev); +	struct altera_ci_state *state = NULL; + +	ci_dbg_print("%s\n", __func__); + +	if (temp_int != NULL) { +		state = temp_int->internal->state[ci_nr - 1]; +		altera_hw_filt_release(dev, ci_nr); + + +		if (((temp_int->internal->filts_used) <= 0) && +				((--(temp_int->internal->cis_used)) <= 0)) { + +			ci_dbg_print("%s: Actually removing\n", __func__); + +			remove_inode(temp_int->internal); +			kfree(state->internal); +		} + +		if (state != NULL) { +			if (state->ca.data != NULL) +				dvb_ca_en50221_release(&state->ca); + +			kfree(state); +		} +	} + +} +EXPORT_SYMBOL(altera_ci_release); + +static void altera_pid_control(struct netup_hw_pid_filter *pid_filt, +		u16 pid, int onoff) +{ +	struct fpga_internal *inter = pid_filt->internal; +	u8 store = 0; + +	/* pid 0-0x1f always enabled, don't touch them */ +	if ((pid == 0x2000) || (pid < 0x20)) +		return; + +	mutex_lock(&inter->fpga_mutex); + +	netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, (pid >> 3) & 0xff, 0); +	netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1, +			((pid >> 11) & 0x03) | (pid_filt->nr << 2), 0); + +	store = netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, 0, NETUP_CI_FLG_RD); + +	if (onoff)/* 0 - on, 1 - off */ +		store |= (1 << (pid & 7)); +	else +		store &= ~(1 << (pid & 7)); + +	netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, store, 0); + +	mutex_unlock(&inter->fpga_mutex); + +	pid_dbg_print("%s: (%d) set pid: %5d 0x%04x '%s'\n", __func__, +		pid_filt->nr, pid, pid, onoff ? "off" : "on"); +} + +static void altera_toggle_fullts_streaming(struct netup_hw_pid_filter *pid_filt, +					int filt_nr, int onoff) +{ +	struct fpga_internal *inter = pid_filt->internal; +	u8 store = 0; +	int i; + +	pid_dbg_print("%s: pid_filt->nr[%d]  now %s\n", __func__, pid_filt->nr, +			onoff ? "off" : "on"); + +	if (onoff)/* 0 - on, 1 - off */ +		store = 0xff;/* ignore pid */ +	else +		store = 0;/* enable pid */ + +	mutex_lock(&inter->fpga_mutex); + +	for (i = 0; i < 1024; i++) { +		netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR0, i & 0xff, 0); + +		netup_fpga_op_rw(inter, NETUP_CI_PID_ADDR1, +				((i >> 8) & 0x03) | (pid_filt->nr << 2), 0); +		/* pid 0-0x1f always enabled */ +		netup_fpga_op_rw(inter, NETUP_CI_PID_DATA, +				(i > 3 ? store : 0), 0); +	} + +	mutex_unlock(&inter->fpga_mutex); +} + +static int altera_pid_feed_control(void *demux_dev, int filt_nr, +		struct dvb_demux_feed *feed, int onoff) +{ +	struct fpga_inode *temp_int = find_dinode(demux_dev); +	struct fpga_internal *inter = temp_int->internal; +	struct netup_hw_pid_filter *pid_filt = inter->pid_filt[filt_nr - 1]; + +	altera_pid_control(pid_filt, feed->pid, onoff ? 0 : 1); +	/* call old feed proc's */ +	if (onoff) +		pid_filt->start_feed(feed); +	else +		pid_filt->stop_feed(feed); + +	if (feed->pid == 0x2000) +		altera_toggle_fullts_streaming(pid_filt, filt_nr, +						onoff ? 0 : 1); + +	return 0; +} +EXPORT_SYMBOL(altera_pid_feed_control); + +static int altera_ci_start_feed(struct dvb_demux_feed *feed, int num) +{ +	altera_pid_feed_control(feed->demux, num, feed, 1); + +	return 0; +} + +static int altera_ci_stop_feed(struct dvb_demux_feed *feed, int num) +{ +	altera_pid_feed_control(feed->demux, num, feed, 0); + +	return 0; +} + +static int altera_ci_start_feed_1(struct dvb_demux_feed *feed) +{ +	return altera_ci_start_feed(feed, 1); +} + +static int altera_ci_stop_feed_1(struct dvb_demux_feed *feed) +{ +	return altera_ci_stop_feed(feed, 1); +} + +static int altera_ci_start_feed_2(struct dvb_demux_feed *feed) +{ +	return altera_ci_start_feed(feed, 2); +} + +static int altera_ci_stop_feed_2(struct dvb_demux_feed *feed) +{ +	return altera_ci_stop_feed(feed, 2); +} + +static int altera_hw_filt_init(struct altera_ci_config *config, int hw_filt_nr) +{ +	struct netup_hw_pid_filter *pid_filt = NULL; +	struct fpga_inode *temp_int = find_inode(config->dev); +	struct fpga_internal *inter = NULL; +	int ret = 0; + +	pid_filt = kzalloc(sizeof(struct netup_hw_pid_filter), GFP_KERNEL); + +	ci_dbg_print("%s\n", __func__); + +	if (!pid_filt) { +		ret = -ENOMEM; +		goto err; +	} + +	if (temp_int != NULL) { +		inter = temp_int->internal; +		(inter->filts_used)++; +		ci_dbg_print("%s: Find Internal Structure!\n", __func__); +	} else { +		inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL); +		if (!inter) { +			ret = -ENOMEM; +			goto err; +		} + +		temp_int = append_internal(inter); +		inter->filts_used = 1; +		inter->dev = config->dev; +		inter->fpga_rw = config->fpga_rw; +		mutex_init(&inter->fpga_mutex); +		inter->strt_wrk = 1; +		ci_dbg_print("%s: Create New Internal Structure!\n", __func__); +	} + +	ci_dbg_print("%s: setting hw pid filter = %p for ci = %d\n", __func__, +						pid_filt, hw_filt_nr - 1); +	inter->pid_filt[hw_filt_nr - 1] = pid_filt; +	pid_filt->demux = config->demux; +	pid_filt->internal = inter; +	pid_filt->nr = hw_filt_nr - 1; +	/* store old feed controls */ +	pid_filt->start_feed = config->demux->start_feed; +	pid_filt->stop_feed = config->demux->stop_feed; +	/* replace with new feed controls */ +	if (hw_filt_nr == 1) { +		pid_filt->demux->start_feed = altera_ci_start_feed_1; +		pid_filt->demux->stop_feed = altera_ci_stop_feed_1; +	} else if (hw_filt_nr == 2) { +		pid_filt->demux->start_feed = altera_ci_start_feed_2; +		pid_filt->demux->stop_feed = altera_ci_stop_feed_2; +	} + +	altera_toggle_fullts_streaming(pid_filt, 0, 1); + +	return 0; +err: +	ci_dbg_print("%s: Can't init hardware filter: Error %d\n", +		     __func__, ret); + +	kfree(pid_filt); + +	return ret; +} +EXPORT_SYMBOL(altera_hw_filt_init); + +int altera_ci_init(struct altera_ci_config *config, int ci_nr) +{ +	struct altera_ci_state *state; +	struct fpga_inode *temp_int = find_inode(config->dev); +	struct fpga_internal *inter = NULL; +	int ret = 0; +	u8 store = 0; + +	state = kzalloc(sizeof(struct altera_ci_state), GFP_KERNEL); + +	ci_dbg_print("%s\n", __func__); + +	if (!state) { +		ret = -ENOMEM; +		goto err; +	} + +	if (temp_int != NULL) { +		inter = temp_int->internal; +		(inter->cis_used)++; +                inter->fpga_rw = config->fpga_rw; +		ci_dbg_print("%s: Find Internal Structure!\n", __func__); +	} else { +		inter = kzalloc(sizeof(struct fpga_internal), GFP_KERNEL); +		if (!inter) { +			ret = -ENOMEM; +			goto err; +		} + +		temp_int = append_internal(inter); +		inter->cis_used = 1; +		inter->dev = config->dev; +		inter->fpga_rw = config->fpga_rw; +		mutex_init(&inter->fpga_mutex); +		inter->strt_wrk = 1; +		ci_dbg_print("%s: Create New Internal Structure!\n", __func__); +	} + +	ci_dbg_print("%s: setting state = %p for ci = %d\n", __func__, +						state, ci_nr - 1); +	state->internal = inter; +	state->nr = ci_nr - 1; + +	state->ca.owner = THIS_MODULE; +	state->ca.read_attribute_mem = altera_ci_read_attribute_mem; +	state->ca.write_attribute_mem = altera_ci_write_attribute_mem; +	state->ca.read_cam_control = altera_ci_read_cam_ctl; +	state->ca.write_cam_control = altera_ci_write_cam_ctl; +	state->ca.slot_reset = altera_ci_slot_reset; +	state->ca.slot_shutdown = altera_ci_slot_shutdown; +	state->ca.slot_ts_enable = altera_ci_slot_ts_ctl; +	state->ca.poll_slot_status = altera_poll_ci_slot_status; +	state->ca.data = state; + +	ret = dvb_ca_en50221_init(config->adapter, +				   &state->ca, +				   /* flags */ 0, +				   /* n_slots */ 1); +	if (0 != ret) +		goto err; + +       inter->state[ci_nr - 1] = state; + +	altera_hw_filt_init(config, ci_nr); + +	if (inter->strt_wrk) { +		INIT_WORK(&inter->work, netup_read_ci_status); +		inter->strt_wrk = 0; +	} + +	ci_dbg_print("%s: CI initialized!\n", __func__); + +	mutex_lock(&inter->fpga_mutex); + +	/* Enable div */ +	netup_fpga_op_rw(inter, NETUP_CI_TSA_DIV, 0x0, 0); +	netup_fpga_op_rw(inter, NETUP_CI_TSB_DIV, 0x0, 0); + +	/* enable TS out */ +	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD); +	store |= (3 << 4); +	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0); + +	ret = netup_fpga_op_rw(inter, NETUP_CI_REVISION, 0, NETUP_CI_FLG_RD); +	/* enable irq */ +	netup_fpga_op_rw(inter, NETUP_CI_INT_CTRL, 0x44, 0); + +	mutex_unlock(&inter->fpga_mutex); + +	ci_dbg_print("%s: NetUP CI Revision = 0x%x\n", __func__, ret); + +	schedule_work(&inter->work); + +	return 0; +err: +	ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret); + +	kfree(state); + +	return ret; +} +EXPORT_SYMBOL(altera_ci_init); + +int altera_ci_tuner_reset(void *dev, int ci_nr) +{ +	struct fpga_inode *temp_int = find_inode(dev); +	struct fpga_internal *inter = NULL; +	u8 store; + +	ci_dbg_print("%s\n", __func__); + +	if (temp_int == NULL) +		return -1; + +	if (temp_int->internal == NULL) +		return -1; + +	inter = temp_int->internal; + +	mutex_lock(&inter->fpga_mutex); + +	store = netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, 0, NETUP_CI_FLG_RD); +	store &= ~(4 << (2 - ci_nr)); +	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0); +	msleep(100); +	store |= (4 << (2 - ci_nr)); +	netup_fpga_op_rw(inter, NETUP_CI_BUSCTRL2, store, 0); + +	mutex_unlock(&inter->fpga_mutex); + +	return 0; +} +EXPORT_SYMBOL(altera_ci_tuner_reset);  | 
