static int mpq_sw_dmx_start_filtering(struct dvb_demux_feed *feed)
{
	int ret = -EINVAL;
	struct mpq_demux *mpq_demux = feed->demux->priv;

	MPQ_DVB_DBG_PRINT("%s(pid=%d) executed\n", __func__, feed->pid);

	if (mpq_demux == NULL) {
		MPQ_DVB_ERR_PRINT("%s: invalid mpq_demux handle\n", __func__);
		goto out;
	}

	if (mpq_demux->source < DMX_SOURCE_DVR0) {
		MPQ_DVB_ERR_PRINT("%s: only DVR source is supported (%d)\n",
			__func__, mpq_demux->source);
		goto out;
	}

	/*
	 * Always feed sections/PES starting from a new one and
	 * do not partial transfer data from older one
	 */
	feed->pusi_seen = 0;

	ret = mpq_dmx_init_mpq_feed(feed);
	if (ret)
		MPQ_DVB_ERR_PRINT("%s: mpq_dmx_init_mpq_feed failed(%d)\n",
			__func__, ret);
out:
	return ret;
}
static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
{
	int ret;
	struct mpq_demux *mpq_demux = feed->demux->priv;

	MPQ_DVB_DBG_PRINT(
		"%s(pid=%d) executed\n",
		__func__,
		feed->pid);

	if (mpq_demux == NULL) {
		MPQ_DVB_ERR_PRINT(
			"%s: invalid mpq_demux handle\n",
			__func__);

		return -EINVAL;
	}

	if (mpq_demux->source < DMX_SOURCE_DVR0) {
		/* source from TSPP, need to configure tspp pipe */
		ret = mpq_tspp_dmx_add_channel(feed);

		if (ret < 0) {
			MPQ_DVB_DBG_PRINT(
				"%s: mpq_tspp_dmx_add_channel failed(%d)\n",
				__func__,
				ret);
			return ret;
		}
	}

	/*
	 * Always feed sections/PES starting from a new one and
	 * do not partial transfer data from older one
	 */
	feed->pusi_seen = 0;

	ret = mpq_dmx_init_mpq_feed(feed);
	if (ret) {
		MPQ_DVB_ERR_PRINT(
			"%s: mpq_dmx_init_mpq_feed failed(%d)\n",
			__func__,
			ret);
		if (mpq_demux->source < DMX_SOURCE_DVR0)
			mpq_tspp_dmx_remove_channel(feed);

		return ret;
	}

	return 0;
}
Beispiel #3
0
int mpq_streambuffer_data_write_deposit(
				struct mpq_streambuffer *sbuff,
				size_t len)
{
	if (NULL == sbuff)
		return -EINVAL;

	if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len))
		return -ENOSPC;

	if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
		DVB_RINGBUFFER_PUSH(&sbuff->raw_data, len);
		wake_up_all(&sbuff->raw_data.queue);
	} else {
		/* Linear buffer group */
		struct mpq_streambuffer_buffer_desc *desc;
		desc = (struct mpq_streambuffer_buffer_desc *)
				&sbuff->raw_data.data[sbuff->raw_data.pwrite];

		if ((sbuff->pending_buffers_count == sbuff->buffers_num) ||
			 ((desc->size - desc->write_ptr) < len)) {
			MPQ_DVB_ERR_PRINT(
				"%s: No space available!\n",
				__func__);
			return -ENOSPC;
		}
		desc->write_ptr += len;
	}

	return 0;
}
/**
 * Stop filtering according to feed parameter.
 *
 * @feed: the feed we are working on.
 *
 * Return	error code.
 */
static int mpq_tsif_dmx_stop_filtering(struct dvb_demux_feed *feed)
{
	int ret = 0;
	struct mpq_demux *mpq_demux = feed->demux->priv;

	MPQ_DVB_DBG_PRINT(
		"%s(%d) executed\n",
		__func__,
		feed->pid);

	if (mpq_demux == NULL) {
		MPQ_DVB_ERR_PRINT(
			"%s: invalid mpq_demux handle\n",
			__func__);

		return -EINVAL;
	}

	mpq_dmx_terminate_feed(feed);

	if (mpq_demux->source < DMX_SOURCE_DVR0) {
		/* Source from TSIF, need to configure TSIF hardware */
		ret = mpq_tsif_dmx_stop(mpq_demux);
	}

	return ret;
}
/**
 * Returns demux capabilities of TSPPv2 plugin
 *
 * @demux: demux device
 * @caps: Returned capbabilities
 *
 * Return     error code
 */
static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux,
				struct dmx_caps *caps)
{
	struct dvb_demux *dvb_demux = demux->priv;

	if ((dvb_demux == NULL) || (caps == NULL)) {
		MPQ_DVB_ERR_PRINT(
			"%s: invalid parameters\n",
			__func__);

		return -EINVAL;
	}

	caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_INDEXING |
		DMX_CAP_VIDEO_DECODER_DATA | DMX_CAP_TS_INSERTION |
		DMX_CAP_SECURED_INPUT_PLAYBACK;
	caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES;
	caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
	caps->num_pid_filters = TSPP_MAX_PID_FILTER_NUM;
	caps->num_section_filters = dvb_demux->filternum;
	caps->num_section_filters_per_pid = dvb_demux->filternum;
	caps->section_filter_length = DMX_FILTER_SIZE;
	caps->num_demod_inputs = TSIF_COUNT;
	caps->num_memory_inputs = BAM_INPUT_COUNT;
	caps->max_bitrate = 320;
	caps->demod_input_max_bitrate = 96;
	caps->memory_input_max_bitrate = 80;
	caps->num_cipher_ops = DMX_MAX_CIPHER_OPERATIONS_COUNT;

	/* TSIF reports 7 bytes STC at unit of 27MHz */
	caps->max_stc = 0x00FFFFFFFFFFFFFF;

	return 0;
}
/**
 * Returns demux capabilities of TSPPv2 plugin
 *
 * @demux: demux device
 * @caps: Returned capbabilities
 *
 * Return     error code
 */
static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux,
				struct dmx_caps *caps)
{
	struct dvb_demux *dvb_demux = demux->priv;

	if ((dvb_demux == NULL) || (caps == NULL)) {
		MPQ_DVB_ERR_PRINT(
			"%s: invalid parameters\n",
			__func__);

		return -EINVAL;
	}

	caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_INDEXING |
		DMX_CAP_VIDEO_DECODER_DATA;
	caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES;
	caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
	caps->num_pid_filters = TSPP_MAX_PID_FILTER_NUM;
	caps->num_section_filters = dvb_demux->filternum;
	caps->num_section_filters_per_pid = dvb_demux->filternum;
	caps->section_filter_length = DMX_FILTER_SIZE;
	caps->num_demod_inputs = TSIF_COUNT;
	caps->num_memory_inputs = BAM_INPUT_COUNT;
	caps->max_bitrate = 320;
	caps->demod_input_max_bitrate = 96;
	caps->memory_input_max_bitrate = 80;

	return 0;
}
static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
{
	int ret;
	struct mpq_demux *mpq_demux = feed->demux->priv;

	MPQ_DVB_DBG_PRINT(
		"%s(pid=%d) executed\n",
		__func__,
		feed->pid);

	if (mpq_demux == NULL) {
		MPQ_DVB_ERR_PRINT(
			"%s: invalid mpq_demux handle\n",
			__func__);

		return -EINVAL;
	}

	if (mpq_demux->source < DMX_SOURCE_DVR0) {
		
		ret = mpq_tspp_dmx_add_channel(feed);

		if (ret < 0) {
			MPQ_DVB_DBG_PRINT(
				"%s: mpq_tspp_dmx_add_channel failed(%d)\n",
				__func__,
				ret);
			return ret;
		}
	}

	feed->pusi_seen = 0;

	ret = mpq_dmx_init_mpq_feed(feed);
	if (ret) {
		MPQ_DVB_ERR_PRINT(
			"%s: mpq_dmx_init_mpq_feed failed(%d)\n",
			__func__,
			ret);
		if (mpq_demux->source < DMX_SOURCE_DVR0)
			mpq_tspp_dmx_remove_channel(feed);

		return ret;
	}

	return 0;
}
static void mpq_dmx_tspp_aggregated_process(int tsif, int channel_id)
{
	const struct tspp_data_descriptor *tspp_data_desc;
	struct mpq_demux *mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
	struct sdmx_buff_descr input;
	size_t aggregate_len = 0;
	size_t aggregate_count = 0;
	phys_addr_t buff_start_addr_phys;
	phys_addr_t buff_current_addr_phys = 0;
	u32 notif_size;
	int i;

	while ((tspp_data_desc = tspp_get_buffer(0, channel_id)) != NULL) {
		if (0 == aggregate_count)
			buff_current_addr_phys = tspp_data_desc->phys_base;
		notif_size = tspp_data_desc->size / TSPP_RAW_TTS_SIZE;
		mpq_dmx_tspp_info.tsif[tsif].aggregate_ids[aggregate_count] =
			tspp_data_desc->id;
		aggregate_len += tspp_data_desc->size;
		aggregate_count++;
		mpq_demux->hw_notification_size += notif_size;

		
		if (mpq_demux->num_active_feeds > mpq_demux->num_secure_feeds)
			mpq_dmx_tspp_swfilter_desc(mpq_demux, tspp_data_desc);

	}

	if (!aggregate_count)
		return;

	buff_start_addr_phys =
		mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base;

	if (buff_start_addr_phys > 0xFFFFFFFF)
		MPQ_DVB_ERR_PRINT(
			"%s: WARNNING - physical address %pa is larger than 32bits!\n",
			__func__, &buff_start_addr_phys);

	input.base_addr = (void *)(u32)buff_start_addr_phys;
	input.size = mpq_dmx_tspp_info.tsif[tsif].buffer_count *
		TSPP_DESCRIPTOR_SIZE;

	if (mpq_sdmx_is_loaded() && mpq_demux->sdmx_filter_count) {
		MPQ_DVB_DBG_PRINT(
			"%s: SDMX Processing %d descriptors: %d bytes at start address 0x%x, read offset %d\n",
			__func__, aggregate_count, aggregate_len,
			(unsigned int)input.base_addr,
			(int)(buff_current_addr_phys - buff_start_addr_phys));

		mpq_sdmx_process(mpq_demux, &input, aggregate_len,
			buff_current_addr_phys - buff_start_addr_phys,
			TSPP_RAW_TTS_SIZE);
	}

	for (i = 0; i < aggregate_count; i++)
		tspp_release_buffer(0, channel_id,
			mpq_dmx_tspp_info.tsif[tsif].aggregate_ids[i]);
}
static int mpq_tspp_add_all_user_filters(int channel_id,
				enum tspp_source source)
{
	struct tspp_filter tspp_filter;
	int tsif = TSPP_GET_TSIF_NUM(channel_id);
	int slot;
	u16 added_count = 0;
	u16 total_filters_count = 0;

