media: dvb: Add support for external demux buffer allocation

demux input/output buffers were allocated internally by the driver,
for MPQ use-cases there's a need for user-space MPQ player to
allocate the buffers through ION driver and set them to demux
similar to the way other kinds of buffers are managed
in android multimedia stack for video

Change-Id: Ic28a279c0841fee12008cea7beac58f82409b9f9
Signed-off-by: Hamad Kadmany <hkadmany@codeaurora.org>
Signed-off-by: Neha Pandey <nehap@codeaurora.org>
diff --git a/drivers/media/dvb/dvb-core/demux.h b/drivers/media/dvb/dvb-core/demux.h
index 071d209..7190af8 100644
--- a/drivers/media/dvb/dvb-core/demux.h
+++ b/drivers/media/dvb/dvb-core/demux.h
@@ -109,6 +109,24 @@
 	};
 };
 
+/*
+ * struct data_buffer: Parameters of buffer allocated by
+ * demux device for input/output. Can be used to directly map the
+ * demux-device buffer to HW output if HW supports it.
+ */
+struct data_buffer {
+	/* dvb_ringbuffer managed by demux-device */
+	const struct dvb_ringbuffer *ringbuff;
+
+
+	/*
+	 * Private handle returned by kernel demux when
+	 * map_buffer is called in case external buffer
+	 * is used. NULL if buffer is allocated internally.
+	 */
+	void *priv_handle;
+};
+
 /*--------------------------------------------------------------------------*/
 /* TS packet reception */
 /*--------------------------------------------------------------------------*/
@@ -168,7 +186,7 @@
 struct dmx_ts_feed {
 	int is_filtering; /* Set to non-zero when filtering in progress */
 	struct dmx_demux *parent; /* Back-pointer */
-	const struct dvb_ringbuffer *buffer;
+	struct data_buffer buffer;
 	void *priv; /* Pointer to private data of the API client */
 	int (*set) (struct dmx_ts_feed *feed,
 		    u16 pid,
@@ -198,7 +216,7 @@
 	u8 filter_mask [DMX_MAX_FILTER_SIZE];
 	u8 filter_mode [DMX_MAX_FILTER_SIZE];
 	struct dmx_section_feed* parent; /* Back-pointer */
-	const struct dvb_ringbuffer *buffer;
+	struct data_buffer buffer;
 	void* priv; /* Pointer to private data of the API client */
 };
 
@@ -316,6 +334,8 @@
 	u32 capabilities;            /* Bitfield of capability flags */
 	struct dmx_frontend* frontend;    /* Front-end connected to the demux */
 	void* priv;                  /* Pointer to private data of the API client */
+	struct data_buffer dvr_input; /* DVR input buffer */
+
 	int (*open) (struct dmx_demux* demux);
 	int (*close) (struct dmx_demux* demux);
 	int (*write) (struct dmx_demux *demux, const char *buf, size_t count);
@@ -359,6 +379,13 @@
 
 	int (*get_stc) (struct dmx_demux* demux, unsigned int num,
 			u64 *stc, unsigned int *base);
+
+	int (*map_buffer) (struct dmx_demux *demux,
+			struct dmx_buffer *dmx_buffer,
+			void **priv_handle, void **mem);
+
+	int (*unmap_buffer) (struct dmx_demux *demux,
+			void *priv_handle);
 };
 
 #endif /* #ifndef __DEMUX_H */
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index 8353f6f..d9f62ad 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -490,6 +490,8 @@
 		dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE);
 		dvb_dmxdev_flush_events(&dmxdev->dvr_output_events);
 		dmxdev->dvr_feeds_count = 0;
+		dmxdev->dvr_buffer_mode = DMX_BUFFER_MODE_INTERNAL;
+		dmxdev->dvr_priv_buff_handle = NULL;
 
 		dvbdev->readers--;
 	} else if (!dvbdev->writers) {
@@ -517,10 +519,14 @@
 
 		dmxdev->demux->disconnect_frontend(dmxdev->demux);
 		dmxdev->demux->connect_frontend(dmxdev->demux, front);
+		dmxdev->dvr_input_buffer_mode = DMX_BUFFER_MODE_INTERNAL;
 
 		dvb_ringbuffer_init(&dmxdev->dvr_input_buffer,
 							mem,
 							DVR_BUFFER_SIZE);
+
+		dmxdev->demux->dvr_input.priv_handle = NULL;
+		dmxdev->demux->dvr_input.ringbuff = &dmxdev->dvr_input_buffer;
 		dvbdev->writers--;
 	}
 
