static int mpq_tspp_remove_accept_all_filter(int channel_id,
				enum tspp_source source)
{
	struct tspp_filter tspp_filter;
	int tsif = TSPP_GET_TSIF_NUM(channel_id);
	int ret;

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

	if (mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag == 0) {
		MPQ_DVB_DBG_PRINT("%s: accept all filter doesn't exist\n",
				__func__);
		return 0;
	}

	tspp_filter.priority = TSPP_LAST_HW_FILTER_INDEX;

	ret = tspp_remove_filter(0, channel_id, &tspp_filter);
	if (!ret) {
		mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag = 0;
		MPQ_DVB_DBG_PRINT(
			"%s: accept all filter removed successfully\n",
			__func__);
	}

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

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

	if (mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag) {
		MPQ_DVB_DBG_PRINT("%s: accept all filter already exists\n",
				__func__);
		return 0;
	}

	
	tspp_filter.priority = TSPP_LAST_HW_FILTER_INDEX;
	
	tspp_filter.pid = 0;
	tspp_filter.mask = 0;
	tspp_filter.mode = TSPP_MODE_RAW;
	tspp_filter.source = source;
	tspp_filter.decrypt = 0;

	ret = tspp_add_filter(0, channel_id, &tspp_filter);
	if (!ret) {
		mpq_dmx_tspp_info.tsif[tsif].accept_all_filter_exists_flag = 1;
		MPQ_DVB_DBG_PRINT(
				"%s: accept all filter added successfully\n",
				__func__);
	}

	return ret;
}
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;
}
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;
}
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;
}
static int mpq_tsif_dmx_start_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;
	}

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

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

	feed->pusi_seen = 0;

	ret = mpq_dmx_init_mpq_feed(feed);
	if (ret < 0) {
		MPQ_DVB_DBG_PRINT(
			"%s: mpq_dmx_init_mpq_feed failed(%d)\n",
			__func__,
			ret);

		if (mpq_demux->source < DMX_SOURCE_DVR0)
			mpq_tsif_dmx_stop(mpq_demux);

		return ret;
	}

	return ret;
}
/**
 * Module exit function.
 */
