aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/media/video/s3c_camera_driver.c1871
-rw-r--r--drivers/media/video/s3c_camif.c1863
-rw-r--r--drivers/media/video/s3c_camif.h404
-rw-r--r--drivers/media/video/videodev2_s3c.h210
4 files changed, 4348 insertions, 0 deletions
diff --git a/drivers/media/video/s3c_camera_driver.c b/drivers/media/video/s3c_camera_driver.c
new file mode 100644
index 00000000000..f442ae3bab7
--- /dev/null
+++ b/drivers/media/video/s3c_camera_driver.c
@@ -0,0 +1,1871 @@
+/* drivers/media/video/s3c_camera_driver.c
+ *
+ * Copyright (c) 2008 Samsung Electronics
+ *
+ * Samsung S3C Camera driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/signal.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/kmod.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/mm.h>
+#include <linux/videodev2.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/semaphore.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-camif.h>
+#include <media/v4l2-dev.h>
+#include "s3c_camif.h"
+#include "videodev2_s3c.h"
+
+static struct clk *cam_clock;
+camif_cfg_t s3c_fimc[CAMIF_DEV_NUM];
+extern camif_cis_t msdma_input;
+extern int s3c_camif_do_postprocess(camif_cfg_t *cfg);
+
+/*************************************************************************
+ * Utility part
+ ************************************************************************/
+camif_cfg_t *s3c_camif_get_fimc_object(int nr)
+{
+ camif_cfg_t *ret = NULL;
+
+ switch (nr) {
+ case CODEC_MINOR:
+ ret = &s3c_fimc[FIMC_CODEC_INDEX];
+ break;
+
+ case PREVIEW_MINOR:
+ ret = &s3c_fimc[FIMC_PREVIEW_INDEX];
+ break;
+
+ default:
+ printk(KERN_ERR "Unknown minor number\n");
+ }
+
+ return ret;
+}
+
+#if defined(FSM_ON_PREVIEW)
+static int s3c_camif_check_global_status(camif_cfg_t *cfg)
+{
+ int ret = 0;
+
+ if (down_interruptible((struct semaphore *) &cfg->cis->lock))
+ return -ERESTARTSYS;
+
+ if (cfg->cis->status & CWANT2START) {
+ cfg->cis->status &= ~CWANT2START;
+ cfg->auto_restart = 1;
+ ret = 1;
+ } else {
+ ret = 0; /* There is no codec */
+ cfg->auto_restart = 0; /* Duplicated ..Dummy */
+ }
+
+ up((struct semaphore *) &cfg->cis->lock);
+
+ return ret;
+}
+#endif
+
+static int s3c_camif_convert_format(int pixfmt, int *fmtptr)
+{
+ int fmt = CAMIF_YCBCR420;
+ int depth = 12;
+
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ fmt = CAMIF_RGB16;
+ depth = 16;
+ break;
+
+ case V4L2_PIX_FMT_BGR24: /* Not tested */
+ case V4L2_PIX_FMT_RGB24:
+ fmt = CAMIF_RGB24;
+ depth = 24;
+ break;
+
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB32:
+ fmt = CAMIF_RGB24;
+ depth = 32;
+ break;
+
+ case V4L2_PIX_FMT_GREY: /* Not tested */
+ fmt = CAMIF_YCBCR420;
+ depth = 8;
+ break;
+
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ fmt = CAMIF_YCBCR422I;
+ depth = 16;
+ break;
+
+ case V4L2_PIX_FMT_YUV422P:
+ fmt = CAMIF_YCBCR422;
+ depth = 16;
+ break;
+
+ case V4L2_PIX_FMT_YUV420:
+ fmt = CAMIF_YCBCR420;
+ depth = 12;
+ break;
+ }
+
+ if (fmtptr) *fmtptr = fmt;
+
+ return depth;
+}
+
+static int s3c_camif_set_fb_info(camif_cfg_t *cfg, int depth, int fourcc)
+{
+ /* To define v4l2_format used currently */
+ cfg->v2.frmbuf.fmt.width = cfg->target_x;
+ cfg->v2.frmbuf.fmt.height = cfg->target_y;
+ cfg->v2.frmbuf.fmt.field = V4L2_FIELD_NONE;
+ cfg->v2.frmbuf.fmt.pixelformat = fourcc;
+ cfg->v2.frmbuf.fmt.bytesperline = cfg->v2.frmbuf.fmt.width * depth >> 3;
+ cfg->v2.frmbuf.fmt.sizeimage = cfg->v2.frmbuf.fmt.height * cfg->v2.frmbuf.fmt.bytesperline;
+
+ return 0;
+}
+
+static int s3c_camif_convert_type(camif_cfg_t *cfg, int f)
+{
+ int pixfmt;
+ cfg->target_x = cfg->v2.frmbuf.fmt.width;
+ cfg->target_y = cfg->v2.frmbuf.fmt.height;
+
+ s3c_camif_convert_format(cfg->v2.frmbuf.fmt.pixelformat, &pixfmt);
+
+ cfg->dst_fmt = pixfmt;
+
+ return 0;
+}
+
+/*************************************************************************
+ * Control part
+ ************************************************************************/
+static int s3c_camif_start_capture(camif_cfg_t * cfg)
+{
+ int ret = 0;
+
+ cfg->capture_enable = CAMIF_DMA_ON;
+
+ s3c_camif_start_dma(cfg);
+
+ cfg->status = CAMIF_STARTED;
+
+ if (!(cfg->fsm == CAMIF_SET_LAST_INT || cfg->fsm == CAMIF_CONTINUOUS_INT)) {
+ cfg->fsm = CAMIF_DUMMY_INT;
+ cfg->perf.frames = 0;
+ }
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ if (cfg->input_channel == MSDMA_FROM_CODEC)
+ s3c_camif_start_codec_msdma(cfg);
+#endif
+ return ret;
+}
+
+ssize_t s3c_camif_start_preview(camif_cfg_t *cfg)
+{
+ cfg->capture_enable = CAMIF_DMA_ON;
+
+ s3c_camif_start_dma(cfg);
+
+ cfg->status = CAMIF_STARTED;
+ cfg->fsm = CAMIF_1st_INT;
+ cfg->perf.frames = 0;
+
+ return 0;
+}
+
+ssize_t s3c_camif_stop_preview(camif_cfg_t *cfg)
+{
+ cfg->capture_enable = CAMIF_DMA_OFF;
+ cfg->status = CAMIF_STOPPED;
+
+ s3c_camif_stop_dma(cfg);
+
+ cfg->perf.frames = 0;
+
+ return 0;
+}
+
+ssize_t s3c_camif_stop_capture(camif_cfg_t *cfg)
+{
+ cfg->capture_enable = CAMIF_DMA_OFF;
+ cfg->status = CAMIF_STOPPED;
+
+ s3c_camif_stop_dma(cfg);
+
+ cfg->perf.frames = 0;
+
+ return 0;
+}
+
+ssize_t s3c_camif_stop_fimc(camif_cfg_t *cfg)
+{
+ cfg->capture_enable = CAMIF_BOTH_DMA_OFF;
+ cfg->fsm = CAMIF_DUMMY_INT;
+ cfg->perf.frames = 0;
+
+ s3c_camif_stop_dma(cfg);
+
+ return 0;
+}
+
+#if defined(FSM_ON_PREVIEW)
+static void s3c_camif_start_preview_with_codec(camif_cfg_t *cfg)
+{
+ camif_cfg_t *other = (camif_cfg_t *)cfg->other;
+
+ /* Preview Stop */
+ cfg->capture_enable = CAMIF_DMA_OFF;
+ s3c_camif_stop_dma(cfg);
+
+ /* Start Preview and CODEC */
+ cfg->capture_enable =CAMIF_BOTH_DMA_ON;
+
+ s3c_camif_start_dma(cfg);
+ cfg->fsm = CAMIF_1st_INT; /* For Preview */
+
+ if (!other)
+ panic("Unexpected error: other is null\n");
+
+ switch (other->pp_num) {
+ case 4:
+ other->fsm = CAMIF_1st_INT; /* For CODEC */
+ break;
+
+ case 1:
+ other->fsm = CAMIF_Yth_INT;
+ break;
+
+ default:
+ panic("Invalid pingpong number");
+ break;
+ }
+}
+
+static void s3c_camif_auto_restart(camif_cfg_t *cfg)
+{
+ if (cfg->auto_restart)
+ s3c_camif_start_preview_with_codec(cfg);
+}
+#endif
+
+static void s3c_camif_change_mode(camif_cfg_t *cfg, int mode)
+{
+ camif_cis_t *cis = cfg->cis;
+ int res;
+
+ if (mode == SENSOR_MAX) {
+#if defined(CONFIG_VIDEO_SAMSUNG_S5K3AA)
+ res = SENSOR_SXGA;
+#elif defined(CONFIG_VIDEO_SAMSUNG_S5K3BA)
+ res = SENSOR_UXGA;
+
+/* 4BA max is UXGA, but we don't have UXGA control values */
+#elif defined(CONFIG_VIDEO_SAMSUNG_S5K4BA)
+ res = SENSOR_SVGA;
+#endif
+ } else if (mode == SENSOR_DEFAULT) {
+#if defined(CONFIG_VIDEO_SAMSUNG_S5K4BA)
+ res = SENSOR_SVGA;
+#else
+ res = SENSOR_VGA;
+#endif
+ } else
+ res = mode;
+
+ s3c_camif_stop_fimc(cfg);
+
+ switch (res) {
+ case SENSOR_SXGA:
+ printk(KERN_INFO "Resolution changed into SXGA (1280x1024) mode -> 1.3M\n");
+ cis->sensor->driver->command(cis->sensor, SENSOR_SXGA, NULL);
+ cis->source_x = 1280;
+ cis->source_y = 1024;
+ break;
+
+ case SENSOR_UXGA:
+ printk(KERN_INFO "Resolution changed into UXGA (1600x1200) mode -> 2.0M\n");
+ cis->sensor->driver->command(cis->sensor, SENSOR_UXGA, NULL);
+ cis->source_x = 1600;
+ cis->source_y = 1200;
+ break;
+
+ case SENSOR_SVGA:
+ printk(KERN_INFO "Resolution changed back to SVGA (800x600) mode\n");
+ cis->sensor->driver->command(cis->sensor, SENSOR_SVGA, NULL);
+ cis->source_x = 800;
+ cis->source_y = 600;
+ break;
+
+ case SENSOR_VGA:
+ printk(KERN_INFO "Resolution changed back to VGA (640x480) mode (default)\n");
+ cis->sensor->driver->command(cis->sensor, SENSOR_VGA, NULL);
+ cis->source_x = 640;
+ cis->source_y = 480;
+ break;
+ }
+
+ cis->win_hor_ofst = cis->win_hor_ofst2 = 0;
+ cis->win_ver_ofst = cis->win_ver_ofst2 = 0;
+
+ s3c_camif_set_source_format(cis);
+}
+
+static int s3c_camif_check_zoom_range(camif_cfg_t *cfg, int type)
+{
+ switch (type) {
+ case V4L2_CID_ZOOMIN:
+ if (((cfg->sc.modified_src_x - (cfg->cis->win_hor_ofst + \
+ ZOOM_AT_A_TIME_IN_PIXELS + cfg->cis->win_hor_ofst2 + \
+ ZOOM_AT_A_TIME_IN_PIXELS)) / cfg->sc.prehratio) > ZOOM_IN_MAX) {
+ printk(KERN_INFO "Invalid Zoom-in: this zoom-in on preview scaler already comes to the maximum\n");
+ return 0;
+ }
+
+ cfg->sc.zoom_in_cnt++;
+ break;
+
+ case V4L2_CID_ZOOMOUT:
+ if (cfg->sc.zoom_in_cnt > 0) {
+ cfg->sc.zoom_in_cnt--;
+ break;
+ } else {
+ printk(KERN_INFO "Invalid Zoom-out: this zoom-out on preview scaler already comes to the minimum\n");
+ return 0;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+static int s3c_camif_restart_preview(camif_cfg_t *cfg)
+{
+ int ret = 0;
+
+ s3c_camif_stop_preview(cfg);
+
+ if (s3c_camif_control_fimc(cfg)) {
+ printk(KERN_ERR "S3C fimc control failed\n");
+ ret = -1;
+ }
+
+ s3c_camif_start_preview(cfg);
+
+ return ret;
+}
+
+static int s3c_camif_send_sensor_command(camif_cfg_t *cfg, unsigned int cmd, int arg)
+{
+ cfg->cis->sensor->driver->command(cfg->cis->sensor, cmd, (void *) arg);
+
+ return 0;
+}
+
+/*************************************************************************
+ * V4L2 part
+ ************************************************************************/
+static int s3c_camif_v4l2_querycap(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_capability *cap = arg;
+
+ strcpy(cap->driver, "S3C FIMC Camera driver");
+ strlcpy(cap->card, cfg->v->name, sizeof(cap->card));
+ sprintf(cap->bus_info, "FIMC AHB Bus");
+
+ cap->version = 0;
+ cap->capabilities = cfg->v->type2;
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_g_fbuf(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_framebuffer *fb = arg;
+
+ *fb = cfg->v2.frmbuf;
+
+ fb->base = cfg->v2.frmbuf.base;
+ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+
+ fb->fmt.pixelformat = cfg->v2.frmbuf.fmt.pixelformat;
+ fb->fmt.width = cfg->v2.frmbuf.fmt.width;
+ fb->fmt.height = cfg->v2.frmbuf.fmt.height;
+ fb->fmt.bytesperline = cfg->v2.frmbuf.fmt.bytesperline;
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_s_fbuf(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_framebuffer *fb = arg;
+ int i, depth;
+
+ for (i = 0; i < NUMBER_OF_PREVIEW_FORMATS; i++)
+ if (fimc_preview_formats[i].pixelformat == fb->fmt.pixelformat)
+ break;
+
+ if (i == NUMBER_OF_PREVIEW_FORMATS)
+ return -EINVAL;
+
+ cfg->v2.frmbuf.base = fb->base;
+ cfg->v2.frmbuf.flags = fb->flags;
+ cfg->v2.frmbuf.capability = fb->capability;
+
+ cfg->target_x = fb->fmt.width;
+ cfg->target_y = fb->fmt.height;
+
+ depth = s3c_camif_convert_format(fb->fmt.pixelformat, (int *) &(cfg->dst_fmt));
+ s3c_camif_set_fb_info(cfg, depth, fb->fmt.pixelformat);
+
+ return s3c_camif_control_fimc(cfg);
+}
+
+static int s3c_camif_v4l2_g_fmt(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_format *f = (struct v4l2_format *) arg;
+ int size = sizeof(struct v4l2_pix_format);
+ int ret = -1;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ memset(&f->fmt.pix, 0, size);
+ memcpy(&f->fmt.pix, &cfg->v2.frmbuf.fmt, size);
+ ret = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int s3c_camif_v4l2_s_fmt(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_format *f = (struct v4l2_format *) arg;
+ int ret = -1;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ cfg->v2.frmbuf.fmt = f->fmt.pix;
+ cfg->v2.status |= CAMIF_v4L2_DIRTY;
+ cfg->v2.status &= ~CAMIF_v4L2_DIRTY; /* dummy ? */
+
+ s3c_camif_convert_type(cfg, 1);
+ s3c_camif_control_fimc(cfg);
+ ret = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int s3c_camif_v4l2_enum_fmt(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_fmtdesc *f = arg;
+ int index = f->index;
+
+ if (index >= NUMBER_OF_CODEC_FORMATS)
+ return -EINVAL;
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ default:
+ return -EINVAL;
+ }
+
+ memset(f, 0, sizeof(*f));
+ memcpy(f, cfg->v2.fmtdesc + index, sizeof(*f));
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_overlay(camif_cfg_t *cfg, void *arg)
+{
+ int on = *((int *) arg);
+ int ret;
+
+ if (on != 0)
+ ret = s3c_camif_start_preview(cfg);
+ else
+ ret = s3c_camif_stop_preview(cfg);
+
+ return ret;
+}
+
+static int s3c_camif_v4l2_g_ctrl(camif_cfg_t *cfg, void *arg)
+{
+ return 0;
+}
+
+static int s3c_camif_v4l2_s_ctrl(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_control *ctrl = (struct v4l2_control *) arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ORIGINAL:
+ case V4L2_CID_ARBITRARY:
+ case V4L2_CID_NEGATIVE:
+ case V4L2_CID_EMBOSSING:
+ case V4L2_CID_ART_FREEZE:
+ case V4L2_CID_SILHOUETTE:
+ cfg->effect = ctrl->value;
+ s3c_camif_change_effect(cfg);
+ break;
+
+ case V4L2_CID_HFLIP:
+ cfg->flip = CAMIF_FLIP_X;
+ s3c_camif_change_flip(cfg);
+ break;
+
+ case V4L2_CID_VFLIP:
+ cfg->flip = CAMIF_FLIP_Y;
+ s3c_camif_change_flip(cfg);
+ break;
+
+ case V4L2_CID_ROTATE_180:
+ cfg->flip = CAMIF_FLIP_MIRROR;
+ s3c_camif_change_flip(cfg);
+ break;
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ case V4L2_CID_ROTATE_90:
+ cfg->flip = CAMIF_ROTATE_90;
+ s3c_camif_change_flip(cfg);
+ break;
+
+ case V4L2_CID_ROTATE_270:
+ cfg->flip = CAMIF_FLIP_ROTATE_270;
+ s3c_camif_change_flip(cfg);
+ break;
+#endif
+
+ case V4L2_CID_ROTATE_BYPASS:
+ cfg->flip = CAMIF_FLIP;
+ s3c_camif_change_flip(cfg);
+ break;
+
+ case V4L2_CID_ZOOMIN:
+ if (s3c_camif_check_zoom_range(cfg, ctrl->id)) {
+ cfg->cis->win_hor_ofst += ZOOM_AT_A_TIME_IN_PIXELS;
+ cfg->cis->win_ver_ofst += ZOOM_AT_A_TIME_IN_PIXELS;
+ cfg->cis->win_hor_ofst2 += ZOOM_AT_A_TIME_IN_PIXELS;
+ cfg->cis->win_ver_ofst2 += ZOOM_AT_A_TIME_IN_PIXELS;
+
+ s3c_camif_restart_preview(cfg);
+ }
+
+ break;
+
+ case V4L2_CID_ZOOMOUT:
+ if (s3c_camif_check_zoom_range(cfg, ctrl->id)) {
+ cfg->cis->win_hor_ofst -= ZOOM_AT_A_TIME_IN_PIXELS;
+ cfg->cis->win_ver_ofst -= ZOOM_AT_A_TIME_IN_PIXELS;
+ cfg->cis->win_hor_ofst2 -= ZOOM_AT_A_TIME_IN_PIXELS;
+ cfg->cis->win_ver_ofst2 -= ZOOM_AT_A_TIME_IN_PIXELS;
+
+ s3c_camif_restart_preview(cfg);
+ }
+
+ break;
+
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_AUTO_WHITE_BALANCE:
+ s3c_camif_send_sensor_command(cfg, SENSOR_WB, ctrl->value);
+ break;
+
+ default:
+ printk(KERN_ERR "Invalid control id: %d\n", ctrl->id);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_streamon(camif_cfg_t *cfg, void *arg)
+{
+ int ret = 0;
+
+ ret = s3c_camif_start_capture(cfg);
+
+ return ret;
+}
+
+static int s3c_camif_v4l2_streamoff(camif_cfg_t *cfg, void *arg)
+{
+ int ret = 0;
+
+ cfg->cis->status &= ~C_WORKING;
+
+ s3c_camif_stop_capture(cfg);
+
+ return ret;
+}
+
+static int s3c_camif_v4l2_g_input(camif_cfg_t *cfg, void *arg)
+{
+ unsigned int index = *((int *) arg);
+
+ index = cfg->v2.input->index;
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_s_input(camif_cfg_t *cfg, unsigned int index)
+{
+ int ret = -1;
+
+ if (index >= NUMBER_OF_INPUTS)
+ ret = -1;
+ else {
+ cfg->v2.input = &fimc_inputs[index];
+
+ if (cfg->v2.input->type == V4L2_INPUT_TYPE_MSDMA) {
+ if (cfg->dma_type & CAMIF_PREVIEW) {
+ cfg->input_channel = MSDMA_FROM_PREVIEW;
+ ret = 0;
+ } else if (cfg->dma_type & CAMIF_CODEC) {
+ cfg->input_channel = MSDMA_FROM_CODEC;
+ ret = 0;
+ }
+ } else {
+ cfg->input_channel = CAMERA_INPUT;
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+static int s3c_camif_v4l2_g_output(camif_cfg_t *cfg, void *arg)
+{
+ unsigned int index = *((int *) arg);
+
+ index = cfg->v2.output->index;
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_s_output(camif_cfg_t *cfg, unsigned int index)
+{
+ if (index >= NUMBER_OF_OUTPUTS)
+ return -EINVAL;
+ else {
+ cfg->v2.output = (struct v4l2_output *) &fimc_outputs[index];
+ return 0;
+ }
+}
+
+static int s3c_camif_v4l2_enum_input(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_input *i = arg;
+
+ if (i->index >= NUMBER_OF_INPUTS)
+ return -EINVAL;
+
+ memcpy(i, &fimc_inputs[i->index], sizeof(struct v4l2_input));
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_enum_output(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_output *i = arg;
+
+ if ((i->index) >= NUMBER_OF_OUTPUTS)
+ return -EINVAL;
+
+ memcpy(i, &fimc_outputs[i->index], sizeof(struct v4l2_output));
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_reqbufs(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_requestbuffers *req = arg;
+
+ if (req->memory != V4L2_MEMORY_MMAP) {
+ printk(KERN_ERR "Only V4L2_MEMORY_MMAP capture is supported\n");
+ return -EINVAL;
+ }
+
+ /* control user input */
+ if (req->count > 2)
+ req->count = 4;
+ else if (req->count > 1)
+ req->count = 2;
+ else
+ req->count = 1;
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_querybuf(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_buffer *buf = arg;
+
+ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && buf->memory != V4L2_MEMORY_MMAP)
+ return -1;
+
+ buf->length = cfg->buffer_size;
+ buf->m.offset = buf->length * buf->index;
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_qbuf(camif_cfg_t *cfg, void *arg)
+{
+ return 0;
+}
+
+static int s3c_camif_v4l2_dqbuf(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_buffer *buf = arg;
+
+ buf->index = cfg->cur_frame_num % cfg->pp_num;
+
+ return 0;
+}
+
+/*
+ * S3C specific
+ */
+static int s3c_camif_v4l2_s_msdma(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_msdma_format *f = arg;
+ int ret = -1;
+
+ switch(f->input_path) {
+ case V4L2_MSDMA_PREVIEW:
+ cfg->cis->user--; /* CIS will be replaced with a CIS for MSDMA */
+
+ cfg->cis = &msdma_input;
+ cfg->cis->user++;
+ cfg->input_channel = MSDMA_FROM_PREVIEW;
+ break;
+
+ case V4L2_MSDMA_CODEC:
+ cfg->cis->user--; /* CIS will be replaced with a CIS for MSDMA */
+
+ cfg->cis = &msdma_input;
+ cfg->cis->user++;
+ cfg->input_channel = MSDMA_FROM_CODEC;
+ break;
+
+ default:
+ cfg->input_channel = CAMERA_INPUT;
+ break;
+ }
+
+ cfg->cis->source_x = f->width;
+ cfg->cis->source_y = f->height;
+
+ s3c_camif_convert_format(f->pixelformat, (int *) &cfg->src_fmt);
+
+ cfg->cis->win_hor_ofst = 0;
+ cfg->cis->win_ver_ofst = 0;
+ cfg->cis->win_hor_ofst2 = 0;
+ cfg->cis->win_ver_ofst2 = 0;
+
+ ret = s3c_camif_control_fimc(cfg);
+
+ switch(f->input_path) {
+ case V4L2_MSDMA_PREVIEW:
+ ret = s3c_camif_start_preview(cfg);
+ break;
+
+ case V4L2_MSDMA_CODEC:
+ ret = s3c_camif_start_capture(cfg);
+ break;
+
+ default:
+ break;
+
+ }
+
+ return ret;
+}
+
+static int s3c_camif_v4l2_msdma_start(camif_cfg_t *cfg, void *arg)
+{
+ if (cfg->input_channel == MSDMA_FROM_PREVIEW) {
+ cfg->msdma_status = 1;
+ s3c_camif_start_preview_msdma(cfg);
+ }
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_msdma_stop(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_msdma_format *f = arg;
+ int ret = -1;
+
+ cfg->cis->status &= ~C_WORKING;
+ cfg->msdma_status = 0;
+
+ switch(f->input_path) {
+ case V4L2_MSDMA_PREVIEW:
+ ret = s3c_camif_stop_preview(cfg);
+ break;
+
+ case V4L2_MSDMA_CODEC:
+ ret = s3c_camif_stop_capture(cfg);
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int s3c_camif_v4l2_camera_start(camif_cfg_t *cfg, void *arg)
+{
+ return 0;
+}
+
+static int s3c_camif_v4l2_camera_stop(camif_cfg_t *cfg, void *arg)
+{
+ return 0;
+}
+
+static int s3c_camif_v4l2_cropcap(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_cropcap *cap = arg;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ /* crop limitations */
+ cfg->v2.crop_bounds.left = 0;
+ cfg->v2.crop_bounds.top = 0;
+ cfg->v2.crop_bounds.width = cfg->cis->source_x;
+ cfg->v2.crop_bounds.height = cfg->cis->source_y;
+
+ /* crop default values */
+ cfg->v2.crop_defrect.left = (cfg->cis->source_x - CROP_DEFAULT_WIDTH) / 2;
+ cfg->v2.crop_defrect.top = (cfg->cis->source_y - CROP_DEFAULT_HEIGHT) / 2;
+ cfg->v2.crop_defrect.width = CROP_DEFAULT_WIDTH;
+ cfg->v2.crop_defrect.height = CROP_DEFAULT_HEIGHT;
+
+ cap->bounds = cfg->v2.crop_bounds;
+ cap->defrect = cfg->v2.crop_defrect;
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_g_crop(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ crop->c = cfg->v2.crop_current;
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_s_crop(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_crop *crop = arg;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
+ return -EINVAL;
+
+ if (crop->c.height < 0)
+ return -EINVAL;
+
+ if (crop->c.width < 0)
+ return -EINVAL;
+
+ if ((crop->c.left + crop->c.width > cfg->cis->source_x) || \
+ (crop->c.top + crop->c.height > cfg->cis->source_y))
+ return -EINVAL;
+
+ cfg->v2.crop_current = crop->c;
+
+ cfg->cis->win_hor_ofst = (cfg->cis->source_x - crop->c.width) / 2;
+ cfg->cis->win_ver_ofst = (cfg->cis->source_y - crop->c.height) / 2;
+
+ cfg->cis->win_hor_ofst2 = cfg->cis->win_hor_ofst;
+ cfg->cis->win_ver_ofst2 = cfg->cis->win_ver_ofst;
+
+ s3c_camif_restart_preview(cfg);
+
+ return 0;
+}
+
+static int s3c_camif_v4l2_s_parm(camif_cfg_t *cfg, void *arg)
+{
+ struct v4l2_streamparm *sp = arg;
+
+ if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ if (sp->parm.capture.capturemode == V4L2_MODE_HIGHQUALITY) {
+ s3c_camif_change_mode(cfg, SENSOR_MAX);
+ s3c_camif_control_fimc(cfg);
+ } else {
+ s3c_camif_change_mode(cfg, SENSOR_DEFAULT);
+ s3c_camif_control_fimc(cfg);
+ }
+
+ return 0;
+}
+
+/*************************************************************************
+ * Interrupt part
+ ************************************************************************/
+#if defined(FSM_ON_CODEC) && !defined(USE_LAST_IRQ)
+int s3c_camif_do_fsm_codec(camif_cfg_t *cfg)
+{
+ int ret;
+
+ cfg->perf.frames++;
+
+ if ((cfg->fsm == CAMIF_DUMMY_INT) && (cfg->perf.frames > CAMIF_CAPTURE_SKIP_FRAMES))
+ cfg->fsm = CAMIF_NORMAL_INT;
+
+ switch (cfg->fsm) {
+ case CAMIF_DUMMY_INT:
+ DPRINTK(KERN_INFO "CAMIF_DUMMY_INT: %d\n", cfg->perf.frames);
+ cfg->status = CAMIF_STARTED;
+ cfg->fsm = CAMIF_DUMMY_INT;
+ ret = INSTANT_SKIP;
+ break;
+
+ case CAMIF_NORMAL_INT:
+ DPRINTK(KERN_INFO "CAMIF_NORMAL_INT: %d\n", cfg->perf.frames);
+ cfg->status = CAMIF_INT_HAPPEN;
+ cfg->fsm = CAMIF_CONTINUOUS_INT;
+ ret = INSTANT_GO;
+ break;
+
+ case CAMIF_CONTINUOUS_INT:
+ DPRINTK(KERN_INFO "CAMIF_CONTINUOS_INT: %d\n", cfg->perf.frames);
+ cfg->status = CAMIF_INT_HAPPEN;
+ cfg->fsm = CAMIF_CONTINUOUS_INT;
+ ret = INSTANT_GO;
+ break;
+
+ default:
+ printk(KERN_INFO "Unexpect INT: %d\n", cfg->fsm);
+ ret = INSTANT_SKIP;
+ break;
+ }
+
+ return ret;
+}
+#endif
+
+#if defined(FSM_ON_CODEC) && defined(USE_LAST_IRQ)
+int s3c_camif_do_fsm_codec_lastirq(camif_cfg_t *cfg)
+{
+ int ret;
+
+ cfg->perf.frames++;
+
+ if ((cfg->fsm == CAMIF_DUMMY_INT) && (cfg->perf.frames > (CAMIF_CAPTURE_SKIP_FRAMES - 2)))
+ cfg->fsm = CAMIF_SET_LAST_INT;
+
+ switch (cfg->fsm) {
+ case CAMIF_DUMMY_INT:
+ DPRINTK(KERN_INFO "CAMIF_DUMMY_INT: %d\n", cfg->perf.frames);
+ cfg->status = CAMIF_STARTED;
+ cfg->fsm = CAMIF_DUMMY_INT;
+ ret = INSTANT_SKIP;
+ break;
+
+ case CAMIF_SET_LAST_INT:
+ DPRINTK(KERN_INFO "CAMIF_SET_LAST_INT: %d\n", cfg->perf.frames);
+ s3c_camif_enable_lastirq(cfg);
+
+/* in 64xx, lastirq is not auto cleared. */
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ s3c_camif_disable_lastirq(cfg);
+#endif
+ cfg->status = CAMIF_INT_HAPPEN;
+ cfg->fsm = CAMIF_STOP_CAPTURE;
+ ret = INSTANT_SKIP;
+ break;
+
+ case CAMIF_STOP_CAPTURE:
+ DPRINTK(KERN_INFO "CAMIF_STOP_CAPTURE: %d\n", cfg->perf.frames);
+ cfg->capture_enable = CAMIF_DMA_OFF;
+ s3c_camif_stop_dma(cfg);
+ cfg->fsm = CAMIF_LAST_IRQ;
+ ret = INSTANT_SKIP;
+ break;
+
+ case CAMIF_LAST_IRQ:
+ DPRINTK(KERN_INFO "CAMIF_LAST_IRQ: %d\n", cfg->perf.frames);
+ cfg->fsm = CAMIF_SET_LAST_INT;
+ cfg->status = CAMIF_INT_HAPPEN;
+ ret = INSTANT_GO;
+ break;
+
+ default:
+ printk(KERN_INFO "Unexpect INT: %d\n", cfg->fsm);
+ ret = INSTANT_SKIP;
+ break;
+ }
+
+ return ret;
+}
+#endif
+
+#if defined(FSM_ON_PREVIEW)
+static int s3c_camif_do_lastirq_preview(camif_cfg_t *cfg)
+{
+ int ret = 0;
+
+ cfg->perf.frames++;
+
+ if (cfg->fsm == CAMIF_NORMAL_INT) {
+ if (cfg->perf.frames % CHECK_FREQ == 0)
+ ret = s3c_camif_check_global_status(cfg);
+ }
+
+ if (ret > 0)
+ cfg->fsm = CAMIF_Xth_INT;
+
+ switch (cfg->fsm) {
+ case CAMIF_1st_INT:
+ DPRINTK(KERN_INFO "CAMIF_1st_INT INT\n");
+ cfg->fsm = CAMIF_NORMAL_INT;
+ ret = INSTANT_SKIP;
+ break;
+
+ case CAMIF_NORMAL_INT:
+ DPRINTK(KERN_INFO "CAMIF_NORMAL_INT\n");
+ cfg->status = CAMIF_INT_HAPPEN;
+ cfg->fsm = CAMIF_NORMAL_INT;
+ ret = INSTANT_GO;
+ break;
+
+ case CAMIF_Xth_INT:
+ DPRINTK(KERN_INFO "CAMIF_Xth_INT\n");
+ s3c_camif_enable_lastirq(cfg);
+ cfg->status = CAMIF_INT_HAPPEN;
+ cfg->fsm = CAMIF_Yth_INT;
+ ret = INSTANT_GO;
+ break;
+
+ case CAMIF_Yth_INT:
+ DPRINTK(KERN_INFO "CAMIF_Yth_INT\n");
+ s3c_camif_disable_lastirq(cfg);
+ cfg->capture_enable = CAMIF_DMA_OFF;
+ cfg->status = CAMIF_INT_HAPPEN;
+ s3c_camif_stop_dma(cfg);
+ cfg->fsm = CAMIF_Zth_INT;
+ ret = INSTANT_GO;
+ break;
+
+ case CAMIF_Zth_INT:
+ DPRINTK(KERN_INFO "CAMIF_Zth_INT\n");
+ cfg->fsm = CAMIF_DUMMY_INT;
+ cfg->status = CAMIF_INT_HAPPEN;
+ ret = INSTANT_GO;
+ s3c_camif_auto_restart(cfg);
+ break;
+
+ case CAMIF_DUMMY_INT:
+ DPRINTK(KERN_INFO "CAMIF_DUMMY_INT\n");
+ cfg->status = CAMIF_STOPPED;
+ ret = INSTANT_SKIP;
+ break;
+
+ default:
+ printk(KERN_INFO "Unexpected INT %d\n", cfg->fsm);
+ ret = INSTANT_SKIP;
+ break;
+ }
+
+ return ret;
+}
+#endif
+
+static irqreturn_t s3c_camif_do_irq_codec(int irq, void *dev_id)
+{
+ camif_cfg_t *cfg = (camif_cfg_t *) dev_id;
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ s3c_gpio_setpin(S3C_GPN15, 1);
+#endif
+ s3c_camif_clear_irq(irq);
+ s3c_camif_get_fifo_status(cfg);
+ s3c_camif_get_frame_num(cfg);
+
+#if defined(FSM_ON_CODEC) && !defined(USE_LAST_IRQ)
+ if (s3c_camif_do_fsm_codec(cfg) == INSTANT_SKIP)
+ return IRQ_HANDLED;
+#endif
+
+#if defined(FSM_ON_CODEC) && defined(USE_LAST_IRQ)
+ if (s3c_camif_do_fsm_codec_lastirq(cfg) == INSTANT_SKIP)
+ return IRQ_HANDLED;
+#endif
+ wake_up_interruptible(&cfg->waitq);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t s3c_camif_do_irq_preview(int irq, void *dev_id)
+{
+ camif_cfg_t *cfg = (camif_cfg_t *) dev_id;
+
+ s3c_camif_clear_irq(irq);
+ s3c_camif_get_fifo_status(cfg);
+ s3c_camif_get_frame_num(cfg);
+ wake_up_interruptible(&cfg->waitq);
+
+#if defined(FSM_ON_PREVIEW)
+ if (s3c_camif_do_lastirq_preview(cfg) == INSTANT_SKIP)
+ return IRQ_HANDLED;
+
+ wake_up_interruptible(&cfg->waitq);
+#endif
+ cfg->status = CAMIF_INT_HAPPEN;
+
+ return IRQ_HANDLED;
+}
+
+static void s3c_camif_release_irq(camif_cfg_t * cfg)
+{
+ disable_irq(cfg->irq);
+ free_irq(cfg->irq, cfg);
+}
+
+static int s3c_camif_request_irq(camif_cfg_t * cfg)
+{
+ int ret = 0;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ if ((ret = request_irq(cfg->irq, s3c_camif_do_irq_codec, SA_INTERRUPT, cfg->shortname, cfg)))
+ printk(KERN_ERR "Request irq (CAM_C) failed\n");
+ else
+ printk(KERN_INFO "Request irq %d for codec\n", cfg->irq);
+ }
+
+ if (cfg->dma_type & CAMIF_PREVIEW) {
+ if ((ret = request_irq(cfg->irq, s3c_camif_do_irq_preview, SA_INTERRUPT, cfg->shortname, cfg)))
+ printk("Request_irq (CAM_P) failed\n");
+ else
+ printk(KERN_INFO "Request irq %d for preview\n", cfg->irq);
+ }
+
+ return 0;
+}
+
+/*************************************************************************
+ * Standard file operations part
+ ************************************************************************/
+int s3c_camif_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg)
+{
+ camif_cfg_t *cfg = file->private_data;
+ int ret = -1;
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ ret = s3c_camif_v4l2_querycap(cfg, arg);
+ break;
+
+ case VIDIOC_G_FBUF:
+ ret = s3c_camif_v4l2_g_fbuf(cfg, arg);
+ break;
+
+ case VIDIOC_S_FBUF:
+ ret = s3c_camif_v4l2_s_fbuf(cfg, arg);
+ break;
+
+ case VIDIOC_G_FMT:
+ ret = s3c_camif_v4l2_g_fmt(cfg, arg);
+ break;
+
+ case VIDIOC_S_FMT:
+ ret = s3c_camif_v4l2_s_fmt(cfg, arg);
+ break;
+
+ case VIDIOC_ENUM_FMT:
+ ret = s3c_camif_v4l2_enum_fmt(cfg, arg);
+ break;
+
+ case VIDIOC_OVERLAY:
+ ret = s3c_camif_v4l2_overlay(cfg, arg);
+ break;
+
+ case VIDIOC_S_CTRL:
+ ret = s3c_camif_v4l2_s_ctrl(cfg, arg);
+ break;
+
+ case VIDIOC_G_CTRL:
+ ret = s3c_camif_v4l2_g_ctrl(cfg, arg);
+ break;
+
+ case VIDIOC_STREAMON:
+ ret = s3c_camif_v4l2_streamon(cfg, arg);
+ break;
+
+ case VIDIOC_STREAMOFF:
+ ret = s3c_camif_v4l2_streamoff(cfg, arg);
+ break;
+
+ case VIDIOC_G_INPUT:
+ ret = s3c_camif_v4l2_g_input(cfg, arg);
+ break;
+
+ case VIDIOC_S_INPUT:
+ ret = s3c_camif_v4l2_s_input(cfg, *((int *) arg));
+ break;
+
+ case VIDIOC_G_OUTPUT:
+ ret = s3c_camif_v4l2_g_output(cfg, arg);
+ break;
+
+ case VIDIOC_S_OUTPUT:
+ ret = s3c_camif_v4l2_s_output(cfg, *((int *) arg));
+ break;
+
+ case VIDIOC_ENUMINPUT:
+ ret = s3c_camif_v4l2_enum_input(cfg, arg);
+ break;
+
+ case VIDIOC_ENUMOUTPUT:
+ ret = s3c_camif_v4l2_enum_output(cfg, arg);
+ break;
+
+ case VIDIOC_REQBUFS:
+ ret = s3c_camif_v4l2_reqbufs(cfg, arg);
+ break;
+
+ case VIDIOC_QUERYBUF:
+ ret = s3c_camif_v4l2_querybuf(cfg, arg);
+ break;
+
+ case VIDIOC_QBUF:
+ ret = s3c_camif_v4l2_qbuf(cfg, arg);
+ break;
+
+ case VIDIOC_DQBUF:
+ ret = s3c_camif_v4l2_dqbuf(cfg, arg);
+ break;
+
+ case VIDIOC_S_MSDMA:
+ ret = s3c_camif_v4l2_s_msdma(cfg, arg);
+ break;
+
+ case VIDIOC_MSDMA_START:
+ ret = s3c_camif_v4l2_msdma_start(cfg, arg);
+ break;
+
+ case VIDIOC_MSDMA_STOP:
+ ret = s3c_camif_v4l2_msdma_stop(cfg, arg);
+ break;
+
+ case VIDIOC_S_CAMERA_START:
+ ret = s3c_camif_v4l2_camera_start(cfg, arg);
+ break;
+
+ case VIDIOC_S_CAMERA_STOP:
+ ret = s3c_camif_v4l2_camera_stop(cfg, arg);
+ break;
+
+ case VIDIOC_CROPCAP:
+ ret = s3c_camif_v4l2_cropcap(cfg, arg);
+ break;
+
+ case VIDIOC_G_CROP:
+ ret = s3c_camif_v4l2_g_crop(cfg, arg);
+ break;
+
+ case VIDIOC_S_CROP:
+ ret = s3c_camif_v4l2_s_crop(cfg, arg);
+ break;
+
+ case VIDIOC_S_PARM:
+ ret = s3c_camif_v4l2_s_parm(cfg, arg);
+ break;
+
+ default: /* For v4l compatability */
+ v4l_compat_translate_ioctl(inode, file, cmd, arg, s3c_camif_ioctl);
+ break;
+ } /* End of Switch */
+
+ return ret;
+}
+
+int s3c_camif_open(struct inode *inode, struct file *file)
+{
+ int err;
+ camif_cfg_t *cfg = s3c_camif_get_fimc_object(MINOR(inode->i_rdev));
+
+ if (!cfg->cis) {
+ printk(KERN_ERR "An object for a CIS is missing\n");
+ printk(KERN_ERR "Using msdma_input as a default CIS data structure\n");
+ cfg->cis = &msdma_input;
+
+ /* global lock for both Codec and Preview */
+ sema_init((struct semaphore *) &cfg->cis->lock, 1);
+ cfg->cis->status |= P_NOT_WORKING;
+ }
+
+ if (cfg->dma_type & CAMIF_PREVIEW) {
+ if (cfg->dma_type & CAMIF_PREVIEW)
+ cfg->cis->status &= ~P_NOT_WORKING;
+
+ up((struct semaphore *) &cfg->cis->lock);
+ }
+
+ err = video_exclusive_open(inode, file);
+ cfg->cis->user++;
+ cfg->status = CAMIF_STOPPED;
+
+ if (err < 0)
+ return err;
+
+ if (file->f_flags & O_NONCAP) {
+ printk(KERN_ERR "Don't support non-capturing open\n");
+ return 0;
+ }
+
+ file->private_data = cfg;
+
+ s3c_camif_init_sensor(cfg);
+
+ return 0;
+}
+
+int s3c_camif_release(struct inode *inode, struct file *file)
+{
+ camif_cfg_t *cfg = s3c_camif_get_fimc_object(MINOR(inode->i_rdev));
+
+ if (cfg->dma_type & CAMIF_PREVIEW) {
+ cfg->cis->status &= ~PWANT2START;
+ cfg->cis->status |= P_NOT_WORKING;
+ s3c_camif_stop_preview(cfg);
+ up((struct semaphore *) &cfg->cis->lock);
+ } else {
+ cfg->cis->status &= ~CWANT2START;
+ s3c_camif_stop_capture(cfg);
+ }
+
+ video_exclusive_release(inode, file);
+
+ if (cfg->cis->sensor == NULL)
+ DPRINTK("A CIS sensor for MSDMA has been used\n");
+ else
+ cfg->cis->sensor->driver->command(cfg->cis->sensor, USER_EXIT, NULL);
+
+ cfg->cis->user--;
+ cfg->status = CAMIF_STOPPED;
+
+ return 0;
+}
+
+ssize_t s3c_camif_read(struct file * file, char *buf, size_t count, loff_t * pos)
+{
+ camif_cfg_t *cfg = NULL;
+ size_t end;
+
+ cfg = s3c_camif_get_fimc_object(MINOR(file->f_dentry->d_inode->i_rdev));
+
+#if defined(FSM_ON_PREVIEW)
+ if (cfg->dma_type == CAMIF_PREVIEW) {
+ if (wait_event_interruptible(cfg->waitq, cfg->status == CAMIF_INT_HAPPEN))
+ return -ERESTARTSYS;
+
+ cfg->status = CAMIF_STOPPED;
+ }
+#endif
+
+#if defined(FSM_ON_CODEC)
+ if (cfg->dma_type == CAMIF_CODEC) {
+ if (wait_event_interruptible(cfg->waitq, cfg->status == CAMIF_INT_HAPPEN))
+ return -ERESTARTSYS;
+
+ cfg->status = CAMIF_STOPPED;
+ }
+#endif
+ end = min_t(size_t, cfg->pp_totalsize / cfg->pp_num, count);
+
+ if (copy_to_user(buf, s3c_camif_get_frame(cfg), end))
+ return -EFAULT;
+
+ return end;
+}
+
+ssize_t s3c_camif_write(struct file * f, const char *b, size_t c, loff_t * offset)
+{
+ camif_cfg_t *cfg;
+ int ret = 0;
+
+ cfg = s3c_camif_get_fimc_object(MINOR(f->f_dentry->d_inode->i_rdev));
+
+ switch (*b) {
+ case 'O':
+ if (cfg->dma_type & CAMIF_PREVIEW)
+ s3c_camif_start_preview(cfg);
+ else {
+ ret = s3c_camif_start_capture(cfg);
+
+ if (ret < 0)
+ ret = 1;
+ }
+
+ break;
+
+ case 'X':
+ if (cfg->dma_type & CAMIF_PREVIEW) {
+ s3c_camif_stop_preview(cfg);
+ cfg->cis->status |= P_NOT_WORKING;
+ } else {
+ cfg->cis->status &= ~C_WORKING;
+ s3c_camif_stop_capture(cfg);
+ }
+
+ break;
+
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+ case 'P':
+ if (cfg->dma_type & CAMIF_PREVIEW) {
+ s3c_camif_start_preview(cfg);
+ s3c_camif_do_postprocess(cfg);
+ return 0;
+ } else
+ return -EFAULT;
+#endif
+ default:
+ panic("s3c_camera_driver.c: s3c_camif_write() - Unexpected Parameter\n");
+ }
+
+ return ret;
+}
+
+int s3c_camif_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+ camif_cfg_t *cfg = filp->private_data;
+
+ unsigned long pageFrameNo;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long total_size;
+
+ if (cfg->dma_type == CAMIF_PREVIEW)
+ total_size = RGB_MEM;
+ else
+ total_size = YUV_MEM;
+
+ /* page frame number of the address for a source RGB frame to be stored at. */
+ pageFrameNo = __phys_to_pfn(cfg->pp_phys_buf);
+
+ if (size > total_size) {
+ printk(KERN_ERR "The size of RGB_MEM mapping is too big\n");
+ return -EINVAL;
+ }
+
+ if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {
+ printk(KERN_ERR "Writable RGB_MEM mapping must be shared\n");
+ return -EINVAL;
+ }
+
+ if (remap_pfn_range(vma, vma->vm_start, pageFrameNo + vma->vm_pgoff, size, vma->vm_page_prot))
+ return -EINVAL;
+
+ return 0;
+}
+
+static unsigned int s3c_camif_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask = 0;
+ camif_cfg_t *cfg = file->private_data;
+
+ poll_wait(file, &cfg->waitq, wait);
+
+ if (cfg->status == CAMIF_INT_HAPPEN)
+ mask = POLLIN | POLLRDNORM;
+
+ cfg->status = CAMIF_STOPPED;
+
+ return mask;
+}
+
+struct file_operations camif_c_fops = {
+ .owner = THIS_MODULE,
+ .open = s3c_camif_open,
+ .release = s3c_camif_release,
+ .ioctl = s3c_camif_ioctl,
+ .read = s3c_camif_read,
+ .write = s3c_camif_write,
+ .mmap = s3c_camif_mmap,
+ .poll = s3c_camif_poll,
+};
+
+struct file_operations camif_p_fops = {
+ .owner = THIS_MODULE,
+ .open = s3c_camif_open,
+ .release = s3c_camif_release,
+ .ioctl = s3c_camif_ioctl,
+ .read = s3c_camif_read,
+ .write = s3c_camif_write,
+ .mmap = s3c_camif_mmap,
+ .poll = s3c_camif_poll,
+};
+
+/*************************************************************************
+ * Templates for V4L2
+ ************************************************************************/
+void camif_vdev_release (struct video_device *vdev) {
+ kfree(vdev);
+}
+
+struct video_device codec_template = {
+ .name = CODEC_DEV_NAME,
+ .type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES,
+ .type2 = V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING,
+ .hardware = VID_HARDWARE_SAMSUNG_FIMC3X,
+ .fops = &camif_c_fops,
+ .release = camif_vdev_release,
+ .minor = CODEC_MINOR,
+};
+
+struct video_device preview_template = {
+ .name = PREVIEW_DEV_NAME,
+ .type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES,
+ .type2 = V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING,
+ .hardware = VID_HARDWARE_SAMSUNG_FIMC3X,
+ .fops = &camif_p_fops,
+ .release = camif_vdev_release,
+ .minor = PREVIEW_MINOR,
+};
+
+/*************************************************************************
+ * Initialize part
+ ************************************************************************/
+void s3c_camif_init_sensor(camif_cfg_t *cfg)
+{
+ camif_cis_t *cis = cfg->cis;
+ camif_cis_t *initialized_cis;
+
+ if (!cis->sensor) {
+ initialized_cis = (camif_cis_t *) get_initialized_cis();
+
+ if (initialized_cis == NULL) {
+ printk(KERN_ERR "An I2C client for CIS sensor isn't registered\n");
+ return;
+ }
+
+ cis = cfg->cis = initialized_cis;
+ cfg->input_channel = 0;
+ cfg->cis->user++;
+ }
+
+ if (!cis->init_sensor) {
+ cis->sensor->driver->command(cis->sensor, SENSOR_INIT, NULL);
+ cis->init_sensor = 1;
+
+#if defined(CONFIG_VIDEO_SAMSUNG_S5K3BA)
+ cis->sensor->driver->command(cis->sensor, SENSOR_VGA, NULL);
+ cis->source_x = 640;
+ cis->source_y = 480;
+#elif defined(CONFIG_VIDEO_SAMSUNG_S5K4BA)
+ cis->sensor->driver->command(cis->sensor, SENSOR_SVGA, NULL);
+ cis->source_x = 800;
+ cis->source_y = 600;
+#endif
+ }
+
+ cis->sensor->driver->command(cis->sensor, USER_ADD, NULL);
+}
+
+static int s3c_camif_init_preview(camif_cfg_t * cfg)
+{
+ cfg->target_x = PREVIEW_DEFAULT_WIDTH;
+ cfg->target_y = PREVIEW_DEFAULT_WIDTH;
+ cfg->pp_num = PREVIEW_DEFAULT_PPNUM;
+ cfg->dma_type = CAMIF_PREVIEW;
+ cfg->input_channel = CAMERA_INPUT;
+ cfg->src_fmt = CAMIF_YCBCR422;
+ cfg->output_channel = CAMIF_OUT_PP;
+ cfg->dst_fmt = CAMIF_RGB16;
+ cfg->flip = CAMIF_FLIP_Y;
+ cfg->v = &preview_template;
+
+ init_MUTEX((struct semaphore *) &cfg->v->lock);
+ init_waitqueue_head(&cfg->waitq);
+
+ cfg->status = CAMIF_STOPPED;
+
+ /* To get the handle of CODEC */
+ cfg->other = s3c_camif_get_fimc_object(CODEC_MINOR);
+
+ return cfg->status;
+}
+
+static int s3c_camif_init_codec(camif_cfg_t * cfg)
+{
+ cfg->target_x = CODEC_DEFAULT_WIDTH;
+ cfg->target_y = CODEC_DEFAULT_HEIGHT;
+ cfg->pp_num = CODEC_DEFAULT_PPNUM;
+ cfg->dma_type = CAMIF_CODEC;
+ cfg->src_fmt = CAMIF_YCBCR422;
+ cfg->input_channel = CAMERA_INPUT;
+ cfg->dst_fmt = CAMIF_YCBCR420;
+ cfg->output_channel = CAMIF_OUT_PP;
+ cfg->flip = CAMIF_FLIP_X;
+ cfg->v = &codec_template;
+
+ init_MUTEX((struct semaphore *) &cfg->v->lock);
+
+ init_waitqueue_head(&cfg->waitq);
+
+ cfg->status = CAMIF_STOPPED;
+
+ /* To get the handle of PREVIEW */
+ cfg->other = s3c_camif_get_fimc_object(PREVIEW_MINOR);
+
+ return cfg->status;
+}
+
+static int s3c_camif_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ camif_cfg_t *codec, *preview;
+ int ret = 0;
+
+ /* Initialize fimc objects */
+ codec = s3c_camif_get_fimc_object(CODEC_MINOR);
+ preview = s3c_camif_get_fimc_object(PREVIEW_MINOR);
+
+ memset(codec, 0, sizeof(camif_cfg_t));
+ memset(preview, 0, sizeof(camif_cfg_t));
+
+ /* Set the fimc name */
+ strcpy(codec->shortname, CODEC_DEV_NAME);
+ strcpy(preview->shortname, PREVIEW_DEV_NAME);
+
+ /* get resource for io memory */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ printk("Failed to get io memory region resouce.\n");
+ return -1;
+ }
+
+ /* request mem region */
+ res = request_mem_region(res->start, res->end - res->start + 1, pdev->name);
+
+ if (!res) {
+ printk("Failed to request io memory region.\n");
+ return -1;
+ }
+
+ /* ioremap for register block */
+ codec->regs = preview->regs = ioremap(res->start, res->end - res->start + 1);
+
+ if (codec->regs == NULL) {
+ printk(KERN_ERR "Failed to remap register block\n");
+ return -1;
+ }
+
+ /* ioremap for reserved memory */
+ codec->pp_phys_buf = PHYS_OFFSET + (MEM_SIZE - RESERVED_MEM);
+ codec->pp_virt_buf = ioremap_nocache(codec->pp_phys_buf, YUV_MEM);
+
+ preview->pp_phys_buf = PHYS_OFFSET + (MEM_SIZE - RESERVED_MEM) + YUV_MEM;
+ preview->pp_virt_buf = ioremap_nocache(preview->pp_phys_buf, RGB_MEM);
+
+ /* Device init */
+ s3c_camif_init();
+ s3c_camif_init_codec(codec);
+ s3c_camif_init_preview(preview);
+
+ /* Set irq */
+ codec->irq = platform_get_irq(pdev, FIMC_CODEC_INDEX);
+ preview->irq = platform_get_irq(pdev, FIMC_PREVIEW_INDEX);
+
+ s3c_camif_request_irq(codec);
+ s3c_camif_request_irq(preview);
+
+ /* Register to video device */
+ if (video_register_device(codec->v, VFL_TYPE_GRABBER, CODEC_MINOR) != 0) {
+ printk(KERN_ERR "Couldn't register this codec driver\n");
+ return -1;
+ }
+
+ if (video_register_device(preview->v, VFL_TYPE_GRABBER, PREVIEW_MINOR) != 0) {
+ printk(KERN_ERR "Couldn't register this preview driver\n");
+ return -1;
+ }
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ cam_clock = clk_get(&pdev->dev, "camera");
+#elif defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2416) || defined(CONFIG_CPU_S3C2450)
+ cam_clock = clk_get(&pdev->dev, "camif-upll");
+#else
+#error cam_clock should be defined
+#endif
+
+ if (IS_ERR(cam_clock)) {
+ printk("Failed to find camera clock source\n");
+ ret = PTR_ERR(cam_clock);
+ }
+
+ clk_enable(cam_clock);
+
+ /* Print banner */
+ printk(KERN_INFO "S3C FIMC v%s\n", FIMC_VER);
+
+ return 0;
+}
+
+static int s3c_camif_remove(struct platform_device *pdev)
+{
+ camif_cfg_t *codec, *preview;
+
+ codec = s3c_camif_get_fimc_object(CODEC_MINOR);
+ preview = s3c_camif_get_fimc_object(PREVIEW_MINOR);
+
+ s3c_camif_release_irq(codec);
+ s3c_camif_release_irq(preview);
+
+ iounmap(codec->pp_virt_buf);
+ codec->pp_virt_buf = 0;
+
+ iounmap(preview->pp_virt_buf);
+ preview->pp_virt_buf = 0;
+
+ video_unregister_device(codec->v);
+ video_unregister_device(preview->v);
+
+ s3c_camif_set_priority(0);
+ clk_disable(cam_clock);
+
+ memset(codec, 0, sizeof(camif_cfg_t));
+ memset(preview, 0, sizeof(camif_cfg_t));
+
+ return 0;
+}
+
+static struct platform_driver s3c_camif_driver =
+{
+ .probe = s3c_camif_probe,
+ .remove = s3c_camif_remove,
+ .driver = {
+ .name = "s3c-camif",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int s3c_camif_register(void)
+{
+ platform_driver_register(&s3c_camif_driver);
+
+ return 0;
+}
+
+static void s3c_camif_unregister(void)
+{
+ platform_driver_unregister(&s3c_camif_driver);
+}
+
+void s3c_camif_open_sensor(camif_cis_t *cis)
+{
+ clk_set_rate(cam_clock, cis->camclk);
+ s3c_camif_reset(cis->reset_type, cis->reset_udelay);
+}
+
+void s3c_camif_register_sensor(struct i2c_client *ptr)
+{
+ camif_cfg_t *codec, *preview;
+ camif_cis_t *cis = (camif_cis_t *) ptr->data;
+
+ codec = s3c_camif_get_fimc_object(CODEC_MINOR);
+ preview = s3c_camif_get_fimc_object(PREVIEW_MINOR);
+
+ codec->cis = preview->cis = cis;
+
+ sema_init((struct semaphore *) &codec->cis->lock, 1);
+ sema_init((struct semaphore *) &preview->cis->lock, 1);
+
+ preview->cis->status |= P_NOT_WORKING; /* Default Value */
+
+ s3c_camif_set_polarity(preview);
+ s3c_camif_set_source_format(cis);
+ s3c_camif_set_priority(1);
+}
+
+void s3c_camif_unregister_sensor(struct i2c_client *ptr)
+{
+ camif_cis_t *cis;
+
+ cis = (camif_cis_t *) (ptr->data);
+ cis->init_sensor = 0;
+}
+
+module_init(s3c_camif_register);
+module_exit(s3c_camif_unregister);
+
+EXPORT_SYMBOL(s3c_camif_register_sensor);
+EXPORT_SYMBOL(s3c_camif_unregister_sensor);
+
+MODULE_AUTHOR("Jinsung Yang <jsgood.yang@samsung.com>");
+MODULE_DESCRIPTION("S3C Camera Driver for FIMC Interface");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/media/video/s3c_camif.c b/drivers/media/video/s3c_camif.c
new file mode 100644
index 00000000000..5d41ef9c0e2
--- /dev/null
+++ b/drivers/media/video/s3c_camif.c
@@ -0,0 +1,1863 @@
+/* drivers/media/video/s3c_camif.c
+ *
+ * Copyright (c) 2008 Samsung Electronics
+ *
+ * Samsung S3C Camera driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <linux/videodev.h>
+#include <asm/io.h>
+#include <asm/semaphore.h>
+#include <asm/hardware.h>
+#include <asm/uaccess.h>
+#include <asm/arch/map.h>
+#include <asm/arch/regs-camif.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-gpioj.h>
+#include <asm/arch/regs-lcd.h>
+
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+#include <asm/arch/regs-irq.h>
+#endif
+
+#include "s3c_camif.h"
+
+static unsigned int irq_old_priority;
+
+/*************************************************************************
+ * Utility part
+ ************************************************************************/
+int s3c_camif_get_frame_num(camif_cfg_t *cfg)
+{
+ int index = 0;
+
+ if (cfg->dma_type & CAMIF_CODEC)
+ index = (readl(cfg->regs + S3C_CICOSTATUS) >> 26) & 0x3;
+ else {
+ assert(cfg->dma_type & CAMIF_PREVIEW);
+ index = (readl(cfg->regs + S3C_CIPRSTATUS) >> 26) & 0x3;
+ }
+
+ cfg->cur_frame_num = (index + 2) % 4; /* When 4 PingPong */
+
+ return 0;
+}
+
+unsigned char* s3c_camif_get_frame(camif_cfg_t *cfg)
+{
+ unsigned char *ret = NULL;
+ int cnt = cfg->cur_frame_num;
+
+ if (cfg->dma_type & CAMIF_PREVIEW)
+ ret = cfg->img_buf[cnt].virt_rgb;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ if ((cfg->dst_fmt & CAMIF_RGB16) || (cfg->dst_fmt & CAMIF_RGB24))
+ ret = cfg->img_buf[cnt].virt_rgb;
+ else
+ ret = cfg->img_buf[cnt].virt_y;
+ }
+
+ return ret;
+}
+
+int s3c_camif_get_fifo_status(camif_cfg_t *cfg)
+{
+ unsigned int reg, val, flag;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ flag = S3C_CICOSTATUS_OVFIY_CO | S3C_CICOSTATUS_OVFICB_CO | S3C_CICOSTATUS_OVFICR_CO;
+ reg = readl(cfg->regs + S3C_CICOSTATUS);
+
+ if (reg & flag) {
+ /* FIFO Error Count ++ */
+ val = readl(cfg->regs + S3C_CIWDOFST);
+ val |= (S3C_CIWDOFST_CLROVCOFIY | S3C_CIWDOFST_CLROVCOFICB | S3C_CIWDOFST_CLROVCOFICR);
+ writel(val, cfg->regs + S3C_CIWDOFST);
+
+ val = readl(cfg->regs + S3C_CIWDOFST);
+ val &= ~(S3C_CIWDOFST_CLROVCOFIY | S3C_CIWDOFST_CLROVCOFICB | S3C_CIWDOFST_CLROVCOFICR);
+ writel(val, cfg->regs + S3C_CIWDOFST);
+
+ return 1; /* Error */
+ }
+ } else if (cfg->dma_type & CAMIF_PREVIEW) {
+ flag = S3C_CIPRSTATUS_OVFICB_PR | S3C_CIPRSTATUS_OVFICR_PR;
+ reg = readl(cfg->regs + S3C_CIPRSTATUS);
+
+ if (reg & flag) {
+ /* FIFO Error Count ++ */
+ val = readl(cfg->regs + S3C_CIWDOFST);
+ val |= (S3C_CIWDOFST_CLROVPRFICB | S3C_CIWDOFST_CLROVPRFICR);
+ writel(val, cfg->regs + S3C_CIWDOFST);
+
+ val = readl(cfg->regs + S3C_CIWDOFST);
+ val &= ~(S3C_CIWDOFST_CLROVPRFIY | S3C_CIWDOFST_CLROVPRFICB | S3C_CIWDOFST_CLROVPRFICR);
+ writel(val, cfg->regs + S3C_CIWDOFST);
+
+ return 1; /* Error */
+ }
+ }
+
+ return 0;
+}
+
+void s3c_camif_set_polarity(camif_cfg_t *cfg)
+{
+ camif_cis_t *cis = cfg->cis;
+ unsigned int val;
+ unsigned int cmd;
+
+ cmd = readl(cfg->regs + S3C_CIGCTRL);
+ cmd &= ~(0x7 << 24);
+
+ if (cis->polarity_pclk)
+ cmd |= S3C_CIGCTRL_INVPOLPCLK;
+
+ if (cis->polarity_vsync)
+ cmd |= S3C_CIGCTRL_INVPOLVSYNC;
+
+ if (cis->polarity_href)
+ cmd |= S3C_CIGCTRL_INVPOLHREF;
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= cmd;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+}
+
+/*************************************************************************
+ * Memory part
+ ************************************************************************/
+static int s3c_camif_request_memory(camif_cfg_t *cfg)
+{
+ unsigned int t_size = 0, i = 0;
+ unsigned int area = 0;
+
+ area = cfg->target_x * cfg->target_y;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ if (cfg->dst_fmt & CAMIF_YCBCR420)
+ t_size = (area * 3 / 2); /* CAMIF_YCBCR420 */
+ else if (cfg->dst_fmt & CAMIF_YCBCR422 || cfg->dst_fmt & CAMIF_YCBCR422I)
+ t_size = (area * 2); /* CAMIF_YCBCR422 */
+ else if (cfg->dst_fmt & CAMIF_RGB16)
+ t_size = (area * 2); /* 2 bytes per one pixel */
+ else if (cfg->dst_fmt & CAMIF_RGB24)
+ t_size = (area * 4); /* 4 bytes per one pixel */
+ else
+ printk(KERN_INFO "Invalid target format\n");
+
+ if ((t_size % PAGE_SIZE) != 0) {
+ i = t_size / PAGE_SIZE;
+ t_size = (i + 1) * PAGE_SIZE;
+ }
+
+ t_size = t_size * cfg->pp_num;
+ cfg->pp_totalsize = t_size;
+
+ printk(KERN_INFO "Codec memory required: 0x%08X bytes\n", t_size);
+
+ return 0;
+ }else if (cfg->dma_type & CAMIF_PREVIEW) {
+
+ if (cfg->dst_fmt & CAMIF_RGB16)
+ t_size = (area * 2); /* 2 bytes per two pixel*/
+ else if (cfg->dst_fmt & CAMIF_RGB24)
+ t_size = (area * 4); /* 4 bytes per one pixel */
+ else
+ printk(KERN_ERR "Invalid target format\n");
+
+ if ((t_size % PAGE_SIZE) != 0) {
+ i = t_size / PAGE_SIZE;
+ t_size = (i + 1) * PAGE_SIZE;
+ }
+
+ t_size = t_size * cfg->pp_num;
+ cfg->pp_totalsize = t_size;
+
+ printk(KERN_INFO "Preview memory required: 0x%08X bytes\n", t_size);
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static void s3c_camif_calc_burst_length_yuv422i(unsigned int hsize, unsigned int *mburst, unsigned int *rburst)
+{
+ unsigned int tmp, wanted;
+
+ tmp = (hsize / 2) & 0xf;
+
+ switch (tmp) {
+ case 0:
+ wanted = 16;
+ break;
+
+ case 4:
+ wanted = 4;
+ break;
+
+ case 8:
+ wanted = 8;
+ break;
+
+ default:
+ wanted = 4;
+ break;
+ }
+
+ *mburst = wanted / 2;
+ *rburst = wanted / 2;
+}
+
+static void s3c_camif_calc_burst_length(unsigned int hsize, unsigned int *mburst, unsigned int *rburst)
+{
+ unsigned int tmp;
+
+ tmp = (hsize / 4) & 0xf;
+
+ switch (tmp) {
+ case 0:
+ *mburst = 16;
+ *rburst = 16;
+ break;
+
+ case 4:
+ *mburst = 16;
+ *rburst = 4;
+ break;
+
+ case 8:
+ *mburst = 16;
+ *rburst = 8;
+ break;
+
+ default:
+ tmp = (hsize / 4) % 8;
+
+ if (tmp == 0) {
+ *mburst = 8;
+ *rburst = 8;
+ } else if (tmp == 4) {
+ *mburst = 8;
+ *rburst = 4;
+ } else {
+ tmp = (hsize / 4) % 4;
+ *mburst = 4;
+ *rburst = (tmp) ? tmp : 4;
+ }
+
+ break;
+ }
+}
+
+int s3c_camif_setup_dma(camif_cfg_t *cfg)
+{
+ int width = cfg->target_x;
+ unsigned int val, yburst_m, yburst_r, cburst_m, cburst_r;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ if ((cfg->dst_fmt == CAMIF_RGB16) || (cfg->dst_fmt == CAMIF_RGB24)) {
+ if (cfg->dst_fmt == CAMIF_RGB24) {
+ if (width % 2 != 0)
+ return BURST_ERR;
+
+ s3c_camif_calc_burst_length(width * 4, &yburst_m, &yburst_r);
+ } else {
+ if ((width / 2) % 2 != 0)
+ return BURST_ERR;
+
+ s3c_camif_calc_burst_length(width * 2, &yburst_m, &yburst_r);
+ }
+
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val &= ~(0xfffff << 4);
+
+ if (cfg->dst_fmt == CAMIF_RGB24) {
+ val = S3C_CICOCTRL_YBURST1_CO(yburst_m / 2) | \
+ S3C_CICOCTRL_YBURST2_CO(yburst_r / 4) | (4 << 9) | (2 << 4);
+ } else {
+ val = S3C_CICOCTRL_YBURST1_CO(yburst_m / 2) | \
+ S3C_CICOCTRL_YBURST2_CO(yburst_r / 2) | (4 << 9) | (2 << 4);
+ }
+
+ writel(val, cfg->regs + S3C_CICOCTRL);
+ } else {
+ /* CODEC DMA WIDHT is multiple of 16 */
+ if (width % 16 != 0)
+ return BURST_ERR;
+
+ if (cfg->dst_fmt == CAMIF_YCBCR422I) {
+ s3c_camif_calc_burst_length_yuv422i(width, &yburst_m, &yburst_r);
+ cburst_m = yburst_m / 2;
+ cburst_r = yburst_r / 2;
+ } else {
+ s3c_camif_calc_burst_length(width, &yburst_m, &yburst_r);
+ s3c_camif_calc_burst_length(width / 2, &cburst_m, &cburst_r);
+ }
+
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val &= ~(0xfffff << 4);
+ val |= (S3C_CICOCTRL_YBURST1_CO(yburst_m) | S3C_CICOCTRL_CBURST1_CO(cburst_m) | \
+ S3C_CICOCTRL_YBURST2_CO(yburst_r) | S3C_CICOCTRL_CBURST2_CO(cburst_r));
+ writel(val, cfg->regs + S3C_CICOCTRL);
+ }
+ } else if (cfg->dma_type & CAMIF_PREVIEW) {
+ if (cfg->dst_fmt == CAMIF_RGB24) {
+ if (width % 2 != 0)
+ return BURST_ERR;
+
+ s3c_camif_calc_burst_length(width * 4, &yburst_m, &yburst_r);
+ } else {
+ if ((width / 2) % 2 != 0)
+ return BURST_ERR;
+
+ s3c_camif_calc_burst_length(width * 2, &yburst_m, &yburst_r);
+ }
+
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val &= ~(0x3ff << 14);
+ val |= (S3C_CICOCTRL_YBURST1_CO(yburst_m) | S3C_CICOCTRL_YBURST2_CO(yburst_r));
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ }
+
+ return 0;
+}
+
+/*************************************************************************
+ * Input path part
+ ************************************************************************/
+/*
+ * 2443 MSDMA (Preview Only)
+ */
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+int s3c_camif_input_msdma_preview(camif_cfg_t * cfg)
+{
+ int ret = 0;
+ unsigned int addr_start_Y = 0, addr_start_CB = 0, addr_start_CR = 0;
+ unsigned int addr_end_Y = 0, addr_end_CB = 0, addr_end_CR = 0;
+ unsigned int val, val_width;
+
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(1 << 2);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val |= (1 << 2);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ if (cfg->src_fmt != CAMIF_YCBCR420 && cfg->src_fmt != CAMIF_YCBCR422 && cfg->src_fmt != CAMIF_YCBCR422I)
+ cfg->src_fmt = CAMIF_YCBCR420;
+
+ switch(cfg->src_fmt) {
+ case CAMIF_YCBCR420:
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val = (val & ~(0x1 << 1)) | (0x1 << 1);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ addr_start_Y = readl(cfg->regs + S3C_CIMSYSA);
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ break;
+
+ case CAMIF_YCBCR422:
+ case CAMIF_YCBCR422I:
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val = (val & ~(0x1 << 5)) | (0x1 << 5); /* Interleave_MS */
+ val &= ~(0x1 << 1);
+ val &= ~(0x3 << 3); /* YCbYCr */
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ addr_start_Y = readl(cfg->regs + S3C_CIMSYSA);
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ break;
+
+ default:
+ break;
+ }
+
+ /* MSDMA memory */
+ writel(addr_start_Y, cfg->regs + S3C_CIMSYSA);
+ writel(addr_start_CB, cfg->regs + S3C_CIMSCBSA);
+ writel(addr_start_CR, cfg->regs + S3C_CIMSCRSA);
+
+ writel(addr_end_Y, cfg->regs + S3C_CIMSYEND);
+ writel(addr_end_CB, cfg->regs + S3C_CIMSCBEND);
+ writel(addr_end_CR, cfg->regs + S3C_CIMSCREND);
+
+ /* MSDMA memory offset - default : 0 */
+ writel(0, cfg->regs + S3C_CIMSYOFF);
+ writel(0, cfg->regs + S3C_CIMSCBOFF);
+ writel(0, cfg->regs + S3C_CIMSCROFF);
+
+ /* MSDMA for codec source image width */
+ val_width = readl(cfg->regs + S3C_CIMSWIDTH);
+ val_width = (val_width & ~(0x1 << 31)); /* AutoLoadDisable */
+ val_width |= (cfg->cis->source_y << 16); /* MSCOHEIGHT */
+ val_width |= cfg->cis->source_x; /* MSCOWIDTH */
+ val_width = cfg->cis->source_x;
+ writel(val_width, cfg->regs + S3C_CIMSWIDTH);
+
+ return ret;
+}
+
+static int s3c_camif_input_msdma(camif_cfg_t *cfg)
+{
+ if (cfg->input_channel == MSDMA_FROM_PREVIEW)
+ s3c_camif_input_msdma_preview(cfg);
+
+ return 0;
+}
+
+/*
+ * 6400 MSDMA (Preview & Codec)
+ */
+#elif defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+int s3c_camif_input_msdma_codec(camif_cfg_t * cfg)
+{
+ int ret = 0;
+ u32 addr_start_Y = 0, addr_start_CB = 0, addr_start_CR = 0;
+ u32 addr_end_Y = 0, addr_end_CB = 0, addr_end_CR = 0;
+ u32 val, val_width;
+
+ /* Codec path input data selection */
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val &= ~(1 << 3);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val |= (1 << 3);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ if (cfg->src_fmt != CAMIF_YCBCR420 && cfg->src_fmt != CAMIF_YCBCR422 && cfg->src_fmt != CAMIF_YCBCR422I)
+ cfg->src_fmt = CAMIF_YCBCR420;
+
+ switch(cfg->src_fmt) {
+ case CAMIF_YCBCR420:
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val &= ~(0x3 << 1);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ addr_start_Y = cfg->pp_phys_buf;
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ break;
+
+ case CAMIF_YCBCR422:
+ case CAMIF_YCBCR422I:
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val = (val & ~(0x3 << 1)) |(0x2 << 1);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ addr_start_Y = cfg->pp_phys_buf;
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ break;
+
+ default:
+ break;
+ }
+
+ /* MSDMA memory */
+ writel(addr_start_Y, cfg->regs + S3C_MSCOY0SA);
+ writel(addr_start_CB, cfg->regs + S3C_MSCOCB0SA);
+ writel(addr_start_CR, cfg->regs + S3C_MSCOCR0SA);
+
+ writel(addr_end_Y, cfg->regs + S3C_MSCOY0END);
+ writel(addr_end_CB, cfg->regs + S3C_MSCOCB0END);
+ writel(addr_end_CR, cfg->regs + S3C_MSCOCR0END);
+
+ /* MSDMA memory offset */
+ writel(0, cfg->regs + S3C_MSCOYOFF);
+ writel(0, cfg->regs + S3C_MSCOCBOFF);
+ writel(0, cfg->regs + S3C_MSCOCROFF);
+
+ /* MSDMA for codec source image width */
+ val_width = readl(cfg->regs + S3C_MSCOWIDTH);
+ val_width = (val_width & ~(0x1 << 31))|(0x1 << 31); /* AutoLoadEnable */
+ val_width |= (cfg->cis->source_y << 16); /* MSCOHEIGHT */
+ val_width |= cfg->cis->source_x; /* MSCOWIDTH */
+ writel(val_width, cfg->regs + S3C_MSCOWIDTH);
+
+ return ret;
+}
+
+int s3c_camif_input_msdma_preview(camif_cfg_t * cfg)
+{
+ int ret = 0;
+ unsigned int addr_start_Y = 0, addr_start_CB = 0, addr_start_CR = 0;
+ unsigned int addr_end_Y = 0, addr_end_CB = 0, addr_end_CR = 0;
+ unsigned int val, val_width;
+
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(0x1 << 3);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val |= (0x1 << 3);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ if (cfg->src_fmt != CAMIF_YCBCR420 && cfg->src_fmt != CAMIF_YCBCR422 && cfg->src_fmt != CAMIF_YCBCR422I)
+ cfg->src_fmt = CAMIF_YCBCR420;
+
+ switch(cfg->src_fmt) {
+ case CAMIF_YCBCR420:
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(0x3 << 1);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ addr_start_Y = readl(cfg->regs + S3C_MSPRY0SA);
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 4);
+ break;
+
+ case CAMIF_YCBCR422:
+ case CAMIF_YCBCR422I:
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val = (val & ~(0x3 << 1)) | (0x2 << 1); /* YCbCr 422 Interleave */
+ val = (val & ~(0x3 << 4)) | (0x3 << 4); /* YCbYCr */
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ addr_start_Y = readl(cfg->regs + S3C_MSPRY0SA);
+ addr_start_CB = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_start_CR = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+
+ addr_end_Y = addr_start_Y + (cfg->cis->source_x * cfg->cis->source_y);
+ addr_end_CB = addr_start_CB + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ addr_end_CR = addr_start_CR + (cfg->cis->source_x * cfg->cis->source_y / 2);
+ break;
+
+ default:
+ break;
+ }
+
+ /* MSDMA memory */
+ writel(addr_start_Y, cfg->regs + S3C_MSPRY0SA);
+ writel(addr_start_CB, cfg->regs + S3C_MSPRCB0SA);
+ writel(addr_start_CR, cfg->regs + S3C_MSPRCR0SA);
+
+ writel(addr_end_Y, cfg->regs + S3C_MSPRY0END);
+ writel(addr_end_CB, cfg->regs + S3C_MSPRCB0END);
+ writel(addr_end_CR, cfg->regs + S3C_MSPRCR0END);
+
+ /* MSDMA memory offset */
+ writel(0, cfg->regs + S3C_MSPRYOFF);
+ writel(0, cfg->regs + S3C_MSPRCBOFF);
+ writel(0, cfg->regs + S3C_MSPRCROFF);
+
+ /* MSDMA for codec source image width */
+ val_width = readl(cfg->regs + S3C_MSPRWIDTH);
+ val_width = (val_width & ~(0x1 << 31)); /* AutoLoadEnable */
+ val_width |= (cfg->cis->source_y << 16); /* MSCOHEIGHT */
+ val_width |= cfg->cis->source_x; /* MSCOWIDTH */
+ writel(val_width, cfg->regs + S3C_MSPRWIDTH);
+
+ return ret;
+}
+
+static int s3c_camif_input_msdma(camif_cfg_t *cfg)
+{
+ if (cfg->input_channel == MSDMA_FROM_PREVIEW)
+ s3c_camif_input_msdma_preview(cfg);
+ else if (cfg->input_channel == MSDMA_FROM_CODEC)
+ s3c_camif_input_msdma_codec(cfg);
+
+ return 0;
+}
+#endif
+
+static int s3c_camif_input_camera(camif_cfg_t *cfg)
+{
+ unsigned int val;
+
+ s3c_camif_set_offset(cfg->cis);
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val &= ~(1 << 3);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+#endif
+ } else if (cfg->dma_type & CAMIF_PREVIEW) {
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450)
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(1 << 2);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+#elif defined(CONFIG_CPU_S3C6400)
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(1 << 3);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+#endif
+ } else
+ printk(KERN_ERR "Invalid DMA type\n");
+
+ return 0;
+}
+
+static int s3c_camif_setup_input_path(camif_cfg_t *cfg)
+{
+ if (cfg->input_channel == CAMERA_INPUT)
+ s3c_camif_input_camera(cfg);
+ else
+ s3c_camif_input_msdma(cfg);
+
+ return 0;
+}
+
+/*************************************************************************
+ * Output path part
+ ************************************************************************/
+static int s3c_camif_output_pp_codec_rgb(camif_cfg_t *cfg)
+{
+ int i;
+ unsigned int val;
+ unsigned int area = cfg->target_x * cfg->target_y;
+
+ if (cfg->dst_fmt & CAMIF_RGB24)
+ area = area * 4;
+ else {
+ assert (cfg->dst_fmt & CAMIF_RGB16);
+ area = area * 2;
+ }
+
+ if ((area % PAGE_SIZE) != 0) {
+ i = area / PAGE_SIZE;
+ area = (i + 1) * PAGE_SIZE;
+ }
+
+ cfg->buffer_size = area;
+
+ if (cfg->input_channel == MSDMA_FROM_CODEC) {
+ val = readl(S3C_VIDW00ADD0B0);
+
+ for (i = 0; i < 4; i++)
+ writel(val, cfg->regs + S3C_CICOYSA(i));
+ } else {
+ switch ( cfg->pp_num ) {
+ case 1:
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf;
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CICOYSA(i));
+ }
+
+ break;
+
+ case 2:
+ for (i = 0; i < 4; i++) {
+ if (i == 0 || i == 2) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf;
+ } else {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf + area;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf + area;
+ }
+
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CICOYSA(i));
+ }
+
+ break;
+
+ case 4:
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf + i * area;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf + i * area;
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CICOYSA(i));
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "Invalid pingpong number %d\n", cfg->pp_num);
+ panic("s3c camif halt\n");
+ }
+ }
+
+ return 0;
+}
+
+static int s3c_camif_output_pp_codec(camif_cfg_t *cfg)
+{
+ unsigned int i, cbcr_size = 0;
+ unsigned int area = cfg->target_x * cfg->target_y;
+ unsigned int one_p_size;
+
+ area = cfg->target_x * cfg->target_y;
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420)
+ cbcr_size = area / 4;
+ else if (cfg->dst_fmt & CAMIF_YCBCR422 || cfg->dst_fmt & CAMIF_YCBCR422I)
+ cbcr_size = area / 2;
+ else if ((cfg->dst_fmt & CAMIF_RGB16) || (cfg->dst_fmt & CAMIF_RGB24)) {
+ s3c_camif_output_pp_codec_rgb(cfg);
+ return 0;
+ } else
+ printk(KERN_ERR "Invalid target format %d\n", cfg->dst_fmt);
+
+ one_p_size = area + (2 * cbcr_size);
+
+ if ((one_p_size % PAGE_SIZE) != 0) {
+ i = one_p_size / PAGE_SIZE;
+ one_p_size = (i + 1) * PAGE_SIZE;
+ }
+
+ cfg->buffer_size = one_p_size;
+
+ switch (cfg->pp_num) {
+ case 1 :
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_y = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_y = cfg->pp_phys_buf;
+ cfg->img_buf[i].virt_cb = cfg->pp_virt_buf + area;
+ cfg->img_buf[i].phys_cb = cfg->pp_phys_buf + area;
+ cfg->img_buf[i].virt_cr = cfg->pp_virt_buf + area + cbcr_size;
+ cfg->img_buf[i].phys_cr = cfg->pp_phys_buf + area + cbcr_size;
+ writel(cfg->img_buf[i].phys_y, cfg->regs + S3C_CICOYSA(i));
+ writel(cfg->img_buf[i].phys_cb, cfg->regs + S3C_CICOCBSA(i));
+ writel(cfg->img_buf[i].phys_cr, cfg->regs + S3C_CICOCRSA(i));
+ }
+
+ break;
+
+ case 2:
+ for (i = 0; i < 4; i++) {
+ if (i == 0 || i == 2) {
+ cfg->img_buf[i].virt_y = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_y = cfg->pp_phys_buf;
+ cfg->img_buf[i].virt_cb = cfg->pp_virt_buf + area;
+ cfg->img_buf[i].phys_cb = cfg->pp_phys_buf + area;
+ cfg->img_buf[i].virt_cr = cfg->pp_virt_buf + area + cbcr_size;
+ cfg->img_buf[i].phys_cr = cfg->pp_phys_buf + area + cbcr_size;
+ } else {
+ cfg->img_buf[i].virt_y = cfg->pp_virt_buf + one_p_size;
+ cfg->img_buf[i].phys_y = cfg->pp_phys_buf + one_p_size;
+ cfg->img_buf[i].virt_cb = cfg->pp_virt_buf + area + one_p_size;
+ cfg->img_buf[i].phys_cb = cfg->pp_phys_buf + area + one_p_size;
+ cfg->img_buf[i].virt_cr = cfg->pp_virt_buf + area + cbcr_size + one_p_size;
+ cfg->img_buf[i].phys_cr = cfg->pp_phys_buf + area + cbcr_size + one_p_size;
+ }
+
+ writel(cfg->img_buf[i].phys_y, cfg->regs + S3C_CICOYSA(i));
+ writel(cfg->img_buf[i].phys_cb, cfg->regs + S3C_CICOCBSA(i));
+ writel(cfg->img_buf[i].phys_cr, cfg->regs + S3C_CICOCRSA(i));
+ }
+
+ break;
+
+ case 4:
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_y = cfg->pp_virt_buf + i * one_p_size;
+ cfg->img_buf[i].phys_y = cfg->pp_phys_buf + i * one_p_size;
+ cfg->img_buf[i].virt_cb = cfg->pp_virt_buf + area + i * one_p_size;
+ cfg->img_buf[i].phys_cb = cfg->pp_phys_buf + area + i * one_p_size;
+ cfg->img_buf[i].virt_cr = cfg->pp_virt_buf + area + cbcr_size + i * one_p_size;
+ cfg->img_buf[i].phys_cr = cfg->pp_phys_buf + area + cbcr_size + i * one_p_size;
+ writel(cfg->img_buf[i].phys_y, cfg->regs + S3C_CICOYSA(i));
+ writel(cfg->img_buf[i].phys_cb, cfg->regs + S3C_CICOCBSA(i));
+ writel(cfg->img_buf[i].phys_cr, cfg->regs + S3C_CICOCRSA(i));
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "Invalid pingpong number %d\n", cfg->pp_num);
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+static int s3c_camif_io_duplex_preview(camif_cfg_t *cfg)
+{
+ unsigned int cbcr_size = 0;
+ unsigned int area = cfg->cis->source_x * cfg->cis->source_y;
+ unsigned int val;
+ int i;
+
+ val = readl(S3C_VIDW01ADD0);
+
+ if (!((cfg->dst_fmt & CAMIF_RGB16) || (cfg->dst_fmt & CAMIF_RGB24)))
+ printk(KERN_ERR "Invalid target format\n");
+
+ for (i = 0; i < 4; i++)
+ writel(val, cfg->regs + S3C_CIPRYSA(i));
+
+ if (cfg->src_fmt & CAMIF_YCBCR420) {
+ cbcr_size = area / 4;
+ cfg->img_buf[0].virt_cb = cfg->pp_virt_buf + area;
+ cfg->img_buf[0].phys_cb = cfg->pp_phys_buf + area;
+ cfg->img_buf[0].virt_cr = cfg->pp_virt_buf + area + cbcr_size;
+ cfg->img_buf[0].phys_cr = cfg->pp_phys_buf + area + cbcr_size;
+ } else if (cfg->src_fmt & CAMIF_YCBCR422 || cfg->dst_fmt & CAMIF_YCBCR422I) {
+ area = area * 2;
+ cfg->img_buf[0].virt_cb = 0;
+ cfg->img_buf[0].phys_cb = 0;
+ cfg->img_buf[0].virt_cr = 0;
+ cfg->img_buf[0].phys_cr = 0;
+ }
+
+ cfg->img_buf[0].virt_y = cfg->pp_virt_buf;
+ cfg->img_buf[0].phys_y = cfg->pp_phys_buf;
+
+ writel(cfg->img_buf[0].phys_y, cfg->regs + S3C_CIMSYSA);
+ writel(cfg->img_buf[0].phys_y + area, cfg->regs + S3C_CIMSYEND);
+
+ writel(cfg->img_buf[0].phys_cb, cfg->regs + S3C_CIMSCBSA);
+ writel(cfg->img_buf[0].phys_cb + cbcr_size, cfg->regs + S3C_CIMSCBEND);
+
+ writel(cfg->img_buf[0].phys_cr, cfg->regs + S3C_CIMSCRSA);
+ writel(cfg->img_buf[0].phys_cr + cbcr_size, cfg->regs + S3C_CIMSCREND);
+
+ writel(cfg->cis->source_x, cfg->regs + S3C_CIMSWIDTH);
+
+ return 0;
+}
+#elif defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+static int s3c_camif_io_duplex_preview(camif_cfg_t *cfg)
+{
+ unsigned int cbcr_size = 0;
+ unsigned int area = cfg->cis->source_x * cfg->cis->source_y;
+ unsigned int val;
+ int i;
+
+ val = readl(S3C_VIDW01ADD0B0);
+
+ if (!((cfg->dst_fmt & CAMIF_RGB16) || (cfg->dst_fmt & CAMIF_RGB24)))
+ printk(KERN_ERR "Invalid target format\n");
+
+ for (i = 0; i < 4; i++)
+ writel(val, cfg->regs + S3C_CIPRYSA(i));
+
+ if (cfg->src_fmt & CAMIF_YCBCR420) {
+ cbcr_size = area / 4;
+ cfg->img_buf[0].virt_cb = cfg->pp_virt_buf + area;
+ cfg->img_buf[0].phys_cb = cfg->pp_phys_buf + area;
+ cfg->img_buf[0].virt_cr = cfg->pp_virt_buf + area + cbcr_size;
+ cfg->img_buf[0].phys_cr = cfg->pp_phys_buf + area + cbcr_size;
+ } else if (cfg->src_fmt & CAMIF_YCBCR422 || cfg->dst_fmt & CAMIF_YCBCR422I){
+ area = area * 2;
+ cfg->img_buf[0].virt_cb = 0;
+ cfg->img_buf[0].phys_cb = 0;
+ cfg->img_buf[0].virt_cr = 0;
+ cfg->img_buf[0].phys_cr = 0;
+ }
+
+ cfg->img_buf[0].virt_y = cfg->pp_virt_buf;
+ cfg->img_buf[0].phys_y = cfg->pp_phys_buf;
+
+ writel(cfg->img_buf[0].phys_y, cfg->regs + S3C_MSPRY0SA);
+ writel(cfg->img_buf[0].phys_y + area, cfg->regs + S3C_MSPRY0END);
+
+ writel(cfg->img_buf[0].phys_cb, cfg->regs + S3C_MSPRCB0SA);
+ writel(cfg->img_buf[0].phys_cb + cbcr_size, cfg->regs + S3C_MSPRCB0END);
+
+ writel(cfg->img_buf[0].phys_cr, cfg->regs + S3C_MSPRCR0SA);
+ writel(cfg->img_buf[0].phys_cr + cbcr_size, cfg->regs + S3C_MSPRCR0END);
+
+ val = readl(cfg->regs + S3C_MSCOWIDTH);
+ val = (val & ~(0x1 << 31)) | (0x1 << 31);
+ val |= (cfg->cis->source_y << 16);
+ val |= cfg->cis->source_x;
+ writel(val, cfg->regs + S3C_MSPRWIDTH);
+
+ return 0;
+}
+#endif
+
+static int s3c_camif_output_pp_preview(camif_cfg_t *cfg)
+{
+ int i;
+ unsigned int cbcr_size = 0;
+ unsigned int area = cfg->target_x * cfg->target_y;
+
+ if (cfg->input_channel) {
+ s3c_camif_io_duplex_preview(cfg);
+ return 0;
+ }
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420)
+ cbcr_size = area / 4;
+ else if (cfg->dst_fmt & CAMIF_YCBCR422 || cfg->dst_fmt & CAMIF_YCBCR422I)
+ cbcr_size = area / 2;
+ else if (cfg->dst_fmt & CAMIF_RGB24)
+ area = area * 4;
+ else if (cfg->dst_fmt & CAMIF_RGB16)
+ area = area * 2;
+ else
+ printk(KERN_ERR "Invalid target format %d\n", cfg->dst_fmt);
+
+ if ((area % PAGE_SIZE) != 0) {
+ i = area / PAGE_SIZE;
+ area = (i + 1) * PAGE_SIZE;
+ }
+
+ cfg->buffer_size = area;
+
+ switch (cfg->pp_num) {
+ case 1:
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf;
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CIPRYSA(i));
+ }
+
+ break;
+
+ case 2:
+ for (i = 0; i < 4; i++) {
+ if (i == 0 || i == 2) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf;
+ } else {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf + area;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf + area;
+ }
+
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CIPRYSA(i));
+ }
+
+ break;
+
+ case 4:
+ for (i = 0; i < 4; i++) {
+ cfg->img_buf[i].virt_rgb = cfg->pp_virt_buf + i * area;
+ cfg->img_buf[i].phys_rgb = cfg->pp_phys_buf + i * area;
+ writel(cfg->img_buf[i].phys_rgb, cfg->regs + S3C_CIPRYSA(i));
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "Invalid pingpong number %d\n", cfg->pp_num);
+ }
+
+ return 0;
+}
+
+static int s3c_camif_output_pp(camif_cfg_t *cfg)
+{
+ if (cfg->dma_type & CAMIF_CODEC)
+ s3c_camif_output_pp_codec(cfg);
+ else if ( cfg->dma_type & CAMIF_PREVIEW)
+ s3c_camif_output_pp_preview(cfg);
+
+ return 0;
+}
+
+static int s3c_camif_output_lcd(camif_cfg_t *cfg)
+{
+ /* To Be Implemented */
+ return 0;
+}
+
+static int s3c_camif_setup_output_path(camif_cfg_t *cfg)
+{
+ if (cfg->output_channel == CAMIF_OUT_FIFO)
+ s3c_camif_output_lcd(cfg);
+ else
+ s3c_camif_output_pp(cfg);
+
+ return 0;
+}
+
+/*************************************************************************
+ * Scaler part
+ ************************************************************************/
+static int s3c_camif_set_target_area(camif_cfg_t *cfg)
+{
+ unsigned int rect = cfg->target_x * cfg->target_y;
+
+ if (cfg->dma_type & CAMIF_CODEC)
+ writel(rect, cfg->regs + S3C_CICOTAREA);
+ else if (cfg->dma_type & CAMIF_PREVIEW)
+ writel(rect, cfg->regs + S3C_CIPRTAREA);
+
+ return 0;
+}
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+static inline int s3c_camif_set_ratio(camif_cfg_t *cfg)
+{
+ unsigned int cmd = (S3C_CICOSCCTRL_CSCR2Y_WIDE | S3C_CICOSCCTRL_CSCY2R_WIDE);
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+
+ writel(S3C_CICOSCPRERATIO_SHFACTOR_CO(cfg->sc.shfactor) | \
+ S3C_CICOSCPRERATIO_PREHORRATIO_CO(cfg->sc.prehratio) | \
+ S3C_CICOSCPRERATIO_PREVERRATIO_CO(cfg->sc.prevratio), cfg->regs + S3C_CICOSCPRERATIO);
+
+ writel(S3C_CICOSCPREDST_PREDSTWIDTH_CO(cfg->sc.predst_x) | \
+ S3C_CICOSCPREDST_PREDSTHEIGHT_CO(cfg->sc.predst_y), cfg->regs + S3C_CICOSCPREDST);
+
+ /* Differ from Preview */
+ if (cfg->sc.scalerbypass)
+ cmd |= S3C_CICOSCCTRL_SCALERBYPASS_CO;
+
+ /* Differ from Codec */
+ if (cfg->dst_fmt & CAMIF_RGB24)
+ cmd |= S3C_CICOSCCTRL_OUTRGB_FMT_RGB888;
+ else
+ cmd |= S3C_CICOSCCTRL_OUTRGB_FMT_RGB565;
+
+ if (cfg->sc.scaleup_h & cfg->sc.scaleup_v)
+ cmd |= (S3C_CICOSCCTRL_SCALEUP_H | S3C_CICOSCCTRL_SCALEUP_V);
+
+ writel(cmd | S3C_CICOSCCTRL_MAINHORRATIO_CO(cfg->sc.mainhratio) | \
+ S3C_CICOSCCTRL_MAINVERRATIO_CO(cfg->sc.mainvratio), cfg->regs + S3C_CICOSCCTRL);
+
+ } else if (cfg->dma_type & CAMIF_PREVIEW) {
+
+ writel(S3C_CIPRSCPRERATIO_SHFACTOR_PR(cfg->sc.shfactor) | \
+ S3C_CIPRSCPRERATIO_PREHORRATIO_PR(cfg->sc.prehratio) | \
+ S3C_CIPRSCPRERATIO_PREVERRATIO_PR(cfg->sc.prevratio), cfg->regs + S3C_CIPRSCPRERATIO);
+
+ writel(S3C_CIPRSCPREDST_PREDSTWIDTH_PR(cfg->sc.predst_x) | \
+ S3C_CIPRSCPREDST_PREDSTHEIGHT_PR(cfg->sc.predst_y), cfg->regs + S3C_CIPRSCPREDST);
+
+ if (cfg->dst_fmt & CAMIF_RGB24)
+ cmd |= S3C_CIPRSCCTRL_OUTRGB_FMT_PR_RGB888;
+ else
+ cmd |= S3C_CIPRSCCTRL_OUTRGB_FMT_PR_RGB565;
+
+ if (cfg->sc.scaleup_h & cfg->sc.scaleup_v)
+ cmd |= ((1 << 30) | (1 << 29));
+
+ writel(cmd | S3C_CIPRSCCTRL_MAINHORRATIO_PR(cfg->sc.mainhratio) | \
+ S3C_CIPRSCCTRL_MAINVERRATIO_PR(cfg->sc.mainvratio), cfg->regs + S3C_CIPRSCCTRL);
+
+ } else
+ printk(KERN_ERR "Invalid DMA type\n");
+
+ return 0;
+}
+#elif defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+static inline int s3c_camif_set_ratio(camif_cfg_t *cfg)
+{
+ u32 cmd = 0;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+
+ writel(S3C_CICOSCPRERATIO_SHFACTOR_CO(cfg->sc.shfactor) | \
+ S3C_CICOSCPRERATIO_PREHORRATIO_CO(cfg->sc.prehratio) | \
+ S3C_CICOSCPRERATIO_PREVERRATIO_CO(cfg->sc.prevratio), cfg->regs + S3C_CICOSCPRERATIO);
+
+ writel(S3C_CICOSCPREDST_PREDSTWIDTH_CO(cfg->sc.predst_x) | \
+ S3C_CICOSCPREDST_PREDSTHEIGHT_CO(cfg->sc.predst_y), cfg->regs + S3C_CICOSCPREDST);
+
+ if (cfg->sc.scalerbypass)
+ cmd |= S3C_CICOSCCTRL_SCALERBYPASS_CO;
+
+ if (cfg->sc.scaleup_h & cfg->sc.scaleup_v)
+ cmd |= (S3C_CICOSCCTRL_SCALEUP_H | S3C_CICOSCCTRL_SCALEUP_V);
+
+ writel(cmd | S3C_CICOSCCTRL_MAINHORRATIO_CO(cfg->sc.mainhratio) | \
+ S3C_CICOSCCTRL_MAINVERRATIO_CO(cfg->sc.mainvratio), cfg->regs + S3C_CICOSCCTRL);
+
+ } else if (cfg->dma_type & CAMIF_PREVIEW) {
+
+ cmd |= S3C_CIPRSCCTRL_SAMPLE_PR;
+
+ writel(S3C_CIPRSCPRERATIO_SHFACTOR_PR(cfg->sc.shfactor) | \
+ S3C_CIPRSCPRERATIO_PREHORRATIO_PR(cfg->sc.prehratio) | \
+ S3C_CIPRSCPRERATIO_PREVERRATIO_PR(cfg->sc.prevratio), cfg->regs + S3C_CIPRSCPRERATIO);
+
+ writel(S3C_CIPRSCPREDST_PREDSTWIDTH_PR(cfg->sc.predst_x) | \
+ S3C_CIPRSCPREDST_PREDSTHEIGHT_PR(cfg->sc.predst_y), cfg->regs + S3C_CIPRSCPREDST);
+
+ if (cfg->dst_fmt & CAMIF_RGB24)
+ cmd |= S3C_CIPRSCCTRL_RGBFORMAT_24;
+
+ if (cfg->sc.scaleup_h & cfg->sc.scaleup_v)
+ cmd |= ((1 << 29) | (1 << 28));
+
+ writel(cmd | S3C_CIPRSCCTRL_MAINHORRATIO_PR(cfg->sc.mainhratio) | \
+ S3C_CIPRSCCTRL_MAINVERRATIO_PR(cfg->sc.mainvratio), cfg->regs + S3C_CIPRSCCTRL);
+
+ } else
+ printk(KERN_ERR "Invalid DMA type\n");
+
+ return 0;
+}
+#endif
+
+static int s3c_camif_calc_ratio(unsigned int src_width, unsigned int dst_width, unsigned int *ratio, unsigned int *shift)
+{
+ if (src_width >= 64 * dst_width) {
+ printk(KERN_ERR "Out of pre-scaler range: src_width / dst_width = %d (< 64)\n", src_width / dst_width);
+ return 1;
+ } else if (src_width >= 32 * dst_width) {
+ *ratio = 32;
+ *shift = 5;
+ } else if (src_width >= 16 * dst_width) {
+ *ratio = 16;
+ *shift = 4;
+ } else if (src_width >= 8 * dst_width) {
+ *ratio = 8;
+ *shift = 3;
+ } else if (src_width >= 4 * dst_width) {
+ *ratio = 4;
+ *shift = 2;
+ } else if (src_width >= 2 * dst_width) {
+ *ratio = 2;
+ *shift = 1;
+ } else {
+ *ratio = 1;
+ *shift = 0;
+ }
+
+ return 0;
+}
+
+static int s3c_camif_setup_scaler(camif_cfg_t *cfg)
+{
+ int tx = cfg->target_x, ty=cfg->target_y;
+ int sx, sy;
+
+ if (tx <= 0 || ty <= 0) {
+ printk(KERN_ERR "Invalid target size\n");
+ return -1;
+ }
+
+ sx = cfg->cis->source_x - (cfg->cis->win_hor_ofst + cfg->cis->win_hor_ofst2);
+ sy = cfg->cis->source_y - (cfg->cis->win_ver_ofst + cfg->cis->win_hor_ofst2);
+
+ if (sx <= 0 || sy <= 0) {
+ printk(KERN_ERR "Invalid source size\n");
+ return -1;
+ }
+
+ cfg->sc.modified_src_x = sx;
+ cfg->sc.modified_src_y = sy;
+
+ /* Pre-scaler control register 1 */
+ s3c_camif_calc_ratio(sx, tx, &cfg->sc.prehratio, &cfg->sc.hfactor);
+ s3c_camif_calc_ratio(sy, ty, &cfg->sc.prevratio, &cfg->sc.vfactor);
+
+ if (cfg->dma_type & CAMIF_PREVIEW) {
+ if ((sx / cfg->sc.prehratio) > 640) {
+ printk(KERN_INFO "Internal preview line buffer length is 640 pixels\n");
+ printk(KERN_INFO "Decrease the resolution or adjust window offset values appropriately\n");
+ }
+ }
+
+ cfg->sc.shfactor = 10 - (cfg->sc.hfactor + cfg->sc.vfactor);
+
+ /* Pre-scaler control register 2 */
+ cfg->sc.predst_x = sx / cfg->sc.prehratio;
+ cfg->sc.predst_y = sy / cfg->sc.prevratio;
+
+ /* Main-scaler control register */
+ cfg->sc.mainhratio = (sx << 8) / (tx << cfg->sc.hfactor);
+ cfg->sc.mainvratio = (sy << 8) / (ty << cfg->sc.vfactor);
+
+ cfg->sc.scaleup_h = (sx <= tx) ? 1 : 0;
+ cfg->sc.scaleup_v = (sy <= ty) ? 1 : 0;
+
+ s3c_camif_set_ratio(cfg);
+ s3c_camif_set_target_area(cfg);
+
+ return 0;
+}
+
+/*************************************************************************
+ * Format part
+ ************************************************************************/
+int s3c_camif_set_source_format(camif_cis_t *cis)
+{
+ camif_cfg_t *cfg = s3c_camif_get_fimc_object(CODEC_MINOR);
+ unsigned int cmd = 0;
+
+ /* Configure CISRCFMT --Source Format */
+ if (cis->itu_fmt & CAMIF_ITU601)
+ cmd = CAMIF_ITU601;
+ else {
+ assert(cis->itu_fmt & CAMIF_ITU656);
+ cmd = CAMIF_ITU656;
+ }
+
+ cmd |= (S3C_CISRCFMT_SOURCEHSIZE(cis->source_x) | S3C_CISRCFMT_SOURCEVSIZE(cis->source_y));
+
+ /* Order422 */
+ cmd |= cis->order422;
+ writel(cmd, cfg->regs + S3C_CISRCFMT);
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ cmd = (cis->order422 >> 14);
+ writel((readl(cfg->regs + S3C_CICOCTRL) & ~(0x3 << 0)) | cmd, cfg->regs + S3C_CICOCTRL);
+#endif
+
+ return 0;
+}
+
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+static int s3c_camif_set_target_format(camif_cfg_t *cfg)
+{
+ unsigned int cmd = 0;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ cmd |= S3C_CICOTRGFMT_TARGETHSIZE_CO(cfg->target_x) | S3C_CICOTRGFMT_TARGETVSIZE_CO(cfg->target_y);
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420) {
+ cmd |= (S3C_CICOTRGFMT_OUT422_420 | S3C_CICOTRGFMT_IN422_422);
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else if (cfg->dst_fmt & CAMIF_YCBCR422) {
+ cmd |= (S3C_CICOTRGFMT_OUT422_422 | S3C_CICOTRGFMT_IN422_422);
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else if ((cfg->dst_fmt & CAMIF_RGB24) || (cfg->dst_fmt & CAMIF_RGB16)) {
+ cmd |= (S3C_CICOTRGFMT_OUT422_422 | S3C_CICOTRGFMT_IN422_422);
+ writel(cmd | (1 << 29), cfg->regs + S3C_CICOTRGFMT);
+ } else
+ printk(KERN_ERR "Invalid target format\n");
+ } else {
+ assert(cfg->dma_type & CAMIF_PREVIEW);
+
+ cmd = readl(cfg->regs + S3C_CIPRTRGFMT);
+ cmd &= (S3C_CIPRTRGFMT_TARGETHSIZE_PR(0) | S3C_CIPRTRGFMT_TARGETVSIZE_PR(0));
+ cmd |= (S3C_CIPRTRGFMT_TARGETHSIZE_PR(cfg->target_x) | S3C_CIPRTRGFMT_TARGETVSIZE_PR(cfg->target_y));
+
+ writel(cmd | (2 << 30), cfg->regs + S3C_CIPRTRGFMT);
+ }
+
+ return 0;
+}
+#elif defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+static int s3c_camif_set_target_format(camif_cfg_t *cfg)
+{
+ unsigned int cmd = 0;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ cmd |= (S3C_CICOTRGFMT_TARGETHSIZE_CO(cfg->target_x) | S3C_CICOTRGFMT_TARGETVSIZE_CO(cfg->target_y));
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420) {
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR420OUT;
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else if (cfg->dst_fmt & CAMIF_YCBCR422) {
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR422OUT;
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else if (cfg->dst_fmt & CAMIF_YCBCR422I) {
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR422OUTINTERLEAVE;
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else if ((cfg->dst_fmt & CAMIF_RGB24) || (cfg->dst_fmt & CAMIF_RGB16)) {
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_RGBOUT;
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else
+ printk(KERN_ERR "Invalid target format\n");
+ } else {
+ assert(cfg->dma_type & CAMIF_PREVIEW);
+
+ cmd = readl(cfg->regs + S3C_CIPRTRGFMT);
+ cmd &= (S3C_CIPRTRGFMT_TARGETHSIZE_PR(0) | S3C_CIPRTRGFMT_TARGETVSIZE_PR(0));
+ cmd |= (S3C_CIPRTRGFMT_TARGETHSIZE_PR(cfg->target_x) | S3C_CIPRTRGFMT_TARGETVSIZE_PR(cfg->target_y));
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420)
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR420OUT;
+ else if (cfg->dst_fmt & CAMIF_YCBCR422)
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR422OUT;
+ else if (cfg->dst_fmt & CAMIF_YCBCR422I)
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_YCBCR422OUTINTERLEAVE;
+ else if ((cfg->dst_fmt & CAMIF_RGB24) || (cfg->dst_fmt & CAMIF_RGB16))
+ cmd |= S3C_CICOTRGFMT_OUTFORMAT_RGBOUT;
+ else
+ printk(KERN_ERR "Invalid target format\n");
+
+ writel(cmd, cfg->regs + S3C_CIPRTRGFMT);
+ }
+
+ return 0;
+}
+#endif
+
+/*************************************************************************
+ * Control part
+ ************************************************************************/
+int s3c_camif_control_fimc(camif_cfg_t *cfg)
+{
+ if (s3c_camif_request_memory(cfg)) {
+ printk(KERN_ERR "Instead of using consistent_alloc(). Let me use dedicated mem for DMA\n");
+ return -1;
+ }
+
+ s3c_camif_setup_input_path(cfg);
+
+ if (s3c_camif_setup_scaler(cfg)) {
+ printk(KERN_ERR "Preview scaler fault: change WinHorOfset or target size\n");
+ return 1;
+ }
+
+ s3c_camif_set_target_format(cfg);
+
+ if (s3c_camif_setup_dma(cfg)) {
+ printk(KERN_ERR "DMA burst length error\n");
+ return 1;
+ }
+
+ s3c_camif_setup_output_path(cfg);
+
+ return 0;
+}
+
+int s3c_camif_start_dma(camif_cfg_t *cfg)
+{
+ unsigned int n_cmd = readl(cfg->regs + S3C_CIIMGCPT);
+ unsigned int val;
+
+ switch(cfg->capture_enable) {
+ case CAMIF_BOTH_DMA_ON:
+ s3c_camif_reset(CAMIF_RESET, 0); /* Flush Camera Core Buffer */
+
+ /* For Codec */
+ val = readl(cfg->regs + S3C_CICOSCCTRL);
+ val |= S3C_CICOSCCTRL_COSCALERSTART;
+ writel(val, cfg->regs + S3C_CICOSCCTRL);
+
+ /* For Preview */
+ val = readl(cfg->regs + S3C_CIPRSCCTRL);
+ val |= S3C_CIPRSCCTRL_START;
+ writel(val, cfg->regs + S3C_CIPRSCCTRL);
+
+ n_cmd |= S3C_CIIMGCPT_IMGCPTEN_COSC | S3C_CIIMGCPT_IMGCPTEN_PRSC;
+ break;
+
+ case CAMIF_DMA_ON:
+ s3c_camif_reset(CAMIF_RESET, 0); /* Flush Camera Core Buffer */
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ val = readl(cfg->regs + S3C_CICOSCCTRL);
+ val |= S3C_CICOSCCTRL_COSCALERSTART;
+ writel(val, cfg->regs + S3C_CICOSCCTRL);
+
+ n_cmd |= S3C_CIIMGCPT_IMGCPTEN_COSC;
+
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+ n_cmd |= (1 << 24);
+#endif
+ } else {
+ val = readl(cfg->regs + S3C_CIPRSCCTRL);
+ val |= S3C_CIPRSCCTRL_START;
+ writel(val, cfg->regs + S3C_CIPRSCCTRL);
+ n_cmd |= S3C_CIIMGCPT_IMGCPTEN_PRSC;
+ }
+
+ /* wait until Sync Time expires */
+ /* First settting, to wait VSYNC fall */
+ /* By VESA spec,in 640x480 @60Hz
+ MAX Delay Time is around 64us which "while" has.*/
+ while (S3C_CICOSTATUS_VSYNC & readl(cfg->regs + S3C_CICOSTATUS));
+ break;
+
+ default:
+ break;
+ }
+
+#if defined(CONFIG_CPU_S3C2443)
+ if (cfg->dma_type & CAMIF_CODEC) {
+ if (cfg->dst_fmt & CAMIF_RGB24)
+ n_cmd |= (3 << 25);
+ else if (cfg->dst_fmt & CAMIF_RGB16)
+ n_cmd |= (1 << 25);
+ else if (cfg->dst_fmt & CAMIF_YCBCR420)
+ n_cmd |= (2 << 25);
+ }
+#endif
+
+ val = readl(cfg->regs + S3C_CIIMGCPT);
+ val &= ~(0x7 << 29);
+ writel(val | n_cmd | S3C_CIIMGCPT_IMGCPTEN, cfg->regs + S3C_CIIMGCPT);
+
+ return 0;
+}
+
+int s3c_camif_stop_dma(camif_cfg_t *cfg)
+{
+ unsigned int n_cmd = readl(cfg->regs + S3C_CIIMGCPT);
+ unsigned int val;
+
+ switch(cfg->capture_enable) {
+ case CAMIF_BOTH_DMA_OFF:
+ val = readl(cfg->regs + S3C_CICOSCCTRL);
+ val &= ~S3C_CICOSCCTRL_COSCALERSTART;
+ writel(val, cfg->regs + S3C_CICOSCCTRL);
+
+ val = readl(cfg->regs + S3C_CIPRSCCTRL);
+ val &= ~S3C_CIPRSCCTRL_START;
+ writel(val, cfg->regs + S3C_CIPRSCCTRL);
+
+ n_cmd = 0;
+ break;
+
+ case CAMIF_DMA_OFF_L_IRQ: /* fall thru */
+ case CAMIF_DMA_OFF:
+ if (cfg->dma_type & CAMIF_CODEC) {
+ val = readl(cfg->regs + S3C_CICOSCCTRL);
+ val &= ~S3C_CICOSCCTRL_COSCALERSTART;
+ writel(val, cfg->regs + S3C_CICOSCCTRL);
+ n_cmd &= ~S3C_CIIMGCPT_IMGCPTEN_COSC;
+
+ if (!(n_cmd & S3C_CIIMGCPT_IMGCPTEN_PRSC))
+ n_cmd = 0;
+ } else {
+ val = readl(cfg->regs + S3C_CIPRSCCTRL);
+ val &= ~S3C_CIPRSCCTRL_START;
+ writel(val, cfg->regs + S3C_CIPRSCCTRL);
+
+ n_cmd &= ~S3C_CIIMGCPT_IMGCPTEN_PRSC;
+
+ if (!(n_cmd & S3C_CIIMGCPT_IMGCPTEN_COSC))
+ n_cmd = 0;
+ }
+
+ break;
+
+ default:
+ printk(KERN_ERR "Unexpected DMA control\n");
+ }
+
+ writel(n_cmd, cfg->regs + S3C_CIIMGCPT);
+
+ if (cfg->capture_enable == CAMIF_DMA_OFF_L_IRQ) { /* Last IRQ */
+ if (cfg->dma_type & CAMIF_CODEC) {
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val |= S3C_CICOCTRL_LASTIRQEN;
+ writel(val, cfg->regs + S3C_CICOCTRL);
+ } else {
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val |= S3C_CIPRCTRL_LASTIRQEN_ENABLE;
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ }
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+int s3c_camif_start_codec_msdma(camif_cfg_t *cfg)
+{
+ int ret = 0;
+ u32 val;
+
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val &= ~(1 << 0);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ val = readl(cfg->regs + S3C_MSCOCTRL);
+ val |= (1 << 0);
+ writel(val, cfg->regs + S3C_MSCOCTRL);
+
+ return ret;
+}
+#endif
+
+int s3c_camif_start_preview_msdma(camif_cfg_t * cfg)
+{
+ unsigned int val;
+ int ret = 0;
+
+#if !defined(CONFIG_CPU_S3C6400) && !defined(CONFIG_CPU_S3C6410)
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val &= ~(1 << 0);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+#endif
+ val = readl(cfg->regs + S3C_CIMSCTRL);
+ val |= (1 << 0);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ while((readl(cfg->regs + S3C_CIMSCTRL) & (1 << 6)) == 0);
+
+ return ret;
+}
+
+void s3c_camif_change_flip(camif_cfg_t *cfg)
+{
+ unsigned int cmd = 0;
+
+ if (cfg->dma_type & CAMIF_CODEC) {
+ cmd = readl(cfg->regs + S3C_CICOTRGFMT);
+ cmd &= ~((1 << 14) | (1 << 15));
+ cmd |= cfg->flip;
+ writel(cmd, cfg->regs + S3C_CICOTRGFMT);
+ } else {
+ /* if ROT90_Pr == 1, dma burst length must be 4 */
+ if (cfg->flip == CAMIF_ROTATE_90 || cfg->flip == CAMIF_FLIP_ROTATE_270) {
+ cmd = readl(cfg->regs + S3C_CIPRCTRL);
+ cmd &= ~(0x3ff << 14);
+ cmd |= (S3C_CICOCTRL_YBURST1_CO(4) | S3C_CICOCTRL_YBURST2_CO(4));
+ writel(cmd, cfg->regs + S3C_CIPRCTRL);
+ }
+
+ cmd = readl(cfg->regs + S3C_CIPRTRGFMT);
+ cmd &= ~(0x7 << 13);
+ cmd |= cfg->flip;
+ writel(cmd, cfg->regs + S3C_CIPRTRGFMT);
+ }
+}
+
+void s3c_camif_change_effect(camif_cfg_t *cfg)
+{
+ unsigned int val = readl(cfg->regs + S3C_CIIMGEFF);
+ val &= ~((1 << 28) | (1 << 27) | (1 << 26));
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ val |= ((1 << 31) | (1 << 30));
+#endif
+
+ switch(cfg->effect) {
+ case CAMIF_SILHOUETTE:
+ val |= S3C_CIIMGEFF_FIN_SILHOUETTE;
+ break;
+
+ case CAMIF_EMBOSSING:
+ val |= S3C_CIIMGEFF_FIN_EMBOSSING;
+ break;
+
+ case CAMIF_ART_FREEZE:
+ val |= S3C_CIIMGEFF_FIN_ARTFREEZE;
+ break;
+
+ case CAMIF_NEGATIVE:
+ val |= S3C_CIIMGEFF_FIN_NEGATIVE;
+ break;
+
+ case CAMIF_ARBITRARY_CB_CR:
+ val |= S3C_CIIMGEFF_FIN_ARBITRARY;
+ break;
+
+ case CAMIF_BYPASS:
+ default:
+ break;
+ }
+
+ writel(val, cfg->regs + S3C_CIIMGEFF);
+}
+
+int s3c_camif_do_postprocess(camif_cfg_t *cfg)
+{
+ unsigned int val= readl(cfg->regs + S3C_CIMSCTRL);
+
+ if (cfg->dst_fmt & CAMIF_YCBCR420)
+ val |= (1 << 1);
+ else
+ val &= ~(1 << 1);
+
+ val &= ~(1 << 0);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ val |= (1 << 0);
+ writel(val, cfg->regs + S3C_CIMSCTRL);
+
+ printk(KERN_INFO "Postprocessing started\n");
+
+ while((readl(cfg->regs + S3C_CIMSCTRL) & (1 << 6)) == 0);
+
+ printk(KERN_INFO "Postprocessing finished\n");
+
+ return 0;
+}
+
+int s3c_camif_set_offset(camif_cis_t *cis)
+{
+ camif_cfg_t *cfg = s3c_camif_get_fimc_object(CODEC_MINOR);
+ unsigned int h = cis->win_hor_ofst; /* Camera input offset ONLY */
+ unsigned int v = cis->win_ver_ofst; /* Camera input offset ONLY */
+ unsigned int h2 = cis->win_hor_ofst2; /* Camera input offset ONLY */
+ unsigned int v2 = cis->win_ver_ofst2; /* Camera input offset ONLY */
+
+ /*Clear Overflow */
+ writel(S3C_CIWDOFST_CLROVCOFIY | S3C_CIWDOFST_CLROVCOFICB | \
+ S3C_CIWDOFST_CLROVCOFICR | S3C_CIWDOFST_CLROVPRFICB | \
+ S3C_CIWDOFST_CLROVPRFICR, cfg->regs + S3C_CIWDOFST);
+
+ writel(0, cfg->regs + S3C_CIWDOFST);
+
+ if (!h && !v) {
+ writel(0, cfg->regs + S3C_CIWDOFST);
+ writel(0, cfg->regs + S3C_CIDOWSFT2);
+ return 0;
+ }
+
+ writel(S3C_CIWDOFST_WINOFSEN | S3C_CIWDOFST_WINHOROFST(h) | S3C_CIWDOFST_WINVEROFST(v), cfg->regs + S3C_CIWDOFST);
+ writel(S3C_CIDOWSFT2_WINHOROFST2(h) | S3C_CIDOWSFT2_WINVEROFST2(v), cfg->regs + S3C_CIDOWSFT2);
+ writel(S3C_CIDOWSFT2_WINHOROFST2(h2) | S3C_CIDOWSFT2_WINVEROFST2(v2), cfg->regs + S3C_CIDOWSFT2);
+
+ return 0;
+}
+
+void s3c_camif_set_priority(int flag)
+{
+ unsigned int val;
+
+ if (flag) {
+ irq_old_priority = readl(S3C_PRIORITY);
+ val = irq_old_priority;
+ val &= ~(3 << 7);
+ writel(val, S3C_PRIORITY);
+
+ /* Arbiter 1, REQ2 first */
+ val |= (1 << 7);
+ writel(val, S3C_PRIORITY);
+
+ /* Disable Priority Rotate */
+ val &= ~(1 << 1);
+ writel(val, S3C_PRIORITY);
+ } else
+ writel(irq_old_priority, S3C_PRIORITY);
+}
+
+/*************************************************************************
+ * Interrupt part
+ ************************************************************************/
+void s3c_camif_enable_lastirq(camif_cfg_t *cfg)
+{
+ unsigned int val;
+
+ if (cfg->capture_enable == CAMIF_BOTH_DMA_ON) {
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val |= S3C_CICOCTRL_LASTIRQEN;
+ writel(val, cfg->regs + S3C_CICOCTRL);
+
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val |= S3C_CIPRCTRL_LASTIRQEN_ENABLE;
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ } else {
+ if (cfg->dma_type & CAMIF_CODEC) {
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val |= S3C_CICOCTRL_LASTIRQEN;
+ writel(val, cfg->regs + S3C_CICOCTRL);
+ } else {
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val |= S3C_CIPRCTRL_LASTIRQEN_ENABLE;
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ }
+ }
+}
+
+void s3c_camif_disable_lastirq(camif_cfg_t *cfg)
+{
+ unsigned int val;
+
+ if (cfg->capture_enable == CAMIF_BOTH_DMA_ON) {
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val &= ~S3C_CICOCTRL_LASTIRQEN;
+ writel(val, cfg->regs + S3C_CICOCTRL);
+
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val &= ~S3C_CIPRCTRL_LASTIRQEN_ENABLE;
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ } else {
+ if (cfg->dma_type & CAMIF_CODEC) {
+ val = readl(cfg->regs + S3C_CICOCTRL);
+ val &= ~S3C_CICOCTRL_LASTIRQEN;
+ writel(val, cfg->regs + S3C_CICOCTRL);
+ } else {
+ val = readl(cfg->regs + S3C_CIPRCTRL);
+ val &= ~S3C_CIPRCTRL_LASTIRQEN_ENABLE;
+ writel(val, cfg->regs + S3C_CIPRCTRL);
+ }
+ }
+}
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+void s3c_camif_clear_irq(int irq)
+{
+ camif_cfg_t *cfg = s3c_camif_get_fimc_object(CODEC_MINOR);
+ unsigned int val = 0;
+
+ if (irq == IRQ_CAMIF_C) {
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= (1 << 19);
+ } else if (irq == IRQ_CAMIF_P) {
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= (1 << 18);
+ }
+
+ writel(val, cfg->regs + S3C_CIGCTRL);
+}
+#else
+void s3c_camif_clear_irq(int irq)
+{
+}
+#endif
+
+/*************************************************************************
+ * Initialize part
+ ************************************************************************/
+#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+static int s3c_camif_set_gpio(void)
+{
+ s3c2410_gpio_cfgpin(S3C2440_GPJ0, S3C2440_GPJ0_CAMDATA0);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ1, S3C2440_GPJ1_CAMDATA1);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ2, S3C2440_GPJ2_CAMDATA2);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ3, S3C2440_GPJ3_CAMDATA3);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ4, S3C2440_GPJ4_CAMDATA4);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ5, S3C2440_GPJ5_CAMDATA5);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ6, S3C2440_GPJ6_CAMDATA6);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ7, S3C2440_GPJ7_CAMDATA7);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ8, S3C2440_GPJ8_CAMPCLK);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ9, S3C2440_GPJ9_CAMVSYNC);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ10, S3C2440_GPJ10_CAMHREF);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ11, S3C2440_GPJ11_CAMCLKOUT);
+ s3c2410_gpio_cfgpin(S3C2440_GPJ12, S3C2440_GPJ12_CAMRESET);
+
+ writel(0x1fff, S3C2443_GPJDN);
+
+ return 0;
+}
+#elif defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+static int s3c_camif_set_gpio(void)
+{
+ s3c_gpio_cfgpin(S3C_GPF5, S3C_GPF5_CAMIF_YDATA0);
+ s3c_gpio_cfgpin(S3C_GPF6, S3C_GPF6_CAMIF_YDATA1);
+ s3c_gpio_cfgpin(S3C_GPF7, S3C_GPF7_CAMIF_YDATA2);
+ s3c_gpio_cfgpin(S3C_GPF8, S3C_GPF8_CAMIF_YDATA03);
+ s3c_gpio_cfgpin(S3C_GPF9, S3C_GPF9_CAMIF_YDATA4);
+ s3c_gpio_cfgpin(S3C_GPF10, S3C_GPF10_CAMIF_YDATA5);
+ s3c_gpio_cfgpin(S3C_GPF11, S3C_GPF11_CAMIF_YDATA06);
+ s3c_gpio_cfgpin(S3C_GPF12, S3C_GPF12_CAMIF_YDATA7);
+ s3c_gpio_cfgpin(S3C_GPF2, S3C_GPF2_CAMIF_CLK);
+ s3c_gpio_cfgpin(S3C_GPF4, S3C_GPF4_CAMIF_VSYNC);
+ s3c_gpio_cfgpin(S3C_GPF1, S3C_GPF1_CAMIF_HREF);
+ s3c_gpio_cfgpin(S3C_GPF0, S3C_GPF0_CAMIF_CLK);
+ s3c_gpio_cfgpin(S3C_GPF3, S3C_GPF3_CAMIF_RST);
+
+ writel(0, S3C_GPFPU);
+
+ return 0;
+}
+#endif
+
+void s3c_camif_reset(int is, int delay)
+{
+ camif_cfg_t *cfg = s3c_camif_get_fimc_object(CODEC_MINOR);
+ unsigned int val;
+ unsigned int tmp;
+
+ switch (is) {
+ case CAMIF_RESET:
+ tmp = readl(cfg->regs + S3C_CISRCFMT);
+
+ if (tmp &= (1 << 31)) {
+ /* ITU-R BT 601 */
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= S3C_CIGCTRL_SWRST;
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ val |= S3C_CIGCTRL_IRQ_LEVEL;
+#endif
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ mdelay(1);
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val &= ~S3C_CIGCTRL_SWRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ } else {
+ /* ITU-R BT 656 */
+ tmp = readl(cfg->regs + S3C_CISRCFMT);
+ tmp |= (1 << 31);
+ writel(tmp, cfg->regs + S3C_CISRCFMT);
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= S3C_CIGCTRL_SWRST;
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+ val |= S3C_CIGCTRL_IRQ_LEVEL;
+#endif
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ mdelay(1);
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val &= ~S3C_CIGCTRL_SWRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+
+ tmp = readl(cfg->regs + S3C_CISRCFMT);
+ tmp &= ~(1 << 31);
+ writel(tmp, cfg->regs + S3C_CISRCFMT);
+ }
+
+ break;
+
+ case CAMIF_EX_RESET_AH:
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= S3C_CIGCTRL_CAMRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ udelay(200);
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val &= ~S3C_CIGCTRL_CAMRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ udelay(delay);
+
+#if defined(CONFIG_VIDEO_SAMSUNG_S5K3AA)
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= S3C_CIGCTRL_CAMRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ udelay(200);
+#endif
+ break;
+
+ case CAMIF_EX_RESET_AL:
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val &= ~S3C_CIGCTRL_CAMRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ udelay(200);
+
+ val = readl(cfg->regs + S3C_CIGCTRL);
+ val |= S3C_CIGCTRL_CAMRST;
+ writel(val, cfg->regs + S3C_CIGCTRL);
+ udelay(delay);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void s3c_camif_init(void)
+{
+ s3c_camif_reset(CAMIF_RESET, 0);
+ s3c_camif_set_gpio();
+}
+
diff --git a/drivers/media/video/s3c_camif.h b/drivers/media/video/s3c_camif.h
new file mode 100644
index 00000000000..961efa7ff84
--- /dev/null
+++ b/drivers/media/video/s3c_camif.h
@@ -0,0 +1,404 @@
+/* drivers/media/video/s3c_camif.h
+ *
+ * Copyright (c) 2008 Samsung Electronics
+ *
+ * Samsung S3C Camera driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __S3C_CAMIF_H_
+#define __S3C_CAMIF_H_
+
+#ifdef __KERNEL__
+#include <linux/videodev.h>
+#include <linux/videodev2.h>
+#include <asm/types.h>
+#include <linux/i2c.h>
+#include <linux/video_decoder.h>
+#endif /* __KERNEL__ */
+
+#if !defined(O_NONCAP)
+#define O_NONCAP O_TRUNC
+#endif
+
+#if defined(CAMIF_DEBUG)
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#if defined(CAMIF_DEBUG)
+#define assert(expr) \
+ if(!(expr)) { \
+ printk( "Assertion failed! %s,%s,%s,line=%d\n", \
+ #expr,__FILE__,__FUNCTION__,__LINE__); \
+ }
+#else
+#define assert(expr)
+#endif
+
+#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
+#define MEM_SIZE 0x08000000
+#define FIMC_VER "3.0"
+#elif defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
+#define MEM_SIZE 0x04000000
+#define FIMC_VER "2.3"
+#else
+#define MEM_SIZE 0x04000000
+#define FIMC_VER "2.x"
+#endif
+
+#undef FSM_ON_PREVIEW
+#define FSM_ON_CODEC
+
+#undef USE_LAST_IRQ /* turn on if pp count is 1 */
+
+#define CODEC_DEV_NAME "CAMIF_CODEC"
+#define PREVIEW_DEV_NAME "CAMIF_PREVIEW"
+
+#define CAMIF_DEV_NUM 2
+#define FIMC_CODEC_INDEX 0
+#define FIMC_PREVIEW_INDEX 1
+
+#define BURST_ERR 1
+#define RESERVED_MEM (15 * 1024 * 1024)
+#define YUV_MEM (10 * 1024 * 1024)
+#define RGB_MEM (RESERVED_MEM - YUV_MEM)
+
+#define CODEC_DEFAULT_WIDTH 640
+#define CODEC_DEFAULT_HEIGHT 480
+#define PREVIEW_DEFAULT_WIDTH 640
+#define PREVIEW_DEFAULT_HEIGHT 480
+
+#define CROP_DEFAULT_WIDTH 352
+#define CROP_DEFAULT_HEIGHT 272
+
+#define CODEC_DEFAULT_PPNUM 4
+#define PREVIEW_DEFAULT_PPNUM 4
+
+#define CODEC_MINOR 12
+#define PREVIEW_MINOR 13
+
+#define CHECK_FREQ 5
+#define INSTANT_SKIP 0
+#define INSTANT_GO 1
+
+#define VID_HARDWARE_SAMSUNG_FIMC3X 236
+
+#define ZOOM_AT_A_TIME_IN_PIXELS 32
+#define ZOOM_IN_MAX 640
+
+/* Codec or Preview Status */
+#define CAMIF_STARTED (1 << 1)
+#define CAMIF_STOPPED (1 << 2)
+#define CAMIF_INT_HAPPEN (1 << 3)
+
+/* Codec or Preview : Interrupt FSM */
+#define CAMIF_1st_INT (1 << 7)
+#define CAMIF_Xth_INT (1 << 8)
+#define CAMIF_Yth_INT (1 << 9)
+#define CAMIF_Zth_INT (1 << 10)
+#define CAMIF_NORMAL_INT (1 << 11)
+#define CAMIF_DUMMY_INT (1 << 12)
+#define CAMIF_CONTINUOUS_INT (1 << 13)
+#define CAMIF_SET_LAST_INT (1 << 14)
+#define CAMIF_STOP_CAPTURE (1 << 15)
+#define CAMIF_LAST_IRQ (1 << 16)
+#define CAMIF_PENDING_INT 0
+
+#define CAMIF_CAPTURE_SKIP_FRAMES 5
+
+/* CAMIF RESET Definition */
+#define CAMIF_RESET (1 << 0)
+#define CAMIF_EX_RESET_AL (1 << 1) /* Active Low */
+#define CAMIF_EX_RESET_AH (1 << 2) /* Active High */
+
+#define USER_EXIT (1 << 2)
+#define USER_ADD (1 << 1)
+#define SENSOR_INIT (1 << 0)
+
+#define SENSOR_MAX 255
+#define SENSOR_QSVGA (1 << 12)
+#define SENSOR_UXGA (1 << 11)
+#define SENSOR_SVGA (1 << 10)
+#define SENSOR_SXGA (1 << 4)
+#define SENSOR_VGA (1 << 3)
+#define SENSOR_DEFAULT 0
+
+#define SENSOR_WB (1 << 9)
+#define SENSOR_AF (1 << 8)
+#define SENSOR_MIRROR (1 << 7)
+#define SENSOR_ZOOMOUT (1 << 6)
+#define SENSOR_ZOOMIN (1 << 5)
+
+/* Global Status Definition */
+#define PWANT2START (1 << 0)
+#define CWANT2START (1 << 1)
+#define BOTH_STARTED (PWANT2START | CWANT2START)
+#define P_NOT_WORKING (1 << 4)
+#define C_WORKING (1 << 5)
+#define P_WORKING (1 << 6)
+#define C_NOT_WORKING (1 << 7)
+
+#define FORMAT_FLAGS_DITHER 0x01
+#define FORMAT_FLAGS_PACKED 0x02
+#define FORMAT_FLAGS_PLANAR 0x04
+#define FORMAT_FLAGS_RAW 0x08
+#define FORMAT_FLAGS_CrCb 0x10
+
+enum camif_itu_fmt {
+ CAMIF_ITU601 = (1 << 31),
+ CAMIF_ITU656 = 0,
+};
+
+/* It is possbie to use two device simultaneously */
+enum camif_dma_type {
+ CAMIF_PREVIEW = (1 << 0),
+ CAMIF_CODEC = (1 << 1),
+};
+
+enum camif_order422 {
+ CAMIF_YCBYCR = 0,
+ CAMIF_YCRYCB = (1 << 14),
+ CAMIF_CBYCRY = (1 << 15),
+ CAMIF_CRYCBY = (1 << 15) | (1 << 14),
+};
+
+enum flip_mode {
+ CAMIF_FLIP = 0,
+ CAMIF_ROTATE_90 = (1 << 13),
+ CAMIF_FLIP_X = (1 << 14),
+ CAMIF_FLIP_Y = (1 << 15),
+ CAMIF_FLIP_MIRROR = (1 << 15) | (1 << 14),
+ CAMIF_FLIP_ROTATE_270 = (1 << 15) | (1 << 14) | (1 << 13),
+};
+
+enum camif_fmt {
+ CAMIF_YCBCR420 = (1 << 0),
+ CAMIF_YCBCR422 = (1 << 1),
+ CAMIF_YCBCR422I = (1 << 2),
+ CAMIF_RGB16 = (1 << 3),
+ CAMIF_RGB24 = (1 << 4),
+ CAMIF_RGB32 = (1 << 5),
+};
+
+enum camif_capturing {
+ CAMIF_BOTH_DMA_ON = (1 << 4),
+ CAMIF_DMA_ON = (1 << 3),
+ CAMIF_BOTH_DMA_OFF = (1 << 1),
+ CAMIF_DMA_OFF = (1 << 0),
+ CAMIF_DMA_OFF_L_IRQ = (1 << 5),
+};
+
+enum image_effect {
+ CAMIF_BYPASS,
+ CAMIF_ARBITRARY_CB_CR,
+ CAMIF_NEGATIVE,
+ CAMIF_ART_FREEZE,
+ CAMIF_EMBOSSING ,
+ CAMIF_SILHOUETTE,
+};
+
+enum input_channel{
+ CAMERA_INPUT,
+ MSDMA_FROM_CODEC,
+ MSDMA_FROM_PREVIEW,
+};
+
+enum output_channel{
+ CAMIF_OUT_PP,
+ CAMIF_OUT_FIFO,
+};
+
+typedef struct camif_performance
+{
+ int frames;
+ int framesdropped;
+ __u64 bytesin;
+ __u64 bytesout;
+ __u32 reserved[4];
+} camif_perf_t;
+
+typedef struct {
+ dma_addr_t phys_y;
+ dma_addr_t phys_cb;
+ dma_addr_t phys_cr;
+ u8 *virt_y;
+ u8 *virt_cb;
+ u8 *virt_cr;
+ dma_addr_t phys_rgb;
+ u8 *virt_rgb;
+} img_buf_t;
+
+/* this structure convers the CIWDOFFST, prescaler, mainscaler */
+typedef struct {
+ u32 modified_src_x; /* After windows applyed to source_x */
+ u32 modified_src_y;
+ u32 hfactor;
+ u32 vfactor;
+ u32 shfactor; /* SHfactor = 10 - ( hfactor + vfactor ) */
+ u32 prehratio;
+ u32 prevratio;
+ u32 predst_x;
+ u32 predst_y;
+ u32 scaleup_h;
+ u32 scaleup_v;
+ u32 mainhratio;
+ u32 mainvratio;
+ u32 scalerbypass; /* only codec */
+ u32 zoom_in_cnt;
+} scaler_t;
+
+enum v4l2_status {
+ CAMIF_V4L2_INIT = (1 << 0),
+ CAMIF_v4L2_DIRTY = (1 << 1),
+};
+
+typedef struct {
+ struct mutex lock;
+ enum camif_itu_fmt itu_fmt;
+ enum camif_order422 order422;
+ struct i2c_client *sensor;
+ u32 win_hor_ofst;
+ u32 win_ver_ofst;
+ u32 win_hor_ofst2;
+ u32 win_ver_ofst2;
+ u32 camclk; /* External Image Sensor Camera Clock */
+ u32 source_x;
+ u32 source_y;
+ u32 polarity_pclk;
+ u32 polarity_vsync;
+ u32 polarity_href;
+ u32 user; /* MAX 2 (codec, preview) */
+ u32 irq_old_priority; /* BUS PRIORITY register */
+ u32 status;
+ u32 init_sensor; /* initializing sensor */
+ u32 reset_type; /* External Sensor Reset Type */
+ u32 reset_udelay;
+ u32 zoom_in_cnt;
+} camif_cis_t;
+
+/* when App want to change v4l2 parameter,
+ * we instantly store it into v4l2_t v2
+ * and then reflect it to hardware
+ */
+typedef struct v4l2 {
+ struct v4l2_fmtdesc *fmtdesc;
+ struct v4l2_framebuffer frmbuf; /* current frame buffer */
+ struct v4l2_input *input;
+ struct v4l2_output *output;
+ enum v4l2_status status;
+
+ /* crop */
+ struct v4l2_rect crop_bounds;
+ struct v4l2_rect crop_defrect;
+ struct v4l2_rect crop_current;
+
+} v4l2_t;
+
+
+typedef struct camif_c_t {
+ struct video_device *v;
+
+ /* V4L2 param only for v4l2 driver */
+ v4l2_t v2;
+ camif_cis_t *cis; /* Common between Codec and Preview */
+
+ /* logical parameter */
+ wait_queue_head_t waitq;
+ u32 status; /* Start/Stop */
+ u32 fsm; /* Start/Stop */
+ u32 open_count; /* duplicated */
+ int irq;
+ char shortname[16];
+ u32 target_x;
+ u32 target_y;
+ scaler_t sc;
+ enum flip_mode flip;
+ enum image_effect effect;
+ enum camif_dma_type dma_type;
+
+ /* 4 pingpong Frame memory */
+ u8 *pp_virt_buf;
+ dma_addr_t pp_phys_buf;
+ u32 pp_totalsize;
+ u32 pp_num; /* used pingpong memory number */
+ img_buf_t img_buf[4];
+ enum camif_fmt src_fmt;
+ enum camif_fmt dst_fmt;
+ enum camif_capturing capture_enable;
+ camif_perf_t perf;
+ u32 cur_frame_num;
+ u32 auto_restart; /* Only For Preview */
+ int input_channel;
+ int output_channel;
+ int buffer_size;
+ void *other; /* other camif_cfg_t */
+ u32 msdma_status; /* 0 : stop, 1 : start */
+ void __iomem *regs;
+} camif_cfg_t;
+
+/* Test Application Usage */
+typedef struct {
+ int src_x;
+ int src_y;
+ int dst_x;
+ int dst_y;
+ int src_fmt;
+ int dst_fmt;
+ int flip;
+ int awb;
+ int effect;
+ int input_channel;
+ int output_channel;
+ unsigned int h_offset;
+ unsigned int v_offset;
+ unsigned int h_offset2;
+ unsigned int v_offset2;
+} camif_param_t;
+
+/* Externs */
+extern camif_cfg_t* s3c_camif_get_fimc_object(int);
+extern int s3c_camif_start_dma(camif_cfg_t *);
+extern int s3c_camif_stop_dma(camif_cfg_t *);
+extern int s3c_camif_get_frame_num(camif_cfg_t *);
+extern unsigned char* s3c_camif_get_frame(camif_cfg_t *);
+extern int s3c_camif_control_fimc(camif_cfg_t *);
+extern void s3c_camif_reset(int, int);
+extern void s3c_camif_init(void);
+extern int s3c_camif_get_fifo_status(camif_cfg_t *);
+extern void s3c_camif_enable_lastirq(camif_cfg_t *);
+extern void s3c_camif_disable_lastirq(camif_cfg_t *);
+extern void s3c_camif_change_flip(camif_cfg_t *);
+extern void s3c_camif_change_effect(camif_cfg_t *);
+extern int s3c_camif_start_codec_msdma(camif_cfg_t *);
+extern int s3c_camif_set_clock(unsigned int camclk);
+extern void s3c_camif_disable_clock(void);
+extern int s3c_camif_start_preview_msdma(camif_cfg_t *);
+extern camif_cis_t* get_initialized_cis(void);
+extern void s3c_camif_clear_irq(int);
+extern int s3c_camif_set_source_format(camif_cis_t *);
+extern void s3c_camif_register_sensor(struct i2c_client *);
+extern void s3c_camif_unregister_sensor(struct i2c_client*);
+extern int s3c_camif_setup_dma(camif_cfg_t *);
+extern void s3c_camif_init_sensor(camif_cfg_t *);
+extern int s3c_camif_set_offset(camif_cis_t *);
+extern void s3c_camif_set_priority(int);
+extern void s3c_camif_open_sensor(camif_cis_t *);
+extern void s3c_camif_set_polarity(camif_cfg_t *cfg);
+
+#endif
+
diff --git a/drivers/media/video/videodev2_s3c.h b/drivers/media/video/videodev2_s3c.h
new file mode 100644
index 00000000000..efbcd71830a
--- /dev/null
+++ b/drivers/media/video/videodev2_s3c.h
@@ -0,0 +1,210 @@
+#ifndef __VIDEODEV2_S3C_H_
+#define __VIDEODEV2_S3C_H_
+
+#include <linux/videodev2.h>
+
+#define V4L2_INPUT_TYPE_MSDMA 3
+#define V4L2_INPUT_TYPE_INTERLACE 4
+
+/****************************************************************
+* struct v4l2_control
+* Control IDs defined by S3C
+*****************************************************************/
+/* Image Effect */
+#define V4L2_CID_ORIGINAL (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_ARBITRARY (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_NEGATIVE (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_ART_FREEZE (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_EMBOSSING (V4L2_CID_PRIVATE_BASE + 4)
+#define V4L2_CID_SILHOUETTE (V4L2_CID_PRIVATE_BASE + 5)
+
+/* Image Rotate */
+#define V4L2_CID_ROTATE_90 (V4L2_CID_PRIVATE_BASE + 6)
+#define V4L2_CID_ROTATE_180 (V4L2_CID_PRIVATE_BASE + 7)
+#define V4L2_CID_ROTATE_270 (V4L2_CID_PRIVATE_BASE + 8)
+#define V4L2_CID_ROTATE_BYPASS (V4L2_CID_PRIVATE_BASE + 9)
+
+/* Zoom-in, Zoom-out */
+#define V4L2_CID_ZOOMIN (V4L2_CID_PRIVATE_BASE + 10)
+#define V4L2_CID_ZOOMOUT (V4L2_CID_PRIVATE_BASE + 11)
+
+/****************************************************************
+* I O C T L C O D E S F O R V I D E O D E V I C E S
+* It's only for S3C
+*****************************************************************/
+#define VIDIOC_S_CAMERA_START _IO ('V', BASE_VIDIOC_PRIVATE + 0)
+#define VIDIOC_S_CAMERA_STOP _IO ('V', BASE_VIDIOC_PRIVATE + 1)
+#define VIDIOC_MSDMA_START _IOW ('V', BASE_VIDIOC_PRIVATE + 2, struct v4l2_msdma_format)
+#define VIDIOC_MSDMA_STOP _IOW ('V', BASE_VIDIOC_PRIVATE + 3, struct v4l2_msdma_format)
+#define VIDIOC_S_MSDMA _IOW ('V', BASE_VIDIOC_PRIVATE + 4, struct v4l2_msdma_format)
+#define VIDIOC_S_INTERLACE_MODE _IOW ('V', BASE_VIDIOC_PRIVATE + 5, struct v4l2_interlace_format)
+
+/*
+ * INTERLACE MODE
+ */
+#define S3C_VIDEO_DECODER_PAL 1 /* can decode PAL signal */
+#define S3C_VIDEO_DECODER_NTSC 2 /* can decode NTSC */
+#define S3C_VIDEO_DECODER_SECAM 4 /* can decode SECAM */
+#define S3C_VIDEO_DECODER_AUTO 8 /* can autosense norm */
+#define S3C_VIDEO_DECODER_CCIR 16 /* CCIR-601 pixel rate (720 pixels per line) instead of square pixel rate */
+
+#define S3C_DECODER_INIT _IOW ('V', BASE_VIDIOC_PRIVATE + 14, struct s3c_video_decoder_init) /* init internal registers at once */
+#define S3C_DECODER_GET_CAPABILITIES _IOR ('V', BASE_VIDIOC_PRIVATE + 6, struct s3c_video_decoder_capability)
+#define S3C_DECODER_GET_STATUS _IOR ('V', BASE_VIDIOC_PRIVATE + 7, int)
+#define S3C_DECODER_SET_NORM _IOW ('V', BASE_VIDIOC_PRIVATE + 8, int)
+#define S3C_DECODER_SET_INPUT _IOW ('V', BASE_VIDIOC_PRIVATE + 9, int) /* 0 <= input < #inputs */
+#define S3C_DECODER_SET_OUTPUT _IOW ('V', BASE_VIDIOC_PRIVATE + 10, int) /* 0 <= output < #outputs */
+#define S3C_DECODER_ENABLE_OUTPUT _IOW ('V', BASE_VIDIOC_PRIVATE + 11, int) /* boolean output enable control */
+#define S3C_DECODER_SET_PICTURE _IOW ('V', BASE_VIDIOC_PRIVATE + 12, struct video_picture)
+#define S3C_DECODER_SET_GPIO _IOW ('V', BASE_VIDIOC_PRIVATE + 13, int) /* switch general purpose pin */
+#define S3C_DECODER_SET_VBI_BYPASS _IOW ('V', BASE_VIDIOC_PRIVATE + 15, int) /* switch vbi bypass */
+#define S3C_DECODER_DUMP _IO ('V', BASE_VIDIOC_PRIVATE + 16) /* debug hook */
+
+enum v4l2_msdma_input {
+ V4L2_MSDMA_CODEC = 1,
+ V4L2_MSDMA_PREVIEW = 2,
+};
+
+struct v4l2_msdma_format
+{
+ __u32 width; /* MSDMA INPUT : Source X size */
+ __u32 height; /* MSDMA INPUT : Source Y size */
+ __u32 pixelformat;
+ enum v4l2_msdma_input input_path;
+};
+
+struct v4l2_interlace_format
+{
+ __u32 width; /* INTERLACE INPUT : Source X size */
+ __u32 height; /* INTERLACE INPUT : Source Y size */
+};
+
+struct s3c_video_decoder_init {
+ unsigned char len;
+ const unsigned char *data;
+};
+
+struct s3c_video_decoder_capability { /* this name is too long */
+ __u32 flags;
+ int inputs; /* number of inputs */
+ int outputs; /* number of outputs */
+};
+
+static struct v4l2_input fimc_inputs[] = {
+ {
+ .index = 0,
+ .name = "S3C FIMC External Camera Input",
+ .type = V4L2_INPUT_TYPE_CAMERA,
+ .audioset = 1,
+ .tuner = 0,
+ .std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M,
+ .status = 0,
+ },
+ {
+ .index = 1,
+ .name = "Memory Input (MSDMA)",
+ .type = V4L2_INPUT_TYPE_MSDMA,
+ .audioset = 2,
+ .tuner = 0,
+ .std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M,
+ .status = 0,
+ }
+};
+
+static struct v4l2_output fimc_outputs[] = {
+ {
+ .index = 0,
+ .name = "Pingpong Memory Output",
+ .type = 0,
+ .audioset = 0,
+ .modulator = 0,
+ .std = 0,
+ },
+ {
+ .index = 1,
+ .name = "LCD FIFO Output",
+ .type = 0,
+ .audioset = 0,
+ .modulator = 0,
+ .std = 0,
+ }
+};
+
+const struct v4l2_fmtdesc fimc_codec_formats[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PACKED,
+ .description = "16 bpp RGB, le",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ },
+ {
+ .index = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PACKED,
+ .description = "32 bpp RGB, le",
+ .pixelformat = V4L2_PIX_FMT_BGR32,
+ },
+ {
+ .index = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .description = "4:2:2, planar, Y-Cb-Cr",
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+
+ },
+ {
+ .index = 3,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .description = "4:2:0, planar, Y-Cb-Cr",
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ }
+};
+
+const struct v4l2_fmtdesc fimc_preview_formats[] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_OVERLAY,
+ .flags = FORMAT_FLAGS_PACKED,
+ .description = "16 bpp RGB, le",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ },
+ {
+ .index = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OVERLAY,
+ .flags = FORMAT_FLAGS_PACKED,
+ .description = "24 bpp RGB, le",
+ .pixelformat = V4L2_PIX_FMT_RGB24,
+ },
+ {
+ .index = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_OVERLAY,
+ .flags = FORMAT_FLAGS_PACKED,
+ .description = "32 bpp RGB, le",
+ .pixelformat = V4L2_PIX_FMT_BGR32,
+ },
+ {
+ .index = 3,
+ .type = V4L2_BUF_TYPE_VIDEO_OVERLAY,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .description = "4:2:2, planar, Y-Cb-Cr",
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+
+ },
+ {
+ .index = 4,
+ .type = V4L2_BUF_TYPE_VIDEO_OVERLAY,
+ .flags = FORMAT_FLAGS_PLANAR,
+ .description = "4:2:0, planar, Y-Cb-Cr",
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ }
+};
+
+#define NUMBER_OF_PREVIEW_FORMATS ARRAY_SIZE(fimc_preview_formats)
+#define NUMBER_OF_CODEC_FORMATS ARRAY_SIZE(fimc_codec_formats)
+#define NUMBER_OF_INPUTS ARRAY_SIZE(fimc_inputs)
+#define NUMBER_OF_OUTPUTS ARRAY_SIZE(fimc_outputs)
+
+#endif
+