@@ -545,7 +551,16 @@
 			dmxdev->dvr_buffer.data = NULL;
 			spin_unlock_irq(&dmxdev->lock);
 			wake_up_all(&dmxdev->dvr_buffer.queue);
-			vfree(mem);
+
+			if (dmxdev->dvr_buffer_mode == DMX_BUFFER_MODE_INTERNAL)
+				vfree(mem);
+		}
+
+		if ((dmxdev->dvr_buffer_mode == DMX_BUFFER_MODE_EXTERNAL) &&
+			dmxdev->dvr_priv_buff_handle) {
+			dmxdev->demux->unmap_buffer(dmxdev->demux,
+					dmxdev->dvr_priv_buff_handle);
+			dmxdev->dvr_priv_buff_handle = NULL;
 		}
 	} else {
 		int i;
@@ -589,7 +604,17 @@
 			spin_lock_irq(&dmxdev->dvr_in_lock);
 			dmxdev->dvr_input_buffer.data = NULL;
 			spin_unlock_irq(&dmxdev->dvr_in_lock);
-			vfree(mem);
+
+			if (dmxdev->dvr_buffer_mode == DMX_BUFFER_MODE_INTERNAL)
+				vfree(mem);
+		}
+
+		if ((dmxdev->dvr_input_buffer_mode ==
+			DMX_BUFFER_MODE_EXTERNAL) &&
+			(dmxdev->demux->dvr_input.priv_handle)) {
+			dmxdev->demux->unmap_buffer(dmxdev->demux,
+					dmxdev->demux->dvr_input.priv_handle);
+			dmxdev->demux->dvr_input.priv_handle = NULL;
 		}
 	}
 	/* TODO */
@@ -611,6 +636,7 @@
 	struct dvb_device *dvbdev = filp->private_data;
 	struct dmxdev *dmxdev = dvbdev->priv;
 	struct dvb_ringbuffer *buffer;
+	enum dmx_buffer_mode buffer_mode;
 	int vma_size;
 	int buffer_size;
 	int ret;
@@ -627,10 +653,18 @@
 		return -ENODEV;
 	}
 
-	if ((filp->f_flags & O_ACCMODE) == O_RDONLY)
+	if ((filp->f_flags & O_ACCMODE) == O_RDONLY) {
 		buffer = &dmxdev->dvr_buffer;
-	else
+		buffer_mode = dmxdev->dvr_buffer_mode;
+	} else {
 		buffer = &dmxdev->dvr_input_buffer;
+		buffer_mode = dmxdev->dvr_input_buffer_mode;
+	}
+
+	if (buffer_mode == DMX_BUFFER_MODE_EXTERNAL) {
+		mutex_unlock(&dmxdev->mutex);
+		return -EINVAL;
+	}
 
 	vma_size = vma->vm_end - vma->vm_start;
 
@@ -669,7 +703,8 @@
 	if (!dmxdev->demux->write)
 		return -EOPNOTSUPP;
 
-	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
+	if (((file->f_flags & O_ACCMODE) == O_RDONLY) ||
+		(!src->data))
 		return -EINVAL;
 
 	if ((file->f_flags & O_NONBLOCK) &&
@@ -859,20 +894,23 @@
 	void *newmem;
 	void *oldmem;
 	spinlock_t *lock;
+	enum dmx_buffer_mode buffer_mode;
 
 	dprintk("function : %s\n", __func__);
 
 	if ((f_flags & O_ACCMODE) == O_RDONLY) {
 		buf = &dmxdev->dvr_buffer;
 		lock = &dmxdev->lock;
+		buffer_mode = dmxdev->dvr_buffer_mode;
 	} else {
 		buf = &dmxdev->dvr_input_buffer;
 		lock = &dmxdev->dvr_in_lock;
+		buffer_mode = dmxdev->dvr_input_buffer_mode;
 	}
 
 	if (buf->size == size)
 		return 0;
-	if (!size)
+	if ((!size) || (buffer_mode == DMX_BUFFER_MODE_EXTERNAL))
 		return -EINVAL;
 
 	newmem = vmalloc_user(size);
@@ -903,6 +941,72 @@
 	return 0;
 }
 
