Merge "media: dvb: External buffers support for decoder filters"
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index f802a38..ed657d7 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -188,6 +188,7 @@
 	struct dmx_demux *parent; /* Back-pointer */
 	struct data_buffer buffer;
 	void *priv; /* Pointer to private data of the API client */
+	struct dmx_decoder_buffers *decoder_buffers;
 	int (*set) (struct dmx_ts_feed *feed,
 		    u16 pid,
 		    int type,
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index 507c014..e956170 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -31,7 +31,7 @@
 #include <linux/ioctl.h>
 #include <linux/wait.h>
 #include <linux/mm.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include "dmxdev.h"
@@ -41,6 +41,8 @@
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
 
+#define DMX_DEFAULT_DECODER_BUFFER_SIZE (32768)
+
 #define dprintk	if (debug) printk
 
 static int dvb_dmxdev_buffer_write(struct dvb_ringbuffer *buf,
@@ -1351,18 +1353,27 @@
 	return 0;
 }
 
-static int dvb_dmxdev_set_pes_buffer_size(struct dmxdev_filter *dmxdevfilter,
-					unsigned long size)
+static int dvb_dmxdev_set_decoder_buffer_size(
+	struct dmxdev_filter *dmxdevfilter,
+	unsigned long size)
 {
-	if (dmxdevfilter->pes_buffer_size == size)
-		return 0;
-	if (!size)
+	if (0 == size)
 		return -EINVAL;
+
+	if (dmxdevfilter->decoder_buffers.buffers_size == size)
+		return 0;
+
 	if (dmxdevfilter->state >= DMXDEV_STATE_GO)
 		return -EBUSY;
 
-	dmxdevfilter->pes_buffer_size = size;
-
+	/*
+	 * In case decoder buffers were already set before to some external
+	 * buffers, setting the decoder buffer size alone implies transition
+	 * to internal buffer mode.
+	 */
+	dmxdevfilter->decoder_buffers.buffers_size = size;
+	dmxdevfilter->decoder_buffers.buffers_num = 0;
+	dmxdevfilter->decoder_buffers.is_linear = 0;
 	return 0;
 }
 
@@ -1524,8 +1535,6 @@
 {
 	struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
 
-	if (!buf->data)
-		return -EINVAL;
 
 	spin_lock_irq(&dmxdevfilter->dev->lock);
 
@@ -1550,6 +1559,11 @@
 		return ret;
 	}
 
+	if (!buf->data) {
+		spin_unlock_irq(&dmxdevfilter->dev->lock);
+		return -EINVAL;
+	}
+
 	dmx_buffer_status->error = buf->error;
 	if (buf->error) {
 		if (buf->error == -EOVERFLOW) {
@@ -2253,6 +2267,9 @@
 		if (!dmxdev->dvr_feeds_count)
 			dmxdev->dvr_feed = filter;
 		dmxdev->dvr_feeds_count++;
+	} else if (filter->params.pes.output == DMX_OUT_DECODER) {
+		tsfeed->decoder_buffers = &filter->decoder_buffers;
+		tsfeed->buffer.priv_handle = filter->priv_buff_handle;
 	} else {
 		tsfeed->buffer.ringbuff = &filter->buffer;
 		tsfeed->buffer.priv_handle = filter->priv_buff_handle;
@@ -2269,7 +2286,8 @@
 
 	ret = tsfeed->set(tsfeed, feed->pid,
 					ts_type, ts_pes,
-					filter->pes_buffer_size, timeout);
+					filter->decoder_buffers.buffers_size,
+					timeout);
 	if (ret < 0) {
 		dmxdev->demux->release_ts_feed(dmxdev->demux, tsfeed);
 		return ret;
@@ -2477,6 +2495,11 @@
 	mutex_init(&dmxdevfilter->mutex);
 	file->private_data = dmxdevfilter;
 
+	memset(&dmxdevfilter->decoder_buffers,
+			0,
+			sizeof(dmxdevfilter->decoder_buffers));
+	dmxdevfilter->decoder_buffers.buffers_size =
+		DMX_DEFAULT_DECODER_BUFFER_SIZE;
 	dmxdevfilter->buffer_mode = DMX_BUFFER_MODE_INTERNAL;
 	dmxdevfilter->priv_buff_handle = NULL;
 	dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
@@ -2486,10 +2509,7 @@
 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_ALLOCATED);
 	init_timer(&dmxdevfilter->timer);
 
-	dmxdevfilter->pes_buffer_size = 32768;
-
 	dmxdevfilter->dmx_tsp_format = DMX_TSP_FORMAT_188;
-
 	dvbdev->users++;
 
 	mutex_unlock(&dmxdev->mutex);
@@ -2515,7 +2535,10 @@
 			vfree(mem);
 	}
 
-	if ((dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL) &&
+	/* Decoder filters do not map buffers via priv_buff_handle */
+	if ((DMXDEV_TYPE_PES == dmxdevfilter->type) &&
+		(DMX_OUT_DECODER != dmxdevfilter->params.pes.output) &&
+		(dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL) &&
 		(dmxdevfilter->priv_buff_handle)) {
 		dmxdev->demux->unmap_buffer(dmxdev->demux,
 			dmxdevfilter->priv_buff_handle);
@@ -2653,6 +2676,47 @@
 	return 0;
 }
 
+static int dvb_dmxdev_set_decoder_buffer(struct dmxdev *dmxdev,
+		struct dmxdev_filter *filter,
+		struct dmx_decoder_buffers *buffs)
+{
+	int i;
+	struct dmx_decoder_buffers *dec_buffs;
+	struct dmx_caps caps;
+
+	if (NULL == dmxdev || NULL == filter || NULL == buffs)
+		return -EINVAL;
+
+	dec_buffs = &filter->decoder_buffers;
+	dmxdev->demux->get_caps(dmxdev->demux, &caps);
+
+	if (0 == buffs->buffers_size ||
+		(buffs->is_linear && buffs->buffers_num <= 1))
+		return -EINVAL;
+
+	if (0 == buffs->buffers_num) {
+		/* Internal mode - linear buffers not supported in this mode */
+		if (!(caps.decoder.flags & DMX_BUFFER_INTERNAL_SUPPORT) ||
+			buffs->is_linear)
+			return -EINVAL;
+	} else {
+		/* External buffer(s) mode */
+		if ((!(caps.decoder.flags & DMX_BUFFER_LINEAR_GROUP_SUPPORT) &&
+			buffs->buffers_num > 1) ||
+			!(caps.decoder.flags & DMX_BUFFER_EXTERNAL_SUPPORT) ||
+			buffs->buffers_num > caps.decoder.max_buffer_num)
+			return -EINVAL;
+
+		dec_buffs->is_linear = buffs->is_linear;
+		dec_buffs->buffers_num = buffs->buffers_num;
+		dec_buffs->buffers_size = buffs->buffers_size;
+		for (i = 0; i < dec_buffs->buffers_num; i++)
+			dec_buffs->handles[i] = buffs->handles[i];
+	}
+
+	return 0;
+}
+
 static ssize_t dvb_dmxdev_read_sec(struct dmxdev_filter *dfil,
 				   struct file *file, char __user *buf,
 				   size_t count, loff_t *ppos)
@@ -2896,7 +2960,7 @@
 			return -ERESTARTSYS;
 		}
 
-		ret = dvb_dmxdev_set_pes_buffer_size(dmxdevfilter, arg);
+		ret = dvb_dmxdev_set_decoder_buffer_size(dmxdevfilter, arg);
 		mutex_unlock(&dmxdevfilter->mutex);
 		break;
 
@@ -2944,6 +3008,15 @@
 		mutex_unlock(&dmxdevfilter->mutex);
 		break;
 
+	case DMX_SET_DECODER_BUFFER:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+		ret = dvb_dmxdev_set_decoder_buffer(dmxdev, dmxdevfilter, parg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
 	default:
 		ret = -EINVAL;
 		break;
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index d1c1cc3..0f7da1b 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -119,9 +119,6 @@
 
 	struct mutex mutex;
 
-	/* relevent for decoder PES */
-	unsigned long pes_buffer_size;
-
 	/* for recording output */
 	enum dmx_tsp_format_t dmx_tsp_format;
 	u32 rec_chunk_size;
@@ -130,6 +127,9 @@
 	struct timer_list timer;
 	int todo;
 	u8 secheader[3];
+
+	/* Decoder buffer(s) related */
+	struct dmx_decoder_buffers decoder_buffers;
 };
 
 struct dmxdev {
diff --git a/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c b/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
index f779851..6840858 100644
--- a/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
+++ b/drivers/media/dvb/mpq/adapter/mpq_stream_buffer.c
@@ -526,3 +526,34 @@
 }
 EXPORT_SYMBOL(mpq_streambuffer_data_avail);
 
+int mpq_streambuffer_get_data_rw_offset(
+	struct mpq_streambuffer *sbuff,
+	u32 *read_offset,
+	u32 *write_offset)
+{
+	if (NULL == sbuff)
+		return -EINVAL;
+
+	if (MPQ_STREAMBUFFER_BUFFER_MODE_RING == sbuff->mode) {
+		if (read_offset)
+			*read_offset = sbuff->raw_data.pread;
+		if (write_offset)
+			*write_offset = sbuff->raw_data.pwrite;
+	} else {
+		struct mpq_streambuffer_buffer_desc *desc;
+
+		if (read_offset) {
+			desc = (struct mpq_streambuffer_buffer_desc *)
+				&sbuff->raw_data.data[sbuff->raw_data.pread];
+			*read_offset = desc->read_ptr;
+		}
+		if (write_offset) {
+			desc = (struct mpq_streambuffer_buffer_desc *)
+				&sbuff->raw_data.data[sbuff->raw_data.pwrite];
+			*write_offset = desc->write_ptr;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_streambuffer_get_data_rw_offset);
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 2a60840..d766862 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -39,6 +39,11 @@
 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 */
+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");
+
 /**
  * Maximum allowed framing pattern size
  */
@@ -295,8 +300,7 @@
 					(patterns[j].size - current_size))) {
 
 					MPQ_DVB_DBG_PRINT(
-						"%s: Found matching pattern"
-						"using prefix of size %d\n",
+						"%s: Found matching pattern using prefix of size %d\n",
 						__func__, current_size);
 					/*
 					 * pattern found using prefix at the
@@ -787,14 +791,81 @@
 }
 EXPORT_SYMBOL(mpq_dmx_set_source);
 
+/**
+ * Takes an ION allocated buffer's file descriptor and handles the details of
+ * mapping it into kernel memory and obtaining an ION handle for it.
+ * Internal helper function.
+ *
+ * @client: ION client
+ * @handle: ION file descriptor to map
+ * @priv_handle: returned ION handle. Must be freed when no longer needed
+ * @kernel_mem: returned kernel mapped pointer
+ *
+ * Note: mapping might not be possible in secured heaps/buffers, and so NULL
+ * might be returned in kernel_mem
+ *
+ * Return errors status
+ */
+static int mpq_map_buffer_to_kernel(
+	struct ion_client *client,
+	int handle,
+	struct ion_handle **priv_handle,
+	void **kernel_mem)
+{
+	struct ion_handle *ion_handle;
+	unsigned long ionflag = 0;
+	int ret;
+
+	if (NULL == client || priv_handle == NULL || kernel_mem == NULL) {
+		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	ion_handle = ion_import_dma_buf(client, handle);
+	if (IS_ERR_OR_NULL(ion_handle)) {
+		ret = PTR_ERR(ion_handle);
+		MPQ_DVB_ERR_PRINT("%s: ion_import_dma_buf failed %d\n",
+			__func__, ret);
+		if (!ret)
+			ret = -ENOMEM;
+
+		goto map_buffer_failed;
+	}
+
+	ret = ion_handle_get_flags(client, ion_handle, &ionflag);
+	if (ret) {
+		MPQ_DVB_ERR_PRINT("%s: ion_handle_get_flags failed %d\n",
+			__func__, ret);
+		goto map_buffer_failed_free_buff;
+	}
+
+	if (ionflag & ION_SECURE) {
+		MPQ_DVB_DBG_PRINT("%s: secured buffer\n", __func__);
+		*kernel_mem = NULL;
+	} else {
+		*kernel_mem = ion_map_kernel(client, ion_handle);
+		if (*kernel_mem == NULL) {
+			MPQ_DVB_ERR_PRINT("%s: ion_map_kernel failed\n",
+				__func__);
+			ret = -ENOMEM;
+			goto map_buffer_failed_free_buff;
+		}
+	}
+
+	*priv_handle = ion_handle;
+	return 0;
+
+map_buffer_failed_free_buff:
+	ion_free(client, ion_handle);
+map_buffer_failed:
+	return ret;
+}
+
 int mpq_dmx_map_buffer(struct dmx_demux *demux, struct dmx_buffer *dmx_buffer,
 		void **priv_handle, void **kernel_mem)
 {
 	struct dvb_demux *dvb_demux = demux->priv;
 	struct mpq_demux *mpq_demux;
-	struct ion_handle *ion_handle;
-	unsigned long ionflag = 0;
-	int ret;
 
 	if ((mpq_dmx_info.devices == NULL) || (dvb_demux == NULL) ||
 		(priv_handle == NULL) || (kernel_mem == NULL)) {
@@ -808,47 +879,10 @@
 		return -EINVAL;
 	}
 
-	ion_handle = ion_import_dma_buf(mpq_demux->ion_client,
-					dmx_buffer->handle);
-	if (IS_ERR_OR_NULL(ion_handle)) {
-		ret = PTR_ERR(ion_handle);
-		if (!ret)
-			ret = -ENOMEM;
-
-		MPQ_DVB_ERR_PRINT("%s: ion_import_dma_buf failed %d\n",
-			__func__, ret);
-		goto map_buffer_failed;
-	}
-
-	ret = ion_handle_get_flags(mpq_demux->ion_client, ion_handle, &ionflag);
-	if (ret) {
-		MPQ_DVB_ERR_PRINT("%s: ion_handle_get_flags failed %d\n",
-			__func__, ret);
-		goto map_buffer_failed_free_buff;
-	}
-
-	if (ionflag & ION_SECURE) {
-		MPQ_DVB_DBG_PRINT("%s: secured buffer\n", __func__);
-		/* TBD: Set buffer as secured */
-		*kernel_mem = NULL;
-	} else {
-		*kernel_mem = ion_map_kernel(mpq_demux->ion_client,
-						ion_handle);
-		if (*kernel_mem == NULL) {
-			MPQ_DVB_ERR_PRINT("%s: ion_map_kernel failed\n",
-				__func__);
-			ret = -ENOMEM;
-			goto map_buffer_failed_free_buff;
-		}
-	}
-
-	*priv_handle = (void *)ion_handle;
-	return 0;
-
-map_buffer_failed_free_buff:
-	ion_free(mpq_demux->ion_client, ion_handle);
-map_buffer_failed:
-	return ret;
+	return mpq_map_buffer_to_kernel(
+		mpq_demux->ion_client,
+		dmx_buffer->handle,
+		(struct ion_handle **)priv_handle, kernel_mem);
 }
 EXPORT_SYMBOL(mpq_dmx_map_buffer);
 
@@ -889,18 +923,256 @@
 }
 EXPORT_SYMBOL(mpq_dmx_unmap_buffer);
 
+/**
+ * Handles the details of internal decoder buffer allocation via ION.
+ * Internal helper function.
+ * @feed_data: decoder feed object
+ * @dec_buffs: buffer information
+ * @client: ION client
+ *
+ * Return error status
+ */
+static int mpq_dmx_init_internal_buffers(
+	struct mpq_video_feed_info *feed_data,
+	struct dmx_decoder_buffers *dec_buffs,
+	struct ion_client *client)
+{
+	struct ion_handle *temp_handle = NULL;
+	void *payload_buffer = NULL;
+	int actual_buffer_size = 0;
+	int ret = 0;
+
+	MPQ_DVB_DBG_PRINT("%s: Internal decoder buffer allocation\n", __func__);
+
+	actual_buffer_size = dec_buffs->buffers_size;
+	actual_buffer_size += (SZ_4K - 1);
+	actual_buffer_size &= ~(SZ_4K - 1);
+
+	temp_handle = ion_alloc(client, actual_buffer_size, SZ_4K,
+		ION_HEAP(video_ion_alloc_heap), ION_FLAG_CACHED);
+
+	if (IS_ERR_OR_NULL(temp_handle)) {
+		ret = PTR_ERR(temp_handle);
+		MPQ_DVB_ERR_PRINT("%s: FAILED to allocate payload buffer %d\n",
+			__func__, ret);
+		if (!ret)
+			ret = -ENOMEM;
+		goto end;
+	}
+
+	payload_buffer = ion_map_kernel(client, temp_handle);
+
+	if (IS_ERR_OR_NULL(payload_buffer)) {
+		ret = PTR_ERR(payload_buffer);
+		MPQ_DVB_ERR_PRINT(
+			"%s: FAILED to map payload buffer %d\n",
+			__func__, ret);
+		if (!ret)
+			ret = -ENOMEM;
+		goto init_failed_free_payload_buffer;
+	}
+	feed_data->buffer_desc.decoder_buffers_num = 1;
+	feed_data->buffer_desc.ion_handle[0] = temp_handle;
+	feed_data->buffer_desc.desc[0].base = payload_buffer;
+	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;
+	}
+
+	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;
+	feed_data->buffer_desc.desc[0].size = 0;
+	feed_data->buffer_desc.decoder_buffers_num = 0;
+end:
+	return ret;
+}
+
+/**
+ * Handles the details of external decoder buffers allocated by user.
+ * Each buffer is mapped into kernel memory and an ION handle is obtained, and
+ * decoder feed object is updated with related information.
+ * Internal helper function.
+ * @feed_data: decoder feed object
+ * @dec_buffs: buffer information
+ * @client: ION client
+ *
+ * Return error status
+ */
+static int mpq_dmx_init_external_buffers(
+	struct mpq_video_feed_info *feed_data,
+	struct dmx_decoder_buffers *dec_buffs,
+	struct ion_client *client)
+{
+	struct ion_handle *temp_handle = NULL;
+	void *payload_buffer = NULL;
+	int actual_buffer_size = 0;
+	int ret = 0;
+	int i;
+
+	/*
+	 * Payload buffer was allocated externally (through ION).
+	 * Map the ion handles to kernel memory
+	 */
+	MPQ_DVB_DBG_PRINT("%s: External decoder buffer allocation\n", __func__);
+
+	actual_buffer_size = dec_buffs->buffers_size;
+	if (!dec_buffs->is_linear) {
+		MPQ_DVB_DBG_PRINT("%s: Ex. Ring-buffer\n", __func__);
+		feed_data->buffer_desc.decoder_buffers_num = 1;
+	} else {
+		MPQ_DVB_DBG_PRINT("%s: Ex. Linear\n", __func__);
+		feed_data->buffer_desc.decoder_buffers_num =
+			dec_buffs->buffers_num;
+	}
+
+	for (i = 0; i < feed_data->buffer_desc.decoder_buffers_num; i++) {
+		ret = mpq_map_buffer_to_kernel(
+			client,
+			dec_buffs->handles[i],
+			&temp_handle,
+			&payload_buffer);
+		if (ret < 0) {
+			MPQ_DVB_ERR_PRINT(
+				"%s: Failed mapping buffer %d\n",
+				__func__, i);
+			goto init_failed;
+		}
+		feed_data->buffer_desc.ion_handle[i] = temp_handle;
+		feed_data->buffer_desc.desc[i].base = payload_buffer;
+		feed_data->buffer_desc.desc[i].handle =
+			dec_buffs->handles[i];
+		feed_data->buffer_desc.desc[i].size =
+			dec_buffs->buffers_size;
+		feed_data->buffer_desc.desc[i].read_ptr = 0;
+		feed_data->buffer_desc.desc[i].write_ptr = 0;
+
+		MPQ_DVB_DBG_PRINT(
+			"%s: Buffer #%d: base=0x%p, handle=%d, size=%d\n",
+			__func__, i ,
+			feed_data->buffer_desc.desc[i].base,
+			feed_data->buffer_desc.desc[i].handle,
+			feed_data->buffer_desc.desc[i].size);
+	}
+
+	return 0;
+
+init_failed:
+	for (i = 0; i < feed_data->buffer_desc.decoder_buffers_num; i++) {
+		if (feed_data->buffer_desc.ion_handle[i]) {
+			if (feed_data->buffer_desc.desc[i].base) {
+				ion_unmap_kernel(client,
+					feed_data->buffer_desc.ion_handle[i]);
+				feed_data->buffer_desc.desc[i].base = NULL;
+			}
+			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;
+		}
+	}
+	return ret;
+}
+
+/**
+ * Handles the details of initializing the mpq_streambuffer object according
+ * to the user decoder buffer configuration: External/Internal buffers and
+ * ring/linear buffering mode.
+ * Internal helper function.
+ * @feed:  dvb demux feed object, contains the buffers configuration
+ * @feed_data: decoder feed object
+ * @stream_buffer: stream buffer object to initialize
+ *
+ * Return error status
+ */
+static int mpq_dmx_init_streambuffer(
+	struct dvb_demux_feed *feed,
+	struct mpq_video_feed_info *feed_data,
+	struct mpq_streambuffer *stream_buffer)
+{
+	int ret;
+	void *packet_buffer = NULL;
+	struct mpq_demux *mpq_demux = feed->demux->priv;
+	struct ion_client *client = mpq_demux->ion_client;
+	struct dmx_decoder_buffers *dec_buffs = NULL;
+	enum mpq_streambuffer_mode mode;
+
+	dec_buffs = feed->feed.ts.decoder_buffers;
+
+	/* Allocate packet buffer holding the meta-data */
+	packet_buffer = vmalloc(VIDEO_META_DATA_BUFFER_SIZE);
+
+	if (packet_buffer == NULL) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: FAILED to allocate packets buffer\n",
+			__func__);
+
+		ret = -ENOMEM;
+		goto end;
+	}
+
+	MPQ_DVB_DBG_PRINT("%s: dec_buffs: num=%d, size=%d, linear=%d\n",
+			__func__,
+			dec_buffs->buffers_num,
+			dec_buffs->buffers_size,
+			dec_buffs->is_linear);
+
+	feed_data->buffer_desc.decoder_buffers_num = dec_buffs->buffers_num;
+	if (0 == dec_buffs->buffers_num)
+		ret = mpq_dmx_init_internal_buffers(
+			feed_data, dec_buffs, client);
+	else
+		ret = mpq_dmx_init_external_buffers(
+			feed_data, dec_buffs, client);
+
+	if (ret != 0)
+		goto init_failed_free_packet_buffer;
+
+	mode = dec_buffs->is_linear ? MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR :
+		MPQ_STREAMBUFFER_BUFFER_MODE_RING;
+	ret = mpq_streambuffer_init(
+			feed_data->video_buffer,
+			mode,
+			feed_data->buffer_desc.desc,
+			feed_data->buffer_desc.decoder_buffers_num,
+			packet_buffer,
+			VIDEO_META_DATA_BUFFER_SIZE);
+
+	if (ret != 0)
+		goto init_failed_free_packet_buffer;
+
+	goto end;
+
+
+init_failed_free_packet_buffer:
+	vfree(packet_buffer);
+end:
+	return ret;
+}
+
 int mpq_dmx_init_video_feed(struct dvb_demux_feed *feed)
 {
 	int ret;
-	void *packet_buffer;
-	void *payload_buffer;
 	struct mpq_video_feed_info *feed_data;
 	struct mpq_demux *mpq_demux = feed->demux->priv;
 	struct mpq_streambuffer *stream_buffer;
-	int actual_buffer_size;
 
 	/* Allocate memory for private feed data */
-	feed_data = vmalloc(sizeof(struct mpq_video_feed_info));
+	feed_data = vzalloc(sizeof(struct mpq_video_feed_info));
 
 	if (feed_data == NULL) {
 		MPQ_DVB_ERR_PRINT(
@@ -925,78 +1197,6 @@
 		}
 	}
 
-	/* Allocate packet buffer holding the meta-data */
-	packet_buffer = vmalloc(VIDEO_META_DATA_BUFFER_SIZE);
-
-	if (packet_buffer == NULL) {
-		MPQ_DVB_ERR_PRINT(
-			"%s: FAILED to allocate packets buffer\n",
-			__func__);
-
-		ret = -ENOMEM;
-		goto init_failed_free_priv_data;
-	}
-
-	/*
-	 * Allocate payload buffer through ION.
-	 * TODO: for scrambling support, need to check if the
-	 * stream is scrambled and allocate the buffer with secure
-	 * flag set.
-	 */
-
-	actual_buffer_size = feed->buffer_size;
-
-	actual_buffer_size += (SZ_4K - 1);
-	actual_buffer_size &= ~(SZ_4K - 1);
-
-	feed_data->payload_buff_handle =
-		ion_alloc(mpq_demux->ion_client,
-				  actual_buffer_size,
-				  SZ_4K,
-				  ION_HEAP(ION_CP_MM_HEAP_ID),
-				  ION_FLAG_CACHED);
-
-	if (IS_ERR_OR_NULL(feed_data->payload_buff_handle)) {
-		ret = PTR_ERR(feed_data->payload_buff_handle);
-
-		MPQ_DVB_ERR_PRINT(
-			"%s: FAILED to allocate payload buffer %d\n",
-			__func__, ret);
-
-		if (!ret)
-			ret = -ENOMEM;
-		goto init_failed_free_packet_buffer;
-	}
-
-	payload_buffer =
-		ion_map_kernel(mpq_demux->ion_client,
-					   feed_data->payload_buff_handle);
-
-	if (IS_ERR_OR_NULL(payload_buffer)) {
-		ret = PTR_ERR(payload_buffer);
-
-		MPQ_DVB_ERR_PRINT(
-			"%s: FAILED to map payload buffer %d\n",
-			__func__, ret);
-
-		if (!ret)
-			ret = -ENOMEM;
-		goto init_failed_free_payload_buffer;
-	}
-
-	feed_data->buffer_desc.read_ptr = 0;
-	feed_data->buffer_desc.write_ptr = 0;
-	feed_data->buffer_desc.base = payload_buffer;
-	feed_data->buffer_desc.size = actual_buffer_size;
-	feed_data->buffer_desc.handle =
-		ion_share_dma_buf(
-			mpq_demux->ion_client,
-			feed_data->payload_buff_handle);
-	if (feed_data->buffer_desc.handle < 0) {
-		ret = -EFAULT;
-		goto init_failed_unmap_payload_buffer;
-	}
-
 	/* Register the new stream-buffer interface to MPQ adapter */
 	switch (feed->pes_type) {
 	case DMX_TS_PES_VIDEO0:
@@ -1025,7 +1225,7 @@
 			__func__,
 			feed->pes_type);
 		ret = -EINVAL;
-		goto init_failed_unshare_payload_buffer;
+		goto init_failed_free_priv_data;
 	}
 
 	/* make sure not occupied already */
