media: dvb: mpq: Support new elementary stream data events

Video elemenatry stream data is usually passed from demux to a video
consumer (e.g. video decoder) in kernel-space via tunneling. However
there are cases when the video consumer is a user-space application
controlled by the middleware. This change adds support for notifying
user-space about newly available elementary stream data and
associated meta-data, using the existing events queue mechanism.

Change-Id: I6d104c6ad7ccc791b29b75e7bf376c985416585e
Signed-off-by: Liron Kuch <lkuch@codeaurora.org>
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index ed657d7..042ba37 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -64,14 +64,15 @@
 
 enum dmx_success {
 	DMX_OK = 0, /* Received Ok */
-	DMX_OK_PES_END, /* Received ok, data reached end of PES packet */
+	DMX_OK_PES_END, /* Received OK, data reached end of PES packet */
 	DMX_OK_PCR, /* Received OK, data with new PCR/STC pair */
 	DMX_LENGTH_ERROR, /* Incorrect length */
 	DMX_OVERRUN_ERROR, /* Receiver ring buffer overrun */
 	DMX_CRC_ERROR, /* Incorrect CRC */
 	DMX_FRAME_ERROR, /* Frame alignment error */
 	DMX_FIFO_ERROR, /* Receiver FIFO overrun */
-	DMX_MISSED_ERROR /* Receiver missed packet */
+	DMX_MISSED_ERROR, /* Receiver missed packet */
+	DMX_OK_DECODER_BUF /* Received OK, new ES data in decoder buffer */
 } ;
 
 
@@ -106,6 +107,21 @@
 			u64 stc;
 			int disc_indicator_set;
 		} pcr;
+
+		struct {
+			int handle;
+			int cookie;
+			u32 offset;
+			u32 len;
+			int pts_exists;
+			u64 pts;
+			int dts_exists;
+			u64 dts;
+			u32 tei_counter;
+			u32 cont_err_counter;
+			u32 ts_packets_num;
+			u32 ts_dropped_bytes;
+		} buf;
 	};
 };
 
@@ -202,6 +218,9 @@
 	int (*get_decoder_buff_status)(
 			struct dmx_ts_feed *feed,
 			struct dmx_buffer_status *dmx_buffer_status);
+	int (*reuse_decoder_buffer)(
+			struct dmx_ts_feed *feed,
+			int cookie);
 	int (*data_ready_cb)(struct dmx_ts_feed *feed,
 			dmx_ts_data_ready_cb callback);
 	int (*notify_data_read)(struct dmx_ts_feed *feed,
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index e956170..2c3afd6 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -1395,6 +1395,29 @@
 	return 0;
 }
 
+static int dvb_dmxdev_reuse_decoder_buf(struct dmxdev_filter *dmxdevfilter,
+						int cookie)
+{
+	if ((dmxdevfilter->type == DMXDEV_TYPE_PES) &&
+		(dmxdevfilter->params.pes.output == DMX_OUT_DECODER)) {
+		struct dmxdev_feed *feed;
+		int ret = -ENODEV;
+
+		/* Only one feed should be in the list in case of decoder */
+		feed = list_first_entry(&dmxdevfilter->feed.ts,
+					struct dmxdev_feed, next);
+
+		if (feed->ts->reuse_decoder_buffer)
+			ret = feed->ts->reuse_decoder_buffer(
+							feed->ts,
+							cookie);
+
+		return ret;
+	}
+
+	return -EPERM;
+}
+
 static int dvb_dmxdev_ts_fullness_callback(
 				struct dmx_ts_feed *filter,
 				int required_space)
