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