@@ -1039,26 +1239,12 @@
 			__func__,
 			feed_data->stream_interface);
 		ret = -EBUSY;
-		goto init_failed_unshare_payload_buffer;
+		goto init_failed_free_priv_data;
 	}
 
 	feed_data->video_buffer =
 		&mpq_dmx_info.decoder_buffers[feed_data->stream_interface];
 
-	ret = mpq_streambuffer_init(
-		feed_data->video_buffer,
-		MPQ_STREAMBUFFER_BUFFER_MODE_RING,
-		&feed_data->buffer_desc,
-		1,
-		packet_buffer,
-		VIDEO_META_DATA_BUFFER_SIZE);
-	if (ret < 0) {
-		MPQ_DVB_ERR_PRINT(
-			"%s: mpq_streambuffer_init failed, err = %d\n",
-			__func__, ret);
-		goto init_failed_unshare_payload_buffer;
-	}
-
 	ret = mpq_adapter_register_stream_if(
 		feed_data->stream_interface,
 		feed_data->video_buffer);
@@ -1068,10 +1254,18 @@
 			"%s: mpq_adapter_register_stream_if failed, "
 			"err = %d\n",
 			__func__, ret);
-		goto init_failed_unshare_payload_buffer;
+		goto init_failed_free_priv_data;
 	}
 