@@ -1535,9 +1558,12 @@
 {
 	struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
 
-
-	spin_lock_irq(&dmxdevfilter->dev->lock);
-
+	/*
+	 * Note: Taking the dmxdevfilter->dev->lock spinlock is required only
+	 * when getting the status of the Demux-userspace data ringbuffer .
+	 * In case we are getting the status of a decoder buffer, taking this
+	 * spinlock is not required and in fact might lead to a deadlock.
+	 */
 	if ((dmxdevfilter->type == DMXDEV_TYPE_PES) &&
 		(dmxdevfilter->params.pes.output == DMX_OUT_DECODER)) {
 		struct dmxdev_feed *feed;
@@ -1555,10 +1581,11 @@
 		else
 			ret = -ENODEV;
 
-		spin_unlock_irq(&dmxdevfilter->dev->lock);
 		return ret;
 	}
 
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+
 	if (!buf->data) {
 		spin_unlock_irq(&dmxdevfilter->dev->lock);
 		return -EINVAL;
@@ -1629,6 +1656,10 @@
 	spin_lock_irq(&dmxdevfilter->dev->lock);
 
 	res = dvb_dmxdev_remove_event(&dmxdevfilter->events, event);
+	if (res) {
+		spin_unlock_irq(&dmxdevfilter->dev->lock);
+		return res;
+	}
 
 	if (event->type == DMX_EVENT_BUFFER_OVERFLOW) {
 		/*
@@ -1645,6 +1676,15 @@
 		dmxdevfilter->buffer.error = 0;
 	}
 
+	/*
+	 * Decoder filters have no data in the data buffer and their
+	 * events can be removed now from the queue.
+	 */
+	if (dmxdevfilter->params.pes.output == DMX_OUT_DECODER)
+		dmxdevfilter->events.read_index =
+			dvb_dmxdev_advance_event_idx(
+				dmxdevfilter->events.read_index);
+
 	spin_unlock_irq(&dmxdevfilter->dev->lock);
 
 	/*
@@ -1980,7 +2020,7 @@
 		event.params.pcr.stc = dmx_data_ready->pcr.stc;
 		if (dmx_data_ready->pcr.disc_indicator_set)
 			event.params.pcr.flags =
-				DMX_FILTER_DISCONTINUITY_INDEICATOR;
+				DMX_FILTER_DISCONTINUITY_INDICATOR;
 		else
 			event.params.pcr.flags = 0;
 
@@ -1990,6 +2030,30 @@
 		return 0;
 	}
 
+	if (dmx_data_ready->status == DMX_OK_DECODER_BUF) {
+		event.type = DMX_EVENT_NEW_ES_DATA;
+		event.params.es_data.buf_handle = dmx_data_ready->buf.handle;
+		event.params.es_data.cookie = dmx_data_ready->buf.cookie;
+		event.params.es_data.offset = dmx_data_ready->buf.offset;
+		event.params.es_data.data_len = dmx_data_ready->buf.len;
+		event.params.es_data.pts_valid = dmx_data_ready->buf.pts_exists;
+		event.params.es_data.pts = dmx_data_ready->buf.pts;
+		event.params.es_data.dts_valid = dmx_data_ready->buf.dts_exists;
+		event.params.es_data.dts = dmx_data_ready->buf.dts;
+		event.params.es_data.transport_error_indicator_counter =
+				dmx_data_ready->buf.tei_counter;
+		event.params.es_data.continuity_error_counter =
+				dmx_data_ready->buf.cont_err_counter;
+		event.params.es_data.ts_packets_num =
+				dmx_data_ready->buf.ts_packets_num;
+		event.params.es_data.ts_dropped_bytes =
+				dmx_data_ready->buf.ts_dropped_bytes;
+		dvb_dmxdev_add_event(events, &event);
+		spin_unlock(&dmxdevfilter->dev->lock);
+		wake_up_all(&buffer->queue);
+		return 0;
+	}
+
 	if ((dmxdevfilter->params.pes.output == DMX_OUT_DECODER) ||
 		(buffer->error)) {
 		spin_unlock(&dmxdevfilter->dev->lock);
@@ -2038,7 +2102,7 @@
 			event.params.pes.flags = 0;
 			if (dmx_data_ready->pes_end.disc_indicator_set)
 				event.params.pes.flags |=
-					DMX_FILTER_DISCONTINUITY_INDEICATOR;
+					DMX_FILTER_DISCONTINUITY_INDICATOR;
 			if (dmx_data_ready->pes_end.pes_length_mismatch)
 				event.params.pes.flags |=
 					DMX_FILTER_PES_LENGTH_ERROR;
@@ -3017,6 +3081,15 @@
 		mutex_unlock(&dmxdevfilter->mutex);
 		break;
 
+	case DMX_REUSE_DECODER_BUFFER:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_reuse_decoder_buf(dmxdevfilter, arg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
 	default:
 		ret = -EINVAL;
 		break;
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index 978cb38d..eea83c2 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -1128,21 +1128,47 @@
 	struct dvb_demux *demux = feed->demux;
 	int ret;
 
-	spin_lock_irq(&demux->lock);
+	mutex_lock(&demux->mutex);
 
 	if (feed->state < DMX_STATE_GO) {
-		spin_unlock_irq(&demux->lock);
+		mutex_unlock(&demux->mutex);
 		return -EINVAL;
 	}
 
 	if (!demux->decoder_buffer_status) {
-		spin_unlock_irq(&demux->lock);
+		mutex_unlock(&demux->mutex);
 		return -ENODEV;
 	}
 
 	ret = demux->decoder_buffer_status(feed, dmx_buffer_status);
 
-	spin_unlock_irq(&demux->lock);
+	mutex_unlock(&demux->mutex);
+
+	return ret;
+}
+
+static int dmx_ts_feed_reuse_decoder_buffer(struct dmx_ts_feed *ts_feed,
+						int cookie)
+{
+	struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed;
+	struct dvb_demux *demux = feed->demux;
+	int ret;
+
+	mutex_lock(&demux->mutex);
+
+	if (feed->state < DMX_STATE_GO) {
+		mutex_unlock(&demux->mutex);
+		return -EINVAL;
+	}
+
+	if (!demux->reuse_decoder_buffer) {
+		mutex_unlock(&demux->mutex);
+		return -ENODEV;
+	}
+
+	ret = demux->reuse_decoder_buffer(feed, cookie);
+
+	mutex_unlock(&demux->mutex);
 
 	return ret;
 }
@@ -1239,6 +1265,7 @@
 	(*ts_feed)->set_indexing_params = dmx_ts_set_indexing_params;
 	(*ts_feed)->set_tsp_out_format = dmx_ts_set_tsp_out_format;
 	(*ts_feed)->get_decoder_buff_status = dmx_ts_feed_decoder_buff_status;
+	(*ts_feed)->reuse_decoder_buffer = dmx_ts_feed_reuse_decoder_buffer;
 	(*ts_feed)->data_ready_cb = dmx_ts_feed_data_ready_cb;
 	(*ts_feed)->notify_data_read = NULL;
 
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index f89367b..2e4a468 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -127,6 +127,8 @@
 	int (*decoder_fullness_abort)(struct dvb_demux_feed *feed);
 	int (*decoder_buffer_status)(struct dvb_demux_feed *feed,
 				struct dmx_buffer_status *dmx_buffer_status);
+	int (*reuse_decoder_buffer)(struct dvb_demux_feed *feed,
+				int cookie);
 	u32 (*check_crc32)(struct dvb_demux_feed *feed,
 			    const u8 *buf, size_t len);
 	void (*memcopy)(struct dvb_demux_feed *feed, u8 *dst,
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
index 94fa1b7..722e317 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -39,11 +39,15 @@
 static int mpq_demux_device_num = CONFIG_DVB_MPQ_NUM_DMX_DEVICES;
 module_param(mpq_demux_device_num, int, S_IRUGO);
 
-/* ION head ID to be used when calling ion_alloc for video decoder buffer */
+/* ION heap ID to be used when calling ion_alloc for video decoder buffer */
 static int video_ion_alloc_heap = ION_CP_MM_HEAP_ID;
 module_param(video_ion_alloc_heap, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(video_ion_alloc_heap, "ION heap ID for allocation");
 
+static int generate_es_events;
+module_param(generate_es_events, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(generate_es_events, "Generate new elementary stream data events");
+
 /**
  * Maximum allowed framing pattern size
  */
@@ -923,6 +927,62 @@
 }
 EXPORT_SYMBOL(mpq_dmx_unmap_buffer);
 
+int mpq_dmx_reuse_decoder_buffer(struct dvb_demux_feed *feed, int cookie)
+{
+	struct mpq_demux *mpq_demux = feed->demux->priv;
+
+	if (!generate_es_events) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: Cannot release decoder buffer when not working with new elementary stream data events\n",
+			__func__);
+		return -EPERM;
+	}
+
+	if (cookie < 0) {
+		MPQ_DVB_ERR_PRINT("%s: invalid cookie parameter\n", __func__);
+		return -EINVAL;
+	}
+
+	if (mpq_dmx_is_video_feed(feed)) {
+		struct mpq_video_feed_info *feed_data;
+		struct mpq_streambuffer *stream_buffer;
+		int ret;
+
+		spin_lock(&mpq_demux->feed_lock);
+
+		if (feed->priv == NULL) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: invalid feed, feed->priv is NULL\n",
+				__func__);
+			spin_unlock(&mpq_demux->feed_lock);
+			return -EINVAL;
+		}
+
+		feed_data = feed->priv;
+		stream_buffer = feed_data->video_buffer;
+		if (stream_buffer == NULL) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: invalid feed, feed_data->video_buffer is NULL\n",
+				__func__);
+			spin_unlock(&mpq_demux->feed_lock);
+			return -EINVAL;
+		}
+
+		ret = mpq_streambuffer_pkt_dispose(stream_buffer, cookie, 1);
+
+		spin_unlock(&mpq_demux->feed_lock);
+
+		return ret;
+	}
+
+	/* else */
+	MPQ_DVB_ERR_PRINT("%s: Invalid feed type %d\n",
+			__func__, feed->pes_type);
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(mpq_dmx_reuse_decoder_buffer);
+
 /**
  * Handles the details of internal decoder buffer allocation via ION.
  * Internal helper function.
@@ -977,23 +1037,11 @@
 	feed_data->buffer_desc.desc[0].size = actual_buffer_size;
 	feed_data->buffer_desc.desc[0].read_ptr = 0;
 	feed_data->buffer_desc.desc[0].write_ptr = 0;
-	feed_data->buffer_desc.desc[0].handle =
-		ion_share_dma_buf(
-			client,
-			temp_handle);
-	if (IS_ERR_VALUE(feed_data->buffer_desc.desc[0].handle)) {
-		MPQ_DVB_ERR_PRINT(
-			"%s: FAILED to share payload buffer %d\n",
-			__func__, ret);
-		ret = -ENOMEM;
-		goto init_failed_unmap_payload_buffer;
-	}
+	/* TODO: share handle using ion_share_dma_buf */
+	feed_data->buffer_desc.desc[0].handle = -1;
 
 	return 0;
 
