summaryrefslogtreecommitdiff
path: root/drivers/media/platform/imx8/mxc-isi-hw.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/imx8/mxc-isi-hw.c')
-rw-r--r--drivers/media/platform/imx8/mxc-isi-hw.c599
1 files changed, 599 insertions, 0 deletions
diff --git a/drivers/media/platform/imx8/mxc-isi-hw.c b/drivers/media/platform/imx8/mxc-isi-hw.c
new file mode 100644
index 000000000000..18750776a94f
--- /dev/null
+++ b/drivers/media/platform/imx8/mxc-isi-hw.c
@@ -0,0 +1,599 @@
+/*
+ * 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 */
+ { 0x0096004D, 0x05DA001D, 0x007005B6, 0x057C009E, 0x000005E6, 0x00000000 },
+};
+
+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_BGR24) ||
+ (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_NV12)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+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;
+ u32 framecount = buf->v4l2_buf.sequence;
+ int val = 0;
+
+ 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 == 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;
+ } else {
+ 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;
+ }
+ writel(val, mxc_isi->regs + CHNL_OUT_BUF_CTRL);
+}
+
+void mxc_isi_channel_hw_reset(struct mxc_isi_dev *mxc_isi)
+{
+ sc_ipc_t ipcHndl;
+ sc_err_t sciErr;
+ uint32_t mu_id;
+
+ sciErr = sc_ipc_getMuID(&mu_id);
+ if (sciErr != SC_ERR_NONE) {
+ pr_err("Cannot obtain MU ID\n");
+ return;
+ }
+
+ sciErr = sc_ipc_open(&ipcHndl, mu_id);
+ if (sciErr != SC_ERR_NONE) {
+ pr_err("sc_ipc_open failed! (sciError = %d)\n", sciErr);
+ return;
+ }
+
+ sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_ISI_CH0, SC_PM_PW_MODE_OFF);
+ if (sciErr != SC_ERR_NONE)
+ pr_err("sc_misc_MIPI reset failed! (sciError = %d)\n", sciErr);
+
+ sciErr = sc_pm_set_resource_power_mode(ipcHndl, SC_R_ISI_CH0, SC_PM_PW_MODE_ON);
+ if (sciErr != SC_ERR_NONE)
+ pr_err("sc_misc_MIPI reset failed! (sciError = %d)\n", sciErr);
+
+ udelay(500);
+
+ sc_ipc_close(mu_id);
+}
+
+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;
+
+ 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_RGB2YUV << 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);
+}
+
+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 (dst_f->height == src_f->height ||
+ dst_f->width == src_f->width) {
+ mxc_isi->scale = 0;
+ 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);
+ writel(0, mxc_isi->regs + CHNL_SCALE_OFFSET);
+
+ return;
+}
+
+void mxc_isi_channel_init(struct mxc_isi_dev *mxc_isi)
+{
+ u32 val;
+
+ /* hw reset */
+ mxc_isi_channel_hw_reset(mxc_isi);
+
+ /* 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;
+
+ /* 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);
+
+ /* 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 */
+#if 0
+ mxc_isi_channel_set_crop(mxc_isi);
+
+ mxc_isi_channel_set_flip(mxc_isi);
+ if (mxc_isi->alphaen)
+ mxc_isi_channel_set_alpha(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_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_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_CTRL);
+}
+
+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);
+}