	MPQ_DVB_DBG_PRINT("%s: executed\n", __func__);

	tspp_filter.mode = TSPP_MODE_RAW;
	tspp_filter.source = source;
	tspp_filter.decrypt = 0;

	for (slot = 0; slot < TSPP_MAX_PID_FILTER_NUM; slot++) {
		if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid == -1)
			continue;

		total_filters_count++;

		if (added_count > TSPP_MAX_HW_PID_FILTER_NUM)
			continue;

		tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif);

		if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid ==
				TSPP_PASS_THROUGH_PID) {
			
			tspp_filter.pid = 0;
			tspp_filter.mask = 0;
		} else {
			tspp_filter.pid =
				mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid;
			tspp_filter.mask = TSPP_PID_MASK;
		}

		MPQ_DVB_DBG_PRINT(
			"%s: adding HW filter, PID = %d, mask = 0x%X, index = %d\n",
				__func__, tspp_filter.pid, tspp_filter.mask,
				tspp_filter.priority);

		if (!tspp_add_filter(0, channel_id, &tspp_filter)) {
			mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index =
				tspp_filter.priority;
			added_count++;
		} else {
			MPQ_DVB_ERR_PRINT("%s: tspp_add_filter failed\n",
						__func__);
		}
	}

	if ((added_count != TSPP_MAX_HW_PID_FILTER_NUM) ||
		(added_count != total_filters_count))
		return -EINVAL;

	return 0;
}
/**
 * Allocate memory for channel output of specific TSIF.
 *
 * @tsif: The TSIF id to which memory should be allocated.
 *
 * Return  error status
 */
static int mpq_dmx_channel_mem_alloc(int tsif)
{
	int result;
	size_t len;

	MPQ_DVB_DBG_PRINT("%s(%d)\n", __func__, tsif);

	mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle =
		ion_alloc(mpq_dmx_tspp_info.ion_client,
		 (mpq_dmx_tspp_info.tsif[tsif].buffer_count *
		  TSPP_DESCRIPTOR_SIZE),
		 SZ_4K,
		 ION_HEAP(tspp_out_ion_heap),
		 0); /* non-cached */

	if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle)) {
		MPQ_DVB_ERR_PRINT("%s: ion_alloc() failed\n", __func__);
		mpq_dmx_channel_mem_free(tsif);
		return -ENOMEM;
	}

	/* save virtual base address of heap */
	mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_virt_base =
		ion_map_kernel(mpq_dmx_tspp_info.ion_client,
			mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle);
	if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].
				ch_mem_heap_virt_base)) {
		MPQ_DVB_ERR_PRINT("%s: ion_map_kernel() failed\n", __func__);
		mpq_dmx_channel_mem_free(tsif);
		return -ENOMEM;
	}

	/* save physical base address of heap */
	result = ion_phys(mpq_dmx_tspp_info.ion_client,
		mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle,
		&(mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base), &len);
	if (result < 0) {
		MPQ_DVB_ERR_PRINT("%s: ion_phys() failed\n", __func__);
		mpq_dmx_channel_mem_free(tsif);
		return -ENOMEM;
	}

	return 0;
}
static int mpq_tspp_add_null_blocking_filters(int channel_id,
				enum tspp_source source)
{
	struct tspp_filter tspp_filter;
	int ret = 0;
	int i, j;
	u16 full_pid_mask = 0x1FFF;
	u8 mask_shift;
	u8 pid_shift;
	int tsif = TSPP_GET_TSIF_NUM(channel_id);

	MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n",
		__func__, channel_id, source);


	tspp_filter.mode = TSPP_MODE_RAW;
	tspp_filter.source = source;
	tspp_filter.decrypt = 0;

	for (i = 0; i < TSPP_BLOCK_NULLS_FILTERS_NUM; i++) {
		tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif);
		if (tspp_filter.priority != i) {
			MPQ_DVB_ERR_PRINT(
				"%s: got unexpected HW index %d, expected %d\n",
				__func__, tspp_filter.priority, i);
			ret = -1;
			break;
		}
		mask_shift = (TSPP_BLOCK_NULLS_FILTERS_NUM - 1 - i);
		pid_shift = (TSPP_BLOCK_NULLS_FILTERS_NUM - i);
		tspp_filter.mask =
			((full_pid_mask >> mask_shift) << mask_shift);
		tspp_filter.pid = ((full_pid_mask >> pid_shift) << pid_shift);

		if (tspp_add_filter(0, channel_id, &tspp_filter)) {
			ret = -1;
			break;
		}
	}

	if (ret) {
		
		for (j = 0; j < i; j++) {
			tspp_filter.priority = j;
			mpq_tspp_release_hw_filter_index(tsif, j);
			tspp_remove_filter(0, channel_id, &tspp_filter);
		}
	} else {
		MPQ_DVB_DBG_PRINT(
			"%s: NULL blocking filters added successfully\n",
			__func__);
	}

	return ret;
}
Beispiel #12
0
ssize_t mpq_streambuffer_data_write(
			struct mpq_streambuffer *sbuff,
			const u8 *buf, size_t len)
{
	int res;

	if ((NULL == sbuff) || (NULL == buf))
		return -EINVAL;

	if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
		if (unlikely(dvb_ringbuffer_free(&sbuff->raw_data) < len))
			return -ENOSPC;
		/*
		 * Secure buffers are not permitted to be mapped into kernel
		 * memory, and so buffer base address may be NULL
		 */
		if (NULL == sbuff->raw_data.data)
			return -EPERM;
		res = dvb_ringbuffer_write(&sbuff->raw_data, buf, len);
		wake_up_all(&sbuff->raw_data.queue);
	} else {
		/* Linear buffer group */
		struct mpq_streambuffer_buffer_desc *desc;

		desc = (struct mpq_streambuffer_buffer_desc *)
				&sbuff->raw_data.data[sbuff->raw_data.pwrite];

		/*
		 * Secure buffers are not permitted to be mapped into kernel
		 * memory, and so buffer base address may be NULL
		 */
		if (NULL == desc->base)
			return -EPERM;

		if ((sbuff->pending_buffers_count == sbuff->buffers_num) ||
			((desc->size - desc->write_ptr) < len)) {
			MPQ_DVB_ERR_PRINT(
				"%s: No space available! %d pending buffers out of %d total buffers. write_ptr=%d, size=%d\n",
				__func__,
				sbuff->pending_buffers_count,
				sbuff->buffers_num,
				desc->write_ptr,
				desc->size);
			return -ENOSPC;
		}
		memcpy(desc->base + desc->write_ptr, buf, len);
		desc->write_ptr += len;
		MPQ_DVB_DBG_PRINT(
			"%s: copied %d data bytes. handle=%d, write_ptr=%d\n",
			__func__, len, desc->handle, desc->write_ptr);
		res = len;
	}

	return res;
}
static int mpq_sw_dmx_stop_filtering(struct dvb_demux_feed *feed)
{
	int ret;

	MPQ_DVB_DBG_PRINT("%s(%d) executed\n", __func__, feed->pid);

	ret = mpq_dmx_terminate_feed(feed);
	if (ret)
		MPQ_DVB_ERR_PRINT("%s: mpq_dmx_terminate_feed failed(%d)\n",
			__func__, ret);

	return ret;
}
static int mpq_sw_dmx_set_source(struct dmx_demux *demux,
		const dmx_source_t *src)
{
	int ret = -EINVAL;

	if (demux == NULL || demux->priv == NULL || src == NULL) {
		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
		goto out;
	}

	if (*src >= DMX_SOURCE_DVR0 && *src <= DMX_SOURCE_DVR3) {
		ret = mpq_dmx_set_source(demux, src);
		if (ret)
			MPQ_DVB_ERR_PRINT(
				"%s: mpq_dmx_set_source(%d) failed, ret=%d\n",
				__func__, *src, ret);
	} else {
		MPQ_DVB_ERR_PRINT("%s: not a DVR source\n", __func__);
	}

out:
	return ret;
}
/**
 * Stop TSIF operation and detach from TSIF driver.
 *
 * @mpq_demux: the mpq_demux we are working on.
 *
 * Return	error code.
 */