-init_failed_unmap_payload_buffer:
-	ion_unmap_kernel(client, temp_handle);
-	feed_data->buffer_desc.desc[0].base = NULL;
 init_failed_free_payload_buffer:
 	ion_free(client, temp_handle);
 	feed_data->buffer_desc.ion_handle[0] = NULL;
@@ -1290,6 +1338,12 @@
 	feed_data->saved_info_used = 1;
 	feed_data->new_info_exists = 0;
 	feed_data->first_pts_dts_copy = 1;
+	feed_data->tei_errs = 0;
+	feed_data->last_continuity = -1;
+	feed_data->continuity_errs = 0;
+	feed_data->ts_packets_num = 0;
+	feed_data->ts_dropped_bytes = 0;
+	feed_data->last_pkt_index = -1;
 
 	spin_lock(&mpq_demux->feed_lock);
 	feed->priv = (void *)feed_data;
@@ -1314,7 +1368,6 @@
 	struct ion_client *client)
 {
 	int buf_num = 0;
-	struct dmx_decoder_buffers *dec_buffs = feed->feed.ts.decoder_buffers;
 	int i;
 
 	mpq_adapter_unregister_stream_if(feed_data->stream_interface);
@@ -1322,6 +1375,7 @@
 	vfree(feed_data->video_buffer->packet_data.data);
 
 	buf_num = feed_data->buffer_desc.decoder_buffers_num;
+
 	for (i = 0; i < buf_num; i++) {
 		if (feed_data->buffer_desc.ion_handle[i]) {
 			if (feed_data->buffer_desc.desc[i].base) {
@@ -1329,17 +1383,10 @@
 					feed_data->buffer_desc.ion_handle[i]);
 				feed_data->buffer_desc.desc[i].base = NULL;
 			}
+			/* TODO: un-share kernel buffer handle */
 			ion_free(client, feed_data->buffer_desc.ion_handle[i]);
 			feed_data->buffer_desc.ion_handle[i] = NULL;
 			feed_data->buffer_desc.desc[i].size = 0;
-
-			/*
-			 * Call put_unused_fd only if kernel it the one that
-			 * shared the buffer handle.
-			 */
-			if (0 == dec_buffs->buffers_num)
-				put_unused_fd(
-					feed_data->buffer_desc.desc[i].handle);
 		}
 	}
 }
