| /* |
| * Copyright 2017-2018 NXP |
| */ |
| /* |
| * The code contained herein is licensed under the GNU General Public |
| * License. You may obtain a copy of the GNU General Public License |
| * Version 2 or later at the following locations: |
| * |
| * http://www.opensource.org/licenses/gpl-license.html |
| * http://www.gnu.org/copyleft/gpl.html |
| */ |
| #include <soc/imx8/sc/sci.h> |
| #include <dt-bindings/pinctrl/pads-imx8qxp.h> |
| |
| #include "mxc-isi-hw.h" |
| #define ISI_DOWNSCALE_THRESHOLD 0x4000 |
| |
| #ifdef DEBUG |
| void dump_isi_regs(struct mxc_isi_dev *mxc_isi) |
| { |
| struct device *dev = &mxc_isi->pdev->dev; |
| |
| dev_dbg(dev, "ISI CHNLC register dump, isi%d\n", mxc_isi->id); |
| dev_dbg(dev, "CHNL_CTRL 0x0h = 0x%8x\n", readl(mxc_isi->regs + 0x0)); |
| dev_dbg(dev, "CHNL_IMG_CTRL 0x4h = 0x%8x\n", readl(mxc_isi->regs + 0x4)); |
| dev_dbg(dev, "CHNL_OUT_BUF_CTRL 0x8h = 0x%8x\n", readl(mxc_isi->regs + 0x8)); |
| dev_dbg(dev, "CHNL_IMG_CFG 0xCh = 0x%8x\n", readl(mxc_isi->regs + 0xC)); |
| dev_dbg(dev, "CHNL_IER 0x10h = 0x%8x\n", readl(mxc_isi->regs + 0x10)); |
| dev_dbg(dev, "CHNL_STS 0x14h = 0x%8x\n", readl(mxc_isi->regs + 0x14)); |
| dev_dbg(dev, "CHNL_SCALE_FACTOR 0x18h = 0x%8x\n", readl(mxc_isi->regs + 0x18)); |
| dev_dbg(dev, "CHNL_SCALE_OFFSET 0x1Ch = 0x%8x\n", readl(mxc_isi->regs + 0x1C)); |
| dev_dbg(dev, "CHNL_CROP_ULC 0x20h = 0x%8x\n", readl(mxc_isi->regs + 0x20)); |
| dev_dbg(dev, "CHNL_CROP_LRC 0x24h = 0x%8x\n", readl(mxc_isi->regs + 0x24)); |
| dev_dbg(dev, "CHNL_CSC_COEFF0 0x28h = 0x%8x\n", readl(mxc_isi->regs + 0x28)); |
| dev_dbg(dev, "CHNL_CSC_COEFF1 0x2Ch = 0x%8x\n", readl(mxc_isi->regs + 0x2C)); |
| dev_dbg(dev, "CHNL_CSC_COEFF2 0x30h = 0x%8x\n", readl(mxc_isi->regs + 0x30)); |
| dev_dbg(dev, "CHNL_CSC_COEFF3 0x34h = 0x%8x\n", readl(mxc_isi->regs + 0x34)); |
| dev_dbg(dev, "CHNL_CSC_COEFF4 0x38h = 0x%8x\n", readl(mxc_isi->regs + 0x38)); |
| dev_dbg(dev, "CHNL_CSC_COEFF5 0x3Ch = 0x%8x\n", readl(mxc_isi->regs + 0x3C)); |
| dev_dbg(dev, "CHNL_ROI_0_ALPHA 0x40h = 0x%8x\n", readl(mxc_isi->regs + 0x40)); |
| dev_dbg(dev, "CHNL_ROI_0_ULC 0x44h = 0x%8x\n", readl(mxc_isi->regs + 0x44)); |
| dev_dbg(dev, "CHNL_ROI_0_LRC 0x48h = 0x%8x\n", readl(mxc_isi->regs + 0x48)); |
| dev_dbg(dev, "CHNL_ROI_1_ALPHA 0x4Ch = 0x%8x\n", readl(mxc_isi->regs + 0x4C)); |
| dev_dbg(dev, "CHNL_ROI_1_ULC 0x50h = 0x%8x\n", readl(mxc_isi->regs + 0x50)); |
| dev_dbg(dev, "CHNL_ROI_1_LRC 0x54h = 0x%8x\n", readl(mxc_isi->regs + 0x54)); |
| dev_dbg(dev, "CHNL_ROI_2_ALPHA 0x58h = 0x%8x\n", readl(mxc_isi->regs + 0x58)); |
| dev_dbg(dev, "CHNL_ROI_2_ULC 0x5Ch = 0x%8x\n", readl(mxc_isi->regs + 0x5C)); |
| dev_dbg(dev, "CHNL_ROI_2_LRC 0x60h = 0x%8x\n", readl(mxc_isi->regs + 0x60)); |
| dev_dbg(dev, "CHNL_ROI_3_ALPHA 0x64h = 0x%8x\n", readl(mxc_isi->regs + 0x64)); |
| dev_dbg(dev, "CHNL_ROI_3_ULC 0x68h = 0x%8x\n", readl(mxc_isi->regs + 0x68)); |
| dev_dbg(dev, "CHNL_ROI_3_LRC 0x6Ch = 0x%8x\n", readl(mxc_isi->regs + 0x6C)); |
| dev_dbg(dev, "CHNL_OUT_BUF1_ADDR_Y 0x70h = 0x%8x\n", readl(mxc_isi->regs + 0x70)); |
| dev_dbg(dev, "CHNL_OUT_BUF1_ADDR_U 0x74h = 0x%8x\n", readl(mxc_isi->regs + 0x74)); |
| dev_dbg(dev, "CHNL_OUT_BUF1_ADDR_V 0x78h = 0x%8x\n", readl(mxc_isi->regs + 0x78)); |
| dev_dbg(dev, "CHNL_OUT_BUF_PITCH 0x7Ch = 0x%8x\n", readl(mxc_isi->regs + 0x7C)); |
| dev_dbg(dev, "CHNL_IN_BUF_ADDR 0x80h = 0x%8x\n", readl(mxc_isi->regs + 0x80)); |
| dev_dbg(dev, "CHNL_IN_BUF_PITCH 0x84h = 0x%8x\n", readl(mxc_isi->regs + 0x84)); |
| dev_dbg(dev, "CHNL_MEM_RD_CTRL 0x88h = 0x%8x\n", readl(mxc_isi->regs + 0x88)); |
| dev_dbg(dev, "CHNL_OUT_BUF2_ADDR_Y 0x8Ch = 0x%8x\n", readl(mxc_isi->regs + 0x8C)); |
| dev_dbg(dev, "CHNL_OUT_BUF2_ADDR_U 0x90h = 0x%8x\n", readl(mxc_isi->regs + 0x90)); |
| dev_dbg(dev, "CHNL_OUT_BUF2_ADDR_V 0x94h = 0x%8x\n", readl(mxc_isi->regs + 0x94)); |
| } |
| #else |
| void dump_isi_regs(struct mxc_isi_dev *mxc_isi) |
| { |
| } |
| #endif |
| |
| static const u32 coeffs[2][6] = { |
| /* A2,A1, B1, A3, B3, B2, C2, C1, D1, C3, D3, D2 */ |
| /* YUV2RGB */ |
| { 0x0000012A, 0x012A0198, 0x0730079C, 0x0204012A, 0x01F00000, 0x01800180 }, |
| /* RGB->YUV */ |
| { 0x00810041, 0x07db0019, 0x007007b6, 0x07a20070, 0x001007ee, 0x00800080 }, |
| }; |
| |
| static void printk_pixelformat(char *prefix, int val) |
| { |
| printk("%s %c%c%c%c\n", prefix ? prefix : "pixelformat", |
| val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff); |
| } |
| |
| static bool is_rgb(u32 pix_fmt) |
| { |
| if ((pix_fmt == V4L2_PIX_FMT_RGB565) || |
| (pix_fmt == V4L2_PIX_FMT_RGB24) || |
| (pix_fmt == V4L2_PIX_FMT_RGB32) || |
| (pix_fmt == V4L2_PIX_FMT_BGR32) || |
| (pix_fmt == V4L2_PIX_FMT_XRGB32) || |
| (pix_fmt == V4L2_PIX_FMT_XBGR32) || |
| (pix_fmt == V4L2_PIX_FMT_BGR24) || |
| (pix_fmt == V4L2_PIX_FMT_RGBA) || |
| (pix_fmt == V4L2_PIX_FMT_ABGR32) || |
| (pix_fmt == V4L2_PIX_FMT_ARGB32)) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static bool is_yuv(u32 pix_fmt) |
| { |
| if ((pix_fmt == V4L2_PIX_FMT_YUYV) || |
| (pix_fmt == V4L2_PIX_FMT_YUV32) || |
| (pix_fmt == V4L2_PIX_FMT_YUV444M) || |
| (pix_fmt == V4L2_PIX_FMT_NV12)) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static void chain_buf(struct mxc_isi_dev *mxc_isi) |
| { |
| struct mxc_isi_frame *src_f; |
| u32 val; |
| |
| if (mxc_isi->is_m2m) |
| src_f = &mxc_isi->m2m.src_f; |
| else |
| src_f = &mxc_isi->isi_cap.src_f; |
| |
| if (src_f->o_width > 2048) { |
| val = readl(mxc_isi->regs + CHNL_CTRL); |
| val &= ~CHNL_CTRL_CHAIN_BUF_MASK; |
| val |= (CHNL_CTRL_CHAIN_BUF_2_CHAIN << CHNL_CTRL_CHAIN_BUF_OFFSET); |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| } else if (!mxc_isi->chain_buf) { |
| val = readl(mxc_isi->regs + CHNL_CTRL); |
| val &= ~CHNL_CTRL_CHAIN_BUF_MASK; |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| } |
| } |
| |
| void mxc_isi_channel_set_outbuf(struct mxc_isi_dev *mxc_isi, struct mxc_isi_buffer *buf) |
| { |
| struct vb2_buffer *vb2_buf = &buf->v4l2_buf.vb2_buf; |
| struct frame_addr *paddr = &buf->paddr; |
| struct v4l2_pix_format_mplane *pix = &mxc_isi->pix; |
| u32 framecount = buf->v4l2_buf.sequence; |
| int val = 0; |
| |
| if (buf->discard) { |
| paddr->y = mxc_isi->discard_buffer_dma[0]; |
| if (pix->num_planes == 2) |
| paddr->cb = mxc_isi->discard_buffer_dma[1]; |
| if (pix->num_planes == 3) { |
| paddr->cb = mxc_isi->discard_buffer_dma[1]; |
| paddr->cr = mxc_isi->discard_buffer_dma[2]; |
| } |
| } else { |
| paddr->y = vb2_dma_contig_plane_dma_addr(vb2_buf, 0); |
| |
| if (vb2_buf->num_planes == 2) |
| paddr->cb = vb2_dma_contig_plane_dma_addr(vb2_buf, 1); |
| if (vb2_buf->num_planes == 3) { |
| paddr->cb = vb2_dma_contig_plane_dma_addr(vb2_buf, 1); |
| paddr->cr = vb2_dma_contig_plane_dma_addr(vb2_buf, 2); |
| } |
| } |
| |
| val = readl(mxc_isi->regs + CHNL_OUT_BUF_CTRL); |
| |
| if (framecount % 2 == 0) { |
| writel(paddr->y, mxc_isi->regs + CHNL_OUT_BUF1_ADDR_Y); |
| writel(paddr->cb, mxc_isi->regs + CHNL_OUT_BUF1_ADDR_U); |
| writel(paddr->cr, mxc_isi->regs + CHNL_OUT_BUF1_ADDR_V); |
| val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF1_ADDR_MASK; |
| } else if (framecount % 2 == 1) { |
| writel(paddr->y, mxc_isi->regs + CHNL_OUT_BUF2_ADDR_Y); |
| writel(paddr->cb, mxc_isi->regs + CHNL_OUT_BUF2_ADDR_U); |
| writel(paddr->cr, mxc_isi->regs + CHNL_OUT_BUF2_ADDR_V); |
| val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF2_ADDR_MASK; |
| } |
| writel(val, mxc_isi->regs + CHNL_OUT_BUF_CTRL); |
| } |
| |
| void mxc_isi_channel_set_m2m_out_addr(struct mxc_isi_dev *mxc_isi, |
| struct mxc_isi_buffer *buf) |
| { |
| mxc_isi_channel_set_outbuf(mxc_isi, buf); |
| } |
| |
| void mxc_isi_channel_set_m2m_src_addr(struct mxc_isi_dev *mxc_isi, |
| struct mxc_isi_buffer *buf) |
| { |
| struct vb2_buffer *vb2_buf = &buf->v4l2_buf.vb2_buf; |
| struct frame_addr *paddr = &buf->paddr; |
| |
| /* Only support one plane */ |
| paddr->y = vb2_dma_contig_plane_dma_addr(vb2_buf, 0); |
| writel(paddr->y, mxc_isi->regs + CHNL_IN_BUF_ADDR); |
| } |
| |
| void mxc_isi_channel_sw_reset(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| val = readl(mxc_isi->regs + CHNL_CTRL); |
| val |= CHNL_CTRL_SW_RST; |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| mdelay(5); |
| val &= ~CHNL_CTRL_SW_RST; |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| } |
| |
| void mxc_isi_channel_source_config(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| val = readl(mxc_isi->regs + CHNL_CTRL); |
| val &= ~(CHNL_CTRL_MIPI_VC_ID_MASK | |
| CHNL_CTRL_SRC_INPUT_MASK | CHNL_CTRL_SRC_TYPE_MASK); |
| |
| switch (mxc_isi->interface[IN_PORT]) { |
| case ISI_INPUT_INTERFACE_MIPI0_CSI2: |
| val |= CHNL_CTRL_SRC_INPUT_MIPI0; |
| if (mxc_isi->interface[SUB_IN_PORT] <= CHNL_CTRL_MIPI_VC_ID_VC3 && |
| mxc_isi->interface[SUB_IN_PORT] >= CHNL_CTRL_MIPI_VC_ID_VC0) |
| val |= (mxc_isi->interface[SUB_IN_PORT] << CHNL_CTRL_MIPI_VC_ID_OFFSET); |
| break; |
| case ISI_INPUT_INTERFACE_MIPI1_CSI2: |
| val |= CHNL_CTRL_SRC_INPUT_MIPI1; |
| if (mxc_isi->interface[SUB_IN_PORT] <= CHNL_CTRL_MIPI_VC_ID_VC3 && |
| mxc_isi->interface[SUB_IN_PORT] >= CHNL_CTRL_MIPI_VC_ID_VC0) |
| val |= (mxc_isi->interface[SUB_IN_PORT] << CHNL_CTRL_MIPI_VC_ID_OFFSET); |
| break; |
| case ISI_INPUT_INTERFACE_DC0: |
| val |= CHNL_CTRL_SRC_INPUT_DC0; |
| break; |
| case ISI_INPUT_INTERFACE_DC1: |
| val |= CHNL_CTRL_SRC_INPUT_DC1; |
| break; |
| case ISI_INPUT_INTERFACE_HDMI: |
| val |= CHNL_CTRL_SRC_INPUT_HDMI; |
| break; |
| case ISI_INPUT_INTERFACE_PARALLEL_CSI: |
| val |= CHNL_CTRL_SRC_INPUT_CSI; |
| break; |
| case ISI_INPUT_INTERFACE_MEM: |
| val |= CHNL_CTRL_SRC_INPUT_MEMORY; |
| val |= (CHNL_CTRL_SRC_TYPE_MEMORY << CHNL_CTRL_SRC_TYPE_OFFSET); |
| break; |
| default: |
| dev_err(&mxc_isi->pdev->dev, "invalid interface\n"); |
| break; |
| } |
| |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| } |
| |
| void mxc_isi_channel_set_flip(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| val = readl(mxc_isi->regs + CHNL_IMG_CTRL); |
| val &= ~(CHNL_IMG_CTRL_VFLIP_EN_MASK | CHNL_IMG_CTRL_HFLIP_EN_MASK); |
| |
| if (mxc_isi->vflip) |
| val |= (CHNL_IMG_CTRL_VFLIP_EN_ENABLE << CHNL_IMG_CTRL_VFLIP_EN_OFFSET); |
| if (mxc_isi->hflip) |
| val |= (CHNL_IMG_CTRL_HFLIP_EN_ENABLE << CHNL_IMG_CTRL_HFLIP_EN_OFFSET); |
| |
| writel(val, mxc_isi->regs + CHNL_IMG_CTRL); |
| } |
| |
| void mxc_isi_channel_set_csc(struct mxc_isi_dev *mxc_isi) |
| { |
| struct mxc_isi_fmt *dst_fmt = mxc_isi->isi_cap.dst_f.fmt; |
| struct mxc_isi_fmt *src_fmt = mxc_isi->isi_cap.src_f.fmt; |
| u32 val, csc = 0; |
| |
| if (mxc_isi->is_m2m) { |
| src_fmt = mxc_isi->m2m.src_f.fmt; |
| dst_fmt = mxc_isi->m2m.dst_f.fmt; |
| } |
| |
| val = readl(mxc_isi->regs + CHNL_IMG_CTRL); |
| val &= ~(CHNL_IMG_CTRL_FORMAT_MASK | |
| CHNL_IMG_CTRL_YCBCR_MODE_MASK | |
| CHNL_IMG_CTRL_CSC_BYPASS_MASK | |
| CHNL_IMG_CTRL_CSC_MODE_MASK); |
| |
| /* set outbuf format */ |
| val |= dst_fmt->color << CHNL_IMG_CTRL_FORMAT_OFFSET; |
| |
| mxc_isi->cscen = 1; |
| |
| if (is_yuv(src_fmt->fourcc) && is_rgb(dst_fmt->fourcc)) { |
| /* YUV2RGB */ |
| csc = YUV2RGB; |
| /* YCbCr enable??? */ |
| val |= (CHNL_IMG_CTRL_CSC_MODE_YCBCR2RGB << CHNL_IMG_CTRL_CSC_MODE_OFFSET); |
| val |= (CHNL_IMG_CTRL_YCBCR_MODE_ENABLE << CHNL_IMG_CTRL_YCBCR_MODE_OFFSET); |
| } else if (is_rgb(src_fmt->fourcc) && is_yuv(dst_fmt->fourcc)) { |
| /* RGB2YUV */ |
| csc = RGB2YUV; |
| val |= (CHNL_IMG_CTRL_CSC_MODE_RGB2YCBCR << CHNL_IMG_CTRL_CSC_MODE_OFFSET); |
| } else { |
| /* Bypass CSC */ |
| printk("bypass csc\n"); |
| mxc_isi->cscen = 0; |
| val |= CHNL_IMG_CTRL_CSC_BYPASS_ENABLE; |
| } |
| |
| printk_pixelformat("input fmt", src_fmt->fourcc); |
| printk_pixelformat("output fmt", dst_fmt->fourcc); |
| |
| if (mxc_isi->cscen) { |
| writel(coeffs[csc][0], mxc_isi->regs + CHNL_CSC_COEFF0); |
| writel(coeffs[csc][1], mxc_isi->regs + CHNL_CSC_COEFF1); |
| writel(coeffs[csc][2], mxc_isi->regs + CHNL_CSC_COEFF2); |
| writel(coeffs[csc][3], mxc_isi->regs + CHNL_CSC_COEFF3); |
| writel(coeffs[csc][4], mxc_isi->regs + CHNL_CSC_COEFF4); |
| writel(coeffs[csc][5], mxc_isi->regs + CHNL_CSC_COEFF5); |
| } |
| |
| writel(val, mxc_isi->regs + CHNL_IMG_CTRL); |
| } |
| void mxc_isi_channel_set_alpha_roi0(struct mxc_isi_dev *mxc_isi, |
| struct v4l2_rect *rect) |
| { |
| u32 val0, val1; |
| val0 = (rect->left << 16) | rect->top; |
| writel(val0, mxc_isi->regs + CHNL_ROI_0_ULC); |
| val1 = (rect->width << 16) | rect->height; |
| writel(val0 + val1, mxc_isi->regs + CHNL_ROI_0_LRC); |
| } |
| |
| void mxc_isi_channel_set_alpha(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| val = readl(mxc_isi->regs + CHNL_IMG_CTRL); |
| val &= ~(CHNL_IMG_CTRL_GBL_ALPHA_VAL_MASK | CHNL_IMG_CTRL_GBL_ALPHA_EN_MASK); |
| val |= ((mxc_isi->alpha << CHNL_IMG_CTRL_GBL_ALPHA_VAL_OFFSET) | |
| (CHNL_IMG_CTRL_GBL_ALPHA_EN_ENABLE << CHNL_IMG_CTRL_GBL_ALPHA_EN_OFFSET)); |
| |
| writel(val, mxc_isi->regs + CHNL_IMG_CTRL); |
| } |
| |
| void mxc_isi_channel_set_chain_buf(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| if (mxc_isi->chain_buf) { |
| printk("%s\n", __func__); |
| val = readl(mxc_isi->regs + CHNL_CTRL); |
| val &= ~CHNL_CTRL_CHAIN_BUF_MASK; |
| val |= (CHNL_CTRL_CHAIN_BUF_2_CHAIN << CHNL_CTRL_CHAIN_BUF_OFFSET); |
| |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| } |
| } |
| |
| void mxc_isi_channel_deinterlace_init(struct mxc_isi_dev *mxc_isi) |
| { |
| /* Config for Blending deinterlace */ |
| } |
| |
| void mxc_isi_channel_set_deinterlace(struct mxc_isi_dev *mxc_isi) |
| { |
| /* de-interlacing method |
| * Weaving-------------Yes |
| * Line Doubling-------No |
| * Blending -----------TODO*/ |
| u32 val; |
| |
| val = readl(mxc_isi->regs + CHNL_IMG_CTRL); |
| val &= ~CHNL_IMG_CTRL_DEINT_MASK; |
| if (mxc_isi->deinterlace) |
| val |= mxc_isi->deinterlace << CHNL_IMG_CTRL_DEINT_OFFSET; |
| if (mxc_isi->deinterlace == CHNL_IMG_CTRL_DEINT_LDOUBLE_ODD_EVEN || |
| mxc_isi->deinterlace == CHNL_IMG_CTRL_DEINT_LDOUBLE_EVEN_ODD) |
| mxc_isi_channel_deinterlace_init(mxc_isi); |
| |
| writel(val, mxc_isi->regs + CHNL_IMG_CTRL); |
| } |
| |
| |
| void mxc_isi_channel_set_crop(struct mxc_isi_dev *mxc_isi) |
| { |
| struct mxc_isi_frame *src_f = &mxc_isi->isi_cap.src_f; |
| struct v4l2_rect crop; |
| u32 val, val0, val1, temp; |
| |
| val = readl(mxc_isi->regs + CHNL_IMG_CTRL); |
| val &= ~CHNL_IMG_CTRL_CROP_EN_MASK; |
| |
| if ((src_f->o_height == src_f->height) && |
| (src_f->o_width == src_f->width)) { |
| mxc_isi->crop = 0; |
| writel(val, mxc_isi->regs + CHNL_IMG_CTRL); |
| return; |
| } |
| |
| if (mxc_isi->scale) { |
| temp = (src_f->h_off << 12) / mxc_isi->xfactor; |
| crop.left = temp >> mxc_isi->pre_dec_x; |
| temp = (src_f->v_off << 12) / mxc_isi->yfactor; |
| crop.top = temp >> mxc_isi->pre_dec_y; |
| temp = (src_f->width << 12) / mxc_isi->xfactor; |
| crop.width = temp >> mxc_isi->pre_dec_x; |
| temp = (src_f->height << 12) / mxc_isi->yfactor; |
| crop.height = temp >> mxc_isi->pre_dec_y; |
| } else { |
| crop.left = src_f->h_off; |
| crop.top = src_f->v_off; |
| crop.width = src_f->width; |
| crop.height = src_f->height; |
| } |
| |
| mxc_isi->crop = 1; |
| val |= (CHNL_IMG_CTRL_CROP_EN_ENABLE << CHNL_IMG_CTRL_CROP_EN_OFFSET); |
| val0 = crop.top | (crop.left << CHNL_CROP_ULC_X_OFFSET); |
| val1 = crop.height | (crop.width << CHNL_CROP_LRC_X_OFFSET); |
| |
| writel(val0, mxc_isi->regs + CHNL_CROP_ULC); |
| writel((val1 + val0), mxc_isi->regs + CHNL_CROP_LRC); |
| writel(val, mxc_isi->regs + CHNL_IMG_CTRL); |
| } |
| |
| static void mxc_isi_channel_clear_scaling(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val0; |
| |
| writel(0x10001000, mxc_isi->regs + CHNL_SCALE_FACTOR); |
| |
| val0 = readl(mxc_isi->regs + CHNL_IMG_CTRL); |
| val0 &= ~(CHNL_IMG_CTRL_DEC_X_MASK | CHNL_IMG_CTRL_DEC_Y_MASK); |
| writel(val0, mxc_isi->regs + CHNL_IMG_CTRL); |
| } |
| |
| void mxc_isi_channel_set_scaling(struct mxc_isi_dev *mxc_isi) |
| { |
| struct mxc_isi_frame *dst_f = &mxc_isi->isi_cap.dst_f; |
| struct mxc_isi_frame *src_f = &mxc_isi->isi_cap.src_f; |
| u32 decx, decy; |
| u32 xscale, yscale; |
| u32 xdec = 0, ydec = 0; |
| u32 val0, val1; |
| |
| if (mxc_isi->is_m2m) { |
| src_f = &mxc_isi->m2m.src_f; |
| dst_f = &mxc_isi->m2m.dst_f; |
| } |
| |
| if (dst_f->height == src_f->height || |
| dst_f->width == src_f->width) { |
| mxc_isi->scale = 0; |
| mxc_isi_channel_clear_scaling(mxc_isi); |
| dev_dbg(&mxc_isi->pdev->dev, "%s: no scale\n", __func__); |
| return; |
| } |
| |
| dev_info(&mxc_isi->pdev->dev, "input_size(%d,%d), output_size(%d,%d)\n", |
| src_f->width, src_f->height, dst_f->width, dst_f->height); |
| |
| mxc_isi->scale = 1; |
| |
| decx = src_f->width / dst_f->width; |
| decy = src_f->height / dst_f->height; |
| |
| if (decx > 1) { |
| /* Down */ |
| if (decx >= 2 && decx < 4) { |
| decx = 2; |
| xdec = 1; |
| } else if (decx >= 4 && decx < 8) { |
| decx = 4; |
| xdec = 2; |
| } else if (decx >= 8) { |
| decx = 8; |
| xdec = 3; |
| } |
| xscale = src_f->width * 0x1000 / (dst_f->width * decx); |
| } else |
| /* Up */ |
| xscale = src_f->width * 0x1000 / dst_f->width; |
| |
| if (decy > 1) { |
| if (decy >= 2 && decy < 4) { |
| decy = 2; |
| ydec = 1; |
| } else if (decy >= 4 && decy < 8) { |
| decy = 4; |
| ydec = 2; |
| } else if (decy >= 8) { |
| decy = 8; |
| ydec = 3; |
| } |
| yscale = src_f->height * 0x1000 / (dst_f->height * decy); |
| } else |
| yscale = src_f->height * 0x1000 / dst_f->height; |
| |
| val0 = readl(mxc_isi->regs + CHNL_IMG_CTRL); |
| val0 |= CHNL_IMG_CTRL_YCBCR_MODE_MASK;//YCbCr Sandor??? |
| val0 &= ~(CHNL_IMG_CTRL_DEC_X_MASK | CHNL_IMG_CTRL_DEC_Y_MASK); |
| val0 |= (xdec << CHNL_IMG_CTRL_DEC_X_OFFSET) | |
| (ydec << CHNL_IMG_CTRL_DEC_Y_OFFSET); |
| writel(val0, mxc_isi->regs + CHNL_IMG_CTRL); |
| |
| if (xscale > ISI_DOWNSCALE_THRESHOLD) |
| xscale = ISI_DOWNSCALE_THRESHOLD; |
| if (yscale > ISI_DOWNSCALE_THRESHOLD) |
| yscale = ISI_DOWNSCALE_THRESHOLD; |
| |
| val1 = xscale | (yscale << CHNL_SCALE_FACTOR_Y_SCALE_OFFSET); |
| |
| writel(val1, mxc_isi->regs + CHNL_SCALE_FACTOR); |
| |
| /* Update scale config if scaling enabled */ |
| val1 = dst_f->o_width | (dst_f->o_height << CHNL_SCL_IMG_CFG_HEIGHT_OFFSET); |
| writel(val1, mxc_isi->regs + CHNL_SCL_IMG_CFG); |
| |
| writel(0, mxc_isi->regs + CHNL_SCALE_OFFSET); |
| |
| return; |
| } |
| |
| void mxc_isi_channel_init(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| /* sw reset */ |
| mxc_isi_channel_sw_reset(mxc_isi); |
| |
| /* Init channel clk first */ |
| val = readl(mxc_isi->regs + CHNL_CTRL); |
| val |= (CHNL_CTRL_CLK_EN_ENABLE << CHNL_CTRL_CLK_EN_OFFSET); |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| } |
| |
| void mxc_isi_channel_deinit(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| /* sw reset */ |
| mxc_isi_channel_sw_reset(mxc_isi); |
| |
| /* deinit channel clk first */ |
| val = (CHNL_CTRL_CLK_EN_ENABLE << CHNL_CTRL_CLK_EN_OFFSET); |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| } |
| |
| void mxc_isi_channel_config(struct mxc_isi_dev *mxc_isi) |
| { |
| struct mxc_isi_frame *dst_f = &mxc_isi->isi_cap.dst_f; |
| struct mxc_isi_frame *src_f = &mxc_isi->isi_cap.src_f; |
| u32 val; |
| |
| /* images having higher than 2048 horizontal resolution */ |
| chain_buf(mxc_isi); |
| |
| /* config output frame size and format */ |
| val = src_f->o_width | (src_f->o_height << CHNL_IMG_CFG_HEIGHT_OFFSET); |
| writel(val, mxc_isi->regs + CHNL_IMG_CFG); |
| |
| /* scale size need to equal input size when scaling disabled*/ |
| writel(val, mxc_isi->regs + CHNL_SCL_IMG_CFG); |
| |
| /* check csc and scaling */ |
| mxc_isi_channel_set_csc(mxc_isi); |
| |
| mxc_isi_channel_set_scaling(mxc_isi); |
| |
| /* select the source input / src type / virtual channel for mipi*/ |
| mxc_isi_channel_source_config(mxc_isi); |
| |
| /* line pitch */ |
| val = dst_f->bytesperline[0]; |
| writel(val, mxc_isi->regs + CHNL_OUT_BUF_PITCH); |
| |
| /* TODO */ |
| mxc_isi_channel_set_flip(mxc_isi); |
| |
| if (mxc_isi->alphaen) |
| mxc_isi_channel_set_alpha(mxc_isi); |
| #if 0 |
| mxc_isi_channel_set_crop(mxc_isi); |
| #endif |
| |
| val = readl(mxc_isi->regs + CHNL_CTRL); |
| val &= ~CHNL_CTRL_CHNL_BYPASS_MASK; |
| |
| /* Bypass channel */ |
| if (!mxc_isi->cscen && !mxc_isi->scale) |
| val |= (CHNL_CTRL_CHNL_BYPASS_ENABLE << CHNL_CTRL_CHNL_BYPASS_OFFSET); |
| |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| } |
| |
| void mxc_isi_clean_registers(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 status; |
| |
| status = mxc_isi_get_irq_status(mxc_isi); |
| mxc_isi_clean_irq_status(mxc_isi, status); |
| } |
| |
| void mxc_isi_channel_enable(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| val = readl(mxc_isi->regs + CHNL_CTRL); |
| val |= (CHNL_CTRL_CHNL_EN_ENABLE << CHNL_CTRL_CHNL_EN_OFFSET); |
| val |= 0xff << CHNL_CTRL_BLANK_PXL_OFFSET; |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| |
| mxc_isi_clean_registers(mxc_isi); |
| mxc_isi_enable_irq(mxc_isi); |
| msleep(300); |
| dump_isi_regs(mxc_isi); |
| } |
| |
| void mxc_isi_channel_disable(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| mxc_isi_disable_irq(mxc_isi); |
| |
| val = readl(mxc_isi->regs + CHNL_CTRL); |
| val &= ~(CHNL_CTRL_CHNL_EN_MASK | CHNL_CTRL_CLK_EN_MASK); |
| val |= (CHNL_CTRL_CHNL_EN_DISABLE << CHNL_CTRL_CHNL_EN_OFFSET); |
| val |= (CHNL_CTRL_CLK_EN_DISABLE << CHNL_CTRL_CLK_EN_OFFSET); |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| } |
| |
| void mxc_isi_enable_irq(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| val = CHNL_IER_FRM_RCVD_EN_MASK | |
| CHNL_IER_OFLW_Y_BUF_EN_MASK | |
| CHNL_IER_AXI_WR_ERR_U_EN_MASK | |
| CHNL_IER_AXI_WR_ERR_V_EN_MASK | |
| CHNL_IER_AXI_WR_ERR_Y_EN_MASK | |
| CHNL_IER_OFLW_PANIC_V_BUF_EN_MASK | |
| CHNL_IER_EXCS_OFLW_V_BUF_EN_MASK | |
| CHNL_IER_OFLW_V_BUF_EN_MASK | |
| CHNL_IER_OFLW_PANIC_U_BUF_EN_MASK | |
| CHNL_IER_EXCS_OFLW_U_BUF_EN_MASK | |
| CHNL_IER_OFLW_U_BUF_EN_MASK | |
| CHNL_IER_OFLW_PANIC_Y_BUF_EN_MASK | |
| CHNL_IER_EXCS_OFLW_Y_BUF_EN_MASK | |
| CHNL_IER_OFLW_Y_BUF_EN_MASK; |
| writel(val, mxc_isi->regs + CHNL_IER); |
| } |
| |
| void mxc_isi_disable_irq(struct mxc_isi_dev *mxc_isi) |
| { |
| writel(0, mxc_isi->regs + CHNL_IER); |
| } |
| |
| u32 mxc_isi_get_irq_status(struct mxc_isi_dev *mxc_isi) |
| { |
| return readl(mxc_isi->regs + CHNL_STS); |
| } |
| |
| void mxc_isi_clean_irq_status(struct mxc_isi_dev *mxc_isi, u32 val) |
| { |
| writel(val, mxc_isi->regs + CHNL_STS); |
| } |
| |
| void mxc_isi_m2m_channel_set_filp(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| val = readl(mxc_isi->regs + CHNL_IMG_CTRL); |
| val &= ~(CHNL_IMG_CTRL_VFLIP_EN_MASK | CHNL_IMG_CTRL_HFLIP_EN_MASK); |
| |
| if (mxc_isi->m2m.vflip) |
| val |= (CHNL_IMG_CTRL_VFLIP_EN_ENABLE << CHNL_IMG_CTRL_VFLIP_EN_OFFSET); |
| if (mxc_isi->m2m.hflip) |
| val |= (CHNL_IMG_CTRL_HFLIP_EN_ENABLE << CHNL_IMG_CTRL_HFLIP_EN_OFFSET); |
| |
| writel(val, mxc_isi->regs + CHNL_IMG_CTRL); |
| } |
| |
| void mxc_isi_m2m_channel_set_alpha(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| val = readl(mxc_isi->regs + CHNL_IMG_CTRL); |
| val &= ~(CHNL_IMG_CTRL_GBL_ALPHA_VAL_MASK | CHNL_IMG_CTRL_GBL_ALPHA_EN_MASK); |
| val |= ((mxc_isi->m2m.alpha << CHNL_IMG_CTRL_GBL_ALPHA_VAL_OFFSET) | |
| (CHNL_IMG_CTRL_GBL_ALPHA_EN_ENABLE << CHNL_IMG_CTRL_GBL_ALPHA_EN_OFFSET)); |
| |
| writel(val, mxc_isi->regs + CHNL_IMG_CTRL); |
| } |
| |
| void mxc_isi_m2m_channel_init(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| /* sw reset */ |
| mxc_isi_channel_sw_reset(mxc_isi); |
| |
| /* Init channel clk first */ |
| val = readl(mxc_isi->regs + CHNL_CTRL); |
| val |= (CHNL_CTRL_CLK_EN_ENABLE << CHNL_CTRL_CLK_EN_OFFSET); |
| writel(val, mxc_isi->regs + CHNL_CTRL); |
| } |
| |
| void mxc_isi_m2m_channel_config(struct mxc_isi_dev *mxc_isi) |
| { |
| struct mxc_isi_frame *src_f, *dst_f; |
| u32 reg; |
| |
| src_f = &mxc_isi->m2m.src_f; |
| dst_f = &mxc_isi->m2m.dst_f; |
| |
| chain_buf(mxc_isi); |
| |
| /* scale size need to equal input size when scaling disabled*/ |
| reg = src_f->o_width | (src_f->o_height << CHNL_IMG_CFG_HEIGHT_OFFSET); |
| writel(reg, mxc_isi->regs + CHNL_SCL_IMG_CFG); |
| |
| /* CSC */ |
| mxc_isi_channel_set_csc(mxc_isi); |
| |
| /* Scaling */ |
| mxc_isi_channel_set_scaling(mxc_isi); |
| |
| /* Horizonal and Vertical flip */ |
| mxc_isi_m2m_channel_set_filp(mxc_isi); |
| |
| if (mxc_isi->m2m.alphaen) |
| mxc_isi_m2m_channel_set_alpha(mxc_isi); |
| } |
| |
| void mxc_isi_m2m_channel_enable(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 reg; |
| |
| /* Read from memory and enable ISI channel */ |
| reg = readl(mxc_isi->regs + CHNL_CTRL); |
| reg &= ~(CHNL_CTRL_SRC_TYPE_MASK | |
| CHNL_CTRL_SRC_INPUT_MASK | |
| CHNL_CTRL_CHNL_EN_MASK); |
| reg |= (CHNL_CTRL_SRC_INPUT_MEMORY << CHNL_CTRL_SRC_INPUT_OFFSET | |
| CHNL_CTRL_SRC_TYPE_MEMORY << CHNL_CTRL_SRC_TYPE_OFFSET| |
| CHNL_CTRL_CHNL_EN_ENABLE << CHNL_CTRL_CHNL_EN_OFFSET); |
| writel(reg, mxc_isi->regs + CHNL_CTRL); |
| |
| mxc_isi_clean_registers(mxc_isi); |
| mxc_isi_enable_irq(mxc_isi); |
| mxc_isi_m2m_start_read(mxc_isi); |
| |
| dump_isi_regs(mxc_isi); |
| } |
| |
| void mxc_isi_m2m_config_src(struct mxc_isi_dev *mxc_isi) |
| { |
| struct mxc_isi_frame *src_f = &mxc_isi->m2m.src_f; |
| u32 val; |
| |
| /* source format */ |
| val = readl(mxc_isi->regs + CHNL_MEM_RD_CTRL); |
| val &= ~CHNL_MEM_RD_CTRL_IMG_TYPE_MASK; |
| val |= src_f->fmt->color << CHNL_MEM_RD_CTRL_IMG_TYPE_OFFSET; |
| writel(val, mxc_isi->regs + CHNL_MEM_RD_CTRL); |
| |
| /* source image width and height */ |
| val = (src_f->width << CHNL_IMG_CFG_WIDTH_OFFSET | |
| src_f->height << CHNL_IMG_CFG_HEIGHT_OFFSET); |
| writel(val, mxc_isi->regs + CHNL_IMG_CFG); |
| |
| /* source pitch */ |
| val = src_f->bytesperline[0] << CHNL_IN_BUF_PITCH_LINE_PITCH_OFFSET; |
| writel(val, mxc_isi->regs + CHNL_IN_BUF_PITCH); |
| } |
| |
| void mxc_isi_m2m_config_dst(struct mxc_isi_dev *mxc_isi) |
| { |
| struct mxc_isi_frame *dst_f = &mxc_isi->m2m.dst_f; |
| u32 val; |
| |
| /* out format */ |
| val = readl(mxc_isi->regs + CHNL_IMG_CTRL); |
| val &= ~CHNL_IMG_CTRL_FORMAT_MASK; |
| val |= dst_f->fmt->color << CHNL_IMG_CTRL_FORMAT_OFFSET; |
| writel(val, mxc_isi->regs + CHNL_IMG_CTRL); |
| |
| /* out pitch */ |
| val = readl(mxc_isi->regs + CHNL_OUT_BUF_PITCH); |
| val &= ~CHNL_IN_BUF_PITCH_LINE_PITCH_MASK; |
| val |= dst_f->bytesperline[0] << CHNL_OUT_BUF_PITCH_LINE_PITCH_OFFSET; |
| writel(val, mxc_isi->regs + CHNL_OUT_BUF_PITCH); |
| } |
| |
| void mxc_isi_m2m_start_read(struct mxc_isi_dev *mxc_isi) |
| { |
| u32 val; |
| |
| val = readl(mxc_isi->regs + CHNL_MEM_RD_CTRL); |
| val &= ~ CHNL_MEM_RD_CTRL_READ_MEM_MASK; |
| writel(val, mxc_isi->regs + CHNL_MEM_RD_CTRL); |
| udelay(300); |
| |
| val |= CHNL_MEM_RD_CTRL_READ_MEM_ENABLE << CHNL_MEM_RD_CTRL_READ_MEM_OFFSET; |
| writel(val, mxc_isi->regs + CHNL_MEM_RD_CTRL); |
| } |