static int mpq_tsif_dmx_stop(struct mpq_demux *mpq_demux)
{
	int tsif;
	struct tsif_driver_info *tsif_driver;

	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);

	/* determine the TSIF we are reading from */
	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
		tsif = 0;
	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
		tsif = 1;
	} else {
		/* invalid source */
		MPQ_DVB_ERR_PRINT(
			"%s: invalid input source (%d)\n",
			__func__,
			mpq_demux->source);

		return -EINVAL;
	}

	if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
		return -ERESTARTSYS;

	mpq_dmx_tsif_info.tsif[tsif].ref_count--;

	if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) {
		tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);
		tsif_stop(tsif_driver->tsif_handler);
		tsif_detach(tsif_driver->tsif_handler);
		tsif_driver->tsif_handler = NULL;
		tsif_driver->data_buffer = NULL;
		tsif_driver->buffer_size = 0;
		atomic_set(&mpq_dmx_tsif_info.tsif[tsif].data_cnt, 0);
		mpq_dmx_tsif_info.tsif[tsif].mpq_demux = NULL;
	}

	mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);

	return 0;
}
static int mpq_tspp_remove_null_blocking_filters(int channel_id,
				enum tspp_source source)
{
	struct tspp_filter tspp_filter;
	int tsif = TSPP_GET_TSIF_NUM(channel_id);
	int ret = 0;
	int i;

	MPQ_DVB_DBG_PRINT("%s: executed, channel id = %d, source = %d\n",
		__func__, channel_id, source);

	for (i = 0; i < TSPP_BLOCK_NULLS_FILTERS_NUM; i++) {
		tspp_filter.priority = i;
		if (tspp_remove_filter(0, channel_id, &tspp_filter)) {
			MPQ_DVB_ERR_PRINT("%s: failed to remove filter %d\n",
				__func__, i);
			ret = -1;
		}

		mpq_tspp_release_hw_filter_index(tsif, i);
	}

	return ret;
}
static int mpq_sw_dmx_get_caps(struct dmx_demux *demux, struct dmx_caps *caps)
{
	struct dvb_demux *dvb_demux = demux->priv;

	if (dvb_demux == NULL || caps == NULL) {
		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
		return -EINVAL;
	}

	caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA |
		DMX_CAP_TS_INSERTION | DMX_CAP_VIDEO_INDEXING |
		DMX_CAP_AUTO_BUFFER_FLUSH;
	caps->recording_max_video_pids_indexed = 0;
	caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES;
	caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
	caps->num_pid_filters = MPQ_MAX_DMX_FILES;
	caps->num_section_filters = dvb_demux->filternum;
	caps->num_section_filters_per_pid = dvb_demux->filternum;
	caps->section_filter_length = DMX_FILTER_SIZE;
	caps->num_demod_inputs = 0;
	caps->num_memory_inputs = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
	caps->max_bitrate = 192;
	caps->demod_input_max_bitrate = 96;
	caps->memory_input_max_bitrate = 96;
	caps->num_cipher_ops = 1;

	/* No STC support */
	caps->max_stc = 0;

	/* Buffer requirements */
	caps->section.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT |
		DMX_BUFFER_INTERNAL_SUPPORT |
		DMX_BUFFER_CACHED;
	caps->section.max_buffer_num = 1;
	caps->section.max_size = 0xFFFFFFFF;
	caps->section.size_alignment = 0;
	caps->pes.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT |
		DMX_BUFFER_INTERNAL_SUPPORT |
		DMX_BUFFER_CACHED;
	caps->pes.max_buffer_num = 1;
	caps->pes.max_size = 0xFFFFFFFF;
	caps->pes.size_alignment = 0;
	caps->recording_188_tsp.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT |
		DMX_BUFFER_INTERNAL_SUPPORT |
		DMX_BUFFER_CACHED;
	caps->recording_188_tsp.max_buffer_num = 1;
	caps->recording_188_tsp.max_size = 0xFFFFFFFF;
	caps->recording_188_tsp.size_alignment = 0;
	caps->recording_192_tsp.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT |
		DMX_BUFFER_INTERNAL_SUPPORT |
		DMX_BUFFER_CACHED;
	caps->recording_192_tsp.max_buffer_num = 1;
	caps->recording_192_tsp.max_size = 0xFFFFFFFF;
	caps->recording_192_tsp.size_alignment = 0;
	caps->playback_188_tsp.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT |
		DMX_BUFFER_INTERNAL_SUPPORT |
		DMX_BUFFER_CACHED;
	caps->playback_188_tsp.max_buffer_num = 1;
	caps->playback_188_tsp.max_size = 0xFFFFFFFF;
	caps->playback_188_tsp.size_alignment = 188;
	caps->playback_192_tsp.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT |
		DMX_BUFFER_INTERNAL_SUPPORT |
		DMX_BUFFER_CACHED;
	caps->playback_192_tsp.max_buffer_num = 1;
	caps->playback_192_tsp.max_size = 0xFFFFFFFF;
	caps->playback_192_tsp.size_alignment = 192;
	caps->decoder.flags =
		DMX_BUFFER_SECURED_IF_DECRYPTED	|
		DMX_BUFFER_EXTERNAL_SUPPORT	|
		DMX_BUFFER_INTERNAL_SUPPORT	|
		DMX_BUFFER_LINEAR_GROUP_SUPPORT |
		DMX_BUFFER_CACHED;
	caps->decoder.max_buffer_num = DMX_MAX_DECODER_BUFFER_NUM;
	caps->decoder.max_size = 0xFFFFFFFF;
	caps->decoder.size_alignment = SZ_4K;

	return 0;
}
static int mpq_tspp_dmx_init(
			struct dvb_adapter *mpq_adapter,
			struct mpq_demux *mpq_demux)
{
	int result;

	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);

	mpq_dmx_tspp_info.ion_client = mpq_demux->ion_client;

	/* Set the kernel-demux object capabilities */
	mpq_demux->demux.dmx.capabilities =
		DMX_TS_FILTERING			|
		DMX_PES_FILTERING			|
		DMX_SECTION_FILTERING			|
		DMX_MEMORY_BASED_FILTERING		|
		DMX_CRC_CHECKING			|
		DMX_TS_DESCRAMBLING;

	/* Set dvb-demux "virtual" function pointers */
	mpq_demux->demux.priv = (void *)mpq_demux;
	mpq_demux->demux.filternum = TSPP_MAX_SECTION_FILTER_NUM;
	mpq_demux->demux.feednum = MPQ_MAX_DMX_FILES;
	mpq_demux->demux.start_feed = mpq_tspp_dmx_start_filtering;
	mpq_demux->demux.stop_feed = mpq_tspp_dmx_stop_filtering;
	mpq_demux->demux.write_to_decoder = mpq_tspp_dmx_write_to_decoder;
	mpq_demux->demux.decoder_fullness_init = mpq_dmx_decoder_fullness_init;
	mpq_demux->demux.decoder_fullness_wait = mpq_dmx_decoder_fullness_wait;
	mpq_demux->demux.decoder_fullness_abort =
		mpq_dmx_decoder_fullness_abort;
	mpq_demux->demux.decoder_buffer_status = mpq_dmx_decoder_buffer_status;
	mpq_demux->demux.reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer;
	mpq_demux->demux.set_secure_mode = mpq_dmx_set_secure_mode;

	/* Initialize dvb_demux object */
	result = dvb_dmx_init(&mpq_demux->demux);
	if (result < 0) {
		MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
		goto init_failed;
	}

	/* Now initailize the dmx-dev object */
	mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES;
	mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
	mpq_demux->dmxdev.capabilities =
		DMXDEV_CAP_DUPLEX |
		DMXDEV_CAP_PULL_MODE |
		DMXDEV_CAP_INDEXING;

	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
	mpq_demux->dmxdev.demux->get_caps = mpq_tspp_dmx_get_caps;
	mpq_demux->dmxdev.demux->map_buffer = mpq_dmx_map_buffer;
	mpq_demux->dmxdev.demux->unmap_buffer = mpq_dmx_unmap_buffer;
	mpq_demux->dmxdev.demux->write = mpq_dmx_write;
	result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
	if (result < 0) {
		MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
						  __func__,
						  result);
		goto init_failed_dmx_release;
	}

	/* Extend dvb-demux debugfs with TSPP statistics. */
	mpq_dmx_init_hw_statistics(mpq_demux);

	return 0;

init_failed_dmx_release:
	dvb_dmx_release(&mpq_demux->demux);