@@ -1770,6 +1817,73 @@
 	return 0;
 }
 
+static void mpq_dmx_check_continuity(struct mpq_video_feed_info *feed_data,
+					int current_continuity,
+					int discontinuity_indicator)
+{
+	const int max_continuity = 0x0F; /* 4 bits in the TS packet header */
+
+	/* sanity check */
+	if (unlikely((current_continuity < 0) ||
+			(current_continuity > max_continuity))) {
+		MPQ_DVB_DBG_PRINT(
+			"%s: received invalid continuity counter value %d\n",
+					__func__, current_continuity);
+		return;
+	}
+
+	/* reset last continuity */
+	if ((feed_data->last_continuity == -1) ||
+		(discontinuity_indicator)) {
+		feed_data->last_continuity = current_continuity;
+		return;
+	}
+
+	/* check for continuity errors */
+	if (current_continuity !=
+			((feed_data->last_continuity + 1) & max_continuity))
+		feed_data->continuity_errs++;
+
+	/* save for next time */
+	feed_data->last_continuity = current_continuity;
+}
+
+static inline void mpq_dmx_prepare_es_event_data(
+			struct mpq_streambuffer_packet_header *packet,
+			struct mpq_adapter_video_meta_data *meta_data,
+			struct mpq_video_feed_info *feed_data,
+			struct mpq_streambuffer *stream_buffer,
+			struct dmx_data_ready *data)
+{
+	size_t len = 0;
+
+	data->data_length = 0;
+	data->buf.handle = packet->raw_data_handle;
+	/* this has to succeed when called here, after packet was written */
+	data->buf.cookie = mpq_streambuffer_pkt_next(stream_buffer,
+				feed_data->last_pkt_index, &len);
+	data->buf.offset = packet->raw_data_offset;
+	data->buf.len = packet->raw_data_len;
+	data->buf.pts_exists = meta_data->info.framing.pts_dts_info.pts_exist;
+	data->buf.pts = meta_data->info.framing.pts_dts_info.pts;
+	data->buf.dts_exists = meta_data->info.framing.pts_dts_info.dts_exist;
+	data->buf.dts = meta_data->info.framing.pts_dts_info.dts;
+	data->buf.tei_counter = feed_data->tei_errs;
+	data->buf.cont_err_counter = feed_data->continuity_errs;
+	data->buf.ts_packets_num = feed_data->ts_packets_num;
+	data->buf.ts_dropped_bytes = feed_data->ts_dropped_bytes;
+	data->status = DMX_OK_DECODER_BUF;
+
+	/* save for next time: */
+	feed_data->last_pkt_index = data->buf.cookie;
+
+	/* reset counters */
+	feed_data->ts_packets_num = 0;
+	feed_data->ts_dropped_bytes = 0;
+	feed_data->tei_errs = 0;
+	feed_data->continuity_errs = 0;
+}
+
 static int mpq_dmx_process_video_packet_framing(
 			struct dvb_demux_feed *feed,
 			const u8 *buf)
