/* 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 #include "s3c_camif.h" #include "videodev2_s3c.h" #include /* @@@ hack - WA */ #include #include #include #include #include #include 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"); ret = &s3c_fimc[FIMC_PREVIEW_INDEX]; } return ret; } #if defined(FSM_ON_PREVIEW) static int s3c_camif_check_global_status(camif_cfg_t *cfg) { int ret = 0; if (down_interruptible(&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(&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 = 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--; } 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 = V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; 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 = 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 = 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; if (on) return s3c_camif_start_preview(cfg); else return s3c_camif_stop_preview(cfg); } 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 = 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)) break; 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)) break; 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) { return s3c_camif_start_capture(cfg); } static int s3c_camif_v4l2_streamoff(camif_cfg_t *cfg, void *arg) { cfg->cis->status &= ~C_WORKING; s3c_camif_stop_capture(cfg); return 0; } static int s3c_camif_v4l2_g_input(camif_cfg_t *cfg, void *arg) { unsigned int *index = 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 = 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; 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) { 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; return INSTANT_SKIP; 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; return INSTANT_GO; 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; return INSTANT_GO; default: printk(KERN_INFO "Unexpect INT: %d\n", cfg->fsm); return INSTANT_SKIP; } } #endif #if defined(FSM_ON_CODEC) && defined(USE_LAST_IRQ) int s3c_camif_do_fsm_codec_lastirq(camif_cfg_t *cfg) { 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; return INSTANT_SKIP; 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; return INSTANT_SKIP; 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; return INSTANT_SKIP; 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; return INSTANT_GO; default: printk(KERN_INFO "Unexpect INT: %d\n", cfg->fsm); return = INSTANT_SKIP; } } #endif #if defined(FSM_ON_PREVIEW) static int s3c_camif_do_lastirq_preview(camif_cfg_t *cfg) { cfg->perf.frames++; if (cfg->fsm == CAMIF_NORMAL_INT) if (cfg->perf.frames % CHECK_FREQ == 0) if (s3c_camif_check_global_status(cfg) > 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; return INSTANT_SKIP; case CAMIF_NORMAL_INT: DPRINTK(KERN_INFO "CAMIF_NORMAL_INT\n"); cfg->status = CAMIF_INT_HAPPEN; cfg->fsm = CAMIF_NORMAL_INT; return INSTANT_GO; 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; return INSTANT_GO; 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; return INSTANT_GO; case CAMIF_Zth_INT: DPRINTK(KERN_INFO "CAMIF_Zth_INT\n"); cfg->fsm = CAMIF_DUMMY_INT; cfg->status = CAMIF_INT_HAPPEN; s3c_camif_auto_restart(cfg); return INSTANT_GO; case CAMIF_DUMMY_INT: DPRINTK(KERN_INFO "CAMIF_DUMMY_INT\n"); cfg->status = CAMIF_STOPPED; return INSTANT_SKIP; default: printk(KERN_INFO "Unexpected INT %d\n", cfg->fsm); return INSTANT_SKIP; } } #endif static irqreturn_t s3c_camif_do_irq_codec(int irq, void *dev_id) { camif_cfg_t *cfg = (camif_cfg_t *) dev_id; /* @@@ SMKD ? - WA */ #if 0 && (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; if (cfg->dma_type & CAMIF_CODEC) { ret = request_irq(cfg->irq, s3c_camif_do_irq_codec, IRQF_SHARED, cfg->shortname, cfg); if (ret) 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) { ret = request_irq(cfg->irq, s3c_camif_do_irq_preview, IRQF_SHARED, cfg->shortname, cfg); if (ret) 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 ************************************************************************/ long s3c_camif_ioctl(struct file *file, unsigned int cmd, unsigned long _arg) { camif_cfg_t *cfg = file->private_data; void *arg = (void *) _arg; /* @@@ - WA */ switch (cmd) { case VIDIOC_QUERYCAP: return s3c_camif_v4l2_querycap(cfg, arg); case VIDIOC_G_FBUF: return s3c_camif_v4l2_g_fbuf(cfg, arg); case VIDIOC_S_FBUF: return s3c_camif_v4l2_s_fbuf(cfg, arg); case VIDIOC_G_FMT: return s3c_camif_v4l2_g_fmt(cfg, arg); case VIDIOC_S_FMT: return s3c_camif_v4l2_s_fmt(cfg, arg); case VIDIOC_ENUM_FMT: return s3c_camif_v4l2_enum_fmt(cfg, arg); case VIDIOC_OVERLAY: return s3c_camif_v4l2_overlay(cfg, arg); case VIDIOC_S_CTRL: return s3c_camif_v4l2_s_ctrl(cfg, arg); case VIDIOC_G_CTRL: return s3c_camif_v4l2_g_ctrl(cfg, arg); case VIDIOC_STREAMON: return s3c_camif_v4l2_streamon(cfg, arg); case VIDIOC_STREAMOFF: return s3c_camif_v4l2_streamoff(cfg, arg); case VIDIOC_G_INPUT: return s3c_camif_v4l2_g_input(cfg, arg); case VIDIOC_S_INPUT: return s3c_camif_v4l2_s_input(cfg, *((int *) arg)); case VIDIOC_G_OUTPUT: return s3c_camif_v4l2_g_output(cfg, arg); case VIDIOC_S_OUTPUT: return s3c_camif_v4l2_s_output(cfg, *((int *) arg)); case VIDIOC_ENUMINPUT: return s3c_camif_v4l2_enum_input(cfg, arg); case VIDIOC_ENUMOUTPUT: return s3c_camif_v4l2_enum_output(cfg, arg); case VIDIOC_REQBUFS: return s3c_camif_v4l2_reqbufs(cfg, arg); case VIDIOC_QUERYBUF: return s3c_camif_v4l2_querybuf(cfg, arg); case VIDIOC_QBUF: return s3c_camif_v4l2_qbuf(cfg, arg); case VIDIOC_DQBUF: return s3c_camif_v4l2_dqbuf(cfg, arg); case VIDIOC_S_MSDMA: return s3c_camif_v4l2_s_msdma(cfg, arg); case VIDIOC_MSDMA_START: return s3c_camif_v4l2_msdma_start(cfg, arg); case VIDIOC_MSDMA_STOP: return s3c_camif_v4l2_msdma_stop(cfg, arg); case VIDIOC_S_CAMERA_START: return s3c_camif_v4l2_camera_start(cfg, arg); case VIDIOC_S_CAMERA_STOP: return s3c_camif_v4l2_camera_stop(cfg, arg); case VIDIOC_CROPCAP: return s3c_camif_v4l2_cropcap(cfg, arg); case VIDIOC_G_CROP: return s3c_camif_v4l2_g_crop(cfg, arg); case VIDIOC_S_CROP: return s3c_camif_v4l2_s_crop(cfg, arg); case VIDIOC_S_PARM: return s3c_camif_v4l2_s_parm(cfg, arg); default: /* For v4l compatability */ return v4l_compat_translate_ioctl(file, cmd, arg, s3c_camif_ioctl); } } void om_3d7k_camera_on(void) { extern struct pcf50633 *om_3d7k_pcf; int i; gpio_direction_output(S3C64XX_GPF(3), 0); /* @@@ hack - WA */ pcf50633_reg_write(om_3d7k_pcf, 0x30, 0x21); for (i = 0; !(pcf50633_reg_read(om_3d7k_pcf, 0x42) & 0x02); i++) { if (i == 100) { printk(KERN_ERR "can't bring up LDO2\n"); break; } msleep(10); } pcf50633_reg_write(om_3d7k_pcf, 0x39, 0x13); pcf50633_reg_write(om_3d7k_pcf, 0x3a, 0x21); for (i = 0; !(pcf50633_reg_read(om_3d7k_pcf, 0x42) & 0x40); i++) { if (i == 100) { printk(KERN_ERR "can't bring up HCLDO\n"); break; } msleep(10); } msleep(100); /* > 0 ms */ if (cam_clock) clk_enable(cam_clock); msleep(1); /* > 100 cycles */ gpio_direction_output(S3C64XX_GPF(3), 1); msleep(25); /* > 1 Mcycles */ s3c_gpio_cfgpin(S3C64XX_GPF(3), S3C64XX_GPF3_CAMIF_nRST); msleep(25); /* just to be sure > 1 Mcycles */ __raw_writel(__raw_readl(S3C64XX_NORMAL_CFG) | S3C64XX_NORMALCFG_DOMAIN_I_ON, S3C64XX_NORMAL_CFG); } void om_3d7k_camera_off(void) { extern struct pcf50633 *om_3d7k_pcf; gpio_direction_output(S3C64XX_GPF(3), 0); msleep(1); /* > 20 cycles */ if (cam_clock) clk_disable(cam_clock); msleep(1); /* > 0 ms */ /* @@@ hack - WA */ pcf50633_reg_write(om_3d7k_pcf, 0x3a, 0x20); /* 2V8, ... */ pcf50633_reg_write(om_3d7k_pcf, 0x30, 0x20); /* ... then 1V5 */ #if 0 __raw_writel(__raw_readl(S3C64XX_NORMAL_CFG) & ~S3C64XX_NORMALCFG_DOMAIN_I_ON, S3C64XX_NORMAL_CFG); #endif } /* @@@ - WA */ #define s3c_camif_exclusive_open(inode, file) 0 #define s3c_camif_exclusive_release(inode, file) int s3c_camif_open(struct file *file) { int err; camif_cfg_t *cfg = s3c_camif_get_fimc_object(MINOR(file->f_dentry->d_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(&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(&cfg->cis->lock); } om_3d7k_camera_on(); err = s3c_camif_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 file *file) { camif_cfg_t *cfg = s3c_camif_get_fimc_object(MINOR(file->f_dentry->d_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(&cfg->cis->lock); } else { cfg->cis->status &= ~CWANT2START; s3c_camif_stop_capture(cfg); } s3c_camif_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; om_3d7k_camera_off(); cfg->cis->init_sensor = 0; 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 v4l2_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 v4l2_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, #if 0 .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, #endif .fops = &camif_c_fops, .release = camif_vdev_release, .minor = CODEC_MINOR, }; struct video_device preview_template = { .name = PREVIEW_DEV_NAME, #if 0 .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, #endif .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; /* @@@ - WA */ //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; /* @@@ - WA */ //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; struct clk *camif_clock; /* 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); camif_clock = clk_get(&pdev->dev, "camif"); if (IS_ERR(camif_clock)) { dev_err(&pdev->dev, "Failed to find camera interface clock source\n"); return PTR_ERR(cam_clock); } clk_enable(camif_clock); /* 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"); return PTR_ERR(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 = i2c_get_clientdata(ptr); codec = s3c_camif_get_fimc_object(CODEC_MINOR); preview = s3c_camif_get_fimc_object(PREVIEW_MINOR); codec->cis = preview->cis = cis; sema_init(&codec->cis->lock, 1); sema_init(&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 = i2c_get_clientdata(ptr); 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");