/**
 * 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;
}
コード例 #2
0
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;
}
/**
 * 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;
}
コード例 #4
0
static int mpq_tspp_dmx_remove_channel(struct dvb_demux_feed *feed)
{
	int tsif;
	int ret;
	int channel_id;
	int slot;
	atomic_t *data_cnt;
	int *channel_ref_count;
	enum tspp_source tspp_source;
	struct tspp_filter tspp_filter;
	struct mpq_demux *mpq_demux = feed->demux->priv;
	int restore_null_blocking_filters = 0;
	int remove_accept_all_filter = 0;
	int remove_user_filters = 0;
	int accept_all_filter_existed = 0;

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

	
	if (mpq_demux->source == DMX_SOURCE_FRONT0) {
		tsif = 0;
		tspp_source = TSPP_SOURCE_TSIF0;
	} else if (mpq_demux->source == DMX_SOURCE_FRONT1) {
		tsif = 1;
		tspp_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;

	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;

	
	if (*channel_ref_count == 0) {
		
		MPQ_DVB_ERR_PRINT(
			"%s: invalid feed (%d)\n",
			__func__,
			channel_id);

		ret = -EINVAL;
		goto remove_channel_failed;
	}

	slot = mpq_tspp_get_filter_slot(tsif, feed->pid);

	if (slot < 0) {
		
		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[slot].ref_count--;

	if (mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count) {
		mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
		return 0;
	}

	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;

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

	if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count <=
					TSPP_MAX_HW_PID_FILTER_NUM) {
		
		tspp_filter.priority =
			mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index;
		ret = tspp_remove_filter(0, channel_id, &tspp_filter);
		if (ret < 0) {
			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_tspp_release_hw_filter_index(tsif, tspp_filter.priority);
		mpq_dmx_tspp_info.tsif[tsif].filters[slot].hw_index = -1;

		MPQ_DVB_DBG_PRINT(
			"%s: HW filtering mode: Removed TSPP HW filter, PID = %d, index = %d\n",
			__func__, feed->pid, tspp_filter.priority);
	} else  if (mpq_dmx_tspp_info.tsif[tsif].current_filter_count ==
					(TSPP_MAX_HW_PID_FILTER_NUM + 1)) {
		

		accept_all_filter_existed =
			mpq_dmx_tspp_info.tsif[tsif].
				accept_all_filter_exists_flag;

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

			goto remove_channel_failed_restore_count;
		}

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

			restore_null_blocking_filters = 1;
			if (!accept_all_filter_existed)
				remove_accept_all_filter = 1;

			goto remove_channel_failed_restore_count;
		}

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

			remove_user_filters = 1;
			restore_null_blocking_filters = 1;
			if (!accept_all_filter_existed)
				remove_accept_all_filter = 1;

			goto remove_channel_failed_restore_count;
		}

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

			remove_user_filters = 1;
			restore_null_blocking_filters = 1;
			if (!accept_all_filter_existed)
				remove_accept_all_filter = 1;

			goto remove_channel_failed_restore_count;
		}
	} else {
		
		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);
			if (ret < 0) {
				MPQ_DVB_ERR_PRINT(
					"%s: mpq_tspp_remove_accept_all_filter(%d, %d) failed\n",
					__func__, channel_id,
					tspp_source);

				goto remove_channel_failed_restore_count;
			}
		}
	}

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

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

	if (*channel_ref_count == 0) {
		
		tspp_unregister_notification(0, channel_id);
		tspp_close_stream(0, channel_id);
		tspp_close_channel(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[slot].pid = feed->pid;
	mpq_dmx_tspp_info.tsif[tsif].filters[slot].ref_count++;

	if (remove_user_filters)
		mpq_tspp_remove_all_user_filters(channel_id, tspp_source);

	if (restore_null_blocking_filters)
		mpq_tspp_add_null_blocking_filters(channel_id, tspp_source);

	if (remove_accept_all_filter)
		mpq_tspp_remove_accept_all_filter(channel_id, tspp_source);

	
	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;

remove_channel_failed:
	mutex_unlock(&mpq_dmx_tspp_info.tsif[tsif].mutex);
	return ret;
}