| /* |
| * ispccdc.c |
| * |
| * Driver Library for CCDC module in TI's OMAP3 Camera ISP |
| * |
| * Copyright (C) 2009 Texas Instruments, Inc. |
| * |
| * Contributors: |
| * Senthilvadivu Guruswamy <svadivu@ti.com> |
| * Pallavi Kulkarni <p-kulkarni@ti.com> |
| * Sergio Aguirre <saaguirre@ti.com> |
| * |
| * This package is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
| * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| */ |
| |
| #include <linux/mutex.h> |
| #include <linux/module.h> |
| #include <linux/uaccess.h> |
| #include <linux/delay.h> |
| #include <linux/device.h> |
| |
| #include "isp.h" |
| #include "ispreg.h" |
| #include "ispccdc.h" |
| |
| #define LSC_TABLE_INIT_SIZE 50052 |
| #define PTR_FREE ((u32)(-ENOMEM)) |
| |
| /* Structure for saving/restoring CCDC module registers*/ |
| static struct isp_reg ispccdc_reg_list[] = { |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HD_VD_WID, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PIX_LINES, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CULLING, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDR_ADDR, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_COLPTN, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_BLKCMP, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC_ADDR, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR0, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR1, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR2, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR3, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR4, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR5, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR6, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_ADDR7, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGEVEN0, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGEVEN1, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGODD0, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PRGODD1, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_INITIAL, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE, 0}, |
| {OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_OFFSET, 0}, |
| {0, ISP_TOK_TERM, 0} |
| }; |
| |
| /** |
| * ispccdc_print_status - Print current CCDC Module register values. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @pipe: Pointer to current ISP pipeline structure. |
| * |
| * Also prints other debug information stored in the CCDC module. |
| **/ |
| static void ispccdc_print_status(struct isp_ccdc_device *isp_ccdc, |
| struct isp_pipeline *pipe) |
| { |
| #ifdef OMAP_ISPCCDC_DEBUG |
| struct device *dev = to_device(isp_ccdc); |
| #endif |
| |
| if (!is_ispccdc_debug_enabled()) |
| return; |
| |
| DPRINTK_ISPCCDC("Module in use =%d\n", isp_ccdc->ccdc_inuse); |
| DPRINTK_ISPCCDC("Accepted CCDC Input (width = %d,Height = %d)\n", |
| pipe->ccdc_in_w, |
| pipe->ccdc_in_h); |
| DPRINTK_ISPCCDC("Accepted CCDC Output (width = %d,Height = %d)\n", |
| pipe->ccdc_out_w, |
| pipe->ccdc_out_h); |
| DPRINTK_ISPCCDC("###CCDC PCR=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_PCR)); |
| DPRINTK_ISPCCDC("ISP_CTRL =0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, |
| ISP_CTRL)); |
| switch (pipe->ccdc_in) { |
| case CCDC_RAW_GRBG: |
| case CCDC_RAW_RGGB: |
| case CCDC_RAW_BGGR: |
| case CCDC_RAW_GBRG: |
| DPRINTK_ISPCCDC("ccdc input format is CCDC_RAW\n"); |
| break; |
| case CCDC_YUV_SYNC: |
| DPRINTK_ISPCCDC("ccdc input format is CCDC_YUV_SYNC\n"); |
| break; |
| case CCDC_YUV_BT: |
| DPRINTK_ISPCCDC("ccdc input format is CCDC_YUV_BT\n"); |
| break; |
| default: |
| break; |
| } |
| |
| switch (pipe->ccdc_out) { |
| case CCDC_OTHERS_VP: |
| DPRINTK_ISPCCDC("ccdc output format is CCDC_OTHERS_VP\n"); |
| break; |
| case CCDC_OTHERS_MEM: |
| DPRINTK_ISPCCDC("ccdc output format is CCDC_OTHERS_MEM\n"); |
| break; |
| case CCDC_YUV_RSZ: |
| DPRINTK_ISPCCDC("ccdc output format is CCDC_YUV_RSZ\n"); |
| break; |
| default: |
| break; |
| } |
| |
| DPRINTK_ISPCCDC("###ISP_CTRL in ccdc =0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, |
| ISP_CTRL)); |
| DPRINTK_ISPCCDC("###ISP_IRQ0ENABLE in ccdc =0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, |
| ISP_IRQ0ENABLE)); |
| DPRINTK_ISPCCDC("###ISP_IRQ0STATUS in ccdc =0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_MAIN, |
| ISP_IRQ0STATUS)); |
| DPRINTK_ISPCCDC("###CCDC SYN_MODE=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SYN_MODE)); |
| DPRINTK_ISPCCDC("###CCDC HORZ_INFO=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_HORZ_INFO)); |
| DPRINTK_ISPCCDC("###CCDC VERT_START=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_VERT_START)); |
| DPRINTK_ISPCCDC("###CCDC VERT_LINES=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_VERT_LINES)); |
| DPRINTK_ISPCCDC("###CCDC CULLING=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_CULLING)); |
| DPRINTK_ISPCCDC("###CCDC HSIZE_OFF=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_HSIZE_OFF)); |
| DPRINTK_ISPCCDC("###CCDC SDOFST=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SDOFST)); |
| DPRINTK_ISPCCDC("###CCDC SDR_ADDR=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SDR_ADDR)); |
| DPRINTK_ISPCCDC("###CCDC CLAMP=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_CLAMP)); |
| DPRINTK_ISPCCDC("###CCDC COLPTN=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_COLPTN)); |
| DPRINTK_ISPCCDC("###CCDC CFG=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_CFG)); |
| DPRINTK_ISPCCDC("###CCDC VP_OUT=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_VP_OUT)); |
| DPRINTK_ISPCCDC("###CCDC_SDR_ADDR= 0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SDR_ADDR)); |
| DPRINTK_ISPCCDC("###CCDC FMTCFG=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_FMTCFG)); |
| DPRINTK_ISPCCDC("###CCDC FMT_HORZ=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_FMT_HORZ)); |
| DPRINTK_ISPCCDC("###CCDC FMT_VERT=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_FMT_VERT)); |
| DPRINTK_ISPCCDC("###CCDC LSC_CONFIG=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_LSC_CONFIG)); |
| DPRINTK_ISPCCDC("###CCDC LSC_INIT=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_LSC_INITIAL)); |
| DPRINTK_ISPCCDC("###CCDC LSC_TABLE BASE=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_LSC_TABLE_BASE)); |
| DPRINTK_ISPCCDC("###CCDC LSC TABLE OFFSET=0x%x\n", |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_LSC_TABLE_OFFSET)); |
| } |
| |
| /** |
| * ispccdc_config_black_clamp - Configures the clamp parameters in CCDC. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @bclamp: Structure containing the optical black average gain, optical black |
| * sample length, sample lines, and the start pixel position of the |
| * samples w.r.t the HS pulse. |
| * |
| * Configures the clamp parameters in CCDC. Either if its being used the |
| * optical black clamp, or the digital clamp. If its a digital clamp, then |
| * assures to put a valid DC substraction level. |
| * |
| * Returns always 0 when completed. |
| **/ |
| static int ispccdc_config_black_clamp(struct isp_ccdc_device *isp_ccdc, |
| struct ispccdc_bclamp bclamp) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| u32 bclamp_val = 0; |
| |
| if (isp_ccdc->obclamp_en) { |
| bclamp_val |= bclamp.obgain << ISPCCDC_CLAMP_OBGAIN_SHIFT; |
| bclamp_val |= bclamp.oblen << ISPCCDC_CLAMP_OBSLEN_SHIFT; |
| bclamp_val |= bclamp.oblines << ISPCCDC_CLAMP_OBSLN_SHIFT; |
| bclamp_val |= bclamp.obstpixel << ISPCCDC_CLAMP_OBST_SHIFT; |
| isp_reg_writel(dev, bclamp_val, |
| OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP); |
| } else { |
| if (omap_rev() < OMAP3430_REV_ES2_0) |
| if (isp_ccdc->syncif_ipmod == YUV16 || |
| isp_ccdc->syncif_ipmod == YUV8 || |
| isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_REC656IF) & |
| ISPCCDC_REC656IF_R656ON) |
| bclamp.dcsubval = 0; |
| isp_reg_writel(dev, bclamp.dcsubval, |
| OMAP3_ISP_IOMEM_CCDC, ISPCCDC_DCSUB); |
| } |
| return 0; |
| } |
| |
| /** |
| * ispccdc_enable_black_clamp - Enables/Disables the optical black clamp. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @enable: 0 Disables optical black clamp, 1 Enables optical black clamp. |
| * |
| * Enables or disables the optical black clamp. When disabled, the digital |
| * clamp operates. |
| **/ |
| static void ispccdc_enable_black_clamp(struct isp_ccdc_device *isp_ccdc, |
| u8 enable) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| isp_reg_and_or(dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CLAMP, |
| ~ISPCCDC_CLAMP_CLAMPEN, |
| enable ? ISPCCDC_CLAMP_CLAMPEN : 0); |
| isp_ccdc->obclamp_en = enable; |
| } |
| |
| /** |
| * ispccdc_config_fpc - Configures the Faulty Pixel Correction parameters. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @fpc: Structure containing the number of faulty pixels corrected in the |
| * frame, address of the FPC table. |
| * |
| * Returns 0 if successful, or -EINVAL if FPC Address is not on the 64 byte |
| * boundary. |
| **/ |
| static int ispccdc_config_fpc(struct isp_ccdc_device *isp_ccdc, |
| struct ispccdc_fpc fpc) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| u32 fpc_val = 0; |
| |
| fpc_val = isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_FPC); |
| |
| if ((fpc.fpcaddr & 0xFFFFFFC0) == fpc.fpcaddr) { |
| isp_reg_writel(dev, fpc_val & (~ISPCCDC_FPC_FPCEN), |
| OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); |
| isp_reg_writel(dev, fpc.fpcaddr, |
| OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC_ADDR); |
| } else { |
| DPRINTK_ISPCCDC("FPC Address should be on 64byte boundary\n"); |
| return -EINVAL; |
| } |
| isp_reg_writel(dev, fpc_val | |
| (fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT), |
| OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC); |
| return 0; |
| } |
| |
| /** |
| * ispccdc_enable_fpc - Enable Faulty Pixel Correction. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @enable: 0 Disables FPC, 1 Enables FPC. |
| **/ |
| static void ispccdc_enable_fpc(struct isp_ccdc_device *isp_ccdc, u8 enable) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| isp_reg_and_or(dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC, |
| ~ISPCCDC_FPC_FPCEN, |
| enable ? ISPCCDC_FPC_FPCEN : 0); |
| } |
| |
| /** |
| * ispccdc_config_black_comp - Configure Black Level Compensation. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @blcomp: Structure containing the black level compensation value for RGrGbB |
| * pixels. in 2's complement. |
| **/ |
| static void ispccdc_config_black_comp(struct isp_ccdc_device *isp_ccdc, |
| struct ispccdc_blcomp blcomp) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| u32 blcomp_val = 0; |
| |
| blcomp_val |= blcomp.b_mg << ISPCCDC_BLKCMP_B_MG_SHIFT; |
| blcomp_val |= blcomp.gb_g << ISPCCDC_BLKCMP_GB_G_SHIFT; |
| blcomp_val |= blcomp.gr_cy << ISPCCDC_BLKCMP_GR_CY_SHIFT; |
| blcomp_val |= blcomp.r_ye << ISPCCDC_BLKCMP_R_YE_SHIFT; |
| |
| isp_reg_writel(dev, blcomp_val, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_BLKCMP); |
| } |
| |
| /** |
| * ispccdc_config_vp - Configure the Video Port. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @vpcfg: Structure containing the Video Port input frequency, and the 10 bit |
| * format. |
| **/ |
| static void ispccdc_config_vp(struct isp_ccdc_device *isp_ccdc, |
| struct ispccdc_vp vpcfg) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| u32 fmtcfg_vp = isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_FMTCFG); |
| |
| fmtcfg_vp &= ISPCCDC_FMTCFG_VPIN_MASK & ISPCCDC_FMTCFG_VPIF_FRQ_MASK; |
| |
| switch (vpcfg.bitshift_sel) { |
| case BIT9_0: |
| fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0; |
| break; |
| case BIT10_1: |
| fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1; |
| break; |
| case BIT11_2: |
| fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2; |
| break; |
| case BIT12_3: |
| fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3; |
| break; |
| }; |
| switch (vpcfg.freq_sel) { |
| case PIXCLKBY2: |
| fmtcfg_vp |= ISPCCDC_FMTCFG_VPIF_FRQ_BY2; |
| break; |
| case PIXCLKBY3_5: |
| fmtcfg_vp |= ISPCCDC_FMTCFG_VPIF_FRQ_BY3; |
| break; |
| case PIXCLKBY4_5: |
| fmtcfg_vp |= ISPCCDC_FMTCFG_VPIF_FRQ_BY4; |
| break; |
| case PIXCLKBY5_5: |
| fmtcfg_vp |= ISPCCDC_FMTCFG_VPIF_FRQ_BY5; |
| break; |
| case PIXCLKBY6_5: |
| fmtcfg_vp |= ISPCCDC_FMTCFG_VPIF_FRQ_BY6; |
| break; |
| }; |
| isp_reg_writel(dev, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_FMTCFG); |
| } |
| |
| /** |
| * ispccdc_enable_vp - Enable Video Port. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @enable: 0 Disables VP, 1 Enables VP |
| * |
| * This is needed for outputting image to Preview, H3A and HIST ISP submodules. |
| **/ |
| static void ispccdc_enable_vp(struct isp_ccdc_device *isp_ccdc, u8 enable) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| isp_reg_and_or(dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, |
| ~ISPCCDC_FMTCFG_VPEN, |
| enable ? ISPCCDC_FMTCFG_VPEN : 0); |
| } |
| |
| /** |
| * ispccdc_config_culling - Configure culling parameters. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @cull: Structure containing the vertical culling pattern, and horizontal |
| * culling pattern for odd and even lines. |
| **/ |
| static void ispccdc_config_culling(struct isp_ccdc_device *isp_ccdc, |
| struct ispccdc_culling cull) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| u32 culling_val = 0; |
| |
| culling_val |= cull.v_pattern << ISPCCDC_CULLING_CULV_SHIFT; |
| culling_val |= cull.h_even << ISPCCDC_CULLING_CULHEVN_SHIFT; |
| culling_val |= cull.h_odd << ISPCCDC_CULLING_CULHODD_SHIFT; |
| |
| isp_reg_writel(dev, culling_val, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_CULLING); |
| } |
| |
| /** |
| * ispccdc_enable_lpf - Enable Low-Pass Filter (LPF). |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @enable: 0 Disables LPF, 1 Enables LPF |
| **/ |
| static void ispccdc_enable_lpf(struct isp_ccdc_device *isp_ccdc, u8 enable) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| isp_reg_and_or(dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE, |
| ~ISPCCDC_SYN_MODE_LPF, |
| enable ? ISPCCDC_SYN_MODE_LPF : 0); |
| } |
| |
| /** |
| * ispccdc_config_alaw - Configure the input width for A-law compression. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @ipwidth: Input width for A-law |
| **/ |
| static void ispccdc_config_alaw(struct isp_ccdc_device *isp_ccdc, |
| enum alaw_ipwidth ipwidth) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| isp_reg_writel(dev, ipwidth << ISPCCDC_ALAW_GWDI_SHIFT, |
| OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW); |
| } |
| |
| /** |
| * ispccdc_enable_alaw - Enable A-law compression. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @enable: 0 - Disables A-law, 1 - Enables A-law |
| **/ |
| static void ispccdc_enable_alaw(struct isp_ccdc_device *isp_ccdc, u8 enable) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| isp_reg_and_or(dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_ALAW, |
| ~ISPCCDC_ALAW_CCDTBL, |
| enable ? ISPCCDC_ALAW_CCDTBL : 0); |
| } |
| |
| /** |
| * ispccdc_config_imgattr - Configure sensor image specific attributes. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @colptn: Color pattern of the sensor. |
| **/ |
| static void ispccdc_config_imgattr(struct isp_ccdc_device *isp_ccdc, u32 colptn) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| isp_reg_writel(dev, colptn, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_COLPTN); |
| } |
| |
| /** |
| * ispccdc_validate_config_lsc - Check that LSC configuration is valid. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @lsc_cfg: the LSC configuration to check. |
| * @pipe: if not NULL, verify the table size against CCDC input size. |
| * |
| * Returns 0 if the LSC configuration is valid, or -EINVAL if invalid. |
| **/ |
| static int ispccdc_validate_config_lsc(struct isp_ccdc_device *isp_ccdc, |
| struct ispccdc_lsc_config *lsc_cfg) |
| { |
| struct isp_device *isp = to_isp_device(isp_ccdc); |
| struct device *dev = to_device(isp_ccdc); |
| unsigned int paxel_width, paxel_height; |
| unsigned int paxel_shift_x, paxel_shift_y; |
| unsigned int min_width, min_height, min_size; |
| unsigned int input_width, input_height; |
| |
| paxel_shift_x = lsc_cfg->gain_mode_m; |
| paxel_shift_y = lsc_cfg->gain_mode_n; |
| |
| if ((paxel_shift_x < 2) || (paxel_shift_x > 6) || |
| (paxel_shift_y < 2) || (paxel_shift_y > 6)) { |
| dev_err(dev, "CCDC: LSC: Invalid paxel size\n"); |
| return -EINVAL; |
| } |
| |
| if (lsc_cfg->offset & 3) { |
| dev_err(dev, |
| "CCDC: LSC: Offset must be a multiple of 4\n"); |
| return -EINVAL; |
| } |
| |
| if ((lsc_cfg->initial_x & 1) || (lsc_cfg->initial_y & 1)) { |
| dev_err(dev, |
| "CCDC: LSC: initial_x and y must be even\n"); |
| return -EINVAL; |
| } |
| |
| input_width = isp->pipeline.ccdc_in_w; |
| input_height = isp->pipeline.ccdc_in_h; |
| |
| /* Calculate minimum bytesize for validation */ |
| paxel_width = 1 << paxel_shift_x; |
| min_width = ((input_width + lsc_cfg->initial_x + paxel_width - 1) |
| >> paxel_shift_x) + 1; |
| |
| paxel_height = 1 << paxel_shift_y; |
| min_height = ((input_height + lsc_cfg->initial_y + paxel_height - 1) |
| >> paxel_shift_y) + 1; |
| |
| min_size = 4 * min_width * min_height; |
| if (min_size > lsc_cfg->size) { |
| dev_err(dev, "CCDC: LSC: too small table\n"); |
| return -EINVAL; |
| } |
| if (lsc_cfg->offset < (min_width * 4)) { |
| dev_err(dev, "CCDC: LSC: Offset is too small\n"); |
| return -EINVAL; |
| } |
| if ((lsc_cfg->size / lsc_cfg->offset) < min_height) { |
| dev_err(dev, "CCDC: LSC: Wrong size/offset combination\n"); |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| /** |
| * ispccdc_program_lsc - Program Lens Shading Compensation table address. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| **/ |
| static void ispccdc_program_lsc(struct isp_ccdc_device *isp_ccdc) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| isp_reg_writel(dev, isp_ccdc->lsc_table_inuse, |
| OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE); |
| } |
| |
| /** |
| * ispccdc_config_lsc - Configures the lens shading compensation module |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| **/ |
| static void ispccdc_config_lsc(struct isp_ccdc_device *isp_ccdc) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| struct ispccdc_lsc_config *lsc_cfg = &isp_ccdc->lsc_config; |
| int reg; |
| |
| isp_reg_writel(dev, lsc_cfg->offset, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_LSC_TABLE_OFFSET); |
| |
| reg = 0; |
| reg |= lsc_cfg->gain_mode_n << ISPCCDC_LSC_GAIN_MODE_N_SHIFT; |
| reg |= lsc_cfg->gain_mode_m << ISPCCDC_LSC_GAIN_MODE_M_SHIFT; |
| reg |= lsc_cfg->gain_format << ISPCCDC_LSC_GAIN_FORMAT_SHIFT; |
| isp_reg_writel(dev, reg, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_LSC_CONFIG); |
| |
| reg = 0; |
| reg &= ~ISPCCDC_LSC_INITIAL_X_MASK; |
| reg |= lsc_cfg->initial_x << ISPCCDC_LSC_INITIAL_X_SHIFT; |
| reg &= ~ISPCCDC_LSC_INITIAL_Y_MASK; |
| reg |= lsc_cfg->initial_y << ISPCCDC_LSC_INITIAL_Y_SHIFT; |
| isp_reg_writel(dev, reg, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_LSC_INITIAL); |
| } |
| |
| void ispccdc_lsc_state_handler(struct isp_ccdc_device *isp_ccdc, |
| unsigned long status) |
| { |
| if (!isp_ccdc->lsc_enable) |
| return; |
| |
| if (status & CCDC_VD1) |
| isp_ccdc->lsc_delay_stop = 0; |
| |
| if (status & LSC_DONE) |
| isp_ccdc->lsc_delay_stop = 1; |
| |
| if (status & LSC_PRE_ERR) { |
| /* If we have LSC prefetch error, LSC engine is blocked |
| * and the only way it can recover is to do isp sw reset */ |
| ispccdc_enable_lsc(isp_ccdc, 0); |
| isp_ccdc->lsc_delay_stop = 1; |
| } |
| } |
| |
| static void __ispccdc_enable_lsc(struct isp_ccdc_device *isp_ccdc, u8 enable) |
| { |
| isp_reg_and_or(to_device(isp_ccdc), OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_LSC_CONFIG, |
| ~ISPCCDC_LSC_ENABLE, |
| enable ? ISPCCDC_LSC_ENABLE : 0); |
| } |
| |
| /** |
| * ispccdc_enable_lsc - Enables/Disables the Lens Shading Compensation module. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @enable: 0 Disables LSC, 1 Enables LSC. |
| **/ |
| void ispccdc_enable_lsc(struct isp_ccdc_device *isp_ccdc, u8 enable) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| if (enable) { |
| isp_reg_writel(dev, IRQ0ENABLE_CCDC_LSC_PREF_COMP_IRQ | |
| IRQ0ENABLE_CCDC_LSC_DONE_IRQ, |
| OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); |
| isp_reg_or(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, |
| IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ | |
| IRQ0ENABLE_CCDC_LSC_DONE_IRQ); |
| |
| isp_reg_or(dev, OMAP3_ISP_IOMEM_MAIN, |
| ISP_CTRL, ISPCTRL_SBL_SHARED_RPORTB |
| | ISPCTRL_SBL_RD_RAM_EN); |
| |
| __ispccdc_enable_lsc(isp_ccdc, 1); |
| isp_ccdc->lsc_request_enable = 0; |
| } else { |
| __ispccdc_enable_lsc(isp_ccdc, 0); |
| |
| isp_reg_and(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, |
| ~(IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ | |
| IRQ0ENABLE_CCDC_LSC_DONE_IRQ)); |
| } |
| isp_ccdc->lsc_enable = enable; |
| } |
| |
| /** |
| * ispccdc_setup_lsc - apply user LSC settings |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @pipe: Pointer to ISP pipeline structure. |
| * |
| * Consume the new LSC configuration and table set by user space application |
| * and program to CCDC. This function must be called from process context |
| * before streamon when ISP is not yet running. |
| */ |
| static void ispccdc_setup_lsc(struct isp_ccdc_device *isp_ccdc, |
| struct isp_pipeline *pipe) |
| { |
| struct isp_device *isp = to_isp_device(isp_ccdc); |
| |
| ispccdc_enable_lsc(isp_ccdc, 0); /* Disable LSC */ |
| if (((pipe->ccdc_in == CCDC_RAW_GRBG) || |
| (pipe->ccdc_in == CCDC_RAW_RGGB) || |
| (pipe->ccdc_in == CCDC_RAW_BGGR) || |
| (pipe->ccdc_in == CCDC_RAW_GBRG)) && |
| isp_ccdc->lsc_request_enable) { |
| /* LSC is requested to be enabled, so configure it */ |
| if (isp_ccdc->update_lsc_table) { |
| BUG_ON(isp_ccdc->lsc_table_new == PTR_FREE); |
| iommu_vfree(isp->iommu, isp_ccdc->lsc_table_inuse); |
| isp_ccdc->lsc_table_inuse = isp_ccdc->lsc_table_new; |
| isp_ccdc->lsc_table_new = PTR_FREE; |
| isp_ccdc->update_lsc_table = 0; |
| } |
| ispccdc_config_lsc(isp_ccdc); |
| ispccdc_program_lsc(isp_ccdc); |
| } |
| isp_ccdc->update_lsc_config = 0; |
| } |
| |
| /** |
| * ispccdc_lsc_error_handler - Handle LSC prefetch error scenario. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * |
| * Disables LSC, and defers enablement to shadow registers update time. |
| **/ |
| void ispccdc_lsc_error_handler(struct isp_ccdc_device *isp_ccdc) |
| { |
| ispccdc_enable_lsc(isp_ccdc, 0); |
| } |
| |
| /** |
| * ispccdc_config_crop - Configures crop parameters for the ISP CCDC. |
| * @left: Left offset of the crop area. |
| * @top: Top offset of the crop area. |
| * @height: Height of the crop area. |
| * @width: Width of the crop area. |
| * |
| * The following restrictions are applied for the crop settings. If incoming |
| * values do not follow these restrictions then we map the settings to the |
| * closest acceptable crop value. |
| * 1) Left offset is always odd. This can be avoided if we enable byte swap |
| * option for incoming data into CCDC. |
| * 2) Top offset is always even. |
| * 3) Crop height is always even. |
| * 4) Crop width is always a multiple of 16 pixels |
| **/ |
| static void ispccdc_config_crop(struct isp_ccdc_device *isp_ccdc, u32 left, |
| u32 top, u32 height, u32 width) |
| { |
| isp_ccdc->ccdcin_woffset = left + (left % 2); |
| isp_ccdc->ccdcin_hoffset = top + (top % 2); |
| |
| isp_ccdc->crop_w = width - (width % 16); |
| isp_ccdc->crop_h = height + (height % 2); |
| |
| DPRINTK_ISPCCDC("\n\tOffsets L %d T %d W %d H %d\n", |
| isp_ccdc->ccdcin_woffset, |
| isp_ccdc->ccdcin_hoffset, |
| isp_ccdc->crop_w, |
| isp_ccdc->crop_h); |
| } |
| |
| /** |
| * ispccdc_config_outlineoffset - Configure memory saving output line offset |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @offset: Address offset to start a new line. Must be twice the |
| * Output width and aligned on 32 byte boundary |
| * @oddeven: Specifies the odd/even line pattern to be chosen to store the |
| * output. |
| * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines. |
| * |
| * - Configures the output line offset when stored in memory |
| * - Sets the odd/even line pattern to store the output |
| * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4)) |
| * - Configures the number of even and odd line fields in case of rearranging |
| * the lines. |
| * |
| * Returns 0 if successful, or -EINVAL if the offset is not in 32 byte |
| * boundary. |
| **/ |
| static int ispccdc_config_outlineoffset(struct isp_ccdc_device *isp_ccdc, |
| u32 offset, u8 oddeven, u8 numlines) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| if ((offset & ISP_32B_BOUNDARY_OFFSET) == offset) { |
| isp_reg_writel(dev, (offset & 0xFFFF), |
| OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF); |
| } else { |
| DPRINTK_ISPCCDC("ISP_ERR : Offset should be in 32 byte" |
| " boundary\n"); |
| return -EINVAL; |
| } |
| |
| isp_reg_and(dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, |
| ~ISPCCDC_SDOFST_FINV); |
| |
| isp_reg_and(dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, |
| ~ISPCCDC_SDOFST_FOFST_4L); |
| |
| switch (oddeven) { |
| case EVENEVEN: |
| isp_reg_or(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SDOFST, |
| (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT); |
| break; |
| case ODDEVEN: |
| isp_reg_or(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SDOFST, |
| (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT); |
| break; |
| case EVENODD: |
| isp_reg_or(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SDOFST, |
| (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT); |
| break; |
| case ODDODD: |
| isp_reg_or(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SDOFST, |
| (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT); |
| break; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| /** |
| * ispccdc_set_outaddr - Set memory address to save output image |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. |
| * |
| * Sets the memory address where the output will be saved. |
| * |
| * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte |
| * boundary. |
| **/ |
| int ispccdc_set_outaddr(struct isp_ccdc_device *isp_ccdc, u32 addr) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| if ((addr & ISP_32B_BOUNDARY_BUF) == addr) { |
| isp_reg_writel(dev, addr, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SDR_ADDR); |
| return 0; |
| } else { |
| DPRINTK_ISPCCDC("ISP_ERR : Address should be in 32 byte" |
| " boundary\n"); |
| return -EINVAL; |
| } |
| |
| } |
| |
| void ispccdc_lsc_pref_comp_handler(struct isp_ccdc_device *isp_ccdc) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| if (!isp_ccdc->lsc_enable) |
| return; |
| isp_reg_and(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, |
| ~IRQ0ENABLE_CCDC_LSC_PREF_COMP_IRQ); |
| |
| ispccdc_enable(isp_ccdc, 1); |
| } |
| |
| /** |
| * ispccdc_config_sync_if - Set CCDC sync interface params between sensor and CCDC. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @syncif: Structure containing the sync parameters like field state, CCDC in |
| * master/slave mode, raw/yuv data, polarity of data, field, hs, vs |
| * signals. |
| **/ |
| static void ispccdc_config_sync_if(struct isp_ccdc_device *isp_ccdc, |
| struct ispccdc_syncif syncif) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| u32 syn_mode = isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SYN_MODE); |
| |
| syn_mode |= ISPCCDC_SYN_MODE_VDHDEN; |
| |
| if (syncif.fldstat) |
| syn_mode |= ISPCCDC_SYN_MODE_FLDSTAT; |
| else |
| syn_mode &= ~ISPCCDC_SYN_MODE_FLDSTAT; |
| |
| syn_mode &= ISPCCDC_SYN_MODE_INPMOD_MASK; |
| isp_ccdc->syncif_ipmod = syncif.ipmod; |
| |
| switch (syncif.ipmod) { |
| case RAW: |
| break; |
| case YUV16: |
| syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16; |
| break; |
| case YUV8: |
| syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR8; |
| break; |
| }; |
| |
| syn_mode &= ISPCCDC_SYN_MODE_DATSIZ_MASK; |
| switch (syncif.datsz) { |
| case DAT8: |
| syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_8; |
| break; |
| case DAT10: |
| syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_10; |
| break; |
| case DAT11: |
| syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_11; |
| break; |
| case DAT12: |
| syn_mode |= ISPCCDC_SYN_MODE_DATSIZ_12; |
| break; |
| }; |
| |
| if (syncif.fldmode) |
| syn_mode |= ISPCCDC_SYN_MODE_FLDMODE; |
| else |
| syn_mode &= ~ISPCCDC_SYN_MODE_FLDMODE; |
| |
| if (syncif.datapol) |
| syn_mode |= ISPCCDC_SYN_MODE_DATAPOL; |
| else |
| syn_mode &= ~ISPCCDC_SYN_MODE_DATAPOL; |
| |
| if (syncif.fldpol) |
| syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; |
| else |
| syn_mode &= ~ISPCCDC_SYN_MODE_FLDPOL; |
| |
| if (syncif.hdpol) |
| syn_mode |= ISPCCDC_SYN_MODE_HDPOL; |
| else |
| syn_mode &= ~ISPCCDC_SYN_MODE_HDPOL; |
| |
| if (syncif.vdpol) |
| syn_mode |= ISPCCDC_SYN_MODE_VDPOL; |
| else |
| syn_mode &= ~ISPCCDC_SYN_MODE_VDPOL; |
| |
| if (syncif.ccdc_mastermode) { |
| syn_mode |= ISPCCDC_SYN_MODE_FLDOUT | ISPCCDC_SYN_MODE_VDHDOUT; |
| isp_reg_writel(dev, |
| syncif.hs_width << ISPCCDC_HD_VD_WID_HDW_SHIFT |
| | syncif.vs_width << ISPCCDC_HD_VD_WID_VDW_SHIFT, |
| OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_HD_VD_WID); |
| |
| isp_reg_writel(dev, |
| syncif.ppln << ISPCCDC_PIX_LINES_PPLN_SHIFT |
| | syncif.hlprf << ISPCCDC_PIX_LINES_HLPRF_SHIFT, |
| OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_PIX_LINES); |
| } else |
| syn_mode &= ~(ISPCCDC_SYN_MODE_FLDOUT | |
| ISPCCDC_SYN_MODE_VDHDOUT); |
| |
| isp_reg_writel(dev, syn_mode, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SYN_MODE); |
| |
| if (!(syncif.bt_r656_en)) { |
| isp_reg_and(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_REC656IF, ~ISPCCDC_REC656IF_R656ON); |
| } |
| } |
| |
| /** |
| * ispccdc_set_wenlog - Set the CCDC Write Enable valid region. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @wenlog: Write enable logic to apply against valid area. 0 - AND, 1 - OR. |
| */ |
| void ispccdc_set_wenlog(struct isp_ccdc_device *isp_ccdc, u32 wenlog) |
| { |
| isp_ccdc->wenlog = wenlog; |
| } |
| |
| /** |
| * ispccdc_config_datapath - Specify the input and output modules for CCDC. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @pipe: Pointer to ISP pipeline structure to base on for config. |
| * |
| * Configures the default configuration for the CCDC to work with. |
| * |
| * The valid values for the input are CCDC_RAW (0), CCDC_YUV_SYNC (1), |
| * CCDC_YUV_BT (2), and CCDC_OTHERS (3). |
| * |
| * The valid values for the output are CCDC_YUV_RSZ (0), CCDC_YUV_MEM_RSZ (1), |
| * CCDC_OTHERS_VP (2), CCDC_OTHERS_MEM (3), CCDC_OTHERS_VP_MEM (4). |
| * |
| * Returns 0 if successful, or -EINVAL if wrong I/O combination or wrong input |
| * or output values. |
| **/ |
| static int ispccdc_config_datapath(struct isp_ccdc_device *isp_ccdc, |
| struct isp_pipeline *pipe) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| u32 syn_mode = 0; |
| struct ispccdc_vp vpcfg; |
| struct ispccdc_syncif syncif; |
| struct ispccdc_bclamp blkcfg; |
| |
| u32 colptn = ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC0_SHIFT | |
| ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC1_SHIFT | |
| ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP0PLC2_SHIFT | |
| ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP0PLC3_SHIFT | |
| ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC0_SHIFT | |
| ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC1_SHIFT | |
| ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP1PLC2_SHIFT | |
| ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP1PLC3_SHIFT | |
| ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC0_SHIFT | |
| ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC1_SHIFT | |
| ISPCCDC_COLPTN_Gr_Cy << ISPCCDC_COLPTN_CP2PLC2_SHIFT | |
| ISPCCDC_COLPTN_R_Ye << ISPCCDC_COLPTN_CP2PLC3_SHIFT | |
| ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC0_SHIFT | |
| ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC1_SHIFT | |
| ISPCCDC_COLPTN_B_Mg << ISPCCDC_COLPTN_CP3PLC2_SHIFT | |
| ISPCCDC_COLPTN_Gb_G << ISPCCDC_COLPTN_CP3PLC3_SHIFT; |
| |
| syn_mode = isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SYN_MODE); |
| |
| switch (pipe->ccdc_out) { |
| case CCDC_YUV_RSZ: |
| syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ; |
| syn_mode &= ~ISPCCDC_SYN_MODE_WEN; |
| break; |
| |
| case CCDC_YUV_MEM_RSZ: |
| syn_mode |= ISPCCDC_SYN_MODE_SDR2RSZ; |
| isp_ccdc->wen = 1; |
| syn_mode |= ISPCCDC_SYN_MODE_WEN; |
| break; |
| |
| case CCDC_OTHERS_VP: |
| syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; |
| syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; |
| syn_mode &= ~ISPCCDC_SYN_MODE_WEN; |
| vpcfg.bitshift_sel = BIT9_0; |
| vpcfg.freq_sel = PIXCLKBY2; |
| ispccdc_config_vp(isp_ccdc, vpcfg); |
| ispccdc_enable_vp(isp_ccdc, 1); |
| break; |
| |
| case CCDC_OTHERS_MEM: |
| syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; |
| syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; |
| syn_mode |= ISPCCDC_SYN_MODE_WEN; |
| syn_mode &= ~ISPCCDC_SYN_MODE_EXWEN; |
| isp_reg_and(dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, |
| ~ISPCCDC_CFG_WENLOG); |
| vpcfg.bitshift_sel = BIT9_0; |
| vpcfg.freq_sel = PIXCLKBY2; |
| ispccdc_config_vp(isp_ccdc, vpcfg); |
| ispccdc_enable_vp(isp_ccdc, 0); |
| break; |
| |
| case CCDC_OTHERS_VP_MEM: |
| syn_mode &= ~ISPCCDC_SYN_MODE_VP2SDR; |
| syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; |
| syn_mode |= ISPCCDC_SYN_MODE_WEN; |
| syn_mode &= ~ISPCCDC_SYN_MODE_EXWEN; |
| |
| isp_reg_and_or(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_CFG, ~ISPCCDC_CFG_WENLOG, |
| isp_ccdc->wenlog); |
| vpcfg.bitshift_sel = BIT9_0; |
| vpcfg.freq_sel = PIXCLKBY2; |
| ispccdc_config_vp(isp_ccdc, vpcfg); |
| ispccdc_enable_vp(isp_ccdc, 1); |
| break; |
| default: |
| DPRINTK_ISPCCDC("ISP_ERR: Wrong CCDC Output\n"); |
| return -EINVAL; |
| }; |
| |
| isp_reg_writel(dev, syn_mode, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_SYN_MODE); |
| |
| switch (pipe->ccdc_in) { |
| case CCDC_RAW_GRBG: |
| case CCDC_RAW_RGGB: |
| case CCDC_RAW_BGGR: |
| case CCDC_RAW_GBRG: |
| syncif.ccdc_mastermode = 0; |
| syncif.datapol = 0; |
| syncif.datsz = DAT10; |
| syncif.fldmode = 0; |
| syncif.fldout = 0; |
| syncif.fldpol = 0; |
| syncif.fldstat = 0; |
| syncif.hdpol = 0; |
| syncif.ipmod = RAW; |
| syncif.vdpol = 0; |
| ispccdc_config_sync_if(isp_ccdc, syncif); |
| ispccdc_config_imgattr(isp_ccdc, colptn); |
| blkcfg.oblen = 0; |
| blkcfg.dcsubval = 64; |
| ispccdc_config_black_clamp(isp_ccdc, blkcfg); |
| break; |
| case CCDC_YUV_SYNC: |
| syncif.ccdc_mastermode = 0; |
| syncif.datapol = 0; |
| syncif.datsz = DAT8; |
| syncif.fldmode = 0; |
| syncif.fldout = 0; |
| syncif.fldpol = 0; |
| syncif.fldstat = 0; |
| syncif.hdpol = 0; |
| syncif.ipmod = YUV16; |
| syncif.vdpol = 1; |
| ispccdc_config_imgattr(isp_ccdc, 0); |
| ispccdc_config_sync_if(isp_ccdc, syncif); |
| blkcfg.oblen = 0; |
| blkcfg.dcsubval = 0; |
| ispccdc_config_black_clamp(isp_ccdc, blkcfg); |
| break; |
| case CCDC_YUV_BT: |
| break; |
| case CCDC_OTHERS: |
| break; |
| default: |
| DPRINTK_ISPCCDC("ISP_ERR: Wrong CCDC Input\n"); |
| return -EINVAL; |
| } |
| |
| ispccdc_config_crop(isp_ccdc, 0, 0, 0, 0); |
| ispccdc_print_status(isp_ccdc, pipe); |
| isp_print_status(dev); |
| return 0; |
| } |
| |
| /** |
| * ispccdc_try_pipeline - Checks if requested Input/output dimensions are valid |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @pipe: Pointer to ISP pipeline structure to fill back. |
| * |
| * Calculates the number of pixels cropped if the reformater is disabled, |
| * Fills up the output width and height variables in the isp_ccdc structure. |
| * |
| * Returns 0 if successful, or -EINVAL if the input width is less than 2 pixels |
| **/ |
| int ispccdc_try_pipeline(struct isp_ccdc_device *isp_ccdc, |
| struct isp_pipeline *pipe) |
| { |
| struct isp_device *isp = to_isp_device(isp_ccdc); |
| |
| if (pipe->ccdc_in_w < 32 || pipe->ccdc_in_h < 32) { |
| DPRINTK_ISPCCDC("ISP_ERR: CCDC cannot handle input width less" |
| " than 32 pixels or height less than 32\n"); |
| return -EINVAL; |
| } |
| |
| /* CCDC does not convert the image format */ |
| if ((pipe->ccdc_in == CCDC_RAW_GRBG || |
| pipe->ccdc_in == CCDC_RAW_RGGB || |
| pipe->ccdc_in == CCDC_RAW_BGGR || |
| pipe->ccdc_in == CCDC_RAW_GBRG || |
| pipe->ccdc_in == CCDC_OTHERS) && |
| pipe->ccdc_out == CCDC_YUV_RSZ) { |
| dev_info(isp->dev, "wrong CCDC I/O Combination\n"); |
| return -EINVAL; |
| } |
| |
| pipe->ccdc_in_h_st = 0; |
| pipe->ccdc_in_v_st = 0; |
| pipe->ccdc_out_w = pipe->ccdc_in_w; |
| pipe->ccdc_out_h = pipe->ccdc_in_h; |
| |
| if (!isp_ccdc->refmt_en |
| && pipe->ccdc_out != CCDC_OTHERS_MEM |
| && pipe->ccdc_out != CCDC_OTHERS_VP_MEM) |
| pipe->ccdc_out_h -= 1; |
| |
| if (pipe->ccdc_out == CCDC_OTHERS_VP) { |
| switch (pipe->ccdc_in) { |
| case CCDC_RAW_GRBG: |
| pipe->ccdc_in_h_st = 1; |
| pipe->ccdc_in_v_st = 0; |
| break; |
| case CCDC_RAW_BGGR: |
| pipe->ccdc_in_h_st = 1; |
| pipe->ccdc_in_v_st = 1; |
| break; |
| case CCDC_RAW_RGGB: |
| pipe->ccdc_in_h_st = 0; |
| pipe->ccdc_in_v_st = 0; |
| break; |
| case CCDC_RAW_GBRG: |
| pipe->ccdc_in_h_st = 0; |
| pipe->ccdc_in_v_st = 1; |
| break; |
| default: |
| break; |
| } |
| pipe->ccdc_out_h -= pipe->ccdc_in_v_st; |
| pipe->ccdc_out_w -= pipe->ccdc_in_h_st; |
| pipe->ccdc_out_h -= (pipe->ccdc_out_h % 2); |
| pipe->ccdc_out_w -= (pipe->ccdc_out_w % 2); |
| } |
| |
| pipe->ccdc_out_w_img = pipe->ccdc_out_w; |
| /* Round up to nearest 32 pixels. */ |
| pipe->ccdc_out_w = ALIGN(pipe->ccdc_out_w, 0x20); |
| |
| return 0; |
| } |
| |
| /** |
| * ispccdc_s_pipeline - Configure the CCDC based on overall ISP pipeline. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @pipe: Pointer to ISP pipeline structure to configure. |
| * |
| * Configures the appropriate values stored in the isp_ccdc structure to |
| * HORZ/VERT_INFO registers and the VP_OUT depending on whether the image |
| * is stored in memory or given to the another module in the ISP pipeline. |
| * |
| * Returns 0 if successful, or -EINVAL if try_size was not called before to |
| * validate the requested dimensions. |
| **/ |
| int ispccdc_s_pipeline(struct isp_ccdc_device *isp_ccdc, |
| struct isp_pipeline *pipe) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| int rval; |
| |
| rval = ispccdc_config_datapath(isp_ccdc, pipe); |
| if (rval) |
| return rval; |
| |
| isp_reg_writel(dev, |
| (pipe->ccdc_in_h_st << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | |
| ((pipe->ccdc_in_w - pipe->ccdc_in_h_st) << |
| ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), |
| OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_FMT_HORZ); |
| isp_reg_writel(dev, |
| (pipe->ccdc_in_v_st << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | |
| ((pipe->ccdc_in_h - pipe->ccdc_in_v_st) << |
| ISPCCDC_FMT_VERT_FMTLNV_SHIFT), |
| OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_FMT_VERT); |
| isp_reg_writel(dev, |
| pipe->ccdc_in_v_st << ISPCCDC_VERT_START_SLV0_SHIFT, |
| OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_VERT_START); |
| isp_reg_writel(dev, (pipe->ccdc_out_h - pipe->ccdc_in_v_st - 1) << |
| ISPCCDC_VERT_LINES_NLV_SHIFT, |
| OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_VERT_LINES); |
| isp_reg_writel(dev, |
| (pipe->ccdc_in_h_st << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
| | ((pipe->ccdc_out_w - pipe->ccdc_in_h_st) |
| << ISPCCDC_HORZ_INFO_NPH_SHIFT), |
| OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_HORZ_INFO); |
| ispccdc_config_outlineoffset(isp_ccdc, pipe->ccdc_out_w * 2, 0, 0); |
| isp_reg_writel(dev, (((pipe->ccdc_out_h - 2) & |
| ISPCCDC_VDINT_0_MASK) << |
| ISPCCDC_VDINT_0_SHIFT) | |
| ((50 & ISPCCDC_VDINT_1_MASK) << |
| ISPCCDC_VDINT_1_SHIFT), |
| OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_VDINT); |
| |
| if (pipe->ccdc_out == CCDC_OTHERS_MEM) { |
| isp_reg_writel(dev, 0, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_VP_OUT); |
| } else { |
| isp_reg_writel(dev, |
| ((pipe->ccdc_out_w - pipe->ccdc_in_h_st) |
| << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | |
| ((pipe->ccdc_out_h - pipe->ccdc_in_v_st - 1) << |
| ISPCCDC_VP_OUT_VERT_NUM_SHIFT), |
| OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_VP_OUT); |
| } |
| |
| ispccdc_setup_lsc(isp_ccdc, pipe); |
| |
| return 0; |
| } |
| |
| /** |
| * ispccdc_set_raw_offset - Store the component order as component offset. |
| * @raw_fmt: Input data component order. |
| * |
| * Turns the component order into a horizontal & vertical offset and store |
| * offsets to be used later. |
| **/ |
| void ispccdc_set_raw_offset(struct isp_ccdc_device *isp_ccdc, |
| enum ispccdc_raw_fmt raw_fmt) |
| { |
| isp_ccdc->raw_fmt_in = raw_fmt; |
| } |
| EXPORT_SYMBOL(ispccdc_set_raw_offset); |
| |
| /** |
| * ispccdc_enable - Enable the CCDC module. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @enable: 0 Disables CCDC, 1 Enables CCDC |
| * |
| * Client should configure all the sub modules in CCDC before this. |
| **/ |
| void ispccdc_enable(struct isp_ccdc_device *isp_ccdc, u8 enable) |
| { |
| struct isp_device *isp = to_isp_device(isp_ccdc); |
| struct device *dev = to_device(isp_ccdc); |
| int enable_lsc; |
| |
| if (enable) { |
| enable_lsc = enable && |
| ((isp->pipeline.ccdc_in == CCDC_RAW_GRBG) || |
| (isp->pipeline.ccdc_in == CCDC_RAW_RGGB) || |
| (isp->pipeline.ccdc_in == CCDC_RAW_BGGR) || |
| (isp->pipeline.ccdc_in == CCDC_RAW_GBRG)) && |
| isp_ccdc->lsc_request_enable && |
| ispccdc_validate_config_lsc(isp_ccdc, |
| &isp_ccdc->lsc_config) == 0; |
| |
| if (enable_lsc) { |
| /* Defer CCDC enablement for |
| * when the prefetch is completed. */ |
| isp_reg_writel(dev, IRQ0ENABLE_CCDC_LSC_PREF_COMP_IRQ, |
| OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); |
| isp_reg_or(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, |
| IRQ0ENABLE_CCDC_LSC_PREF_COMP_IRQ); |
| ispccdc_enable_lsc(isp_ccdc, 1); |
| return; |
| } |
| isp_reg_writel(dev, IRQ0ENABLE_CCDC_VD1_IRQ, |
| OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); |
| isp_reg_or(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, |
| IRQ0ENABLE_CCDC_VD1_IRQ); |
| } else { |
| ispccdc_enable_lsc(isp_ccdc, 0); |
| isp_reg_and(dev, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE, |
| ~IRQ0ENABLE_CCDC_VD1_IRQ); |
| isp_ccdc->lsc_request_enable = isp_ccdc->lsc_enable; |
| } |
| isp_reg_and_or(dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR, |
| ~ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0); |
| } |
| |
| /** |
| * ispccdc_sbl_busy - Poll idle state of CCDC and related SBL memory write bits |
| * @_isp_ccdc: Pointer to ISP CCDC device. |
| * |
| * Returns zero if the CCDC is idle and the image has been written to |
| * memory, too. |
| **/ |
| int ispccdc_sbl_busy(void *_isp_ccdc) |
| { |
| struct isp_ccdc_device *isp_ccdc = _isp_ccdc; |
| struct device *dev = to_device(isp_ccdc); |
| |
| return ispccdc_busy(isp_ccdc) |
| | (isp_reg_readl(dev, OMAP3_ISP_IOMEM_SBL, |
| ISPSBL_CCDC_WR_0) & |
| ISPSBL_CCDC_WR_0_DATA_READY) |
| | (isp_reg_readl(dev, OMAP3_ISP_IOMEM_SBL, |
| ISPSBL_CCDC_WR_1) & |
| ISPSBL_CCDC_WR_0_DATA_READY) |
| | (isp_reg_readl(dev, OMAP3_ISP_IOMEM_SBL, |
| ISPSBL_CCDC_WR_2) & |
| ISPSBL_CCDC_WR_0_DATA_READY) |
| | (isp_reg_readl(dev, OMAP3_ISP_IOMEM_SBL, |
| ISPSBL_CCDC_WR_3) & |
| ISPSBL_CCDC_WR_0_DATA_READY); |
| } |
| |
| /** |
| * ispccdc_busy - Get busy state of the CCDC. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| **/ |
| int ispccdc_busy(struct isp_ccdc_device *isp_ccdc) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| return isp_reg_readl(dev, OMAP3_ISP_IOMEM_CCDC, |
| ISPCCDC_PCR) & |
| ISPCCDC_PCR_BUSY; |
| } |
| |
| /** |
| * ispccdc_lsc_can_stop - Indicate if ccdc lsc module can be stopped corectly. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| **/ |
| int ispccdc_lsc_delay_stop(struct isp_ccdc_device *isp_ccdc) |
| { |
| return isp_ccdc->lsc_delay_stop; |
| } |
| EXPORT_SYMBOL(ispccdc_lsc_delay_stop); |
| |
| /** |
| * ispccdc_config_shadow_registers - Configure CCDC during interframe time. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * |
| * Executes LSC deferred enablement before next frame starts. |
| **/ |
| void ispccdc_config_shadow_registers(struct isp_ccdc_device *isp_ccdc) |
| { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&isp_ccdc->lock, flags); |
| if (isp_ccdc->shadow_update) |
| goto skip; |
| |
| if (isp_ccdc->lsc_defer_setup) { |
| __ispccdc_enable_lsc(isp_ccdc, 0); |
| isp_ccdc->lsc_defer_setup = 0; |
| goto skip; |
| } |
| |
| if (isp_ccdc->update_lsc_table) { |
| unsigned long n = isp_ccdc->lsc_table_new; |
| /* Swap tables--no need to vfree in interrupt context */ |
| isp_ccdc->lsc_table_new = isp_ccdc->lsc_table_inuse; |
| isp_ccdc->lsc_table_inuse = n; |
| ispccdc_program_lsc(isp_ccdc); |
| isp_ccdc->update_lsc_table = 0; |
| } |
| |
| if (isp_ccdc->update_lsc_config) { |
| ispccdc_config_lsc(isp_ccdc); |
| __ispccdc_enable_lsc(isp_ccdc, isp_ccdc->lsc_request_enable); |
| isp_ccdc->update_lsc_config = 0; |
| } |
| |
| skip: |
| spin_unlock_irqrestore(&isp_ccdc->lock, flags); |
| } |
| |
| /** |
| * ispccdc_config - Set CCDC configuration from userspace |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * @userspace_add: Structure containing CCDC configuration sent from userspace. |
| * |
| * Returns 0 if successful, -EINVAL if the pointer to the configuration |
| * structure is null, or the copy_from_user function fails to copy user space |
| * memory to kernel space memory. |
| **/ |
| int ispccdc_config(struct isp_ccdc_device *isp_ccdc, |
| void *userspace_add) |
| { |
| struct isp_device *isp = to_isp_device(isp_ccdc); |
| struct device *dev = to_device(isp_ccdc); |
| struct ispccdc_bclamp bclamp_t; |
| struct ispccdc_blcomp blcomp_t; |
| struct ispccdc_fpc fpc_t; |
| struct ispccdc_culling cull_t; |
| struct ispccdc_update_config *ccdc_struct; |
| unsigned long flags; |
| int ret = 0; |
| |
| if (userspace_add == NULL) |
| return -EINVAL; |
| |
| ccdc_struct = userspace_add; |
| |
| spin_lock_irqsave(&isp_ccdc->lock, flags); |
| isp_ccdc->shadow_update = 1; |
| spin_unlock_irqrestore(&isp_ccdc->lock, flags); |
| |
| if (ISP_ABS_CCDC_ALAW & ccdc_struct->flag) { |
| if (ISP_ABS_CCDC_ALAW & ccdc_struct->update) |
| ispccdc_config_alaw(isp_ccdc, ccdc_struct->alawip); |
| ispccdc_enable_alaw(isp_ccdc, 1); |
| } else if (ISP_ABS_CCDC_ALAW & ccdc_struct->update) |
| ispccdc_enable_alaw(isp_ccdc, 0); |
| |
| if (ISP_ABS_CCDC_LPF & ccdc_struct->flag) |
| ispccdc_enable_lpf(isp_ccdc, 1); |
| else |
| ispccdc_enable_lpf(isp_ccdc, 0); |
| |
| if (ISP_ABS_CCDC_BLCLAMP & ccdc_struct->flag) { |
| if (ISP_ABS_CCDC_BLCLAMP & ccdc_struct->update) { |
| if (copy_from_user(&bclamp_t, (struct ispccdc_bclamp *) |
| ccdc_struct->bclamp, |
| sizeof(struct ispccdc_bclamp))) { |
| ret = -EFAULT; |
| goto out; |
| } |
| |
| ispccdc_enable_black_clamp(isp_ccdc, 1); |
| ispccdc_config_black_clamp(isp_ccdc, bclamp_t); |
| } else |
| ispccdc_enable_black_clamp(isp_ccdc, 1); |
| } else { |
| if (ISP_ABS_CCDC_BLCLAMP & ccdc_struct->update) { |
| if (copy_from_user(&bclamp_t, (struct ispccdc_bclamp *) |
| ccdc_struct->bclamp, |
| sizeof(struct ispccdc_bclamp))) { |
| ret = -EFAULT; |
| goto out; |
| } |
| |
| ispccdc_enable_black_clamp(isp_ccdc, 0); |
| ispccdc_config_black_clamp(isp_ccdc, bclamp_t); |
| } |
| } |
| |
| if (ISP_ABS_CCDC_BCOMP & ccdc_struct->update) { |
| if (copy_from_user(&blcomp_t, (struct ispccdc_blcomp *) |
| ccdc_struct->blcomp, |
| sizeof(blcomp_t))) { |
| ret = -EFAULT; |
| goto out; |
| } |
| |
| ispccdc_config_black_comp(isp_ccdc, blcomp_t); |
| } |
| |
| if (ISP_ABS_CCDC_FPC & ccdc_struct->flag) { |
| if (ISP_ABS_CCDC_FPC & ccdc_struct->update) { |
| if (copy_from_user(&fpc_t, (struct ispccdc_fpc *) |
| ccdc_struct->fpc, |
| sizeof(fpc_t))) { |
| ret = -EFAULT; |
| goto out; |
| } |
| isp_ccdc->fpc_table_add = kmalloc(64 + fpc_t.fpnum * 4, |
| GFP_KERNEL | GFP_DMA); |
| if (!isp_ccdc->fpc_table_add) { |
| ret = -ENOMEM; |
| goto out; |
| } |
| while (((unsigned long)isp_ccdc->fpc_table_add |
| & 0xFFFFFFC0) |
| != (unsigned long)isp_ccdc->fpc_table_add) |
| isp_ccdc->fpc_table_add++; |
| |
| isp_ccdc->fpc_table_add_m = iommu_kmap( |
| isp->iommu, |
| 0, |
| virt_to_phys(isp_ccdc->fpc_table_add), |
| fpc_t.fpnum * 4, |
| IOMMU_FLAG); |
| /* FIXME: Correct unwinding */ |
| BUG_ON(IS_ERR_VALUE(isp_ccdc->fpc_table_add_m)); |
| |
| if (copy_from_user(isp_ccdc->fpc_table_add, |
| (u32 *)fpc_t.fpcaddr, |
| fpc_t.fpnum * 4)) { |
| ret = -EFAULT; |
| goto out; |
| } |
| |
| fpc_t.fpcaddr = isp_ccdc->fpc_table_add_m; |
| ispccdc_config_fpc(isp_ccdc, fpc_t); |
| } |
| ispccdc_enable_fpc(isp_ccdc, 1); |
| } else if (ISP_ABS_CCDC_FPC & ccdc_struct->update) |
| ispccdc_enable_fpc(isp_ccdc, 0); |
| |
| if (ISP_ABS_CCDC_CULL & ccdc_struct->update) { |
| if (copy_from_user(&cull_t, (struct ispccdc_culling *) |
| ccdc_struct->cull, |
| sizeof(cull_t))) { |
| ret = -EFAULT; |
| goto out; |
| } |
| ispccdc_config_culling(isp_ccdc, cull_t); |
| } |
| |
| if (ISP_ABS_CCDC_CONFIG_LSC & ccdc_struct->update) { |
| if (ISP_ABS_CCDC_CONFIG_LSC & ccdc_struct->flag) { |
| struct ispccdc_lsc_config cfg; |
| if (copy_from_user(&cfg, ccdc_struct->lsc_cfg, |
| sizeof(cfg))) { |
| ret = -EFAULT; |
| goto out; |
| } |
| ret = ispccdc_validate_config_lsc(isp_ccdc, &cfg); |
| if (ret) |
| goto out; |
| memcpy(&isp_ccdc->lsc_config, &cfg, |
| sizeof(isp_ccdc->lsc_config)); |
| isp_ccdc->lsc_request_enable = 1; |
| } else { |
| isp_ccdc->lsc_request_enable = 0; |
| } |
| isp_ccdc->update_lsc_config = 1; |
| } |
| |
| if (ISP_ABS_TBL_LSC & ccdc_struct->update) { |
| void *n; |
| if (isp_ccdc->lsc_table_new != PTR_FREE) |
| iommu_vfree(isp->iommu, isp_ccdc->lsc_table_new); |
| isp_ccdc->lsc_table_new = iommu_vmalloc(isp->iommu, 0, |
| isp_ccdc->lsc_config.size, |
| IOMMU_FLAG); |
| if (IS_ERR_VALUE(isp_ccdc->lsc_table_new)) { |
| /* Disable LSC if table can not be allocated */ |
| isp_ccdc->lsc_table_new = PTR_FREE; |
| isp_ccdc->lsc_request_enable = 0; |
| isp_ccdc->update_lsc_config = 1; |
| ret = -ENOMEM; |
| goto out; |
| } |
| n = da_to_va(isp->iommu, isp_ccdc->lsc_table_new); |
| if (copy_from_user(n, ccdc_struct->lsc, |
| isp_ccdc->lsc_config.size)) { |
| ret = -EFAULT; |
| goto out; |
| } |
| isp_ccdc->update_lsc_table = 1; |
| } |
| |
| if (isp_ccdc->update_lsc_table || isp_ccdc->update_lsc_config) { |
| if (isp->running != ISP_RUNNING) |
| ispccdc_setup_lsc(isp_ccdc, &isp->pipeline); |
| else if (isp_ccdc->lsc_enable) |
| isp_ccdc->lsc_defer_setup = 1; |
| else if (isp_ccdc->lsc_request_enable) { |
| ispccdc_setup_lsc(isp_ccdc, &isp->pipeline); |
| ispccdc_enable_lsc(isp_ccdc, 1); |
| } |
| } |
| |
| if (ISP_ABS_CCDC_COLPTN & ccdc_struct->update) |
| ispccdc_config_imgattr(isp_ccdc, ccdc_struct->colptn); |
| |
| out: |
| if (ret == -EFAULT) |
| dev_err(dev, |
| "ccdc: user provided bad configuration data address"); |
| |
| if (ret == -ENOMEM) |
| dev_err(dev, |
| "ccdc: can not allocate memory"); |
| |
| isp_ccdc->shadow_update = 0; |
| return ret; |
| } |
| |
| /** |
| * ispccdc_request - Reserve the CCDC module. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * Reserves the CCDC module and assures that is used only once at a time. |
| * |
| * Returns 0 if successful, or -EBUSY if CCDC module is busy. |
| **/ |
| int ispccdc_request(struct isp_ccdc_device *isp_ccdc) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| mutex_lock(&isp_ccdc->mutexlock); |
| if (isp_ccdc->ccdc_inuse) { |
| mutex_unlock(&isp_ccdc->mutexlock); |
| DPRINTK_ISPCCDC("ISP_ERR : CCDC Module Busy\n"); |
| return -EBUSY; |
| } |
| |
| isp_ccdc->ccdc_inuse = 1; |
| mutex_unlock(&isp_ccdc->mutexlock); |
| isp_reg_or(dev, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, |
| ISPCTRL_CCDC_RAM_EN | ISPCTRL_CCDC_CLK_EN | |
| ISPCTRL_SBL_WR1_RAM_EN); |
| isp_reg_or(dev, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, |
| ISPCCDC_CFG_VDLC); |
| return 0; |
| } |
| |
| /** |
| * ispccdc_free - Free the CCDC module. |
| * @isp_ccdc: Pointer to ISP CCDC device. |
| * |
| * Frees the CCDC module so it can be used by another process. |
| * |
| * Returns 0 if successful, or -EINVAL if module has been already freed. |
| **/ |
| int ispccdc_free(struct isp_ccdc_device *isp_ccdc) |
| { |
| struct device *dev = to_device(isp_ccdc); |
| |
| mutex_lock(&isp_ccdc->mutexlock); |
| if (!isp_ccdc->ccdc_inuse) { |
| mutex_unlock(&isp_ccdc->mutexlock); |
| DPRINTK_ISPCCDC("ISP_ERR: CCDC Module already freed\n"); |
| return -EINVAL; |
| } |
| |
| isp_ccdc->ccdc_inuse = 0; |
| mutex_unlock(&isp_ccdc->mutexlock); |
| isp_reg_and(dev, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, |
| ~(ISPCTRL_CCDC_CLK_EN | |
| ISPCTRL_CCDC_RAM_EN | |
| ISPCTRL_SBL_WR1_RAM_EN)); |
| return 0; |
| } |
| |
| /** |
| * ispccdc_save_context - Save values of the CCDC module registers |
| * @dev: Device pointer specific to the OMAP3 ISP. |
| **/ |
| void ispccdc_save_context(struct device *dev) |
| { |
| DPRINTK_ISPCCDC("Saving context\n"); |
| isp_save_context(dev, ispccdc_reg_list); |
| } |
| |
| /** |
| * ispccdc_restore_context - Restore values of the CCDC module registers |
| * @dev: Pointer to ISP device |
| **/ |
| void ispccdc_restore_context(struct device *dev) |
| { |
| DPRINTK_ISPCCDC("Restoring context\n"); |
| isp_restore_context(dev, ispccdc_reg_list); |
| } |
| |
| /** |
| * isp_ccdc_init - CCDC module initialization. |
| * @dev: Device pointer specific to the OMAP3 ISP. |
| * |
| * Always returns 0 |
| **/ |
| int __init isp_ccdc_init(struct device *dev) |
| { |
| struct isp_device *isp = dev_get_drvdata(dev); |
| struct isp_ccdc_device *isp_ccdc = &isp->isp_ccdc; |
| void *p; |
| |
| isp_ccdc->ccdc_inuse = 0; |
| ispccdc_config_crop(isp_ccdc, 0, 0, 0, 0); |
| mutex_init(&isp_ccdc->mutexlock); |
| |
| isp_ccdc->update_lsc_config = 0; |
| isp_ccdc->lsc_request_enable = 1; |
| isp_ccdc->lsc_defer_setup = 0; |
| isp_ccdc->lsc_delay_stop = 0; |
| |
| isp_ccdc->lsc_config.initial_x = 0; |
| isp_ccdc->lsc_config.initial_y = 0; |
| isp_ccdc->lsc_config.gain_mode_n = 0x6; |
| isp_ccdc->lsc_config.gain_mode_m = 0x6; |
| isp_ccdc->lsc_config.gain_format = 0x4; |
| isp_ccdc->lsc_config.offset = 0x60; |
| isp_ccdc->lsc_config.size = LSC_TABLE_INIT_SIZE; |
| |
| isp_ccdc->update_lsc_table = 0; |
| isp_ccdc->lsc_table_new = PTR_FREE; |
| isp_ccdc->lsc_table_inuse = iommu_vmalloc(isp->iommu, 0, |
| LSC_TABLE_INIT_SIZE, |
| IOMMU_FLAG); |
| if (IS_ERR_VALUE(isp_ccdc->lsc_table_inuse)) |
| return -ENOMEM; |
| p = da_to_va(isp->iommu, isp_ccdc->lsc_table_inuse); |
| memset(p, 0x40, LSC_TABLE_INIT_SIZE); |
| |
| isp_ccdc->shadow_update = 0; |
| spin_lock_init(&isp_ccdc->lock); |
| |
| return 0; |
| } |
| |
| /** |
| * isp_ccdc_cleanup - CCDC module cleanup. |
| * @dev: Device pointer specific to the OMAP3 ISP. |
| **/ |
| void isp_ccdc_cleanup(struct device *dev) |
| { |
| struct isp_device *isp = dev_get_drvdata(dev); |
| struct isp_ccdc_device *isp_ccdc = &isp->isp_ccdc; |
| |
| iommu_vfree(isp->iommu, isp_ccdc->lsc_table_inuse); |
| if (isp_ccdc->lsc_table_new != PTR_FREE) |
| iommu_vfree(isp->iommu, isp_ccdc->lsc_table_new); |
| |
| if (isp_ccdc->fpc_table_add_m != 0) { |
| iommu_kunmap(isp->iommu, isp_ccdc->fpc_table_add_m); |
| kfree(isp_ccdc->fpc_table_add); |
| } |
| } |