-	feed->buffer_size = actual_buffer_size;
+	ret = mpq_dmx_init_streambuffer(
+		feed, feed_data, feed_data->video_buffer);
+	if (ret) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: mpq_dmx_init_streambuffer failed, err = %d\n",
+			__func__, ret);
+		goto init_failed_unregister_stream_if;
+	}
+
 	feed_data->pes_payload_address =
 		(u32)feed_data->video_buffer->raw_data.data;
 
@@ -1085,7 +1279,6 @@
 	feed_data->found_sequence_header_pattern = 0;
 	memset(&feed_data->prefix_size, 0,
 			sizeof(struct mpq_framing_prefix_size_masks));
-	feed_data->first_pattern_offset = 0;
 	feed_data->first_prefix_size = 0;
 	feed_data->saved_pts_dts_info.pts_exist = 0;
 	feed_data->saved_pts_dts_info.dts_exist = 0;
@@ -1101,16 +1294,8 @@
 
 	return 0;
 
-init_failed_unshare_payload_buffer:
-	put_unused_fd(feed_data->buffer_desc.handle);
-init_failed_unmap_payload_buffer:
-	ion_unmap_kernel(mpq_demux->ion_client,
-					 feed_data->payload_buff_handle);
-init_failed_free_payload_buffer:
-	ion_free(mpq_demux->ion_client,
-			feed_data->payload_buff_handle);
-init_failed_free_packet_buffer:
-	vfree(packet_buffer);
+init_failed_unregister_stream_if:
+	mpq_adapter_unregister_stream_if(feed_data->stream_interface);
 init_failed_free_priv_data:
 	vfree(feed_data);
 	feed->priv = NULL;