static void __exit mpq_dmx_tsif_plugin_exit(void)
{
	int i;
	struct tsif_driver_info *tsif_driver;

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

	for (i = 0; i < TSIF_COUNT; i++) {
		mutex_lock(&mpq_dmx_tsif_info.tsif[i].mutex);

		tsif_driver = &(mpq_dmx_tsif_info.tsif[i].tsif_driver);
		if (mpq_dmx_tsif_info.tsif[i].ref_count > 0) {
			mpq_dmx_tsif_info.tsif[i].ref_count = 0;
			if (tsif_driver->tsif_handler)
				tsif_stop(tsif_driver->tsif_handler);
		}

		/* Detach from TSIF driver to avoid further notifications. */
		if (tsif_driver->tsif_handler)
			tsif_detach(tsif_driver->tsif_handler);

		mutex_unlock(&mpq_dmx_tsif_info.tsif[i].mutex);
		kthread_stop(mpq_dmx_tsif_info.tsif[i].thread);
		mutex_destroy(&mpq_dmx_tsif_info.tsif[i].mutex);
	}

	mpq_dmx_plugin_exit();
}
static void __exit mpq_dmx_tspp_plugin_exit(void)
{
	int i;

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

	for (i = 0; i < TSIF_COUNT; i++) {
		mutex_lock(&mpq_dmx_tspp_info.tsif[i].mutex);

		/*
		 * Note: tspp_close_channel will also free the TSPP buffers
		 * even if we allocated them ourselves,
		 * using our free function.
		 */
		if (mpq_dmx_tspp_info.tsif[i].channel_ref) {
			tspp_unregister_notification(0,
				TSPP_CHANNEL_ID(i, TSPP_CHANNEL));
			tspp_close_channel(0,
				TSPP_CHANNEL_ID(i, TSPP_CHANNEL));

			if (allocation_mode ==
				MPQ_DMX_TSPP_CONTIGUOUS_PHYS_ALLOC)
				mpq_dmx_channel_mem_free(i);
		}
		if (mpq_dmx_tspp_info.tsif[i].aggregate_ids)
			vfree(mpq_dmx_tspp_info.tsif[i].aggregate_ids);

		mutex_unlock(&mpq_dmx_tspp_info.tsif[i].mutex);
		kthread_stop(mpq_dmx_tspp_info.tsif[i].thread);
		mutex_destroy(&mpq_dmx_tspp_info.tsif[i].mutex);
	}

	mpq_dmx_plugin_exit();
}
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;
}
/**
 * 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;
}
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_dmx_stop_filtering(struct dvb_demux_feed *feed)
{
	MPQ_DVB_DBG_PRINT(
		"%s(%d) executed\n",
		__func__,
		feed->pid);

	return 0;
}
Exemple #13
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_tsif_dmx_write_to_decoder(
					struct dvb_demux_feed *feed,
					const u8 *buf,
					size_t len)
{
	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);

	if (len > TSIF_PKT_SIZE)
		MPQ_DVB_DBG_PRINT(
				"%s: warnning - len larger than one packet\n",
				__func__);

	if (dvb_dmx_is_video_feed(feed))
		return mpq_dmx_process_video_packet(feed, buf);

	if (dvb_dmx_is_pcr_feed(feed))
		return mpq_dmx_process_pcr_packet(feed, buf);

	return 0;
}
Exemple #15
0
int mpq_streambuffer_pkt_write(
			struct mpq_streambuffer *sbuff,
			struct mpq_streambuffer_packet_header *packet,
			u8 *user_data)
{
	ssize_t idx;
	size_t len;

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

	MPQ_DVB_DBG_PRINT(
		"%s: handle=%d, offset=%d, len=%d\n",
		__func__,
		packet->raw_data_handle,
		packet->raw_data_offset,
		packet->raw_data_len);

	len = sizeof(struct mpq_streambuffer_packet_header) +
		packet->user_data_len;

	/* Make sure enough space available for packet header */
	if (dvb_ringbuffer_free(&sbuff->packet_data) < len)
		return -ENOSPC;

	/* Starting writing packet header */
	idx = dvb_ringbuffer_pkt_start(&sbuff->packet_data, len);

	/* Write non-user private data header */
	dvb_ringbuffer_write(&sbuff->packet_data,
		(u8 *)packet,
		sizeof(struct mpq_streambuffer_packet_header));

	/* Write user's own private data header */
	dvb_ringbuffer_write(&sbuff->packet_data,
		user_data,
		packet->user_data_len);

	dvb_ringbuffer_pkt_close(&sbuff->packet_data, idx);

	/* Move write pointer to next linear buffer for subsequent writes */
	if ((MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR == sbuff->mode) &&
		(packet->raw_data_len > 0)) {
		if (sbuff->pending_buffers_count == sbuff->buffers_num)
			return -ENOSPC;
		DVB_RINGBUFFER_PUSH(&sbuff->raw_data,
				sizeof(struct mpq_streambuffer_buffer_desc));
		sbuff->pending_buffers_count++;
	}

	wake_up_all(&sbuff->packet_data.queue);

	return 0;
}
static int mpq_tspp_dmx_start_filtering(struct dvb_demux_feed *feed)
{
	MPQ_DVB_DBG_PRINT(
		"%s(%d) executed\n",
		__func__,
		feed->pid);

	/* Always feed sections/PES starting from a new one and
	 * do not partial transfer data from older one
	 */
	feed->pusi_seen = 0;
	return 0;
}
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;
}
/**
 * Callback function from TSIF driver when new data is ready.
 *
 * @user: user-data holding TSIF number
 */