+static int dvb_dvr_set_buffer_mode(struct dmxdev *dmxdev,
+			unsigned int f_flags, enum dmx_buffer_mode mode)
+{
+	if ((mode != DMX_BUFFER_MODE_INTERNAL) &&
+		(mode != DMX_BUFFER_MODE_EXTERNAL))
+		return -EINVAL;
+
+	if ((mode == DMX_BUFFER_MODE_INTERNAL) &&
+		(dmxdev->capabilities & DMXDEV_CAP_EXTERNAL_BUFFS_ONLY))
+		return -EINVAL;
+
+	if ((mode == DMX_BUFFER_MODE_EXTERNAL) &&
+		(!dmxdev->demux->map_buffer || !dmxdev->demux->unmap_buffer))
+		return -EINVAL;
+
+	if ((f_flags & O_ACCMODE) == O_RDONLY)
+		dmxdev->dvr_buffer_mode = mode;
+	else
+		dmxdev->dvr_input_buffer_mode = mode;
+
+	return 0;
+}
+
+static int dvb_dvr_set_buffer(struct dmxdev *dmxdev,
+			unsigned int f_flags, struct dmx_buffer *dmx_buffer)
+{
+	struct dvb_ringbuffer *buf;
+	spinlock_t *lock;
+	enum dmx_buffer_mode buffer_mode;
+	void **buff_handle;
+	void *newmem;
+	void *oldmem;
+
+	if ((f_flags & O_ACCMODE) == O_RDONLY) {
+		buf = &dmxdev->dvr_buffer;
+		lock = &dmxdev->lock;
+		buffer_mode = dmxdev->dvr_buffer_mode;
+		buff_handle = &dmxdev->dvr_priv_buff_handle;
+	} else {
+		buf = &dmxdev->dvr_input_buffer;
+		lock = &dmxdev->dvr_in_lock;
+		buffer_mode = dmxdev->dvr_input_buffer_mode;
+		buff_handle = &dmxdev->demux->dvr_input.priv_handle;
+	}
+
+	if ((!dmx_buffer->size) ||
+		(buffer_mode == DMX_BUFFER_MODE_INTERNAL))
+		return -EINVAL;
+
+	oldmem = *buff_handle;
+	if (dmxdev->demux->map_buffer(dmxdev->demux, dmx_buffer,
+				buff_handle, &newmem))
+		return -ENOMEM;
+
+	spin_lock_irq(lock);
+	buf->data = newmem;
+	buf->size = dmx_buffer->size;
+	dvb_ringbuffer_reset(buf);
+	spin_unlock_irq(lock);
+
+	if (oldmem)
+		dmxdev->demux->unmap_buffer(dmxdev->demux, oldmem);
+
+	return 0;
+}
+
 static int dvb_dvr_get_event(struct dmxdev *dmxdev,
 				unsigned int f_flags,
 				struct dmx_filter_event *event)
@@ -1058,7 +1162,7 @@
 
 	if (buf->size == size)
 		return 0;
-	if (!size)
+	if ((!size) || (dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL))
 		return -EINVAL;
 	if (dmxdevfilter->state >= DMXDEV_STATE_GO)
 		return -EBUSY;
@@ -1081,6 +1185,85 @@
 	return 0;
 }
 