@@ -1793,6 +1907,8 @@
 	int is_video_frame = 0;
 	int pending_data_len = 0;
 	int ret = 0;
+	int discontinuity_indicator = 0;
+	struct dmx_data_ready data;
 
 	mpq_demux = feed->demux->priv;
 
@@ -1854,9 +1970,18 @@
 
 	ts_payload_offset = sizeof(struct ts_packet_header);
 
-	/* Skip adaptation field if exists */
-	if (ts_header->adaptation_field_control == 3)
+	/*
+	 * Skip adaptation field if exists.
+	 * Save discontinuity indicator if exists.
+	 */
+	if (ts_header->adaptation_field_control == 3) {
+		const struct ts_adaptation_field *adaptation_field;
+		adaptation_field = (const struct ts_adaptation_field *)
+			(buf + ts_payload_offset);
+		discontinuity_indicator =
+			adaptation_field->discontinuity_indicator;
 		ts_payload_offset += buf[ts_payload_offset] + 1;
+	}
 
 	/* 188 bytes: the size of a TS packet including the TS packet header */
 	bytes_avail = 188 - ts_payload_offset;
@@ -1940,6 +2065,13 @@
 		return 0;
 	}
 
+	/* Update error counters based on TS header */
+	feed_data->ts_packets_num++;
+	feed_data->tei_errs += ts_header->transport_error_indicator;
+	mpq_dmx_check_continuity(feed_data,
+				ts_header->continuity_counter,
+				discontinuity_indicator);
+
 	/*
 	 * write prefix used to find first Sequence pattern, if needed.
 	 * feed_data->patterns[0].pattern always contains the Sequence
@@ -1951,6 +2083,8 @@
 					feed_data->first_prefix_size) < 0) {
 			mpq_demux->decoder_drop_count +=
 				feed_data->first_prefix_size;
+			feed_data->ts_dropped_bytes +=
+				feed_data->first_prefix_size;
 			MPQ_DVB_DBG_PRINT("%s: could not write prefix\n",
 				__func__);
 		} else {
@@ -2003,6 +2137,7 @@
 			(buf + ts_payload_offset + bytes_written),
 			bytes_to_write) < 0) {
 			mpq_demux->decoder_drop_count += bytes_to_write;
+			feed_data->ts_dropped_bytes += bytes_to_write;
 			MPQ_DVB_DBG_PRINT(
 				"%s: Couldn't write %d bytes to data buffer\n",
 				__func__, bytes_to_write);
@@ -2043,6 +2178,15 @@
 					"Should never happen\n",
 					__func__);
 			}
+
+			if (generate_es_events) {
+				mpq_dmx_prepare_es_event_data(
+					&packet, &meta_data, feed_data,
+					stream_buffer, &data);
+
+				feed->data_ready_cb.ts(&feed->feed.ts, &data);
+			}
+
 			feed_data->pending_pattern_len = 0;
 			mpq_streambuffer_get_data_rw_offset(
 				feed_data->video_buffer,
@@ -2064,6 +2208,7 @@
 			pending_data_len);
 		if (ret < 0) {
 			mpq_demux->decoder_drop_count += pending_data_len;
+			feed_data->ts_dropped_bytes += pending_data_len;
 			MPQ_DVB_DBG_PRINT(
 				"%s: Couldn't write %d bytes to data buffer\n",
 				__func__, pending_data_len);
@@ -2087,6 +2232,8 @@
 	struct mpq_streambuffer *stream_buffer;
 	struct pes_packet_header *pes_header;
 	struct mpq_demux *mpq_demux;
+	int discontinuity_indicator = 0;
+	struct dmx_data_ready data;
 
 	mpq_demux = feed->demux->priv;
 
@@ -2100,7 +2247,7 @@
 
 	ts_header = (const struct ts_packet_header *)buf;
 
-	stream_buffer =	feed_data->video_buffer;
+	stream_buffer = feed_data->video_buffer;
 
 	pes_header = &feed_data->pes_header;
 
@@ -2153,11 +2300,22 @@
 						"Couldn't write packet. "
 						"Should never happen\n",
 						__func__);
+
 				/* Save write offset where new PES will begin */
 				mpq_streambuffer_get_data_rw_offset(
 					stream_buffer,
 					NULL,
 					&feed_data->frame_offset);