init_failed:
	return result;
}
/**
 * Returns demux capabilities of TSPPv1 plugin
 *
 * @demux: demux device
 * @caps: Returned capbabilities
 *
 * Return     error code
 */
static int mpq_tspp_dmx_get_caps(struct dmx_demux *demux,
				struct dmx_caps *caps)
{
	struct dvb_demux *dvb_demux = demux->priv;

	if ((dvb_demux == NULL) || (caps == NULL)) {
		MPQ_DVB_ERR_PRINT(
			"%s: invalid parameters\n",
			__func__);

		return -EINVAL;
	}

	caps->caps = DMX_CAP_PULL_MODE | DMX_CAP_VIDEO_DECODER_DATA;
	caps->num_decoders = MPQ_ADAPTER_MAX_NUM_OF_INTERFACES;
	caps->num_demux_devices = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
	caps->num_pid_filters = TSPP_MAX_PID_FILTER_NUM;
	caps->num_section_filters = dvb_demux->filternum;
	caps->num_section_filters_per_pid = dvb_demux->filternum;
	caps->section_filter_length = DMX_FILTER_SIZE;
	caps->num_demod_inputs = TSIF_COUNT;
	caps->num_memory_inputs = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
	caps->max_bitrate = 144;
	caps->demod_input_max_bitrate = 72;
	caps->memory_input_max_bitrate = 72;

	/* Buffer requirements */
	caps->section.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT	|
		DMX_BUFFER_INTERNAL_SUPPORT;
	caps->section.max_buffer_num = 1;
	caps->section.max_size = 0xFFFFFFFF;
	caps->section.size_alignment = 0;
	caps->pes.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT	|
		DMX_BUFFER_INTERNAL_SUPPORT;
	caps->pes.max_buffer_num = 1;
	caps->pes.max_size = 0xFFFFFFFF;
	caps->pes.size_alignment = 0;
	caps->recording_188_tsp.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT	|
		DMX_BUFFER_INTERNAL_SUPPORT;
	caps->recording_188_tsp.max_buffer_num = 1;
	caps->recording_188_tsp.max_size = 0xFFFFFFFF;
	caps->recording_188_tsp.size_alignment = 0;
	caps->recording_192_tsp.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT	|
		DMX_BUFFER_INTERNAL_SUPPORT;
	caps->recording_192_tsp.max_buffer_num = 1;
	caps->recording_192_tsp.max_size = 0xFFFFFFFF;
	caps->recording_192_tsp.size_alignment = 0;
	caps->playback_188_tsp.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT	|
		DMX_BUFFER_INTERNAL_SUPPORT;
	caps->playback_188_tsp.max_buffer_num = 1;
	caps->playback_188_tsp.max_size = 0xFFFFFFFF;
	caps->playback_188_tsp.size_alignment = 0;
	caps->playback_192_tsp.flags =
		DMX_BUFFER_EXTERNAL_SUPPORT	|
		DMX_BUFFER_INTERNAL_SUPPORT;
	caps->playback_192_tsp.max_buffer_num = 1;
	caps->playback_192_tsp.max_size = 0xFFFFFFFF;
	caps->playback_192_tsp.size_alignment = 0;
	caps->decoder.flags =
		DMX_BUFFER_CONTIGUOUS_MEM	|
		DMX_BUFFER_SECURED_IF_DECRYPTED	|
		DMX_BUFFER_EXTERNAL_SUPPORT	|
		DMX_BUFFER_INTERNAL_SUPPORT	|
		DMX_BUFFER_LINEAR_GROUP_SUPPORT;
	caps->decoder.max_buffer_num = DMX_MAX_DECODER_BUFFER_NUM;
	caps->decoder.max_size = 0xFFFFFFFF;
	caps->decoder.size_alignment = SZ_4K;

	return 0;
}
/**
 * Removes filter from TSPP.
 *
 * @feed: The feed to remove
 *
 * Return  error status
 *
 * The function checks if this is the only PID allocated within
 * the channel, if so, the channel is closed as well.
 */
static int mpq_tspp_dmx_remove_channel(struct dvb_demux_feed *feed)
{
	int tsif;
	int ret;
	int channel_id;
	atomic_t *data_cnt;
	int *channel_ref_count;
	struct tspp_filter tspp_filter;
	struct mpq_demux *mpq_demux = feed->demux->priv;

	/* determine the TSIF we are reading from */
	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
		tsif = 0;
	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
		tsif = 1;
	} else {
		/* invalid source */
		MPQ_DVB_ERR_PRINT(
			"%s: invalid input source (%d)\n",
			__func__,
			mpq_demux->source);

		return -EINVAL;
	}

	if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
		return -ERESTARTSYS;

	channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL);
	channel_ref_count = &mpq_dmx_tspp_info.tsif[tsif].channel_ref;
	data_cnt = &mpq_dmx_tspp_info.tsif[tsif].data_cnt;

	/* check if required TSPP pipe is already allocated or not */
	if (*channel_ref_count == 0) {
		/* invalid feed provided as the channel is not allocated */
		MPQ_DVB_ERR_PRINT(
			"%s: invalid feed (%d)\n",
			__func__,
			channel_id);

		ret = -EINVAL;
		goto remove_channel_failed;
	}

	tspp_filter.priority = mpq_tspp_get_filter_slot(tsif, feed->pid);

	if (tspp_filter.priority < 0) {
		/* invalid feed provided as it has no filter allocated */
		MPQ_DVB_ERR_PRINT(
			"%s: mpq_tspp_get_filter_slot failed (%d,%d)\n",
			__func__,
			feed->pid,
			tsif);

		ret = -EINVAL;
		goto remove_channel_failed;
	}

	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count--;

	if (mpq_dmx_tspp_info.tsif[tsif].
		filters[tspp_filter.priority].ref_count) {
		/*
		 * there are still references to this pid, do not
		 * remove the filter yet
		 */
		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
		return 0;
	}

	ret = tspp_remove_filter(0, channel_id, &tspp_filter);
	if (ret < 0) {
		/* invalid feed provided as it has no filter allocated */
		MPQ_DVB_ERR_PRINT(
			"%s: tspp_remove_filter failed (%d,%d)\n",
			__func__,
			channel_id,
			tspp_filter.priority);

		goto remove_channel_failed_restore_count;
	}

	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].pid = -1;
	(*channel_ref_count)--;

	if (*channel_ref_count == 0) {
		/* channel is not used any more, release it */
		tspp_unregister_notification(0, channel_id);
		tspp_close_channel(0, channel_id);
		tspp_close_stream(0, channel_id);
		atomic_set(data_cnt, 0);

		if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC)
			mpq_dmx_channel_mem_free(tsif);
	}

	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
	return 0;

remove_channel_failed_restore_count:
	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count++;

remove_channel_failed:
	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
	return ret;
}
/**
 * Configure TSPP channel to filter the PID of new feed.
 *
 * @feed: The feed to configure the channel with
 *
 * Return  error status
 *
 * The function checks if the new PID can be added to an already
 * allocated channel, if not, a new channel is allocated and configured.
 */
