linux/drivers/media/platform/davinci/dm644x_ccdc.c
Thomas Gleixner c942fddf87 treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 157
Based on 3 normalized pattern(s):

  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

  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 [author] [kishon] [vijay] [abraham]
  [i] [kishon]@[ti] [com] 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

  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 [author] [graeme] [gregory]
  [gg]@[slimlogic] [co] [uk] [author] [kishon] [vijay] [abraham] [i]
  [kishon]@[ti] [com] [based] [on] [twl6030]_[usb] [c] [author] [hema]
  [hk] [hemahk]@[ti] [com] 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

extracted by the scancode license scanner the SPDX license identifier

  GPL-2.0-or-later

has been chosen to replace the boilerplate/reference in 1105 file(s).

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Allison Randal <allison@lohutok.net>
Reviewed-by: Richard Fontana <rfontana@redhat.com>
Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org>
Cc: linux-spdx@vger.kernel.org
Link: https://lkml.kernel.org/r/20190527070033.202006027@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-05-30 11:26:37 -07:00

881 lines
26 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2006-2009 Texas Instruments Inc
*
* CCDC hardware module for DM6446
* ------------------------------
*
* This module is for configuring CCD controller of DM6446 VPFE to capture
* Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
* such as Defect Pixel Correction, Color Space Conversion etc to
* pre-process the Raw Bayer RGB data, before writing it to SDRAM.
* This file is named DM644x so that other variants such DM6443
* may be supported using the same module.
*
* TODO: Test Raw bayer parameter settings and bayer capture
* Split module parameter structure to module specific ioctl structs
* investigate if enum used for user space type definition
* to be replaced by #defines or integer
*/
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/videodev2.h>
#include <linux/gfp.h>
#include <linux/err.h>
#include <linux/module.h>
#include <media/davinci/dm644x_ccdc.h>
#include <media/davinci/vpss.h>
#include "dm644x_ccdc_regs.h"
#include "ccdc_hw_device.h"
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CCDC Driver for DM6446");
MODULE_AUTHOR("Texas Instruments");
static struct ccdc_oper_config {
struct device *dev;
/* CCDC interface type */
enum vpfe_hw_if_type if_type;
/* Raw Bayer configuration */
struct ccdc_params_raw bayer;
/* YCbCr configuration */
struct ccdc_params_ycbcr ycbcr;
/* ccdc base address */
void __iomem *base_addr;
} ccdc_cfg = {
/* Raw configurations */
.bayer = {
.pix_fmt = CCDC_PIXFMT_RAW,
.frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
.win = CCDC_WIN_VGA,
.fid_pol = VPFE_PINPOL_POSITIVE,
.vd_pol = VPFE_PINPOL_POSITIVE,
.hd_pol = VPFE_PINPOL_POSITIVE,
.config_params = {
.data_sz = CCDC_DATA_10BITS,
},
},
.ycbcr = {
.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
.frm_fmt = CCDC_FRMFMT_INTERLACED,
.win = CCDC_WIN_PAL,
.fid_pol = VPFE_PINPOL_POSITIVE,
.vd_pol = VPFE_PINPOL_POSITIVE,
.hd_pol = VPFE_PINPOL_POSITIVE,
.bt656_enable = 1,
.pix_order = CCDC_PIXORDER_CBYCRY,
.buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
},
};
#define CCDC_MAX_RAW_YUV_FORMATS 2
/* Raw Bayer formats */
static u32 ccdc_raw_bayer_pix_formats[] =
{V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
/* Raw YUV formats */
static u32 ccdc_raw_yuv_pix_formats[] =
{V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
/* CCDC Save/Restore context */
static u32 ccdc_ctx[CCDC_REG_END / sizeof(u32)];
/* register access routines */
static inline u32 regr(u32 offset)
{
return __raw_readl(ccdc_cfg.base_addr + offset);
}
static inline void regw(u32 val, u32 offset)
{
__raw_writel(val, ccdc_cfg.base_addr + offset);
}
static void ccdc_enable(int flag)
{
regw(flag, CCDC_PCR);
}
static void ccdc_enable_vport(int flag)
{
if (flag)
/* enable video port */
regw(CCDC_ENABLE_VIDEO_PORT, CCDC_FMTCFG);
else
regw(CCDC_DISABLE_VIDEO_PORT, CCDC_FMTCFG);
}
/*
* ccdc_setwin()
* This function will configure the window size
* to be capture in CCDC reg
*/
static void ccdc_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 val = 0, mid_img = 0;
dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_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;
regw((horz_start << CCDC_HORZ_INFO_SPH_SHIFT) | horz_nr_pixels,
CCDC_HORZ_INFO);
vert_start = image_win->top;
if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
vert_nr_lines = (image_win->height >> 1) - 1;
vert_start >>= 1;
/* Since first line doesn't have any data */
vert_start += 1;
/* configure VDINT0 */
val = (vert_start << CCDC_VDINT_VDINT0_SHIFT);
regw(val, CCDC_VDINT);
} else {
/* Since first line doesn't have any data */
vert_start += 1;
vert_nr_lines = image_win->height - 1;
/*
* configure VDINT0 and VDINT1. VDINT1 will be at half
* of image height
*/
mid_img = vert_start + (image_win->height / 2);
val = (vert_start << CCDC_VDINT_VDINT0_SHIFT) |
(mid_img & CCDC_VDINT_VDINT1_MASK);
regw(val, CCDC_VDINT);
}
regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start,
CCDC_VERT_START);
regw(vert_nr_lines, CCDC_VERT_LINES);
dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin...");
}
static void ccdc_readregs(void)
{
unsigned int val = 0;
val = regr(CCDC_ALAW);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to ALAW...\n", val);
val = regr(CCDC_CLAMP);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to CLAMP...\n", val);
val = regr(CCDC_DCSUB);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to DCSUB...\n", val);
val = regr(CCDC_BLKCMP);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to BLKCMP...\n", val);
val = regr(CCDC_FPC_ADDR);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC_ADDR...\n", val);
val = regr(CCDC_FPC);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC...\n", val);
val = regr(CCDC_FMTCFG);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMTCFG...\n", val);
val = regr(CCDC_COLPTN);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to COLPTN...\n", val);
val = regr(CCDC_FMT_HORZ);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_HORZ...\n", val);
val = regr(CCDC_FMT_VERT);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_VERT...\n", val);
val = regr(CCDC_HSIZE_OFF);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HSIZE_OFF...\n", val);
val = regr(CCDC_SDOFST);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SDOFST...\n", val);
val = regr(CCDC_VP_OUT);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VP_OUT...\n", val);
val = regr(CCDC_SYN_MODE);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SYN_MODE...\n", val);
val = regr(CCDC_HORZ_INFO);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HORZ_INFO...\n", val);
val = regr(CCDC_VERT_START);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_START...\n", val);
val = regr(CCDC_VERT_LINES);
dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val);
}
static int ccdc_close(struct device *dev)
{
return 0;
}
/*
* ccdc_restore_defaults()
* This function will write defaults to all CCDC registers
*/
static void ccdc_restore_defaults(void)
{
int i;
/* disable CCDC */
ccdc_enable(0);
/* set all registers to default value */
for (i = 4; i <= 0x94; i += 4)
regw(0, i);
regw(CCDC_NO_CULLING, CCDC_CULLING);
regw(CCDC_GAMMA_BITS_11_2, CCDC_ALAW);
}
static int ccdc_open(struct device *device)
{
ccdc_restore_defaults();
if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
ccdc_enable_vport(1);
return 0;
}
static void ccdc_sbl_reset(void)
{
vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O);
}
/*
* ccdc_config_ycbcr()
* This function will configure CCDC for YCbCr video capture
*/
static void ccdc_config_ycbcr(void)
{
struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
u32 syn_mode;
dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr...");
/*
* first restore the CCDC registers to default values
* This is important since we assume default values to be set in
* a lot of registers that we didn't touch
*/
ccdc_restore_defaults();
/*
* configure pixel format, frame format, configure video frame
* format, enable output to SDRAM, enable internal timing generator
* and 8bit pack mode
*/
syn_mode = (((params->pix_fmt & CCDC_SYN_MODE_INPMOD_MASK) <<
CCDC_SYN_MODE_INPMOD_SHIFT) |
((params->frm_fmt & CCDC_SYN_FLDMODE_MASK) <<
CCDC_SYN_FLDMODE_SHIFT) | CCDC_VDHDEN_ENABLE |
CCDC_WEN_ENABLE | CCDC_DATA_PACK_ENABLE);
/* setup BT.656 sync mode */
if (params->bt656_enable) {
regw(CCDC_REC656IF_BT656_EN, CCDC_REC656IF);
/*
* configure the FID, VD, HD pin polarity,
* fld,hd pol positive, vd negative, 8-bit data
*/
syn_mode |= CCDC_SYN_MODE_VD_POL_NEGATIVE;
if (ccdc_cfg.if_type == VPFE_BT656_10BIT)
syn_mode |= CCDC_SYN_MODE_10BITS;
else
syn_mode |= CCDC_SYN_MODE_8BITS;
} else {
/* y/c external sync mode */
syn_mode |= (((params->fid_pol & CCDC_FID_POL_MASK) <<
CCDC_FID_POL_SHIFT) |
((params->hd_pol & CCDC_HD_POL_MASK) <<
CCDC_HD_POL_SHIFT) |
((params->vd_pol & CCDC_VD_POL_MASK) <<
CCDC_VD_POL_SHIFT));
}
regw(syn_mode, CCDC_SYN_MODE);
/* configure video window */
ccdc_setwin(&params->win, params->frm_fmt, 2);
/*
* configure the order of y cb cr in SDRAM, and disable latch
* internal register on vsync
*/
if (ccdc_cfg.if_type == VPFE_BT656_10BIT)
regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) |
CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_BW656_10BIT,
CCDC_CCDCFG);
else
regw((params->pix_order << CCDC_CCDCFG_Y8POS_SHIFT) |
CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG);
/*
* configure the horizontal line offset. This should be a
* on 32 byte boundary. So clear LSB 5 bits
*/
regw(((params->win.width * 2 + 31) & ~0x1f), CCDC_HSIZE_OFF);
/* configure the memory line offset */
if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)
/* two fields are interleaved in memory */
regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST);
ccdc_sbl_reset();
dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n");
}
static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
{
u32 val;
if (!bclamp->enable) {
/* configure DCSub */
val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK;
regw(val, CCDC_DCSUB);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to DCSUB...\n", val);
regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to CLAMP...\n");
return;
}
/*
* Configure gain, Start pixel, No of line to be avg,
* No of pixel/line to be avg, & Enable the Black clamping
*/
val = ((bclamp->sgain & CCDC_BLK_SGAIN_MASK) |
((bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) <<
CCDC_BLK_ST_PXL_SHIFT) |
((bclamp->sample_ln & CCDC_BLK_SAMPLE_LINE_MASK) <<
CCDC_BLK_SAMPLE_LINE_SHIFT) |
((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) <<
CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE);
regw(val, CCDC_CLAMP);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to CLAMP...\n", val);
/* If Black clamping is enable then make dcsub 0 */
regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x00000000 to DCSUB...\n");
}
static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
{
u32 val;
val = ((bcomp->b & CCDC_BLK_COMP_MASK) |
((bcomp->gb & CCDC_BLK_COMP_MASK) <<
CCDC_BLK_COMP_GB_COMP_SHIFT) |
((bcomp->gr & CCDC_BLK_COMP_MASK) <<
CCDC_BLK_COMP_GR_COMP_SHIFT) |
((bcomp->r & CCDC_BLK_COMP_MASK) <<
CCDC_BLK_COMP_R_COMP_SHIFT));
regw(val, CCDC_BLKCMP);
}
/*
* ccdc_config_raw()
* This function will configure CCDC for Raw capture mode
*/
static void ccdc_config_raw(void)
{
struct ccdc_params_raw *params = &ccdc_cfg.bayer;
struct ccdc_config_params_raw *config_params =
&ccdc_cfg.bayer.config_params;
unsigned int syn_mode = 0;
unsigned int val;
dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw...");
/* Reset CCDC */
ccdc_restore_defaults();
/* Disable latching function registers on VSYNC */
regw(CCDC_LATCH_ON_VSYNC_DISABLE, CCDC_CCDCFG);
/*
* Configure the vertical sync polarity(SYN_MODE.VDPOL),
* horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity
* (SYN_MODE.FLDPOL), frame format(progressive or interlace),
* data size(SYNMODE.DATSIZ), &pixel format (Input mode), output
* SDRAM, enable internal timing generator
*/
syn_mode =
(((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) |
((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) |
((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) |
((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) |
((config_params->data_sz & CCDC_DATA_SZ_MASK) <<
CCDC_DATA_SZ_SHIFT) |
((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT) |
CCDC_WEN_ENABLE | CCDC_VDHDEN_ENABLE);
/* Enable and configure aLaw register if needed */
if (config_params->alaw.enable) {
val = ((config_params->alaw.gamma_wd &
CCDC_ALAW_GAMMA_WD_MASK) | CCDC_ALAW_ENABLE);
regw(val, CCDC_ALAW);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val);
}
/* Configure video window */
ccdc_setwin(&params->win, params->frm_fmt, CCDC_PPC_RAW);
/* Configure Black Clamp */
ccdc_config_black_clamp(&config_params->blk_clamp);
/* Configure Black level compensation */
ccdc_config_black_compense(&config_params->blk_comp);
/* If data size is 8 bit then pack the data */
if ((config_params->data_sz == CCDC_DATA_8BITS) ||
config_params->alaw.enable)
syn_mode |= CCDC_DATA_PACK_ENABLE;
/* disable video port */
val = CCDC_DISABLE_VIDEO_PORT;
if (config_params->data_sz == CCDC_DATA_8BITS)
val |= (CCDC_DATA_10BITS & CCDC_FMTCFG_VPIN_MASK)
<< CCDC_FMTCFG_VPIN_SHIFT;
else
val |= (config_params->data_sz & CCDC_FMTCFG_VPIN_MASK)
<< CCDC_FMTCFG_VPIN_SHIFT;
/* Write value in FMTCFG */
regw(val, CCDC_FMTCFG);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMTCFG...\n", val);
/* Configure the color pattern according to mt9t001 sensor */
regw(CCDC_COLPTN_VAL, CCDC_COLPTN);
dev_dbg(ccdc_cfg.dev, "\nWriting 0xBB11BB11 to COLPTN...\n");
/*
* Configure Data formatter(Video port) pixel selection
* (FMT_HORZ, FMT_VERT)
*/
val = ((params->win.left & CCDC_FMT_HORZ_FMTSPH_MASK) <<
CCDC_FMT_HORZ_FMTSPH_SHIFT) |
(params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK);
regw(val, CCDC_FMT_HORZ);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_HORZ...\n", val);
val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK)
<< CCDC_FMT_VERT_FMTSLV_SHIFT;
if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
val |= (params->win.height) & CCDC_FMT_VERT_FMTLNV_MASK;
else
val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK;
dev_dbg(ccdc_cfg.dev, "\nparams->win.height 0x%x ...\n",
params->win.height);
regw(val, CCDC_FMT_VERT);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_VERT...\n", val);
dev_dbg(ccdc_cfg.dev, "\nbelow regw(val, FMT_VERT)...");
/*
* Configure Horizontal offset register. If pack 8 is enabled then
* 1 pixel will take 1 byte
*/
if ((config_params->data_sz == CCDC_DATA_8BITS) ||
config_params->alaw.enable)
regw((params->win.width + CCDC_32BYTE_ALIGN_VAL) &
CCDC_HSIZE_OFF_MASK, CCDC_HSIZE_OFF);
else
/* else one pixel will take 2 byte */
regw(((params->win.width * CCDC_TWO_BYTES_PER_PIXEL) +
CCDC_32BYTE_ALIGN_VAL) & CCDC_HSIZE_OFF_MASK,
CCDC_HSIZE_OFF);
/* Set value for SDOFST */
if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
if (params->image_invert_enable) {
/* For intelace inverse mode */
regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x4B6D to SDOFST..\n");
}
else {
/* For intelace non inverse mode */
regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x0249 to SDOFST..\n");
}
} else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to SDOFST...\n");
}
/*
* Configure video port pixel selection (VPOUT)
* Here -1 is to make the height value less than FMT_VERT.FMTLNV
*/
if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE)
val = (((params->win.height - 1) & CCDC_VP_OUT_VERT_NUM_MASK))
<< CCDC_VP_OUT_VERT_NUM_SHIFT;
else
val =
((((params->win.height >> CCDC_INTERLACED_HEIGHT_SHIFT) -
1) & CCDC_VP_OUT_VERT_NUM_MASK)) <<
CCDC_VP_OUT_VERT_NUM_SHIFT;
val |= ((((params->win.width))) & CCDC_VP_OUT_HORZ_NUM_MASK)
<< CCDC_VP_OUT_HORZ_NUM_SHIFT;
val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK;
regw(val, CCDC_VP_OUT);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to VP_OUT...\n", val);
regw(syn_mode, CCDC_SYN_MODE);
dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode);
ccdc_sbl_reset();
dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw...");
ccdc_readregs();
}
static int ccdc_configure(void)
{
if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
ccdc_config_raw();
else
ccdc_config_ycbcr();
return 0;
}
static int ccdc_set_buftype(enum ccdc_buftype buf_type)
{
if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
ccdc_cfg.bayer.buf_type = buf_type;
else
ccdc_cfg.ycbcr.buf_type = buf_type;
return 0;
}
static enum ccdc_buftype ccdc_get_buftype(void)
{
if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
return ccdc_cfg.bayer.buf_type;
return ccdc_cfg.ycbcr.buf_type;
}
static int ccdc_enum_pix(u32 *pix, int i)
{
int ret = -EINVAL;
if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
*pix = ccdc_raw_bayer_pix_formats[i];
ret = 0;
}
} else {
if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) {
*pix = ccdc_raw_yuv_pix_formats[i];
ret = 0;
}
}
return ret;
}
static int ccdc_set_pixel_format(u32 pixfmt)
{
if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
if (pixfmt == V4L2_PIX_FMT_SBGGR8)
ccdc_cfg.bayer.config_params.alaw.enable = 1;
else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
return -EINVAL;
} else {
if (pixfmt == V4L2_PIX_FMT_YUYV)
ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
else if (pixfmt == V4L2_PIX_FMT_UYVY)
ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
else
return -EINVAL;
}
return 0;
}
static u32 ccdc_get_pixel_format(void)
{
struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
u32 pixfmt;
if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
if (alaw->enable)
pixfmt = V4L2_PIX_FMT_SBGGR8;
else
pixfmt = V4L2_PIX_FMT_SBGGR16;
else {
if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
pixfmt = V4L2_PIX_FMT_YUYV;
else
pixfmt = V4L2_PIX_FMT_UYVY;
}
return pixfmt;
}
static int ccdc_set_image_window(struct v4l2_rect *win)
{
if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
ccdc_cfg.bayer.win = *win;
else
ccdc_cfg.ycbcr.win = *win;
return 0;
}
static void ccdc_get_image_window(struct v4l2_rect *win)
{
if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
*win = ccdc_cfg.bayer.win;
else
*win = ccdc_cfg.ycbcr.win;
}
static unsigned int ccdc_get_line_length(void)
{
struct ccdc_config_params_raw *config_params =
&ccdc_cfg.bayer.config_params;
unsigned int len;
if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
if ((config_params->alaw.enable) ||
(config_params->data_sz == CCDC_DATA_8BITS))
len = ccdc_cfg.bayer.win.width;
else
len = ccdc_cfg.bayer.win.width * 2;
} else
len = ccdc_cfg.ycbcr.win.width * 2;
return ALIGN(len, 32);
}
static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
{
if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
ccdc_cfg.bayer.frm_fmt = frm_fmt;
else
ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
return 0;
}
static enum ccdc_frmfmt ccdc_get_frame_format(void)
{
if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
return ccdc_cfg.bayer.frm_fmt;
else
return ccdc_cfg.ycbcr.frm_fmt;
}
static int ccdc_getfid(void)
{
return (regr(CCDC_SYN_MODE) >> 15) & 1;
}
/* misc operations */
static inline void ccdc_setfbaddr(unsigned long addr)
{
regw(addr & 0xffffffe0, CCDC_SDR_ADDR);
}
static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
{
ccdc_cfg.if_type = params->if_type;
switch (params->if_type) {
case VPFE_BT656:
case VPFE_YCBCR_SYNC_16:
case VPFE_YCBCR_SYNC_8:
case VPFE_BT656_10BIT:
ccdc_cfg.ycbcr.vd_pol = params->vdpol;
ccdc_cfg.ycbcr.hd_pol = params->hdpol;
break;
default:
/* TODO add support for raw bayer here */
return -EINVAL;
}
return 0;
}
static void ccdc_save_context(void)
{
ccdc_ctx[CCDC_PCR >> 2] = regr(CCDC_PCR);
ccdc_ctx[CCDC_SYN_MODE >> 2] = regr(CCDC_SYN_MODE);
ccdc_ctx[CCDC_HD_VD_WID >> 2] = regr(CCDC_HD_VD_WID);
ccdc_ctx[CCDC_PIX_LINES >> 2] = regr(CCDC_PIX_LINES);
ccdc_ctx[CCDC_HORZ_INFO >> 2] = regr(CCDC_HORZ_INFO);
ccdc_ctx[CCDC_VERT_START >> 2] = regr(CCDC_VERT_START);
ccdc_ctx[CCDC_VERT_LINES >> 2] = regr(CCDC_VERT_LINES);
ccdc_ctx[CCDC_CULLING >> 2] = regr(CCDC_CULLING);
ccdc_ctx[CCDC_HSIZE_OFF >> 2] = regr(CCDC_HSIZE_OFF);
ccdc_ctx[CCDC_SDOFST >> 2] = regr(CCDC_SDOFST);
ccdc_ctx[CCDC_SDR_ADDR >> 2] = regr(CCDC_SDR_ADDR);
ccdc_ctx[CCDC_CLAMP >> 2] = regr(CCDC_CLAMP);
ccdc_ctx[CCDC_DCSUB >> 2] = regr(CCDC_DCSUB);
ccdc_ctx[CCDC_COLPTN >> 2] = regr(CCDC_COLPTN);
ccdc_ctx[CCDC_BLKCMP >> 2] = regr(CCDC_BLKCMP);
ccdc_ctx[CCDC_FPC >> 2] = regr(CCDC_FPC);
ccdc_ctx[CCDC_FPC_ADDR >> 2] = regr(CCDC_FPC_ADDR);
ccdc_ctx[CCDC_VDINT >> 2] = regr(CCDC_VDINT);
ccdc_ctx[CCDC_ALAW >> 2] = regr(CCDC_ALAW);
ccdc_ctx[CCDC_REC656IF >> 2] = regr(CCDC_REC656IF);
ccdc_ctx[CCDC_CCDCFG >> 2] = regr(CCDC_CCDCFG);
ccdc_ctx[CCDC_FMTCFG >> 2] = regr(CCDC_FMTCFG);
ccdc_ctx[CCDC_FMT_HORZ >> 2] = regr(CCDC_FMT_HORZ);
ccdc_ctx[CCDC_FMT_VERT >> 2] = regr(CCDC_FMT_VERT);
ccdc_ctx[CCDC_FMT_ADDR0 >> 2] = regr(CCDC_FMT_ADDR0);
ccdc_ctx[CCDC_FMT_ADDR1 >> 2] = regr(CCDC_FMT_ADDR1);
ccdc_ctx[CCDC_FMT_ADDR2 >> 2] = regr(CCDC_FMT_ADDR2);
ccdc_ctx[CCDC_FMT_ADDR3 >> 2] = regr(CCDC_FMT_ADDR3);
ccdc_ctx[CCDC_FMT_ADDR4 >> 2] = regr(CCDC_FMT_ADDR4);
ccdc_ctx[CCDC_FMT_ADDR5 >> 2] = regr(CCDC_FMT_ADDR5);
ccdc_ctx[CCDC_FMT_ADDR6 >> 2] = regr(CCDC_FMT_ADDR6);
ccdc_ctx[CCDC_FMT_ADDR7 >> 2] = regr(CCDC_FMT_ADDR7);
ccdc_ctx[CCDC_PRGEVEN_0 >> 2] = regr(CCDC_PRGEVEN_0);
ccdc_ctx[CCDC_PRGEVEN_1 >> 2] = regr(CCDC_PRGEVEN_1);
ccdc_ctx[CCDC_PRGODD_0 >> 2] = regr(CCDC_PRGODD_0);
ccdc_ctx[CCDC_PRGODD_1 >> 2] = regr(CCDC_PRGODD_1);
ccdc_ctx[CCDC_VP_OUT >> 2] = regr(CCDC_VP_OUT);
}
static void ccdc_restore_context(void)
{
regw(ccdc_ctx[CCDC_SYN_MODE >> 2], CCDC_SYN_MODE);
regw(ccdc_ctx[CCDC_HD_VD_WID >> 2], CCDC_HD_VD_WID);
regw(ccdc_ctx[CCDC_PIX_LINES >> 2], CCDC_PIX_LINES);
regw(ccdc_ctx[CCDC_HORZ_INFO >> 2], CCDC_HORZ_INFO);
regw(ccdc_ctx[CCDC_VERT_START >> 2], CCDC_VERT_START);
regw(ccdc_ctx[CCDC_VERT_LINES >> 2], CCDC_VERT_LINES);
regw(ccdc_ctx[CCDC_CULLING >> 2], CCDC_CULLING);
regw(ccdc_ctx[CCDC_HSIZE_OFF >> 2], CCDC_HSIZE_OFF);
regw(ccdc_ctx[CCDC_SDOFST >> 2], CCDC_SDOFST);
regw(ccdc_ctx[CCDC_SDR_ADDR >> 2], CCDC_SDR_ADDR);
regw(ccdc_ctx[CCDC_CLAMP >> 2], CCDC_CLAMP);
regw(ccdc_ctx[CCDC_DCSUB >> 2], CCDC_DCSUB);
regw(ccdc_ctx[CCDC_COLPTN >> 2], CCDC_COLPTN);
regw(ccdc_ctx[CCDC_BLKCMP >> 2], CCDC_BLKCMP);
regw(ccdc_ctx[CCDC_FPC >> 2], CCDC_FPC);
regw(ccdc_ctx[CCDC_FPC_ADDR >> 2], CCDC_FPC_ADDR);
regw(ccdc_ctx[CCDC_VDINT >> 2], CCDC_VDINT);
regw(ccdc_ctx[CCDC_ALAW >> 2], CCDC_ALAW);
regw(ccdc_ctx[CCDC_REC656IF >> 2], CCDC_REC656IF);
regw(ccdc_ctx[CCDC_CCDCFG >> 2], CCDC_CCDCFG);
regw(ccdc_ctx[CCDC_FMTCFG >> 2], CCDC_FMTCFG);
regw(ccdc_ctx[CCDC_FMT_HORZ >> 2], CCDC_FMT_HORZ);
regw(ccdc_ctx[CCDC_FMT_VERT >> 2], CCDC_FMT_VERT);
regw(ccdc_ctx[CCDC_FMT_ADDR0 >> 2], CCDC_FMT_ADDR0);
regw(ccdc_ctx[CCDC_FMT_ADDR1 >> 2], CCDC_FMT_ADDR1);
regw(ccdc_ctx[CCDC_FMT_ADDR2 >> 2], CCDC_FMT_ADDR2);
regw(ccdc_ctx[CCDC_FMT_ADDR3 >> 2], CCDC_FMT_ADDR3);
regw(ccdc_ctx[CCDC_FMT_ADDR4 >> 2], CCDC_FMT_ADDR4);
regw(ccdc_ctx[CCDC_FMT_ADDR5 >> 2], CCDC_FMT_ADDR5);
regw(ccdc_ctx[CCDC_FMT_ADDR6 >> 2], CCDC_FMT_ADDR6);
regw(ccdc_ctx[CCDC_FMT_ADDR7 >> 2], CCDC_FMT_ADDR7);
regw(ccdc_ctx[CCDC_PRGEVEN_0 >> 2], CCDC_PRGEVEN_0);
regw(ccdc_ctx[CCDC_PRGEVEN_1 >> 2], CCDC_PRGEVEN_1);
regw(ccdc_ctx[CCDC_PRGODD_0 >> 2], CCDC_PRGODD_0);
regw(ccdc_ctx[CCDC_PRGODD_1 >> 2], CCDC_PRGODD_1);
regw(ccdc_ctx[CCDC_VP_OUT >> 2], CCDC_VP_OUT);
regw(ccdc_ctx[CCDC_PCR >> 2], CCDC_PCR);
}
static const struct ccdc_hw_device ccdc_hw_dev = {
.name = "DM6446 CCDC",
.owner = THIS_MODULE,
.hw_ops = {
.open = ccdc_open,
.close = ccdc_close,
.reset = ccdc_sbl_reset,
.enable = ccdc_enable,
.set_hw_if_params = ccdc_set_hw_if_params,
.configure = ccdc_configure,
.set_buftype = ccdc_set_buftype,
.get_buftype = ccdc_get_buftype,
.enum_pix = ccdc_enum_pix,
.set_pixel_format = ccdc_set_pixel_format,
.get_pixel_format = ccdc_get_pixel_format,
.set_frame_format = ccdc_set_frame_format,
.get_frame_format = ccdc_get_frame_format,
.set_image_window = ccdc_set_image_window,
.get_image_window = ccdc_get_image_window,
.get_line_length = ccdc_get_line_length,
.setfbaddr = ccdc_setfbaddr,
.getfid = ccdc_getfid,
},
};
static int dm644x_ccdc_probe(struct platform_device *pdev)
{
struct resource *res;
int status = 0;
/*
* first try to register with vpfe. If not correct platform, then we
* don't have to iomap
*/
status = vpfe_register_ccdc_device(&ccdc_hw_dev);
if (status < 0)
return status;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
status = -ENODEV;
goto fail_nores;
}
res = request_mem_region(res->start, resource_size(res), res->name);
if (!res) {
status = -EBUSY;
goto fail_nores;
}
ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
if (!ccdc_cfg.base_addr) {
status = -ENOMEM;
goto fail_nomem;
}
ccdc_cfg.dev = &pdev->dev;
printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
return 0;
fail_nomem:
release_mem_region(res->start, resource_size(res));
fail_nores:
vpfe_unregister_ccdc_device(&ccdc_hw_dev);
return status;
}
static int dm644x_ccdc_remove(struct platform_device *pdev)
{
struct resource *res;
iounmap(ccdc_cfg.base_addr);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res)
release_mem_region(res->start, resource_size(res));
vpfe_unregister_ccdc_device(&ccdc_hw_dev);
return 0;
}
static int dm644x_ccdc_suspend(struct device *dev)
{
/* Save CCDC context */
ccdc_save_context();
/* Disable CCDC */
ccdc_enable(0);
return 0;
}
static int dm644x_ccdc_resume(struct device *dev)
{
/* Restore CCDC context */
ccdc_restore_context();
return 0;
}
static const struct dev_pm_ops dm644x_ccdc_pm_ops = {
.suspend = dm644x_ccdc_suspend,
.resume = dm644x_ccdc_resume,
};
static struct platform_driver dm644x_ccdc_driver = {
.driver = {
.name = "dm644x_ccdc",
.pm = &dm644x_ccdc_pm_ops,
},
.remove = dm644x_ccdc_remove,
.probe = dm644x_ccdc_probe,
};
module_platform_driver(dm644x_ccdc_driver);