162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Samsung s3c24xx/s3c64xx SoC CAMIF driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Sylwester Nawrocki <sylvester.nawrocki@gmail.com> 662306a36Sopenharmony_ci * Copyright (C) 2012 Tomasz Figa <tomasz.figa@gmail.com> 762306a36Sopenharmony_ci*/ 862306a36Sopenharmony_ci#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include "camif-regs.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define camif_write(_camif, _off, _val) writel(_val, (_camif)->io_base + (_off)) 1462306a36Sopenharmony_ci#define camif_read(_camif, _off) readl((_camif)->io_base + (_off)) 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_civoid camif_hw_reset(struct camif_dev *camif) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci u32 cfg; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CISRCFMT); 2162306a36Sopenharmony_ci cfg |= CISRCFMT_ITU601_8BIT; 2262306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CISRCFMT, cfg); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci /* S/W reset */ 2562306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); 2662306a36Sopenharmony_ci cfg |= CIGCTRL_SWRST; 2762306a36Sopenharmony_ci if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) 2862306a36Sopenharmony_ci cfg |= CIGCTRL_IRQ_LEVEL; 2962306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); 3062306a36Sopenharmony_ci udelay(10); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); 3362306a36Sopenharmony_ci cfg &= ~CIGCTRL_SWRST; 3462306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); 3562306a36Sopenharmony_ci udelay(10); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_civoid camif_hw_clear_pending_irq(struct camif_vp *vp) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci u32 cfg = camif_read(vp->camif, S3C_CAMIF_REG_CIGCTRL); 4162306a36Sopenharmony_ci cfg |= CIGCTRL_IRQ_CLR(vp->id); 4262306a36Sopenharmony_ci camif_write(vp->camif, S3C_CAMIF_REG_CIGCTRL, cfg); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Sets video test pattern (off, color bar, horizontal or vertical gradient). 4762306a36Sopenharmony_ci * External sensor pixel clock must be active for the test pattern to work. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_civoid camif_hw_set_test_pattern(struct camif_dev *camif, unsigned int pattern) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci u32 cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); 5262306a36Sopenharmony_ci cfg &= ~CIGCTRL_TESTPATTERN_MASK; 5362306a36Sopenharmony_ci cfg |= (pattern << 27); 5462306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_civoid camif_hw_set_effect(struct camif_dev *camif, unsigned int effect, 5862306a36Sopenharmony_ci unsigned int cr, unsigned int cb) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci static const struct v4l2_control colorfx[] = { 6162306a36Sopenharmony_ci { V4L2_COLORFX_NONE, CIIMGEFF_FIN_BYPASS }, 6262306a36Sopenharmony_ci { V4L2_COLORFX_BW, CIIMGEFF_FIN_ARBITRARY }, 6362306a36Sopenharmony_ci { V4L2_COLORFX_SEPIA, CIIMGEFF_FIN_ARBITRARY }, 6462306a36Sopenharmony_ci { V4L2_COLORFX_NEGATIVE, CIIMGEFF_FIN_NEGATIVE }, 6562306a36Sopenharmony_ci { V4L2_COLORFX_ART_FREEZE, CIIMGEFF_FIN_ARTFREEZE }, 6662306a36Sopenharmony_ci { V4L2_COLORFX_EMBOSS, CIIMGEFF_FIN_EMBOSSING }, 6762306a36Sopenharmony_ci { V4L2_COLORFX_SILHOUETTE, CIIMGEFF_FIN_SILHOUETTE }, 6862306a36Sopenharmony_ci { V4L2_COLORFX_SET_CBCR, CIIMGEFF_FIN_ARBITRARY }, 6962306a36Sopenharmony_ci }; 7062306a36Sopenharmony_ci unsigned int i, cfg; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(colorfx); i++) 7362306a36Sopenharmony_ci if (colorfx[i].id == effect) 7462306a36Sopenharmony_ci break; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (i == ARRAY_SIZE(colorfx)) 7762306a36Sopenharmony_ci return; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGEFF(camif->vp->offset)); 8062306a36Sopenharmony_ci /* Set effect */ 8162306a36Sopenharmony_ci cfg &= ~CIIMGEFF_FIN_MASK; 8262306a36Sopenharmony_ci cfg |= colorfx[i].value; 8362306a36Sopenharmony_ci /* Set both paths */ 8462306a36Sopenharmony_ci if (camif->variant->ip_revision >= S3C6400_CAMIF_IP_REV) { 8562306a36Sopenharmony_ci if (effect == V4L2_COLORFX_NONE) 8662306a36Sopenharmony_ci cfg &= ~CIIMGEFF_IE_ENABLE_MASK; 8762306a36Sopenharmony_ci else 8862306a36Sopenharmony_ci cfg |= CIIMGEFF_IE_ENABLE_MASK; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci cfg &= ~CIIMGEFF_PAT_CBCR_MASK; 9162306a36Sopenharmony_ci cfg |= cr | (cb << 13); 9262306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CIIMGEFF(camif->vp->offset), cfg); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic const u32 src_pixfmt_map[8][2] = { 9662306a36Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, CISRCFMT_ORDER422_YCBYCR }, 9762306a36Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, CISRCFMT_ORDER422_YCRYCB }, 9862306a36Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, CISRCFMT_ORDER422_CBYCRY }, 9962306a36Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, CISRCFMT_ORDER422_CRYCBY }, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* Set camera input pixel format and resolution */ 10362306a36Sopenharmony_civoid camif_hw_set_source_format(struct camif_dev *camif) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; 10662306a36Sopenharmony_ci int i; 10762306a36Sopenharmony_ci u32 cfg; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci for (i = ARRAY_SIZE(src_pixfmt_map) - 1; i >= 0; i--) { 11062306a36Sopenharmony_ci if (src_pixfmt_map[i][0] == mf->code) 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci if (i < 0) { 11462306a36Sopenharmony_ci i = 0; 11562306a36Sopenharmony_ci dev_err(camif->dev, 11662306a36Sopenharmony_ci "Unsupported pixel code, falling back to %#08x\n", 11762306a36Sopenharmony_ci src_pixfmt_map[i][0]); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CISRCFMT); 12162306a36Sopenharmony_ci cfg &= ~(CISRCFMT_ORDER422_MASK | CISRCFMT_SIZE_CAM_MASK); 12262306a36Sopenharmony_ci cfg |= (mf->width << 16) | mf->height; 12362306a36Sopenharmony_ci cfg |= src_pixfmt_map[i][1]; 12462306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CISRCFMT, cfg); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* Set the camera host input window offsets (cropping) */ 12862306a36Sopenharmony_civoid camif_hw_set_camera_crop(struct camif_dev *camif) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt; 13162306a36Sopenharmony_ci struct v4l2_rect *crop = &camif->camif_crop; 13262306a36Sopenharmony_ci u32 hoff2, voff2; 13362306a36Sopenharmony_ci u32 cfg; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Note: s3c244x requirement: left = f_width - rect.width / 2 */ 13662306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CIWDOFST); 13762306a36Sopenharmony_ci cfg &= ~(CIWDOFST_OFST_MASK | CIWDOFST_WINOFSEN); 13862306a36Sopenharmony_ci cfg |= (crop->left << 16) | crop->top; 13962306a36Sopenharmony_ci if (crop->left != 0 || crop->top != 0) 14062306a36Sopenharmony_ci cfg |= CIWDOFST_WINOFSEN; 14162306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CIWDOFST, cfg); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) { 14462306a36Sopenharmony_ci hoff2 = mf->width - crop->width - crop->left; 14562306a36Sopenharmony_ci voff2 = mf->height - crop->height - crop->top; 14662306a36Sopenharmony_ci cfg = (hoff2 << 16) | voff2; 14762306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CIWDOFST2, cfg); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_civoid camif_hw_clear_fifo_overflow(struct camif_vp *vp) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 15462306a36Sopenharmony_ci u32 cfg; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CIWDOFST); 15762306a36Sopenharmony_ci if (vp->id == 0) 15862306a36Sopenharmony_ci cfg |= (CIWDOFST_CLROVCOFIY | CIWDOFST_CLROVCOFICB | 15962306a36Sopenharmony_ci CIWDOFST_CLROVCOFICR); 16062306a36Sopenharmony_ci else 16162306a36Sopenharmony_ci cfg |= (/* CIWDOFST_CLROVPRFIY | */ CIWDOFST_CLROVPRFICB | 16262306a36Sopenharmony_ci CIWDOFST_CLROVPRFICR); 16362306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CIWDOFST, cfg); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* Set video bus signals polarity */ 16762306a36Sopenharmony_civoid camif_hw_set_camera_bus(struct camif_dev *camif) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci unsigned int flags = camif->pdata.sensor.flags; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci u32 cfg = camif_read(camif, S3C_CAMIF_REG_CIGCTRL); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci cfg &= ~(CIGCTRL_INVPOLPCLK | CIGCTRL_INVPOLVSYNC | 17462306a36Sopenharmony_ci CIGCTRL_INVPOLHREF | CIGCTRL_INVPOLFIELD); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) 17762306a36Sopenharmony_ci cfg |= CIGCTRL_INVPOLPCLK; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) 18062306a36Sopenharmony_ci cfg |= CIGCTRL_INVPOLVSYNC; 18162306a36Sopenharmony_ci /* 18262306a36Sopenharmony_ci * HREF is normally high during frame active data 18362306a36Sopenharmony_ci * transmission and low during horizontal synchronization 18462306a36Sopenharmony_ci * period. Thus HREF active high means HSYNC active low. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) 18762306a36Sopenharmony_ci cfg |= CIGCTRL_INVPOLHREF; /* HREF active low */ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) { 19062306a36Sopenharmony_ci if (flags & V4L2_MBUS_FIELD_EVEN_LOW) 19162306a36Sopenharmony_ci cfg |= CIGCTRL_INVPOLFIELD; 19262306a36Sopenharmony_ci cfg |= CIGCTRL_FIELDMODE; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci pr_debug("Setting CIGCTRL to: %#x\n", cfg); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CIGCTRL, cfg); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_civoid camif_hw_set_output_addr(struct camif_vp *vp, 20162306a36Sopenharmony_ci struct camif_addr *paddr, int i) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CIYSA(vp->id, i), paddr->y); 20662306a36Sopenharmony_ci if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV 20762306a36Sopenharmony_ci || vp->id == VP_CODEC) { 20862306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CICBSA(vp->id, i), 20962306a36Sopenharmony_ci paddr->cb); 21062306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CICRSA(vp->id, i), 21162306a36Sopenharmony_ci paddr->cr); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci pr_debug("dst_buf[%d]: %pad, cb: %pad, cr: %pad\n", 21562306a36Sopenharmony_ci i, &paddr->y, &paddr->cb, &paddr->cr); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic void camif_hw_set_out_dma_size(struct camif_vp *vp) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct camif_frame *frame = &vp->out_frame; 22162306a36Sopenharmony_ci u32 cfg; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci cfg = camif_read(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset)); 22462306a36Sopenharmony_ci cfg &= ~CITRGFMT_TARGETSIZE_MASK; 22562306a36Sopenharmony_ci cfg |= (frame->f_width << 16) | frame->f_height; 22662306a36Sopenharmony_ci camif_write(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void camif_get_dma_burst(u32 width, u32 ybpp, u32 *mburst, u32 *rburst) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci unsigned int nwords = width * ybpp / 4; 23262306a36Sopenharmony_ci unsigned int div, rem; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (WARN_ON(width < 8 || (width * ybpp) & 7)) 23562306a36Sopenharmony_ci return; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci for (div = 16; div >= 2; div /= 2) { 23862306a36Sopenharmony_ci if (nwords < div) 23962306a36Sopenharmony_ci continue; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci rem = nwords & (div - 1); 24262306a36Sopenharmony_ci if (rem == 0) { 24362306a36Sopenharmony_ci *mburst = div; 24462306a36Sopenharmony_ci *rburst = div; 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci if (rem == div / 2 || rem == div / 4) { 24862306a36Sopenharmony_ci *mburst = div; 24962306a36Sopenharmony_ci *rburst = rem; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_civoid camif_hw_set_output_dma(struct camif_vp *vp) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 25862306a36Sopenharmony_ci struct camif_frame *frame = &vp->out_frame; 25962306a36Sopenharmony_ci const struct camif_fmt *fmt = vp->out_fmt; 26062306a36Sopenharmony_ci unsigned int ymburst = 0, yrburst = 0; 26162306a36Sopenharmony_ci u32 cfg; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci camif_hw_set_out_dma_size(vp); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) { 26662306a36Sopenharmony_ci struct camif_dma_offset *offset = &frame->dma_offset; 26762306a36Sopenharmony_ci /* Set the input dma offsets. */ 26862306a36Sopenharmony_ci cfg = S3C_CISS_OFFS_INITIAL(offset->initial); 26962306a36Sopenharmony_ci cfg |= S3C_CISS_OFFS_LINE(offset->line); 27062306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CISSY(vp->id), cfg); 27162306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CISSCB(vp->id), cfg); 27262306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CISSCR(vp->id), cfg); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Configure DMA burst values */ 27662306a36Sopenharmony_ci camif_get_dma_burst(frame->rect.width, fmt->ybpp, &ymburst, &yrburst); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CICTRL(vp->id, vp->offset)); 27962306a36Sopenharmony_ci cfg &= ~CICTRL_BURST_MASK; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci cfg |= CICTRL_YBURST1(ymburst) | CICTRL_YBURST2(yrburst); 28262306a36Sopenharmony_ci cfg |= CICTRL_CBURST1(ymburst / 2) | CICTRL_CBURST2(yrburst / 2); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CICTRL(vp->id, vp->offset), cfg); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci pr_debug("ymburst: %u, yrburst: %u\n", ymburst, yrburst); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_civoid camif_hw_set_input_path(struct camif_vp *vp) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci u32 cfg = camif_read(vp->camif, S3C_CAMIF_REG_MSCTRL(vp->id)); 29262306a36Sopenharmony_ci cfg &= ~MSCTRL_SEL_DMA_CAM; 29362306a36Sopenharmony_ci camif_write(vp->camif, S3C_CAMIF_REG_MSCTRL(vp->id), cfg); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_civoid camif_hw_set_target_format(struct camif_vp *vp) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 29962306a36Sopenharmony_ci struct camif_frame *frame = &vp->out_frame; 30062306a36Sopenharmony_ci u32 cfg; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci pr_debug("fw: %d, fh: %d color: %d\n", frame->f_width, 30362306a36Sopenharmony_ci frame->f_height, vp->out_fmt->color); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset)); 30662306a36Sopenharmony_ci cfg &= ~CITRGFMT_TARGETSIZE_MASK; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (camif->variant->ip_revision == S3C244X_CAMIF_IP_REV) { 30962306a36Sopenharmony_ci /* We currently support only YCbCr 4:2:2 at the camera input */ 31062306a36Sopenharmony_ci cfg |= CITRGFMT_IN422; 31162306a36Sopenharmony_ci cfg &= ~CITRGFMT_OUT422; 31262306a36Sopenharmony_ci if (vp->out_fmt->color == IMG_FMT_YCBCR422P) 31362306a36Sopenharmony_ci cfg |= CITRGFMT_OUT422; 31462306a36Sopenharmony_ci } else { 31562306a36Sopenharmony_ci cfg &= ~CITRGFMT_OUTFORMAT_MASK; 31662306a36Sopenharmony_ci switch (vp->out_fmt->color) { 31762306a36Sopenharmony_ci case IMG_FMT_RGB565...IMG_FMT_XRGB8888: 31862306a36Sopenharmony_ci cfg |= CITRGFMT_OUTFORMAT_RGB; 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci case IMG_FMT_YCBCR420...IMG_FMT_YCRCB420: 32162306a36Sopenharmony_ci cfg |= CITRGFMT_OUTFORMAT_YCBCR420; 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci case IMG_FMT_YCBCR422P: 32462306a36Sopenharmony_ci cfg |= CITRGFMT_OUTFORMAT_YCBCR422; 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci case IMG_FMT_YCBYCR422...IMG_FMT_CRYCBY422: 32762306a36Sopenharmony_ci cfg |= CITRGFMT_OUTFORMAT_YCBCR422I; 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* Rotation is only supported by s3c64xx */ 33362306a36Sopenharmony_ci if (vp->rotation == 90 || vp->rotation == 270) 33462306a36Sopenharmony_ci cfg |= (frame->f_height << 16) | frame->f_width; 33562306a36Sopenharmony_ci else 33662306a36Sopenharmony_ci cfg |= (frame->f_width << 16) | frame->f_height; 33762306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* Target area, output pixel width * height */ 34062306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CITAREA(vp->id, vp->offset)); 34162306a36Sopenharmony_ci cfg &= ~CITAREA_MASK; 34262306a36Sopenharmony_ci cfg |= (frame->f_width * frame->f_height); 34362306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CITAREA(vp->id, vp->offset), cfg); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_civoid camif_hw_set_flip(struct camif_vp *vp) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci u32 cfg = camif_read(vp->camif, 34962306a36Sopenharmony_ci S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset)); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci cfg &= ~CITRGFMT_FLIP_MASK; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (vp->hflip) 35462306a36Sopenharmony_ci cfg |= CITRGFMT_FLIP_Y_MIRROR; 35562306a36Sopenharmony_ci if (vp->vflip) 35662306a36Sopenharmony_ci cfg |= CITRGFMT_FLIP_X_MIRROR; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci camif_write(vp->camif, S3C_CAMIF_REG_CITRGFMT(vp->id, vp->offset), cfg); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic void camif_hw_set_prescaler(struct camif_vp *vp) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 36462306a36Sopenharmony_ci struct camif_scaler *sc = &vp->scaler; 36562306a36Sopenharmony_ci u32 cfg, shfactor, addr; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci addr = S3C_CAMIF_REG_CISCPRERATIO(vp->id, vp->offset); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci shfactor = 10 - (sc->h_shift + sc->v_shift); 37062306a36Sopenharmony_ci cfg = shfactor << 28; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci cfg |= (sc->pre_h_ratio << 16) | sc->pre_v_ratio; 37362306a36Sopenharmony_ci camif_write(camif, addr, cfg); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci cfg = (sc->pre_dst_width << 16) | sc->pre_dst_height; 37662306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CISCPREDST(vp->id, vp->offset), cfg); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void camif_s3c244x_hw_set_scaler(struct camif_vp *vp) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 38262306a36Sopenharmony_ci struct camif_scaler *scaler = &vp->scaler; 38362306a36Sopenharmony_ci unsigned int color = vp->out_fmt->color; 38462306a36Sopenharmony_ci u32 cfg; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci camif_hw_set_prescaler(vp); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset)); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci cfg &= ~(CISCCTRL_SCALEUP_MASK | CISCCTRL_SCALERBYPASS | 39162306a36Sopenharmony_ci CISCCTRL_MAIN_RATIO_MASK | CIPRSCCTRL_RGB_FORMAT_24BIT); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (scaler->enable) { 39462306a36Sopenharmony_ci if (scaler->scaleup_h) { 39562306a36Sopenharmony_ci if (vp->id == VP_CODEC) 39662306a36Sopenharmony_ci cfg |= CISCCTRL_SCALEUP_H; 39762306a36Sopenharmony_ci else 39862306a36Sopenharmony_ci cfg |= CIPRSCCTRL_SCALEUP_H; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci if (scaler->scaleup_v) { 40162306a36Sopenharmony_ci if (vp->id == VP_CODEC) 40262306a36Sopenharmony_ci cfg |= CISCCTRL_SCALEUP_V; 40362306a36Sopenharmony_ci else 40462306a36Sopenharmony_ci cfg |= CIPRSCCTRL_SCALEUP_V; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci } else { 40762306a36Sopenharmony_ci if (vp->id == VP_CODEC) 40862306a36Sopenharmony_ci cfg |= CISCCTRL_SCALERBYPASS; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci cfg |= ((scaler->main_h_ratio & 0x1ff) << 16); 41262306a36Sopenharmony_ci cfg |= scaler->main_v_ratio & 0x1ff; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (vp->id == VP_PREVIEW) { 41562306a36Sopenharmony_ci if (color == IMG_FMT_XRGB8888) 41662306a36Sopenharmony_ci cfg |= CIPRSCCTRL_RGB_FORMAT_24BIT; 41762306a36Sopenharmony_ci cfg |= CIPRSCCTRL_SAMPLE; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset), cfg); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci pr_debug("main: h_ratio: %#x, v_ratio: %#x", 42362306a36Sopenharmony_ci scaler->main_h_ratio, scaler->main_v_ratio); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic void camif_s3c64xx_hw_set_scaler(struct camif_vp *vp) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 42962306a36Sopenharmony_ci struct camif_scaler *scaler = &vp->scaler; 43062306a36Sopenharmony_ci unsigned int color = vp->out_fmt->color; 43162306a36Sopenharmony_ci u32 cfg; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci camif_hw_set_prescaler(vp); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset)); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci cfg &= ~(CISCCTRL_CSCR2Y_WIDE | CISCCTRL_CSCY2R_WIDE 43862306a36Sopenharmony_ci | CISCCTRL_SCALEUP_H | CISCCTRL_SCALEUP_V 43962306a36Sopenharmony_ci | CISCCTRL_SCALERBYPASS | CISCCTRL_ONE2ONE 44062306a36Sopenharmony_ci | CISCCTRL_INRGB_FMT_MASK | CISCCTRL_OUTRGB_FMT_MASK 44162306a36Sopenharmony_ci | CISCCTRL_INTERLACE | CISCCTRL_EXTRGB_EXTENSION 44262306a36Sopenharmony_ci | CISCCTRL_MAIN_RATIO_MASK); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci cfg |= (CISCCTRL_CSCR2Y_WIDE | CISCCTRL_CSCY2R_WIDE); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (!scaler->enable) { 44762306a36Sopenharmony_ci cfg |= CISCCTRL_SCALERBYPASS; 44862306a36Sopenharmony_ci } else { 44962306a36Sopenharmony_ci if (scaler->scaleup_h) 45062306a36Sopenharmony_ci cfg |= CISCCTRL_SCALEUP_H; 45162306a36Sopenharmony_ci if (scaler->scaleup_v) 45262306a36Sopenharmony_ci cfg |= CISCCTRL_SCALEUP_V; 45362306a36Sopenharmony_ci if (scaler->copy) 45462306a36Sopenharmony_ci cfg |= CISCCTRL_ONE2ONE; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci switch (color) { 45862306a36Sopenharmony_ci case IMG_FMT_RGB666: 45962306a36Sopenharmony_ci cfg |= CISCCTRL_OUTRGB_FMT_RGB666; 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci case IMG_FMT_XRGB8888: 46262306a36Sopenharmony_ci cfg |= CISCCTRL_OUTRGB_FMT_RGB888; 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci cfg |= (scaler->main_h_ratio & 0x1ff) << 16; 46762306a36Sopenharmony_ci cfg |= scaler->main_v_ratio & 0x1ff; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset), cfg); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci pr_debug("main: h_ratio: %#x, v_ratio: %#x", 47262306a36Sopenharmony_ci scaler->main_h_ratio, scaler->main_v_ratio); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_civoid camif_hw_set_scaler(struct camif_vp *vp) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci unsigned int ip_rev = vp->camif->variant->ip_revision; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (ip_rev == S3C244X_CAMIF_IP_REV) 48062306a36Sopenharmony_ci camif_s3c244x_hw_set_scaler(vp); 48162306a36Sopenharmony_ci else 48262306a36Sopenharmony_ci camif_s3c64xx_hw_set_scaler(vp); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_civoid camif_hw_enable_scaler(struct camif_vp *vp, bool on) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci u32 addr = S3C_CAMIF_REG_CISCCTRL(vp->id, vp->offset); 48862306a36Sopenharmony_ci u32 cfg; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci cfg = camif_read(vp->camif, addr); 49162306a36Sopenharmony_ci if (on) 49262306a36Sopenharmony_ci cfg |= CISCCTRL_SCALERSTART; 49362306a36Sopenharmony_ci else 49462306a36Sopenharmony_ci cfg &= ~CISCCTRL_SCALERSTART; 49562306a36Sopenharmony_ci camif_write(vp->camif, addr, cfg); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_civoid camif_hw_set_lastirq(struct camif_vp *vp, int enable) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci u32 addr = S3C_CAMIF_REG_CICTRL(vp->id, vp->offset); 50162306a36Sopenharmony_ci u32 cfg; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci cfg = camif_read(vp->camif, addr); 50462306a36Sopenharmony_ci if (enable) 50562306a36Sopenharmony_ci cfg |= CICTRL_LASTIRQ_ENABLE; 50662306a36Sopenharmony_ci else 50762306a36Sopenharmony_ci cfg &= ~CICTRL_LASTIRQ_ENABLE; 50862306a36Sopenharmony_ci camif_write(vp->camif, addr, cfg); 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_civoid camif_hw_enable_capture(struct camif_vp *vp) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 51462306a36Sopenharmony_ci u32 cfg; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset)); 51762306a36Sopenharmony_ci camif->stream_count++; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (camif->variant->ip_revision == S3C6410_CAMIF_IP_REV) 52062306a36Sopenharmony_ci cfg |= CIIMGCPT_CPT_FREN_ENABLE(vp->id); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (vp->scaler.enable) 52362306a36Sopenharmony_ci cfg |= CIIMGCPT_IMGCPTEN_SC(vp->id); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (camif->stream_count == 1) 52662306a36Sopenharmony_ci cfg |= CIIMGCPT_IMGCPTEN; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset), cfg); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci pr_debug("CIIMGCPT: %#x, camif->stream_count: %d\n", 53162306a36Sopenharmony_ci cfg, camif->stream_count); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_civoid camif_hw_disable_capture(struct camif_vp *vp) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct camif_dev *camif = vp->camif; 53762306a36Sopenharmony_ci u32 cfg; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci cfg = camif_read(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset)); 54062306a36Sopenharmony_ci cfg &= ~CIIMGCPT_IMGCPTEN_SC(vp->id); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (WARN_ON(--(camif->stream_count) < 0)) 54362306a36Sopenharmony_ci camif->stream_count = 0; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (camif->stream_count == 0) 54662306a36Sopenharmony_ci cfg &= ~CIIMGCPT_IMGCPTEN; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci pr_debug("CIIMGCPT: %#x, camif->stream_count: %d\n", 54962306a36Sopenharmony_ci cfg, camif->stream_count); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci camif_write(camif, S3C_CAMIF_REG_CIIMGCPT(vp->offset), cfg); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_civoid camif_hw_dump_regs(struct camif_dev *camif, const char *label) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci static const struct { 55762306a36Sopenharmony_ci u32 offset; 55862306a36Sopenharmony_ci const char * const name; 55962306a36Sopenharmony_ci } registers[] = { 56062306a36Sopenharmony_ci { S3C_CAMIF_REG_CISRCFMT, "CISRCFMT" }, 56162306a36Sopenharmony_ci { S3C_CAMIF_REG_CIWDOFST, "CIWDOFST" }, 56262306a36Sopenharmony_ci { S3C_CAMIF_REG_CIGCTRL, "CIGCTRL" }, 56362306a36Sopenharmony_ci { S3C_CAMIF_REG_CIWDOFST2, "CIWDOFST2" }, 56462306a36Sopenharmony_ci { S3C_CAMIF_REG_CIYSA(0, 0), "CICOYSA0" }, 56562306a36Sopenharmony_ci { S3C_CAMIF_REG_CICBSA(0, 0), "CICOCBSA0" }, 56662306a36Sopenharmony_ci { S3C_CAMIF_REG_CICRSA(0, 0), "CICOCRSA0" }, 56762306a36Sopenharmony_ci { S3C_CAMIF_REG_CIYSA(0, 1), "CICOYSA1" }, 56862306a36Sopenharmony_ci { S3C_CAMIF_REG_CICBSA(0, 1), "CICOCBSA1" }, 56962306a36Sopenharmony_ci { S3C_CAMIF_REG_CICRSA(0, 1), "CICOCRSA1" }, 57062306a36Sopenharmony_ci { S3C_CAMIF_REG_CIYSA(0, 2), "CICOYSA2" }, 57162306a36Sopenharmony_ci { S3C_CAMIF_REG_CICBSA(0, 2), "CICOCBSA2" }, 57262306a36Sopenharmony_ci { S3C_CAMIF_REG_CICRSA(0, 2), "CICOCRSA2" }, 57362306a36Sopenharmony_ci { S3C_CAMIF_REG_CIYSA(0, 3), "CICOYSA3" }, 57462306a36Sopenharmony_ci { S3C_CAMIF_REG_CICBSA(0, 3), "CICOCBSA3" }, 57562306a36Sopenharmony_ci { S3C_CAMIF_REG_CICRSA(0, 3), "CICOCRSA3" }, 57662306a36Sopenharmony_ci { S3C_CAMIF_REG_CIYSA(1, 0), "CIPRYSA0" }, 57762306a36Sopenharmony_ci { S3C_CAMIF_REG_CIYSA(1, 1), "CIPRYSA1" }, 57862306a36Sopenharmony_ci { S3C_CAMIF_REG_CIYSA(1, 2), "CIPRYSA2" }, 57962306a36Sopenharmony_ci { S3C_CAMIF_REG_CIYSA(1, 3), "CIPRYSA3" }, 58062306a36Sopenharmony_ci { S3C_CAMIF_REG_CITRGFMT(0, 0), "CICOTRGFMT" }, 58162306a36Sopenharmony_ci { S3C_CAMIF_REG_CITRGFMT(1, 0), "CIPRTRGFMT" }, 58262306a36Sopenharmony_ci { S3C_CAMIF_REG_CICTRL(0, 0), "CICOCTRL" }, 58362306a36Sopenharmony_ci { S3C_CAMIF_REG_CICTRL(1, 0), "CIPRCTRL" }, 58462306a36Sopenharmony_ci { S3C_CAMIF_REG_CISCPREDST(0, 0), "CICOSCPREDST" }, 58562306a36Sopenharmony_ci { S3C_CAMIF_REG_CISCPREDST(1, 0), "CIPRSCPREDST" }, 58662306a36Sopenharmony_ci { S3C_CAMIF_REG_CISCPRERATIO(0, 0), "CICOSCPRERATIO" }, 58762306a36Sopenharmony_ci { S3C_CAMIF_REG_CISCPRERATIO(1, 0), "CIPRSCPRERATIO" }, 58862306a36Sopenharmony_ci { S3C_CAMIF_REG_CISCCTRL(0, 0), "CICOSCCTRL" }, 58962306a36Sopenharmony_ci { S3C_CAMIF_REG_CISCCTRL(1, 0), "CIPRSCCTRL" }, 59062306a36Sopenharmony_ci { S3C_CAMIF_REG_CITAREA(0, 0), "CICOTAREA" }, 59162306a36Sopenharmony_ci { S3C_CAMIF_REG_CITAREA(1, 0), "CIPRTAREA" }, 59262306a36Sopenharmony_ci { S3C_CAMIF_REG_CISTATUS(0, 0), "CICOSTATUS" }, 59362306a36Sopenharmony_ci { S3C_CAMIF_REG_CISTATUS(1, 0), "CIPRSTATUS" }, 59462306a36Sopenharmony_ci { S3C_CAMIF_REG_CIIMGCPT(0), "CIIMGCPT" }, 59562306a36Sopenharmony_ci }; 59662306a36Sopenharmony_ci u32 i; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci pr_info("--- %s ---\n", label); 59962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(registers); i++) { 60062306a36Sopenharmony_ci u32 cfg = readl(camif->io_base + registers[i].offset); 60162306a36Sopenharmony_ci dev_info(camif->dev, "%s:\t0x%08x\n", registers[i].name, cfg); 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci} 604