static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed)
{
	struct mpq_demux *mpq_demux = feed->demux->priv;
	struct tspp_select_source tspp_source;
	struct tspp_filter tspp_filter;
	int tsif;
	int ret;
	int channel_id;
	int *channel_ref_count;
	u32 buffer_size;

	tspp_source.clk_inverse = clock_inv;
	tspp_source.data_inverse = 0;
	tspp_source.sync_inverse = 0;
	tspp_source.enable_inverse = 0;

	switch (tsif_mode) {
	case 1:
		tspp_source.mode = TSPP_TSIF_MODE_1;
		break;
	case 2:
		tspp_source.mode = TSPP_TSIF_MODE_2;
		break;
	default:
		tspp_source.mode = TSPP_TSIF_MODE_LOOPBACK;
		break;
	}

	/* determine the TSIF we are reading from */
	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
		tsif = 0;
		tspp_source.source = TSPP_SOURCE_TSIF0;
	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
		tsif = 1;
		tspp_source.source = TSPP_SOURCE_TSIF1;
	} else {
		/* invalid source */
		MPQ_DVB_ERR_PRINT(
			"%s: invalid input source (%d)\n",
			__func__,
			mpq_demux->source);

		return -EINVAL;
	}

	if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
		return -ERESTARTSYS;

	/*
	 * It is possible that this PID was already requested before.
	 * Can happen if we play and record same PES or PCR
	 * piggypacked on video packet.
	 */
	ret = mpq_tspp_get_filter_slot(tsif, feed->pid);
	if (ret >= 0) {
		/* PID already configured */
		mpq_dmx_tspp_info.tsif[tsif].filters[ret].ref_count++;
		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
		return 0;
	}

	channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL);
	channel_ref_count = &mpq_dmx_tspp_info.tsif[tsif].channel_ref;
	buffer_size = TSPP_DESCRIPTOR_SIZE;

	/* check if required TSPP pipe is already allocated or not */
	if (*channel_ref_count == 0) {
		if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
			ret = mpq_dmx_channel_mem_alloc(tsif);
			if (ret < 0) {
				MPQ_DVB_ERR_PRINT(
					"%s: mpq_dmx_channel_mem_alloc(%d) failed (%d)\n",
					__func__,
					channel_id,
					ret);

				goto add_channel_failed;
			}
		}

		ret = tspp_open_channel(0, channel_id);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: tspp_open_channel(%d) failed (%d)\n",
				__func__,
				channel_id,
				ret);

			goto add_channel_failed;
		}

		/* set TSPP source */
		ret = tspp_open_stream(0, channel_id, &tspp_source);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: tspp_select_source(%d,%d) failed (%d)\n",
				__func__,
				channel_id,
				tspp_source.source,
				ret);

			goto add_channel_close_ch;
		}

		/* register notification on TS packets */
		tspp_register_notification(0,
					   channel_id,
					   mpq_tspp_callback,
					   (void *)tsif,
					   tspp_channel_timeout);

		/* register allocator and provide allocation function
		 * that allocates from contiguous memory so that we can have
		 * big notification size, smallest descriptor, and still provide
		 * TZ with single big buffer based on notification size.
		 */
		if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
			ret = tspp_allocate_buffers(0, channel_id,
				   mpq_dmx_tspp_info.tsif[tsif].buffer_count,
				   buffer_size, tspp_notification_size,
				   tspp_mem_allocator, tspp_mem_free, NULL);
		} else {
			ret = tspp_allocate_buffers(0, channel_id,
				   mpq_dmx_tspp_info.tsif[tsif].buffer_count,
				   buffer_size, tspp_notification_size,
				   NULL, NULL, NULL);
		}
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: tspp_allocate_buffers(%d) failed (%d)\n",
				__func__,
				channel_id,
				ret);

			goto add_channel_unregister_notif;
		}

		mpq_dmx_tspp_info.tsif[tsif].mpq_demux = mpq_demux;
	}

	/* add new PID to the existing pipe */
	ret = mpq_tspp_get_free_filter_slot(tsif, channel_id);
	if (ret < 0) {
		MPQ_DVB_ERR_PRINT(
			"%s: mpq_allocate_filter_slot(%d, %d) failed\n",
			__func__,
			tsif,
			channel_id);

		goto add_channel_unregister_notif;
	}

	mpq_dmx_tspp_info.tsif[tsif].filters[ret].pid = feed->pid;
	mpq_dmx_tspp_info.tsif[tsif].filters[ret].ref_count++;

	tspp_filter.priority = ret;
	if (feed->pid == TSPP_PASS_THROUGH_PID) {
		/* pass all pids */
		tspp_filter.pid = 0;
		tspp_filter.mask = 0;
	} else {
		tspp_filter.pid = feed->pid;
		tspp_filter.mask = TSPP_PID_MASK;
	}

	/*
	 * Include TTS in RAW packets, if you change this to
	 * TSPP_MODE_RAW_NO_SUFFIX you must also change TSPP_RAW_TTS_SIZE
	 * accordingly.
	 */
	tspp_filter.mode = TSPP_MODE_RAW;
	tspp_filter.source = tspp_source.source;
	tspp_filter.decrypt = 0;
	ret = tspp_add_filter(0, channel_id, &tspp_filter);
	if (ret < 0) {
		MPQ_DVB_ERR_PRINT(
			"%s: tspp_add_filter(%d) failed (%d)\n",
			__func__,
			channel_id,
			ret);

		goto add_channel_free_filter_slot;
	}

	(*channel_ref_count)++;

	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
	return 0;

add_channel_free_filter_slot:
	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].pid = -1;
	mpq_dmx_tspp_info.tsif[tsif].filters[tspp_filter.priority].ref_count--;
add_channel_unregister_notif:
	tspp_unregister_notification(0, channel_id);
add_channel_close_ch:
	tspp_close_channel(0, channel_id);
add_channel_failed:
	if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC)
		mpq_dmx_channel_mem_free(tsif);

	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
	return ret;
}
static int mpq_tspp_dmx_add_channel(struct dvb_demux_feed *feed)
{
	struct mpq_demux *mpq_demux = feed->demux->priv;
	struct tspp_select_source tspp_source;
	struct tspp_filter tspp_filter;
	int tsif;
	int ret = 0;
	int slot;
	int channel_id;
	int *channel_ref_count;
	u32 buffer_size;
	int restore_user_filters = 0;
	int remove_accept_all_filter = 0;
	int remove_null_blocking_filters = 0;

	tspp_source.clk_inverse = clock_inv;
	tspp_source.data_inverse = 0;
	tspp_source.sync_inverse = 0;
	tspp_source.enable_inverse = 0;

	MPQ_DVB_DBG_PRINT("%s: executed, PID = %d\n", __func__, feed->pid);

	switch (tsif_mode) {
	case 1:
		tspp_source.mode = TSPP_TSIF_MODE_1;
		break;
	case 2:
		tspp_source.mode = TSPP_TSIF_MODE_2;
		break;
	default:
		tspp_source.mode = TSPP_TSIF_MODE_LOOPBACK;
		break;
	}

	
	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
		tsif = 0;
		tspp_source.source = TSPP_SOURCE_TSIF0;
	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
		tsif = 1;
		tspp_source.source = TSPP_SOURCE_TSIF1;
	} else {
		
		MPQ_DVB_ERR_PRINT(
			"%s: invalid input source (%d)\n",
			__func__,
			mpq_demux->source);

		return -EINVAL;
	}

	if (mutex_lock_interruptible(&mpq_dmx_tspp_info.tsif[tsif].mutex))
		return -ERESTARTSYS;

	slot = mpq_tspp_get_filter_slot(tsif, feed->pid);
	if (slot >= 0) {
		
		mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count++;
		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
		return 0;
	}

	channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL);
	channel_ref_count = &mpq_dmx_tspp_info.tsif[tsif].channel_ref;
	buffer_size = TSPP_DESCRIPTOR_SIZE;

	
	if (*channel_ref_count == 0) {
		if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
			ret = mpq_dmx_channel_mem_alloc(tsif);
			if (ret < 0) {
				MPQ_DVB_ERR_PRINT(
					"%s: mpq_dmx_channel_mem_alloc(%d) failed (%d)\n",
					__func__,
					channel_id,
					ret);

				goto add_channel_failed;
			}
		}

		ret = tspp_open_channel(0, channel_id);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: tspp_open_channel(%d) failed (%d)\n",
				__func__,
				channel_id,
				ret);

			goto add_channel_failed;
		}

		
		ret = tspp_open_stream(0, channel_id, &tspp_source);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: tspp_select_source(%d,%d) failed (%d)\n",
				__func__,
				channel_id,
				tspp_source.source,
				ret);

			goto add_channel_close_ch;
		}

		
		tspp_register_notification(0,
					   channel_id,
					   mpq_tspp_callback,
					   (void *)tsif,
					   tspp_channel_timeout);

		if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC) {
			ret = tspp_allocate_buffers(0, channel_id,
				   mpq_dmx_tspp_info.tsif[tsif].buffer_count,
				   buffer_size, tspp_notification_size,
				   tspp_mem_allocator, tspp_mem_free, NULL);
		} else {
			ret = tspp_allocate_buffers(0, channel_id,
				   mpq_dmx_tspp_info.tsif[tsif].buffer_count,
				   buffer_size, tspp_notification_size,
				   NULL, NULL, NULL);
		}
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: tspp_allocate_buffers(%d) failed (%d)\n",
				__func__,
				channel_id,
				ret);

			goto add_channel_unregister_notif;
		}

		mpq_dmx_tspp_info.tsif[tsif].mpq_demux = mpq_demux;
	}

	
	slot = mpq_tspp_get_free_filter_slot(tsif);
	if (slot < 0) {
		MPQ_DVB_ERR_PRINT(
			"%s: mpq_tspp_get_free_filter_slot(%d) failed\n",
			__func__, tsif);

		goto add_channel_unregister_notif;
	}

	if (feed->pid == TSPP_PASS_THROUGH_PID)
		mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 1;
	else if (feed->pid == TSPP_NULL_PACKETS_PID)
		mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 1;

	mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = feed->pid;
	mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count++;

	tspp_filter.priority = -1;

	if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count <
					TSPP_MAX_HW_PID_FILTER_NUM) {
		
		tspp_filter.priority = mpq_tspp_allocate_hw_filter_index(tsif);
		if (tspp_filter.priority < 0)
			goto add_channel_free_filter_slot;

		if (feed->pid == TSPP_PASS_THROUGH_PID) {
			
			tspp_filter.pid = 0;
			tspp_filter.mask = 0;
		} else {
			tspp_filter.pid = feed->pid;
			tspp_filter.mask = TSPP_PID_MASK;
		}

		tspp_filter.mode = TSPP_MODE_RAW;
		tspp_filter.source = tspp_source.source;
		tspp_filter.decrypt = 0;
		ret = tspp_add_filter(0, channel_id, &tspp_filter);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: tspp_add_filter(%d) failed (%d)\n",
				__func__,
				channel_id,
				ret);

			goto add_channel_free_filter_slot;
		}
		mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index =
			tspp_filter.priority;

		MPQ_DVB_DBG_PRINT(
			"%s: HW filtering mode: added TSPP HW filter, PID = %d, mask = 0x%X, index = %d\n",
			__func__, tspp_filter.pid, tspp_filter.mask,
			tspp_filter.priority);
	} else if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count ==
					TSPP_MAX_HW_PID_FILTER_NUM) {
		

		
		ret = mpq_tspp_add_accept_all_filter(channel_id,
					tspp_source.source);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: mpq_tspp_add_accept_all_filter(%d, %d) failed\n",
				__func__, channel_id, tspp_source.source);

			goto add_channel_free_filter_slot;
		}

		
		ret = mpq_tspp_remove_all_user_filters(channel_id,
					tspp_source.source);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: mpq_tspp_remove_all_user_filters(%d, %d) failed\n",
				__func__, channel_id, tspp_source.source);

			restore_user_filters = 1;
			remove_accept_all_filter = 1;

			goto add_channel_free_filter_slot;
		}

		
		ret = mpq_tspp_add_null_blocking_filters(channel_id,
					tspp_source.source);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: mpq_tspp_add_null_blocking_filters(%d, %d) failed\n",
				__func__, channel_id, tspp_source.source);

			restore_user_filters = 1;
			remove_accept_all_filter = 1;

			goto add_channel_free_filter_slot;
		}

		
		if ((mpq_dmx_tspp_info.tsif[tsif].pass_all_flag == 0) &&
			(mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag == 0)) {

			ret = mpq_tspp_remove_accept_all_filter(channel_id,
						tspp_source.source);
			if (ret < 0) {
				MPQ_DVB_ERR_PRINT(
					"%s: mpq_tspp_remove_accept_all_filter(%d, %d) failed\n",
					__func__, channel_id,
					tspp_source.source);

				remove_null_blocking_filters = 1;
				restore_user_filters = 1;
				remove_accept_all_filter = 1;

				goto add_channel_free_filter_slot;
			}
		}
	} else {
		
		if (mpq_dmx_tspp_info.tsif[tsif].pass_all_flag ||
			mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag) {

			ret = mpq_tspp_add_accept_all_filter(channel_id,
						tspp_source.source);
			if (ret < 0) {
				MPQ_DVB_ERR_PRINT(
					"%s: mpq_tspp_add_accept_all_filter(%d, %d) failed\n",
					__func__, channel_id,
					tspp_source.source);

				goto add_channel_free_filter_slot;
			}
		}
	}

	(*channel_ref_count)++;
	mpq_dmx_tspp_info.tsif[tsif].current_filter_count++;

	MPQ_DVB_DBG_PRINT("%s: success, current_filter_count = %d\n",
		__func__, mpq_dmx_tspp_info.tsif[tsif].current_filter_count);

	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
	return 0;

