/* linux/drivers/media/video/samsung/s3c_fimc_v4l2.c
*
* V4L2 interface support file for Samsung Camera Interface (FIMC) driver
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/videodev2.h>
#include <media/v4l2-ioctl.h>
#include "s3c_fimc.h"
static struct v4l2_input s3c_fimc_input_types[] = {
{
.index = 0,
.name = "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",
.type = V4L2_INPUT_TYPE_MEMORY,
.audioset = 2,
.tuner = 0,
.std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M,
.status = 0,
}
};
static struct v4l2_output s3c_fimc_output_types[] = {
{
.index = 0,
.name = "Memory Output",
.type = V4L2_OUTPUT_TYPE_MEMORY,
.audioset = 0,
.modulator = 0,
.std = 0,
},
{
.index = 1,
.name = "LCD FIFO Output",
.type = V4L2_OUTPUT_TYPE_LCDFIFO,
.audioset = 0,
.modulator = 0,
.std = 0,
}
};
const static struct v4l2_fmtdesc s3c_fimc_capture_formats[] = {
{
.index = 0,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.flags = FORMAT_FLAGS_PLANAR,
.description = "4:2:0, planar, Y-Cb-Cr",
.pixelformat = V4L2_PIX_FMT_YUV420,
},
{
.index = 1,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.flags = FORMAT_FLAGS_PLANAR,
.description = "4:2:2, planar, Y-Cb-Cr",
.pixelformat = V4L2_PIX_FMT_YUV422P,
},
{
.index = 2,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.flags = FORMAT_FLAGS_PACKED,
.description = "4:2:2, packed, YCBYCR",
.pixelformat = V4L2_PIX_FMT_YUYV,
},
{
.index = 3,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.flags = FORMAT_FLAGS_PACKED,
.description = "4:2:2, packed, CBYCRY",
.pixelformat = V4L2_PIX_FMT_UYVY,
}
};
const static struct v4l2_fmtdesc s3c_fimc_overlay_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,
},
};
#define S3C_FIMC_MAX_INPUT_TYPES ARRAY_SIZE(s3c_fimc_input_types)
#define S3C_FIMC_MAX_OUTPUT_TYPES ARRAY_SIZE(s3c_fimc_output_types)
#define S3C_FIMC_MAX_CAPTURE_FORMATS ARRAY_SIZE(s3c_fimc_capture_formats)
#define S3C_FIMC_MAX_OVERLAY_FORMATS ARRAY_SIZE(s3c_fimc_overlay_formats)
static int s3c_fimc_v4l2_querycap(struct file *filp, void *fh,
struct v4l2_capability *cap)
{
struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh;
printk("s3c_fimc_v4l2_querycap 0\n");
strcpy(cap->driver, "Samsung FIMC Driver");
printk("s3c_fimc_v4l2_querycap 1\n");
printk("ctrl->vd->name = %s\n",ctrl->vd->name);
strlcpy(cap->card, ctrl->vd->name, sizeof(cap->card));
printk("s3c_fimc_v4l2_querycap 2\n");
sprintf(cap->bus_info, "FIMC AHB-bus");
printk("s3c_fimc_v4l2_querycap 3\n");
cap->version = 0;
printk("s3c_fimc_v4l2_querycap 4\n");
cap->capabilities = (V4L2_CAP_VIDEO_OVERLAY | \
V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING);
printk("s3c_fimc_v4l2_querycap 5\n");
return 0;
}
static int s3c_fimc_v4l2_g_fbuf(struct file *filp, void *fh,
struct v4l2_framebuffer *fb)
{
struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh;
*fb = ctrl->v4l2.frmbuf;
fb->base = ctrl->v4l2.frmbuf.base;
fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
fb->fmt.pixelformat = ctrl->v4l2.frmbuf.fmt.pixelformat;
fb->fmt.width = ctrl->v4l2.frmbuf.fmt.width;
fb->fmt.height = ctrl->v4l2.frmbuf.fmt.height;
fb->fmt.bytesperline = ctrl->v4l2.frmbuf.fmt.bytesperline;
return 0;
}
static int s3c_fimc_v4l2_s_fbuf(struct file *filp, void *fh,
struct v4l2_framebuffer *fb)
{
struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh;
struct v4l2_framebuffer *frmbuf = &(ctrl->v4l2.frmbuf);
int i, bpp;
for (i = 0; i < S3C_FIMC_MAX_OVERLAY_FORMATS; i++) {
if (s3c_fimc_overlay_formats[i].pixelformat == fb->fmt.pixelformat)
break;
}
if (i == S3C_FIMC_MAX_OVERLAY_FORMATS)
return -EINVAL;
bpp = s3c_fimc_set_output_frame(ctrl, &fb->fmt);
frmbuf->base = fb->base;
frmbuf->flags = fb->flags;
frmbuf->capability = fb->capability;
frmbuf->fmt.width = fb->fmt.width;
frmbuf->fmt.height = fb->fmt.height;
frmbuf->fmt.field = fb->fmt.field;
frmbuf->fmt.pixelformat = fb->fmt.pixelformat;
frmbuf->fmt.bytesperline = fb->fmt.width * bpp / 8;
frmbuf->fmt.sizeimage = fb->fmt.width * frmbuf->fmt.bytesperline;
return 0;
}
static int s3c_fimc_v4l2_enum_fmt_vid_cap(struct file *filp, void *fh,
struct v4l2_fmtdesc *f)
{
struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh;
int index = f->index;
if (index >= S3C_FIMC_MAX_CAPTURE_FORMATS)
return -EINVAL;
memset(f, 0, sizeof(*f));
memcpy(f, ctrl->v4l2.fmtdesc + index, sizeof(*f));
return 0;
}
static int s3c_fimc_v4l2_g_fmt_vid_cap(struct file *filp, void *fh,
struct v4l2_format *f)
{
struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh;
int size = sizeof(struct v4l2_pix_format);
memset(&f->fmt.pix, 0, size);
memcpy(&f->fmt.pix, &(ctrl->v4l2.frmbuf.fmt), size);
return 0;
}
static int s3c_fimc_v4l2_s_fmt_vid_cap(struct file *filp, void *fh,
struct v4l2_format *f)
{
printk("s3c_fimc_v4l2_s_fmt_vid_cap filp=%x,fh=%x,f=%x\n",filp,fh,f);
struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh;
ctrl->v4l2.frmbuf.fmt = f->fmt.pix;
if (f->fmt.pix.priv == V4L2_FMT_IN)
s3c_fimc_set_input_frame(ctrl, &f->fmt.pix);
else
s3c_fimc_set_output_frame(ctrl, &f->fmt.pix);
return 0;
}
static int s3c_fimc_v4l2_try_fmt_vid_cap(struct file *filp, void *fh,
struct v4l2_format *f)
{
return 0;
}
static int s3c_fimc_v4l2_try_fmt_overlay(struct file *filp, void *fh,
struct v4l2_format *f)
{
return 0;
}
static int s3c_fimc_v4l2_overlay(struct file *filp, void *fh, unsigned int i)
{
struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh;
//printk("[CAM]s3c_fimc_v4l2_overlay.\n");
if (i) {
if (ctrl->in_type != PATH_IN_DMA)
s3c_fimc_init_camera(ctrl);
//printk("[CAM]s3c_fimc_init_camera(ctrl);.\n");
FSET_PREVIEW(ctrl);
//printk("[CAM]FSET_PREVIEW(ctrl);.\n");
s3c_fimc_start_dma(ctrl);
//printk("[CAM]s3c_fimc_start_dma(ctrl);.\n");
} else {
s3c_fimc_stop_dma(ctrl);
if (ctrl->out_type != PATH_OUT_LCDFIFO) {
s3c_fimc_free_output_memory(&ctrl->out_frame);
s3c_fimc_set_output_address(ctrl);
}
}
return 0;
}
static int s3c_fimc_v4l2_g_ctrl(struct file *filp, void *fh,
struct v4l2_control *c)
{
struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh;
struct s3c_fimc_out_frame *frame = &ctrl->out_frame;
switch (c->id) {
case V4L2_CID_OUTPUT_ADDR:
c->value = frame->addr[c->value].phys_y;
break;
default:
err("invalid control id: %d\n", c->id);
return -EINVAL;
}
return 0;
}
static int s3c_fimc_v4l2_s_ctrl(struct file *filp, void *fh,
struct v4l2_control *c)
{
struct s3c_fimc_control *ctrl = (struct s3c_fimc_control *) fh;
struct s3c_fimc_out_frame *frame = &ctrl->out_frame;
struct s3c_fimc_window_offset *offset = &ctrl->in_cam->offset;
switch (c->id) {
case V4L2_CID_EFFECT_ORIGINAL:
frame->effect.type = EFFECT_ORIGINAL;
s3c_fimc_change_effect(ctrl);
break;
case V4L2_CID_EFFECT_NEGATIVE:
frame->effect.type = EFFECT_NEGATIVE;
s3c_fimc_change_effect(ctrl);
break;
case V4L2_CID_EFFECT_EMBOSSING:
frame->effect.type = EFFECT_EMBOSSING;
s3c_fimc_change_effect(ctrl);
break;
case V4L2_CID_EFFECT_ARTFREEZE:
frame->effect.type = EFFECT_ARTFREEZE;
s3c_fimc_change_effect(ctrl);
break;
case V4L2_CID_EFFECT_SILHOUETTE: