linux/drivers/media/platform/davinci/dm355_ccdc.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2005-2009 Texas Instruments Inc
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License as published by
   6 * the Free Software Foundation; either version 2 of the License, or
   7 * (at your option) any later version.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * along with this program; if not, write to the Free Software
  16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  17 *
  18 * CCDC hardware module for DM355
  19 * ------------------------------
  20 *
  21 * This module is for configuring DM355 CCD controller of VPFE to capture
  22 * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules
  23 * such as Defect Pixel Correction, Color Space Conversion etc to
  24 * pre-process the Bayer RGB data, before writing it to SDRAM. This
  25 * module also allows application to configure individual
  26 * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL.
  27 * To do so, application include dm355_ccdc.h and vpfe_capture.h header
  28 * files. The setparams() API is called by vpfe_capture driver
  29 * to configure module parameters
  30 *
  31 * TODO: 1) Raw bayer parameter settings and bayer capture
  32 *       2) Split module parameter structure to module specific ioctl structs
  33 *       3) add support for lense shading correction
  34 *       4) investigate if enum used for user space type definition
  35 *          to be replaced by #defines or integer
  36 */
  37#include <linux/platform_device.h>
  38#include <linux/uaccess.h>
  39#include <linux/videodev2.h>
  40#include <linux/err.h>
  41#include <linux/module.h>
  42
  43#include <media/davinci/dm355_ccdc.h>
  44#include <media/davinci/vpss.h>
  45
  46#include "dm355_ccdc_regs.h"
  47#include "ccdc_hw_device.h"
  48
  49MODULE_LICENSE("GPL");
  50MODULE_DESCRIPTION("CCDC Driver for DM355");
  51MODULE_AUTHOR("Texas Instruments");
  52
  53static struct ccdc_oper_config {
  54        struct device *dev;
  55        /* CCDC interface type */
  56        enum vpfe_hw_if_type if_type;
  57        /* Raw Bayer configuration */
  58        struct ccdc_params_raw bayer;
  59        /* YCbCr configuration */
  60        struct ccdc_params_ycbcr ycbcr;
  61        /* ccdc base address */
  62        void __iomem *base_addr;
  63} ccdc_cfg = {
  64        /* Raw configurations */
  65        .bayer = {
  66                .pix_fmt = CCDC_PIXFMT_RAW,
  67                .frm_fmt = CCDC_FRMFMT_PROGRESSIVE,
  68                .win = CCDC_WIN_VGA,
  69                .fid_pol = VPFE_PINPOL_POSITIVE,
  70                .vd_pol = VPFE_PINPOL_POSITIVE,
  71                .hd_pol = VPFE_PINPOL_POSITIVE,
  72                .gain = {
  73                        .r_ye = 256,
  74                        .gb_g = 256,
  75                        .gr_cy = 256,
  76                        .b_mg = 256
  77                },
  78                .config_params = {
  79                        .datasft = 2,
  80                        .mfilt1 = CCDC_NO_MEDIAN_FILTER1,
  81                        .mfilt2 = CCDC_NO_MEDIAN_FILTER2,
  82                        .alaw = {
  83                                .gamma_wd = 2,
  84                        },
  85                        .blk_clamp = {
  86                                .sample_pixel = 1,
  87                                .dc_sub = 25
  88                        },
  89                        .col_pat_field0 = {
  90                                .olop = CCDC_GREEN_BLUE,
  91                                .olep = CCDC_BLUE,
  92                                .elop = CCDC_RED,
  93                                .elep = CCDC_GREEN_RED
  94                        },
  95                        .col_pat_field1 = {
  96                                .olop = CCDC_GREEN_BLUE,
  97                                .olep = CCDC_BLUE,
  98                                .elop = CCDC_RED,
  99                                .elep = CCDC_GREEN_RED
 100                        },
 101                },
 102        },
 103        /* YCbCr configuration */
 104        .ycbcr = {
 105                .win = CCDC_WIN_PAL,
 106                .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
 107                .frm_fmt = CCDC_FRMFMT_INTERLACED,
 108                .fid_pol = VPFE_PINPOL_POSITIVE,
 109                .vd_pol = VPFE_PINPOL_POSITIVE,
 110                .hd_pol = VPFE_PINPOL_POSITIVE,
 111                .bt656_enable = 1,
 112                .pix_order = CCDC_PIXORDER_CBYCRY,
 113                .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
 114        },
 115};
 116
 117
 118/* Raw Bayer formats */
 119static u32 ccdc_raw_bayer_pix_formats[] =
 120                {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16};
 121
 122/* Raw YUV formats */
 123static u32 ccdc_raw_yuv_pix_formats[] =
 124                {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV};
 125
 126/* register access routines */
 127static inline u32 regr(u32 offset)
 128{
 129        return __raw_readl(ccdc_cfg.base_addr + offset);
 130}
 131
 132static inline void regw(u32 val, u32 offset)
 133{
 134        __raw_writel(val, ccdc_cfg.base_addr + offset);
 135}
 136
 137static void ccdc_enable(int en)
 138{
 139        unsigned int temp;
 140        temp = regr(SYNCEN);
 141        temp &= (~CCDC_SYNCEN_VDHDEN_MASK);
 142        temp |= (en & CCDC_SYNCEN_VDHDEN_MASK);
 143        regw(temp, SYNCEN);
 144}
 145
 146static void ccdc_enable_output_to_sdram(int en)
 147{
 148        unsigned int temp;
 149        temp = regr(SYNCEN);
 150        temp &= (~(CCDC_SYNCEN_WEN_MASK));
 151        temp |= ((en << CCDC_SYNCEN_WEN_SHIFT) & CCDC_SYNCEN_WEN_MASK);
 152        regw(temp, SYNCEN);
 153}
 154
 155static void ccdc_config_gain_offset(void)
 156{
 157        /* configure gain */
 158        regw(ccdc_cfg.bayer.gain.r_ye, RYEGAIN);
 159        regw(ccdc_cfg.bayer.gain.gr_cy, GRCYGAIN);
 160        regw(ccdc_cfg.bayer.gain.gb_g, GBGGAIN);
 161        regw(ccdc_cfg.bayer.gain.b_mg, BMGGAIN);
 162        /* configure offset */
 163        regw(ccdc_cfg.bayer.ccdc_offset, OFFSET);
 164}
 165
 166/*
 167 * ccdc_restore_defaults()
 168 * This function restore power on defaults in the ccdc registers
 169 */
 170static int ccdc_restore_defaults(void)
 171{
 172        int i;
 173
 174        dev_dbg(ccdc_cfg.dev, "\nstarting ccdc_restore_defaults...");
 175        /* set all registers to zero */
 176        for (i = 0; i <= CCDC_REG_LAST; i += 4)
 177                regw(0, i);
 178
 179        /* now override the values with power on defaults in registers */
 180        regw(MODESET_DEFAULT, MODESET);
 181        /* no culling support */
 182        regw(CULH_DEFAULT, CULH);
 183        regw(CULV_DEFAULT, CULV);
 184        /* Set default Gain and Offset */
 185        ccdc_cfg.bayer.gain.r_ye = GAIN_DEFAULT;
 186        ccdc_cfg.bayer.gain.gb_g = GAIN_DEFAULT;
 187        ccdc_cfg.bayer.gain.gr_cy = GAIN_DEFAULT;
 188        ccdc_cfg.bayer.gain.b_mg = GAIN_DEFAULT;
 189        ccdc_config_gain_offset();
 190        regw(OUTCLIP_DEFAULT, OUTCLIP);
 191        regw(LSCCFG2_DEFAULT, LSCCFG2);
 192        /* select ccdc input */
 193        if (vpss_select_ccdc_source(VPSS_CCDCIN)) {
 194                dev_dbg(ccdc_cfg.dev, "\ncouldn't select ccdc input source");
 195                return -EFAULT;
 196        }
 197        /* select ccdc clock */
 198        if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) {
 199                dev_dbg(ccdc_cfg.dev, "\ncouldn't enable ccdc clock");
 200                return -EFAULT;
 201        }
 202        dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_restore_defaults...");
 203        return 0;
 204}
 205
 206static int ccdc_open(struct device *device)
 207{
 208        return ccdc_restore_defaults();
 209}
 210
 211static int ccdc_close(struct device *device)
 212{
 213        /* disable clock */
 214        vpss_enable_clock(VPSS_CCDC_CLOCK, 0);
 215        /* do nothing for now */
 216        return 0;
 217}
 218/*
 219 * ccdc_setwin()
 220 * This function will configure the window size to
 221 * be capture in CCDC reg.
 222 */
 223static void ccdc_setwin(struct v4l2_rect *image_win,
 224                        enum ccdc_frmfmt frm_fmt, int ppc)
 225{
 226        int horz_start, horz_nr_pixels;
 227        int vert_start, vert_nr_lines;
 228        int mid_img = 0;
 229
 230        dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin...");
 231
 232        /*
 233         * ppc - per pixel count. indicates how many pixels per cell
 234         * output to SDRAM. example, for ycbcr, it is one y and one c, so 2.
 235         * raw capture this is 1
 236         */
 237        horz_start = image_win->left << (ppc - 1);
 238        horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1;
 239
 240        /* Writing the horizontal info into the registers */
 241        regw(horz_start, SPH);
 242        regw(horz_nr_pixels, NPH);
 243        vert_start = image_win->top;
 244
 245        if (frm_fmt == CCDC_FRMFMT_INTERLACED) {
 246                vert_nr_lines = (image_win->height >> 1) - 1;
 247                vert_start >>= 1;
 248                /* Since first line doesn't have any data */
 249                vert_start += 1;
 250                /* configure VDINT0 and VDINT1 */
 251                regw(vert_start, VDINT0);
 252        } else {
 253                /* Since first line doesn't have any data */
 254                vert_start += 1;
 255                vert_nr_lines = image_win->height - 1;
 256                /* configure VDINT0 and VDINT1 */
 257                mid_img = vert_start + (image_win->height / 2);
 258                regw(vert_start, VDINT0);
 259                regw(mid_img, VDINT1);
 260        }
 261        regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0);
 262        regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1);
 263        regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV);
 264        dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin...");
 265}
 266
 267static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam)
 268{
 269        if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT ||
 270            ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) {
 271                dev_dbg(ccdc_cfg.dev, "Invalid value of data shift\n");
 272                return -EINVAL;
 273        }
 274
 275        if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 ||
 276            ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) {
 277                dev_dbg(ccdc_cfg.dev, "Invalid value of median filter1\n");
 278                return -EINVAL;
 279        }
 280
 281        if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 ||
 282            ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) {
 283                dev_dbg(ccdc_cfg.dev, "Invalid value of median filter2\n");
 284                return -EINVAL;
 285        }
 286
 287        if ((ccdcparam->med_filt_thres < 0) ||
 288           (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) {
 289                dev_dbg(ccdc_cfg.dev,
 290                        "Invalid value of median filter threshold\n");
 291                return -EINVAL;
 292        }
 293
 294        if (ccdcparam->data_sz < CCDC_DATA_16BITS ||
 295            ccdcparam->data_sz > CCDC_DATA_8BITS) {
 296                dev_dbg(ccdc_cfg.dev, "Invalid value of data size\n");
 297                return -EINVAL;
 298        }
 299
 300        if (ccdcparam->alaw.enable) {
 301                if (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_13_4 ||
 302                    ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) {
 303                        dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n");
 304                        return -EINVAL;
 305                }
 306        }
 307
 308        if (ccdcparam->blk_clamp.b_clamp_enable) {
 309                if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS ||
 310                    ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) {
 311                        dev_dbg(ccdc_cfg.dev,
 312                                "Invalid value of sample pixel\n");
 313                        return -EINVAL;
 314                }
 315                if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES ||
 316                    ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) {
 317                        dev_dbg(ccdc_cfg.dev,
 318                                "Invalid value of sample lines\n");
 319                        return -EINVAL;
 320                }
 321        }
 322        return 0;
 323}
 324
 325/* Parameter operations */
 326static int ccdc_set_params(void __user *params)
 327{
 328        struct ccdc_config_params_raw ccdc_raw_params;
 329        int x;
 330
 331        /* only raw module parameters can be set through the IOCTL */
 332        if (ccdc_cfg.if_type != VPFE_RAW_BAYER)
 333                return -EINVAL;
 334
 335        x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params));
 336        if (x) {
 337                dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying ccdc"
 338                        "params, %d\n", x);
 339                return -EFAULT;
 340        }
 341
 342        if (!validate_ccdc_param(&ccdc_raw_params)) {
 343                memcpy(&ccdc_cfg.bayer.config_params,
 344                        &ccdc_raw_params,
 345                        sizeof(ccdc_raw_params));
 346                return 0;
 347        }
 348        return -EINVAL;
 349}
 350
 351/* This function will configure CCDC for YCbCr video capture */
 352static void ccdc_config_ycbcr(void)
 353{
 354        struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr;
 355        u32 temp;
 356
 357        /* first set the CCDC power on defaults values in all registers */
 358        dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr...");
 359        ccdc_restore_defaults();
 360
 361        /* configure pixel format & video frame format */
 362        temp = (((params->pix_fmt & CCDC_INPUT_MODE_MASK) <<
 363                CCDC_INPUT_MODE_SHIFT) |
 364                ((params->frm_fmt & CCDC_FRM_FMT_MASK) <<
 365                CCDC_FRM_FMT_SHIFT));
 366
 367        /* setup BT.656 sync mode */
 368        if (params->bt656_enable) {
 369                regw(CCDC_REC656IF_BT656_EN, REC656IF);
 370                /*
 371                 * configure the FID, VD, HD pin polarity fld,hd pol positive,
 372                 * vd negative, 8-bit pack mode
 373                 */
 374                temp |= CCDC_VD_POL_NEGATIVE;
 375        } else {                /* y/c external sync mode */
 376                temp |= (((params->fid_pol & CCDC_FID_POL_MASK) <<
 377                        CCDC_FID_POL_SHIFT) |
 378                        ((params->hd_pol & CCDC_HD_POL_MASK) <<
 379                        CCDC_HD_POL_SHIFT) |
 380                        ((params->vd_pol & CCDC_VD_POL_MASK) <<
 381                        CCDC_VD_POL_SHIFT));
 382        }
 383
 384        /* pack the data to 8-bit */
 385        temp |= CCDC_DATA_PACK_ENABLE;
 386
 387        regw(temp, MODESET);
 388
 389        /* configure video window */
 390        ccdc_setwin(&params->win, params->frm_fmt, 2);
 391
 392        /* configure the order of y cb cr in SD-RAM */
 393        temp = (params->pix_order << CCDC_Y8POS_SHIFT);
 394        temp |= CCDC_LATCH_ON_VSYNC_DISABLE | CCDC_CCDCFG_FIDMD_NO_LATCH_VSYNC;
 395        regw(temp, CCDCFG);
 396
 397        /*
 398         * configure the horizontal line offset. This is done by rounding up
 399         * width to a multiple of 16 pixels and multiply by two to account for
 400         * y:cb:cr 4:2:2 data
 401         */
 402        regw(((params->win.width * 2 + 31) >> 5), HSIZE);
 403
 404        /* configure the memory line offset */
 405        if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) {
 406                /* two fields are interleaved in memory */
 407                regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST);
 408        }
 409
 410        dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n");
 411}
 412
 413/*
 414 * ccdc_config_black_clamp()
 415 * configure parameters for Optical Black Clamp
 416 */
 417static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp)
 418{
 419        u32 val;
 420
 421        if (!bclamp->b_clamp_enable) {
 422                /* configure DCSub */
 423                regw(bclamp->dc_sub & CCDC_BLK_DC_SUB_MASK, DCSUB);
 424                regw(0x0000, CLAMP);
 425                return;
 426        }
 427        /* Enable the Black clamping, set sample lines and pixels */
 428        val = (bclamp->start_pixel & CCDC_BLK_ST_PXL_MASK) |
 429              ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) <<
 430                CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE;
 431        regw(val, CLAMP);
 432
 433        /* If Black clamping is enable then make dcsub 0 */
 434        val = (bclamp->sample_ln & CCDC_NUM_LINE_CALC_MASK)
 435                        << CCDC_NUM_LINE_CALC_SHIFT;
 436        regw(val, DCSUB);
 437}
 438
 439/*
 440 * ccdc_config_black_compense()
 441 * configure parameters for Black Compensation
 442 */
 443static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp)
 444{
 445        u32 val;
 446
 447        val = (bcomp->b & CCDC_BLK_COMP_MASK) |
 448                ((bcomp->gb & CCDC_BLK_COMP_MASK) <<
 449                CCDC_BLK_COMP_GB_COMP_SHIFT);
 450        regw(val, BLKCMP1);
 451
 452        val = ((bcomp->gr & CCDC_BLK_COMP_MASK) <<
 453                CCDC_BLK_COMP_GR_COMP_SHIFT) |
 454                ((bcomp->r & CCDC_BLK_COMP_MASK) <<
 455                CCDC_BLK_COMP_R_COMP_SHIFT);
 456        regw(val, BLKCMP0);
 457}
 458
 459/*
 460 * ccdc_write_dfc_entry()
 461 * write an entry in the dfc table.
 462 */
 463static int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc)
 464{
 465/* TODO This is to be re-visited and adjusted */
 466#define DFC_WRITE_WAIT_COUNT    1000
 467        u32 val, count = DFC_WRITE_WAIT_COUNT;
 468
 469        regw(dfc->dft_corr_vert[index], DFCMEM0);
 470        regw(dfc->dft_corr_horz[index], DFCMEM1);
 471        regw(dfc->dft_corr_sub1[index], DFCMEM2);
 472        regw(dfc->dft_corr_sub2[index], DFCMEM3);
 473        regw(dfc->dft_corr_sub3[index], DFCMEM4);
 474        /* set WR bit to write */
 475        val = regr(DFCMEMCTL) | CCDC_DFCMEMCTL_DFCMWR_MASK;
 476        regw(val, DFCMEMCTL);
 477
 478        /*
 479         * Assume, it is very short. If we get an error, we need to
 480         * adjust this value
 481         */
 482        while (regr(DFCMEMCTL) & CCDC_DFCMEMCTL_DFCMWR_MASK)
 483                count--;
 484        /*
 485         * TODO We expect the count to be non-zero to be successful. Adjust
 486         * the count if write requires more time
 487         */
 488
 489        if (count) {
 490                dev_err(ccdc_cfg.dev, "defect table write timeout !!!\n");
 491                return -1;
 492        }
 493        return 0;
 494}
 495
 496/*
 497 * ccdc_config_vdfc()
 498 * configure parameters for Vertical Defect Correction
 499 */
 500static int ccdc_config_vdfc(struct ccdc_vertical_dft *dfc)
 501{
 502        u32 val;
 503        int i;
 504
 505        /* Configure General Defect Correction. The table used is from IPIPE */
 506        val = dfc->gen_dft_en & CCDC_DFCCTL_GDFCEN_MASK;
 507
 508        /* Configure Vertical Defect Correction if needed */
 509        if (!dfc->ver_dft_en) {
 510                /* Enable only General Defect Correction */
 511                regw(val, DFCCTL);
 512                return 0;
 513        }
 514
 515        if (dfc->table_size > CCDC_DFT_TABLE_SIZE)
 516                return -EINVAL;
 517
 518        val |= CCDC_DFCCTL_VDFC_DISABLE;
 519        val |= (dfc->dft_corr_ctl.vdfcsl & CCDC_DFCCTL_VDFCSL_MASK) <<
 520                CCDC_DFCCTL_VDFCSL_SHIFT;
 521        val |= (dfc->dft_corr_ctl.vdfcuda & CCDC_DFCCTL_VDFCUDA_MASK) <<
 522                CCDC_DFCCTL_VDFCUDA_SHIFT;
 523        val |= (dfc->dft_corr_ctl.vdflsft & CCDC_DFCCTL_VDFLSFT_MASK) <<
 524                CCDC_DFCCTL_VDFLSFT_SHIFT;
 525        regw(val , DFCCTL);
 526
 527        /* clear address ptr to offset 0 */
 528        val = CCDC_DFCMEMCTL_DFCMARST_MASK << CCDC_DFCMEMCTL_DFCMARST_SHIFT;
 529
 530        /* write defect table entries */
 531        for (i = 0; i < dfc->table_size; i++) {
 532                /* increment address for non zero index */
 533                if (i != 0)
 534                        val = CCDC_DFCMEMCTL_INC_ADDR;
 535                regw(val, DFCMEMCTL);
 536                if (ccdc_write_dfc_entry(i, dfc) < 0)
 537                        return -EFAULT;
 538        }
 539
 540        /* update saturation level and enable dfc */
 541        regw(dfc->saturation_ctl & CCDC_VDC_DFCVSAT_MASK, DFCVSAT);
 542        val = regr(DFCCTL) | (CCDC_DFCCTL_VDFCEN_MASK <<
 543                        CCDC_DFCCTL_VDFCEN_SHIFT);
 544        regw(val, DFCCTL);
 545        return 0;
 546}
 547
 548/*
 549 * ccdc_config_csc()
 550 * configure parameters for color space conversion
 551 * Each register CSCM0-7 has two values in S8Q5 format.
 552 */
 553static void ccdc_config_csc(struct ccdc_csc *csc)
 554{
 555        u32 val1 = 0, val2;
 556        int i;
 557
 558        if (!csc->enable)
 559                return;
 560
 561        /* Enable the CSC sub-module */
 562        regw(CCDC_CSC_ENABLE, CSCCTL);
 563
 564        /* Converting the co-eff as per the format of the register */
 565        for (i = 0; i < CCDC_CSC_COEFF_TABLE_SIZE; i++) {
 566                if ((i % 2) == 0) {
 567                        /* CSCM - LSB */
 568                        val1 = (csc->coeff[i].integer &
 569                                CCDC_CSC_COEF_INTEG_MASK)
 570                                << CCDC_CSC_COEF_INTEG_SHIFT;
 571                        /*
 572                         * convert decimal part to binary. Use 2 decimal
 573                         * precision, user values range from .00 - 0.99
 574                         */
 575                        val1 |= (((csc->coeff[i].decimal &
 576                                CCDC_CSC_COEF_DECIMAL_MASK) *
 577                                CCDC_CSC_DEC_MAX) / 100);
 578                } else {
 579
 580                        /* CSCM - MSB */
 581                        val2 = (csc->coeff[i].integer &
 582                                CCDC_CSC_COEF_INTEG_MASK)
 583                                << CCDC_CSC_COEF_INTEG_SHIFT;
 584                        val2 |= (((csc->coeff[i].decimal &
 585                                 CCDC_CSC_COEF_DECIMAL_MASK) *
 586                                 CCDC_CSC_DEC_MAX) / 100);
 587                        val2 <<= CCDC_CSCM_MSB_SHIFT;
 588                        val2 |= val1;
 589                        regw(val2, (CSCM0 + ((i - 1) << 1)));
 590                }
 591        }
 592}
 593
 594/*
 595 * ccdc_config_color_patterns()
 596 * configure parameters for color patterns
 597 */
 598static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0,
 599                                       struct ccdc_col_pat *pat1)
 600{
 601        u32 val;
 602
 603        val = (pat0->olop | (pat0->olep << 2) | (pat0->elop << 4) |
 604                (pat0->elep << 6) | (pat1->olop << 8) | (pat1->olep << 10) |
 605                (pat1->elop << 12) | (pat1->elep << 14));
 606        regw(val, COLPTN);
 607}
 608
 609/* This function will configure CCDC for Raw mode image capture */
 610static int ccdc_config_raw(void)
 611{
 612        struct ccdc_params_raw *params = &ccdc_cfg.bayer;
 613        struct ccdc_config_params_raw *config_params =
 614                                        &ccdc_cfg.bayer.config_params;
 615        unsigned int val;
 616
 617        dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw...");
 618
 619        /* restore power on defaults to register */
 620        ccdc_restore_defaults();
 621
 622        /* CCDCFG register:
 623         * set CCD Not to swap input since input is RAW data
 624         * set FID detection function to Latch at V-Sync
 625         * set WENLOG - ccdc valid area to AND
 626         * set TRGSEL to WENBIT
 627         * set EXTRG to DISABLE
 628         * disable latching function on VSYNC - shadowed registers
 629         */
 630        regw(CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC |
 631             CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN |
 632             CCDC_CCDCFG_EXTRG_DISABLE | CCDC_LATCH_ON_VSYNC_DISABLE, CCDCFG);
 633
 634        /*
 635         * Set VDHD direction to input,  input type to raw input
 636         * normal data polarity, do not use external WEN
 637         */
 638        val = (CCDC_VDHDOUT_INPUT | CCDC_RAW_IP_MODE | CCDC_DATAPOL_NORMAL |
 639                CCDC_EXWEN_DISABLE);
 640
 641        /*
 642         * Configure the vertical sync polarity (MODESET.VDPOL), horizontal
 643         * sync polarity (MODESET.HDPOL), field id polarity (MODESET.FLDPOL),
 644         * frame format(progressive or interlace), & pixel format (Input mode)
 645         */
 646        val |= (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT) |
 647                ((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT) |
 648                ((params->fid_pol & CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT) |
 649                ((params->frm_fmt & CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) |
 650                ((params->pix_fmt & CCDC_PIX_FMT_MASK) << CCDC_PIX_FMT_SHIFT));
 651
 652        /* set pack for alaw compression */
 653        if ((config_params->data_sz == CCDC_DATA_8BITS) ||
 654             config_params->alaw.enable)
 655                val |= CCDC_DATA_PACK_ENABLE;
 656
 657        /* Configure for LPF */
 658        if (config_params->lpf_enable)
 659                val |= (config_params->lpf_enable & CCDC_LPF_MASK) <<
 660                        CCDC_LPF_SHIFT;
 661
 662        /* Configure the data shift */
 663        val |= (config_params->datasft & CCDC_DATASFT_MASK) <<
 664                CCDC_DATASFT_SHIFT;
 665        regw(val , MODESET);
 666        dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to MODESET...\n", val);
 667
 668        /* Configure the Median Filter threshold */
 669        regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT);
 670
 671        /* Configure GAMMAWD register. defaur 11-2, and Mosaic cfa pattern */
 672        val = CCDC_GAMMA_BITS_11_2 << CCDC_GAMMAWD_INPUT_SHIFT |
 673                CCDC_CFA_MOSAIC;
 674
 675        /* Enable and configure aLaw register if needed */
 676        if (config_params->alaw.enable) {
 677                val |= (CCDC_ALAW_ENABLE |
 678                        ((config_params->alaw.gamma_wd &
 679                        CCDC_ALAW_GAMMA_WD_MASK) <<
 680                        CCDC_GAMMAWD_INPUT_SHIFT));
 681        }
 682
 683        /* Configure Median filter1 & filter2 */
 684        val |= ((config_params->mfilt1 << CCDC_MFILT1_SHIFT) |
 685                (config_params->mfilt2 << CCDC_MFILT2_SHIFT));
 686
 687        regw(val, GAMMAWD);
 688        dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to GAMMAWD...\n", val);
 689
 690        /* configure video window */
 691        ccdc_setwin(&params->win, params->frm_fmt, 1);
 692
 693        /* Optical Clamp Averaging */
 694        ccdc_config_black_clamp(&config_params->blk_clamp);
 695
 696        /* Black level compensation */
 697        ccdc_config_black_compense(&config_params->blk_comp);
 698
 699        /* Vertical Defect Correction if needed */
 700        if (ccdc_config_vdfc(&config_params->vertical_dft) < 0)
 701                return -EFAULT;
 702
 703        /* color space conversion */
 704        ccdc_config_csc(&config_params->csc);
 705
 706        /* color pattern */
 707        ccdc_config_color_patterns(&config_params->col_pat_field0,
 708                                   &config_params->col_pat_field1);
 709
 710        /* Configure the Gain  & offset control */
 711        ccdc_config_gain_offset();
 712
 713        dev_dbg(ccdc_cfg.dev, "\nWriting %x to COLPTN...\n", val);
 714
 715        /* Configure DATAOFST  register */
 716        val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) <<
 717                CCDC_DATAOFST_H_SHIFT;
 718        val |= (config_params->data_offset.vert_offset & CCDC_DATAOFST_MASK) <<
 719                CCDC_DATAOFST_V_SHIFT;
 720        regw(val, DATAOFST);
 721
 722        /* configuring HSIZE register */
 723        val = (params->horz_flip_enable & CCDC_HSIZE_FLIP_MASK) <<
 724                CCDC_HSIZE_FLIP_SHIFT;
 725
 726        /* If pack 8 is enable then 1 pixel will take 1 byte */
 727        if ((config_params->data_sz == CCDC_DATA_8BITS) ||
 728             config_params->alaw.enable) {
 729                val |= (((params->win.width) + 31) >> 5) &
 730                        CCDC_HSIZE_VAL_MASK;
 731
 732                /* adjust to multiple of 32 */
 733                dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n",
 734                       (((params->win.width) + 31) >> 5) &
 735                        CCDC_HSIZE_VAL_MASK);
 736        } else {
 737                /* else one pixel will take 2 byte */
 738                val |= (((params->win.width * 2) + 31) >> 5) &
 739                        CCDC_HSIZE_VAL_MASK;
 740
 741                dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n",
 742                       (((params->win.width * 2) + 31) >> 5) &
 743                        CCDC_HSIZE_VAL_MASK);
 744        }
 745        regw(val, HSIZE);
 746
 747        /* Configure SDOFST register */
 748        if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
 749                if (params->image_invert_enable) {
 750                        /* For interlace inverse mode */
 751                        regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST);
 752                        dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
 753                                CCDC_SDOFST_INTERLACE_INVERSE);
 754                } else {
 755                        /* For interlace non inverse mode */
 756                        regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST);
 757                        dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
 758                                CCDC_SDOFST_INTERLACE_NORMAL);
 759                }
 760        } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) {
 761                if (params->image_invert_enable) {
 762                        /* For progessive inverse mode */
 763                        regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST);
 764                        dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
 765                                CCDC_SDOFST_PROGRESSIVE_INVERSE);
 766                } else {
 767                        /* For progessive non inverse mode */
 768                        regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST);
 769                        dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n",
 770                                CCDC_SDOFST_PROGRESSIVE_NORMAL);
 771                }
 772        }
 773        dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw...");
 774        return 0;
 775}
 776
 777static int ccdc_configure(void)
 778{
 779        if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
 780                return ccdc_config_raw();
 781        else
 782                ccdc_config_ycbcr();
 783        return 0;
 784}
 785
 786static int ccdc_set_buftype(enum ccdc_buftype buf_type)
 787{
 788        if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
 789                ccdc_cfg.bayer.buf_type = buf_type;
 790        else
 791                ccdc_cfg.ycbcr.buf_type = buf_type;
 792        return 0;
 793}
 794static enum ccdc_buftype ccdc_get_buftype(void)
 795{
 796        if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
 797                return ccdc_cfg.bayer.buf_type;
 798        return ccdc_cfg.ycbcr.buf_type;
 799}
 800
 801static int ccdc_enum_pix(u32 *pix, int i)
 802{
 803        int ret = -EINVAL;
 804        if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
 805                if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) {
 806                        *pix = ccdc_raw_bayer_pix_formats[i];
 807                        ret = 0;
 808                }
 809        } else {
 810                if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) {
 811                        *pix = ccdc_raw_yuv_pix_formats[i];
 812                        ret = 0;
 813                }
 814        }
 815        return ret;
 816}
 817
 818static int ccdc_set_pixel_format(u32 pixfmt)
 819{
 820        struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
 821
 822        if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
 823                ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW;
 824                if (pixfmt == V4L2_PIX_FMT_SBGGR8)
 825                        alaw->enable = 1;
 826                else if (pixfmt != V4L2_PIX_FMT_SBGGR16)
 827                        return -EINVAL;
 828        } else {
 829                if (pixfmt == V4L2_PIX_FMT_YUYV)
 830                        ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR;
 831                else if (pixfmt == V4L2_PIX_FMT_UYVY)
 832                        ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY;
 833                else
 834                        return -EINVAL;
 835        }
 836        return 0;
 837}
 838static u32 ccdc_get_pixel_format(void)
 839{
 840        struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw;
 841        u32 pixfmt;
 842
 843        if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
 844                if (alaw->enable)
 845                        pixfmt = V4L2_PIX_FMT_SBGGR8;
 846                else
 847                        pixfmt = V4L2_PIX_FMT_SBGGR16;
 848        else {
 849                if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR)
 850                        pixfmt = V4L2_PIX_FMT_YUYV;
 851                else
 852                        pixfmt = V4L2_PIX_FMT_UYVY;
 853        }
 854        return pixfmt;
 855}
 856static int ccdc_set_image_window(struct v4l2_rect *win)
 857{
 858        if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
 859                ccdc_cfg.bayer.win = *win;
 860        else
 861                ccdc_cfg.ycbcr.win = *win;
 862        return 0;
 863}
 864
 865static void ccdc_get_image_window(struct v4l2_rect *win)
 866{
 867        if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
 868                *win = ccdc_cfg.bayer.win;
 869        else
 870                *win = ccdc_cfg.ycbcr.win;
 871}
 872
 873static unsigned int ccdc_get_line_length(void)
 874{
 875        struct ccdc_config_params_raw *config_params =
 876                                &ccdc_cfg.bayer.config_params;
 877        unsigned int len;
 878
 879        if (ccdc_cfg.if_type == VPFE_RAW_BAYER) {
 880                if ((config_params->alaw.enable) ||
 881                    (config_params->data_sz == CCDC_DATA_8BITS))
 882                        len = ccdc_cfg.bayer.win.width;
 883                else
 884                        len = ccdc_cfg.bayer.win.width * 2;
 885        } else
 886                len = ccdc_cfg.ycbcr.win.width * 2;
 887        return ALIGN(len, 32);
 888}
 889
 890static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt)
 891{
 892        if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
 893                ccdc_cfg.bayer.frm_fmt = frm_fmt;
 894        else
 895                ccdc_cfg.ycbcr.frm_fmt = frm_fmt;
 896        return 0;
 897}
 898
 899static enum ccdc_frmfmt ccdc_get_frame_format(void)
 900{
 901        if (ccdc_cfg.if_type == VPFE_RAW_BAYER)
 902                return ccdc_cfg.bayer.frm_fmt;
 903        else
 904                return ccdc_cfg.ycbcr.frm_fmt;
 905}
 906
 907static int ccdc_getfid(void)
 908{
 909        return  (regr(MODESET) >> 15) & 1;
 910}
 911
 912/* misc operations */
 913static inline void ccdc_setfbaddr(unsigned long addr)
 914{
 915        regw((addr >> 21) & 0x007f, STADRH);
 916        regw((addr >> 5) & 0x0ffff, STADRL);
 917}
 918
 919static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params)
 920{
 921        ccdc_cfg.if_type = params->if_type;
 922
 923        switch (params->if_type) {
 924        case VPFE_BT656:
 925        case VPFE_YCBCR_SYNC_16:
 926        case VPFE_YCBCR_SYNC_8:
 927                ccdc_cfg.ycbcr.vd_pol = params->vdpol;
 928                ccdc_cfg.ycbcr.hd_pol = params->hdpol;
 929                break;
 930        default:
 931                /* TODO add support for raw bayer here */
 932                return -EINVAL;
 933        }
 934        return 0;
 935}
 936
 937static struct ccdc_hw_device ccdc_hw_dev = {
 938        .name = "DM355 CCDC",
 939        .owner = THIS_MODULE,
 940        .hw_ops = {
 941                .open = ccdc_open,
 942                .close = ccdc_close,
 943                .enable = ccdc_enable,
 944                .enable_out_to_sdram = ccdc_enable_output_to_sdram,
 945                .set_hw_if_params = ccdc_set_hw_if_params,
 946                .set_params = ccdc_set_params,
 947                .configure = ccdc_configure,
 948                .set_buftype = ccdc_set_buftype,
 949                .get_buftype = ccdc_get_buftype,
 950                .enum_pix = ccdc_enum_pix,
 951                .set_pixel_format = ccdc_set_pixel_format,
 952                .get_pixel_format = ccdc_get_pixel_format,
 953                .set_frame_format = ccdc_set_frame_format,
 954                .get_frame_format = ccdc_get_frame_format,
 955                .set_image_window = ccdc_set_image_window,
 956                .get_image_window = ccdc_get_image_window,
 957                .get_line_length = ccdc_get_line_length,
 958                .setfbaddr = ccdc_setfbaddr,
 959                .getfid = ccdc_getfid,
 960        },
 961};
 962
 963static int dm355_ccdc_probe(struct platform_device *pdev)
 964{
 965        void (*setup_pinmux)(void);
 966        struct resource *res;
 967        int status = 0;
 968
 969        /*
 970         * first try to register with vpfe. If not correct platform, then we
 971         * don't have to iomap
 972         */
 973        status = vpfe_register_ccdc_device(&ccdc_hw_dev);
 974        if (status < 0)
 975                return status;
 976
 977        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 978        if (!res) {
 979                status = -ENODEV;
 980                goto fail_nores;
 981        }
 982
 983        res = request_mem_region(res->start, resource_size(res), res->name);
 984        if (!res) {
 985                status = -EBUSY;
 986                goto fail_nores;
 987        }
 988
 989        ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res));
 990        if (!ccdc_cfg.base_addr) {
 991                status = -ENOMEM;
 992                goto fail_nomem;
 993        }
 994
 995        /* Platform data holds setup_pinmux function ptr */
 996        if (NULL == pdev->dev.platform_data) {
 997                status = -ENODEV;
 998                goto fail_nomap;
 999        }