add_channel_free_filter_slot:
	
	mpq_dmx_tspp_info.tsif[tsif].filters[slot].pid = -1;
	mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count--;

	
	if (tspp_filter.priority >= 0) {
		mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index = -1;
		mpq_tspp_release_hw_filter_index(tsif, tspp_filter.priority);
	}

	
	if (remove_null_blocking_filters)
		mpq_tspp_remove_null_blocking_filters(channel_id,
						tspp_source.source);

	if (restore_user_filters)
		mpq_tspp_add_all_user_filters(channel_id, tspp_source.source);

	if (remove_accept_all_filter)
		mpq_tspp_remove_accept_all_filter(channel_id,
						tspp_source.source);

	
	if (feed->pid == TSPP_PASS_THROUGH_PID)
		mpq_dmx_tspp_info.tsif[tsif].pass_all_flag = 0;
	else if (feed->pid == TSPP_NULL_PACKETS_PID)
		mpq_dmx_tspp_info.tsif[tsif].pass_nulls_flag = 0;

add_channel_unregister_notif:
	if (*channel_ref_count == 0) {
		tspp_unregister_notification(0, channel_id);
		tspp_close_stream(0, channel_id);
	}
add_channel_close_ch:
	if (*channel_ref_count == 0)
		tspp_close_channel(0, channel_id);
add_channel_failed:
	if (*channel_ref_count == 0)
		if (allocation_mode == MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC)
			mpq_dmx_channel_mem_free(tsif);

	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
	return ret;
}
/**
 * Demux thread function handling data from specific TSIF.
 *
 * @arg: TSIF number
 */
static int mpq_dmx_tspp_thread(void *arg)
{
	int tsif = (int)arg;
	struct mpq_demux *mpq_demux;
	const struct tspp_data_descriptor *tspp_data_desc;
	atomic_t *data_cnt;
	u32 notif_size;
	int channel_id;
	int ref_count;
	int ret;
	int j;

	do {
		ret = wait_event_interruptible(
			mpq_dmx_tspp_info.tsif[tsif].wait_queue,
			atomic_read(&mpq_dmx_tspp_info.tsif[tsif].data_cnt) ||
			kthread_should_stop());

		if ((ret < 0) || kthread_should_stop()) {
			MPQ_DVB_ERR_PRINT("%s: exit\n", __func__);
			break;
		}

		/* Lock against the TSPP filters data-structure */
		if (mutex_lock_interruptible(
			&mpq_dmx_tspp_info.tsif[tsif].mutex))
			return -ERESTARTSYS;

		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL);

		ref_count = mpq_dmx_tspp_info.tsif[tsif].channel_ref;
		data_cnt = &mpq_dmx_tspp_info.tsif[tsif].data_cnt;

		/* Make sure channel is still active */
		if (ref_count == 0) {
			mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
			continue;
		}

		atomic_dec(data_cnt);

		mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
		mpq_demux->hw_notification_size = 0;

		if (MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC != allocation_mode &&
			mpq_sdmx_is_loaded())
			pr_err_once(
				"%s: TSPP Allocation mode does not support secure demux.\n",
				__func__);

		if (MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC == allocation_mode &&
			mpq_sdmx_is_loaded()) {
			mpq_dmx_tspp_aggregated_process(tsif, channel_id);
		} else {
			/*
			 * Go through all filled descriptors
			 * and perform demuxing on them
			 */
			while ((tspp_data_desc = tspp_get_buffer(0, channel_id))
					!= NULL) {
				notif_size = tspp_data_desc->size /
					TSPP_RAW_TTS_SIZE;
				mpq_demux->hw_notification_size += notif_size;

				for (j = 0; j < notif_size; j++)
					dvb_dmx_swfilter_packet(
					 &mpq_demux->demux,
					 ((u8 *)tspp_data_desc->virt_base) +
					 j * TSPP_RAW_TTS_SIZE,
					 ((u8 *)tspp_data_desc->virt_base) +
					 j * TSPP_RAW_TTS_SIZE + TSPP_RAW_SIZE);
				/*
				 * Notify TSPP that the buffer
				 * is no longer needed
				 */
				tspp_release_buffer(0, channel_id,
					tspp_data_desc->id);
			}
		}

		if (mpq_demux->hw_notification_size &&
			(mpq_demux->hw_notification_size <
			mpq_demux->hw_notification_min_size))
			mpq_demux->hw_notification_min_size =
				mpq_demux->hw_notification_size;

		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
	} while (1);

	return 0;
}
static int mpq_tsif_dmx_start(struct mpq_demux *mpq_demux)
{
	int ret = 0;
	int tsif;
	struct tsif_driver_info *tsif_driver;

	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);

	
	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
		tsif = 0;
	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
		tsif = 1;
	} else {
		
		MPQ_DVB_ERR_PRINT(
			"%s: invalid input source (%d)\n",
			__func__,
			mpq_demux->source);

		return -EINVAL;
	}

	if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
		return -ERESTARTSYS;

	if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) {
		tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);

		
		tsif_driver->tsif_handler =
			tsif_attach(tsif, mpq_tsif_callback, (void *)tsif);
		if (IS_ERR_OR_NULL(tsif_driver->tsif_handler)) {
			tsif_driver->tsif_handler = NULL;
			mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
			MPQ_DVB_DBG_PRINT("%s: tsif_attach(%d) failed\n",
					__func__, tsif);
			return -ENODEV;
		}

		ret = tsif_set_clk_inverse(tsif_driver->tsif_handler,
					clock_inv);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: tsif_set_clk_inverse (%d) failed\n",
				__func__, clock_inv);
		}

		
		ret = tsif_set_mode(tsif_driver->tsif_handler, tsif_mode);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT("%s: tsif_set_mode (%d) failed\n",
				__func__, tsif_mode);
		}

		
		ret = tsif_set_buf_config(tsif_driver->tsif_handler,
						threshold,
						DMX_TSIF_CHUNKS_IN_BUF);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: tsif_set_buf_config (%d, %d) failed\n",
				__func__, threshold,
				DMX_TSIF_CHUNKS_IN_BUF);
			MPQ_DVB_ERR_PRINT("Using default TSIF driver values\n");
		}

		
		ret = tsif_start(tsif_driver->tsif_handler);
		if (ret < 0) {
			mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
			MPQ_DVB_ERR_PRINT("%s: tsif_start failed\n", __func__);
			return ret;
		}

		tsif_get_info(tsif_driver->tsif_handler,
				&(tsif_driver->data_buffer),
				&(tsif_driver->buffer_size));

		
		mpq_dmx_tsif_info.tsif[tsif].mpq_demux = mpq_demux;
	}
	mpq_dmx_tsif_info.tsif[tsif].ref_count++;

	mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);

	return ret;
}
static int mpq_tsif_dmx_init(
		struct dvb_adapter *mpq_adapter,
		struct mpq_demux *mpq_demux)
{
	int result;

	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);

	
	mpq_demux->demux.dmx.capabilities =
		DMX_TS_FILTERING		|
		DMX_PES_FILTERING		|
		DMX_SECTION_FILTERING		|
		DMX_MEMORY_BASED_FILTERING	|
		DMX_CRC_CHECKING		|
		DMX_TS_DESCRAMBLING;

	
	mpq_demux->demux.priv = (void *)mpq_demux;
	mpq_demux->demux.filternum = DMX_TSIF_MAX_SECTION_FILTER_NUM;
	mpq_demux->demux.feednum = MPQ_MAX_DMX_FILES;
	mpq_demux->demux.start_feed = mpq_tsif_dmx_start_filtering;
	mpq_demux->demux.stop_feed = mpq_tsif_dmx_stop_filtering;
	mpq_demux->demux.write_to_decoder = mpq_tsif_dmx_write_to_decoder;
	mpq_demux->demux.decoder_fullness_init = mpq_dmx_decoder_fullness_init;
	mpq_demux->demux.decoder_fullness_wait = mpq_dmx_decoder_fullness_wait;
	mpq_demux->demux.decoder_fullness_abort =
		mpq_dmx_decoder_fullness_abort;
	mpq_demux->demux.decoder_buffer_status = mpq_dmx_decoder_buffer_status;
	mpq_demux->demux.reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer;
	mpq_demux->demux.set_secure_mode = NULL;
	mpq_demux->demux.oob_command = mpq_dmx_oob_command;
	mpq_demux->demux.convert_ts = mpq_dmx_convert_tts;

	
	result = dvb_dmx_init(&mpq_demux->demux);
	if (result < 0) {
		MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed\n", __func__);
		goto init_failed;
	}

	
	mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES;
	mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
	mpq_demux->dmxdev.capabilities = DMXDEV_CAP_DUPLEX;

	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
	mpq_demux->dmxdev.demux->get_stc = mpq_tsif_dmx_get_stc;
	mpq_demux->dmxdev.demux->get_caps = mpq_tsif_dmx_get_caps;
	mpq_demux->dmxdev.demux->map_buffer = mpq_dmx_map_buffer;
	mpq_demux->dmxdev.demux->unmap_buffer = mpq_dmx_unmap_buffer;

	result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
	if (result < 0) {
		MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed (errno=%d)\n",
						  __func__,
						  result);
		goto init_failed_dmx_release;
	}

	
	mpq_dmx_init_debugfs_entries(mpq_demux);

	return 0;