+
+				if (generate_es_events) {
+					mpq_dmx_prepare_es_event_data(
+						&packet, &meta_data,
+						feed_data,
+						stream_buffer, &data);
+
+					feed->data_ready_cb.ts(
+						&feed->feed.ts, &data);
+				}
 			} else {
 				MPQ_DVB_ERR_PRINT(
 					"%s: received PUSI"
@@ -2191,9 +2349,18 @@
 
 	ts_payload_offset = sizeof(struct ts_packet_header);
 
-	/* Skip adaptation field if exists */
-	if (ts_header->adaptation_field_control == 3)
+	/*
+	 * Skip adaptation field if exists.
+	 * Save discontinuity indicator if exists.
+	 */
+	if (ts_header->adaptation_field_control == 3) {
+		const struct ts_adaptation_field *adaptation_field;
+		adaptation_field = (const struct ts_adaptation_field *)
+			(buf + ts_payload_offset);
+		discontinuity_indicator =
+			adaptation_field->discontinuity_indicator;
 		ts_payload_offset += buf[ts_payload_offset] + 1;
+	}
 
 	/* 188 bytes: size of a TS packet including the TS packet header */
 	bytes_avail = 188 - ts_payload_offset;
@@ -2224,13 +2391,22 @@
 		return 0;
 	}
 
+	/* Update error counters based on TS header */
+	feed_data->ts_packets_num++;
+	feed_data->tei_errs += ts_header->transport_error_indicator;
+	mpq_dmx_check_continuity(feed_data,
+				ts_header->continuity_counter,
+				discontinuity_indicator);
+
 	if (mpq_streambuffer_data_write(
 				stream_buffer,
 				buf+ts_payload_offset,
-				bytes_avail) < 0)
+				bytes_avail) < 0) {
 		mpq_demux->decoder_drop_count += bytes_avail;
-	else
+		feed_data->ts_dropped_bytes += bytes_avail;
+	} else {
 		feed->peslen += bytes_avail;
+	}
 
 	spin_unlock(&mpq_demux->feed_lock);
 
