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 --- Index: cam/drivers/media/video/s3c_camera_driver.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ cam/drivers/media/video/s3c_camera_driver.c 2009-03-04 19:56:56.000000000 +0800 @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 "); +MODULE_DESCRIPTION("S3C Camera Driver for FIMC Interface"); +MODULE_LICENSE("GPL"); + Index: cam/drivers/media/video/s3c_camif.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ cam/drivers/media/video/s3c_camif.c 2009-03-04 19:58:58.000000000 +0800 @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416) +#include +#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(); +} + Index: cam/drivers/media/video/s3c_camif.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ cam/drivers/media/video/s3c_camif.h 2009-03-04 19:59:05.000000000 +0800 @@ -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 +#include +#include +#include +#include +#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 + Index: cam/drivers/media/video/videodev2_s3c.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ cam/drivers/media/video/videodev2_s3c.h 2009-03-04 20:00:02.000000000 +0800 @@ -0,0 +1,210 @@ +#ifndef __VIDEODEV2_S3C_H_ +#define __VIDEODEV2_S3C_H_ + +#include + +#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 + --