@@ -1120,6 +1305,41 @@
 }
 EXPORT_SYMBOL(mpq_dmx_init_video_feed);
 
+void mpq_dmx_release_streambuffer(
+	struct dvb_demux_feed *feed,
+	struct mpq_video_feed_info *feed_data,
+	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);
+
+	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) {
+				ion_unmap_kernel(client,
+					feed_data->buffer_desc.ion_handle[i]);
+				feed_data->buffer_desc.desc[i].base = NULL;
+			}
+			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);
+		}
+	}
+}
 
 int mpq_dmx_terminate_video_feed(struct dvb_demux_feed *feed)
 {
@@ -1143,17 +1363,7 @@
 
 	wake_up_all(&feed_data->video_buffer->raw_data.queue);
 
-	mpq_adapter_unregister_stream_if(feed_data->stream_interface);
-
-	vfree(feed_data->video_buffer->packet_data.data);
-
-	put_unused_fd(feed_data->buffer_desc.handle);
-
-	ion_unmap_kernel(mpq_demux->ion_client,
-					 feed_data->payload_buff_handle);
-
-	ion_free(mpq_demux->ion_client,
-			 feed_data->payload_buff_handle);
+	mpq_dmx_release_streambuffer(feed, feed_data, mpq_demux->ion_client);
 
 	vfree(feed_data);
 
@@ -1196,78 +1406,98 @@
 }
 EXPORT_SYMBOL(mpq_dmx_decoder_fullness_init);
 