@@ -2261,8 +2437,13 @@
 		spin_unlock(&mpq_demux->feed_lock);
 		return -EINVAL;
 	}
+
 	feed_data = feed->priv;
 	video_buff = feed_data->video_buffer;
+	if (!video_buff) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return -EINVAL;
+	}
 
 	dmx_buffer_status->error = video_buff->raw_data.error;
 
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
index 744900a..ff196d6 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
@@ -333,6 +333,15 @@
  * new_pts_dts_info that should be saved to saved_pts_dts_info.
  * @first_pts_dts_copy: a flag used to indicate if PTS/DTS information needs
  * to be copied from the currently parsed PES header to the saved_pts_dts_info.
+ * @tei_errs: Transport stream Transport Error Indicator (TEI) counter.
+ * @last_continuity: last continuity counter value found in TS packet header.
+ * Initialized to -1.
+ * @continuity_errs: Transport stream continuity error counter.
+ * @ts_packets_num: TS packets counter.
+ * @ts_dropped_bytes: counts the number of bytes dropped due to insufficient
+ * buffer space.
+ * @last_pkt_index: used to save the last streambuffer packet index reported in
+ * a new elementary stream data event.
  */
 struct mpq_video_feed_info {
 	void *plugin_data;
@@ -358,6 +367,12 @@
 	int saved_info_used;
 	int new_info_exists;
 	int first_pts_dts_copy;
+	u32 tei_errs;
+	int last_continuity;
+	u32 continuity_errs;
+	u32 ts_packets_num;
+	u32 ts_dropped_bytes;
+	int last_pkt_index;
 };
 
 /**
@@ -500,6 +515,20 @@
 		struct dmx_buffer_status *dmx_buffer_status);
 
 /**
+ * mpq_dmx_reuse_decoder_buffer - release buffer passed to decoder for reuse
+ * by the stream-buffer.
+ *
+ * @feed: The decoder's feed.
+ * @cookie: stream-buffer handle of the buffer.
+ *
+ * Return     error code
+ *
+ * The function releases the buffer provided by the stream-buffer
+ * connected to the decoder back to the stream-buffer for reuse.
+ */
+int mpq_dmx_reuse_decoder_buffer(struct dvb_demux_feed *feed, int cookie);
+
+/**
  * mpq_dmx_process_video_packet - Assemble PES data and output it
  * to the stream-buffer connected to the decoder.
  *
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
index c5c3518..3ad3d21 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
@@ -668,6 +668,9 @@
 	mpq_demux->demux.decoder_buffer_status =
 		mpq_dmx_decoder_buffer_status;
 
+	mpq_demux->demux.reuse_decoder_buffer =
+		mpq_dmx_reuse_decoder_buffer;
+
 	/* Initialize dvb_demux object */
 	result = dvb_dmx_init(&mpq_demux->demux);
 	if (result < 0) {
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
index 61c1761..46be260 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v1.c
@@ -1090,6 +1090,9 @@
 	mpq_demux->demux.decoder_buffer_status =
 		mpq_dmx_decoder_buffer_status;
 
+	mpq_demux->demux.reuse_decoder_buffer =
+		mpq_dmx_reuse_decoder_buffer;
+
 	/* Initialize dvb_demux object */
 	result = dvb_dmx_init(&mpq_demux->demux);
 	if (result < 0) {
diff --git a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
index e4858fa..4211d6c 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tspp_v2.c
@@ -125,6 +125,7 @@
 	mpq_demux->demux.decoder_fullness_wait = NULL;
 	mpq_demux->demux.decoder_fullness_abort = NULL;
 	mpq_demux->demux.decoder_buffer_status = NULL;
+	mpq_demux->demux.reuse_decoder_buffer = NULL;
 
 	/* Initialize dvb_demux object */
 	result = dvb_dmx_init(&mpq_demux->demux);
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h
index fd4447f..460cba3 100644
--- a/include/linux/dvb/dmx.h
+++ b/include/linux/dvb/dmx.h
@@ -208,7 +208,10 @@
 	DMX_EVENT_SECTION_CRC_ERROR,
 
 	/* End-of-stream, no more data from this filter */
-	DMX_EVENT_EOS
+	DMX_EVENT_EOS,
+
+	/* New Elementary Stream data is ready */
+	DMX_EVENT_NEW_ES_DATA
 };
 
 /* Flags passed in filter events */
@@ -217,7 +220,7 @@
 #define DMX_FILTER_CC_ERROR			0x01
 
 /* Discontinuity indicator was set */
-#define DMX_FILTER_DISCONTINUITY_INDEICATOR	0x02
+#define DMX_FILTER_DISCONTINUITY_INDICATOR	0x02
 
 /* PES legnth in PES header is not correct */
 #define DMX_FILTER_PES_LENGTH_ERROR		0x04
@@ -296,6 +299,57 @@
 };
 
 /*
+ * Elementary stream data information associated
+ * with DMX_EVENT_NEW_ES_DATA event
+ */
+struct dmx_es_data_event_info {
+	/* Buffer user-space handle */
+	int buf_handle;
+
+	/*
+	 * Cookie to provide when releasing the buffer
+	 * using the DMX_RELEASE_DECODER_BUFFER ioctl command
+	 */
+	int cookie;
+
+	/* Offset of data from the beginning of the buffer */
+	__u32 offset;
+
+	/* Length of data in buffer (in bytes) */
+	__u32 data_len;
+
+	/* Indication whether PTS value is valid */
+	int pts_valid;
+
+	/* PTS value associated with the buffer */
+	__u64 pts;
+
+	/* Indication whether DTS value is valid */
+	int dts_valid;
+
+	/* DTS value associated with the buffer */
+	__u64 dts;
+
+	/*
+	 * Number of TS packets with Transport Error Indicator (TEI) set
+	 * in the TS packet header since last reported event
+	 */
+	__u32 transport_error_indicator_counter;
+
+	/* Number of continuity errors since last reported event */
+	__u32 continuity_error_counter;
+
+	/* Total number of TS packets processed since last reported event */
+	__u32 ts_packets_num;
+
+	/*
+	 * Number of dropped bytes due to insufficient buffer space,
+	 * since last reported event
+	 */
+	__u32 ts_dropped_bytes;
+};
+
+/*
  * Filter's event returned through DMX_GET_EVENT.
  * poll with POLLPRI would block until events are available.
  */
