// SPDX-License-Identifier: GPL-2.0-or-later 
 | 
/* 
 | 
 * Copyright (C) 2008-2009 Texas Instruments Inc 
 | 
 * 
 | 
 * 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> 
 | 
#include <linux/module.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 const 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 calculation parameters */ 
 | 
        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 calculation parameters */ 
 | 
  
 | 
        /* 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, "\nStarting 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; 
 | 
  
 | 
    val |= (params->data_msb << ISIF_ALAW_GAMMA_WD_SHIFT); 
 | 
    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(¶ms->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, "\nEnd 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; 
 | 
    u32 modeset = 0, ccdcfg = 0; 
 | 
  
 | 
    dev_dbg(isif_cfg.dev, "\nStarting 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(¶ms->win, params->frm_fmt, 1); 
 | 
    else 
 | 
        isif_setwin(¶ms->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); 
 | 
  
 | 
    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; 
 | 
} 
 | 
  
 | 
static const struct ccdc_hw_device isif_hw_dev = { 
 | 
    .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, 
 | 
    }, 
 | 
}; 
 | 
  
 | 
static int isif_probe(struct platform_device *pdev) 
 | 
{ 
 | 
    void (*setup_pinmux)(void); 
 | 
    struct resource    *res; 
 | 
    void __iomem *addr; 
 | 
    int status = 0, i; 
 | 
  
 | 
    /* Platform data holds setup_pinmux function ptr */ 
 | 
    if (!pdev->dev.platform_data) 
 | 
        return -ENODEV; 
 | 
  
 | 
    /* 
 | 
     * 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(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); 
 | 
        if (res) 
 | 
            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; 
 | 
} 
 | 
  
 | 
static struct platform_driver isif_driver = { 
 | 
    .driver = { 
 | 
        .name    = "isif", 
 | 
    }, 
 | 
    .remove = isif_remove, 
 | 
    .probe = isif_probe, 
 | 
}; 
 | 
  
 | 
module_platform_driver(isif_driver); 
 | 
  
 | 
MODULE_LICENSE("GPL"); 
 |