init_failed_dmx_release:
	dvb_dmx_release(&mpq_demux->demux);
init_failed:
	return result;
}
/**
 * Attach to TSIF driver and start TSIF operation.
 *
 * @mpq_demux: the mpq_demux we are working on.
 *
 * Return	error code.
 */
static int mpq_tsif_dmx_start(struct mpq_demux *mpq_demux)
{
	int ret = 0;
	int tsif;
	struct tsif_driver_info *tsif_driver;

	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);

	/* determine the TSIF we are reading from */
	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
		tsif = 0;
	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
		tsif = 1;
	} else {
		/* invalid source */
		MPQ_DVB_ERR_PRINT(
			"%s: invalid input source (%d)\n",
			__func__,
			mpq_demux->source);

		return -EINVAL;
	}

	if (mutex_lock_interruptible(&mpq_dmx_tsif_info.tsif[tsif].mutex))
		return -ERESTARTSYS;

	if (mpq_dmx_tsif_info.tsif[tsif].ref_count == 0) {
		tsif_driver = &(mpq_dmx_tsif_info.tsif[tsif].tsif_driver);

		/* Attach to TSIF driver */
		tsif_driver->tsif_handler =
			tsif_attach(tsif, mpq_tsif_callback, (void *)tsif);
		if (IS_ERR_OR_NULL(tsif_driver->tsif_handler)) {
			tsif_driver->tsif_handler = NULL;
			mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
			MPQ_DVB_DBG_PRINT("%s: tsif_attach(%d) failed\n",
					__func__, tsif);
			return -ENODEV;
		}

		ret = tsif_set_clk_inverse(tsif_driver->tsif_handler,
					clock_inv);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: tsif_set_clk_inverse (%d) failed\n",
				__func__, clock_inv);
		}

		/* Set TSIF driver mode */
		ret = tsif_set_mode(tsif_driver->tsif_handler, tsif_mode);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT("%s: tsif_set_mode (%d) failed\n",
				__func__, tsif_mode);
		}

		/* Set TSIF buffer configuration */
		ret = tsif_set_buf_config(tsif_driver->tsif_handler,
						threshold,
						DMX_TSIF_CHUNKS_IN_BUF);
		if (ret < 0) {
			MPQ_DVB_ERR_PRINT(
				"%s: tsif_set_buf_config (%d, %d) failed\n",
				__func__, threshold,
				DMX_TSIF_CHUNKS_IN_BUF);
			MPQ_DVB_ERR_PRINT("Using default TSIF driver values\n");
		}

		/* Start TSIF driver */
		ret = tsif_start(tsif_driver->tsif_handler);
		if (ret < 0) {
			mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);
			MPQ_DVB_ERR_PRINT("%s: tsif_start failed\n", __func__);
			return ret;
		}

		/*
		 * Get data buffer information from TSIF driver
		 * (must be called after tsif_start)
		 */
		tsif_get_info(tsif_driver->tsif_handler,
				&(tsif_driver->data_buffer),
				&(tsif_driver->buffer_size));

		/* save pointer to the mpq_demux we are working on */
		mpq_dmx_tsif_info.tsif[tsif].mpq_demux = mpq_demux;
	}
	mpq_dmx_tsif_info.tsif[tsif].ref_count++;

	mutex_unlock(&mpq_dmx_tsif_info.tsif[tsif].mutex);

	return ret;
}
static int mpq_sw_dmx_init(struct dvb_adapter *mpq_adapter,
		struct mpq_demux *mpq_demux)
{
	int ret;
	struct dvb_demux *dvb_demux = &mpq_demux->demux;

	/* Set the kernel-demux object capabilities */
	mpq_demux->demux.dmx.capabilities =
		DMX_TS_FILTERING			|
		DMX_PES_FILTERING			|
		DMX_SECTION_FILTERING			|
		DMX_MEMORY_BASED_FILTERING		|
		DMX_CRC_CHECKING			|
		DMX_TS_DESCRAMBLING;

	mpq_demux->decoder_alloc_flags = ION_FLAG_CACHED;

	/* Set dvb-demux "virtual" function pointers */
	dvb_demux->priv = (void *)mpq_demux;
	dvb_demux->filternum = MPQ_MAX_DMX_FILES;
	dvb_demux->feednum = MPQ_MAX_DMX_FILES;
	dvb_demux->start_feed = mpq_sw_dmx_start_filtering;
	dvb_demux->stop_feed = mpq_sw_dmx_stop_filtering;
	dvb_demux->write_to_decoder = mpq_sw_dmx_write_to_decoder;
	dvb_demux->decoder_fullness_init = mpq_dmx_decoder_fullness_init;
	dvb_demux->decoder_fullness_wait = mpq_dmx_decoder_fullness_wait;
	dvb_demux->decoder_fullness_abort = mpq_dmx_decoder_fullness_abort;
	dvb_demux->decoder_buffer_status = mpq_dmx_decoder_buffer_status;
	dvb_demux->reuse_decoder_buffer = mpq_dmx_reuse_decoder_buffer;
	dvb_demux->set_cipher_op = mpq_dmx_set_cipher_ops;
	dvb_demux->oob_command = mpq_dmx_oob_command;
	dvb_demux->convert_ts = mpq_dmx_convert_tts;
	dvb_demux->flush_decoder_buffer = NULL;

	/* Initialize dvb_demux object */
	ret = dvb_dmx_init(dvb_demux);
	if (ret) {
		MPQ_DVB_ERR_PRINT("%s: dvb_dmx_init failed, ret=%d\n",
			__func__, ret);
		goto init_failed;
	}

	/* Now initialize the dmx-dev object */
	mpq_demux->dmxdev.filternum = MPQ_MAX_DMX_FILES;
	mpq_demux->dmxdev.demux = &mpq_demux->demux.dmx;
	mpq_demux->dmxdev.capabilities = DMXDEV_CAP_DUPLEX;

	mpq_demux->dmxdev.demux->set_source = mpq_sw_dmx_set_source;
	mpq_demux->dmxdev.demux->get_stc = NULL;
	mpq_demux->dmxdev.demux->get_caps = mpq_sw_dmx_get_caps;
	mpq_demux->dmxdev.demux->map_buffer = mpq_dmx_map_buffer;
	mpq_demux->dmxdev.demux->unmap_buffer = mpq_dmx_unmap_buffer;
	mpq_demux->dmxdev.demux->write = mpq_dmx_write;
	ret = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
	if (ret) {
		MPQ_DVB_ERR_PRINT("%s: dvb_dmxdev_init failed, ret=%d\n",
			__func__, ret);
		goto init_failed_dmx_release;
	}