+
+static inline int mpq_dmx_check_decoder_fullness(
+	struct mpq_streambuffer *sbuff,
+	size_t required_space)
+{
+	u32 free = mpq_streambuffer_data_free(sbuff);
+	MPQ_DVB_DBG_PRINT("%s: stream buffer free = %d, required = %d\n",
+		__func__, free, required_space);
+
+	/*
+	 * For linear buffers, verify there's enough space for this TSP
+	 * and an additional buffer is free, as framing might required one
+	 * more buffer to be available.
+	 */
+	if (MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR == sbuff->mode)
+		return (free >= required_space &&
+			sbuff->pending_buffers_count < sbuff->buffers_num-1);
+	else
+		/* Ring buffer mode */
+		return (free >= required_space);
+}
+
 int mpq_dmx_decoder_fullness_wait(
 		struct dvb_demux_feed *feed,
 		size_t required_space)
 {
 	struct mpq_demux *mpq_demux = feed->demux->priv;
+	struct mpq_streambuffer *sbuff = NULL;
+	struct mpq_video_feed_info *feed_data;
+	int ret;
 
-	if (mpq_dmx_is_video_feed(feed)) {
-		int ret;
-		struct mpq_video_feed_info *feed_data;
-		struct dvb_ringbuffer *video_buff;
-
-		spin_lock(&mpq_demux->feed_lock);
-
-		if (feed->priv == NULL) {
-			spin_unlock(&mpq_demux->feed_lock);
-			return -EINVAL;
-		}
-
-		feed_data = feed->priv;
-		video_buff = &feed_data->video_buffer->raw_data;
-
-		ret = 0;
-		if ((feed_data != NULL) &&
-			(!feed_data->fullness_wait_cancel) &&
-			(dvb_ringbuffer_free(video_buff) < required_space)) {
-			DEFINE_WAIT(__wait);
-			for (;;) {
-				prepare_to_wait(
-					&video_buff->queue,
-					&__wait,
-					TASK_INTERRUPTIBLE);
-
-				if ((feed->priv == NULL) ||
-					(feed_data->fullness_wait_cancel) ||
-					(dvb_ringbuffer_free(video_buff) >=
-					required_space))
-					break;
-
-				if (!signal_pending(current)) {
-					spin_unlock(&mpq_demux->feed_lock);
-					schedule();
-					spin_lock(&mpq_demux->feed_lock);
-					continue;
-				}
-				ret = -ERESTARTSYS;
-				break;
-			}
-			finish_wait(&video_buff->queue, &__wait);
-		}
-
-		if (ret < 0) {
-			spin_unlock(&mpq_demux->feed_lock);
-			return ret;
-		}
-
-		if ((feed->priv == NULL) ||
-			(feed_data->fullness_wait_cancel)) {
-			spin_unlock(&mpq_demux->feed_lock);
-			return -EINVAL;
-		}
-
-		spin_unlock(&mpq_demux->feed_lock);
-		return 0;
+	if (!mpq_dmx_is_video_feed(feed)) {
+		MPQ_DVB_DBG_PRINT("%s: Invalid feed type %d\n",
+			__func__,
+			feed->pes_type);
+		return -EINVAL;
 	}
 
-	/* else */
-	MPQ_DVB_DBG_PRINT(
-		"%s: Invalid feed type %d\n",
-		__func__,
-		feed->pes_type);
+	spin_lock(&mpq_demux->feed_lock);
+	if (feed->priv == NULL) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return -EINVAL;
+	}
+	feed_data = feed->priv;
+	sbuff = feed_data->video_buffer;
+	if (sbuff == NULL) {
+		spin_unlock(&mpq_demux->feed_lock);
+		MPQ_DVB_ERR_PRINT("%s: mpq_streambuffer object is NULL\n",
+			__func__);
+		return -EINVAL;
+	}
 
-	return -EINVAL;
+	if ((feed_data != NULL) &&
+		(!feed_data->fullness_wait_cancel) &&
+		(!mpq_dmx_check_decoder_fullness(sbuff, required_space))) {
+		DEFINE_WAIT(__wait);
+		for (;;) {
+			prepare_to_wait(&sbuff->raw_data.queue,
+				&__wait,
+				TASK_INTERRUPTIBLE);
+
+			if ((feed->priv == NULL) ||
+				feed_data->fullness_wait_cancel ||
+				mpq_dmx_check_decoder_fullness(sbuff,
+					required_space))
+				break;
+
+			if (!signal_pending(current)) {
+				spin_unlock(&mpq_demux->feed_lock);
+				schedule();
+				spin_lock(&mpq_demux->feed_lock);
+				continue;
+			}
+
+			ret = -ERESTARTSYS;
+			break;
+		}
+		finish_wait(&sbuff->raw_data.queue, &__wait);
+	}
+
+	if (ret < 0) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return ret;
+	}
+
+	if ((feed->priv == NULL) || (feed_data->fullness_wait_cancel)) {
+		spin_unlock(&mpq_demux->feed_lock);
+		return -EINVAL;
+	}
+
+	spin_unlock(&mpq_demux->feed_lock);
+	return 0;
 }
 EXPORT_SYMBOL(mpq_dmx_decoder_fullness_wait);
 
