From 20d79137ecaa6c7dad007d9ea1d7be5550db4839 Mon Sep 17 00:00:00 2001
From: Vaibhav Hiremath <hvaibhav@ti.com>
Date: Fri, 13 Feb 2009 15:40:25 +0530
Subject: [PATCH 2/2] Resizer bug fixes on top of 1.0.2 release

This commit contains resizer bug fixes on top of
    PSP1.0.2 release -
       - 4096 aligned address constraint
       - workaround for extra page allocation for page aligned
         size buffers

Signed-off-by: Vaibhav Hiremath <hvaibhav@ti.com>
---
 drivers/media/video/isp/omap_resizer.c |  417 ++++++++++++++++++++++++--------
 include/linux/omap_resizer.h           |    3 +-
 2 files changed, 321 insertions(+), 99 deletions(-)

diff --git a/drivers/media/video/isp/omap_resizer.c b/drivers/media/video/isp/omap_resizer.c
index 54bc425..8059c70 100644
--- a/drivers/media/video/isp/omap_resizer.c
+++ b/drivers/media/video/isp/omap_resizer.c
@@ -28,6 +28,7 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
+#include <linux/pci.h>
 #include <media/v4l2-dev.h>
 #include <asm/cacheflush.h>
 
@@ -76,6 +77,10 @@
 #define MAX_COEF_COUNTER	16
 #define COEFF_ADDRESS_OFFSET	0x04
 
+#define RSZ_DEF_REQ_EXP		0xE	/* Default read operation expand
+					 * for the Resizer driver; value
+					 * taken from Davinci.
+					 */
 /* Global structure which contains information about number of channels
    and protection variables */
 struct device_params {
@@ -85,6 +90,7 @@ struct device_params {
 	struct mutex reszwrap_mutex;		/* Semaphore for array */
 
 	struct videobuf_queue_ops vbq_ops;	/* videobuf queue operations */
+	unsigned long extra_page_addr;
 };
 
 /* Register mapped structure which contains the every register
@@ -126,6 +132,9 @@ struct resizer_config {
 	u32 rsz_yehn;				/* yehn(luma)register mapping
 						 * variable.
 						 */
+	u32 sdr_req_exp;			/* Configuration for Non
+						 * real time read expand
+						 */
 };
 
 struct rsz_mult {
@@ -179,6 +188,7 @@ struct channel_config {
 						 * channel is busy or not
 						 */
 	struct mutex chanprotection_mutex;
+	int buf_address[VIDEO_MAX_FRAME];
 	enum config_done config_state;
 	u8 input_buf_index;
 	u8 output_buf_index;
@@ -200,8 +210,6 @@ struct rsz_fh {
 	struct videobuf_queue vbq;
 	struct device_params *device;
 
-	dma_addr_t isp_addr_read;		/* Input/Output address */
-	dma_addr_t isp_addr_write;		/* Input/Output address */
 	u32 rsz_bufsize;			/* channel specific buffersize
 						 */
 };
@@ -227,6 +235,10 @@ static int rsz_set_ratio(struct rsz_mult *multipass,
 static void rsz_config_ratio(struct rsz_mult *multipass,
 					struct channel_config *rsz_conf_chan);
 
+static void inline rsz_set_exp(unsigned int exp)
+{
+	omap_writel(((exp & 0x3FF) << 10), OMAP3ISP_SBL_REG(0xF8));
+}
 /**
  * rsz_hardware_setup - Sets hardware configuration registers
  * @rsz_conf_chan: Structure containing channel configuration
@@ -271,12 +283,15 @@ static void rsz_hardware_setup(struct channel_config *rsz_conf_chan)
 						+ coeffoffset));
 		coeffoffset = coeffoffset + COEFF_ADDRESS_OFFSET;
 	}
+	/* Configure the read expand register */
+	rsz_set_exp(rsz_conf_chan->register_config.sdr_req_exp);
 }
 
 /**
  * rsz_start - Enables Resizer Wrapper
  * @arg: Currently not used.
- * @device: Structure containing ISP resizer wrapper global information
+ * @fh: File structure containing ISP resizer information specific to
+ *      channel opened.
  *
  * Submits a resizing task specified by the rsz_resize structure. The call can
  * either be blocked until the task is completed or returned immediately based
@@ -292,12 +307,18 @@ int rsz_start(int *arg, struct rsz_fh *fh)
 	struct channel_config *rsz_conf_chan = fh->config;
 	struct rsz_mult *multipass = fh->multipass;
 	struct videobuf_queue *q = &fh->vbq;
+	struct videobuf_buffer *buf;
 	int ret;
 
 	if (rsz_conf_chan->config_state) {
 		dev_err(rsz_device, "State not configured \n");
 		goto err_einval;
 	}
+	if (!rsz_conf_chan->register_config.rsz_sdr_inadd ||
+			!rsz_conf_chan->register_config.rsz_sdr_outadd) {
+		dev_err(rsz_device, "address is null\n");
+		goto err_einval;
+	}
 
 	rsz_conf_chan->status = CHANNEL_BUSY;
 
@@ -325,33 +346,22 @@ mult:
 		goto mult;
 	}
 
-	if (fh->isp_addr_read) {
-		ispmmu_vunmap(fh->isp_addr_read);
-		fh->isp_addr_read = 0;
-	}
-	if (fh->isp_addr_write) {
-		ispmmu_vunmap(fh->isp_addr_write);
-		fh->isp_addr_write = 0;
-	}
-
 	rsz_conf_chan->status = CHANNEL_FREE;
-	q->bufs[rsz_conf_chan->input_buf_index]->state = VIDEOBUF_NEEDS_INIT;
-	q->bufs[rsz_conf_chan->output_buf_index]->state = VIDEOBUF_NEEDS_INIT;
 	rsz_conf_chan->register_config.rsz_sdr_outadd = 0;
 	rsz_conf_chan->register_config.rsz_sdr_inadd = 0;
 
-	/* Unmap and free the DMA memory allocated for buffers */
-	videobuf_dma_unmap(q, videobuf_to_dma(
-				q->bufs[rsz_conf_chan->input_buf_index]));
-	videobuf_dma_unmap(q, videobuf_to_dma(
-				q->bufs[rsz_conf_chan->output_buf_index]));
-	videobuf_dma_free(videobuf_to_dma(
-				q->bufs[rsz_conf_chan->input_buf_index]));
-	videobuf_dma_free(videobuf_to_dma(
-				q->bufs[rsz_conf_chan->output_buf_index]));
-
 	isp_unset_callback(CBK_RESZ_DONE);
 
+	/* Empty the Videobuf queue which was filled during the qbuf */
+	buf = q->bufs[rsz_conf_chan->input_buf_index];
+	buf->state = VIDEOBUF_IDLE;
+	list_del(&buf->stream);
+	if (rsz_conf_chan->input_buf_index != rsz_conf_chan->output_buf_index) {
+		buf = q->bufs[rsz_conf_chan->output_buf_index];
+		buf->state = VIDEOBUF_IDLE;
+		list_del(&buf->stream);
+	}
+
 	return 0;
 err_einval:
 	return -EINVAL;
@@ -359,6 +369,8 @@ err_einval:
 
 /**
  * rsz_set_multipass - Set resizer multipass
+ * @multipass: Structure containing channel configuration
+			for multipass support
  * @rsz_conf_chan: Structure containing channel configuration
  *
  * Returns always 0
@@ -384,6 +396,8 @@ static int rsz_set_multipass(struct rsz_mult *multipass,
 
 /**
  * rsz_copy_data - Copy data
+ * @multipass: Structure containing channel configuration
+			for multipass support
  * @params: Structure containing the Resizer Wrapper parameters
  *
  * Copy data
@@ -413,6 +427,8 @@ static void rsz_copy_data(struct rsz_mult *multipass, struct rsz_params *params)
 
 /**
  * rsz_set_params - Set parameters for resizer wrapper
+ * @multipass: Structure containing channel configuration
+			for multipass support
  * @params: Structure containing the Resizer Wrapper parameters
  * @rsz_conf_chan: Structure containing channel configuration
  *
@@ -524,6 +540,8 @@ static int rsz_set_params(struct rsz_mult *multipass, struct rsz_params *params,
 	}
 
 	rsz_config_ratio(multipass, rsz_conf_chan);
+	/* Default value for read expand:Taken from Davinci */
+	rsz_conf_chan->register_config.sdr_req_exp = RSZ_DEF_REQ_EXP;
 
 	rsz_conf_chan->config_state = STATE_CONFIGURED;
 
@@ -534,6 +552,8 @@ err_einval:
 
 /**
  * rsz_set_ratio - Set ratio
+ * @multipass: Structure containing channel configuration
+			for multipass support
  * @rsz_conf_chan: Structure containing channel configuration
  *
  * Returns 0 if successful, -EINVAL if invalid output size, upscaling ratio is
@@ -548,7 +568,8 @@ static int rsz_set_ratio(struct rsz_mult *multipass,
 
 	if ((multipass->out_hsize > MAX_IMAGE_WIDTH) ||
 			(multipass->out_vsize > MAX_IMAGE_WIDTH)) {
-		dev_err(rsz_device, "Invalid output size!");
+		dev_err(rsz_device, "Invalid output size! - %d", \
+					multipass->out_hsize);
 		goto err_einval;
 	}
 	if (multipass->cbilin) {
@@ -758,6 +779,8 @@ err_einval:
 
 /**
  * rsz_config_ratio - Configure ratio
+ * @multipass: Structure containing channel configuration
+			for multipass support
  * @rsz_conf_chan: Structure containing channel configuration
  *
  * Configure ratio
@@ -789,6 +812,20 @@ static void rsz_config_ratio(struct rsz_mult *multipass,
 					((vsize << ISPRSZ_IN_SIZE_VERT_SHIFT)
 					& ISPRSZ_IN_SIZE_VERT_MASK);
 
+	/* This is another workaround for the ISP-MMU translation fault.
+	   For the parameters whose image size comes exactly to PAGE_SIZE
+	   generates ISP-MMU translation fault. The root-cause is the equation
+		input width = (32*sph + (ow - 1)*hrsz + 16) >> 8 + 7
+			= (64*sph + (ow - 1)*hrsz + 32) >> 8 + 7
+		input height = (32*spv + (oh - 1)*vrsz + 16) >> 8 + 4
+		= (64*spv + (oh - 1)*vrsz + 32) >> 8 + 7
+
+	   we are adjusting the input width to suit for Resizer module,
+	   application should use this configuration henceforth.
+	 */
+	multipass->in_hsize = hsize;
+	multipass->in_vsize = vsize;
+
 	for (coeffcounter = 0; coeffcounter < MAX_COEF_COUNTER;
 							coeffcounter++) {
 		if (multipass->num_htap) {
@@ -990,24 +1027,15 @@ static void rsz_calculate_crop(struct channel_config *rsz_conf_chan,
 static void rsz_vbq_release(struct videobuf_queue *q,
 						struct videobuf_buffer *vb)
 {
-	int i;
 	struct rsz_fh *fh = q->priv_data;
+	struct videobuf_dmabuf *dma = NULL;
 
-	for (i = 0; i < VIDEO_MAX_FRAME; i++) {
-		struct videobuf_dmabuf *dma = NULL;
-		if (!q->bufs[i])
-			continue;
-		if (q->bufs[i]->memory != V4L2_MEMORY_MMAP)
-			continue;
-		dma = videobuf_to_dma(q->bufs[i]);
-		videobuf_dma_unmap(q, dma);
-		videobuf_dma_free(dma);
-	}
+	dma = videobuf_to_dma(q->bufs[vb->i]);
+	videobuf_dma_unmap(q, dma);
+	videobuf_dma_free(dma);
+	ispmmu_vunmap(fh->config->buf_address[vb->i]);
+	fh->config->buf_address[vb->i] = 0;
 
-	ispmmu_vunmap(fh->isp_addr_read);
-	ispmmu_vunmap(fh->isp_addr_write);
-	fh->isp_addr_read = 0;
-	fh->isp_addr_write = 0;
 	spin_lock(&fh->vbq_lock);
 	vb->state = VIDEOBUF_NEEDS_INIT;
 	spin_unlock(&fh->vbq_lock);
@@ -1062,7 +1090,105 @@ err_einval:
 	spin_unlock(&fh->vbq_lock);
 	return -EINVAL;
 }
+/*
+ * This function is work around for the videobuf_iolock API,
+ * for User memory allocated with ioremap (VM_IO flag) the API
+ * get_user_pages fails.
+ *
+ * To fulfill this requirement, we have completely ignored VM layer of
+ * Linux, and configuring the ISP MMU with physical address.
+ */
+static int omap_videobuf_dma_init_user(struct videobuf_buffer *vb,
+		unsigned long physp, unsigned long asize)
+{
+	struct videobuf_dmabuf *dma;
+	struct scatterlist *sglist;
+	unsigned long data, first, last;
+	int len, i = 0;
+
+	dma = videobuf_to_dma(vb);
+	data = vb->baddr;
+
+        first = (data & PAGE_MASK) >> PAGE_SHIFT;
+        last  = ((data+asize-1) & PAGE_MASK) >> PAGE_SHIFT;
+        dma->offset   = data & ~PAGE_MASK;
+        dma->nr_pages = last-first+1;
+
+	dma->direction = PCI_DMA_FROMDEVICE;
+	/*
+	 * Allocate array of sglen + 1, to add entry of extra page
+	 * for input buffer. Driver always uses 0th buffer as input buffer.
+	 */
+	len = dma->nr_pages + (vb->i ? 0 : 1);
+	sglist = kcalloc(len, sizeof(*sglist), GFP_KERNEL);
+	if (NULL == sglist)
+                return -ENOMEM;
+
+	sglist[0].offset = 0;
+	sglist[0].length = PAGE_SIZE - dma->offset;
+	sglist[0].dma_address = (dma_addr_t)physp;
+	physp += sglist[0].length;
+	/*
+	 * Iterate in a loop for the number of pages
+	 */
+	for (i = 1; i < (len - (vb->i ? 0 : 1)); i++) {
+		sglist[i].offset = 0;
+		sglist[i].length = PAGE_SIZE;
+		sglist[i].dma_address = (dma_addr_t)physp;
+		physp += PAGE_SIZE;
+	}
+	if (0 == vb->i) {
+		sglist[i].offset = 0;
+		sglist[i].length = PAGE_SIZE;
+		sglist[i].dma_address =
+			(dma_addr_t)device_config->extra_page_addr;
+        }
+	dma->sglist = sglist;
+	dma->sglen = len;
+        return 0;
+
+		}
+/*
+ * This function is workaround for the issue, where ISP-MMU generated
+ * translation fault for specific params whose size is aligned to PAGE_SIZE.
+
+ * As a workaround we are padding one extra page for input buffer. This page
+ * we are allocating during init time and will not be released through-out
+ * life time of resizer driver. Please note that Resizer module only reads
+ * from this extra page.
+ */
+int omap_create_sg(struct videobuf_queue *q, struct videobuf_dmabuf *dma)
+{
+	struct scatterlist *sglist;
+	int sglen;
 
+	sglen = dma->sglen;
+	sglist = kcalloc(sglen + 1, sizeof(*sglist), GFP_KERNEL);
+	if (NULL == sglist)
+		return -ENOMEM;
+	/*
+	 * Copy the sglist locally
+	 */
+	memcpy(sglist, dma->sglist, sglen * sizeof(*sglist));
+	/*
+	 * Release the old sglist, since we already copied it locally
+	 */
+	videobuf_dma_unmap(q, dma);
+	/*
+	 * Add extra entry to sglist to work with specific params, whose
+	 * buffer address alined to PAGE_SIZE.
+	 */
+	sglist[sglen].offset = 0;
+	sglist[sglen].length = PAGE_SIZE;
+	sglist[sglen].dma_address = (dma_addr_t)device_config->extra_page_addr;
+	sglen++;
+	/*
+	 * Save the sglist for mapping to ISP-MMU space
+	 */
+	dma->sglist = sglist;
+	dma->sglen = sglen;
+	return 0;
+}
 /**
  * rsz_vbq_prepare - Videobuffer is prepared and mmapped.
  * @q: Structure containing the videobuffer queue file handle, and device
@@ -1079,19 +1205,24 @@ static int rsz_vbq_prepare(struct videobuf_queue *q,
 {
 	struct rsz_fh *fh = q->priv_data;
 	struct channel_config *rsz_conf_chan = fh->config;
-	struct rsz_mult *multipass = fh->multipass;
 	int err = 0;
 	unsigned int isp_addr, insize, outsize;
-	struct videobuf_dmabuf *dma = videobuf_to_dma(vb);
-
+	struct rsz_mult *multipass = fh->multipass;
 	spin_lock(&fh->vbq_lock);
 	if (vb->baddr) {
+		/* Check for 32 byte alignement */
+		if (vb->baddr != (vb->baddr & ~0x1F)) {
+			spin_unlock(&fh->vbq_lock);
+			dev_err(rsz_device, "Buffer address should be aligned \
+					to 32 byte\n");
+			return -EINVAL;
+		}
 		vb->size = fh->rsz_bufsize;
 		vb->bsize = fh->rsz_bufsize;
 	} else {
 		spin_unlock(&fh->vbq_lock);
 		dev_err(rsz_device, "No user buffer allocated\n");
-		goto out;
+		return -EINVAL;
 	}
 	if (vb->i) {
 		vb->width = fh->params->out_hsize;
@@ -1103,55 +1234,128 @@ static int rsz_vbq_prepare(struct videobuf_queue *q,
 
 	vb->field = field;
 	spin_unlock(&fh->vbq_lock);
+	/*
+	 * Calculate input and output sizes, will be used while mapping
+	 * user pages
+	 */
+	outsize = multipass->out_pitch * multipass->out_vsize;
+	insize = multipass->in_pitch * multipass->in_vsize;
 
 	if (vb->state == VIDEOBUF_NEEDS_INIT) {
-		err = videobuf_iolock(q, vb, NULL);
-		if (!err) {
-			isp_addr = ispmmu_vmap(dma->sglist, dma->sglen);
-			if (!isp_addr)
-				err = -EIO;
-			else {
-				if (vb->i) {
-					rsz_conf_chan->register_config.
-							rsz_sdr_outadd
-							= isp_addr;
-					fh->isp_addr_write = isp_addr;
-					rsz_conf_chan->output_buf_index = vb->i;
-				} else {
+		struct videobuf_dmabuf *dma;
+		struct vm_area_struct *vma;
+		spin_lock(&fh->vbq_lock);
+		dma = videobuf_to_dma(vb);
+		vma = find_vma(current->mm, vb->baddr);
+		if ((vma) && (vma->vm_flags & VM_IO) && (vma->vm_pgoff)) {
+			/* This will catch ioremaped buffers to the kernel.
+			 *  It gives two possible scenarios -
+			 *  - Driver allocates buffer using either
+			 *    dma_alloc_coherent or get_free_pages,
+			 *    and maps to user space using
+			 *    io_remap_pfn_range/remap_pfn_range
+			 *  - Drivers maps memory outside from Linux using
+			 *    io_remap
+			 */
+			unsigned long physp = 0, asize;
+			asize = vb->i ? outsize : insize;
+			if ((vb->baddr + asize) > vma->vm_end) {
+				spin_unlock(&fh->vbq_lock);
+				dev_err(rsz_device, "User Buffer Allocation:" \
+					"err=%lu[%lu]\n",\
+					(vma->vm_end - vb->baddr), asize);
+				return -ENOMEM;
+			}
+			physp = (vma->vm_pgoff << PAGE_SHIFT) +
+					(vb->baddr - vma->vm_start);
+			err = omap_videobuf_dma_init_user(vb, physp, asize);
+			spin_unlock(&fh->vbq_lock);
+			if (0 != err)
+				return err;
+		} else {
+			err = videobuf_iolock(q, vb, NULL);
+			/*
+			 * In case of user pointer mode, the get_user_pages
+			 * will fail if user has allocated less memory than
+			 * vb->size. But it is not error from resizer driver
+			 * point of view. so handled seperately
+			 */
+			if ((err < 0) && (dma->nr_pages > 0))
+				err = videobuf_dma_map(q, dma);
+			if (err)
+				goto buf_release;
+			/*
+			 * Add one extra page for input buffer
+			 */
+			if (0 == vb->i)
+				err = omap_create_sg(q, dma);
+			if (err)
+				goto buf_release;
+			spin_unlock(&fh->vbq_lock);
+		}
+		isp_addr = ispmmu_vmap(dma->sglist, dma->sglen);
+		if (!isp_addr)
+			err = -EIO;
+		else {
+			if (vb->i) {
+				rsz_conf_chan->buf_address[vb->i] = isp_addr;
+				rsz_conf_chan->register_config.
+					rsz_sdr_outadd
+					= isp_addr;
+				rsz_conf_chan->output_buf_index = vb->i;
+			} else {
+				rsz_conf_chan->buf_address[vb->i] = isp_addr;
+				rsz_conf_chan->register_config.
+					rsz_sdr_inadd
+					= isp_addr;
+				rsz_conf_chan->input_buf_index = vb->i;
+				if (outsize < insize && rsz_conf_chan->
+						register_config.
+						rsz_sdr_outadd == 0) {
 					rsz_conf_chan->register_config.
-							rsz_sdr_inadd
-							= isp_addr;
-					rsz_conf_chan->input_buf_index = vb->i;
-					outsize = multipass->out_pitch *
-							multipass->out_vsize;
-					insize = multipass->in_pitch *
-							multipass->in_vsize;
-					if (outsize < insize) {
-						rsz_conf_chan->register_config.
-								rsz_sdr_outadd
-								= isp_addr;
-						rsz_conf_chan->
-							output_buf_index =
-							vb->i;
-					}
-
-					fh->isp_addr_read = isp_addr;
+						rsz_sdr_outadd
+						= isp_addr;
+					rsz_conf_chan->
+						output_buf_index =
+						vb->i;
 				}
 			}
 		}
 
-	}
+	} else {
+		if(vb->i) {
+			rsz_conf_chan->register_config.
+				rsz_sdr_outadd =
+					rsz_conf_chan->buf_address[vb->i];
+			rsz_conf_chan->output_buf_index = vb->i;
+		} else {
+			rsz_conf_chan->register_config.
+				rsz_sdr_inadd =
+					rsz_conf_chan->buf_address[vb->i];
+			rsz_conf_chan->input_buf_index = vb->i;
+			if(outsize < insize && rsz_conf_chan->
+					register_config.
+					rsz_sdr_outadd == 0) {
+				rsz_conf_chan->register_config.
+					rsz_sdr_outadd
+					= rsz_conf_chan->buf_address[vb->i];
+				rsz_conf_chan->output_buf_index = vb->i;
+			}
+
+		}
 
+	}
 	if (!err) {
 		spin_lock(&fh->vbq_lock);
 		vb->state = VIDEOBUF_PREPARED;
 		spin_unlock(&fh->vbq_lock);
-		flush_cache_user_range(NULL, vb->baddr, (vb->baddr
-								+ vb->bsize));
 	} else
 		rsz_vbq_release(q, vb);
 
-out:
+	return err;
+buf_release:
+	spin_unlock(&fh->vbq_lock);
+	rsz_vbq_release(q, vb);
 	return err;
 }
 
@@ -1255,7 +1459,8 @@ err_enomem0:
  **/
 static int rsz_release(struct inode *inode, struct file *filp)
 {
-	u32 timeout = 0;
+	int i;
+	unsigned int timeout = 0;
 	struct rsz_fh *fh = filp->private_data;
 	struct channel_config *rsz_conf_chan = fh->config;
 	struct rsz_params *params = fh->params;
@@ -1266,17 +1471,17 @@ static int rsz_release(struct inode *inode, struct file *filp)
 		timeout++;
 		schedule();
 	}
-	if (mutex_lock_interruptible(&device_config->reszwrap_mutex))
-		return -EINTR;
-	device_config->opened--;
-	mutex_unlock(&device_config->reszwrap_mutex);
-	/* This will Free memory allocated to the buffers,
-	 * and flushes the queue
-	 */
-	videobuf_queue_cancel(q);
-	fh->params = NULL;
-	fh->config = NULL;
+	/* Free memory allocated to the buffers */
+	for (i = 0 ; i < VIDEO_MAX_FRAME ; i++) {
+		struct videobuf_dmabuf *dma = NULL;
+		if (!q->bufs[i])
+			continue;
+		dma = videobuf_to_dma(q->bufs[i]);
+		videobuf_dma_unmap(q, dma);
+		videobuf_dma_free(dma);
+	}
 
+	videobuf_mmap_free(q);
 	fh->rsz_bufsize = 0;
 	filp->private_data = NULL;
 
@@ -1286,7 +1491,8 @@ static int rsz_release(struct inode *inode, struct file *filp)
 	kfree(fh);
 
 	isp_put();
-
+	fh->params = NULL;
+	fh->config = NULL;
 	return 0;
 }
 
@@ -1353,6 +1559,12 @@ static long rsz_unlocked_ioctl(struct file *file, unsigned int cmd,
 							chanprotection_mutex))
 			return -EINTR;
 		ret = videobuf_reqbufs(&fh->vbq, (void *)&req_buf);
+		if (ret >= 0) {
+			if (copy_to_user((struct v4l2_requestbuffers *)arg,
+						&req_buf, sizeof(struct
+						v4l2_requestbuffers)))
+				return -EFAULT;
+		}
 		mutex_unlock(&rsz_conf_chan->chanprotection_mutex);
 		break;
 	}
@@ -1394,11 +1606,7 @@ static long rsz_unlocked_ioctl(struct file *file, unsigned int cmd,
 						sizeof(struct rsz_params))) {
 			return -EFAULT;
 		}
-		if (mutex_lock_interruptible(&rsz_conf_chan->
-							chanprotection_mutex))
-			return -EINTR;
-		ret = rsz_set_params(fh->multipass, params, rsz_conf_chan);
-		mutex_unlock(&rsz_conf_chan->chanprotection_mutex);
+		ret = rsz_set_params(fh->multipass, fh->params,	rsz_conf_chan);
 		break;
 	}
 	case RSZ_G_PARAM:
@@ -1433,6 +1641,12 @@ static long rsz_unlocked_ioctl(struct file *file, unsigned int cmd,
 		rsz_calculate_crop(rsz_conf_chan, (struct rsz_cropsize *)arg);
 		break;
 
+	case RSZ_S_EXP:
+		if (mutex_lock_interruptible(&rsz_conf_chan->chanprotection_mutex))
+			return -EINTR;
+		rsz_conf_chan->register_config.sdr_req_exp = *((unsigned int *)arg);
+		mutex_unlock(&rsz_conf_chan->chanprotection_mutex);
+		break;
 	default:
 		dev_err(rsz_device, "resizer_ioctl: Invalid Command Value");
 		return -EINVAL;
@@ -1535,14 +1749,18 @@ static int __init omap_rsz_init(void)
 								"memory\n");
 		return -ENOMEM;
 	}
-
+	device->extra_page_addr = __get_free_pages(GFP_KERNEL | GFP_DMA, 0);
+	if (!device->extra_page_addr) {
+		dev_err(rsz_device, OMAP_REZR_NAME ":Allocation failed. ");
+		kfree(device);
+		return -ENOMEM;
+	}
 	ret = alloc_chrdev_region(&dev, 0, 1, OMAP_REZR_NAME);
 	if (ret < 0) {
 		dev_err(rsz_device, OMAP_REZR_NAME ": intialization failed. "
 			"Could not allocate region "
 			"for character device\n");
-		kfree(device);
-		return -ENODEV;
+		goto fail1;
 	}
 
 	/* Register the driver in the kernel */
@@ -1608,6 +1826,8 @@ fail3:
 	cdev_del(&c_dev);
 fail2:
 	unregister_chrdev_region(dev, 1);
+fail1:
+	free_pages((unsigned long)device->extra_page_addr, 0);
 	kfree(device);
 	return ret;
 }
