isif.c (29594B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2008-2009 Texas Instruments Inc 4 * 5 * Image Sensor Interface (ISIF) driver 6 * 7 * This driver is for configuring the ISIF IP available on DM365 or any other 8 * TI SoCs. This is used for capturing yuv or bayer video or image data 9 * from a decoder or sensor. This IP is similar to the CCDC IP on DM355 10 * and DM6446, but with enhanced or additional ip blocks. The driver 11 * configures the ISIF upon commands from the vpfe bridge driver through 12 * ccdc_hw_device interface. 13 * 14 * TODO: 1) Raw bayer parameter settings and bayer capture 15 * 2) Add support for control ioctl 16 */ 17#include <linux/delay.h> 18#include <linux/platform_device.h> 19#include <linux/uaccess.h> 20#include <linux/io.h> 21#include <linux/videodev2.h> 22#include <linux/err.h> 23#include <linux/module.h> 24 25#include <media/davinci/isif.h> 26#include <media/davinci/vpss.h> 27 28#include "isif_regs.h" 29#include "ccdc_hw_device.h" 30 31/* Defaults for module configuration parameters */ 32static const struct isif_config_params_raw isif_config_defaults = { 33 .linearize = { 34 .en = 0, 35 .corr_shft = ISIF_NO_SHIFT, 36 .scale_fact = {1, 0}, 37 }, 38 .df_csc = { 39 .df_or_csc = 0, 40 .csc = { 41 .en = 0, 42 }, 43 }, 44 .dfc = { 45 .en = 0, 46 }, 47 .bclamp = { 48 .en = 0, 49 }, 50 .gain_offset = { 51 .gain = { 52 .r_ye = {1, 0}, 53 .gr_cy = {1, 0}, 54 .gb_g = {1, 0}, 55 .b_mg = {1, 0}, 56 }, 57 }, 58 .culling = { 59 .hcpat_odd = 0xff, 60 .hcpat_even = 0xff, 61 .vcpat = 0xff, 62 }, 63 .compress = { 64 .alg = ISIF_ALAW, 65 }, 66}; 67 68/* ISIF operation configuration */ 69static struct isif_oper_config { 70 struct device *dev; 71 enum vpfe_hw_if_type if_type; 72 struct isif_ycbcr_config ycbcr; 73 struct isif_params_raw bayer; 74 enum isif_data_pack data_pack; 75 /* ISIF base address */ 76 void __iomem *base_addr; 77 /* ISIF Linear Table 0 */ 78 void __iomem *linear_tbl0_addr; 79 /* ISIF Linear Table 1 */ 80 void __iomem *linear_tbl1_addr; 81} isif_cfg = { 82 .ycbcr = { 83 .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, 84 .frm_fmt = CCDC_FRMFMT_INTERLACED, 85 .win = ISIF_WIN_NTSC, 86 .fid_pol = VPFE_PINPOL_POSITIVE, 87 .vd_pol = VPFE_PINPOL_POSITIVE, 88 .hd_pol = VPFE_PINPOL_POSITIVE, 89 .pix_order = CCDC_PIXORDER_CBYCRY, 90 .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED, 91 }, 92 .bayer = { 93 .pix_fmt = CCDC_PIXFMT_RAW, 94 .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, 95 .win = ISIF_WIN_VGA, 96 .fid_pol = VPFE_PINPOL_POSITIVE, 97 .vd_pol = VPFE_PINPOL_POSITIVE, 98 .hd_pol = VPFE_PINPOL_POSITIVE, 99 .gain = { 100 .r_ye = {1, 0}, 101 .gr_cy = {1, 0}, 102 .gb_g = {1, 0}, 103 .b_mg = {1, 0}, 104 }, 105 .cfa_pat = ISIF_CFA_PAT_MOSAIC, 106 .data_msb = ISIF_BIT_MSB_11, 107 .config_params = { 108 .data_shift = ISIF_NO_SHIFT, 109 .col_pat_field0 = { 110 .olop = ISIF_GREEN_BLUE, 111 .olep = ISIF_BLUE, 112 .elop = ISIF_RED, 113 .elep = ISIF_GREEN_RED, 114 }, 115 .col_pat_field1 = { 116 .olop = ISIF_GREEN_BLUE, 117 .olep = ISIF_BLUE, 118 .elop = ISIF_RED, 119 .elep = ISIF_GREEN_RED, 120 }, 121 .test_pat_gen = 0, 122 }, 123 }, 124 .data_pack = ISIF_DATA_PACK8, 125}; 126 127/* Raw Bayer formats */ 128static const u32 isif_raw_bayer_pix_formats[] = { 129 V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; 130 131/* Raw YUV formats */ 132static const u32 isif_raw_yuv_pix_formats[] = { 133 V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; 134 135/* register access routines */ 136static inline u32 regr(u32 offset) 137{ 138 return __raw_readl(isif_cfg.base_addr + offset); 139} 140 141static inline void regw(u32 val, u32 offset) 142{ 143 __raw_writel(val, isif_cfg.base_addr + offset); 144} 145 146/* reg_modify() - read, modify and write register */ 147static inline u32 reg_modify(u32 mask, u32 val, u32 offset) 148{ 149 u32 new_val = (regr(offset) & ~mask) | (val & mask); 150 151 regw(new_val, offset); 152 return new_val; 153} 154 155static inline void regw_lin_tbl(u32 val, u32 offset, int i) 156{ 157 if (!i) 158 __raw_writel(val, isif_cfg.linear_tbl0_addr + offset); 159 else 160 __raw_writel(val, isif_cfg.linear_tbl1_addr + offset); 161} 162 163static void isif_disable_all_modules(void) 164{ 165 /* disable BC */ 166 regw(0, CLAMPCFG); 167 /* disable vdfc */ 168 regw(0, DFCCTL); 169 /* disable CSC */ 170 regw(0, CSCCTL); 171 /* disable linearization */ 172 regw(0, LINCFG0); 173 /* disable other modules here as they are supported */ 174} 175 176static void isif_enable(int en) 177{ 178 if (!en) { 179 /* Before disable isif, disable all ISIF modules */ 180 isif_disable_all_modules(); 181 /* 182 * wait for next VD. Assume lowest scan rate is 12 Hz. So 183 * 100 msec delay is good enough 184 */ 185 msleep(100); 186 } 187 reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN); 188} 189 190static void isif_enable_output_to_sdram(int en) 191{ 192 reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN); 193} 194 195static void isif_config_culling(struct isif_cul *cul) 196{ 197 u32 val; 198 199 /* Horizontal pattern */ 200 val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd; 201 regw(val, CULH); 202 203 /* vertical pattern */ 204 regw(cul->vcpat, CULV); 205 206 /* LPF */ 207 reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT, 208 cul->en_lpf << ISIF_LPF_SHIFT, MODESET); 209} 210 211static void isif_config_gain_offset(void) 212{ 213 struct isif_gain_offsets_adj *gain_off_p = 214 &isif_cfg.bayer.config_params.gain_offset; 215 u32 val; 216 217 val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) | 218 (!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) | 219 (!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) | 220 (!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) | 221 (!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) | 222 (!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT); 223 224 reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD); 225 226 val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) | 227 gain_off_p->gain.r_ye.decimal; 228 regw(val, CRGAIN); 229 230 val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) | 231 gain_off_p->gain.gr_cy.decimal; 232 regw(val, CGRGAIN); 233 234 val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) | 235 gain_off_p->gain.gb_g.decimal; 236 regw(val, CGBGAIN); 237 238 val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) | 239 gain_off_p->gain.b_mg.decimal; 240 regw(val, CBGAIN); 241 242 regw(gain_off_p->offset, COFSTA); 243} 244 245static void isif_restore_defaults(void) 246{ 247 enum vpss_ccdc_source_sel source = VPSS_CCDCIN; 248 249 dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults..."); 250 isif_cfg.bayer.config_params = isif_config_defaults; 251 /* Enable clock to ISIF, IPIPEIF and BL */ 252 vpss_enable_clock(VPSS_CCDC_CLOCK, 1); 253 vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); 254 vpss_enable_clock(VPSS_BL_CLOCK, 1); 255 /* Set default offset and gain */ 256 isif_config_gain_offset(); 257 vpss_select_ccdc_source(source); 258 dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults..."); 259} 260 261static int isif_open(struct device *device) 262{ 263 isif_restore_defaults(); 264 return 0; 265} 266 267/* This function will configure the window size to be capture in ISIF reg */ 268static void isif_setwin(struct v4l2_rect *image_win, 269 enum ccdc_frmfmt frm_fmt, int ppc) 270{ 271 int horz_start, horz_nr_pixels; 272 int vert_start, vert_nr_lines; 273 int mid_img = 0; 274 275 dev_dbg(isif_cfg.dev, "\nStarting isif_setwin..."); 276 /* 277 * ppc - per pixel count. indicates how many pixels per cell 278 * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. 279 * raw capture this is 1 280 */ 281 horz_start = image_win->left << (ppc - 1); 282 horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; 283 284 /* Writing the horizontal info into the registers */ 285 regw(horz_start & START_PX_HOR_MASK, SPH); 286 regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH); 287 vert_start = image_win->top; 288 289 if (frm_fmt == CCDC_FRMFMT_INTERLACED) { 290 vert_nr_lines = (image_win->height >> 1) - 1; 291 vert_start >>= 1; 292 /* To account for VD since line 0 doesn't have any data */ 293 vert_start += 1; 294 } else { 295 /* To account for VD since line 0 doesn't have any data */ 296 vert_start += 1; 297 vert_nr_lines = image_win->height - 1; 298 /* configure VDINT0 and VDINT1 */ 299 mid_img = vert_start + (image_win->height / 2); 300 regw(mid_img, VDINT1); 301 } 302 303 regw(0, VDINT0); 304 regw(vert_start & START_VER_ONE_MASK, SLV0); 305 regw(vert_start & START_VER_TWO_MASK, SLV1); 306 regw(vert_nr_lines & NUM_LINES_VER, LNV); 307} 308 309static void isif_config_bclamp(struct isif_black_clamp *bc) 310{ 311 u32 val; 312 313 /* 314 * DC Offset is always added to image data irrespective of bc enable 315 * status 316 */ 317 regw(bc->dc_offset, CLDCOFST); 318 319 if (bc->en) { 320 val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT; 321 322 /* Enable BC and horizontal clamp calculation parameters */ 323 val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT); 324 325 regw(val, CLAMPCFG); 326 327 if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) { 328 /* 329 * Window count for calculation 330 * Base window selection 331 * pixel limit 332 * Horizontal size of window 333 * vertical size of the window 334 * Horizontal start position of the window 335 * Vertical start position of the window 336 */ 337 val = bc->horz.win_count_calc | 338 ((!!bc->horz.base_win_sel_calc) << 339 ISIF_HORZ_BC_WIN_SEL_SHIFT) | 340 ((!!bc->horz.clamp_pix_limit) << 341 ISIF_HORZ_BC_PIX_LIMIT_SHIFT) | 342 (bc->horz.win_h_sz_calc << 343 ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) | 344 (bc->horz.win_v_sz_calc << 345 ISIF_HORZ_BC_WIN_V_SIZE_SHIFT); 346 regw(val, CLHWIN0); 347 348 regw(bc->horz.win_start_h_calc, CLHWIN1); 349 regw(bc->horz.win_start_v_calc, CLHWIN2); 350 } 351 352 /* vertical clamp calculation parameters */ 353 354 /* Reset clamp value sel for previous line */ 355 val |= 356 (bc->vert.reset_val_sel << ISIF_VERT_BC_RST_VAL_SEL_SHIFT) | 357 (bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT); 358 regw(val, CLVWIN0); 359 360 /* Optical Black horizontal start position */ 361 regw(bc->vert.ob_start_h, CLVWIN1); 362 /* Optical Black vertical start position */ 363 regw(bc->vert.ob_start_v, CLVWIN2); 364 /* Optical Black vertical size for calculation */ 365 regw(bc->vert.ob_v_sz_calc, CLVWIN3); 366 /* Vertical start position for BC subtraction */ 367 regw(bc->vert_start_sub, CLSV); 368 } 369} 370 371static void isif_config_linearization(struct isif_linearize *linearize) 372{ 373 u32 val, i; 374 375 if (!linearize->en) { 376 regw(0, LINCFG0); 377 return; 378 } 379 380 /* shift value for correction & enable linearization (set lsb) */ 381 val = (linearize->corr_shft << ISIF_LIN_CORRSFT_SHIFT) | 1; 382 regw(val, LINCFG0); 383 384 /* Scale factor */ 385 val = ((!!linearize->scale_fact.integer) << 386 ISIF_LIN_SCALE_FACT_INTEG_SHIFT) | 387 linearize->scale_fact.decimal; 388 regw(val, LINCFG1); 389 390 for (i = 0; i < ISIF_LINEAR_TAB_SIZE; i++) { 391 if (i % 2) 392 regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 1); 393 else 394 regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 0); 395 } 396} 397 398static int isif_config_dfc(struct isif_dfc *vdfc) 399{ 400 /* initialize retries to loop for max ~ 250 usec */ 401 u32 val, count, retries = loops_per_jiffy / (4000/HZ); 402 int i; 403 404 if (!vdfc->en) 405 return 0; 406 407 /* Correction mode */ 408 val = (vdfc->corr_mode << ISIF_VDFC_CORR_MOD_SHIFT); 409 410 /* Correct whole line or partial */ 411 if (vdfc->corr_whole_line) 412 val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT; 413 414 /* level shift value */ 415 val |= vdfc->def_level_shift << ISIF_VDFC_LEVEL_SHFT_SHIFT; 416 417 regw(val, DFCCTL); 418 419 /* Defect saturation level */ 420 regw(vdfc->def_sat_level, VDFSATLV); 421 422 regw(vdfc->table[0].pos_vert, DFCMEM0); 423 regw(vdfc->table[0].pos_horz, DFCMEM1); 424 if (vdfc->corr_mode == ISIF_VDFC_NORMAL || 425 vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { 426 regw(vdfc->table[0].level_at_pos, DFCMEM2); 427 regw(vdfc->table[0].level_up_pixels, DFCMEM3); 428 regw(vdfc->table[0].level_low_pixels, DFCMEM4); 429 } 430 431 /* set DFCMARST and set DFCMWR */ 432 val = regr(DFCMEMCTL) | (1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT) | 1; 433 regw(val, DFCMEMCTL); 434 435 count = retries; 436 while (count && (regr(DFCMEMCTL) & 0x1)) 437 count--; 438 439 if (!count) { 440 dev_dbg(isif_cfg.dev, "defect table write timeout !!!\n"); 441 return -1; 442 } 443 444 for (i = 1; i < vdfc->num_vdefects; i++) { 445 regw(vdfc->table[i].pos_vert, DFCMEM0); 446 regw(vdfc->table[i].pos_horz, DFCMEM1); 447 if (vdfc->corr_mode == ISIF_VDFC_NORMAL || 448 vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { 449 regw(vdfc->table[i].level_at_pos, DFCMEM2); 450 regw(vdfc->table[i].level_up_pixels, DFCMEM3); 451 regw(vdfc->table[i].level_low_pixels, DFCMEM4); 452 } 453 val = regr(DFCMEMCTL); 454 /* clear DFCMARST and set DFCMWR */ 455 val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT); 456 val |= 1; 457 regw(val, DFCMEMCTL); 458 459 count = retries; 460 while (count && (regr(DFCMEMCTL) & 0x1)) 461 count--; 462 463 if (!count) { 464 dev_err(isif_cfg.dev, 465 "defect table write timeout !!!\n"); 466 return -1; 467 } 468 } 469 if (vdfc->num_vdefects < ISIF_VDFC_TABLE_SIZE) { 470 /* Extra cycle needed */ 471 regw(0, DFCMEM0); 472 regw(0x1FFF, DFCMEM1); 473 regw(1, DFCMEMCTL); 474 } 475 476 /* enable VDFC */ 477 reg_modify((1 << ISIF_VDFC_EN_SHIFT), (1 << ISIF_VDFC_EN_SHIFT), 478 DFCCTL); 479 return 0; 480} 481 482static void isif_config_csc(struct isif_df_csc *df_csc) 483{ 484 u32 val1 = 0, val2 = 0, i; 485 486 if (!df_csc->csc.en) { 487 regw(0, CSCCTL); 488 return; 489 } 490 for (i = 0; i < ISIF_CSC_NUM_COEFF; i++) { 491 if ((i % 2) == 0) { 492 /* CSCM - LSB */ 493 val1 = (df_csc->csc.coeff[i].integer << 494 ISIF_CSC_COEF_INTEG_SHIFT) | 495 df_csc->csc.coeff[i].decimal; 496 } else { 497 498 /* CSCM - MSB */ 499 val2 = (df_csc->csc.coeff[i].integer << 500 ISIF_CSC_COEF_INTEG_SHIFT) | 501 df_csc->csc.coeff[i].decimal; 502 val2 <<= ISIF_CSCM_MSB_SHIFT; 503 val2 |= val1; 504 regw(val2, (CSCM0 + ((i - 1) << 1))); 505 } 506 } 507 508 /* program the active area */ 509 regw(df_csc->start_pix, FMTSPH); 510 /* 511 * one extra pixel as required for CSC. Actually number of 512 * pixel - 1 should be configured in this register. So we 513 * need to subtract 1 before writing to FMTSPH, but we will 514 * not do this since csc requires one extra pixel 515 */ 516 regw(df_csc->num_pixels, FMTLNH); 517 regw(df_csc->start_line, FMTSLV); 518 /* 519 * one extra line as required for CSC. See reason documented for 520 * num_pixels 521 */ 522 regw(df_csc->num_lines, FMTLNV); 523 524 /* Enable CSC */ 525 regw(1, CSCCTL); 526} 527 528static int isif_config_raw(void) 529{ 530 struct isif_params_raw *params = &isif_cfg.bayer; 531 struct isif_config_params_raw *module_params = 532 &isif_cfg.bayer.config_params; 533 struct vpss_pg_frame_size frame_size; 534 struct vpss_sync_pol sync; 535 u32 val; 536 537 dev_dbg(isif_cfg.dev, "\nStarting isif_config_raw..\n"); 538 539 /* 540 * Configure CCDCFG register:- 541 * Set CCD Not to swap input since input is RAW data 542 * Set FID detection function to Latch at V-Sync 543 * Set WENLOG - isif valid area 544 * Set TRGSEL 545 * Set EXTRG 546 * Packed to 8 or 16 bits 547 */ 548 549 val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC | 550 ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN | 551 ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg.data_pack; 552 553 dev_dbg(isif_cfg.dev, "Writing 0x%x to ...CCDCFG \n", val); 554 regw(val, CCDCFG); 555 556 /* 557 * Configure the vertical sync polarity(MODESET.VDPOL) 558 * Configure the horizontal sync polarity (MODESET.HDPOL) 559 * Configure frame id polarity (MODESET.FLDPOL) 560 * Configure data polarity 561 * Configure External WEN Selection 562 * Configure frame format(progressive or interlace) 563 * Configure pixel format (Input mode) 564 * Configure the data shift 565 */ 566 567 val = ISIF_VDHDOUT_INPUT | (params->vd_pol << ISIF_VD_POL_SHIFT) | 568 (params->hd_pol << ISIF_HD_POL_SHIFT) | 569 (params->fid_pol << ISIF_FID_POL_SHIFT) | 570 (ISIF_DATAPOL_NORMAL << ISIF_DATAPOL_SHIFT) | 571 (ISIF_EXWEN_DISABLE << ISIF_EXWEN_SHIFT) | 572 (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | 573 (params->pix_fmt << ISIF_INPUT_SHIFT) | 574 (params->config_params.data_shift << ISIF_DATASFT_SHIFT); 575 576 regw(val, MODESET); 577 dev_dbg(isif_cfg.dev, "Writing 0x%x to MODESET...\n", val); 578 579 /* 580 * Configure GAMMAWD register 581 * CFA pattern setting 582 */ 583 val = params->cfa_pat << ISIF_GAMMAWD_CFA_SHIFT; 584 585 /* Gamma msb */ 586 if (module_params->compress.alg == ISIF_ALAW) 587 val |= ISIF_ALAW_ENABLE; 588 589 val |= (params->data_msb << ISIF_ALAW_GAMMA_WD_SHIFT); 590 regw(val, CGAMMAWD); 591 592 /* Configure DPCM compression settings */ 593 if (module_params->compress.alg == ISIF_DPCM) { 594 val = BIT(ISIF_DPCM_EN_SHIFT) | 595 (module_params->compress.pred << 596 ISIF_DPCM_PREDICTOR_SHIFT); 597 } 598 599 regw(val, MISC); 600 601 /* Configure Gain & Offset */ 602 isif_config_gain_offset(); 603 604 /* Configure Color pattern */ 605 val = (params->config_params.col_pat_field0.olop) | 606 (params->config_params.col_pat_field0.olep << 2) | 607 (params->config_params.col_pat_field0.elop << 4) | 608 (params->config_params.col_pat_field0.elep << 6) | 609 (params->config_params.col_pat_field1.olop << 8) | 610 (params->config_params.col_pat_field1.olep << 10) | 611 (params->config_params.col_pat_field1.elop << 12) | 612 (params->config_params.col_pat_field1.elep << 14); 613 regw(val, CCOLP); 614 dev_dbg(isif_cfg.dev, "Writing %x to CCOLP ...\n", val); 615 616 /* Configure HSIZE register */ 617 val = (!!params->horz_flip_en) << ISIF_HSIZE_FLIP_SHIFT; 618 619 /* calculate line offset in 32 bytes based on pack value */ 620 if (isif_cfg.data_pack == ISIF_PACK_8BIT) 621 val |= ((params->win.width + 31) >> 5); 622 else if (isif_cfg.data_pack == ISIF_PACK_12BIT) 623 val |= (((params->win.width + 624 (params->win.width >> 2)) + 31) >> 5); 625 else 626 val |= (((params->win.width * 2) + 31) >> 5); 627 regw(val, HSIZE); 628 629 /* Configure SDOFST register */ 630 if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { 631 if (params->image_invert_en) { 632 /* For interlace inverse mode */ 633 regw(0x4B6D, SDOFST); 634 dev_dbg(isif_cfg.dev, "Writing 0x4B6D to SDOFST...\n"); 635 } else { 636 /* For interlace non inverse mode */ 637 regw(0x0B6D, SDOFST); 638 dev_dbg(isif_cfg.dev, "Writing 0x0B6D to SDOFST...\n"); 639 } 640 } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { 641 if (params->image_invert_en) { 642 /* For progressive inverse mode */ 643 regw(0x4000, SDOFST); 644 dev_dbg(isif_cfg.dev, "Writing 0x4000 to SDOFST...\n"); 645 } else { 646 /* For progressive non inverse mode */ 647 regw(0x0000, SDOFST); 648 dev_dbg(isif_cfg.dev, "Writing 0x0000 to SDOFST...\n"); 649 } 650 } 651 652 /* Configure video window */ 653 isif_setwin(¶ms->win, params->frm_fmt, 1); 654 655 /* Configure Black Clamp */ 656 isif_config_bclamp(&module_params->bclamp); 657 658 /* Configure Vertical Defection Pixel Correction */ 659 if (isif_config_dfc(&module_params->dfc) < 0) 660 return -EFAULT; 661 662 if (!module_params->df_csc.df_or_csc) 663 /* Configure Color Space Conversion */ 664 isif_config_csc(&module_params->df_csc); 665 666 isif_config_linearization(&module_params->linearize); 667 668 /* Configure Culling */ 669 isif_config_culling(&module_params->culling); 670 671 /* Configure horizontal and vertical offsets(DFC,LSC,Gain) */ 672 regw(module_params->horz_offset, DATAHOFST); 673 regw(module_params->vert_offset, DATAVOFST); 674 675 /* Setup test pattern if enabled */ 676 if (params->config_params.test_pat_gen) { 677 /* Use the HD/VD pol settings from user */ 678 sync.ccdpg_hdpol = params->hd_pol; 679 sync.ccdpg_vdpol = params->vd_pol; 680 dm365_vpss_set_sync_pol(sync); 681 frame_size.hlpfr = isif_cfg.bayer.win.width; 682 frame_size.pplen = isif_cfg.bayer.win.height; 683 dm365_vpss_set_pg_frame_size(frame_size); 684 vpss_select_ccdc_source(VPSS_PGLPBK); 685 } 686 687 dev_dbg(isif_cfg.dev, "\nEnd of isif_config_ycbcr...\n"); 688 return 0; 689} 690 691static int isif_set_buftype(enum ccdc_buftype buf_type) 692{ 693 if (isif_cfg.if_type == VPFE_RAW_BAYER) 694 isif_cfg.bayer.buf_type = buf_type; 695 else 696 isif_cfg.ycbcr.buf_type = buf_type; 697 698 return 0; 699 700} 701static enum ccdc_buftype isif_get_buftype(void) 702{ 703 if (isif_cfg.if_type == VPFE_RAW_BAYER) 704 return isif_cfg.bayer.buf_type; 705 706 return isif_cfg.ycbcr.buf_type; 707} 708 709static int isif_enum_pix(u32 *pix, int i) 710{ 711 int ret = -EINVAL; 712 713 if (isif_cfg.if_type == VPFE_RAW_BAYER) { 714 if (i < ARRAY_SIZE(isif_raw_bayer_pix_formats)) { 715 *pix = isif_raw_bayer_pix_formats[i]; 716 ret = 0; 717 } 718 } else { 719 if (i < ARRAY_SIZE(isif_raw_yuv_pix_formats)) { 720 *pix = isif_raw_yuv_pix_formats[i]; 721 ret = 0; 722 } 723 } 724 725 return ret; 726} 727 728static int isif_set_pixel_format(unsigned int pixfmt) 729{ 730 if (isif_cfg.if_type == VPFE_RAW_BAYER) { 731 if (pixfmt == V4L2_PIX_FMT_SBGGR8) { 732 if ((isif_cfg.bayer.config_params.compress.alg != 733 ISIF_ALAW) && 734 (isif_cfg.bayer.config_params.compress.alg != 735 ISIF_DPCM)) { 736 dev_dbg(isif_cfg.dev, 737 "Either configure A-Law or DPCM\n"); 738 return -EINVAL; 739 } 740 isif_cfg.data_pack = ISIF_PACK_8BIT; 741 } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) { 742 isif_cfg.bayer.config_params.compress.alg = 743 ISIF_NO_COMPRESSION; 744 isif_cfg.data_pack = ISIF_PACK_16BIT; 745 } else 746 return -EINVAL; 747 isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; 748 } else { 749 if (pixfmt == V4L2_PIX_FMT_YUYV) 750 isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; 751 else if (pixfmt == V4L2_PIX_FMT_UYVY) 752 isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; 753 else 754 return -EINVAL; 755 isif_cfg.data_pack = ISIF_PACK_8BIT; 756 } 757 return 0; 758} 759 760static u32 isif_get_pixel_format(void) 761{ 762 u32 pixfmt; 763 764 if (isif_cfg.if_type == VPFE_RAW_BAYER) 765 if (isif_cfg.bayer.config_params.compress.alg == ISIF_ALAW || 766 isif_cfg.bayer.config_params.compress.alg == ISIF_DPCM) 767 pixfmt = V4L2_PIX_FMT_SBGGR8; 768 else 769 pixfmt = V4L2_PIX_FMT_SBGGR16; 770 else { 771 if (isif_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) 772 pixfmt = V4L2_PIX_FMT_YUYV; 773 else 774 pixfmt = V4L2_PIX_FMT_UYVY; 775 } 776 return pixfmt; 777} 778 779static int isif_set_image_window(struct v4l2_rect *win) 780{ 781 if (isif_cfg.if_type == VPFE_RAW_BAYER) { 782 isif_cfg.bayer.win.top = win->top; 783 isif_cfg.bayer.win.left = win->left; 784 isif_cfg.bayer.win.width = win->width; 785 isif_cfg.bayer.win.height = win->height; 786 } else { 787 isif_cfg.ycbcr.win.top = win->top; 788 isif_cfg.ycbcr.win.left = win->left; 789 isif_cfg.ycbcr.win.width = win->width; 790 isif_cfg.ycbcr.win.height = win->height; 791 } 792 return 0; 793} 794 795static void isif_get_image_window(struct v4l2_rect *win) 796{ 797 if (isif_cfg.if_type == VPFE_RAW_BAYER) 798 *win = isif_cfg.bayer.win; 799 else 800 *win = isif_cfg.ycbcr.win; 801} 802 803static unsigned int isif_get_line_length(void) 804{ 805 unsigned int len; 806 807 if (isif_cfg.if_type == VPFE_RAW_BAYER) { 808 if (isif_cfg.data_pack == ISIF_PACK_8BIT) 809 len = ((isif_cfg.bayer.win.width)); 810 else if (isif_cfg.data_pack == ISIF_PACK_12BIT) 811 len = (((isif_cfg.bayer.win.width * 2) + 812 (isif_cfg.bayer.win.width >> 2))); 813 else 814 len = (((isif_cfg.bayer.win.width * 2))); 815 } else 816 len = (((isif_cfg.ycbcr.win.width * 2))); 817 return ALIGN(len, 32); 818} 819 820static int isif_set_frame_format(enum ccdc_frmfmt frm_fmt) 821{ 822 if (isif_cfg.if_type == VPFE_RAW_BAYER) 823 isif_cfg.bayer.frm_fmt = frm_fmt; 824 else 825 isif_cfg.ycbcr.frm_fmt = frm_fmt; 826 return 0; 827} 828static enum ccdc_frmfmt isif_get_frame_format(void) 829{ 830 if (isif_cfg.if_type == VPFE_RAW_BAYER) 831 return isif_cfg.bayer.frm_fmt; 832 return isif_cfg.ycbcr.frm_fmt; 833} 834 835static int isif_getfid(void) 836{ 837 return (regr(MODESET) >> 15) & 0x1; 838} 839 840/* misc operations */ 841static void isif_setfbaddr(unsigned long addr) 842{ 843 regw((addr >> 21) & 0x07ff, CADU); 844 regw((addr >> 5) & 0x0ffff, CADL); 845} 846 847static int isif_set_hw_if_params(struct vpfe_hw_if_param *params) 848{ 849 isif_cfg.if_type = params->if_type; 850 851 switch (params->if_type) { 852 case VPFE_BT656: 853 case VPFE_BT656_10BIT: 854 case VPFE_YCBCR_SYNC_8: 855 isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT; 856 isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; 857 break; 858 case VPFE_BT1120: 859 case VPFE_YCBCR_SYNC_16: 860 isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT; 861 isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; 862 break; 863 case VPFE_RAW_BAYER: 864 isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; 865 break; 866 default: 867 dev_dbg(isif_cfg.dev, "Invalid interface type\n"); 868 return -EINVAL; 869 } 870 871 return 0; 872} 873 874/* This function will configure ISIF for YCbCr parameters. */ 875static int isif_config_ycbcr(void) 876{ 877 struct isif_ycbcr_config *params = &isif_cfg.ycbcr; 878 u32 modeset = 0, ccdcfg = 0; 879 880 dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr..."); 881 882 /* configure pixel format or input mode */ 883 modeset = modeset | (params->pix_fmt << ISIF_INPUT_SHIFT) | 884 (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | 885 (params->fid_pol << ISIF_FID_POL_SHIFT) | 886 (params->hd_pol << ISIF_HD_POL_SHIFT) | 887 (params->vd_pol << ISIF_VD_POL_SHIFT); 888 889 /* pack the data to 8-bit ISIFCFG */ 890 switch (isif_cfg.if_type) { 891 case VPFE_BT656: 892 if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { 893 dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); 894 return -EINVAL; 895 } 896 modeset |= (VPFE_PINPOL_NEGATIVE << ISIF_VD_POL_SHIFT); 897 regw(3, REC656IF); 898 ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR; 899 break; 900 case VPFE_BT656_10BIT: 901 if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { 902 dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); 903 return -EINVAL; 904 } 905 /* setup BT.656, embedded sync */ 906 regw(3, REC656IF); 907 /* enable 10 bit mode in ccdcfg */ 908 ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR | 909 ISIF_BW656_ENABLE; 910 break; 911 case VPFE_BT1120: 912 if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { 913 dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); 914 return -EINVAL; 915 } 916 regw(3, REC656IF); 917 break; 918 919 case VPFE_YCBCR_SYNC_8: 920 ccdcfg |= ISIF_DATA_PACK8; 921 ccdcfg |= ISIF_YCINSWP_YCBCR; 922 if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { 923 dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); 924 return -EINVAL; 925 } 926 break; 927 case VPFE_YCBCR_SYNC_16: 928 if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { 929 dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); 930 return -EINVAL; 931 } 932 break; 933 default: 934 /* should never come here */ 935 dev_dbg(isif_cfg.dev, "Invalid interface type\n"); 936 return -EINVAL; 937 } 938 939 regw(modeset, MODESET); 940 941 /* Set up pix order */ 942 ccdcfg |= params->pix_order << ISIF_PIX_ORDER_SHIFT; 943 944 regw(ccdcfg, CCDCFG); 945 946 /* configure video window */ 947 if ((isif_cfg.if_type == VPFE_BT1120) || 948 (isif_cfg.if_type == VPFE_YCBCR_SYNC_16)) 949 isif_setwin(¶ms->win, params->frm_fmt, 1); 950 else 951 isif_setwin(¶ms->win, params->frm_fmt, 2); 952 953 /* 954 * configure the horizontal line offset 955 * this is done by rounding up width to a multiple of 16 pixels 956 * and multiply by two to account for y:cb:cr 4:2:2 data 957 */ 958 regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE); 959 960 /* configure the memory line offset */ 961 if ((params->frm_fmt == CCDC_FRMFMT_INTERLACED) && 962 (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)) 963 /* two fields are interleaved in memory */ 964 regw(0x00000249, SDOFST); 965 966 return 0; 967} 968 969static int isif_configure(void) 970{ 971 if (isif_cfg.if_type == VPFE_RAW_BAYER) 972 return isif_config_raw(); 973 return isif_config_ycbcr(); 974} 975 976static int isif_close(struct device *device) 977{ 978 /* copy defaults to module params */ 979 isif_cfg.bayer.config_params = isif_config_defaults; 980 return 0; 981} 982 983static const struct ccdc_hw_device isif_hw_dev = { 984 .name = "ISIF", 985 .owner = THIS_MODULE, 986 .hw_ops = { 987 .open = isif_open, 988 .close = isif_close, 989 .enable = isif_enable, 990 .enable_out_to_sdram = isif_enable_output_to_sdram, 991 .set_hw_if_params = isif_set_hw_if_params, 992 .configure = isif_configure, 993 .set_buftype = isif_set_buftype, 994 .get_buftype = isif_get_buftype, 995 .enum_pix = isif_enum_pix, 996 .set_pixel_format = isif_set_pixel_format, 997 .get_pixel_format = isif_get_pixel_format, 998 .set_frame_format = isif_set_frame_format, 999 .get_frame_format = isif_get_frame_format, 1000 .set_image_window = isif_set_image_window, 1001 .get_image_window = isif_get_image_window, 1002 .get_line_length = isif_get_line_length, 1003 .setfbaddr = isif_setfbaddr, 1004 .getfid = isif_getfid, 1005 }, 1006}; 1007 1008static int isif_probe(struct platform_device *pdev) 1009{ 1010 void (*setup_pinmux)(void); 1011 struct resource *res; 1012 void __iomem *addr; 1013 int status = 0, i; 1014 1015 /* Platform data holds setup_pinmux function ptr */ 1016 if (!pdev->dev.platform_data) 1017 return -ENODEV; 1018 1019 /* 1020 * first try to register with vpfe. If not correct platform, then we 1021 * don't have to iomap 1022 */ 1023 status = vpfe_register_ccdc_device(&isif_hw_dev); 1024 if (status < 0) 1025 return status; 1026 1027 setup_pinmux = pdev->dev.platform_data; 1028 /* 1029 * setup Mux configuration for ccdc which may be different for 1030 * different SoCs using this CCDC 1031 */ 1032 setup_pinmux(); 1033 1034 i = 0; 1035 /* Get the ISIF base address, linearization table0 and table1 addr. */ 1036 while (i < 3) { 1037 res = platform_get_resource(pdev, IORESOURCE_MEM, i); 1038 if (!res) { 1039 status = -ENODEV; 1040 goto fail_nobase_res; 1041 } 1042 res = request_mem_region(res->start, resource_size(res), 1043 res->name); 1044 if (!res) { 1045 status = -EBUSY; 1046 goto fail_nobase_res; 1047 } 1048 addr = ioremap(res->start, resource_size(res)); 1049 if (!addr) { 1050 status = -ENOMEM; 1051 goto fail_base_iomap; 1052 } 1053 switch (i) { 1054 case 0: 1055 /* ISIF base address */ 1056 isif_cfg.base_addr = addr; 1057 break; 1058 case 1: 1059 /* ISIF linear tbl0 address */ 1060 isif_cfg.linear_tbl0_addr = addr; 1061 break; 1062 default: 1063 /* ISIF linear tbl0 address */ 1064 isif_cfg.linear_tbl1_addr = addr; 1065 break; 1066 } 1067 i++; 1068 } 1069 isif_cfg.dev = &pdev->dev; 1070 1071 printk(KERN_NOTICE "%s is registered with vpfe.\n", 1072 isif_hw_dev.name); 1073 return 0; 1074fail_base_iomap: 1075 release_mem_region(res->start, resource_size(res)); 1076 i--; 1077fail_nobase_res: 1078 if (isif_cfg.base_addr) { 1079 iounmap(isif_cfg.base_addr); 1080 isif_cfg.base_addr = NULL; 1081 } 1082 if (isif_cfg.linear_tbl0_addr) { 1083 iounmap(isif_cfg.linear_tbl0_addr); 1084 isif_cfg.linear_tbl0_addr = NULL; 1085 } 1086 1087 while (i >= 0) { 1088 res = platform_get_resource(pdev, IORESOURCE_MEM, i); 1089 if (res) 1090 release_mem_region(res->start, resource_size(res)); 1091 i--; 1092 } 1093 vpfe_unregister_ccdc_device(&isif_hw_dev); 1094 return status; 1095} 1096 1097static int isif_remove(struct platform_device *pdev) 1098{ 1099 struct resource *res; 1100 int i = 0; 1101 1102 iounmap(isif_cfg.base_addr); 1103 isif_cfg.base_addr = NULL; 1104 iounmap(isif_cfg.linear_tbl0_addr); 1105 isif_cfg.linear_tbl0_addr = NULL; 1106 iounmap(isif_cfg.linear_tbl1_addr); 1107 isif_cfg.linear_tbl1_addr = NULL; 1108 while (i < 3) { 1109 res = platform_get_resource(pdev, IORESOURCE_MEM, i); 1110 release_mem_region(res->start, resource_size(res)); 1111 i++; 1112 } 1113 vpfe_unregister_ccdc_device(&isif_hw_dev); 1114 return 0; 1115} 1116 1117static struct platform_driver isif_driver = { 1118 .driver = { 1119 .name = "isif", 1120 }, 1121 .remove = isif_remove, 1122 .probe = isif_probe, 1123}; 1124 1125module_platform_driver(isif_driver); 1126 1127MODULE_LICENSE("GPL");