Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -11,10 +11,14 @@
#ifndef FIMC_CORE_H_
#define FIMC_CORE_H_

/*#define DEBUG*/

#include <linux/types.h>
#include <media/videobuf-core.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-mediabus.h>
#include <media/s3c_fimc.h>
#include <linux/videodev2.h>
#include "regs-fimc.h"

@@ -28,6 +32,8 @@
#define dbg(fmt, args...)
#endif

/* Time to wait for next frame VSYNC interrupt while stopping operation. */
#define FIMC_SHUTDOWN_TIMEOUT ((100*HZ)/1000)
#define NUM_FIMC_CLOCKS 2
#define MODULE_NAME "s5p-fimc"
#define FIMC_MAX_DEVS 3
@@ -36,19 +42,41 @@
#define SCALER_MAX_VRATIO 64
#define DMA_MIN_SIZE 8

enum {
/* FIMC device state flags */
enum fimc_dev_flags {
/* for m2m node */
ST_IDLE,
ST_OUTDMA_RUN,
ST_M2M_PEND,
/* for capture node */
ST_CAPT_PEND,
ST_CAPT_RUN,
ST_CAPT_STREAM,
ST_CAPT_SHUT,
};

#define fimc_m2m_active(dev) test_bit(ST_OUTDMA_RUN, &(dev)->state)
#define fimc_m2m_pending(dev) test_bit(ST_M2M_PEND, &(dev)->state)

#define fimc_capture_running(dev) test_bit(ST_CAPT_RUN, &(dev)->state)
#define fimc_capture_pending(dev) test_bit(ST_CAPT_PEND, &(dev)->state)

#define fimc_capture_active(dev) \
(test_bit(ST_CAPT_RUN, &(dev)->state) || \
test_bit(ST_CAPT_PEND, &(dev)->state))

#define fimc_capture_streaming(dev) \
test_bit(ST_CAPT_STREAM, &(dev)->state)

#define fimc_buf_finish(dev, vid_buf) do { \
spin_lock(&(dev)->irqlock); \
(vid_buf)->vb.state = VIDEOBUF_DONE; \
spin_unlock(&(dev)->irqlock); \
wake_up(&(vid_buf)->vb.done); \
} while (0)

enum fimc_datapath {
FIMC_ITU_CAM_A,
FIMC_ITU_CAM_B,
FIMC_MIPI_CAM,
FIMC_CAMERA,
FIMC_DMA,
FIMC_LCDFIFO,
FIMC_WRITEBACK
@@ -123,20 +151,25 @@ enum fimc_color_fmt {

/**
* struct fimc_fmt - the driver's internal color format data
* @mbus_code: Media Bus pixel code, -1 if not applicable
* @name: format description
* @fourcc: the fourcc code for this format
* @fourcc: the fourcc code for this format, 0 if not applicable
* @color: the corresponding fimc_color_fmt
* @depth: number of bits per pixel
* @depth: driver's private 'number of bits per pixel'
* @buff_cnt: number of physically non-contiguous data planes
* @planes_cnt: number of physically contiguous data planes
*/
struct fimc_fmt {
enum v4l2_mbus_pixelcode mbus_code;
char *name;
u32 fourcc;
u32 color;
u32 depth;
u16 buff_cnt;
u16 planes_cnt;
u16 depth;
u16 flags;
#define FMT_FLAGS_CAM (1 << 0)
#define FMT_FLAGS_M2M (1 << 1)
};

/**
@@ -220,10 +253,14 @@ struct fimc_addr {

/**
* struct fimc_vid_buffer - the driver's video buffer
* @vb: v4l videobuf buffer
* @vb: v4l videobuf buffer
* @paddr: precalculated physical address set
* @index: buffer index for the output DMA engine
*/
struct fimc_vid_buffer {
struct videobuf_buffer vb;
struct fimc_addr paddr;
int index;
};

/**
@@ -273,6 +310,40 @@ struct fimc_m2m_device {
int refcnt;
};

/**
* struct fimc_vid_cap - camera capture device information
* @ctx: hardware context data
* @vfd: video device node for camera capture mode
* @v4l2_dev: v4l2_device struct to manage subdevs
* @sd: pointer to camera sensor subdevice currently in use
* @fmt: Media Bus format configured at selected image sensor
* @pending_buf_q: the pending buffer queue head
* @active_buf_q: the queue head of buffers scheduled in hardware
* @vbq: the capture am video buffer queue
* @active_buf_cnt: number of video buffers scheduled in hardware
* @buf_index: index for managing the output DMA buffers
* @frame_count: the frame counter for statistics
* @reqbufs_count: the number of buffers requested in REQBUFS ioctl
* @input_index: input (camera sensor) index
* @refcnt: driver's private reference counter
*/
struct fimc_vid_cap {
struct fimc_ctx *ctx;
struct video_device *vfd;
struct v4l2_device v4l2_dev;
struct v4l2_subdev *sd;
struct v4l2_mbus_framefmt fmt;
struct list_head pending_buf_q;
struct list_head active_buf_q;
struct videobuf_queue vbq;
int active_buf_cnt;
int buf_index;
unsigned int frame_count;
unsigned int reqbufs_count;
int input_index;
int refcnt;
};

/**
* struct samsung_fimc_variant - camera interface variant information
*
@@ -308,10 +379,12 @@ struct samsung_fimc_variant {
*
* @variant: the variant information for this driver.
* @dev_cnt: number of fimc sub-devices available in SoC
* @lclk_frequency: fimc bus clock frequency
*/
struct samsung_fimc_driverdata {
struct samsung_fimc_variant *variant[FIMC_MAX_DEVS];
int devs_cnt;
unsigned long lclk_frequency;
int devs_cnt;
};

struct fimc_ctx;
@@ -322,27 +395,33 @@ struct fimc_ctx;
* @slock: the spinlock protecting this data structure
* @lock: the mutex protecting this data structure
* @pdev: pointer to the FIMC platform device
* @pdata: pointer to the device platform data
* @id: FIMC device index (0..2)
* @clock[]: the clocks required for FIMC operation
* @regs: the mapped hardware registers
* @regs_res: the resource claimed for IO registers
* @irq: interrupt number of the FIMC subdevice
* @irqlock: spinlock protecting videobuffer queue
* @irq_queue:
* @m2m: memory-to-memory V4L2 device information
* @state: the FIMC device state flags
* @vid_cap: camera capture device information
* @state: flags used to synchronize m2m and capture mode operation
*/
struct fimc_dev {
spinlock_t slock;
struct mutex lock;
struct platform_device *pdev;
struct s3c_platform_fimc *pdata;
struct samsung_fimc_variant *variant;
int id;
struct clk *clock[NUM_FIMC_CLOCKS];
void __iomem *regs;
struct resource *regs_res;
int irq;
spinlock_t irqlock;
wait_queue_head_t irq_queue;
struct fimc_m2m_device m2m;
struct fimc_vid_cap vid_cap;
unsigned long state;
};

@@ -387,6 +466,7 @@ struct fimc_ctx {
struct v4l2_m2m_ctx *m2m_ctx;
};

extern struct videobuf_queue_ops fimc_qops;

static inline int tiled_fmt(struct fimc_fmt *fmt)
{
@@ -433,7 +513,10 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx,
struct fimc_frame *frame;

if (V4L2_BUF_TYPE_VIDEO_OUTPUT == type) {
frame = &ctx->s_frame;
if (ctx->state & FIMC_CTX_M2M)
frame = &ctx->s_frame;
else
return ERR_PTR(-EINVAL);
} else if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) {
frame = &ctx->d_frame;
} else {
@@ -445,6 +528,13 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx,
return frame;
}

static inline u32 fimc_hw_get_frame_index(struct fimc_dev *dev)
{
u32 reg = readl(dev->regs + S5P_CISTATUS);
return (reg & S5P_CISTATUS_FRAMECNT_MASK) >>
S5P_CISTATUS_FRAMECNT_SHIFT;
}

/* -----------------------------------------------------*/
/* fimc-reg.c */
void fimc_hw_reset(struct fimc_dev *fimc);
@@ -462,6 +552,52 @@ void fimc_hw_set_output_path(struct fimc_ctx *ctx);
void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr);
void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr,
int index);
int fimc_hw_set_camera_source(struct fimc_dev *fimc,
struct s3c_fimc_isp_info *cam);
int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f);
int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
struct s3c_fimc_isp_info *cam);
int fimc_hw_set_camera_type(struct fimc_dev *fimc,
struct s3c_fimc_isp_info *cam);

/* -----------------------------------------------------*/
/* fimc-core.c */
int fimc_vidioc_enum_fmt(struct file *file, void *priv,
struct v4l2_fmtdesc *f);
int fimc_vidioc_g_fmt(struct file *file, void *priv,
struct v4l2_format *f);
int fimc_vidioc_try_fmt(struct file *file, void *priv,
struct v4l2_format *f);
int fimc_vidioc_g_crop(struct file *file, void *fh,
struct v4l2_crop *cr);
int fimc_vidioc_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *cr);
int fimc_vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *qc);
int fimc_vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl);

int fimc_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr);
int check_ctrl_val(struct fimc_ctx *ctx, struct v4l2_control *ctrl);
int fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_control *ctrl);

struct fimc_fmt *find_format(struct v4l2_format *f, unsigned int mask);
struct fimc_fmt *find_mbus_format(struct v4l2_mbus_framefmt *f,
unsigned int mask);

int fimc_check_scaler_ratio(struct v4l2_rect *r, struct fimc_frame *f);
int fimc_set_scaler_info(struct fimc_ctx *ctx);
int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags);
int fimc_prepare_addr(struct fimc_ctx *ctx, struct fimc_vid_buffer *buf,
struct fimc_frame *frame, struct fimc_addr *paddr);

/* -----------------------------------------------------*/
/* fimc-capture.c */
int fimc_register_capture_device(struct fimc_dev *fimc);
void fimc_unregister_capture_device(struct fimc_dev *fimc);
int fimc_sensor_sd_init(struct fimc_dev *fimc, int index);
int fimc_vid_cap_buf_queue(struct fimc_dev *fimc,
struct fimc_vid_buffer *fimc_vb);

/* Locking: the caller holds fimc->slock */
static inline void fimc_activate_capture(struct fimc_ctx *ctx)
@@ -478,4 +614,51 @@ static inline void fimc_deactivate_capture(struct fimc_dev *fimc)
fimc_hw_en_lastirq(fimc, false);
}

/*
* Add video buffer to the active buffers queue.
* The caller holds irqlock spinlock.
*/
static inline void active_queue_add(struct fimc_vid_cap *vid_cap,
struct fimc_vid_buffer *buf)
{
buf->vb.state = VIDEOBUF_ACTIVE;
list_add_tail(&buf->vb.queue, &vid_cap->active_buf_q);
vid_cap->active_buf_cnt++;
}

/*
* Pop a video buffer from the capture active buffers queue
* Locking: Need to be called with dev->slock held.
*/
static inline struct fimc_vid_buffer *
active_queue_pop(struct fimc_vid_cap *vid_cap)
{
struct fimc_vid_buffer *buf;
buf = list_entry(vid_cap->active_buf_q.next,
struct fimc_vid_buffer, vb.queue);
list_del(&buf->vb.queue);
vid_cap->active_buf_cnt--;
return buf;
}

/* Add video buffer to the capture pending buffers queue */
static inline void fimc_pending_queue_add(struct fimc_vid_cap *vid_cap,
struct fimc_vid_buffer *buf)
{
buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue, &vid_cap->pending_buf_q);
}

/* Add video buffer to the capture pending buffers queue */
static inline struct fimc_vid_buffer *
pending_queue_pop(struct fimc_vid_cap *vid_cap)
{
struct fimc_vid_buffer *buf;
buf = list_entry(vid_cap->pending_buf_q.next,
struct fimc_vid_buffer, vb.queue);
list_del(&buf->vb.queue);
return buf;
}


#endif /* FIMC_CORE_H_ */
@@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <mach/map.h>
#include <media/s3c_fimc.h>

#include "fimc-core.h"

@@ -176,6 +177,15 @@ static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx)
cfg = S5P_ORIG_SIZE_HOR(frame->f_width);
cfg |= S5P_ORIG_SIZE_VER(frame->f_height);
writel(cfg, dev->regs + S5P_ORGOSIZE);

/* Select color space conversion equation (HD/SD size).*/
cfg = readl(dev->regs + S5P_CIGCTRL);
if (frame->f_width >= 1280) /* HD */
cfg |= S5P_CIGCTRL_CSC_ITU601_709;
else /* SD */
cfg &= ~S5P_CIGCTRL_CSC_ITU601_709;
writel(cfg, dev->regs + S5P_CIGCTRL);

}

void fimc_hw_set_out_dma(struct fimc_ctx *ctx)
@@ -231,19 +241,12 @@ static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable)

void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable)
{
unsigned long flags;
u32 cfg;

spin_lock_irqsave(&dev->slock, flags);

cfg = readl(dev->regs + S5P_CIOCTRL);
u32 cfg = readl(dev->regs + S5P_CIOCTRL);
if (enable)
cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE;
else
cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE;
writel(cfg, dev->regs + S5P_CIOCTRL);

spin_unlock_irqrestore(&dev->slock, flags);
}

static void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
@@ -325,14 +328,18 @@ void fimc_hw_set_scaler(struct fimc_ctx *ctx)
void fimc_hw_en_capture(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
u32 cfg;

cfg = readl(dev->regs + S5P_CIIMGCPT);
/* One shot mode for output DMA or freerun for FIFO. */
if (ctx->out_path == FIMC_DMA)
cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE;
else
cfg &= ~S5P_CIIMGCPT_CPT_FREN_ENABLE;
u32 cfg = readl(dev->regs + S5P_CIIMGCPT);

if (ctx->out_path == FIMC_DMA) {
/* one shot mode */
cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE | S5P_CIIMGCPT_IMGCPTEN;
} else {
/* Continous frame capture mode (freerun). */
cfg &= ~(S5P_CIIMGCPT_CPT_FREN_ENABLE |
S5P_CIIMGCPT_CPT_FRMOD_CNT);
cfg |= S5P_CIIMGCPT_IMGCPTEN;
}

if (ctx->scaler.enabled)
cfg |= S5P_CIIMGCPT_IMGCPTEN_SC;
@@ -523,3 +530,139 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev,
i, paddr->y, paddr->cb, paddr->cr);
} while (index == -1 && ++i < FIMC_MAX_OUT_BUFS);
}

int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
struct s3c_fimc_isp_info *cam)
{
u32 cfg = readl(fimc->regs + S5P_CIGCTRL);

cfg &= ~(S5P_CIGCTRL_INVPOLPCLK | S5P_CIGCTRL_INVPOLVSYNC |
S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC);

if (cam->flags & FIMC_CLK_INV_PCLK)
cfg |= S5P_CIGCTRL_INVPOLPCLK;

if (cam->flags & FIMC_CLK_INV_VSYNC)
cfg |= S5P_CIGCTRL_INVPOLVSYNC;

if (cam->flags & FIMC_CLK_INV_HREF)
cfg |= S5P_CIGCTRL_INVPOLHREF;

if (cam->flags & FIMC_CLK_INV_HSYNC)
cfg |= S5P_CIGCTRL_INVPOLHSYNC;

writel(cfg, fimc->regs + S5P_CIGCTRL);

return 0;
}

int fimc_hw_set_camera_source(struct fimc_dev *fimc,
struct s3c_fimc_isp_info *cam)
{
struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
u32 cfg = 0;

if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) {

switch (fimc->vid_cap.fmt.code) {
case V4L2_MBUS_FMT_YUYV8_2X8:
cfg = S5P_CISRCFMT_ORDER422_YCBYCR;
break;
case V4L2_MBUS_FMT_YVYU8_2X8:
cfg = S5P_CISRCFMT_ORDER422_YCRYCB;
break;
case V4L2_MBUS_FMT_VYUY8_2X8:
cfg = S5P_CISRCFMT_ORDER422_CRYCBY;
break;
case V4L2_MBUS_FMT_UYVY8_2X8:
cfg = S5P_CISRCFMT_ORDER422_CBYCRY;
break;
default:
err("camera image format not supported: %d",
fimc->vid_cap.fmt.code);
return -EINVAL;
}

if (cam->bus_type == FIMC_ITU_601) {
if (cam->bus_width == 8) {
cfg |= S5P_CISRCFMT_ITU601_8BIT;
} else if (cam->bus_width == 16) {
cfg |= S5P_CISRCFMT_ITU601_16BIT;
} else {
err("invalid bus width: %d", cam->bus_width);
return -EINVAL;
}
} /* else defaults to ITU-R BT.656 8-bit */
}

cfg |= S5P_CISRCFMT_HSIZE(f->o_width) | S5P_CISRCFMT_VSIZE(f->o_height);
writel(cfg, fimc->regs + S5P_CISRCFMT);
return 0;
}


int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f)
{
u32 hoff2, voff2;

u32 cfg = readl(fimc->regs + S5P_CIWDOFST);

cfg &= ~(S5P_CIWDOFST_HOROFF_MASK | S5P_CIWDOFST_VEROFF_MASK);
cfg |= S5P_CIWDOFST_OFF_EN |
S5P_CIWDOFST_HOROFF(f->offs_h) |
S5P_CIWDOFST_VEROFF(f->offs_v);

writel(cfg, fimc->regs + S5P_CIWDOFST);

/* See CIWDOFSTn register description in the datasheet for details. */
hoff2 = f->o_width - f->width - f->offs_h;
voff2 = f->o_height - f->height - f->offs_v;
cfg = S5P_CIWDOFST2_HOROFF(hoff2) | S5P_CIWDOFST2_VEROFF(voff2);

writel(cfg, fimc->regs + S5P_CIWDOFST2);
return 0;
}

int fimc_hw_set_camera_type(struct fimc_dev *fimc,
struct s3c_fimc_isp_info *cam)
{
u32 cfg, tmp;
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;

cfg = readl(fimc->regs + S5P_CIGCTRL);

/* Select ITU B interface, disable Writeback path and test pattern. */
cfg &= ~(S5P_CIGCTRL_TESTPAT_MASK | S5P_CIGCTRL_SELCAM_ITU_A |
S5P_CIGCTRL_SELCAM_MIPI | S5P_CIGCTRL_CAMIF_SELWB |
S5P_CIGCTRL_SELCAM_MIPI_A);

if (cam->bus_type == FIMC_MIPI_CSI2) {
cfg |= S5P_CIGCTRL_SELCAM_MIPI;

if (cam->mux_id == 0)
cfg |= S5P_CIGCTRL_SELCAM_MIPI_A;

/* TODO: add remaining supported formats. */
if (vid_cap->fmt.code == V4L2_MBUS_FMT_VYUY8_2X8) {
tmp = S5P_CSIIMGFMT_YCBCR422_8BIT;
} else {
err("camera image format not supported: %d",
vid_cap->fmt.code);
return -EINVAL;
}
writel(tmp | (0x1 << 8), fimc->regs + S5P_CSIIMGFMT);

} else if (cam->bus_type == FIMC_ITU_601 ||
cam->bus_type == FIMC_ITU_656) {
if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */
cfg |= S5P_CIGCTRL_SELCAM_ITU_A;
} else if (cam->bus_type == FIMC_LCD_WB) {
cfg |= S5P_CIGCTRL_CAMIF_SELWB;
} else {
err("invalid camera bus type selected\n");
return -EINVAL;
}
writel(cfg, fimc->regs + S5P_CIGCTRL);

return 0;
}
@@ -0,0 +1,60 @@
/*
* Samsung S5P SoC camera interface driver header
*
* Copyright (c) 2010 Samsung Electronics Co., Ltd
* Author: Sylwester Nawrocki, <s.nawrocki@samsung.com>
*
* 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.
*/

#ifndef S3C_FIMC_H_
#define S3C_FIMC_H_

enum cam_bus_type {
FIMC_ITU_601 = 1,
FIMC_ITU_656,
FIMC_MIPI_CSI2,
FIMC_LCD_WB, /* FIFO link from LCD mixer */
};

#define FIMC_CLK_INV_PCLK (1 << 0)
#define FIMC_CLK_INV_VSYNC (1 << 1)
#define FIMC_CLK_INV_HREF (1 << 2)
#define FIMC_CLK_INV_HSYNC (1 << 3)

struct i2c_board_info;

/**
* struct s3c_fimc_isp_info - image sensor information required for host
* interace configuration.
*
* @board_info: pointer to I2C subdevice's board info
* @bus_type: determines bus type, MIPI, ITU-R BT.601 etc.
* @i2c_bus_num: i2c control bus id the sensor is attached to
* @mux_id: FIMC camera interface multiplexer index (separate for MIPI and ITU)
* @bus_width: camera data bus width in bits
* @flags: flags defining bus signals polarity inversion (High by default)
*/
struct s3c_fimc_isp_info {
struct i2c_board_info *board_info;
enum cam_bus_type bus_type;
u16 i2c_bus_num;
u16 mux_id;
u16 bus_width;
u16 flags;
};


#define FIMC_MAX_CAMIF_CLIENTS 2

/**
* struct s3c_platform_fimc - camera host interface platform data
*
* @isp_info: properties of camera sensor required for host interface setup
*/
struct s3c_platform_fimc {
struct s3c_fimc_isp_info *isp_info[FIMC_MAX_CAMIF_CLIENTS];
};
#endif /* S3C_FIMC_H_ */