@@ -1623,6 +1843,7 @@ void __exit omap_rsz_exit(void)
 	platform_driver_unregister(&omap_resizer_driver);
 	cdev_del(&c_dev);
 	unregister_chrdev_region(dev, 1);
+	free_pages((unsigned long)device_config->extra_page_addr, 0);
 	kfree(device_config);
 }
 
diff --git a/include/linux/omap_resizer.h b/include/linux/omap_resizer.h
index 5ac0c88..47b8dd8 100644
--- a/include/linux/omap_resizer.h
+++ b/include/linux/omap_resizer.h
@@ -21,7 +21,7 @@
 
 /* ioctls definition */
 #define RSZ_IOC_BASE		'R'
-#define RSZ_IOC_MAXNR		8
+#define RSZ_IOC_MAXNR		9
 
 /*Ioctl options which are to be passed while calling the ioctl*/
 #define RSZ_REQBUF		_IOWR(RSZ_IOC_BASE, 1,\
@@ -33,6 +33,7 @@
 #define RSZ_G_STATUS		_IOWR(RSZ_IOC_BASE, 6, struct rsz_status)
 #define RSZ_QUEUEBUF		_IOWR(RSZ_IOC_BASE, 7, struct v4l2_buffer)
 #define RSZ_GET_CROPSIZE	_IOWR(RSZ_IOC_BASE, 8, struct rsz_cropsize)
+#define RSZ_S_EXP		_IOWR(RSZ_IOC_BASE, 9, __s32)
 
 #define RSZ_INTYPE_YCBCR422_16BIT	0
 #define RSZ_INTYPE_PLANAR_8BIT		1
-- 
1.6.0.3

