/*
Raw bayer parameter settings and bayer capture
*/
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/videodev2.h>
#include <linux/err.h>
#include <linux/module.h>
#include <mach/mux.h>
#include <media/davinci/isif.h>
#include <media/davinci/vpss.h>
#include "isif_regs.h"
#include "ccdc_hw_device.h"
/* Defaults for module configuration parameters */
static struct isif_config_params_raw isif_config_defaults = {
.linearize = {
.en = 0,
.corr_shft = ISIF_NO_SHIFT,
.scale_fact = {1, 0},
},
.df_csc = {
.df_or_csc = 0,
.csc = {
.en = 0,
},
},
.dfc = {
.en = 0,
},
.bclamp = {
.en = 0,
},
.gain_offset = {
.gain = {
.r_ye = {1, 0},
.gr_cy = {1, 0},
.gb_g = {1, 0},
.b_mg = {1, 0},
},
},
.culling = {
.hcpat_odd = 0xff,
.hcpat_even = 0xff,
.vcpat = 0xff,
},
.compress = {
.alg = ISIF_ALAW,
},
};
/* ISIF operation configuration */
static struct isif_oper_config {
struct device *dev;
enum vpfe_hw_if_type if_type;
struct isif_ycbcr_config ycbcr;
struct isif_params_raw bayer;
enum isif_data_pack data_pack;
/* ISIF base address */
void __iomem *base_addr;
/* ISIF Linear Table 0 */
void __iomem *linear_tbl0_addr;
/* ISIF Linear Table 1 */
void __iomem *linear_tbl1_addr;
} isif_cfg = {
.ycbcr = {
.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
.frm_fmt = CCDC_FRMFMT_INTERLACED,
.win = ISIF_WIN_NTSC,
.fid_pol = VPFE_PINPOL_POSITIVE,
.vd_pol = VPFE_PINPOL_POSITIVE,
.hd_pol = VPFE_PINPOL_POSITIVE,
.pix_order = CCDC_PIXORDER_CBYCRY,
.buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED,
},
.bayer = {
.pix_fmt = CCDC_PIXFMT_RAW,
.frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
.win = ISIF_WIN_VGA,
.fid_pol = VPFE_PINPOL_POSITIVE,
.vd_pol = VPFE_PINPOL_POSITIVE,
.hd_pol = VPFE_PINPOL_POSITIVE,
.gain = {
.r_ye = {1, 0},
.gr_cy = {1, 0},
.gb_g = {1, 0},
.b_mg = {1, 0},
},
.cfa_pat = ISIF_CFA_PAT_MOSAIC,
.data_msb = ISIF_BIT_MSB_11,
.config_params = {
.data_shift = ISIF_NO_SHIFT,
.col_pat_field0 = {
.olop = ISIF_GREEN_BLUE,
.olep = ISIF_BLUE,
.elop = ISIF_RED,
.elep = ISIF_GREEN_RED,
},
.col_pat_field1 = {
.olop = ISIF_GREEN_BLUE,
.olep = ISIF_BLUE,
.elop = ISIF_RED,
.elep = ISIF_GREEN_RED,
},
.test_pat_gen = 0,
},
},
.data_pack = ISIF_DATA_PACK8,
};
/* Raw Bayer formats */
static const u32 isif_raw_bayer_pix_formats[] = {
V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
/* Raw YUV formats */
static const u32 isif_raw_yuv_pix_formats[] = {
V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
/* register access routines */
static inline u32 regr(u32 offset)
{
return __raw_readl(isif_cfg.base_addr + offset);
}
static inline void regw(u32 val, u32 offset)
{
__raw_writel(val, isif_cfg.base_addr + offset);
}
/* reg_modify() - read, modify and write register */
static inline u32 reg_modify(u32 mask, u32 val, u32 offset)
{
u32 new_val = (regr(offset) & ~mask) | (val & mask);
regw(new_val, offset);
return new_val;
}
static inline void regw_lin_tbl(u32 val, u32 offset, int i)
{
if (!i)
__raw_writel(val, isif_cfg.linear_tbl0_addr + offset);
else
__raw_writel(val, isif_cfg.linear_tbl1_addr + offset);
}
static void isif_disable_all_modules(void)
{
/* disable BC */
regw(0, CLAMPCFG);
/* disable vdfc */
regw(0, DFCCTL);
/* disable CSC */
regw(0, CSCCTL);
/* disable linearization */
regw(0, LINCFG0);
/* disable other modules here as they are supported */
}
static void isif_enable(int en)
{
if (!en) {
/* Before disable isif, disable all ISIF modules */
isif_disable_all_modules();
/*
* wait for next VD. Assume lowest scan rate is 12 Hz. So
* 100 msec delay is good enough
*/
msleep(100);
}
reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN);
}
static void isif_enable_output_to_sdram(int en)
{
reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN);
}
static void isif_config_culling(struct isif_cul *cul)
{
u32 val;
/* Horizontal pattern */
val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd;
regw(val, CULH);
/* vertical pattern */
regw(cul->vcpat, CULV);
/* LPF */
reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT,
cul->en_lpf << ISIF_LPF_SHIFT, MODESET);
}
static void isif_config_gain_offset(void)
{
struct isif_gain_offsets_adj *gain_off_p =
&isif_cfg.bayer.config_params.gain_offset;
u32 val;
val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) |
(!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) |
(!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) |
(!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) |
(!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) |
(!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT);
reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD);
val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) |
gain_off_p->gain.r_ye.decimal;
regw(val, CRGAIN);
val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) |
gain_off_p->gain.gr_cy.decimal;
regw(val, CGRGAIN);
val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) |
gain_off_p->gain.gb_g.decimal;
regw(val, CGBGAIN);
val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) |
gain_off_p->gain.b_mg.decimal;
regw(val, CBGAIN);
regw(gain_off_p->offset, COFSTA);
}
static void isif_restore_defaults(void)
{
enum vpss_ccdc_source_sel source = VPSS_CCDCIN;
dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults...");
isif_cfg.bayer.config_params = isif_config_defaults;
/* Enable clock to ISIF, IPIPEIF and BL */
vpss_enable_clock(VPSS_CCDC_CLOCK, 1);
vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1);
vpss_enable_clock(VPSS_BL_CLOCK, 1);
/* Set default offset and gain */
isif_config_gain_offset();
vpss_select_ccdc_source(source);
dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults...");
}
static int isif_open(struct device *device)
{
isif_restore_defaults();
return 0;
}
/* This function will configure the window size to be capture in ISIF reg */
static void isif_setwin(struct v4l2_rect *image_win,
enum ccdc_frmfmt frm_fmt, int ppc)
{
int horz_start, horz_nr_pixels;
int vert_start, vert_nr_lines;
int mid_img = 0;
dev_dbg(isif_cfg.dev, "\nStarting isif_setwin...");
/*
* ppc - per pixel count. indicates how many pixels per cell
* output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
* raw capture this is 1
*/
horz_start = image_win->left << (ppc - 1);
horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1;
/* Writing the horizontal info into the registers */
regw(horz_start & START_PX_HOR_MASK, SPH);
regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH);
vert_start = image_win->top;
if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
vert_nr_lines = (image_win->height >> 1) - 1;
vert_start >>= 1;
/* To account for VD since line 0 doesn't have any data */
vert_start += 1;
} else {
/* To account for VD since line 0 doesn't have any data */
vert_start += 1;
vert_nr_lines = image_win->height - 1;
/* configure VDINT0 and VDINT1 */
mid_img = vert_start + (image_win->height / 2);
regw(mid_img, VDINT1);
}
regw(0, VDINT0);
regw(vert_start & START_VER_ONE_MASK, SLV0);
regw(vert_start & START_VER_TWO_MASK, SLV1);
regw(vert_nr_lines & NUM_LINES_VER, LNV);
}
static void isif_config_bclamp(struct isif_black_clamp *bc)
{
u32 val;
/*
* DC Offset is always added to image data irrespective of bc enable
* status
*/
regw(bc->dc_offset, CLDCOFST);
if (bc->en) {
val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT;
/* Enable BC and horizontal clamp caculation paramaters */
val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT);
regw(val, CLAMPCFG);
if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) {
/*
* Window count for calculation
* Base window selection
* pixel limit
* Horizontal size of window
* vertical size of the window
* Horizontal start position of the window