cyb4_linux/drivers/media/video/s3c_camera_driver.c

1872 lines
42 KiB
C

/* drivers/media/video/s3c_camera_driver.c
*
* Copyright (c) 2008 Samsung Electronics
*
* Samsung S3C Camera driver
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/signal.h>
#include <linux/ioport.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/kmod.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/mm.h>
#include <linux/videodev2.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/page.h>
#include <asm/semaphore.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-camif.h>
#include <media/v4l2-dev.h>
#include "s3c_camif.h"
#include "videodev2_s3c.h"
static struct clk *cam_clock;
camif_cfg_t s3c_fimc[CAMIF_DEV_NUM];
extern camif_cis_t msdma_input;
extern int s3c_camif_do_postprocess(camif_cfg_t *cfg);
/*************************************************************************
* Utility part
************************************************************************/
camif_cfg_t *s3c_camif_get_fimc_object(int nr)
{
camif_cfg_t *ret = NULL;
switch (nr) {
case CODEC_MINOR:
ret = &s3c_fimc[FIMC_CODEC_INDEX];
break;
case PREVIEW_MINOR:
ret = &s3c_fimc[FIMC_PREVIEW_INDEX];
break;
default:
printk(KERN_ERR "Unknown minor number\n");
}
return ret;
}
#if defined(FSM_ON_PREVIEW)
static int s3c_camif_check_global_status(camif_cfg_t *cfg)
{
int ret = 0;
if (down_interruptible((struct semaphore *) &cfg->cis->lock))
return -ERESTARTSYS;
if (cfg->cis->status & CWANT2START) {
cfg->cis->status &= ~CWANT2START;
cfg->auto_restart = 1;
ret = 1;
} else {
ret = 0; /* There is no codec */
cfg->auto_restart = 0; /* Duplicated ..Dummy */
}
up((struct semaphore *) &cfg->cis->lock);
return ret;
}
#endif
static int s3c_camif_convert_format(int pixfmt, int *fmtptr)
{
int fmt = CAMIF_YCBCR420;
int depth = 12;
switch (pixfmt) {
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_RGB565X:
fmt = CAMIF_RGB16;
depth = 16;
break;
case V4L2_PIX_FMT_BGR24: /* Not tested */
case V4L2_PIX_FMT_RGB24:
fmt = CAMIF_RGB24;
depth = 24;
break;
case V4L2_PIX_FMT_BGR32:
case V4L2_PIX_FMT_RGB32:
fmt = CAMIF_RGB24;
depth = 32;
break;
case V4L2_PIX_FMT_GREY: /* Not tested */
fmt = CAMIF_YCBCR420;
depth = 8;
break;
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
fmt = CAMIF_YCBCR422I;
depth = 16;
break;
case V4L2_PIX_FMT_YUV422P:
fmt = CAMIF_YCBCR422;
depth = 16;
break;
case V4L2_PIX_FMT_YUV420:
fmt = CAMIF_YCBCR420;
depth = 12;
break;
}
if (fmtptr) *fmtptr = fmt;
return depth;
}
static int s3c_camif_set_fb_info(camif_cfg_t *cfg, int depth, int fourcc)
{
/* To define v4l2_format used currently */
cfg->v2.frmbuf.fmt.width = cfg->target_x;
cfg->v2.frmbuf.fmt.height = cfg->target_y;
cfg->v2.frmbuf.fmt.field = V4L2_FIELD_NONE;
cfg->v2.frmbuf.fmt.pixelformat = fourcc;
cfg->v2.frmbuf.fmt.bytesperline = cfg->v2.frmbuf.fmt.width * depth >> 3;
cfg->v2.frmbuf.fmt.sizeimage = cfg->v2.frmbuf.fmt.height * cfg->v2.frmbuf.fmt.bytesperline;
return 0;
}
static int s3c_camif_convert_type(camif_cfg_t *cfg, int f)
{
int pixfmt;
cfg->target_x = cfg->v2.frmbuf.fmt.width;
cfg->target_y = cfg->v2.frmbuf.fmt.height;
s3c_camif_convert_format(cfg->v2.frmbuf.fmt.pixelformat, &pixfmt);
cfg->dst_fmt = pixfmt;
return 0;
}
/*************************************************************************
* Control part
************************************************************************/
static int s3c_camif_start_capture(camif_cfg_t * cfg)
{
int ret = 0;
cfg->capture_enable = CAMIF_DMA_ON;
s3c_camif_start_dma(cfg);
cfg->status = CAMIF_STARTED;
if (!(cfg->fsm == CAMIF_SET_LAST_INT || cfg->fsm == CAMIF_CONTINUOUS_INT)) {
cfg->fsm = CAMIF_DUMMY_INT;
cfg->perf.frames = 0;
}
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
if (cfg->input_channel == MSDMA_FROM_CODEC)
s3c_camif_start_codec_msdma(cfg);
#endif
return ret;
}
ssize_t s3c_camif_start_preview(camif_cfg_t *cfg)
{
cfg->capture_enable = CAMIF_DMA_ON;
s3c_camif_start_dma(cfg);
cfg->status = CAMIF_STARTED;
cfg->fsm = CAMIF_1st_INT;
cfg->perf.frames = 0;
return 0;
}
ssize_t s3c_camif_stop_preview(camif_cfg_t *cfg)
{
cfg->capture_enable = CAMIF_DMA_OFF;
cfg->status = CAMIF_STOPPED;
s3c_camif_stop_dma(cfg);
cfg->perf.frames = 0;
return 0;
}
ssize_t s3c_camif_stop_capture(camif_cfg_t *cfg)
{
cfg->capture_enable = CAMIF_DMA_OFF;
cfg->status = CAMIF_STOPPED;
s3c_camif_stop_dma(cfg);
cfg->perf.frames = 0;
return 0;
}
ssize_t s3c_camif_stop_fimc(camif_cfg_t *cfg)
{
cfg->capture_enable = CAMIF_BOTH_DMA_OFF;
cfg->fsm = CAMIF_DUMMY_INT;
cfg->perf.frames = 0;
s3c_camif_stop_dma(cfg);
return 0;
}
#if defined(FSM_ON_PREVIEW)
static void s3c_camif_start_preview_with_codec(camif_cfg_t *cfg)
{
camif_cfg_t *other = (camif_cfg_t *)cfg->other;
/* Preview Stop */
cfg->capture_enable = CAMIF_DMA_OFF;
s3c_camif_stop_dma(cfg);
/* Start Preview and CODEC */
cfg->capture_enable =CAMIF_BOTH_DMA_ON;
s3c_camif_start_dma(cfg);
cfg->fsm = CAMIF_1st_INT; /* For Preview */
if (!other)
panic("Unexpected error: other is null\n");
switch (other->pp_num) {
case 4:
other->fsm = CAMIF_1st_INT; /* For CODEC */
break;
case 1:
other->fsm = CAMIF_Yth_INT;
break;
default:
panic("Invalid pingpong number");
break;
}
}
static void s3c_camif_auto_restart(camif_cfg_t *cfg)
{
if (cfg->auto_restart)
s3c_camif_start_preview_with_codec(cfg);
}
#endif
static void s3c_camif_change_mode(camif_cfg_t *cfg, int mode)
{
camif_cis_t *cis = cfg->cis;
int res;
if (mode == SENSOR_MAX) {
#if defined(CONFIG_VIDEO_SAMSUNG_S5K3AA)
res = SENSOR_SXGA;
#elif defined(CONFIG_VIDEO_SAMSUNG_S5K3BA)
res = SENSOR_UXGA;
/* 4BA max is UXGA, but we don't have UXGA control values */
#elif defined(CONFIG_VIDEO_SAMSUNG_S5K4BA)
res = SENSOR_SVGA;
#endif
} else if (mode == SENSOR_DEFAULT) {
#if defined(CONFIG_VIDEO_SAMSUNG_S5K4BA)
res = SENSOR_SVGA;
#else
res = SENSOR_VGA;
#endif
} else
res = mode;
s3c_camif_stop_fimc(cfg);
switch (res) {
case SENSOR_SXGA:
printk(KERN_INFO "Resolution changed into SXGA (1280x1024) mode -> 1.3M\n");
cis->sensor->driver->command(cis->sensor, SENSOR_SXGA, NULL);
cis->source_x = 1280;
cis->source_y = 1024;
break;
case SENSOR_UXGA:
printk(KERN_INFO "Resolution changed into UXGA (1600x1200) mode -> 2.0M\n");
cis->sensor->driver->command(cis->sensor, SENSOR_UXGA, NULL);
cis->source_x = 1600;
cis->source_y = 1200;
break;
case SENSOR_SVGA:
printk(KERN_INFO "Resolution changed back to SVGA (800x600) mode\n");
cis->sensor->driver->command(cis->sensor, SENSOR_SVGA, NULL);
cis->source_x = 800;
cis->source_y = 600;
break;
case SENSOR_VGA:
printk(KERN_INFO "Resolution changed back to VGA (640x480) mode (default)\n");
cis->sensor->driver->command(cis->sensor, SENSOR_VGA, NULL);
cis->source_x = 640;
cis->source_y = 480;
break;
}
cis->win_hor_ofst = cis->win_hor_ofst2 = 0;
cis->win_ver_ofst = cis->win_ver_ofst2 = 0;
s3c_camif_set_source_format(cis);
}
static int s3c_camif_check_zoom_range(camif_cfg_t *cfg, int type)
{
switch (type) {
case V4L2_CID_ZOOMIN:
if (((cfg->sc.modified_src_x - (cfg->cis->win_hor_ofst + \
ZOOM_AT_A_TIME_IN_PIXELS + cfg->cis->win_hor_ofst2 + \
ZOOM_AT_A_TIME_IN_PIXELS)) / cfg->sc.prehratio) > ZOOM_IN_MAX) {
printk(KERN_INFO "Invalid Zoom-in: this zoom-in on preview scaler already comes to the maximum\n");
return 0;
}
cfg->sc.zoom_in_cnt++;
break;
case V4L2_CID_ZOOMOUT:
if (cfg->sc.zoom_in_cnt > 0) {
cfg->sc.zoom_in_cnt--;
break;
} else {
printk(KERN_INFO "Invalid Zoom-out: this zoom-out on preview scaler already comes to the minimum\n");
return 0;
}
break;
default:
break;
}
return 1;
}
static int s3c_camif_restart_preview(camif_cfg_t *cfg)
{
int ret = 0;
s3c_camif_stop_preview(cfg);
if (s3c_camif_control_fimc(cfg)) {
printk(KERN_ERR "S3C fimc control failed\n");
ret = -1;
}
s3c_camif_start_preview(cfg);
return ret;
}
static int s3c_camif_send_sensor_command(camif_cfg_t *cfg, unsigned int cmd, int arg)
{
cfg->cis->sensor->driver->command(cfg->cis->sensor, cmd, (void *) arg);
return 0;
}
/*************************************************************************
* V4L2 part
************************************************************************/
static int s3c_camif_v4l2_querycap(camif_cfg_t *cfg, void *arg)
{
struct v4l2_capability *cap = arg;
strcpy(cap->driver, "S3C FIMC Camera driver");
strlcpy(cap->card, cfg->v->name, sizeof(cap->card));
sprintf(cap->bus_info, "FIMC AHB Bus");
cap->version = 0;
cap->capabilities = cfg->v->type2;
return 0;
}
static int s3c_camif_v4l2_g_fbuf(camif_cfg_t *cfg, void *arg)
{
struct v4l2_framebuffer *fb = arg;
*fb = cfg->v2.frmbuf;
fb->base = cfg->v2.frmbuf.base;
fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
fb->fmt.pixelformat = cfg->v2.frmbuf.fmt.pixelformat;
fb->fmt.width = cfg->v2.frmbuf.fmt.width;
fb->fmt.height = cfg->v2.frmbuf.fmt.height;
fb->fmt.bytesperline = cfg->v2.frmbuf.fmt.bytesperline;
return 0;
}
static int s3c_camif_v4l2_s_fbuf(camif_cfg_t *cfg, void *arg)
{
struct v4l2_framebuffer *fb = arg;
int i, depth;
for (i = 0; i < NUMBER_OF_PREVIEW_FORMATS; i++)
if (fimc_preview_formats[i].pixelformat == fb->fmt.pixelformat)
break;
if (i == NUMBER_OF_PREVIEW_FORMATS)
return -EINVAL;
cfg->v2.frmbuf.base = fb->base;
cfg->v2.frmbuf.flags = fb->flags;
cfg->v2.frmbuf.capability = fb->capability;
cfg->target_x = fb->fmt.width;
cfg->target_y = fb->fmt.height;
depth = s3c_camif_convert_format(fb->fmt.pixelformat, (int *) &(cfg->dst_fmt));
s3c_camif_set_fb_info(cfg, depth, fb->fmt.pixelformat);
return s3c_camif_control_fimc(cfg);
}
static int s3c_camif_v4l2_g_fmt(camif_cfg_t *cfg, void *arg)
{
struct v4l2_format *f = (struct v4l2_format *) arg;
int size = sizeof(struct v4l2_pix_format);
int ret = -1;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
memset(&f->fmt.pix, 0, size);
memcpy(&f->fmt.pix, &cfg->v2.frmbuf.fmt, size);
ret = 0;
break;
default:
break;
}
return ret;
}
static int s3c_camif_v4l2_s_fmt(camif_cfg_t *cfg, void *arg)
{
struct v4l2_format *f = (struct v4l2_format *) arg;
int ret = -1;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
cfg->v2.frmbuf.fmt = f->fmt.pix;
cfg->v2.status |= CAMIF_v4L2_DIRTY;
cfg->v2.status &= ~CAMIF_v4L2_DIRTY; /* dummy ? */
s3c_camif_convert_type(cfg, 1);
s3c_camif_control_fimc(cfg);
ret = 0;
break;
default:
break;
}
return ret;
}
static int s3c_camif_v4l2_enum_fmt(camif_cfg_t *cfg, void *arg)
{
struct v4l2_fmtdesc *f = arg;
int index = f->index;
if (index >= NUMBER_OF_CODEC_FORMATS)
return -EINVAL;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
default:
return -EINVAL;
}
memset(f, 0, sizeof(*f));
memcpy(f, cfg->v2.fmtdesc + index, sizeof(*f));
return 0;
}
static int s3c_camif_v4l2_overlay(camif_cfg_t *cfg, void *arg)
{
int on = *((int *) arg);
int ret;
if (on != 0)
ret = s3c_camif_start_preview(cfg);
else
ret = s3c_camif_stop_preview(cfg);
return ret;
}
static int s3c_camif_v4l2_g_ctrl(camif_cfg_t *cfg, void *arg)
{
return 0;
}
static int s3c_camif_v4l2_s_ctrl(camif_cfg_t *cfg, void *arg)
{
struct v4l2_control *ctrl = (struct v4l2_control *) arg;
switch (ctrl->id) {
case V4L2_CID_ORIGINAL:
case V4L2_CID_ARBITRARY:
case V4L2_CID_NEGATIVE:
case V4L2_CID_EMBOSSING:
case V4L2_CID_ART_FREEZE:
case V4L2_CID_SILHOUETTE:
cfg->effect = ctrl->value;
s3c_camif_change_effect(cfg);
break;
case V4L2_CID_HFLIP:
cfg->flip = CAMIF_FLIP_X;
s3c_camif_change_flip(cfg);
break;
case V4L2_CID_VFLIP:
cfg->flip = CAMIF_FLIP_Y;
s3c_camif_change_flip(cfg);
break;
case V4L2_CID_ROTATE_180:
cfg->flip = CAMIF_FLIP_MIRROR;
s3c_camif_change_flip(cfg);
break;
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
case V4L2_CID_ROTATE_90:
cfg->flip = CAMIF_ROTATE_90;
s3c_camif_change_flip(cfg);
break;
case V4L2_CID_ROTATE_270:
cfg->flip = CAMIF_FLIP_ROTATE_270;
s3c_camif_change_flip(cfg);
break;
#endif
case V4L2_CID_ROTATE_BYPASS:
cfg->flip = CAMIF_FLIP;
s3c_camif_change_flip(cfg);
break;
case V4L2_CID_ZOOMIN:
if (s3c_camif_check_zoom_range(cfg, ctrl->id)) {
cfg->cis->win_hor_ofst += ZOOM_AT_A_TIME_IN_PIXELS;
cfg->cis->win_ver_ofst += ZOOM_AT_A_TIME_IN_PIXELS;
cfg->cis->win_hor_ofst2 += ZOOM_AT_A_TIME_IN_PIXELS;
cfg->cis->win_ver_ofst2 += ZOOM_AT_A_TIME_IN_PIXELS;
s3c_camif_restart_preview(cfg);
}
break;
case V4L2_CID_ZOOMOUT:
if (s3c_camif_check_zoom_range(cfg, ctrl->id)) {
cfg->cis->win_hor_ofst -= ZOOM_AT_A_TIME_IN_PIXELS;
cfg->cis->win_ver_ofst -= ZOOM_AT_A_TIME_IN_PIXELS;
cfg->cis->win_hor_ofst2 -= ZOOM_AT_A_TIME_IN_PIXELS;
cfg->cis->win_ver_ofst2 -= ZOOM_AT_A_TIME_IN_PIXELS;
s3c_camif_restart_preview(cfg);
}
break;
case V4L2_CID_CONTRAST:
case V4L2_CID_AUTO_WHITE_BALANCE:
s3c_camif_send_sensor_command(cfg, SENSOR_WB, ctrl->value);
break;
default:
printk(KERN_ERR "Invalid control id: %d\n", ctrl->id);
return -1;
}
return 0;
}
static int s3c_camif_v4l2_streamon(camif_cfg_t *cfg, void *arg)
{
int ret = 0;
ret = s3c_camif_start_capture(cfg);
return ret;
}
static int s3c_camif_v4l2_streamoff(camif_cfg_t *cfg, void *arg)
{
int ret = 0;
cfg->cis->status &= ~C_WORKING;
s3c_camif_stop_capture(cfg);
return ret;
}
static int s3c_camif_v4l2_g_input(camif_cfg_t *cfg, void *arg)
{
unsigned int index = *((int *) arg);
index = cfg->v2.input->index;
return 0;
}
static int s3c_camif_v4l2_s_input(camif_cfg_t *cfg, unsigned int index)
{
int ret = -1;
if (index >= NUMBER_OF_INPUTS)
ret = -1;
else {
cfg->v2.input = &fimc_inputs[index];
if (cfg->v2.input->type == V4L2_INPUT_TYPE_MSDMA) {
if (cfg->dma_type & CAMIF_PREVIEW) {
cfg->input_channel = MSDMA_FROM_PREVIEW;
ret = 0;
} else if (cfg->dma_type & CAMIF_CODEC) {
cfg->input_channel = MSDMA_FROM_CODEC;
ret = 0;
}
} else {
cfg->input_channel = CAMERA_INPUT;
ret = 0;
}
}
return ret;
}
static int s3c_camif_v4l2_g_output(camif_cfg_t *cfg, void *arg)
{
unsigned int index = *((int *) arg);
index = cfg->v2.output->index;
return 0;
}
static int s3c_camif_v4l2_s_output(camif_cfg_t *cfg, unsigned int index)
{
if (index >= NUMBER_OF_OUTPUTS)
return -EINVAL;
else {
cfg->v2.output = (struct v4l2_output *) &fimc_outputs[index];
return 0;
}
}
static int s3c_camif_v4l2_enum_input(camif_cfg_t *cfg, void *arg)
{
struct v4l2_input *i = arg;
if (i->index >= NUMBER_OF_INPUTS)
return -EINVAL;
memcpy(i, &fimc_inputs[i->index], sizeof(struct v4l2_input));
return 0;
}
static int s3c_camif_v4l2_enum_output(camif_cfg_t *cfg, void *arg)
{
struct v4l2_output *i = arg;
if ((i->index) >= NUMBER_OF_OUTPUTS)
return -EINVAL;
memcpy(i, &fimc_outputs[i->index], sizeof(struct v4l2_output));
return 0;
}
static int s3c_camif_v4l2_reqbufs(camif_cfg_t *cfg, void *arg)
{
struct v4l2_requestbuffers *req = arg;
if (req->memory != V4L2_MEMORY_MMAP) {
printk(KERN_ERR "Only V4L2_MEMORY_MMAP capture is supported\n");
return -EINVAL;
}
/* control user input */
if (req->count > 2)
req->count = 4;
else if (req->count > 1)
req->count = 2;
else
req->count = 1;
return 0;
}
static int s3c_camif_v4l2_querybuf(camif_cfg_t *cfg, void *arg)
{
struct v4l2_buffer *buf = arg;
if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && buf->memory != V4L2_MEMORY_MMAP)
return -1;
buf->length = cfg->buffer_size;
buf->m.offset = buf->length * buf->index;
return 0;
}
static int s3c_camif_v4l2_qbuf(camif_cfg_t *cfg, void *arg)
{
return 0;
}
static int s3c_camif_v4l2_dqbuf(camif_cfg_t *cfg, void *arg)
{
struct v4l2_buffer *buf = arg;
buf->index = cfg->cur_frame_num % cfg->pp_num;
return 0;
}
/*
* S3C specific
*/
static int s3c_camif_v4l2_s_msdma(camif_cfg_t *cfg, void *arg)
{
struct v4l2_msdma_format *f = arg;
int ret = -1;
switch(f->input_path) {
case V4L2_MSDMA_PREVIEW:
cfg->cis->user--; /* CIS will be replaced with a CIS for MSDMA */
cfg->cis = &msdma_input;
cfg->cis->user++;
cfg->input_channel = MSDMA_FROM_PREVIEW;
break;
case V4L2_MSDMA_CODEC:
cfg->cis->user--; /* CIS will be replaced with a CIS for MSDMA */
cfg->cis = &msdma_input;
cfg->cis->user++;
cfg->input_channel = MSDMA_FROM_CODEC;
break;
default:
cfg->input_channel = CAMERA_INPUT;
break;
}
cfg->cis->source_x = f->width;
cfg->cis->source_y = f->height;
s3c_camif_convert_format(f->pixelformat, (int *) &cfg->src_fmt);
cfg->cis->win_hor_ofst = 0;
cfg->cis->win_ver_ofst = 0;
cfg->cis->win_hor_ofst2 = 0;
cfg->cis->win_ver_ofst2 = 0;
ret = s3c_camif_control_fimc(cfg);
switch(f->input_path) {
case V4L2_MSDMA_PREVIEW:
ret = s3c_camif_start_preview(cfg);
break;
case V4L2_MSDMA_CODEC:
ret = s3c_camif_start_capture(cfg);
break;
default:
break;
}
return ret;
}
static int s3c_camif_v4l2_msdma_start(camif_cfg_t *cfg, void *arg)
{
if (cfg->input_channel == MSDMA_FROM_PREVIEW) {
cfg->msdma_status = 1;
s3c_camif_start_preview_msdma(cfg);
}
return 0;
}
static int s3c_camif_v4l2_msdma_stop(camif_cfg_t *cfg, void *arg)
{
struct v4l2_msdma_format *f = arg;
int ret = -1;
cfg->cis->status &= ~C_WORKING;
cfg->msdma_status = 0;
switch(f->input_path) {
case V4L2_MSDMA_PREVIEW:
ret = s3c_camif_stop_preview(cfg);
break;
case V4L2_MSDMA_CODEC:
ret = s3c_camif_stop_capture(cfg);
break;
default:
break;
}
return ret;
}
static int s3c_camif_v4l2_camera_start(camif_cfg_t *cfg, void *arg)
{
return 0;
}
static int s3c_camif_v4l2_camera_stop(camif_cfg_t *cfg, void *arg)
{
return 0;
}
static int s3c_camif_v4l2_cropcap(camif_cfg_t *cfg, void *arg)
{
struct v4l2_cropcap *cap = arg;
if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
return -EINVAL;
/* crop limitations */
cfg->v2.crop_bounds.left = 0;
cfg->v2.crop_bounds.top = 0;
cfg->v2.crop_bounds.width = cfg->cis->source_x;
cfg->v2.crop_bounds.height = cfg->cis->source_y;
/* crop default values */
cfg->v2.crop_defrect.left = (cfg->cis->source_x - CROP_DEFAULT_WIDTH) / 2;
cfg->v2.crop_defrect.top = (cfg->cis->source_y - CROP_DEFAULT_HEIGHT) / 2;
cfg->v2.crop_defrect.width = CROP_DEFAULT_WIDTH;
cfg->v2.crop_defrect.height = CROP_DEFAULT_HEIGHT;
cap->bounds = cfg->v2.crop_bounds;
cap->defrect = cfg->v2.crop_defrect;
return 0;
}
static int s3c_camif_v4l2_g_crop(camif_cfg_t *cfg, void *arg)
{
struct v4l2_crop *crop = arg;
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
return -EINVAL;
crop->c = cfg->v2.crop_current;
return 0;
}
static int s3c_camif_v4l2_s_crop(camif_cfg_t *cfg, void *arg)
{
struct v4l2_crop *crop = arg;
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY)
return -EINVAL;
if (crop->c.height < 0)
return -EINVAL;
if (crop->c.width < 0)
return -EINVAL;
if ((crop->c.left + crop->c.width > cfg->cis->source_x) || \
(crop->c.top + crop->c.height > cfg->cis->source_y))
return -EINVAL;
cfg->v2.crop_current = crop->c;
cfg->cis->win_hor_ofst = (cfg->cis->source_x - crop->c.width) / 2;
cfg->cis->win_ver_ofst = (cfg->cis->source_y - crop->c.height) / 2;
cfg->cis->win_hor_ofst2 = cfg->cis->win_hor_ofst;
cfg->cis->win_ver_ofst2 = cfg->cis->win_ver_ofst;
s3c_camif_restart_preview(cfg);
return 0;
}
static int s3c_camif_v4l2_s_parm(camif_cfg_t *cfg, void *arg)
{
struct v4l2_streamparm *sp = arg;
if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (sp->parm.capture.capturemode == V4L2_MODE_HIGHQUALITY) {
s3c_camif_change_mode(cfg, SENSOR_MAX);
s3c_camif_control_fimc(cfg);
} else {
s3c_camif_change_mode(cfg, SENSOR_DEFAULT);
s3c_camif_control_fimc(cfg);
}
return 0;
}
/*************************************************************************
* Interrupt part
************************************************************************/
#if defined(FSM_ON_CODEC) && !defined(USE_LAST_IRQ)
int s3c_camif_do_fsm_codec(camif_cfg_t *cfg)
{
int ret;
cfg->perf.frames++;
if ((cfg->fsm == CAMIF_DUMMY_INT) && (cfg->perf.frames > CAMIF_CAPTURE_SKIP_FRAMES))
cfg->fsm = CAMIF_NORMAL_INT;
switch (cfg->fsm) {
case CAMIF_DUMMY_INT:
DPRINTK(KERN_INFO "CAMIF_DUMMY_INT: %d\n", cfg->perf.frames);
cfg->status = CAMIF_STARTED;
cfg->fsm = CAMIF_DUMMY_INT;
ret = INSTANT_SKIP;
break;
case CAMIF_NORMAL_INT:
DPRINTK(KERN_INFO "CAMIF_NORMAL_INT: %d\n", cfg->perf.frames);
cfg->status = CAMIF_INT_HAPPEN;
cfg->fsm = CAMIF_CONTINUOUS_INT;
ret = INSTANT_GO;
break;
case CAMIF_CONTINUOUS_INT:
DPRINTK(KERN_INFO "CAMIF_CONTINUOS_INT: %d\n", cfg->perf.frames);
cfg->status = CAMIF_INT_HAPPEN;
cfg->fsm = CAMIF_CONTINUOUS_INT;
ret = INSTANT_GO;
break;
default:
printk(KERN_INFO "Unexpect INT: %d\n", cfg->fsm);
ret = INSTANT_SKIP;
break;
}
return ret;
}
#endif
#if defined(FSM_ON_CODEC) && defined(USE_LAST_IRQ)
int s3c_camif_do_fsm_codec_lastirq(camif_cfg_t *cfg)
{
int ret;
cfg->perf.frames++;
if ((cfg->fsm == CAMIF_DUMMY_INT) && (cfg->perf.frames > (CAMIF_CAPTURE_SKIP_FRAMES - 2)))
cfg->fsm = CAMIF_SET_LAST_INT;
switch (cfg->fsm) {
case CAMIF_DUMMY_INT:
DPRINTK(KERN_INFO "CAMIF_DUMMY_INT: %d\n", cfg->perf.frames);
cfg->status = CAMIF_STARTED;
cfg->fsm = CAMIF_DUMMY_INT;
ret = INSTANT_SKIP;
break;
case CAMIF_SET_LAST_INT:
DPRINTK(KERN_INFO "CAMIF_SET_LAST_INT: %d\n", cfg->perf.frames);
s3c_camif_enable_lastirq(cfg);
/* in 64xx, lastirq is not auto cleared. */
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
s3c_camif_disable_lastirq(cfg);
#endif
cfg->status = CAMIF_INT_HAPPEN;
cfg->fsm = CAMIF_STOP_CAPTURE;
ret = INSTANT_SKIP;
break;
case CAMIF_STOP_CAPTURE:
DPRINTK(KERN_INFO "CAMIF_STOP_CAPTURE: %d\n", cfg->perf.frames);
cfg->capture_enable = CAMIF_DMA_OFF;
s3c_camif_stop_dma(cfg);
cfg->fsm = CAMIF_LAST_IRQ;
ret = INSTANT_SKIP;
break;
case CAMIF_LAST_IRQ:
DPRINTK(KERN_INFO "CAMIF_LAST_IRQ: %d\n", cfg->perf.frames);
cfg->fsm = CAMIF_SET_LAST_INT;
cfg->status = CAMIF_INT_HAPPEN;
ret = INSTANT_GO;
break;
default:
printk(KERN_INFO "Unexpect INT: %d\n", cfg->fsm);
ret = INSTANT_SKIP;
break;
}
return ret;
}
#endif
#if defined(FSM_ON_PREVIEW)
static int s3c_camif_do_lastirq_preview(camif_cfg_t *cfg)
{
int ret = 0;
cfg->perf.frames++;
if (cfg->fsm == CAMIF_NORMAL_INT) {
if (cfg->perf.frames % CHECK_FREQ == 0)
ret = s3c_camif_check_global_status(cfg);
}
if (ret > 0)
cfg->fsm = CAMIF_Xth_INT;
switch (cfg->fsm) {
case CAMIF_1st_INT:
DPRINTK(KERN_INFO "CAMIF_1st_INT INT\n");
cfg->fsm = CAMIF_NORMAL_INT;
ret = INSTANT_SKIP;
break;
case CAMIF_NORMAL_INT:
DPRINTK(KERN_INFO "CAMIF_NORMAL_INT\n");
cfg->status = CAMIF_INT_HAPPEN;
cfg->fsm = CAMIF_NORMAL_INT;
ret = INSTANT_GO;
break;
case CAMIF_Xth_INT:
DPRINTK(KERN_INFO "CAMIF_Xth_INT\n");
s3c_camif_enable_lastirq(cfg);
cfg->status = CAMIF_INT_HAPPEN;
cfg->fsm = CAMIF_Yth_INT;
ret = INSTANT_GO;
break;
case CAMIF_Yth_INT:
DPRINTK(KERN_INFO "CAMIF_Yth_INT\n");
s3c_camif_disable_lastirq(cfg);
cfg->capture_enable = CAMIF_DMA_OFF;
cfg->status = CAMIF_INT_HAPPEN;
s3c_camif_stop_dma(cfg);
cfg->fsm = CAMIF_Zth_INT;
ret = INSTANT_GO;
break;
case CAMIF_Zth_INT:
DPRINTK(KERN_INFO "CAMIF_Zth_INT\n");
cfg->fsm = CAMIF_DUMMY_INT;
cfg->status = CAMIF_INT_HAPPEN;
ret = INSTANT_GO;
s3c_camif_auto_restart(cfg);
break;
case CAMIF_DUMMY_INT:
DPRINTK(KERN_INFO "CAMIF_DUMMY_INT\n");
cfg->status = CAMIF_STOPPED;
ret = INSTANT_SKIP;
break;
default:
printk(KERN_INFO "Unexpected INT %d\n", cfg->fsm);
ret = INSTANT_SKIP;
break;
}
return ret;
}
#endif
static irqreturn_t s3c_camif_do_irq_codec(int irq, void *dev_id)
{
camif_cfg_t *cfg = (camif_cfg_t *) dev_id;
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
s3c_gpio_setpin(S3C_GPN15, 1);
#endif
s3c_camif_clear_irq(irq);
s3c_camif_get_fifo_status(cfg);
s3c_camif_get_frame_num(cfg);
#if defined(FSM_ON_CODEC) && !defined(USE_LAST_IRQ)
if (s3c_camif_do_fsm_codec(cfg) == INSTANT_SKIP)
return IRQ_HANDLED;
#endif
#if defined(FSM_ON_CODEC) && defined(USE_LAST_IRQ)
if (s3c_camif_do_fsm_codec_lastirq(cfg) == INSTANT_SKIP)
return IRQ_HANDLED;
#endif
wake_up_interruptible(&cfg->waitq);
return IRQ_HANDLED;
}
static irqreturn_t s3c_camif_do_irq_preview(int irq, void *dev_id)
{
camif_cfg_t *cfg = (camif_cfg_t *) dev_id;
s3c_camif_clear_irq(irq);
s3c_camif_get_fifo_status(cfg);
s3c_camif_get_frame_num(cfg);
wake_up_interruptible(&cfg->waitq);
#if defined(FSM_ON_PREVIEW)
if (s3c_camif_do_lastirq_preview(cfg) == INSTANT_SKIP)
return IRQ_HANDLED;
wake_up_interruptible(&cfg->waitq);
#endif
cfg->status = CAMIF_INT_HAPPEN;
return IRQ_HANDLED;
}
static void s3c_camif_release_irq(camif_cfg_t * cfg)
{
disable_irq(cfg->irq);
free_irq(cfg->irq, cfg);
}
static int s3c_camif_request_irq(camif_cfg_t * cfg)
{
int ret = 0;
if (cfg->dma_type & CAMIF_CODEC) {
if ((ret = request_irq(cfg->irq, s3c_camif_do_irq_codec, SA_INTERRUPT, cfg->shortname, cfg)))
printk(KERN_ERR "Request irq (CAM_C) failed\n");
else
printk(KERN_INFO "Request irq %d for codec\n", cfg->irq);
}
if (cfg->dma_type & CAMIF_PREVIEW) {
if ((ret = request_irq(cfg->irq, s3c_camif_do_irq_preview, SA_INTERRUPT, cfg->shortname, cfg)))
printk("Request_irq (CAM_P) failed\n");
else
printk(KERN_INFO "Request irq %d for preview\n", cfg->irq);
}
return 0;
}
/*************************************************************************
* Standard file operations part
************************************************************************/
int s3c_camif_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg)
{
camif_cfg_t *cfg = file->private_data;
int ret = -1;
switch (cmd) {
case VIDIOC_QUERYCAP:
ret = s3c_camif_v4l2_querycap(cfg, arg);
break;
case VIDIOC_G_FBUF:
ret = s3c_camif_v4l2_g_fbuf(cfg, arg);
break;
case VIDIOC_S_FBUF:
ret = s3c_camif_v4l2_s_fbuf(cfg, arg);
break;
case VIDIOC_G_FMT:
ret = s3c_camif_v4l2_g_fmt(cfg, arg);
break;
case VIDIOC_S_FMT:
ret = s3c_camif_v4l2_s_fmt(cfg, arg);
break;
case VIDIOC_ENUM_FMT:
ret = s3c_camif_v4l2_enum_fmt(cfg, arg);
break;
case VIDIOC_OVERLAY:
ret = s3c_camif_v4l2_overlay(cfg, arg);
break;
case VIDIOC_S_CTRL:
ret = s3c_camif_v4l2_s_ctrl(cfg, arg);
break;
case VIDIOC_G_CTRL:
ret = s3c_camif_v4l2_g_ctrl(cfg, arg);
break;
case VIDIOC_STREAMON:
ret = s3c_camif_v4l2_streamon(cfg, arg);
break;
case VIDIOC_STREAMOFF:
ret = s3c_camif_v4l2_streamoff(cfg, arg);
break;
case VIDIOC_G_INPUT:
ret = s3c_camif_v4l2_g_input(cfg, arg);
break;
case VIDIOC_S_INPUT:
ret = s3c_camif_v4l2_s_input(cfg, *((int *) arg));
break;
case VIDIOC_G_OUTPUT:
ret = s3c_camif_v4l2_g_output(cfg, arg);
break;
case VIDIOC_S_OUTPUT:
ret = s3c_camif_v4l2_s_output(cfg, *((int *) arg));
break;
case VIDIOC_ENUMINPUT:
ret = s3c_camif_v4l2_enum_input(cfg, arg);
break;
case VIDIOC_ENUMOUTPUT:
ret = s3c_camif_v4l2_enum_output(cfg, arg);
break;
case VIDIOC_REQBUFS:
ret = s3c_camif_v4l2_reqbufs(cfg, arg);
break;
case VIDIOC_QUERYBUF:
ret = s3c_camif_v4l2_querybuf(cfg, arg);
break;
case VIDIOC_QBUF:
ret = s3c_camif_v4l2_qbuf(cfg, arg);
break;
case VIDIOC_DQBUF:
ret = s3c_camif_v4l2_dqbuf(cfg, arg);
break;
case VIDIOC_S_MSDMA:
ret = s3c_camif_v4l2_s_msdma(cfg, arg);
break;
case VIDIOC_MSDMA_START:
ret = s3c_camif_v4l2_msdma_start(cfg, arg);
break;
case VIDIOC_MSDMA_STOP:
ret = s3c_camif_v4l2_msdma_stop(cfg, arg);
break;
case VIDIOC_S_CAMERA_START:
ret = s3c_camif_v4l2_camera_start(cfg, arg);
break;
case VIDIOC_S_CAMERA_STOP:
ret = s3c_camif_v4l2_camera_stop(cfg, arg);
break;
case VIDIOC_CROPCAP:
ret = s3c_camif_v4l2_cropcap(cfg, arg);
break;
case VIDIOC_G_CROP:
ret = s3c_camif_v4l2_g_crop(cfg, arg);
break;
case VIDIOC_S_CROP:
ret = s3c_camif_v4l2_s_crop(cfg, arg);
break;
case VIDIOC_S_PARM:
ret = s3c_camif_v4l2_s_parm(cfg, arg);
break;
default: /* For v4l compatability */
v4l_compat_translate_ioctl(inode, file, cmd, arg, s3c_camif_ioctl);
break;
} /* End of Switch */
return ret;
}
int s3c_camif_open(struct inode *inode, struct file *file)
{
int err;
camif_cfg_t *cfg = s3c_camif_get_fimc_object(MINOR(inode->i_rdev));
if (!cfg->cis) {
printk(KERN_ERR "An object for a CIS is missing\n");
printk(KERN_ERR "Using msdma_input as a default CIS data structure\n");
cfg->cis = &msdma_input;
/* global lock for both Codec and Preview */
sema_init((struct semaphore *) &cfg->cis->lock, 1);
cfg->cis->status |= P_NOT_WORKING;
}
if (cfg->dma_type & CAMIF_PREVIEW) {
if (cfg->dma_type & CAMIF_PREVIEW)
cfg->cis->status &= ~P_NOT_WORKING;
up((struct semaphore *) &cfg->cis->lock);
}
err = video_exclusive_open(inode, file);
cfg->cis->user++;
cfg->status = CAMIF_STOPPED;
if (err < 0)
return err;
if (file->f_flags & O_NONCAP) {
printk(KERN_ERR "Don't support non-capturing open\n");
return 0;
}
file->private_data = cfg;
s3c_camif_init_sensor(cfg);
return 0;
}
int s3c_camif_release(struct inode *inode, struct file *file)
{
camif_cfg_t *cfg = s3c_camif_get_fimc_object(MINOR(inode->i_rdev));
if (cfg->dma_type & CAMIF_PREVIEW) {
cfg->cis->status &= ~PWANT2START;
cfg->cis->status |= P_NOT_WORKING;
s3c_camif_stop_preview(cfg);
up((struct semaphore *) &cfg->cis->lock);
} else {
cfg->cis->status &= ~CWANT2START;
s3c_camif_stop_capture(cfg);
}
video_exclusive_release(inode, file);
if (cfg->cis->sensor == NULL)
DPRINTK("A CIS sensor for MSDMA has been used\n");
else
cfg->cis->sensor->driver->command(cfg->cis->sensor, USER_EXIT, NULL);
cfg->cis->user--;
cfg->status = CAMIF_STOPPED;
return 0;
}
ssize_t s3c_camif_read(struct file * file, char *buf, size_t count, loff_t * pos)
{
camif_cfg_t *cfg = NULL;
size_t end;
cfg = s3c_camif_get_fimc_object(MINOR(file->f_dentry->d_inode->i_rdev));
#if defined(FSM_ON_PREVIEW)
if (cfg->dma_type == CAMIF_PREVIEW) {
if (wait_event_interruptible(cfg->waitq, cfg->status == CAMIF_INT_HAPPEN))
return -ERESTARTSYS;
cfg->status = CAMIF_STOPPED;
}
#endif
#if defined(FSM_ON_CODEC)
if (cfg->dma_type == CAMIF_CODEC) {
if (wait_event_interruptible(cfg->waitq, cfg->status == CAMIF_INT_HAPPEN))
return -ERESTARTSYS;
cfg->status = CAMIF_STOPPED;
}
#endif
end = min_t(size_t, cfg->pp_totalsize / cfg->pp_num, count);
if (copy_to_user(buf, s3c_camif_get_frame(cfg), end))
return -EFAULT;
return end;
}
ssize_t s3c_camif_write(struct file * f, const char *b, size_t c, loff_t * offset)
{
camif_cfg_t *cfg;
int ret = 0;
cfg = s3c_camif_get_fimc_object(MINOR(f->f_dentry->d_inode->i_rdev));
switch (*b) {
case 'O':
if (cfg->dma_type & CAMIF_PREVIEW)
s3c_camif_start_preview(cfg);
else {
ret = s3c_camif_start_capture(cfg);
if (ret < 0)
ret = 1;
}
break;
case 'X':
if (cfg->dma_type & CAMIF_PREVIEW) {
s3c_camif_stop_preview(cfg);
cfg->cis->status |= P_NOT_WORKING;
} else {
cfg->cis->status &= ~C_WORKING;
s3c_camif_stop_capture(cfg);
}
break;
#if defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
case 'P':
if (cfg->dma_type & CAMIF_PREVIEW) {
s3c_camif_start_preview(cfg);
s3c_camif_do_postprocess(cfg);
return 0;
} else
return -EFAULT;
#endif
default:
panic("s3c_camera_driver.c: s3c_camif_write() - Unexpected Parameter\n");
}
return ret;
}
int s3c_camif_mmap(struct file* filp, struct vm_area_struct *vma)
{
camif_cfg_t *cfg = filp->private_data;
unsigned long pageFrameNo;
unsigned long size = vma->vm_end - vma->vm_start;
unsigned long total_size;
if (cfg->dma_type == CAMIF_PREVIEW)
total_size = RGB_MEM;
else
total_size = YUV_MEM;
/* page frame number of the address for a source RGB frame to be stored at. */
pageFrameNo = __phys_to_pfn(cfg->pp_phys_buf);
if (size > total_size) {
printk(KERN_ERR "The size of RGB_MEM mapping is too big\n");
return -EINVAL;
}
if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {
printk(KERN_ERR "Writable RGB_MEM mapping must be shared\n");
return -EINVAL;
}
if (remap_pfn_range(vma, vma->vm_start, pageFrameNo + vma->vm_pgoff, size, vma->vm_page_prot))
return -EINVAL;
return 0;
}
static unsigned int s3c_camif_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
camif_cfg_t *cfg = file->private_data;
poll_wait(file, &cfg->waitq, wait);
if (cfg->status == CAMIF_INT_HAPPEN)
mask = POLLIN | POLLRDNORM;
cfg->status = CAMIF_STOPPED;
return mask;
}
struct file_operations camif_c_fops = {
.owner = THIS_MODULE,
.open = s3c_camif_open,
.release = s3c_camif_release,
.ioctl = s3c_camif_ioctl,
.read = s3c_camif_read,
.write = s3c_camif_write,
.mmap = s3c_camif_mmap,
.poll = s3c_camif_poll,
};
struct file_operations camif_p_fops = {
.owner = THIS_MODULE,
.open = s3c_camif_open,
.release = s3c_camif_release,
.ioctl = s3c_camif_ioctl,
.read = s3c_camif_read,
.write = s3c_camif_write,
.mmap = s3c_camif_mmap,
.poll = s3c_camif_poll,
};
/*************************************************************************
* Templates for V4L2
************************************************************************/
void camif_vdev_release (struct video_device *vdev) {
kfree(vdev);
}
struct video_device codec_template = {
.name = CODEC_DEV_NAME,
.type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES,
.type2 = V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING,
.hardware = VID_HARDWARE_SAMSUNG_FIMC3X,
.fops = &camif_c_fops,
.release = camif_vdev_release,
.minor = CODEC_MINOR,
};
struct video_device preview_template = {
.name = PREVIEW_DEV_NAME,
.type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES,
.type2 = V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING,
.hardware = VID_HARDWARE_SAMSUNG_FIMC3X,
.fops = &camif_p_fops,
.release = camif_vdev_release,
.minor = PREVIEW_MINOR,
};
/*************************************************************************
* Initialize part
************************************************************************/
void s3c_camif_init_sensor(camif_cfg_t *cfg)
{
camif_cis_t *cis = cfg->cis;
camif_cis_t *initialized_cis;
if (!cis->sensor) {
initialized_cis = (camif_cis_t *) get_initialized_cis();
if (initialized_cis == NULL) {
printk(KERN_ERR "An I2C client for CIS sensor isn't registered\n");
return;
}
cis = cfg->cis = initialized_cis;
cfg->input_channel = 0;
cfg->cis->user++;
}
if (!cis->init_sensor) {
cis->sensor->driver->command(cis->sensor, SENSOR_INIT, NULL);
cis->init_sensor = 1;
#if defined(CONFIG_VIDEO_SAMSUNG_S5K3BA)
cis->sensor->driver->command(cis->sensor, SENSOR_VGA, NULL);
cis->source_x = 640;
cis->source_y = 480;
#elif defined(CONFIG_VIDEO_SAMSUNG_S5K4BA)
cis->sensor->driver->command(cis->sensor, SENSOR_SVGA, NULL);
cis->source_x = 800;
cis->source_y = 600;
#endif
}
cis->sensor->driver->command(cis->sensor, USER_ADD, NULL);
}
static int s3c_camif_init_preview(camif_cfg_t * cfg)
{
cfg->target_x = PREVIEW_DEFAULT_WIDTH;
cfg->target_y = PREVIEW_DEFAULT_WIDTH;
cfg->pp_num = PREVIEW_DEFAULT_PPNUM;
cfg->dma_type = CAMIF_PREVIEW;
cfg->input_channel = CAMERA_INPUT;
cfg->src_fmt = CAMIF_YCBCR422;
cfg->output_channel = CAMIF_OUT_PP;
cfg->dst_fmt = CAMIF_RGB16;
cfg->flip = CAMIF_FLIP_Y;
cfg->v = &preview_template;
init_MUTEX((struct semaphore *) &cfg->v->lock);
init_waitqueue_head(&cfg->waitq);
cfg->status = CAMIF_STOPPED;
/* To get the handle of CODEC */
cfg->other = s3c_camif_get_fimc_object(CODEC_MINOR);
return cfg->status;
}
static int s3c_camif_init_codec(camif_cfg_t * cfg)
{
cfg->target_x = CODEC_DEFAULT_WIDTH;
cfg->target_y = CODEC_DEFAULT_HEIGHT;
cfg->pp_num = CODEC_DEFAULT_PPNUM;
cfg->dma_type = CAMIF_CODEC;
cfg->src_fmt = CAMIF_YCBCR422;
cfg->input_channel = CAMERA_INPUT;
cfg->dst_fmt = CAMIF_YCBCR420;
cfg->output_channel = CAMIF_OUT_PP;
cfg->flip = CAMIF_FLIP_X;
cfg->v = &codec_template;
init_MUTEX((struct semaphore *) &cfg->v->lock);
init_waitqueue_head(&cfg->waitq);
cfg->status = CAMIF_STOPPED;
/* To get the handle of PREVIEW */
cfg->other = s3c_camif_get_fimc_object(PREVIEW_MINOR);
return cfg->status;
}
static int s3c_camif_probe(struct platform_device *pdev)
{
struct resource *res;
camif_cfg_t *codec, *preview;
int ret = 0;
/* Initialize fimc objects */
codec = s3c_camif_get_fimc_object(CODEC_MINOR);
preview = s3c_camif_get_fimc_object(PREVIEW_MINOR);
memset(codec, 0, sizeof(camif_cfg_t));
memset(preview, 0, sizeof(camif_cfg_t));
/* Set the fimc name */
strcpy(codec->shortname, CODEC_DEV_NAME);
strcpy(preview->shortname, PREVIEW_DEV_NAME);
/* get resource for io memory */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
printk("Failed to get io memory region resouce.\n");
return -1;
}
/* request mem region */
res = request_mem_region(res->start, res->end - res->start + 1, pdev->name);
if (!res) {
printk("Failed to request io memory region.\n");
return -1;
}
/* ioremap for register block */
codec->regs = preview->regs = ioremap(res->start, res->end - res->start + 1);
if (codec->regs == NULL) {
printk(KERN_ERR "Failed to remap register block\n");
return -1;
}
/* ioremap for reserved memory */
codec->pp_phys_buf = PHYS_OFFSET + (MEM_SIZE - RESERVED_MEM);
codec->pp_virt_buf = ioremap_nocache(codec->pp_phys_buf, YUV_MEM);
preview->pp_phys_buf = PHYS_OFFSET + (MEM_SIZE - RESERVED_MEM) + YUV_MEM;
preview->pp_virt_buf = ioremap_nocache(preview->pp_phys_buf, RGB_MEM);
/* Device init */
s3c_camif_init();
s3c_camif_init_codec(codec);
s3c_camif_init_preview(preview);
/* Set irq */
codec->irq = platform_get_irq(pdev, FIMC_CODEC_INDEX);
preview->irq = platform_get_irq(pdev, FIMC_PREVIEW_INDEX);
s3c_camif_request_irq(codec);
s3c_camif_request_irq(preview);
/* Register to video device */
if (video_register_device(codec->v, VFL_TYPE_GRABBER, CODEC_MINOR) != 0) {
printk(KERN_ERR "Couldn't register this codec driver\n");
return -1;
}
if (video_register_device(preview->v, VFL_TYPE_GRABBER, PREVIEW_MINOR) != 0) {
printk(KERN_ERR "Couldn't register this preview driver\n");
return -1;
}
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
cam_clock = clk_get(&pdev->dev, "camera");
#elif defined(CONFIG_CPU_S3C2443) || defined(CONFIG_CPU_S3C2416) || defined(CONFIG_CPU_S3C2450)
cam_clock = clk_get(&pdev->dev, "camif-upll");
#else
#error cam_clock should be defined
#endif
if (IS_ERR(cam_clock)) {
printk("Failed to find camera clock source\n");
ret = PTR_ERR(cam_clock);
}
clk_enable(cam_clock);
/* Print banner */
printk(KERN_INFO "S3C FIMC v%s\n", FIMC_VER);
return 0;
}
static int s3c_camif_remove(struct platform_device *pdev)
{
camif_cfg_t *codec, *preview;
codec = s3c_camif_get_fimc_object(CODEC_MINOR);
preview = s3c_camif_get_fimc_object(PREVIEW_MINOR);
s3c_camif_release_irq(codec);
s3c_camif_release_irq(preview);
iounmap(codec->pp_virt_buf);
codec->pp_virt_buf = 0;
iounmap(preview->pp_virt_buf);
preview->pp_virt_buf = 0;
video_unregister_device(codec->v);
video_unregister_device(preview->v);
s3c_camif_set_priority(0);
clk_disable(cam_clock);
memset(codec, 0, sizeof(camif_cfg_t));
memset(preview, 0, sizeof(camif_cfg_t));
return 0;
}
static struct platform_driver s3c_camif_driver =
{
.probe = s3c_camif_probe,
.remove = s3c_camif_remove,
.driver = {
.name = "s3c-camif",
.owner = THIS_MODULE,
},
};
static int s3c_camif_register(void)
{
platform_driver_register(&s3c_camif_driver);
return 0;
}
static void s3c_camif_unregister(void)
{
platform_driver_unregister(&s3c_camif_driver);
}
void s3c_camif_open_sensor(camif_cis_t *cis)
{
clk_set_rate(cam_clock, cis->camclk);
s3c_camif_reset(cis->reset_type, cis->reset_udelay);
}
void s3c_camif_register_sensor(struct i2c_client *ptr)
{
camif_cfg_t *codec, *preview;
camif_cis_t *cis = (camif_cis_t *) ptr->data;
codec = s3c_camif_get_fimc_object(CODEC_MINOR);
preview = s3c_camif_get_fimc_object(PREVIEW_MINOR);
codec->cis = preview->cis = cis;
sema_init((struct semaphore *) &codec->cis->lock, 1);
sema_init((struct semaphore *) &preview->cis->lock, 1);
preview->cis->status |= P_NOT_WORKING; /* Default Value */
s3c_camif_set_polarity(preview);
s3c_camif_set_source_format(cis);
s3c_camif_set_priority(1);
}
void s3c_camif_unregister_sensor(struct i2c_client *ptr)
{
camif_cis_t *cis;
cis = (camif_cis_t *) (ptr->data);
cis->init_sensor = 0;
}
module_init(s3c_camif_register);
module_exit(s3c_camif_unregister);
EXPORT_SYMBOL(s3c_camif_register_sensor);
EXPORT_SYMBOL(s3c_camif_unregister_sensor);
MODULE_AUTHOR("Jinsung Yang <jsgood.yang@samsung.com>");
MODULE_DESCRIPTION("S3C Camera Driver for FIMC Interface");
MODULE_LICENSE("GPL");