	/* Extend dvb-demux debugfs with mpq demux statistics. */
	mpq_dmx_init_debugfs_entries(mpq_demux);

	return 0;

init_failed_dmx_release:
	dvb_dmx_release(dvb_demux);
init_failed:
	return ret;
}
/**
 * Module initialization function.
 *
 * Return	error code
 */
static int __init mpq_dmx_tsif_plugin_init(void)
{
	int i;
	int ret;

	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);

	/* check module parameters validity */
	if (threshold < 1) {
		MPQ_DVB_ERR_PRINT(
			"%s: invalid threshold parameter, using %d instead\n",
			__func__, DMX_TSIF_PACKETS_IN_CHUNK_DEF);
		threshold = DMX_TSIF_PACKETS_IN_CHUNK_DEF;
	}
	if ((tsif_mode < 1) || (tsif_mode > 3)) {
		MPQ_DVB_ERR_PRINT(
			"%s: invalid mode parameter, using %d instead\n",
			__func__, DMX_TSIF_DRIVER_MODE_DEF);
		tsif_mode = DMX_TSIF_DRIVER_MODE_DEF;
	}

	for (i = 0; i < TSIF_COUNT; i++) {
		snprintf(mpq_dmx_tsif_info.tsif[i].name,
				TSIF_NAME_LENGTH,
				"dmx_tsif%d",
				i);

		atomic_set(&mpq_dmx_tsif_info.tsif[i].data_cnt, 0);
		init_waitqueue_head(&mpq_dmx_tsif_info.tsif[i].wait_queue);
		mpq_dmx_tsif_info.tsif[i].thread =
			kthread_run(
				mpq_dmx_tsif_thread, (void *)i,
				mpq_dmx_tsif_info.tsif[i].name);

		if (IS_ERR(mpq_dmx_tsif_info.tsif[i].thread)) {
			int j;

			for (j = 0; j < i; j++) {
				kthread_stop(mpq_dmx_tsif_info.tsif[j].thread);
				mutex_destroy(&mpq_dmx_tsif_info.tsif[j].mutex);
			}

			MPQ_DVB_ERR_PRINT(
				"%s: kthread_run failed\n",
				__func__);

			return -ENOMEM;
		}

		mutex_init(&mpq_dmx_tsif_info.tsif[i].mutex);

		mpq_dmx_tsif_info.tsif[i].tsif_driver.tsif_handler = NULL;
		mpq_dmx_tsif_info.tsif[i].ref_count = 0;
	}

	ret = mpq_dmx_plugin_init(mpq_tsif_dmx_init);

	if (ret < 0) {
		MPQ_DVB_ERR_PRINT(
			"%s: mpq_dmx_plugin_init failed (errno=%d)\n",
			__func__,
			ret);

		for (i = 0; i < TSIF_COUNT; i++) {
			kthread_stop(mpq_dmx_tsif_info.tsif[i].thread);
			mutex_destroy(&mpq_dmx_tsif_info.tsif[i].mutex);
		}
	}

	return ret;
}
static int __init mpq_dmx_tspp_plugin_init(void)
{
	int i;
	int j;
	int ret;

	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);

	for (i = 0; i < TSIF_COUNT; i++) {
		mpq_dmx_tspp_info.tsif[i].buffer_count =
				TSPP_BUFFER_COUNT(tspp_out_buffer_size);

		mpq_dmx_tspp_info.tsif[i].aggregate_ids =
			vzalloc(mpq_dmx_tspp_info.tsif[i].buffer_count *
				sizeof(int));
		if (NULL == mpq_dmx_tspp_info.tsif[i].aggregate_ids) {
			MPQ_DVB_ERR_PRINT(
				"%s: Failed to allocate memory for buffer descriptors aggregation\n",
				__func__);
			for (j = 0; j < i; j++) {
				kthread_stop(mpq_dmx_tspp_info.tsif[j].thread);
				vfree(mpq_dmx_tspp_info.tsif[j].aggregate_ids);
				mutex_destroy(&mpq_dmx_tspp_info.tsif[j].mutex);
			}
			return -ENOMEM;
		}
		mpq_dmx_tspp_info.tsif[i].channel_ref = 0;
		mpq_dmx_tspp_info.tsif[i].buff_index = 0;
		mpq_dmx_tspp_info.tsif[i].ch_mem_heap_handle = NULL;
		mpq_dmx_tspp_info.tsif[i].ch_mem_heap_virt_base = NULL;
		mpq_dmx_tspp_info.tsif[i].ch_mem_heap_phys_base = 0;
		atomic_set(&mpq_dmx_tspp_info.tsif[i].data_cnt, 0);

		for (j = 0; j < TSPP_MAX_PID_FILTER_NUM; j++) {
			mpq_dmx_tspp_info.tsif[i].filters[j].pid = -1;
			mpq_dmx_tspp_info.tsif[i].filters[j].ref_count = 0;
		}

		snprintf(mpq_dmx_tspp_info.tsif[i].name,
				TSIF_NAME_LENGTH,
				"dmx_tsif%d",
				i);

		init_waitqueue_head(&mpq_dmx_tspp_info.tsif[i].wait_queue);
		mpq_dmx_tspp_info.tsif[i].thread =
			kthread_run(
				mpq_dmx_tspp_thread, (void *)i,
				mpq_dmx_tspp_info.tsif[i].name);

		if (IS_ERR(mpq_dmx_tspp_info.tsif[i].thread)) {
			vfree(mpq_dmx_tspp_info.tsif[i].aggregate_ids);

			for (j = 0; j < i; j++) {
				kthread_stop(mpq_dmx_tspp_info.tsif[j].thread);
				vfree(mpq_dmx_tspp_info.tsif[j].aggregate_ids);
				mutex_destroy(&mpq_dmx_tspp_info.tsif[j].mutex);
			}

			MPQ_DVB_ERR_PRINT(
				"%s: kthread_run failed\n",
				__func__);

			return -ENOMEM;
		}

		mutex_init(&mpq_dmx_tspp_info.tsif[i].mutex);
	}

	ret = mpq_dmx_plugin_init(mpq_tspp_dmx_init);

	if (ret < 0) {
		MPQ_DVB_ERR_PRINT(
			"%s: mpq_dmx_plugin_init failed (errno=%d)\n",
			__func__,
			ret);

		for (i = 0; i < TSIF_COUNT; i++) {
			kthread_stop(mpq_dmx_tspp_info.tsif[i].thread);
			vfree(mpq_dmx_tspp_info.tsif[i].aggregate_ids);
			mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
		}
	}

	return ret;
}
static int mpq_dmx_tspp_thread(void *arg)
{
	int tsif = (int)arg;
	struct mpq_demux *mpq_demux;
	const struct tspp_data_descriptor *tspp_data_desc;
	atomic_t *data_cnt;
	u32 notif_size;
	int channel_id;
	int ref_count;
	int ret;

	do {
		ret = wait_event_interruptible(
			mpq_dmx_tspp_info.tsif[tsif].wait_queue,
			atomic_read(&mpq_dmx_tspp_info.tsif[tsif].data_cnt) ||
			kthread_should_stop());

		if ((ret < 0) || kthread_should_stop()) {
			MPQ_DVB_ERR_PRINT("%s: exit\n", __func__);
			break;
		}

		
		if (mutex_lock_interruptible(
			&mpq_dmx_tspp_info.tsif[tsif].mutex))
			return -ERESTARTSYS;

		channel_id = TSPP_CHANNEL_ID(tsif, TSPP_CHANNEL);

		ref_count = mpq_dmx_tspp_info.tsif[tsif].channel_ref;
		data_cnt = &mpq_dmx_tspp_info.tsif[tsif].data_cnt;

		
		if (ref_count == 0) {
			mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
			continue;
		}

		atomic_dec(data_cnt);

		mpq_demux = mpq_dmx_tspp_info.tsif[tsif].mpq_demux;
		mpq_demux->hw_notification_size = 0;

		if (MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC != allocation_mode &&
			mpq_sdmx_is_loaded())
			pr_err_once(
				"%s: TSPP Allocation mode does not support secure demux.\n",
				__func__);

		if (MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC == allocation_mode &&
			mpq_sdmx_is_loaded()) {
			mpq_dmx_tspp_aggregated_process(tsif, channel_id);
		} else {
			while ((tspp_data_desc = tspp_get_buffer(0, channel_id))
					!= NULL) {
				notif_size = tspp_data_desc->size /
					TSPP_RAW_TTS_SIZE;
				mpq_demux->hw_notification_size += notif_size;

				mpq_dmx_tspp_swfilter_desc(mpq_demux,
					tspp_data_desc);
				tspp_release_buffer(0, channel_id,
					tspp_data_desc->id);
			}
		}

		if (mpq_demux->hw_notification_size &&
			(mpq_demux->hw_notification_size <
			mpq_demux->hw_notification_min_size))
			mpq_demux->hw_notification_min_size =
				mpq_demux->hw_notification_size;

		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
	} while (1);

	return 0;
}