+static int dvb_dmxdev_set_buffer_mode(struct dmxdev_filter *dmxdevfilter,
+					enum dmx_buffer_mode mode)
+{
+	struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+	struct dmxdev *dmxdev = dmxdevfilter->dev;
+	void *oldmem;
+
+	if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+		return -EBUSY;
+
+	if ((mode != DMX_BUFFER_MODE_INTERNAL) &&
+		(mode != DMX_BUFFER_MODE_EXTERNAL))
+		return -EINVAL;
+
+	if ((mode == DMX_BUFFER_MODE_INTERNAL) &&
+		(dmxdev->capabilities & DMXDEV_CAP_EXTERNAL_BUFFS_ONLY))
+		return -EINVAL;
+
+	if ((mode == DMX_BUFFER_MODE_EXTERNAL) &&
+		(!dmxdev->demux->map_buffer || !dmxdev->demux->unmap_buffer))
+		return -EINVAL;
+
+	if (mode == dmxdevfilter->buffer_mode)
+		return 0;
+
+	oldmem = buf->data;
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+	buf->data = NULL;
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+	dmxdevfilter->buffer_mode = mode;
+
+	if (mode == DMX_BUFFER_MODE_INTERNAL) {
+		/* switched from external to internal */
+		if (dmxdevfilter->priv_buff_handle) {
+			dmxdev->demux->unmap_buffer(dmxdev->demux,
+				dmxdevfilter->priv_buff_handle);
+			dmxdevfilter->priv_buff_handle = NULL;
+		}
+	} else if (oldmem) {
+		/* switched from internal to external */
+		vfree(oldmem);
+	}
+
+	return 0;
+}
+
+static int dvb_dmxdev_set_buffer(struct dmxdev_filter *dmxdevfilter,
+					struct dmx_buffer *buffer)
+{
+	struct dvb_ringbuffer *buf = &dmxdevfilter->buffer;
+	struct dmxdev *dmxdev = dmxdevfilter->dev;
+	void *newmem;
+	void *oldmem;
+
+	if (dmxdevfilter->state >= DMXDEV_STATE_GO)
+		return -EBUSY;
+
+	if ((!buffer->size) ||
+		(dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_INTERNAL))
+		return -EINVAL;
+
+	oldmem = dmxdevfilter->priv_buff_handle;
+	if (dmxdev->demux->map_buffer(dmxdev->demux, buffer,
+			&dmxdevfilter->priv_buff_handle, &newmem))
+		return -ENOMEM;
+
+	spin_lock_irq(&dmxdevfilter->dev->lock);
+	buf->data = newmem;
+	buf->size = buffer->size;
+	dvb_ringbuffer_reset(buf);
+	spin_unlock_irq(&dmxdevfilter->dev->lock);
+
+	if (oldmem)
+		dmxdev->demux->unmap_buffer(dmxdev->demux, oldmem);
+
+	return 0;
+}
+
 static int dvb_dmxdev_set_pes_buffer_size(struct dmxdev_filter *dmxdevfilter,
 					unsigned long size)
 {
@@ -1962,12 +2145,14 @@
 	tsfeed->priv = filter;
 
 	if (filter->params.pes.output == DMX_OUT_TS_TAP) {
-		tsfeed->buffer = &dmxdev->dvr_buffer;
+		tsfeed->buffer.ringbuff = &dmxdev->dvr_buffer;
+		tsfeed->buffer.priv_handle = dmxdev->dvr_priv_buff_handle;
 		if (!dmxdev->dvr_feeds_count)
 			dmxdev->dvr_feed = filter;
 		dmxdev->dvr_feeds_count++;
 	} else {
-		tsfeed->buffer = &filter->buffer;
+		tsfeed->buffer.ringbuff = &filter->buffer;
+		tsfeed->buffer.priv_handle = filter->priv_buff_handle;
 	}
 
 	if (tsfeed->data_ready_cb) {
@@ -2028,6 +2213,9 @@
 		dvb_dmxdev_filter_stop(filter);
 
 	if (!filter->buffer.data) {
+		if ((filter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL) ||
+			(dmxdev->capabilities & DMXDEV_CAP_EXTERNAL_BUFFS_ONLY))
+			return -ENOMEM;
 		mem = vmalloc_user(filter->buffer.size);
 		if (!mem)
 			return -ENOMEM;
@@ -2105,7 +2293,8 @@
 		}
 
 		(*secfilter)->priv = filter;
-		(*secfilter)->buffer = &filter->buffer;
+		(*secfilter)->buffer.ringbuff = &filter->buffer;
+		(*secfilter)->buffer.priv_handle = filter->priv_buff_handle;
 
 		memcpy(&((*secfilter)->filter_value[3]),
 		       &(para->filter.filter[1]), DMX_FILTER_SIZE - 1);
@@ -2182,6 +2371,8 @@
 	mutex_init(&dmxdevfilter->mutex);
 	file->private_data = dmxdevfilter;
 
+	dmxdevfilter->buffer_mode = DMX_BUFFER_MODE_INTERNAL;
+	dmxdevfilter->priv_buff_handle = NULL;
 	dvb_ringbuffer_init(&dmxdevfilter->buffer, NULL, 8192);
 	dvb_dmxdev_flush_events(&dmxdevfilter->events);
 
@@ -2212,7 +2403,15 @@
 		spin_lock_irq(&dmxdev->lock);
 		dmxdevfilter->buffer.data = NULL;
 		spin_unlock_irq(&dmxdev->lock);
-		vfree(mem);
+		if (dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_INTERNAL)
+			vfree(mem);
+	}
+
+	if ((dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL) &&
+		(dmxdevfilter->priv_buff_handle)) {
+		dmxdev->demux->unmap_buffer(dmxdev->demux,
+			dmxdevfilter->priv_buff_handle);
+		dmxdevfilter->priv_buff_handle = NULL;
 	}
 
 	dvb_dmxdev_filter_state_set(dmxdevfilter, DMXDEV_STATE_FREE);
@@ -2487,6 +2686,25 @@
 		mutex_unlock(&dmxdevfilter->mutex);
 		break;
 
+	case DMX_SET_BUFFER_MODE:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_set_buffer_mode(dmxdevfilter,
+				*(enum dmx_buffer_mode *)parg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
+	case DMX_SET_BUFFER:
+		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
+			mutex_unlock(&dmxdev->mutex);
+			return -ERESTARTSYS;
+		}
+		ret = dvb_dmxdev_set_buffer(dmxdevfilter, parg);
+		mutex_unlock(&dmxdevfilter->mutex);
+		break;
+
 	case DMX_GET_BUFFER_STATUS:
 		if (mutex_lock_interruptible(&dmxdevfilter->mutex)) {
 			mutex_unlock(&dmxdev->mutex);
@@ -2679,7 +2897,8 @@
 		return -ERESTARTSYS;
 	}
 
-	if (!dmxdevfilter->buffer.data) {
+	if ((!dmxdevfilter->buffer.data) ||
+		(dmxdevfilter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL)) {
 		mutex_unlock(&dmxdevfilter->mutex);
 		mutex_unlock(&dmxdev->mutex);
 		return -EINVAL;
@@ -2767,6 +2986,15 @@
 		ret = dvb_dvr_set_buffer_size(dmxdev, file->f_flags, arg);
 		break;
 
+	case DMX_SET_BUFFER_MODE:
+		ret = dvb_dvr_set_buffer_mode(dmxdev, file->f_flags,
+			*(enum dmx_buffer_mode *)parg);
+		break;
+
+	case DMX_SET_BUFFER:
+		ret = dvb_dvr_set_buffer(dmxdev, file->f_flags, parg);
+		break;
+
 	case DMX_GET_BUFFER_STATUS:
 		ret = dvb_dvr_get_buffer_status(dmxdev, file->f_flags, parg);
 		break;
diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h
index 9fd900e..e30c2c3 100644
--- a/drivers/media/dvb/dvb-core/dmxdev.h
+++ b/drivers/media/dvb/dvb-core/dmxdev.h
@@ -113,6 +113,8 @@
 	enum dmxdev_state state;
 	struct dmxdev *dev;
 	struct dvb_ringbuffer buffer;
+	void *priv_buff_handle;
+	enum dmx_buffer_mode buffer_mode;
 	u32 flush_data_len;
 
 	struct mutex mutex;
@@ -139,9 +141,10 @@
 
 	int filternum;
 	int capabilities;
-#define DMXDEV_CAP_DUPLEX	0x1
-#define DMXDEV_CAP_PULL_MODE	0x2
-#define DMXDEV_CAP_INDEXING	0x4
+#define DMXDEV_CAP_DUPLEX	0x01
+#define DMXDEV_CAP_PULL_MODE	0x02
+#define DMXDEV_CAP_INDEXING	0x04
+#define DMXDEV_CAP_EXTERNAL_BUFFS_ONLY	0x08
 
 	enum dmx_playback_mode_t playback_mode;
 	dmx_source_t source;
@@ -153,12 +156,15 @@
 	struct dmx_frontend *dvr_orig_fe;
 
 	struct dvb_ringbuffer dvr_buffer;
+	void *dvr_priv_buff_handle;
+	enum dmx_buffer_mode dvr_buffer_mode;
 	struct dmxdev_events_queue dvr_output_events;
 	struct dmxdev_filter *dvr_feed;
 	u32 dvr_flush_data_len;
 	int dvr_feeds_count;
 
 	struct dvb_ringbuffer dvr_input_buffer;
+	enum dmx_buffer_mode dvr_input_buffer_mode;
 	struct workqueue_struct *dvr_input_workqueue;
 
 #define DVR_BUFFER_SIZE (10*188*1024)
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index df71f76..de7b28d 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -1794,6 +1794,8 @@
 	dmx->release_ts_feed = dvbdmx_release_ts_feed;
 	dmx->allocate_section_feed = dvbdmx_allocate_section_feed;
 	dmx->release_section_feed = dvbdmx_release_section_feed;
+	dmx->map_buffer = NULL;
+	dmx->unmap_buffer = NULL;
 
 	dmx->add_frontend = dvbdmx_add_frontend;
 	dmx->remove_frontend = dvbdmx_remove_frontend;
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 c0a41ea..7435ab2 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.c
@@ -595,7 +595,7 @@
 	 * for decoder's buffers.
 	 */
 	mpq_dmx_info.ion_client =
-		msm_ion_client_create(UINT_MAX, "demux client");
+		msm_ion_client_create(UINT_MAX, "demux_client");
 
 	if (IS_ERR_OR_NULL(mpq_dmx_info.ion_client)) {
 		MPQ_DVB_ERR_PRINT(
@@ -603,6 +603,8 @@
 				__func__);
 
 		result = PTR_ERR(mpq_dmx_info.ion_client);
+		if (!result)
+			result = -ENOMEM;
 		mpq_dmx_info.ion_client = NULL;
 		goto init_failed_free_demux_devices;
 	}
@@ -683,7 +685,7 @@
 
 	if (mpq_dmx_info.devices != NULL) {
 		for (i = 0; i < mpq_demux_device_num; i++) {
-			mpq_demux = mpq_dmx_info.devices+i;
+			mpq_demux = mpq_dmx_info.devices + i;
 
 			if (mpq_demux->is_initialized) {
 				mpq_demux->demux.dmx.remove_frontend(
@@ -764,6 +766,107 @@
 }
 EXPORT_SYMBOL(mpq_dmx_set_source);
 
+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)) {
+		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	mpq_demux = dvb_demux->priv;
+	if (mpq_demux == NULL) {
+		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+		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, ionflag);
+		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;
+}
+EXPORT_SYMBOL(mpq_dmx_map_buffer);
+
+int mpq_dmx_unmap_buffer(struct dmx_demux *demux,
+		void *priv_handle)
+{
+	struct dvb_demux *dvb_demux = demux->priv;
+	struct ion_handle *ion_handle = priv_handle;
+	struct mpq_demux *mpq_demux;
+	unsigned long ionflag = 0;
+	int ret;
+
+	if ((mpq_dmx_info.devices == NULL) || (dvb_demux == NULL) ||
+		(priv_handle == NULL)) {
+		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	mpq_demux = dvb_demux->priv;
+	if (mpq_demux == NULL) {
+		MPQ_DVB_ERR_PRINT("%s: invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	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);
+		return -EINVAL;
+	}
+
+	if (!(ionflag & ION_SECURE))
+		ion_unmap_kernel(mpq_demux->ion_client, ion_handle);
+
+	ion_free(mpq_demux->ion_client, ion_handle);
+
+	return 0;
+}
+EXPORT_SYMBOL(mpq_dmx_unmap_buffer);
 
 int mpq_dmx_init_video_feed(struct dvb_demux_feed *feed)
 {
@@ -837,9 +940,10 @@
 
 		MPQ_DVB_ERR_PRINT(
 			"%s: FAILED to allocate payload buffer %d\n",
-			__func__,
-			ret);
+			__func__, ret);
 
+		if (!ret)
+			ret = -ENOMEM;
 		goto init_failed_free_packet_buffer;
 	}
 
@@ -852,9 +956,10 @@
 
 		MPQ_DVB_ERR_PRINT(
 			"%s: FAILED to map payload buffer %d\n",
-			__func__,
-			ret);
+			__func__, ret);
 
+		if (!ret)
+			ret = -ENOMEM;
 		goto init_failed_free_payload_buffer;
 	}
 
@@ -922,8 +1027,7 @@
 		MPQ_DVB_ERR_PRINT(
 			"%s: mpq_adapter_register_stream_if failed, "
 			"err = %d\n",
-			__func__,
-			ret);
+			__func__, ret);
 		goto init_failed_unmap_payload_buffer;
 	}
 
@@ -991,8 +1095,7 @@
 
 	wake_up_all(&feed_data->video_buffer->raw_data.queue);
 
-	mpq_adapter_unregister_stream_if(
-		feed_data->stream_interface);
+	mpq_adapter_unregister_stream_if(feed_data->stream_interface);
 
 	vfree(feed_data->video_buffer->packet_data.data);
 
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 893273d..3500eda 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_common.h
@@ -332,7 +332,7 @@
  * them to the dvb adapter.
  *
  * @dmx_init_func: Pointer to the function to be used
- *  to initialize demux of the udnerlying HW plugin.
+ *  to initialize demux of the underlying HW plugin.
  *
  * Return     error code
  *
@@ -361,6 +361,38 @@
 int mpq_dmx_set_source(struct dmx_demux *demux, const dmx_source_t *src);
 
 /**
+ * mpq_dmx_map_buffer - map user-space buffer into kernel space.
+ *
+ * @demux: The demux device.
+ * @dmx_buffer: The demux buffer from user-space, assumes that
+ * buffer handle is ION file-handle.
+ * @priv_handle: Saves ION-handle of the buffer imported by this function.
+ * @kernel_mem: Saves kernel mapped address of the buffer.
+ *
+ * Return     error code
+ *
+ * The function maps the buffer into kernel memory only if the buffer
+ * was not allocated with secure flag, otherwise the returned kernel
+ * memory address is set to NULL.
+ */
+int mpq_dmx_map_buffer(struct dmx_demux *demux, struct dmx_buffer *dmx_buffer,
+		void **priv_handle, void **kernel_mem);
+
+/**
+ * mpq_dmx_unmap_buffer - unmap user-space buffer from kernel space memory.
+ *
+ * @demux: The demux device.
+ * @priv_handle: ION-handle of the buffer returned from mpq_dmx_map_buffer.
+ *
+ * Return     error code
+ *
+ * The function unmaps the buffer from kernel memory only if the buffer
+ * was not allocated with secure flag.
+ */
+int mpq_dmx_unmap_buffer(struct dmx_demux *demux,
+		void *priv_handle);
+
+/**
  * mpq_dmx_init_video_feed - Initializes video feed
  * used to pass data to decoder directly.
  *
@@ -396,8 +428,7 @@
  *
  * Return     error code.
  */
-int mpq_dmx_decoder_fullness_init(
-		struct dvb_demux_feed *feed);
+int mpq_dmx_decoder_fullness_init(struct dvb_demux_feed *feed);
 
 /**
  * mpq_dmx_decoder_fullness_wait - Checks whether decoder buffer
@@ -408,8 +439,7 @@
  *
  * Return     error code.
  */
-int mpq_dmx_decoder_fullness_wait(
-		struct dvb_demux_feed *feed,
+int mpq_dmx_decoder_fullness_wait(struct dvb_demux_feed *feed,
 		size_t required_space);
 
 /**
@@ -422,8 +452,7 @@
  *
  * Return     error code.
  */
-int mpq_dmx_decoder_fullness_abort(
-		struct dvb_demux_feed *feed);
+int mpq_dmx_decoder_fullness_abort(struct dvb_demux_feed *feed);
 
 /**
  * mpq_dmx_decoder_buffer_status - Returns the
@@ -452,9 +481,7 @@
  * the packet and does not write them to the output buffer.
  * Scrambled packets are bypassed.
  */
-int mpq_dmx_process_video_packet(
-		struct dvb_demux_feed *feed,
-		const u8 *buf);
+int mpq_dmx_process_video_packet(struct dvb_demux_feed *feed, const u8 *buf);
 
 /**
  * mpq_dmx_process_pcr_packet - Extract PCR/STC pairs from
@@ -475,9 +502,7 @@
  * The function callbacks dmxdev after extraction of the pcr/stc
  * pair.
  */
-int mpq_dmx_process_pcr_packet(
-			struct dvb_demux_feed *feed,
-			const u8 *buf);
+int mpq_dmx_process_pcr_packet(struct dvb_demux_feed *feed, const u8 *buf);
 
 /**
  * mpq_dmx_is_video_feed - Returns whether the PES feed
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 74a0dbe..627c5a2 100644
--- a/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
+++ b/drivers/media/dvb/mpq/demux/mpq_dmx_plugin_tsif.c
@@ -579,6 +579,24 @@
 	caps->max_bitrate = 144;
 	caps->demod_input_max_bitrate = 72;
 	caps->memory_input_max_bitrate = 72;
+	caps->section.flags = 0;
+	caps->section.max_size = 0xFFFFFFFF;
+	caps->section.size_alignment = 0;
+	caps->pes.flags = 0;
+	caps->pes.max_size = 0xFFFFFFFF;
+	caps->pes.size_alignment = 0;
+	caps->recording_188_tsp.flags = 0;
+	caps->recording_188_tsp.max_size = 0xFFFFFFFF;
+	caps->recording_188_tsp.size_alignment = 0;
+	caps->recording_192_tsp.flags = 0;
+	caps->recording_192_tsp.max_size = 0xFFFFFFFF;
+	caps->recording_192_tsp.size_alignment = 0;
+	caps->playback_188_tsp.flags = 0;
+	caps->playback_188_tsp.max_size = 0xFFFFFFFF;
+	caps->playback_188_tsp.size_alignment = 0;
+	caps->playback_192_tsp.flags = 0;
+	caps->playback_192_tsp.max_size = 0xFFFFFFFF;
+	caps->playback_192_tsp.size_alignment = 0;
 
 	return 0;
 }
@@ -645,6 +663,8 @@
 
 	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
 	mpq_demux->dmxdev.demux->get_caps = mpq_tsif_dmx_get_caps;
+	mpq_demux->dmxdev.demux->map_buffer = mpq_dmx_map_buffer;
+	mpq_demux->dmxdev.demux->unmap_buffer = mpq_dmx_unmap_buffer;
 
 	result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
 	if (result < 0) {
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 c1d1462..e4f00c0 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
@@ -708,6 +708,24 @@
 	caps->max_bitrate = 144;
 	caps->demod_input_max_bitrate = 72;
 	caps->memory_input_max_bitrate = 72;
+	caps->section.flags = 0;
+	caps->section.max_size = 0xFFFFFFFF;
+	caps->section.size_alignment = 0;
+	caps->pes.flags = 0;
+	caps->pes.max_size = 0xFFFFFFFF;
+	caps->pes.size_alignment = 0;
+	caps->recording_188_tsp.flags = 0;
+	caps->recording_188_tsp.max_size = 0xFFFFFFFF;
+	caps->recording_188_tsp.size_alignment = 0;
+	caps->recording_192_tsp.flags = 0;
+	caps->recording_192_tsp.max_size = 0xFFFFFFFF;
+	caps->recording_192_tsp.size_alignment = 0;
+	caps->playback_188_tsp.flags = 0;
+	caps->playback_188_tsp.max_size = 0xFFFFFFFF;
+	caps->playback_188_tsp.size_alignment = 0;
+	caps->playback_192_tsp.flags = 0;
+	caps->playback_192_tsp.max_size = 0xFFFFFFFF;
+	caps->playback_192_tsp.size_alignment = 0;
 
 	return 0;
 }
@@ -766,6 +784,8 @@
 
 	mpq_demux->dmxdev.demux->set_source = mpq_dmx_set_source;
 	mpq_demux->dmxdev.demux->get_caps = mpq_tspp_dmx_get_caps;
+	mpq_demux->dmxdev.demux->map_buffer = mpq_dmx_map_buffer;
+	mpq_demux->dmxdev.demux->unmap_buffer = mpq_dmx_unmap_buffer;
 
 	result = dvb_dmxdev_init(&mpq_demux->dmxdev, mpq_adapter);
 	if (result < 0) {
diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h
index d19dfa5..257e069 100644
--- a/include/linux/dvb/dmx.h
+++ b/include/linux/dvb/dmx.h
@@ -307,6 +307,22 @@
 	} params;
 };
 
+/* Filter's buffer requirement returned in dmx_caps */
+struct dmx_buffer_requirement {
+	/* Buffer size alignment, 0 means no special requirement */
+	__u32 size_alignment;
+
+	/* Maximum buffer size allowed */
+	__u32 max_size;
+	__u32 flags;
+
+/* Buffer allocated as physically contiguous memory */
+#define DMX_BUFFER_CONTIGEOUS_MEM			0x1
+
+/* If the filter's data is decrypted, the buffer should be secured one */
+#define DMX_BUFFER_SECURED_IF_DECRYPTED		0x2
+};
+
 typedef struct dmx_caps {
 	__u32 caps;
 
@@ -363,6 +379,23 @@
 
 	/* Max bitrate from single memory input. Mbit/sec */
 	int memory_input_max_bitrate;
+
+	struct dmx_buffer_requirement section;
+
+	/* For PES not sent to decoder */
+	struct dmx_buffer_requirement pes;
+
+	/* Recording buffer for recording of 188 bytes packets */
+	struct dmx_buffer_requirement recording_188_tsp;
+
+	/* Recording buffer for recording of 192 bytes packets */
+	struct dmx_buffer_requirement recording_192_tsp;
+
+	/* DVR input buffer for playback of 188 bytes packets */
+	struct dmx_buffer_requirement playback_188_tsp;
+
+	/* DVR input buffer for playback of 192 bytes packets */
+	struct dmx_buffer_requirement playback_192_tsp;
 } dmx_caps_t;
 
 typedef enum {
@@ -410,6 +443,28 @@
 	__u64 stc;		/* output: stc in 'base'*90 kHz units */
 };
 
+enum dmx_buffer_mode {
+	/*
+	 * demux buffers are allocated internally
+	 * by the demux driver. This is the default mode.
+	 * DMX_SET_BUFFER_SIZE can be used to set the size of
+	 * this buffer.
+	 */
+	DMX_BUFFER_MODE_INTERNAL,
+
+	/*
+	 * demux buffers are allocated externally and provided
+	 * to demux through DMX_SET_BUFFER.
+	 * When this mode is used DMX_SET_BUFFER_SIZE and
+	 * mmap are prohibited.
+	 */
+	DMX_BUFFER_MODE_EXTERNAL,
+};
+
+struct dmx_buffer {
+	unsigned int size;
+	int handle;
+};
 
 #define DMX_START                _IO('o', 41)
 #define DMX_STOP                 _IO('o', 42)
@@ -430,5 +485,7 @@
 #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)
 
 #endif /*_DVBDMX_H_*/