static void mpq_tsif_callback(void *user)
{
	int tsif = (int)user;
	struct mpq_demux *mpq_demux;

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

	/* Save statistics on TSIF notifications */
	mpq_demux = mpq_dmx_tsif_info.tsif[tsif].mpq_demux;
	mpq_dmx_update_hw_statistics(mpq_demux);

	atomic_inc(&mpq_dmx_tsif_info.tsif[tsif].data_cnt);
	wake_up(&mpq_dmx_tsif_info.tsif[tsif].wait_queue);
}
static int mpq_tspp_remove_all_user_filters(int channel_id,
				enum tspp_source source)
{
	struct tspp_filter tspp_filter;
	int ret = 0;
	int tsif = TSPP_GET_TSIF_NUM(channel_id);
	int i;

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

	for (i = 0; i < TSPP_MAX_HW_PID_FILTER_NUM; i++) {
		tspp_filter.priority = i;
		MPQ_DVB_DBG_PRINT("%s: Removing HW filter %d\n",
			__func__, tspp_filter.priority);
		if (tspp_remove_filter(0, channel_id, &tspp_filter))
			ret = -1;

		mpq_tspp_release_hw_filter_index(tsif, i);
		mpq_dmx_tspp_info.tsif[tsif].filters[i].hw_index = -1;
	}

	return ret;
}
/**
 * TSIF demux plugin write-to-decoder function.
 *
 * @feed: The feed we are working on.
 * @buf: The data buffer to process.
 * @len: The data buffer length.
 *
 * Return	error code
 */
static int mpq_tsif_dmx_write_to_decoder(
					struct dvb_demux_feed *feed,
					const u8 *buf,
					size_t len)
{
	MPQ_DVB_DBG_PRINT("%s executed\n", __func__);

	/*
	 * It is assumed that this function is called once for each
	 * TS packet of the relevant feed.
	 */
	if (len > TSIF_PKT_SIZE)
		MPQ_DVB_DBG_PRINT(
				"%s: warnning - len larger than one packet\n",
				__func__);

	if (dvb_dmx_is_video_feed(feed))
		return mpq_dmx_process_video_packet(feed, buf);

	if (dvb_dmx_is_pcr_feed(feed))
		return mpq_dmx_process_pcr_packet(feed, buf);

	return 0;
}
static int mpq_tspp_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);

	mpq_dmx_terminate_feed(feed);

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

	return ret;
}
/**
 * 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;
}
/**
 * Demux TS packets from TSPP by secure-demux.
 * The fucntion assumes the buffer is physically contiguous
 * and that TSPP descriptors are continuous in memory.
 *
 * @tsif: The TSIF interface to process its packets
 * @channel_id: the TSPP output pipe with the TS packets
 */
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_addr_t buff_current_addr;
	int i;

	while ((tspp_data_desc = tspp_get_buffer(0, channel_id)) != NULL) {
		if (0 == aggregate_count)
			buff_current_addr = tspp_data_desc->phys_base;
		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 +=
			tspp_data_desc->size / TSPP_RAW_TTS_SIZE;
	}

	if (!aggregate_count)
		return;

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

	MPQ_DVB_DBG_PRINT(
		"%s: Processing %d descriptors: %d bytes at start address 0x%x, read offset %d\n",
		__func__, aggregate_count, aggregate_len,
		(unsigned int)input.base_addr,
		buff_current_addr - buff_start_addr);

	mpq_sdmx_process(mpq_demux, &input, aggregate_len,
		 buff_current_addr - buff_start_addr);

	for (i = 0; i < aggregate_count; i++)
		tspp_release_buffer(0, channel_id,
			mpq_dmx_tspp_info.tsif[tsif].aggregate_ids[i]);
}
/**
 * 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;
}
/**
 * Free memory of channel output of specific TSIF.
 *
 * @tsif: The TSIF id to which memory should be freed.
 */
static void mpq_dmx_channel_mem_free(int tsif)
{
	MPQ_DVB_DBG_PRINT("%s(%d)\n", __func__, tsif);

	mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_phys_base = 0;

	if (!IS_ERR_OR_NULL(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))
			ion_unmap_kernel(mpq_dmx_tspp_info.ion_client,
				mpq_dmx_tspp_info.tsif[tsif].
					ch_mem_heap_handle);

		ion_free(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_virt_base = NULL;
	mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle = NULL;
}
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_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;
}
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;
}
/**
 * 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;
}
/**
 * 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;
}