diff options
author | Werner Almesberger <werner@openmoko.org> | 2009-03-05 14:39:41 +0000 |
---|---|---|
committer | Andy Green <agreen@octopus.localdomain> | 2009-03-05 14:39:41 +0000 |
commit | 8cc1cf3520d685d3d5b26b5d489e536c389c9058 (patch) | |
tree | 39e44a3f6da5b01099b66b797a5b2fb2a607831b /drivers | |
parent | 9e723603e46500e2c9fbf09ac823de210bf05d31 (diff) |
Add Samsung S3C camera driver for V4L
This is the original Samsung S3C camera driver code from the 2.6.21 BSP.
The changes that are needed to make this work in 2.6.29 are in the next
patch.
Signed-off-by: Werner Almesberger <werner@openmoko.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/s3c_camera_driver.c | 1871 | ||||
-rw-r--r-- | drivers/media/video/s3c_camif.c | 1863 | ||||
-rw-r--r-- | drivers/media/video/s3c_camif.h | 404 | ||||
-rw-r--r-- | drivers/media/video/videodev2_s3c.h | 210 |
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 + |