2010-02-21 15:48:27 -03:00
/*
* Copyright ( C ) 2008 - 2009 Texas Instruments Inc
*
* 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 .
*
* Image Sensor Interface ( ISIF ) driver
*
* This driver is for configuring the ISIF IP available on DM365 or any other
* TI SoCs . This is used for capturing yuv or bayer video or image data
* from a decoder or sensor . This IP is similar to the CCDC IP on DM355
* and DM6446 , but with enhanced or additional ip blocks . The driver
* configures the ISIF upon commands from the vpfe bridge driver through
* ccdc_hw_device interface .
*
* TODO : 1 ) Raw bayer parameter settings and bayer capture
* 2 ) Add support for control ioctl
*/
# 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>
2012-02-08 12:44:30 -03:00
# include <linux/module.h>
2010-02-21 15:48:27 -03:00
# 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 , " \n starting 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 , " \n End 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 , " \n Starting 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
* Vertical start position of the window
*/
val = bc - > horz . win_count_calc |
( ( ! ! bc - > horz . base_win_sel_calc ) < <
ISIF_HORZ_BC_WIN_SEL_SHIFT ) |
( ( ! ! bc - > horz . clamp_pix_limit ) < <
ISIF_HORZ_BC_PIX_LIMIT_SHIFT ) |
( bc - > horz . win_h_sz_calc < <
ISIF_HORZ_BC_WIN_H_SIZE_SHIFT ) |
( bc - > horz . win_v_sz_calc < <
ISIF_HORZ_BC_WIN_V_SIZE_SHIFT ) ;
regw ( val , CLHWIN0 ) ;
regw ( bc - > horz . win_start_h_calc , CLHWIN1 ) ;
regw ( bc - > horz . win_start_v_calc , CLHWIN2 ) ;
}
/* vertical clamp caculation paramaters */
/* Reset clamp value sel for previous line */
val | =
( bc - > vert . reset_val_sel < < ISIF_VERT_BC_RST_VAL_SEL_SHIFT ) |
( bc - > vert . line_ave_coef < < ISIF_VERT_BC_LINE_AVE_COEF_SHIFT ) ;
regw ( val , CLVWIN0 ) ;
/* Optical Black horizontal start position */
regw ( bc - > vert . ob_start_h , CLVWIN1 ) ;
/* Optical Black vertical start position */
regw ( bc - > vert . ob_start_v , CLVWIN2 ) ;
/* Optical Black vertical size for calculation */
regw ( bc - > vert . ob_v_sz_calc , CLVWIN3 ) ;
/* Vertical start position for BC subtraction */
regw ( bc - > vert_start_sub , CLSV ) ;
}
}
static void isif_config_linearization ( struct isif_linearize * linearize )
{
u32 val , i ;
if ( ! linearize - > en ) {
regw ( 0 , LINCFG0 ) ;
return ;
}
/* shift value for correction & enable linearization (set lsb) */
val = ( linearize - > corr_shft < < ISIF_LIN_CORRSFT_SHIFT ) | 1 ;
regw ( val , LINCFG0 ) ;
/* Scale factor */
val = ( ( ! ! linearize - > scale_fact . integer ) < <
ISIF_LIN_SCALE_FACT_INTEG_SHIFT ) |
linearize - > scale_fact . decimal ;
regw ( val , LINCFG1 ) ;
for ( i = 0 ; i < ISIF_LINEAR_TAB_SIZE ; i + + ) {
if ( i % 2 )
regw_lin_tbl ( linearize - > table [ i ] , ( ( i > > 1 ) < < 2 ) , 1 ) ;
else
regw_lin_tbl ( linearize - > table [ i ] , ( ( i > > 1 ) < < 2 ) , 0 ) ;
}
}
static int isif_config_dfc ( struct isif_dfc * vdfc )
{
/* initialize retries to loop for max ~ 250 usec */
u32 val , count , retries = loops_per_jiffy / ( 4000 / HZ ) ;
int i ;
if ( ! vdfc - > en )
return 0 ;
/* Correction mode */
val = ( vdfc - > corr_mode < < ISIF_VDFC_CORR_MOD_SHIFT ) ;
/* Correct whole line or partial */
if ( vdfc - > corr_whole_line )
val | = 1 < < ISIF_VDFC_CORR_WHOLE_LN_SHIFT ;
/* level shift value */
val | = vdfc - > def_level_shift < < ISIF_VDFC_LEVEL_SHFT_SHIFT ;
regw ( val , DFCCTL ) ;
/* Defect saturation level */
regw ( vdfc - > def_sat_level , VDFSATLV ) ;
regw ( vdfc - > table [ 0 ] . pos_vert , DFCMEM0 ) ;
regw ( vdfc - > table [ 0 ] . pos_horz , DFCMEM1 ) ;
if ( vdfc - > corr_mode = = ISIF_VDFC_NORMAL | |
vdfc - > corr_mode = = ISIF_VDFC_HORZ_INTERPOL_IF_SAT ) {
regw ( vdfc - > table [ 0 ] . level_at_pos , DFCMEM2 ) ;
regw ( vdfc - > table [ 0 ] . level_up_pixels , DFCMEM3 ) ;
regw ( vdfc - > table [ 0 ] . level_low_pixels , DFCMEM4 ) ;
}
/* set DFCMARST and set DFCMWR */
val = regr ( DFCMEMCTL ) | ( 1 < < ISIF_DFCMEMCTL_DFCMARST_SHIFT ) | 1 ;
regw ( val , DFCMEMCTL ) ;
count = retries ;
while ( count & & ( regr ( DFCMEMCTL ) & 0x1 ) )
count - - ;
if ( ! count ) {
dev_dbg ( isif_cfg . dev , " defect table write timeout !!! \n " ) ;
return - 1 ;
}
for ( i = 1 ; i < vdfc - > num_vdefects ; i + + ) {
regw ( vdfc - > table [ i ] . pos_vert , DFCMEM0 ) ;
regw ( vdfc - > table [ i ] . pos_horz , DFCMEM1 ) ;
if ( vdfc - > corr_mode = = ISIF_VDFC_NORMAL | |
vdfc - > corr_mode = = ISIF_VDFC_HORZ_INTERPOL_IF_SAT ) {
regw ( vdfc - > table [ i ] . level_at_pos , DFCMEM2 ) ;
regw ( vdfc - > table [ i ] . level_up_pixels , DFCMEM3 ) ;
regw ( vdfc - > table [ i ] . level_low_pixels , DFCMEM4 ) ;
}
val = regr ( DFCMEMCTL ) ;
/* clear DFCMARST and set DFCMWR */
val & = ~ BIT ( ISIF_DFCMEMCTL_DFCMARST_SHIFT ) ;
val | = 1 ;
regw ( val , DFCMEMCTL ) ;
count = retries ;
while ( count & & ( regr ( DFCMEMCTL ) & 0x1 ) )
count - - ;
if ( ! count ) {
dev_err ( isif_cfg . dev ,
" defect table write timeout !!! \n " ) ;
return - 1 ;
}
}
if ( vdfc - > num_vdefects < ISIF_VDFC_TABLE_SIZE ) {
/* Extra cycle needed */
regw ( 0 , DFCMEM0 ) ;
regw ( 0x1FFF , DFCMEM1 ) ;
regw ( 1 , DFCMEMCTL ) ;
}
/* enable VDFC */
reg_modify ( ( 1 < < ISIF_VDFC_EN_SHIFT ) , ( 1 < < ISIF_VDFC_EN_SHIFT ) ,
DFCCTL ) ;
return 0 ;
}
static void isif_config_csc ( struct isif_df_csc * df_csc )
{
u32 val1 = 0 , val2 = 0 , i ;
if ( ! df_csc - > csc . en ) {
regw ( 0 , CSCCTL ) ;
return ;
}
for ( i = 0 ; i < ISIF_CSC_NUM_COEFF ; i + + ) {
if ( ( i % 2 ) = = 0 ) {
/* CSCM - LSB */
val1 = ( df_csc - > csc . coeff [ i ] . integer < <
ISIF_CSC_COEF_INTEG_SHIFT ) |
df_csc - > csc . coeff [ i ] . decimal ;
} else {
/* CSCM - MSB */
val2 = ( df_csc - > csc . coeff [ i ] . integer < <
ISIF_CSC_COEF_INTEG_SHIFT ) |
df_csc - > csc . coeff [ i ] . decimal ;
val2 < < = ISIF_CSCM_MSB_SHIFT ;
val2 | = val1 ;
regw ( val2 , ( CSCM0 + ( ( i - 1 ) < < 1 ) ) ) ;
}
}
/* program the active area */
regw ( df_csc - > start_pix , FMTSPH ) ;
/*
* one extra pixel as required for CSC . Actually number of
* pixel - 1 should be configured in this register . So we
* need to subtract 1 before writing to FMTSPH , but we will
* not do this since csc requires one extra pixel
*/
regw ( df_csc - > num_pixels , FMTLNH ) ;
regw ( df_csc - > start_line , FMTSLV ) ;
/*
* one extra line as required for CSC . See reason documented for
* num_pixels
*/
regw ( df_csc - > num_lines , FMTLNV ) ;
/* Enable CSC */
regw ( 1 , CSCCTL ) ;
}
static int isif_config_raw ( void )
{
struct isif_params_raw * params = & isif_cfg . bayer ;
struct isif_config_params_raw * module_params =
& isif_cfg . bayer . config_params ;
struct vpss_pg_frame_size frame_size ;
struct vpss_sync_pol sync ;
u32 val ;
dev_dbg ( isif_cfg . dev , " \n Starting isif_config_raw.. \n " ) ;
/*
* Configure CCDCFG register : -
* Set CCD Not to swap input since input is RAW data
* Set FID detection function to Latch at V - Sync
* Set WENLOG - isif valid area
* Set TRGSEL
* Set EXTRG
* Packed to 8 or 16 bits
*/
val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC |
ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN |
ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg . data_pack ;
dev_dbg ( isif_cfg . dev , " Writing 0x%x to ...CCDCFG \n " , val ) ;
regw ( val , CCDCFG ) ;
/*
* Configure the vertical sync polarity ( MODESET . VDPOL )
* Configure the horizontal sync polarity ( MODESET . HDPOL )
* Configure frame id polarity ( MODESET . FLDPOL )
* Configure data polarity
* Configure External WEN Selection
* Configure frame format ( progressive or interlace )
* Configure pixel format ( Input mode )
* Configure the data shift
*/
val = ISIF_VDHDOUT_INPUT | ( params - > vd_pol < < ISIF_VD_POL_SHIFT ) |
( params - > hd_pol < < ISIF_HD_POL_SHIFT ) |
( params - > fid_pol < < ISIF_FID_POL_SHIFT ) |
( ISIF_DATAPOL_NORMAL < < ISIF_DATAPOL_SHIFT ) |
( ISIF_EXWEN_DISABLE < < ISIF_EXWEN_SHIFT ) |
( params - > frm_fmt < < ISIF_FRM_FMT_SHIFT ) |
( params - > pix_fmt < < ISIF_INPUT_SHIFT ) |
( params - > config_params . data_shift < < ISIF_DATASFT_SHIFT ) ;
regw ( val , MODESET ) ;
dev_dbg ( isif_cfg . dev , " Writing 0x%x to MODESET... \n " , val ) ;
/*
* Configure GAMMAWD register
* CFA pattern setting
*/
val = params - > cfa_pat < < ISIF_GAMMAWD_CFA_SHIFT ;
/* Gamma msb */
if ( module_params - > compress . alg = = ISIF_ALAW )
val | = ISIF_ALAW_ENABLE ;
2013-03-04 07:24:07 -03:00
val | = ( params - > data_msb < < ISIF_ALAW_GAMMA_WD_SHIFT ) ;
2010-02-21 15:48:27 -03:00
regw ( val , CGAMMAWD ) ;
/* Configure DPCM compression settings */
if ( module_params - > compress . alg = = ISIF_DPCM ) {
val = BIT ( ISIF_DPCM_EN_SHIFT ) |
( module_params - > compress . pred < <
ISIF_DPCM_PREDICTOR_SHIFT ) ;
}
regw ( val , MISC ) ;
/* Configure Gain & Offset */
isif_config_gain_offset ( ) ;
/* Configure Color pattern */
val = ( params - > config_params . col_pat_field0 . olop ) |
( params - > config_params . col_pat_field0 . olep < < 2 ) |
( params - > config_params . col_pat_field0 . elop < < 4 ) |
( params - > config_params . col_pat_field0 . elep < < 6 ) |
( params - > config_params . col_pat_field1 . olop < < 8 ) |
( params - > config_params . col_pat_field1 . olep < < 10 ) |
( params - > config_params . col_pat_field1 . elop < < 12 ) |
( params - > config_params . col_pat_field1 . elep < < 14 ) ;
regw ( val , CCOLP ) ;
dev_dbg ( isif_cfg . dev , " Writing %x to CCOLP ... \n " , val ) ;
/* Configure HSIZE register */
val = ( ! ! params - > horz_flip_en ) < < ISIF_HSIZE_FLIP_SHIFT ;
/* calculate line offset in 32 bytes based on pack value */
if ( isif_cfg . data_pack = = ISIF_PACK_8BIT )
val | = ( ( params - > win . width + 31 ) > > 5 ) ;
else if ( isif_cfg . data_pack = = ISIF_PACK_12BIT )
val | = ( ( ( params - > win . width +
( params - > win . width > > 2 ) ) + 31 ) > > 5 ) ;
else
val | = ( ( ( params - > win . width * 2 ) + 31 ) > > 5 ) ;
regw ( val , HSIZE ) ;
/* Configure SDOFST register */
if ( params - > frm_fmt = = CCDC_FRMFMT_INTERLACED ) {
if ( params - > image_invert_en ) {
/* For interlace inverse mode */
regw ( 0x4B6D , SDOFST ) ;
dev_dbg ( isif_cfg . dev , " Writing 0x4B6D to SDOFST... \n " ) ;
} else {
/* For interlace non inverse mode */
regw ( 0x0B6D , SDOFST ) ;
dev_dbg ( isif_cfg . dev , " Writing 0x0B6D to SDOFST... \n " ) ;
}
} else if ( params - > frm_fmt = = CCDC_FRMFMT_PROGRESSIVE ) {
if ( params - > image_invert_en ) {
/* For progressive inverse mode */
regw ( 0x4000 , SDOFST ) ;
dev_dbg ( isif_cfg . dev , " Writing 0x4000 to SDOFST... \n " ) ;
} else {
/* For progressive non inverse mode */
regw ( 0x0000 , SDOFST ) ;
dev_dbg ( isif_cfg . dev , " Writing 0x0000 to SDOFST... \n " ) ;
}
}
/* Configure video window */
isif_setwin ( & params - > win , params - > frm_fmt , 1 ) ;
/* Configure Black Clamp */
isif_config_bclamp ( & module_params - > bclamp ) ;
/* Configure Vertical Defection Pixel Correction */
if ( isif_config_dfc ( & module_params - > dfc ) < 0 )
return - EFAULT ;
if ( ! module_params - > df_csc . df_or_csc )
/* Configure Color Space Conversion */
isif_config_csc ( & module_params - > df_csc ) ;
isif_config_linearization ( & module_params - > linearize ) ;
/* Configure Culling */
isif_config_culling ( & module_params - > culling ) ;
/* Configure horizontal and vertical offsets(DFC,LSC,Gain) */
regw ( module_params - > horz_offset , DATAHOFST ) ;
regw ( module_params - > vert_offset , DATAVOFST ) ;
/* Setup test pattern if enabled */
if ( params - > config_params . test_pat_gen ) {
/* Use the HD/VD pol settings from user */
sync . ccdpg_hdpol = params - > hd_pol ;
sync . ccdpg_vdpol = params - > vd_pol ;
dm365_vpss_set_sync_pol ( sync ) ;
frame_size . hlpfr = isif_cfg . bayer . win . width ;
frame_size . pplen = isif_cfg . bayer . win . height ;
dm365_vpss_set_pg_frame_size ( frame_size ) ;
vpss_select_ccdc_source ( VPSS_PGLPBK ) ;
}
dev_dbg ( isif_cfg . dev , " \n End of isif_config_ycbcr... \n " ) ;
return 0 ;
}
static int isif_set_buftype ( enum ccdc_buftype buf_type )
{
if ( isif_cfg . if_type = = VPFE_RAW_BAYER )
isif_cfg . bayer . buf_type = buf_type ;
else
isif_cfg . ycbcr . buf_type = buf_type ;
return 0 ;
}
static enum ccdc_buftype isif_get_buftype ( void )
{
if ( isif_cfg . if_type = = VPFE_RAW_BAYER )
return isif_cfg . bayer . buf_type ;
return isif_cfg . ycbcr . buf_type ;
}
static int isif_enum_pix ( u32 * pix , int i )
{
int ret = - EINVAL ;
if ( isif_cfg . if_type = = VPFE_RAW_BAYER ) {
if ( i < ARRAY_SIZE ( isif_raw_bayer_pix_formats ) ) {
* pix = isif_raw_bayer_pix_formats [ i ] ;
ret = 0 ;
}
} else {
if ( i < ARRAY_SIZE ( isif_raw_yuv_pix_formats ) ) {
* pix = isif_raw_yuv_pix_formats [ i ] ;
ret = 0 ;
}
}
return ret ;
}
static int isif_set_pixel_format ( unsigned int pixfmt )
{
if ( isif_cfg . if_type = = VPFE_RAW_BAYER ) {
if ( pixfmt = = V4L2_PIX_FMT_SBGGR8 ) {
if ( ( isif_cfg . bayer . config_params . compress . alg ! =
ISIF_ALAW ) & &
( isif_cfg . bayer . config_params . compress . alg ! =
ISIF_DPCM ) ) {
dev_dbg ( isif_cfg . dev ,
" Either configure A-Law or DPCM \n " ) ;
return - EINVAL ;
}
isif_cfg . data_pack = ISIF_PACK_8BIT ;
} else if ( pixfmt = = V4L2_PIX_FMT_SBGGR16 ) {
isif_cfg . bayer . config_params . compress . alg =
ISIF_NO_COMPRESSION ;
isif_cfg . data_pack = ISIF_PACK_16BIT ;
} else
return - EINVAL ;
isif_cfg . bayer . pix_fmt = CCDC_PIXFMT_RAW ;
} else {
if ( pixfmt = = V4L2_PIX_FMT_YUYV )
isif_cfg . ycbcr . pix_order = CCDC_PIXORDER_YCBYCR ;
else if ( pixfmt = = V4L2_PIX_FMT_UYVY )
isif_cfg . ycbcr . pix_order = CCDC_PIXORDER_CBYCRY ;
else
return - EINVAL ;
isif_cfg . data_pack = ISIF_PACK_8BIT ;
}
return 0 ;
}
static u32 isif_get_pixel_format ( void )
{
u32 pixfmt ;
if ( isif_cfg . if_type = = VPFE_RAW_BAYER )
if ( isif_cfg . bayer . config_params . compress . alg = = ISIF_ALAW | |
isif_cfg . bayer . config_params . compress . alg = = ISIF_DPCM )
pixfmt = V4L2_PIX_FMT_SBGGR8 ;
else
pixfmt = V4L2_PIX_FMT_SBGGR16 ;
else {
if ( isif_cfg . ycbcr . pix_order = = CCDC_PIXORDER_YCBYCR )
pixfmt = V4L2_PIX_FMT_YUYV ;
else
pixfmt = V4L2_PIX_FMT_UYVY ;
}
return pixfmt ;
}
static int isif_set_image_window ( struct v4l2_rect * win )
{
if ( isif_cfg . if_type = = VPFE_RAW_BAYER ) {
isif_cfg . bayer . win . top = win - > top ;
isif_cfg . bayer . win . left = win - > left ;
isif_cfg . bayer . win . width = win - > width ;
isif_cfg . bayer . win . height = win - > height ;
} else {
isif_cfg . ycbcr . win . top = win - > top ;
isif_cfg . ycbcr . win . left = win - > left ;
isif_cfg . ycbcr . win . width = win - > width ;
isif_cfg . ycbcr . win . height = win - > height ;
}
return 0 ;
}
static void isif_get_image_window ( struct v4l2_rect * win )
{
if ( isif_cfg . if_type = = VPFE_RAW_BAYER )
* win = isif_cfg . bayer . win ;
else
* win = isif_cfg . ycbcr . win ;
}
static unsigned int isif_get_line_length ( void )
{
unsigned int len ;
if ( isif_cfg . if_type = = VPFE_RAW_BAYER ) {
if ( isif_cfg . data_pack = = ISIF_PACK_8BIT )
len = ( ( isif_cfg . bayer . win . width ) ) ;
else if ( isif_cfg . data_pack = = ISIF_PACK_12BIT )
len = ( ( ( isif_cfg . bayer . win . width * 2 ) +
( isif_cfg . bayer . win . width > > 2 ) ) ) ;
else
len = ( ( ( isif_cfg . bayer . win . width * 2 ) ) ) ;
} else
len = ( ( ( isif_cfg . ycbcr . win . width * 2 ) ) ) ;
return ALIGN ( len , 32 ) ;
}
static int isif_set_frame_format ( enum ccdc_frmfmt frm_fmt )
{
if ( isif_cfg . if_type = = VPFE_RAW_BAYER )
isif_cfg . bayer . frm_fmt = frm_fmt ;
else
isif_cfg . ycbcr . frm_fmt = frm_fmt ;
return 0 ;
}
static enum ccdc_frmfmt isif_get_frame_format ( void )
{
if ( isif_cfg . if_type = = VPFE_RAW_BAYER )
return isif_cfg . bayer . frm_fmt ;
return isif_cfg . ycbcr . frm_fmt ;
}
static int isif_getfid ( void )
{
return ( regr ( MODESET ) > > 15 ) & 0x1 ;
}
/* misc operations */
static void isif_setfbaddr ( unsigned long addr )
{
regw ( ( addr > > 21 ) & 0x07ff , CADU ) ;
regw ( ( addr > > 5 ) & 0x0ffff , CADL ) ;
}
static int isif_set_hw_if_params ( struct vpfe_hw_if_param * params )
{
isif_cfg . if_type = params - > if_type ;
switch ( params - > if_type ) {
case VPFE_BT656 :
case VPFE_BT656_10BIT :
case VPFE_YCBCR_SYNC_8 :
isif_cfg . ycbcr . pix_fmt = CCDC_PIXFMT_YCBCR_8BIT ;
isif_cfg . ycbcr . pix_order = CCDC_PIXORDER_CBYCRY ;
break ;
case VPFE_BT1120 :
case VPFE_YCBCR_SYNC_16 :
isif_cfg . ycbcr . pix_fmt = CCDC_PIXFMT_YCBCR_16BIT ;
isif_cfg . ycbcr . pix_order = CCDC_PIXORDER_CBYCRY ;
break ;
case VPFE_RAW_BAYER :
isif_cfg . bayer . pix_fmt = CCDC_PIXFMT_RAW ;
break ;
default :
dev_dbg ( isif_cfg . dev , " Invalid interface type \n " ) ;
return - EINVAL ;
}
return 0 ;
}
/* This function will configure ISIF for YCbCr parameters. */
static int isif_config_ycbcr ( void )
{
struct isif_ycbcr_config * params = & isif_cfg . ycbcr ;
struct vpss_pg_frame_size frame_size ;
u32 modeset = 0 , ccdcfg = 0 ;
struct vpss_sync_pol sync ;
dev_dbg ( isif_cfg . dev , " \n Starting isif_config_ycbcr... " ) ;
/* configure pixel format or input mode */
modeset = modeset | ( params - > pix_fmt < < ISIF_INPUT_SHIFT ) |
( params - > frm_fmt < < ISIF_FRM_FMT_SHIFT ) |
( params - > fid_pol < < ISIF_FID_POL_SHIFT ) |
( params - > hd_pol < < ISIF_HD_POL_SHIFT ) |
( params - > vd_pol < < ISIF_VD_POL_SHIFT ) ;
/* pack the data to 8-bit ISIFCFG */
switch ( isif_cfg . if_type ) {
case VPFE_BT656 :
if ( params - > pix_fmt ! = CCDC_PIXFMT_YCBCR_8BIT ) {
dev_dbg ( isif_cfg . dev , " Invalid pix_fmt(input mode) \n " ) ;
return - EINVAL ;
}
modeset | = ( VPFE_PINPOL_NEGATIVE < < ISIF_VD_POL_SHIFT ) ;
regw ( 3 , REC656IF ) ;
ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR ;
break ;
case VPFE_BT656_10BIT :
if ( params - > pix_fmt ! = CCDC_PIXFMT_YCBCR_8BIT ) {
dev_dbg ( isif_cfg . dev , " Invalid pix_fmt(input mode) \n " ) ;
return - EINVAL ;
}
/* setup BT.656, embedded sync */
regw ( 3 , REC656IF ) ;
/* enable 10 bit mode in ccdcfg */
ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR |
ISIF_BW656_ENABLE ;
break ;
case VPFE_BT1120 :
if ( params - > pix_fmt ! = CCDC_PIXFMT_YCBCR_16BIT ) {
dev_dbg ( isif_cfg . dev , " Invalid pix_fmt(input mode) \n " ) ;
return - EINVAL ;
}
regw ( 3 , REC656IF ) ;
break ;
case VPFE_YCBCR_SYNC_8 :
ccdcfg | = ISIF_DATA_PACK8 ;
ccdcfg | = ISIF_YCINSWP_YCBCR ;
if ( params - > pix_fmt ! = CCDC_PIXFMT_YCBCR_8BIT ) {
dev_dbg ( isif_cfg . dev , " Invalid pix_fmt(input mode) \n " ) ;
return - EINVAL ;
}
break ;
case VPFE_YCBCR_SYNC_16 :
if ( params - > pix_fmt ! = CCDC_PIXFMT_YCBCR_16BIT ) {
dev_dbg ( isif_cfg . dev , " Invalid pix_fmt(input mode) \n " ) ;
return - EINVAL ;
}
break ;
default :
/* should never come here */
dev_dbg ( isif_cfg . dev , " Invalid interface type \n " ) ;
return - EINVAL ;
}
regw ( modeset , MODESET ) ;
/* Set up pix order */
ccdcfg | = params - > pix_order < < ISIF_PIX_ORDER_SHIFT ;
regw ( ccdcfg , CCDCFG ) ;
/* configure video window */
if ( ( isif_cfg . if_type = = VPFE_BT1120 ) | |
( isif_cfg . if_type = = VPFE_YCBCR_SYNC_16 ) )
isif_setwin ( & params - > win , params - > frm_fmt , 1 ) ;
else
isif_setwin ( & params - > win , params - > frm_fmt , 2 ) ;
/*
* configure the horizontal line offset
* this is done by rounding up width to a multiple of 16 pixels
* and multiply by two to account for y : cb : cr 4 : 2 : 2 data
*/
regw ( ( ( ( ( params - > win . width * 2 ) + 31 ) & 0xffffffe0 ) > > 5 ) , HSIZE ) ;
/* configure the memory line offset */
if ( ( params - > frm_fmt = = CCDC_FRMFMT_INTERLACED ) & &
( params - > buf_type = = CCDC_BUFTYPE_FLD_INTERLEAVED ) )
/* two fields are interleaved in memory */
regw ( 0x00000249 , SDOFST ) ;
/* Setup test pattern if enabled */
if ( isif_cfg . bayer . config_params . test_pat_gen ) {
sync . ccdpg_hdpol = params - > hd_pol ;
sync . ccdpg_vdpol = params - > vd_pol ;
dm365_vpss_set_sync_pol ( sync ) ;
dm365_vpss_set_pg_frame_size ( frame_size ) ;
}
return 0 ;
}
static int isif_configure ( void )
{
if ( isif_cfg . if_type = = VPFE_RAW_BAYER )
return isif_config_raw ( ) ;
return isif_config_ycbcr ( ) ;
}
static int isif_close ( struct device * device )
{
/* copy defaults to module params */
isif_cfg . bayer . config_params = isif_config_defaults ;
return 0 ;
}
2017-10-17 08:27:25 -04:00
static const struct ccdc_hw_device isif_hw_dev = {
2010-02-21 15:48:27 -03:00
. name = " ISIF " ,
. owner = THIS_MODULE ,
. hw_ops = {
. open = isif_open ,
. close = isif_close ,
. enable = isif_enable ,
. enable_out_to_sdram = isif_enable_output_to_sdram ,
. set_hw_if_params = isif_set_hw_if_params ,
. configure = isif_configure ,
. set_buftype = isif_set_buftype ,
. get_buftype = isif_get_buftype ,
. enum_pix = isif_enum_pix ,
. set_pixel_format = isif_set_pixel_format ,
. get_pixel_format = isif_get_pixel_format ,
. set_frame_format = isif_set_frame_format ,
. get_frame_format = isif_get_frame_format ,
. set_image_window = isif_set_image_window ,
. get_image_window = isif_get_image_window ,
. get_line_length = isif_get_line_length ,
. setfbaddr = isif_setfbaddr ,
. getfid = isif_getfid ,
} ,
} ;
2012-12-21 13:17:53 -08:00
static int isif_probe ( struct platform_device * pdev )
2010-02-21 15:48:27 -03:00
{
void ( * setup_pinmux ) ( void ) ;
struct resource * res ;
2018-04-06 08:02:46 -04:00
void __iomem * addr ;
2010-02-21 15:48:27 -03:00
int status = 0 , i ;
2013-03-22 04:53:12 -03:00
/* Platform data holds setup_pinmux function ptr */
if ( ! pdev - > dev . platform_data )
return - ENODEV ;
2010-02-21 15:48:27 -03:00
/*
* first try to register with vpfe . If not correct platform , then we
* don ' t have to iomap
*/
status = vpfe_register_ccdc_device ( & isif_hw_dev ) ;
if ( status < 0 )
return status ;
setup_pinmux = pdev - > dev . platform_data ;
/*
* setup Mux configuration for ccdc which may be different for
* different SoCs using this CCDC
*/
setup_pinmux ( ) ;
i = 0 ;
/* Get the ISIF base address, linearization table0 and table1 addr. */
while ( i < 3 ) {
res = platform_get_resource ( pdev , IORESOURCE_MEM , i ) ;
if ( ! res ) {
status = - ENODEV ;
goto fail_nobase_res ;
}
res = request_mem_region ( res - > start , resource_size ( res ) ,
res - > name ) ;
if ( ! res ) {
status = - EBUSY ;
goto fail_nobase_res ;
}
addr = ioremap_nocache ( res - > start , resource_size ( res ) ) ;
if ( ! addr ) {
status = - ENOMEM ;
goto fail_base_iomap ;
}
switch ( i ) {
case 0 :
/* ISIF base address */
isif_cfg . base_addr = addr ;
break ;
case 1 :
/* ISIF linear tbl0 address */
isif_cfg . linear_tbl0_addr = addr ;
break ;
default :
/* ISIF linear tbl0 address */
isif_cfg . linear_tbl1_addr = addr ;
break ;
}
i + + ;
}
isif_cfg . dev = & pdev - > dev ;
printk ( KERN_NOTICE " %s is registered with vpfe. \n " ,
isif_hw_dev . name ) ;
return 0 ;
fail_base_iomap :
release_mem_region ( res - > start , resource_size ( res ) ) ;
i - - ;
fail_nobase_res :
if ( isif_cfg . base_addr )
iounmap ( isif_cfg . base_addr ) ;
if ( isif_cfg . linear_tbl0_addr )
iounmap ( isif_cfg . linear_tbl0_addr ) ;
while ( i > = 0 ) {
res = platform_get_resource ( pdev , IORESOURCE_MEM , i ) ;
release_mem_region ( res - > start , resource_size ( res ) ) ;
i - - ;
}
vpfe_unregister_ccdc_device ( & isif_hw_dev ) ;
return status ;
}
static int isif_remove ( struct platform_device * pdev )
{
struct resource * res ;
int i = 0 ;
iounmap ( isif_cfg . base_addr ) ;
iounmap ( isif_cfg . linear_tbl0_addr ) ;
iounmap ( isif_cfg . linear_tbl1_addr ) ;
while ( i < 3 ) {
res = platform_get_resource ( pdev , IORESOURCE_MEM , i ) ;
if ( res )
release_mem_region ( res - > start , resource_size ( res ) ) ;
i + + ;
}
vpfe_unregister_ccdc_device ( & isif_hw_dev ) ;
return 0 ;
}
2012-08-14 01:23:09 -03:00
static struct platform_driver isif_driver = {
2010-02-21 15:48:27 -03:00
. driver = {
. name = " isif " ,
} ,
2012-12-21 13:17:53 -08:00
. remove = isif_remove ,
2010-02-21 15:48:27 -03:00
. probe = isif_probe ,
} ;
2012-01-10 03:21:49 -03:00
module_platform_driver ( isif_driver ) ;
2010-02-21 15:48:27 -03:00
MODULE_LICENSE ( " GPL " ) ;