@@ -307,6 +361,7 @@
 		struct dmx_section_event_info section;
 		struct dmx_rec_chunk_event_info recording_chunk;
 		struct dmx_pcr_event_info pcr;
+		struct dmx_es_data_event_info es_data;
 	} params;
 };
 
@@ -525,10 +580,10 @@
 #define DMX_RELEASE_DATA		 _IO('o', 57)
 #define DMX_FEED_DATA			 _IO('o', 58)
 #define DMX_SET_PLAYBACK_MODE	 _IOW('o', 59, enum dmx_playback_mode_t)
-#define DMX_GET_EVENT			 _IOR('o', 60, struct dmx_filter_event)
-#define DMX_SET_BUFFER_MODE		 _IOW('o', 61, enum dmx_buffer_mode)
-#define DMX_SET_BUFFER			 _IOW('o', 62, struct dmx_buffer)
+#define DMX_GET_EVENT		 _IOR('o', 60, struct dmx_filter_event)
+#define DMX_SET_BUFFER_MODE	 _IOW('o', 61, enum dmx_buffer_mode)
+#define DMX_SET_BUFFER		 _IOW('o', 62, struct dmx_buffer)
 #define DMX_SET_DECODER_BUFFER	 _IOW('o', 63, struct dmx_decoder_buffers)
-
+#define DMX_REUSE_DECODER_BUFFER _IO('o', 64)
 
 #endif /*_DVBDMX_H_*/