summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRobby Cai <R63905@freescale.com>2012-09-27 21:50:59 +0800
committerRobby Cai <R63905@freescale.com>2012-09-29 19:33:31 +0800
commit8798da481e86647f5dcadbb16ea93a6c4352df37 (patch)
treecf1e30f34cc6885a6a0bee2b9c74361e912cacfe /drivers
parent8a5015df05daac83d72b93c4dc2c1b6caa9038d2 (diff)
ENGR00225981-6 csi: Add PxP post-processing for CSC
The default output format from camera is UYVY. For preview purpose, need convert the format to RGB565. Use PxP CSC as post processing. Signed-off-by: Robby Cai <R63905@freescale.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/video/mxc/capture/csi_v4l2_capture.c215
-rw-r--r--drivers/media/video/mxc/capture/mxc_v4l2_capture.h12
2 files changed, 227 insertions, 0 deletions
diff --git a/drivers/media/video/mxc/capture/csi_v4l2_capture.c b/drivers/media/video/mxc/capture/csi_v4l2_capture.c
index a4ce862627f0..ada6d5e8afbd 100644
--- a/drivers/media/video/mxc/capture/csi_v4l2_capture.c
+++ b/drivers/media/video/mxc/capture/csi_v4l2_capture.c
@@ -61,6 +61,198 @@ static struct v4l2_int_device csi_v4l2_int_device = {
},
};
+/* Callback function triggered after PxP receives an EOF interrupt */
+static void pxp_dma_done(void *arg)
+{
+ struct pxp_tx_desc *tx_desc = to_tx_desc(arg);
+ struct dma_chan *chan = tx_desc->txd.chan;
+ struct pxp_channel *pxp_chan = to_pxp_channel(chan);
+ cam_data *cam = pxp_chan->client;
+
+ /* This call will signal wait_for_completion_timeout() */
+ complete(&cam->pxp_tx_cmpl);
+}
+
+static bool chan_filter(struct dma_chan *chan, void *arg)
+{
+ if (imx_dma_is_pxp(chan))
+ return true;
+ else
+ return false;
+}
+
+/* Function to request PXP DMA channel */
+static int pxp_chan_init(cam_data *cam)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+
+ /* Request a free channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_PRIVATE, mask);
+ chan = dma_request_channel(mask, chan_filter, NULL);
+ if (!chan) {
+ pr_err("Unsuccessfully request channel!\n");
+ return -EBUSY;
+ }
+
+ cam->pxp_chan = to_pxp_channel(chan);
+ cam->pxp_chan->client = cam;
+
+ init_completion(&cam->pxp_tx_cmpl);
+
+ return 0;
+}
+
+/*
+ * Function to call PxP DMA driver and send our new V4L2 buffer
+ * through the PxP and PxP will process this buffer in place.
+ * Note: This is a blocking call, so upon return the PxP tx should be complete.
+ */
+static int pxp_process_update(cam_data *cam)
+{
+ dma_cookie_t cookie;
+ struct scatterlist *sg = cam->sg;
+ struct dma_chan *dma_chan;
+ struct pxp_tx_desc *desc;
+ struct dma_async_tx_descriptor *txd;
+ struct pxp_config_data *pxp_conf = &cam->pxp_conf;
+ struct pxp_proc_data *proc_data = &cam->pxp_conf.proc_data;
+ int i, ret;
+ int length;
+
+ pr_debug("Starting PxP Send Buffer\n");
+
+ /* First, check to see that we have acquired a PxP Channel object */
+ if (cam->pxp_chan == NULL) {
+ /*
+ * PxP Channel has not yet been created and initialized,
+ * so let's go ahead and try
+ */
+ ret = pxp_chan_init(cam);
+ if (ret) {
+ /*
+ * PxP channel init failed, and we can't use the
+ * PxP until the PxP DMA driver has loaded, so we abort
+ */
+ pr_err("PxP chan init failed\n");
+ return -ENODEV;
+ }
+ }
+
+ /*
+ * Init completion, so that we can be properly informed of
+ * the completion of the PxP task when it is done.
+ */
+ init_completion(&cam->pxp_tx_cmpl);
+
+ dma_chan = &cam->pxp_chan->dma_chan;
+
+ txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg, 2,
+ DMA_TO_DEVICE,
+ DMA_PREP_INTERRUPT);
+ if (!txd) {
+ pr_err("Error preparing a DMA transaction descriptor.\n");
+ return -EIO;
+ }
+
+ txd->callback_param = txd;
+ txd->callback = pxp_dma_done;
+
+ /*
+ * Configure PxP for processing of new v4l2 buf
+ */
+ pxp_conf->s0_param.pixel_fmt = PXP_PIX_FMT_UYVY;
+ pxp_conf->s0_param.color_key = -1;
+ pxp_conf->s0_param.color_key_enable = false;
+ pxp_conf->s0_param.width = cam->v2f.fmt.pix.width;
+ pxp_conf->s0_param.height = cam->v2f.fmt.pix.height;
+
+ pxp_conf->ol_param[0].combine_enable = false;
+
+ proc_data->srect.top = 0;
+ proc_data->srect.left = 0;
+ proc_data->srect.width = pxp_conf->s0_param.width;
+ proc_data->srect.height = pxp_conf->s0_param.height;
+
+ proc_data->drect.top = 0;
+ proc_data->drect.left = 0;
+ proc_data->drect.width = proc_data->srect.width;
+ proc_data->drect.height = proc_data->srect.height;
+ proc_data->scaling = 0;
+ proc_data->hflip = 0;
+ proc_data->vflip = 0;
+ proc_data->rotate = 0;
+ proc_data->bgcolor = 0;
+
+ pxp_conf->out_param.pixel_fmt = PXP_PIX_FMT_RGB565;
+ pxp_conf->out_param.width = proc_data->drect.width;
+ pxp_conf->out_param.height = proc_data->drect.height;
+
+ if (cam->rotation >= IPU_ROTATE_90_RIGHT)
+ pxp_conf->out_param.stride = pxp_conf->out_param.height;
+ else
+ pxp_conf->out_param.stride = pxp_conf->out_param.width;
+
+ desc = to_tx_desc(txd);
+ length = desc->len;
+ for (i = 0; i < length; i++) {
+ if (i == 0) {/* S0 */
+ memcpy(&desc->proc_data, proc_data,
+ sizeof(struct pxp_proc_data));
+ pxp_conf->s0_param.paddr = sg_dma_address(&sg[0]);
+ memcpy(&desc->layer_param.s0_param, &pxp_conf->s0_param,
+ sizeof(struct pxp_layer_param));
+ } else if (i == 1) {
+ pxp_conf->out_param.paddr = sg_dma_address(&sg[1]);
+ memcpy(&desc->layer_param.out_param,
+ &pxp_conf->out_param,
+ sizeof(struct pxp_layer_param));
+ }
+
+ desc = desc->next;
+ }
+
+ /* Submitting our TX starts the PxP processing task */
+ cookie = txd->tx_submit(txd);
+ if (cookie < 0) {
+ pr_err("Error sending FB through PxP\n");
+ return -EIO;
+ }
+
+ cam->txd = txd;
+
+ /* trigger PxP */
+ dma_async_issue_pending(dma_chan);
+
+ return 0;
+}
+
+static int pxp_complete_update(cam_data *cam)
+{
+ int ret;
+ /*
+ * Wait for completion event, which will be set
+ * through our TX callback function.
+ */
+ ret = wait_for_completion_timeout(&cam->pxp_tx_cmpl, HZ / 10);
+ if (ret <= 0) {
+ pr_warning("PxP operation failed due to %s\n",
+ ret < 0 ? "user interrupt" : "timeout");
+ dma_release_channel(&cam->pxp_chan->dma_chan);
+ cam->pxp_chan = NULL;
+ return ret ? : -ETIMEDOUT;
+ }
+
+ dma_release_channel(&cam->pxp_chan->dma_chan);
+ cam->pxp_chan = NULL;
+
+ pr_debug("TX completed\n");
+
+ return 0;
+}
+
/*!
* Camera V4l2 callback function.
*
@@ -666,6 +858,23 @@ static int csi_v4l_dqueue(cam_data *cam, struct v4l2_buffer *buf)
buf->flags = frame->buffer.flags;
buf->m = cam->frame[frame->index].buffer.m;
+ /*
+ * Note:
+ * If want to do preview on LCD, use PxP CSC to convert from UYVY
+ * to RGB565; but for encoding, usually we don't use RGB format.
+ */
+ if (cam->v2f.fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) {
+ /* PxP processes it in place */
+ sg_dma_address(&cam->sg[0]) = buf->m.offset;
+ sg_dma_address(&cam->sg[1]) = buf->m.offset;
+ retval = pxp_process_update(cam);
+ if (retval) {
+ pr_err("Unable to submit PxP update task.\n");
+ return retval;
+ }
+ pxp_complete_update(cam);
+ }
+
return retval;
}
@@ -1382,6 +1591,7 @@ static void csi_v4l2_master_detach(struct v4l2_int_device *slave)
*/
static __init int camera_init(void)
{
+ struct scatterlist *sg;
u8 err = 0;
/* Register the device driver structure. */
@@ -1430,6 +1640,11 @@ static __init int camera_init(void)
pr_debug(" Video device registered: %s #%d\n",
g_cam->video_dev->name, g_cam->video_dev->minor);
+ g_cam->pxp_chan = NULL;
+ /* Initialize Scatter-gather list containing 2 buffer addresses. */
+ sg = g_cam->sg;
+ sg_init_table(sg, 2);
+
return err;
}
diff --git a/drivers/media/video/mxc/capture/mxc_v4l2_capture.h b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
index 27f8a78a93b3..26b14a3eedf0 100644
--- a/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
+++ b/drivers/media/video/mxc/capture/mxc_v4l2_capture.h
@@ -31,6 +31,10 @@
#include <linux/list.h>
#include <linux/ipu.h>
#include <linux/mxc_v4l2.h>
+#include <linux/completion.h>
+#include <linux/dmaengine.h>
+#include <linux/pxp_dma.h>
+#include <mach/dma.h>
#include <mach/ipu-v3.h>
#include <media/v4l2-dev.h>
@@ -205,6 +209,14 @@ typedef struct _cam_data {
struct v4l2_int_device *self;
int sensor_index;
void *ipu;
+
+ /* v4l2 buf elements related to PxP DMA */
+ struct completion pxp_tx_cmpl;
+ struct pxp_channel *pxp_chan;
+ struct pxp_config_data pxp_conf;
+ struct dma_async_tx_descriptor *txd;
+ dma_cookie_t cookie;
+ struct scatterlist sg[2];
} cam_data;
struct sensor_data {