@@ -1572,17 +1802,6 @@
 
 	pes_header = &feed_data->pes_header;
 
-	/* MPQ_DVB_DBG_PRINT("TS packet: %X %X %X %X %X%X %X %X %X\n",
-		ts_header->sync_byte,
-		ts_header->transport_error_indicator,
-		ts_header->payload_unit_start_indicator,
-		ts_header->transport_priority,
-		ts_header->pid_msb,
-		ts_header->pid_lsb,
-		ts_header->transport_scrambling_control,
-		ts_header->adaptation_field_control,
-		ts_header->continuity_counter); */
-
 	/* Make sure this TS packet has a payload and not scrambled */
 	if ((ts_header->sync_byte != 0x47) ||
 		(ts_header->adaptation_field_control == 0) ||
@@ -1752,8 +1971,11 @@
 		feed->peslen += bytes_avail;
 
 		meta_data.packet_type = DMX_FRAMING_INFO_PACKET;
-		packet.raw_data_handle = feed_data->buffer_desc.handle;
-		packet.raw_data_offset = 0;
+		packet.raw_data_handle = feed_data->buffer_desc.desc[0].handle;
+		mpq_streambuffer_get_data_rw_offset(
+			stream_buffer,
+			&packet.raw_data_offset,
+			NULL);
 		packet.user_data_len =
 				sizeof(struct mpq_adapter_video_meta_data);
 
@@ -1874,17 +2096,6 @@
 
 	pes_header = &feed_data->pes_header;
 
-	/* MPQ_DVB_DBG_PRINT("TS packet: %X %X %X %X %X%X %X %X %X\n",
-		ts_header->sync_byte,
-		ts_header->transport_error_indicator,
-		ts_header->payload_unit_start_indicator,
-		ts_header->transport_priority,
-		ts_header->pid_msb,
-		ts_header->pid_lsb,
-		ts_header->transport_scrambling_control,
-		ts_header->adaptation_field_control,
-		ts_header->continuity_counter); */
-
 	/* Make sure this TS packet has a payload and not scrambled */
 	if ((ts_header->sync_byte != 0x47) ||
 		(ts_header->adaptation_field_control == 0) ||
@@ -1910,8 +2121,11 @@
 			if (0 == feed_data->pes_header_left_bytes) {
 				packet.raw_data_len = feed->peslen;
 				packet.raw_data_handle =
-					feed_data->buffer_desc.handle;
-				packet.raw_data_offset = 0;
+					feed_data->buffer_desc.desc[0].handle;
+				mpq_streambuffer_get_data_rw_offset(
+					stream_buffer,
+					&packet.raw_data_offset,
+					NULL);
 				packet.user_data_len =
 					sizeof(struct
 						mpq_adapter_video_meta_data);
@@ -2014,43 +2228,57 @@
 		struct dmx_buffer_status *dmx_buffer_status)
 {
 	struct mpq_demux *mpq_demux = feed->demux->priv;
+	struct mpq_video_feed_info *feed_data;
+	struct mpq_streambuffer *video_buff;
 
-	if (mpq_dmx_is_video_feed(feed)) {
-		struct mpq_video_feed_info *feed_data;
-		struct dvb_ringbuffer *video_buff;
-
-		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;
-		video_buff = &feed_data->video_buffer->raw_data;
-
-		dmx_buffer_status->error = video_buff->error;
-		dmx_buffer_status->fullness = dvb_ringbuffer_avail(video_buff);
-		dmx_buffer_status->free_bytes = dvb_ringbuffer_free(video_buff);
-		dmx_buffer_status->read_offset = video_buff->pread;
-		dmx_buffer_status->write_offset = video_buff->pwrite;
-		dmx_buffer_status->size = video_buff->size;
-
-		spin_unlock(&mpq_demux->feed_lock);
-
-		return 0;
+	if (!mpq_dmx_is_video_feed(feed)) {
+		MPQ_DVB_ERR_PRINT(
+			"%s: Invalid feed type %d\n",
+			__func__,
+			feed->pes_type);
+		return -EINVAL;
 	}
 
-	/* else */
-	MPQ_DVB_ERR_PRINT(
-		"%s: Invalid feed type %d\n",
-		__func__,
-		feed->pes_type);
+	spin_lock(&mpq_demux->feed_lock);
 
-	return -EINVAL;
+	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;
+	video_buff = feed_data->video_buffer;
+
+	dmx_buffer_status->error = video_buff->raw_data.error;
+	if (MPQ_STREAMBUFFER_BUFFER_MODE_LINEAR == video_buff->mode) {
+		dmx_buffer_status->fullness =
+			video_buff->buffers[0].size *
+			video_buff->pending_buffers_count;
+		dmx_buffer_status->free_bytes =
+			video_buff->buffers[0].size *
+			(video_buff->buffers_num -
+			video_buff->pending_buffers_count);
+		dmx_buffer_status->size =
+			video_buff->buffers[0].size *
+			video_buff->buffers_num;
+	} else {
+		dmx_buffer_status->fullness =
+			mpq_streambuffer_data_avail(video_buff);
+		dmx_buffer_status->free_bytes =
+			mpq_streambuffer_data_free(video_buff);
+		dmx_buffer_status->size = video_buff->buffers[0].size;
+	}
+
+	mpq_streambuffer_get_data_rw_offset(
+		video_buff,
+		&dmx_buffer_status->read_offset,
+		&dmx_buffer_status->write_offset);
+
+	spin_unlock(&mpq_demux->feed_lock);
+
+	return 0;
 }
 EXPORT_SYMBOL(mpq_dmx_decoder_buffer_status);
 
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 f7af1ef..daf8aa9 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
@@ -269,6 +269,24 @@
 	u32 size_mask[MPQ_MAX_FOUND_PATTERNS];
 };
 
+/**
+ * mpq_decoder_buffers_desc - decoder buffer(s) management information.
+ *
+ * @desc: Array of buffer descriptors as they are passed to mpq_streambuffer
+ * upon its initialization. These descriptors must remain valid as long as
+ * the mpq_streambuffer object is used.
+ * @ion_handle: Array of ION handles, one for each decoder buffer, used for
+ * kernel memory mapping or allocation. Handles are saved in order to release
+ * resources properly later on.
+ * @decoder_buffers_num: number of buffers that are managed, either externally
+ * or internally by the mpq_streambuffer object
+ */
+struct mpq_decoder_buffers_desc {
+	struct mpq_streambuffer_buffer_desc desc[DMX_MAX_DECODER_BUFFER_NUM];
+	struct ion_handle *ion_handle[DMX_MAX_DECODER_BUFFER_NUM];
+	u32 decoder_buffers_num;
+};
+
 /*
  * mpq_video_feed_info - private data used for video feed.
  *
@@ -286,8 +304,7 @@
  * decoder's fullness.
  * @pes_payload_address: Used for feeds that output data to decoder,
  * holds current PES payload start address.
- * @payload_buff_handle: ION handle for the allocated payload buffer
- * @stream_interface: The ID of the video stream interface registered
+  * @stream_interface: The ID of the video stream interface registered
  * with this stream buffer.
  * @patterns: pointer to the framing patterns to look for.
  * @patterns_num: number of framing patterns.
@@ -320,13 +337,12 @@
 struct mpq_video_feed_info {
 	void *plugin_data;
 	struct mpq_streambuffer *video_buffer;
-	struct mpq_streambuffer_buffer_desc buffer_desc;
+	struct mpq_decoder_buffers_desc buffer_desc;
 	struct pes_packet_header pes_header;
 	u32 pes_header_left_bytes;
 	u32 pes_header_offset;
 	u32 pes_payload_address;
 	int fullness_wait_cancel;
-	struct ion_handle *payload_buff_handle;
 	enum mpq_adapter_stream_if stream_interface;
 	const struct mpq_framing_pattern_lookup_params *patterns;
 	int patterns_num;
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 bbf9d0a..c5c3518 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
@@ -572,24 +572,53 @@
 	caps->max_bitrate = 144;
 	caps->demod_input_max_bitrate = 72;
 	caps->memory_input_max_bitrate = 72;
-	caps->section.flags = 0;
+
+	/* 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 = 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 = 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 = 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 = 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 = 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;
 }
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 ac03e43..61c1761 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
@@ -868,24 +868,53 @@
 	caps->max_bitrate = 144;
 	caps->demod_input_max_bitrate = 72;
 	caps->memory_input_max_bitrate = 72;
-	caps->section.flags = 0;
+
+	/* 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 = 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 = 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 = 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 = 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 = 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;
 }
diff --git a/drivers/media/dvb/mpq/include/mpq_stream_buffer.h b/drivers/media/dvb/mpq/include/mpq_stream_buffer.h
index 9476c73..e5ba635 100644
--- a/drivers/media/dvb/mpq/include/mpq_stream_buffer.h
+++ b/drivers/media/dvb/mpq/include/mpq_stream_buffer.h
@@ -413,7 +413,21 @@
 	mpq_streambuffer_pkt_dispose_cb cb_func,
 	void *user_data);
 
-
+/**
+ * mpq_streambuffer_data_rw_offset - returns read/write offsets of current data
+ * buffer.
+ * @sbuff: The stream buffer object
+ * @read_offset: returned read offset
+ * @write_offset: returned write offset
+ *
+ * Note: read offset or write offset may be NULL if not required.
+ * Returns error status
+ * -EINVAL if arguments are invalid
+ */
+int mpq_streambuffer_get_data_rw_offset(
+	struct mpq_streambuffer *sbuff,
+	u32 *read_offset,
+	u32 *write_offset);
 
 #endif /* _MPQ_STREAM_BUFFER_H */
 
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h
index 257e069..fd4447f 100644
--- a/include/linux/dvb/dmx.h
+++ b/include/linux/dvb/dmx.h
@@ -39,6 +39,9 @@
 /* Min recording chunk upon which event is generated */
 #define DMX_REC_BUFF_CHUNK_MIN_SIZE		(100*188)
 
+/* Decoder buffers are usually large ~1MB, 10 should suffice */
+#define DMX_MAX_DECODER_BUFFER_NUM		(10)
+
 typedef enum
 {
 	DMX_OUT_DECODER, /* Streaming directly to decoder. */
@@ -314,13 +317,27 @@
 
 	/* Maximum buffer size allowed */
 	__u32 max_size;
+
+	/* Maximum number of linear buffers handled by demux */
+	__u32 max_buffer_num;
+
+	/* Feature support bitmap as detailed below */
 	__u32 flags;
 
-/* Buffer allocated as physically contiguous memory */
-#define DMX_BUFFER_CONTIGEOUS_MEM			0x1
+/* Buffer must be allocated as physically contiguous memory */
+#define DMX_BUFFER_CONTIGUOUS_MEM		0x1
 
 /* If the filter's data is decrypted, the buffer should be secured one */
 #define DMX_BUFFER_SECURED_IF_DECRYPTED		0x2
+
+/* Buffer can be allocated externally */
+#define DMX_BUFFER_EXTERNAL_SUPPORT		0x4
+
+/* Buffer can be allocated internally */
+#define DMX_BUFFER_INTERNAL_SUPPORT		0x8
+
+/* Filter output can be output to a linear buffer group */
+#define DMX_BUFFER_LINEAR_GROUP_SUPPORT		0x10
 };
 
 typedef struct dmx_caps {
@@ -385,6 +402,9 @@
 	/* For PES not sent to decoder */
 	struct dmx_buffer_requirement pes;
 
+	/* For PES sent to decoder */
+	struct dmx_buffer_requirement decoder;
+
 	/* Recording buffer for recording of 188 bytes packets */
 	struct dmx_buffer_requirement recording_188_tsp;
 
@@ -438,9 +458,9 @@
 };
 
 struct dmx_stc {
-	unsigned int num;	/* input : which STC? 0..N */
-	unsigned int base;	/* output: divisor for stc to get 90 kHz clock */
-	__u64 stc;		/* output: stc in 'base'*90 kHz units */
+	unsigned int num; /* input : which STC? 0..N */
+	unsigned int base; /* output: divisor for stc to get 90 kHz clock */
+	__u64 stc; /* output: stc in 'base'*90 kHz units */
 };
 
 enum dmx_buffer_mode {
@@ -466,6 +486,27 @@
 	int handle;
 };
 
+
+struct dmx_decoder_buffers {
+	/*
+	 * Specify if linear buffer support is requested. If set, buffers_num
+	 * must be greater than 1
+	 */
+	int is_linear;
+
+	/*
+	 * Specify number of external buffers allocated by user.
+	 * If set to 0 means internal buffer allocation is requested
+	 */
+	__u32 buffers_num;
+
+	/* Specify buffer size, either external or internal */
+	__u32 buffers_size;
+
+	/* Array of externally allocated buffer handles */
+	int handles[DMX_MAX_DECODER_BUFFER_NUM];
+};
+
 #define DMX_START                _IO('o', 41)
 #define DMX_STOP                 _IO('o', 42)
 #define DMX_SET_FILTER           _IOW('o', 43, struct dmx_sct_filter_params)
@@ -487,5 +528,7 @@
 #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)
+
 
 #endif /*_DVBDMX_H_*/