1000        setup_pinmux = pdev->dev.platform_data;
1001        /*
1002         * setup Mux configuration for ccdc which may be different for
1003         * different SoCs using this CCDC
1004         */
1005        setup_pinmux();
1006        ccdc_cfg.dev = &pdev->dev;
1007        printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name);
1008        return 0;
1009fail_nomap:
1010        iounmap(ccdc_cfg.base_addr);
1011fail_nomem:
1012        release_mem_region(res->start, resource_size(res));
1013fail_nores:
1014        vpfe_unregister_ccdc_device(&ccdc_hw_dev);
1015        return status;
1016}
1017
1018static int dm355_ccdc_remove(struct platform_device *pdev)
1019{
1020        struct resource *res;
1021
1022        iounmap(ccdc_cfg.base_addr);
1023        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1024        if (res)
1025                release_mem_region(res->start, resource_size(res));
1026        vpfe_unregister_ccdc_device(&ccdc_hw_dev);
1027        return 0;
1028}
1029
1030static struct platform_driver dm355_ccdc_driver = {
1031        .driver = {
1032                .name   = "dm355_ccdc",
1033        },
1034        .remove = dm355_ccdc_remove,
1035        .probe = dm355_ccdc_probe,
1036};
1037
1038module_platform_driver(dm355_ccdc_driver);
1039