/* * Copyright 2005-2016 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * 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 */ /*! * @file ipu_common.c * * @brief This file contains the IPU driver common API functions. * * @ingroup IPU */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ipu_param_mem.h" #include "ipu_regs.h" static struct ipu_soc ipu_array[MXC_IPU_MAX_NUM]; /* Static functions */ static irqreturn_t ipu_sync_irq_handler(int irq, void *desc); static irqreturn_t ipu_err_irq_handler(int irq, void *desc); static inline uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type) { return ((uint32_t) ch >> (6 * type)) & 0x3F; }; static inline int _ipu_is_ic_chan(uint32_t dma_chan) { return (((dma_chan >= 11) && (dma_chan <= 22) && (dma_chan != 17) && (dma_chan != 18))); } static inline int _ipu_is_vdi_out_chan(uint32_t dma_chan) { return (dma_chan == 5); } static inline int _ipu_is_ic_graphic_chan(uint32_t dma_chan) { return (dma_chan == 14 || dma_chan == 15); } /* Either DP BG or DP FG can be graphic window */ static inline int _ipu_is_dp_graphic_chan(uint32_t dma_chan) { return (dma_chan == 23 || dma_chan == 27); } static inline int _ipu_is_irt_chan(uint32_t dma_chan) { return ((dma_chan >= 45) && (dma_chan <= 50)); } static inline int _ipu_is_dmfc_chan(uint32_t dma_chan) { return ((dma_chan >= 23) && (dma_chan <= 29)); } static inline int _ipu_is_trb_chan(struct ipu_soc *ipu, uint32_t dma_chan) { return (((dma_chan == 8) || (dma_chan == 9) || (dma_chan == 10) || (dma_chan == 13) || (dma_chan == 21) || (dma_chan == 23) || (dma_chan == 27) || (dma_chan == 28)) && (ipu->devtype >= IPUv3EX)); } /* * We usually use IDMAC 23 as full plane and IDMAC 27 as partial * plane. * IDMAC 23/24/28/41 can drive a display respectively - primary * IDMAC 27 depends on IDMAC 23 - nonprimary */ static inline int _ipu_is_primary_disp_chan(uint32_t dma_chan) { return ((dma_chan == 23) || (dma_chan == 24) || (dma_chan == 28) || (dma_chan == 41)); } static inline int _ipu_is_sync_irq(uint32_t irq) { /* sync interrupt register number */ int reg_num = irq / 32 + 1; return ((reg_num == 1) || (reg_num == 2) || (reg_num == 3) || (reg_num == 4) || (reg_num == 7) || (reg_num == 8) || (reg_num == 11) || (reg_num == 12) || (reg_num == 13) || (reg_num == 14) || (reg_num == 15)); } static inline uint32_t tri_cur_buf_mask(uint32_t dma_chan) { uint32_t mask = 1UL << ((dma_chan * 2) & 0x1F); return mask * 3; } static inline uint32_t tri_cur_buf_shift(uint32_t dma_chan) { uint32_t mask = 1UL << ((dma_chan * 2) & 0x1F); return ffs(mask) - 1; } #define idma_is_valid(ch) ((ch) != NO_DMA) #define idma_mask(ch) (idma_is_valid(ch) ? (1UL << ((ch) & 0x1F)) : 0) static inline bool idma_is_set(struct ipu_soc *ipu, uint32_t reg, uint32_t dma) { return !!(ipu_idmac_read(ipu, reg) & idma_mask(dma)); } static int ipu_clk_setup_enable(struct ipu_soc *ipu) { char pixel_clk[11]; char pixel_clk_sel[15]; char pixel_clk_div[15]; char pixel_clk_parent0[5]; char pixel_clk_parent1[9]; char *pixel_clk_parents[2]; char di_clk[4]; char di_clk_sel[8]; struct clk *clk; unsigned int di; unsigned int ipu_id; /* for clk naming */ int ret; dev_dbg(ipu->dev, "ipu_clk = %lu\n", clk_get_rate(ipu->ipu_clk)); ipu_id = ipu->id + 1; pixel_clk_parents[0] = pixel_clk_parent0; pixel_clk_parents[1] = pixel_clk_parent1; for (di = 0; di < 2; di++) { snprintf(pixel_clk_sel, sizeof(pixel_clk_sel), "ipu%u_pclk%u_sel", ipu_id, di); snprintf(pixel_clk_parent0, sizeof(pixel_clk_parent0), "ipu%u", ipu_id); snprintf(pixel_clk_parent1, sizeof(pixel_clk_parent1), "ipu%u_di%u", ipu_id, di); clk = clk_register_mux_pix_clk(ipu->dev, pixel_clk_sel, (const char **)pixel_clk_parents, ARRAY_SIZE(pixel_clk_parents), 0, ipu->id, di, 0); if (IS_ERR(clk)) { dev_err(ipu->dev, "di%u mux clk register failed\n", di); return PTR_ERR(clk); } ipu->pixel_clk_sel[di] = clk; snprintf(pixel_clk_div, sizeof(pixel_clk_div), "ipu%u_pclk%u_div", ipu_id, di); clk = clk_register_div_pix_clk(ipu->dev, pixel_clk_div, pixel_clk_sel, 0, ipu->id, di, 0); if (IS_ERR(clk)) { dev_err(ipu->dev, "di%u div clk register failed\n", di); return PTR_ERR(clk); } snprintf(pixel_clk, sizeof(pixel_clk), "ipu%u_pclk%u", ipu_id, di); ipu->pixel_clk[di] = clk_register_gate_pix_clk(ipu->dev, pixel_clk, pixel_clk_div, CLK_SET_RATE_PARENT, ipu->id, di, 0); if (IS_ERR(ipu->pixel_clk[di])) { dev_err(ipu->dev, "di%u gate clk register failed\n", di); return PTR_ERR(ipu->pixel_clk[di]); } ret = clk_set_parent(ipu->pixel_clk_sel[di], ipu->ipu_clk); if (ret) { dev_err(ipu->dev, "pixel clk set parent failed\n"); return ret; } snprintf(di_clk, sizeof(di_clk), "di%u", di); ipu->di_clk[di] = devm_clk_get(ipu->dev, di_clk); if (IS_ERR(ipu->di_clk[di])) { dev_err(ipu->dev, "di%u clk get failed\n", di); return PTR_ERR(ipu->di_clk[di]); } snprintf(di_clk_sel, sizeof(di_clk_sel), "di%u_sel", di); ipu->di_clk_sel[di] = devm_clk_get(ipu->dev, di_clk_sel); if (IS_ERR(ipu->di_clk_sel[di])) { dev_err(ipu->dev, "di%u sel clk get failed\n", di); return PTR_ERR(ipu->di_clk_sel[di]); } } return 0; } static int ipu_mem_reset(struct ipu_soc *ipu) { int timeout = 1000; ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST); while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) { if (!timeout--) return -ETIME; msleep(1); } return 0; } struct ipu_soc *ipu_get_soc(int id) { if (id >= MXC_IPU_MAX_NUM) return ERR_PTR(-ENODEV); else if (!ipu_array[id].online) return ERR_PTR(-ENODEV); else return &(ipu_array[id]); } EXPORT_SYMBOL_GPL(ipu_get_soc); void _ipu_get(struct ipu_soc *ipu) { int ret; ret = clk_enable(ipu->ipu_clk); if (ret < 0) BUG(); } void _ipu_put(struct ipu_soc *ipu) { clk_disable(ipu->ipu_clk); } void ipu_disable_hsp_clk(struct ipu_soc *ipu) { _ipu_put(ipu); } EXPORT_SYMBOL(ipu_disable_hsp_clk); struct ipu_devtype { const char *name; unsigned long cm_ofs; unsigned long idmac_ofs; unsigned long ic_ofs; unsigned long csi0_ofs; unsigned long csi1_ofs; unsigned long di0_ofs; unsigned long di1_ofs; unsigned long smfc_ofs; unsigned long dc_ofs; unsigned long dmfc_ofs; unsigned long vdi_ofs; unsigned long cpmem_ofs; unsigned long srm_ofs; unsigned long tpm_ofs; unsigned long dc_tmpl_ofs; enum ipuv3_type type; bool idmac_used_bufs_present; }; struct ipu_platform_type { struct ipu_devtype devtype; unsigned int ch0123_axi; unsigned int ch23_axi; unsigned int ch27_axi; unsigned int ch28_axi; unsigned int normal_axi; bool smfc_idmac_12bit_3planar_bs_fixup; /* workaround little stripes */ bool idmac_used_bufs_en_r; bool idmac_used_bufs_en_w; unsigned int idmac_used_bufs_max_r; unsigned int idmac_used_bufs_max_w; }; static struct ipu_platform_type ipu_type_imx51 = { .devtype = { .name = "IPUv3EX", .cm_ofs = 0x1E000000, .idmac_ofs = 0x1E008000, .ic_ofs = 0x1E020000, .csi0_ofs = 0x1E030000, .csi1_ofs = 0x1E038000, .di0_ofs = 0x1E040000, .di1_ofs = 0x1E048000, .smfc_ofs = 0x1E050000, .dc_ofs = 0x1E058000, .dmfc_ofs = 0x1E060000, .vdi_ofs = 0x1E068000, .cpmem_ofs = 0x1F000000, .srm_ofs = 0x1F040000, .tpm_ofs = 0x1F060000, .dc_tmpl_ofs = 0x1F080000, .type = IPUv3EX, .idmac_used_bufs_present = false, }, .ch0123_axi = 1, .ch23_axi = 1, .ch23_axi = 1, .ch27_axi = 1, .ch28_axi = 1, .normal_axi = 0, .smfc_idmac_12bit_3planar_bs_fixup = false, }; static struct ipu_platform_type ipu_type_imx53 = { .devtype = { .name = "IPUv3M", .cm_ofs = 0x06000000, .idmac_ofs = 0x06008000, .ic_ofs = 0x06020000, .csi0_ofs = 0x06030000, .csi1_ofs = 0x06038000, .di0_ofs = 0x06040000, .di1_ofs = 0x06048000, .smfc_ofs = 0x06050000, .dc_ofs = 0x06058000, .dmfc_ofs = 0x06060000, .vdi_ofs = 0x06068000, .cpmem_ofs = 0x07000000, .srm_ofs = 0x07040000, .tpm_ofs = 0x07060000, .dc_tmpl_ofs = 0x07080000, .type = IPUv3M, .idmac_used_bufs_present = true, }, .ch0123_axi = 1, .ch23_axi = 1, .ch27_axi = 1, .ch28_axi = 1, .normal_axi = 0, .idmac_used_bufs_en_r = false, .idmac_used_bufs_en_w = false, .smfc_idmac_12bit_3planar_bs_fixup = false, }; static struct ipu_platform_type ipu_type_imx6q = { .devtype = { .name = "IPUv3H", .cm_ofs = 0x00200000, .idmac_ofs = 0x00208000, .ic_ofs = 0x00220000, .csi0_ofs = 0x00230000, .csi1_ofs = 0x00238000, .di0_ofs = 0x00240000, .di1_ofs = 0x00248000, .smfc_ofs = 0x00250000, .dc_ofs = 0x00258000, .dmfc_ofs = 0x00260000, .vdi_ofs = 0x00268000, .cpmem_ofs = 0x00300000, .srm_ofs = 0x00340000, .tpm_ofs = 0x00360000, .dc_tmpl_ofs = 0x00380000, .type = IPUv3H, .idmac_used_bufs_present = true, }, .ch0123_axi = 0, .ch23_axi = 0, .ch27_axi = 0, .ch28_axi = 0, .normal_axi = 1, .idmac_used_bufs_en_r = false, .idmac_used_bufs_en_w = false, .smfc_idmac_12bit_3planar_bs_fixup = false, }; static struct ipu_platform_type ipu_type_imx6qp = { .devtype = { .name = "IPUv3H", .cm_ofs = 0x00200000, .idmac_ofs = 0x00208000, .ic_ofs = 0x00220000, .csi0_ofs = 0x00230000, .csi1_ofs = 0x00238000, .di0_ofs = 0x00240000, .di1_ofs = 0x00248000, .smfc_ofs = 0x00250000, .dc_ofs = 0x00258000, .dmfc_ofs = 0x00260000, .vdi_ofs = 0x00268000, .cpmem_ofs = 0x00300000, .srm_ofs = 0x00340000, .tpm_ofs = 0x00360000, .dc_tmpl_ofs = 0x00380000, .type = IPUv3H, .idmac_used_bufs_present = true, }, .ch0123_axi = 0, .ch23_axi = 0, .ch27_axi = 2, .ch28_axi = 3, .normal_axi = 1, .idmac_used_bufs_en_r = true, .idmac_used_bufs_en_w = true, .idmac_used_bufs_max_r = 0x3, .idmac_used_bufs_max_w = 0x3, .smfc_idmac_12bit_3planar_bs_fixup = true, }; static const struct of_device_id imx_ipuv3_dt_ids[] = { { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, }, { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, }, { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, }, { .compatible = "fsl,imx6qp-ipu", .data = &ipu_type_imx6qp, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_ipuv3_dt_ids); /*! * This function is called by the driver framework to initialize the IPU * hardware. * * @param dev The device structure for the IPU passed in by the * driver framework. * * @return Returns 0 on success or negative error code on error */ static int ipu_probe(struct platform_device *pdev) { struct ipu_soc *ipu; struct resource *res; unsigned long ipu_base; const struct of_device_id *of_id = of_match_device(imx_ipuv3_dt_ids, &pdev->dev); const struct ipu_platform_type *iputype = of_id->data; const struct ipu_devtype *devtype = &iputype->devtype; int ret = 0, id; u32 bypass_reset, reg; dev_dbg(&pdev->dev, "<%s>\n", __func__); ret = of_property_read_u32(pdev->dev.of_node, "bypass_reset", &bypass_reset); if (ret < 0) { dev_dbg(&pdev->dev, "can not get bypass_reset\n"); return ret; } id = of_alias_get_id(pdev->dev.of_node, "ipu"); if (id < 0) { dev_dbg(&pdev->dev, "can not get alias id\n"); return id; } ipu = &ipu_array[id]; memset(ipu, 0, sizeof(struct ipu_soc)); ipu->bypass_reset = (bool)bypass_reset; ipu->dev = &pdev->dev; ipu->id = id; ipu->devtype = devtype->type; ipu->ch0123_axi = iputype->ch0123_axi; ipu->ch23_axi = iputype->ch23_axi; ipu->ch27_axi = iputype->ch27_axi; ipu->ch28_axi = iputype->ch28_axi; ipu->normal_axi = iputype->normal_axi; ipu->smfc_idmac_12bit_3planar_bs_fixup = iputype->smfc_idmac_12bit_3planar_bs_fixup; spin_lock_init(&ipu->int_reg_spin_lock); spin_lock_init(&ipu->rdy_reg_spin_lock); mutex_init(&ipu->mutex_lock); dev_dbg(&pdev->dev, "revision is %s\n", devtype->name); ipu->irq_sync = platform_get_irq(pdev, 0); ipu->irq_err = platform_get_irq(pdev, 1); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res || ipu->irq_sync < 0 || ipu->irq_err < 0) { dev_err(&pdev->dev, "can't get device resources\n"); return -ENODEV; } if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), pdev->name)) return -EBUSY; ret = devm_request_irq(&pdev->dev, ipu->irq_sync, ipu_sync_irq_handler, 0, pdev->name, ipu); if (ret) { dev_err(ipu->dev, "request SYNC interrupt failed\n"); return ret; } ret = devm_request_irq(&pdev->dev, ipu->irq_err, ipu_err_irq_handler, 0, pdev->name, ipu); if (ret) { dev_err(ipu->dev, "request ERR interrupt failed\n"); return ret; } ipu_base = res->start; ipu->cm_reg = devm_ioremap(&pdev->dev, ipu_base + devtype->cm_ofs, PAGE_SIZE); ipu->ic_reg = devm_ioremap(&pdev->dev, ipu_base + devtype->ic_ofs, PAGE_SIZE); ipu->idmac_reg = devm_ioremap(&pdev->dev, ipu_base + devtype->idmac_ofs, PAGE_SIZE); /* DP Registers are accessed thru the SRM */ ipu->dp_reg = devm_ioremap(&pdev->dev, ipu_base + devtype->srm_ofs, PAGE_SIZE); ipu->dc_reg = devm_ioremap(&pdev->dev, ipu_base + devtype->dc_ofs, PAGE_SIZE); ipu->dmfc_reg = devm_ioremap(&pdev->dev, ipu_base + devtype->dmfc_ofs, PAGE_SIZE); ipu->di_reg[0] = devm_ioremap(&pdev->dev, ipu_base + devtype->di0_ofs, PAGE_SIZE); ipu->di_reg[1] = devm_ioremap(&pdev->dev, ipu_base + devtype->di1_ofs, PAGE_SIZE); ipu->smfc_reg = devm_ioremap(&pdev->dev, ipu_base + devtype->smfc_ofs, PAGE_SIZE); ipu->csi_reg[0] = devm_ioremap(&pdev->dev, ipu_base + devtype->csi0_ofs, PAGE_SIZE); ipu->csi_reg[1] = devm_ioremap(&pdev->dev, ipu_base + devtype->csi1_ofs, PAGE_SIZE); ipu->cpmem_base = devm_ioremap(&pdev->dev, ipu_base + devtype->cpmem_ofs, SZ_128K); ipu->tpmem_base = devm_ioremap(&pdev->dev, ipu_base + devtype->tpm_ofs, SZ_64K); ipu->dc_tmpl_reg = devm_ioremap(&pdev->dev, ipu_base + devtype->dc_tmpl_ofs, SZ_128K); ipu->vdi_reg = devm_ioremap(&pdev->dev, ipu_base + devtype->vdi_ofs, PAGE_SIZE); if (!ipu->cm_reg || !ipu->ic_reg || !ipu->idmac_reg || !ipu->dp_reg || !ipu->dc_reg || !ipu->dmfc_reg || !ipu->di_reg[0] || !ipu->di_reg[1] || !ipu->smfc_reg || !ipu->csi_reg[0] || !ipu->csi_reg[1] || !ipu->cpmem_base || !ipu->tpmem_base || !ipu->dc_tmpl_reg || !ipu->vdi_reg) return -ENOMEM; dev_dbg(ipu->dev, "IPU CM Regs = %p\n", ipu->cm_reg); dev_dbg(ipu->dev, "IPU IC Regs = %p\n", ipu->ic_reg); dev_dbg(ipu->dev, "IPU IDMAC Regs = %p\n", ipu->idmac_reg); dev_dbg(ipu->dev, "IPU DP Regs = %p\n", ipu->dp_reg); dev_dbg(ipu->dev, "IPU DC Regs = %p\n", ipu->dc_reg); dev_dbg(ipu->dev, "IPU DMFC Regs = %p\n", ipu->dmfc_reg); dev_dbg(ipu->dev, "IPU DI0 Regs = %p\n", ipu->di_reg[0]); dev_dbg(ipu->dev, "IPU DI1 Regs = %p\n", ipu->di_reg[1]); dev_dbg(ipu->dev, "IPU SMFC Regs = %p\n", ipu->smfc_reg); dev_dbg(ipu->dev, "IPU CSI0 Regs = %p\n", ipu->csi_reg[0]); dev_dbg(ipu->dev, "IPU CSI1 Regs = %p\n", ipu->csi_reg[1]); dev_dbg(ipu->dev, "IPU CPMem = %p\n", ipu->cpmem_base); dev_dbg(ipu->dev, "IPU TPMem = %p\n", ipu->tpmem_base); dev_dbg(ipu->dev, "IPU DC Template Mem = %p\n", ipu->dc_tmpl_reg); dev_dbg(ipu->dev, "IPU VDI Regs = %p\n", ipu->vdi_reg); ipu->ipu_clk = devm_clk_get(ipu->dev, "bus"); if (IS_ERR(ipu->ipu_clk)) { dev_err(ipu->dev, "clk_get ipu failed"); return PTR_ERR(ipu->ipu_clk); } /* ipu_clk is always prepared */ ret = clk_prepare_enable(ipu->ipu_clk); if (ret < 0) { dev_err(ipu->dev, "ipu clk enable failed\n"); return ret; } ipu->prg_clk = devm_clk_get(ipu->dev, "prg"); if (IS_ERR(ipu->prg_clk)) ipu->prg_clk = NULL; ipu->online = true; platform_set_drvdata(pdev, ipu); if (!bypass_reset) { ret = device_reset(&pdev->dev); if (ret) { dev_err(&pdev->dev, "failed to reset: %d\n", ret); return ret; } ipu_mem_reset(ipu); ipu_disp_init(ipu); /* Set MCU_T to divide MCU access window into 2 */ ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN); } /* setup ipu clk tree after ipu reset */ ret = ipu_clk_setup_enable(ipu); if (ret < 0) { dev_err(ipu->dev, "ipu clk setup failed\n"); ipu->online = false; return ret; } if (devtype->idmac_used_bufs_present) { reg = ipu_idmac_read(ipu, IDMAC_CONF); if (iputype->idmac_used_bufs_en_r) reg |= IDMAC_CONF_USED_BUFS_EN_R; else reg &= ~IDMAC_CONF_USED_BUFS_EN_R; if (iputype->idmac_used_bufs_en_w) reg |= IDMAC_CONF_USED_BUFS_EN_W; else reg &= ~IDMAC_CONF_USED_BUFS_EN_W; reg &= ~IDMAC_CONF_USED_BUFS_MAX_R_MASK; reg |= (iputype->idmac_used_bufs_max_r << IDMAC_CONF_USED_BUFS_MAX_R_OFFSET); reg &= ~IDMAC_CONF_USED_BUFS_MAX_W_MASK; reg |= (iputype->idmac_used_bufs_max_w << IDMAC_CONF_USED_BUFS_MAX_W_OFFSET); ipu_idmac_write(ipu, reg, IDMAC_CONF); } /* Set sync refresh channels and CSI->mem channel as high priority */ ipu_idmac_write(ipu, 0x18800003L, IDMAC_CHA_PRI(0)); /* Enable error interrupts by default */ ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(5)); ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(6)); ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(9)); ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(10)); if (!bypass_reset) clk_disable(ipu->ipu_clk); register_ipu_device(ipu, id); pm_runtime_enable(&pdev->dev); return ret; } int ipu_remove(struct platform_device *pdev) { struct ipu_soc *ipu = platform_get_drvdata(pdev); unregister_ipu_device(ipu, ipu->id); clk_put(ipu->ipu_clk); return 0; } void ipu_dump_registers(struct ipu_soc *ipu) { dev_dbg(ipu->dev, "IPU_CONF = \t0x%08X\n", ipu_cm_read(ipu, IPU_CONF)); dev_dbg(ipu->dev, "IDMAC_CONF = \t0x%08X\n", ipu_idmac_read(ipu, IDMAC_CONF)); dev_dbg(ipu->dev, "IDMAC_CHA_EN1 = \t0x%08X\n", ipu_idmac_read(ipu, IDMAC_CHA_EN(0))); dev_dbg(ipu->dev, "IDMAC_CHA_EN2 = \t0x%08X\n", ipu_idmac_read(ipu, IDMAC_CHA_EN(32))); dev_dbg(ipu->dev, "IDMAC_CHA_PRI1 = \t0x%08X\n", ipu_idmac_read(ipu, IDMAC_CHA_PRI(0))); dev_dbg(ipu->dev, "IDMAC_CHA_PRI2 = \t0x%08X\n", ipu_idmac_read(ipu, IDMAC_CHA_PRI(32))); dev_dbg(ipu->dev, "IDMAC_BAND_EN1 = \t0x%08X\n", ipu_idmac_read(ipu, IDMAC_BAND_EN(ipu->devtype, 0))); dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", ipu_idmac_read(ipu, IDMAC_BAND_EN(ipu->devtype, 32))); dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n", ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0))); dev_dbg(ipu->dev, "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n", ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32))); if (ipu->devtype >= IPUv3EX) { dev_dbg(ipu->dev, "IPU_CHA_TRB_MODE_SEL0 = \t0x%08X\n", ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, 0))); dev_dbg(ipu->dev, "IPU_CHA_TRB_MODE_SEL1 = \t0x%08X\n", ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, 32))); } dev_dbg(ipu->dev, "DMFC_WR_CHAN = \t0x%08X\n", ipu_dmfc_read(ipu, DMFC_WR_CHAN)); dev_dbg(ipu->dev, "DMFC_WR_CHAN_DEF = \t0x%08X\n", ipu_dmfc_read(ipu, DMFC_WR_CHAN_DEF)); dev_dbg(ipu->dev, "DMFC_DP_CHAN = \t0x%08X\n", ipu_dmfc_read(ipu, DMFC_DP_CHAN)); dev_dbg(ipu->dev, "DMFC_DP_CHAN_DEF = \t0x%08X\n", ipu_dmfc_read(ipu, DMFC_DP_CHAN_DEF)); dev_dbg(ipu->dev, "DMFC_IC_CTRL = \t0x%08X\n", ipu_dmfc_read(ipu, DMFC_IC_CTRL)); dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW1 = \t0x%08X\n", ipu_cm_read(ipu, IPU_FS_PROC_FLOW1)); dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW2 = \t0x%08X\n", ipu_cm_read(ipu, IPU_FS_PROC_FLOW2)); dev_dbg(ipu->dev, "IPU_FS_PROC_FLOW3 = \t0x%08X\n", ipu_cm_read(ipu, IPU_FS_PROC_FLOW3)); dev_dbg(ipu->dev, "IPU_FS_DISP_FLOW1 = \t0x%08X\n", ipu_cm_read(ipu, IPU_FS_DISP_FLOW1)); dev_dbg(ipu->dev, "IPU_VDIC_VDI_FSIZE = \t0x%08X\n", ipu_vdi_read(ipu, VDI_FSIZE)); dev_dbg(ipu->dev, "IPU_VDIC_VDI_C = \t0x%08X\n", ipu_vdi_read(ipu, VDI_C)); dev_dbg(ipu->dev, "IPU_IC_CONF = \t0x%08X\n", ipu_ic_read(ipu, IC_CONF)); } /*! * This function is called to initialize a logical IPU channel. * * @param ipu ipu handler * @param channel Input parameter for the logical channel ID to init. * * @param params Input parameter containing union of channel * initialization parameters. * * @return Returns 0 on success or negative error code on fail */ int32_t ipu_init_channel(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params) { int ret = 0; bool bad_pixfmt; uint32_t ipu_conf, reg, in_g_pixel_fmt, sec_dma; dev_dbg(ipu->dev, "init channel = %d\n", IPU_CHAN_ID(channel)); ret = pm_runtime_get_sync(ipu->dev); if (ret < 0) { dev_err(ipu->dev, "ch = %d, pm_runtime_get failed:%d!\n", IPU_CHAN_ID(channel), ret); dump_stack(); return ret; } /* * Here, ret could be 1 if the device's runtime PM status was * already 'active', so clear it to be 0. */ ret = 0; _ipu_get(ipu); mutex_lock(&ipu->mutex_lock); /* Re-enable error interrupts every time a channel is initialized */ ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(5)); ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(6)); ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(9)); ipu_cm_write(ipu, 0xFFFFFFFF, IPU_INT_CTRL(10)); if (ipu->channel_init_mask & (1L << IPU_CHAN_ID(channel))) { dev_warn(ipu->dev, "Warning: channel already initialized %d\n", IPU_CHAN_ID(channel)); } ipu_conf = ipu_cm_read(ipu, IPU_CONF); switch (channel) { case CSI_MEM0: case CSI_MEM1: case CSI_MEM2: case CSI_MEM3: if (params->csi_mem.csi > 1) { ret = -EINVAL; goto err; } if (params->csi_mem.interlaced) ipu->chan_is_interlaced[channel_2_dma(channel, IPU_OUTPUT_BUFFER)] = true; else ipu->chan_is_interlaced[channel_2_dma(channel, IPU_OUTPUT_BUFFER)] = false; ipu->smfc_use_count++; ipu->csi_channel[params->csi_mem.csi] = channel; /*SMFC setting*/ if (params->csi_mem.mipi_en) { ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_mem.csi)); _ipu_smfc_init(ipu, channel, params->csi_mem.mipi_vc, params->csi_mem.csi); _ipu_csi_set_mipi_di(ipu, params->csi_mem.mipi_vc, params->csi_mem.mipi_id, params->csi_mem.csi); } else { ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_mem.csi)); _ipu_smfc_init(ipu, channel, 0, params->csi_mem.csi); } /*CSI data (include compander) dest*/ _ipu_csi_init(ipu, channel, params->csi_mem.csi); break; case CSI_PRP_ENC_MEM: if (params->csi_prp_enc_mem.csi > 1) { ret = -EINVAL; goto err; } if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || (ipu->using_ic_dirct_ch == MEM_VDI_MEM)) { ret = -EINVAL; goto err; } ipu->using_ic_dirct_ch = CSI_PRP_ENC_MEM; ipu->ic_use_count++; ipu->csi_channel[params->csi_prp_enc_mem.csi] = channel; if (params->csi_prp_enc_mem.mipi_en) { ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_prp_enc_mem.csi)); _ipu_csi_set_mipi_di(ipu, params->csi_prp_enc_mem.mipi_vc, params->csi_prp_enc_mem.mipi_id, params->csi_prp_enc_mem.csi); } else ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_prp_enc_mem.csi)); /*CSI0/1 feed into IC*/ ipu_conf &= ~IPU_CONF_IC_INPUT; if (params->csi_prp_enc_mem.csi) ipu_conf |= IPU_CONF_CSI_SEL; else ipu_conf &= ~IPU_CONF_CSI_SEL; /*PRP skip buffer in memory, only valid when RWS_EN is true*/ reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); ipu_cm_write(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1); /*CSI data (include compander) dest*/ _ipu_csi_init(ipu, channel, params->csi_prp_enc_mem.csi); _ipu_ic_init_prpenc(ipu, params, true); break; case CSI_PRP_VF_MEM: if (params->csi_prp_vf_mem.csi > 1) { ret = -EINVAL; goto err; } if ((ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || (ipu->using_ic_dirct_ch == MEM_VDI_MEM)) { ret = -EINVAL; goto err; } ipu->using_ic_dirct_ch = CSI_PRP_VF_MEM; ipu->ic_use_count++; ipu->csi_channel[params->csi_prp_vf_mem.csi] = channel; if (params->csi_prp_vf_mem.mipi_en) { ipu_conf |= (1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_prp_vf_mem.csi)); _ipu_csi_set_mipi_di(ipu, params->csi_prp_vf_mem.mipi_vc, params->csi_prp_vf_mem.mipi_id, params->csi_prp_vf_mem.csi); } else ipu_conf &= ~(1 << (IPU_CONF_CSI0_DATA_SOURCE_OFFSET + params->csi_prp_vf_mem.csi)); /*CSI0/1 feed into IC*/ ipu_conf &= ~IPU_CONF_IC_INPUT; if (params->csi_prp_vf_mem.csi) ipu_conf |= IPU_CONF_CSI_SEL; else ipu_conf &= ~IPU_CONF_CSI_SEL; /*PRP skip buffer in memory, only valid when RWS_EN is true*/ reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); /*CSI data (include compander) dest*/ _ipu_csi_init(ipu, channel, params->csi_prp_vf_mem.csi); _ipu_ic_init_prpvf(ipu, params, true); break; case MEM_PRP_VF_MEM: if (params->mem_prp_vf_mem.graphics_combine_en) { sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); in_g_pixel_fmt = params->mem_prp_vf_mem.in_g_pixel_fmt; bad_pixfmt = _ipu_ch_param_bad_alpha_pos(in_g_pixel_fmt); if (params->mem_prp_vf_mem.alpha_chan_en) { if (bad_pixfmt) { dev_err(ipu->dev, "bad pixel format " "for graphics plane from " "ch%d\n", sec_dma); ret = -EINVAL; goto err; } ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; } ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; } reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); ipu_cm_write(ipu, reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); _ipu_ic_init_prpvf(ipu, params, false); ipu->ic_use_count++; break; case MEM_VDI_PRP_VF_MEM: if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) || (ipu->using_ic_dirct_ch == MEM_VDI_MEM) || (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) { ret = -EINVAL; goto err; } ipu->using_ic_dirct_ch = MEM_VDI_PRP_VF_MEM; ipu->ic_use_count++; ipu->vdi_use_count++; reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); reg &= ~FS_VDI_SRC_SEL_MASK; ipu_cm_write(ipu, reg , IPU_FS_PROC_FLOW1); if (params->mem_prp_vf_mem.graphics_combine_en) ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; _ipu_ic_init_prpvf(ipu, params, false); _ipu_vdi_init(ipu, channel, params); break; case MEM_VDI_PRP_VF_MEM_P: case MEM_VDI_PRP_VF_MEM_N: case MEM_VDI_MEM_P: case MEM_VDI_MEM_N: _ipu_vdi_init(ipu, channel, params); break; case MEM_VDI_MEM: if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) || (ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) || (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM)) { ret = -EINVAL; goto err; } ipu->using_ic_dirct_ch = MEM_VDI_MEM; ipu->ic_use_count++; ipu->vdi_use_count++; _ipu_vdi_init(ipu, channel, params); break; case MEM_ROT_VF_MEM: ipu->ic_use_count++; ipu->rot_use_count++; _ipu_ic_init_rotate_vf(ipu, params); break; case MEM_PRP_ENC_MEM: ipu->ic_use_count++; reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); ipu_cm_write(ipu, reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1); _ipu_ic_init_prpenc(ipu, params, false); break; case MEM_ROT_ENC_MEM: ipu->ic_use_count++; ipu->rot_use_count++; _ipu_ic_init_rotate_enc(ipu, params); break; case MEM_PP_MEM: if (params->mem_pp_mem.graphics_combine_en) { sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); in_g_pixel_fmt = params->mem_pp_mem.in_g_pixel_fmt; bad_pixfmt = _ipu_ch_param_bad_alpha_pos(in_g_pixel_fmt); if (params->mem_pp_mem.alpha_chan_en) { if (bad_pixfmt) { dev_err(ipu->dev, "bad pixel format " "for graphics plane from " "ch%d\n", sec_dma); ret = -EINVAL; goto err; } ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; } ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; } _ipu_ic_init_pp(ipu, params); ipu->ic_use_count++; break; case MEM_ROT_PP_MEM: _ipu_ic_init_rotate_pp(ipu, params); ipu->ic_use_count++; ipu->rot_use_count++; break; case MEM_DC_SYNC: if (params->mem_dc_sync.di > 1) { ret = -EINVAL; goto err; } ipu->dc_di_assignment[1] = params->mem_dc_sync.di; _ipu_dc_init(ipu, 1, params->mem_dc_sync.di, params->mem_dc_sync.interlaced, params->mem_dc_sync.out_pixel_fmt); ipu->di_use_count[params->mem_dc_sync.di]++; ipu->dc_use_count++; ipu->dmfc_use_count++; break; case MEM_BG_SYNC: if (params->mem_dp_bg_sync.di > 1) { ret = -EINVAL; goto err; } if (params->mem_dp_bg_sync.alpha_chan_en) ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; ipu->dc_di_assignment[5] = params->mem_dp_bg_sync.di; _ipu_dp_init(ipu, channel, params->mem_dp_bg_sync.in_pixel_fmt, params->mem_dp_bg_sync.out_pixel_fmt); _ipu_dc_init(ipu, 5, params->mem_dp_bg_sync.di, params->mem_dp_bg_sync.interlaced, params->mem_dp_bg_sync.out_pixel_fmt); ipu->di_use_count[params->mem_dp_bg_sync.di]++; ipu->dc_use_count++; ipu->dp_use_count++; ipu->dmfc_use_count++; break; case MEM_FG_SYNC: _ipu_dp_init(ipu, channel, params->mem_dp_fg_sync.in_pixel_fmt, params->mem_dp_fg_sync.out_pixel_fmt); if (params->mem_dp_fg_sync.alpha_chan_en) ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; ipu->dc_use_count++; ipu->dp_use_count++; ipu->dmfc_use_count++; break; case DIRECT_ASYNC0: if (params->direct_async.di > 1) { ret = -EINVAL; goto err; } ipu->dc_di_assignment[8] = params->direct_async.di; _ipu_dc_init(ipu, 8, params->direct_async.di, false, IPU_PIX_FMT_GENERIC); ipu->di_use_count[params->direct_async.di]++; ipu->dc_use_count++; break; case DIRECT_ASYNC1: if (params->direct_async.di > 1) { ret = -EINVAL; goto err; } ipu->dc_di_assignment[9] = params->direct_async.di; _ipu_dc_init(ipu, 9, params->direct_async.di, false, IPU_PIX_FMT_GENERIC); ipu->di_use_count[params->direct_async.di]++; ipu->dc_use_count++; break; default: dev_err(ipu->dev, "Missing channel initialization\n"); break; } ipu->channel_init_mask |= 1L << IPU_CHAN_ID(channel); ipu_cm_write(ipu, ipu_conf, IPU_CONF); err: mutex_unlock(&ipu->mutex_lock); return ret; } EXPORT_SYMBOL(ipu_init_channel); /*! * This function is called to uninitialize a logical IPU channel. * * @param ipu ipu handler * @param channel Input parameter for the logical channel ID to uninit. */ void ipu_uninit_channel(struct ipu_soc *ipu, ipu_channel_t channel) { uint32_t reg; uint32_t in_dma, out_dma = 0; uint32_t ipu_conf; uint32_t dc_chan = 0; int ret; mutex_lock(&ipu->mutex_lock); if ((ipu->channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) { dev_dbg(ipu->dev, "Channel already uninitialized %d\n", IPU_CHAN_ID(channel)); mutex_unlock(&ipu->mutex_lock); return; } /* Make sure channel is disabled */ /* Get input and output dma channels */ in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); if (idma_is_set(ipu, IDMAC_CHA_EN(in_dma), in_dma) || idma_is_set(ipu, IDMAC_CHA_EN(out_dma), out_dma)) { dev_err(ipu->dev, "Channel %d is not disabled, disable first\n", IPU_CHAN_ID(channel)); mutex_unlock(&ipu->mutex_lock); return; } ipu_conf = ipu_cm_read(ipu, IPU_CONF); /* Reset the double buffer */ reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(in_dma)); ipu_cm_write(ipu, reg & ~idma_mask(in_dma), IPU_CHA_DB_MODE_SEL(in_dma)); reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(out_dma)); ipu_cm_write(ipu, reg & ~idma_mask(out_dma), IPU_CHA_DB_MODE_SEL(out_dma)); /* Reset the triple buffer */ reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, in_dma)); ipu_cm_write(ipu, reg & ~idma_mask(in_dma), IPU_CHA_TRB_MODE_SEL(ipu->devtype, in_dma)); reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, out_dma)); ipu_cm_write(ipu, reg & ~idma_mask(out_dma), IPU_CHA_TRB_MODE_SEL(ipu->devtype, out_dma)); if (_ipu_is_ic_chan(in_dma) || _ipu_is_dp_graphic_chan(in_dma)) { ipu->sec_chan_en[IPU_CHAN_ID(channel)] = false; ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = false; } switch (channel) { case CSI_MEM0: case CSI_MEM1: case CSI_MEM2: case CSI_MEM3: ipu->smfc_use_count--; if (ipu->csi_channel[0] == channel) { ipu->csi_channel[0] = CHAN_NONE; } else if (ipu->csi_channel[1] == channel) { ipu->csi_channel[1] = CHAN_NONE; } break; case CSI_PRP_ENC_MEM: ipu->ic_use_count--; if (ipu->using_ic_dirct_ch == CSI_PRP_ENC_MEM) ipu->using_ic_dirct_ch = 0; _ipu_ic_uninit_prpenc(ipu); if (ipu->csi_channel[0] == channel) { ipu->csi_channel[0] = CHAN_NONE; } else if (ipu->csi_channel[1] == channel) { ipu->csi_channel[1] = CHAN_NONE; } break; case CSI_PRP_VF_MEM: ipu->ic_use_count--; if (ipu->using_ic_dirct_ch == CSI_PRP_VF_MEM) ipu->using_ic_dirct_ch = 0; _ipu_ic_uninit_prpvf(ipu); if (ipu->csi_channel[0] == channel) { ipu->csi_channel[0] = CHAN_NONE; } else if (ipu->csi_channel[1] == channel) { ipu->csi_channel[1] = CHAN_NONE; } break; case MEM_PRP_VF_MEM: ipu->ic_use_count--; _ipu_ic_uninit_prpvf(ipu); reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); break; case MEM_VDI_PRP_VF_MEM: ipu->ic_use_count--; ipu->vdi_use_count--; if (ipu->using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) ipu->using_ic_dirct_ch = 0; _ipu_ic_uninit_prpvf(ipu); _ipu_vdi_uninit(ipu); reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); ipu_cm_write(ipu, reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); break; case MEM_VDI_MEM: ipu->ic_use_count--; ipu->vdi_use_count--; if (ipu->using_ic_dirct_ch == MEM_VDI_MEM) ipu->using_ic_dirct_ch = 0; _ipu_vdi_uninit(ipu); break; case MEM_VDI_PRP_VF_MEM_P: case MEM_VDI_PRP_VF_MEM_N: case MEM_VDI_MEM_P: case MEM_VDI_MEM_N: break; case MEM_ROT_VF_MEM: ipu->rot_use_count--; ipu->ic_use_count--; _ipu_ic_uninit_rotate_vf(ipu); break; case MEM_PRP_ENC_MEM: ipu->ic_use_count--; _ipu_ic_uninit_prpenc(ipu); reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); ipu_cm_write(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1); break; case MEM_ROT_ENC_MEM: ipu->rot_use_count--; ipu->ic_use_count--; _ipu_ic_uninit_rotate_enc(ipu); break; case MEM_PP_MEM: ipu->ic_use_count--; _ipu_ic_uninit_pp(ipu); break; case MEM_ROT_PP_MEM: ipu->rot_use_count--; ipu->ic_use_count--; _ipu_ic_uninit_rotate_pp(ipu); break; case MEM_DC_SYNC: dc_chan = 1; _ipu_dc_uninit(ipu, 1); ipu->di_use_count[ipu->dc_di_assignment[1]]--; ipu->dc_use_count--; ipu->dmfc_use_count--; break; case MEM_BG_SYNC: dc_chan = 5; _ipu_dp_uninit(ipu, channel); _ipu_dc_uninit(ipu, 5); ipu->di_use_count[ipu->dc_di_assignment[5]]--; ipu->dc_use_count--; ipu->dp_use_count--; ipu->dmfc_use_count--; break; case MEM_FG_SYNC: _ipu_dp_uninit(ipu, channel); ipu->dc_use_count--; ipu->dp_use_count--; ipu->dmfc_use_count--; break; case DIRECT_ASYNC0: dc_chan = 8; _ipu_dc_uninit(ipu, 8); ipu->di_use_count[ipu->dc_di_assignment[8]]--; ipu->dc_use_count--; break; case DIRECT_ASYNC1: dc_chan = 9; _ipu_dc_uninit(ipu, 9); ipu->di_use_count[ipu->dc_di_assignment[9]]--; ipu->dc_use_count--; break; default: break; } if (ipu->ic_use_count == 0) ipu_conf &= ~IPU_CONF_IC_EN; if (ipu->vdi_use_count == 0) { ipu_conf &= ~IPU_CONF_ISP_EN; ipu_conf &= ~IPU_CONF_VDI_EN; ipu_conf &= ~IPU_CONF_IC_INPUT; } if (ipu->rot_use_count == 0) ipu_conf &= ~IPU_CONF_ROT_EN; if (ipu->dc_use_count == 0) ipu_conf &= ~IPU_CONF_DC_EN; if (ipu->dp_use_count == 0) ipu_conf &= ~IPU_CONF_DP_EN; if (ipu->dmfc_use_count == 0) ipu_conf &= ~IPU_CONF_DMFC_EN; if (ipu->di_use_count[0] == 0) { ipu_conf &= ~IPU_CONF_DI0_EN; } if (ipu->di_use_count[1] == 0) { ipu_conf &= ~IPU_CONF_DI1_EN; } if (ipu->smfc_use_count == 0) ipu_conf &= ~IPU_CONF_SMFC_EN; ipu_cm_write(ipu, ipu_conf, IPU_CONF); ipu->channel_init_mask &= ~(1L << IPU_CHAN_ID(channel)); /* * Disable pixel clk and its parent clock(if the parent clock * usecount is 1) after clearing DC/DP/DI bits in IPU_CONF * register to prevent LVDS display channel starvation. */ if (_ipu_is_primary_disp_chan(in_dma) && ipu->pixel_clk_en[ipu->dc_di_assignment[dc_chan]]) { clk_disable_unprepare(ipu->pixel_clk[ipu->dc_di_assignment[dc_chan]]); ipu->pixel_clk_en[ipu->dc_di_assignment[dc_chan]] = false; } mutex_unlock(&ipu->mutex_lock); _ipu_put(ipu); ret = pm_runtime_put_sync_suspend(ipu->dev); if (ret < 0) { dev_err(ipu->dev, "ch = %d, pm_runtime_put failed:%d!\n", IPU_CHAN_ID(channel), ret); dump_stack(); } WARN_ON(ipu->ic_use_count < 0); WARN_ON(ipu->vdi_use_count < 0); WARN_ON(ipu->rot_use_count < 0); WARN_ON(ipu->dc_use_count < 0); WARN_ON(ipu->dp_use_count < 0); WARN_ON(ipu->dmfc_use_count < 0); WARN_ON(ipu->smfc_use_count < 0); } EXPORT_SYMBOL(ipu_uninit_channel); /*! * This function is called to initialize buffer(s) for logical IPU channel. * * @param ipu ipu handler * * @param channel Input parameter for the logical channel ID. * * @param type Input parameter which buffer to initialize. * * @param pixel_fmt Input parameter for pixel format of buffer. * Pixel format is a FOURCC ASCII code. * * @param width Input parameter for width of buffer in pixels. * * @param height Input parameter for height of buffer in pixels. * * @param stride Input parameter for stride length of buffer * in pixels. * * @param rot_mode Input parameter for rotation setting of buffer. * A rotation setting other than * IPU_ROTATE_VERT_FLIP * should only be used for input buffers of * rotation channels. * * @param phyaddr_0 Input parameter buffer 0 physical address. * * @param phyaddr_1 Input parameter buffer 1 physical address. * Setting this to a value other than NULL enables * double buffering mode. * * @param phyaddr_2 Input parameter buffer 2 physical address. * Setting this to a value other than NULL enables * triple buffering mode, phyaddr_1 should not be * NULL then. * * @param u private u offset for additional cropping, * zero if not used. * * @param v private v offset for additional cropping, * zero if not used. * * @return Returns 0 on success or negative error code on fail */ int32_t ipu_init_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, uint32_t pixel_fmt, uint16_t width, uint16_t height, uint32_t stride, ipu_rotate_mode_t rot_mode, dma_addr_t phyaddr_0, dma_addr_t phyaddr_1, dma_addr_t phyaddr_2, uint32_t u, uint32_t v) { uint32_t reg; uint32_t dma_chan; uint32_t burst_size; dma_chan = channel_2_dma(channel, type); if (!idma_is_valid(dma_chan)) return -EINVAL; if (stride < width * bytes_per_pixel(pixel_fmt)) stride = width * bytes_per_pixel(pixel_fmt); if (stride % 4) { dev_err(ipu->dev, "Stride not 32-bit aligned, stride = %d\n", stride); return -EINVAL; } /* IC & IRT channels' width must be multiple of 8 pixels */ if ((_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan)) && (width % 8)) { dev_err(ipu->dev, "Width must be 8 pixel multiple\n"); return -EINVAL; } if (_ipu_is_vdi_out_chan(dma_chan) && ((width < 16) || (height < 16) || (width % 2) || (height % 4))) { dev_err(ipu->dev, "vdi width/height limited err\n"); return -EINVAL; } /* IPUv3EX and IPUv3M support triple buffer */ if ((!_ipu_is_trb_chan(ipu, dma_chan)) && phyaddr_2) { dev_err(ipu->dev, "Chan%d doesn't support triple buffer " "mode\n", dma_chan); return -EINVAL; } if (!phyaddr_1 && phyaddr_2) { dev_err(ipu->dev, "Chan%d's buf1 physical addr is NULL for " "triple buffer mode\n", dma_chan); return -EINVAL; } mutex_lock(&ipu->mutex_lock); /* Build parameter memory data for DMA channel */ _ipu_ch_param_init(ipu, dma_chan, pixel_fmt, width, height, stride, u, v, 0, phyaddr_0, phyaddr_1, phyaddr_2); /* Set correlative channel parameter of local alpha channel */ if ((_ipu_is_ic_graphic_chan(dma_chan) || _ipu_is_dp_graphic_chan(dma_chan)) && (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] == true)) { _ipu_ch_param_set_alpha_use_separate_channel(ipu, dma_chan, true); _ipu_ch_param_set_alpha_buffer_memory(ipu, dma_chan); _ipu_ch_param_set_alpha_condition_read(ipu, dma_chan); /* fix alpha width as 8 and burst size as 16*/ _ipu_ch_params_set_alpha_width(ipu, dma_chan, 8); _ipu_ch_param_set_burst_size(ipu, dma_chan, 16); } else if (_ipu_is_ic_graphic_chan(dma_chan) && ipu_pixel_format_has_alpha(pixel_fmt)) _ipu_ch_param_set_alpha_use_separate_channel(ipu, dma_chan, false); if (rot_mode) _ipu_ch_param_set_rotation(ipu, dma_chan, rot_mode); /* IC and ROT channels have restriction of 8 or 16 pix burst length */ if (_ipu_is_ic_chan(dma_chan) || _ipu_is_vdi_out_chan(dma_chan)) { if ((width % 16) == 0) _ipu_ch_param_set_burst_size(ipu, dma_chan, 16); else _ipu_ch_param_set_burst_size(ipu, dma_chan, 8); } else if (_ipu_is_irt_chan(dma_chan)) { _ipu_ch_param_set_burst_size(ipu, dma_chan, 8); _ipu_ch_param_set_block_mode(ipu, dma_chan); } else if (_ipu_is_dmfc_chan(dma_chan)) { burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan); _ipu_dmfc_set_wait4eot(ipu, dma_chan, width); _ipu_dmfc_set_burst_size(ipu, dma_chan, burst_size); } if (_ipu_disp_chan_is_interlaced(ipu, channel) || ipu->chan_is_interlaced[dma_chan]) _ipu_ch_param_set_interlaced_scan(ipu, dma_chan); if (_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan) || _ipu_is_vdi_out_chan(dma_chan)) { burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan); _ipu_ic_idma_init(ipu, dma_chan, width, height, burst_size, rot_mode); } else if (_ipu_is_smfc_chan(dma_chan)) { burst_size = _ipu_ch_param_get_burst_size(ipu, dma_chan); /* * This is different from IPUv3 spec, but it is confirmed * in IPUforum that SMFC burst size should be NPB[6:3] * when IDMAC works in 16-bit generic data mode. */ if (pixel_fmt == IPU_PIX_FMT_GENERIC) /* 8 bits per pixel */ burst_size = burst_size >> 4; else if (pixel_fmt == IPU_PIX_FMT_GENERIC_16) /* 16 bits per pixel */ burst_size = burst_size >> 3; else burst_size = burst_size >> 2; _ipu_smfc_set_burst_size(ipu, channel, burst_size-1); } switch (dma_chan) { case 0: case 1: case 2: case 3: _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch0123_axi); break; case 23: _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch23_axi); break; case 27: _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch27_axi); break; case 28: _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->ch28_axi); break; default: _ipu_ch_param_set_axi_id(ipu, dma_chan, ipu->normal_axi); break; } if (idma_is_set(ipu, IDMAC_CHA_PRI(dma_chan), dma_chan) && ipu->devtype == IPUv3H) { uint32_t reg = IDMAC_CH_LOCK_EN_1(ipu->devtype); uint32_t value = 0; switch (dma_chan) { case 5: value = 0x3; break; case 11: value = 0x3 << 2; break; case 12: value = 0x3 << 4; break; case 14: value = 0x3 << 6; break; case 15: value = 0x3 << 8; break; case 20: value = 0x3 << 10; break; case 21: value = 0x3 << 12; break; case 22: value = 0x3 << 14; break; case 23: value = 0x3 << 16; break; case 27: value = 0x3 << 18; break; case 28: value = 0x3 << 20; break; case 45: reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); value = 0x3 << 0; break; case 46: reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); value = 0x3 << 2; break; case 47: reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); value = 0x3 << 4; break; case 48: reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); value = 0x3 << 6; break; case 49: reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); value = 0x3 << 8; break; case 50: reg = IDMAC_CH_LOCK_EN_2(ipu->devtype); value = 0x3 << 10; break; default: break; } value |= ipu_idmac_read(ipu, reg); ipu_idmac_write(ipu, value, reg); } _ipu_ch_param_dump(ipu, dma_chan); if (phyaddr_2 && ipu->devtype >= IPUv3EX) { reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(dma_chan)); reg &= ~idma_mask(dma_chan); ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(dma_chan)); reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan)); reg |= idma_mask(dma_chan); ipu_cm_write(ipu, reg, IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan)); /* Set IDMAC third buffer's cpmem number */ /* See __ipu_ch_get_third_buf_cpmem_num() for mapping */ ipu_idmac_write(ipu, 0x00444047L, IDMAC_SUB_ADDR_4(ipu->devtype)); ipu_idmac_write(ipu, 0x46004241L, IDMAC_SUB_ADDR_3(ipu->devtype)); ipu_idmac_write(ipu, 0x00000045L, IDMAC_SUB_ADDR_1(ipu->devtype)); /* Reset to buffer 0 */ ipu_cm_write(ipu, tri_cur_buf_mask(dma_chan), IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, dma_chan)); } else { reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan)); reg &= ~idma_mask(dma_chan); ipu_cm_write(ipu, reg, IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan)); reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(dma_chan)); if (phyaddr_1) reg |= idma_mask(dma_chan); else reg &= ~idma_mask(dma_chan); ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(dma_chan)); /* Reset to buffer 0 */ ipu_cm_write(ipu, idma_mask(dma_chan), IPU_CHA_CUR_BUF(ipu->devtype, dma_chan)); } mutex_unlock(&ipu->mutex_lock); return 0; } EXPORT_SYMBOL(ipu_init_channel_buffer); /*! * This function is called to update the physical address of a buffer for * a logical IPU channel. * * @param ipu ipu handler * @param channel Input parameter for the logical channel ID. * * @param type Input parameter which buffer to initialize. * * @param bufNum Input parameter for buffer number to update. * 0 or 1 are the only valid values. * * @param phyaddr Input parameter buffer physical address. * * @return This function returns 0 on success or negative error code on * fail. This function will fail if the buffer is set to ready. */ int32_t ipu_update_channel_buffer(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, uint32_t bufNum, dma_addr_t phyaddr) { uint32_t reg; int ret = 0; uint32_t dma_chan = channel_2_dma(channel, type); unsigned long lock_flags; if (dma_chan == IDMA_CHAN_INVALID) return -EINVAL; spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags); if (bufNum == 0) reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(ipu->devtype, dma_chan)); else if (bufNum == 1) reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(ipu->devtype, dma_chan)); else reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(ipu->devtype, dma_chan)); if ((reg & idma_mask(dma_chan)) == 0) _ipu_ch_param_set_buffer(ipu, dma_chan, bufNum, phyaddr); else ret = -EACCES; spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags); return ret; } EXPORT_SYMBOL(ipu_update_channel_buffer); /*! * This function is called to update the band mode setting for * a logical IPU channel. * * @param ipu ipu handler * * @param channel Input parameter for the logical channel ID. * * @param type Input parameter which buffer to initialize. * * @param band_height Input parameter for band lines: * shoule be log2(4/8/16/32/64/128/256). * * @return This function returns 0 on success or negative error code on * fail. */ int32_t ipu_set_channel_bandmode(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, uint32_t band_height) { uint32_t reg; int ret = 0; uint32_t dma_chan = channel_2_dma(channel, type); if ((2 > band_height) || (8 < band_height)) return -EINVAL; mutex_lock(&ipu->mutex_lock); reg = ipu_idmac_read(ipu, IDMAC_BAND_EN(ipu->devtype, dma_chan)); reg |= 1 << (dma_chan % 32); ipu_idmac_write(ipu, reg, IDMAC_BAND_EN(ipu->devtype, dma_chan)); _ipu_ch_param_set_bandmode(ipu, dma_chan, band_height); dev_dbg(ipu->dev, "dma_chan:%d, band_height:%d.\n\n", dma_chan, 1 << band_height); mutex_unlock(&ipu->mutex_lock); return ret; } EXPORT_SYMBOL(ipu_set_channel_bandmode); /*! * This function is called to initialize a buffer for logical IPU channel. * * @param ipu ipu handler * @param channel Input parameter for the logical channel ID. * * @param type Input parameter which buffer to initialize. * * @param pixel_fmt Input parameter for pixel format of buffer. * Pixel format is a FOURCC ASCII code. * * @param width Input parameter for width of buffer in pixels. * * @param height Input parameter for height of buffer in pixels. * * @param stride Input parameter for stride length of buffer * in pixels. * * @param u predefined private u offset for additional cropping, * zero if not used. * * @param v predefined private v offset for additional cropping, * zero if not used. * * @param vertical_offset vertical offset for Y coordinate * in the existed frame * * * @param horizontal_offset horizontal offset for X coordinate * in the existed frame * * * @return Returns 0 on success or negative error code on fail * This function will fail if any buffer is set to ready. */ int32_t ipu_update_channel_offset(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, uint32_t pixel_fmt, uint16_t width, uint16_t height, uint32_t stride, uint32_t u, uint32_t v, uint32_t vertical_offset, uint32_t horizontal_offset) { int ret = 0; uint32_t dma_chan = channel_2_dma(channel, type); unsigned long lock_flags; if (dma_chan == IDMA_CHAN_INVALID) return -EINVAL; spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags); if ((ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(ipu->devtype, dma_chan)) & idma_mask(dma_chan)) || (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(ipu->devtype, dma_chan)) & idma_mask(dma_chan)) || ((ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(ipu->devtype, dma_chan)) & idma_mask(dma_chan)) && (ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan)) & idma_mask(dma_chan)) && _ipu_is_trb_chan(ipu, dma_chan))) ret = -EACCES; else _ipu_ch_offset_update(ipu, dma_chan, pixel_fmt, width, height, stride, u, v, 0, vertical_offset, horizontal_offset); spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags); return ret; } EXPORT_SYMBOL(ipu_update_channel_offset); int32_t ipu_get_channel_offset(uint32_t pixel_fmt, uint16_t width, uint16_t height, uint32_t stride, uint32_t u, uint32_t v, uint32_t vertical_offset, uint32_t horizontal_offset, uint32_t *u_offset, uint32_t *v_offset) { return __ipu_ch_offset_calc(pixel_fmt, width, height, stride, u, v, 0, vertical_offset, horizontal_offset, u_offset, v_offset); } EXPORT_SYMBOL(ipu_get_channel_offset); /*! * This function is called to set a channel's buffer as ready. * * @param ipu ipu handler * @param channel Input parameter for the logical channel ID. * * @param type Input parameter which buffer to initialize. * * @param bufNum Input parameter for which buffer number set to * ready state. * * @return Returns 0 on success or negative error code on fail */ int32_t ipu_select_buffer(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, uint32_t bufNum) { uint32_t dma_chan = channel_2_dma(channel, type); unsigned long lock_flags; if (dma_chan == IDMA_CHAN_INVALID) return -EINVAL; spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags); /* Mark buffer to be ready. */ if (bufNum == 0) ipu_cm_write(ipu, idma_mask(dma_chan), IPU_CHA_BUF0_RDY(ipu->devtype, dma_chan)); else if (bufNum == 1) ipu_cm_write(ipu, idma_mask(dma_chan), IPU_CHA_BUF1_RDY(ipu->devtype, dma_chan)); else ipu_cm_write(ipu, idma_mask(dma_chan), IPU_CHA_BUF2_RDY(ipu->devtype, dma_chan)); spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags); return 0; } EXPORT_SYMBOL(ipu_select_buffer); /*! * This function is called to set a channel's buffer as ready. * * @param ipu ipu handler * @param bufNum Input parameter for which buffer number set to * ready state. * * @return Returns 0 on success or negative error code on fail */ int32_t ipu_select_multi_vdi_buffer(struct ipu_soc *ipu, uint32_t bufNum) { uint32_t dma_chan = channel_2_dma(MEM_VDI_PRP_VF_MEM, IPU_INPUT_BUFFER); uint32_t mask_bit = idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_P, IPU_INPUT_BUFFER))| idma_mask(dma_chan)| idma_mask(channel_2_dma(MEM_VDI_PRP_VF_MEM_N, IPU_INPUT_BUFFER)); unsigned long lock_flags; spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags); /* Mark buffers to be ready. */ if (bufNum == 0) ipu_cm_write(ipu, mask_bit, IPU_CHA_BUF0_RDY(ipu->devtype, dma_chan)); else ipu_cm_write(ipu, mask_bit, IPU_CHA_BUF1_RDY(ipu->devtype, dma_chan)); spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags); return 0; } EXPORT_SYMBOL(ipu_select_multi_vdi_buffer); #define NA -1 static int proc_dest_sel[] = { 0, 1, 1, 3, 5, 5, 4, 7, 8, 9, 10, 11, 12, 14, 15, 16, 0, 1, 1, 5, 5, 5, 5, 5, 7, 8, 9, 10, 11, 12, 14, 31 }; static int proc_src_sel[] = { 0, 6, 7, 6, 7, 8, 5, NA, NA, NA, NA, NA, NA, NA, NA, 1, 2, 3, 4, 7, 8, NA, 8, NA }; static int disp_src_sel[] = { 0, 6, 7, 8, 3, 4, 5, NA, NA, NA, NA, NA, NA, NA, NA, 1, NA, 2, NA, 3, 4, 4, 4, 4 }; /*! * This function links 2 channels together for automatic frame * synchronization. The output of the source channel is linked to the input of * the destination channel. * * @param ipu ipu handler * @param src_ch Input parameter for the logical channel ID of * the source channel. * * @param dest_ch Input parameter for the logical channel ID of * the destination channel. * * @return This function returns 0 on success or negative error code on * fail. */ int32_t ipu_link_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch) { int retval = 0; uint32_t fs_proc_flow1; uint32_t fs_proc_flow2; uint32_t fs_proc_flow3; uint32_t fs_disp_flow1; mutex_lock(&ipu->mutex_lock); fs_proc_flow1 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); fs_proc_flow2 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW2); fs_proc_flow3 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW3); fs_disp_flow1 = ipu_cm_read(ipu, IPU_FS_DISP_FLOW1); switch (src_ch) { case CSI_MEM0: fs_proc_flow3 &= ~FS_SMFC0_DEST_SEL_MASK; fs_proc_flow3 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_SMFC0_DEST_SEL_OFFSET; break; case CSI_MEM1: fs_proc_flow3 &= ~FS_SMFC1_DEST_SEL_MASK; fs_proc_flow3 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_SMFC1_DEST_SEL_OFFSET; break; case CSI_MEM2: fs_proc_flow3 &= ~FS_SMFC2_DEST_SEL_MASK; fs_proc_flow3 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_SMFC2_DEST_SEL_OFFSET; break; case CSI_MEM3: fs_proc_flow3 &= ~FS_SMFC3_DEST_SEL_MASK; fs_proc_flow3 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_SMFC3_DEST_SEL_OFFSET; break; case CSI_PRP_ENC_MEM: fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK; fs_proc_flow2 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_PRPENC_DEST_SEL_OFFSET; break; case CSI_PRP_VF_MEM: fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; fs_proc_flow2 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_PRPVF_DEST_SEL_OFFSET; break; case MEM_PP_MEM: fs_proc_flow2 &= ~FS_PP_DEST_SEL_MASK; fs_proc_flow2 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_PP_DEST_SEL_OFFSET; break; case MEM_ROT_PP_MEM: fs_proc_flow2 &= ~FS_PP_ROT_DEST_SEL_MASK; fs_proc_flow2 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_PP_ROT_DEST_SEL_OFFSET; break; case MEM_PRP_ENC_MEM: fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK; fs_proc_flow2 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_PRPENC_DEST_SEL_OFFSET; break; case MEM_ROT_ENC_MEM: fs_proc_flow2 &= ~FS_PRPENC_ROT_DEST_SEL_MASK; fs_proc_flow2 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_PRPENC_ROT_DEST_SEL_OFFSET; break; case MEM_PRP_VF_MEM: fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; fs_proc_flow2 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_PRPVF_DEST_SEL_OFFSET; break; case MEM_VDI_PRP_VF_MEM: fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; fs_proc_flow2 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_PRPVF_DEST_SEL_OFFSET; break; case MEM_ROT_VF_MEM: fs_proc_flow2 &= ~FS_PRPVF_ROT_DEST_SEL_MASK; fs_proc_flow2 |= proc_dest_sel[IPU_CHAN_ID(dest_ch)] << FS_PRPVF_ROT_DEST_SEL_OFFSET; break; case MEM_VDOA_MEM: fs_proc_flow3 &= ~FS_VDOA_DEST_SEL_MASK; if (MEM_VDI_MEM == dest_ch) fs_proc_flow3 |= FS_VDOA_DEST_SEL_VDI; else if (MEM_PP_MEM == dest_ch) fs_proc_flow3 |= FS_VDOA_DEST_SEL_IC; else { retval = -EINVAL; goto err; } break; default: retval = -EINVAL; goto err; } switch (dest_ch) { case MEM_PP_MEM: fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK; if (MEM_VDOA_MEM == src_ch) fs_proc_flow1 |= FS_PP_SRC_SEL_VDOA; else fs_proc_flow1 |= proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PP_SRC_SEL_OFFSET; break; case MEM_ROT_PP_MEM: fs_proc_flow1 &= ~FS_PP_ROT_SRC_SEL_MASK; fs_proc_flow1 |= proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PP_ROT_SRC_SEL_OFFSET; break; case MEM_PRP_ENC_MEM: fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; fs_proc_flow1 |= proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET; break; case MEM_ROT_ENC_MEM: fs_proc_flow1 &= ~FS_PRPENC_ROT_SRC_SEL_MASK; fs_proc_flow1 |= proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRPENC_ROT_SRC_SEL_OFFSET; break; case MEM_PRP_VF_MEM: fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; fs_proc_flow1 |= proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET; break; case MEM_VDI_PRP_VF_MEM: fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; fs_proc_flow1 |= proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRP_SRC_SEL_OFFSET; break; case MEM_ROT_VF_MEM: fs_proc_flow1 &= ~FS_PRPVF_ROT_SRC_SEL_MASK; fs_proc_flow1 |= proc_src_sel[IPU_CHAN_ID(src_ch)] << FS_PRPVF_ROT_SRC_SEL_OFFSET; break; case MEM_DC_SYNC: fs_disp_flow1 &= ~FS_DC1_SRC_SEL_MASK; fs_disp_flow1 |= disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DC1_SRC_SEL_OFFSET; break; case MEM_BG_SYNC: fs_disp_flow1 &= ~FS_DP_SYNC0_SRC_SEL_MASK; fs_disp_flow1 |= disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DP_SYNC0_SRC_SEL_OFFSET; break; case MEM_FG_SYNC: fs_disp_flow1 &= ~FS_DP_SYNC1_SRC_SEL_MASK; fs_disp_flow1 |= disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DP_SYNC1_SRC_SEL_OFFSET; break; case MEM_DC_ASYNC: fs_disp_flow1 &= ~FS_DC2_SRC_SEL_MASK; fs_disp_flow1 |= disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DC2_SRC_SEL_OFFSET; break; case MEM_BG_ASYNC0: fs_disp_flow1 &= ~FS_DP_ASYNC0_SRC_SEL_MASK; fs_disp_flow1 |= disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DP_ASYNC0_SRC_SEL_OFFSET; break; case MEM_FG_ASYNC0: fs_disp_flow1 &= ~FS_DP_ASYNC1_SRC_SEL_MASK; fs_disp_flow1 |= disp_src_sel[IPU_CHAN_ID(src_ch)] << FS_DP_ASYNC1_SRC_SEL_OFFSET; break; case MEM_VDI_MEM: fs_proc_flow1 &= ~FS_VDI_SRC_SEL_MASK; if (MEM_VDOA_MEM == src_ch) fs_proc_flow1 |= FS_VDI_SRC_SEL_VDOA; else { retval = -EINVAL; goto err; } break; default: retval = -EINVAL; goto err; } ipu_cm_write(ipu, fs_proc_flow1, IPU_FS_PROC_FLOW1); ipu_cm_write(ipu, fs_proc_flow2, IPU_FS_PROC_FLOW2); ipu_cm_write(ipu, fs_proc_flow3, IPU_FS_PROC_FLOW3); ipu_cm_write(ipu, fs_disp_flow1, IPU_FS_DISP_FLOW1); err: mutex_unlock(&ipu->mutex_lock); return retval; } EXPORT_SYMBOL(ipu_link_channels); /*! * This function unlinks 2 channels and disables automatic frame * synchronization. * * @param ipu ipu handler * @param src_ch Input parameter for the logical channel ID of * the source channel. * * @param dest_ch Input parameter for the logical channel ID of * the destination channel. * * @return This function returns 0 on success or negative error code on * fail. */ int32_t ipu_unlink_channels(struct ipu_soc *ipu, ipu_channel_t src_ch, ipu_channel_t dest_ch) { int retval = 0; uint32_t fs_proc_flow1; uint32_t fs_proc_flow2; uint32_t fs_proc_flow3; uint32_t fs_disp_flow1; mutex_lock(&ipu->mutex_lock); fs_proc_flow1 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); fs_proc_flow2 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW2); fs_proc_flow3 = ipu_cm_read(ipu, IPU_FS_PROC_FLOW3); fs_disp_flow1 = ipu_cm_read(ipu, IPU_FS_DISP_FLOW1); switch (src_ch) { case CSI_MEM0: fs_proc_flow3 &= ~FS_SMFC0_DEST_SEL_MASK; break; case CSI_MEM1: fs_proc_flow3 &= ~FS_SMFC1_DEST_SEL_MASK; break; case CSI_MEM2: fs_proc_flow3 &= ~FS_SMFC2_DEST_SEL_MASK; break; case CSI_MEM3: fs_proc_flow3 &= ~FS_SMFC3_DEST_SEL_MASK; break; case CSI_PRP_ENC_MEM: fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK; break; case CSI_PRP_VF_MEM: fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; break; case MEM_PP_MEM: fs_proc_flow2 &= ~FS_PP_DEST_SEL_MASK; break; case MEM_ROT_PP_MEM: fs_proc_flow2 &= ~FS_PP_ROT_DEST_SEL_MASK; break; case MEM_PRP_ENC_MEM: fs_proc_flow2 &= ~FS_PRPENC_DEST_SEL_MASK; break; case MEM_ROT_ENC_MEM: fs_proc_flow2 &= ~FS_PRPENC_ROT_DEST_SEL_MASK; break; case MEM_PRP_VF_MEM: fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; break; case MEM_VDI_PRP_VF_MEM: fs_proc_flow2 &= ~FS_PRPVF_DEST_SEL_MASK; break; case MEM_ROT_VF_MEM: fs_proc_flow2 &= ~FS_PRPVF_ROT_DEST_SEL_MASK; break; case MEM_VDOA_MEM: fs_proc_flow3 &= ~FS_VDOA_DEST_SEL_MASK; break; default: retval = -EINVAL; goto err; } switch (dest_ch) { case MEM_PP_MEM: fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK; break; case MEM_ROT_PP_MEM: fs_proc_flow1 &= ~FS_PP_ROT_SRC_SEL_MASK; break; case MEM_PRP_ENC_MEM: fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; break; case MEM_ROT_ENC_MEM: fs_proc_flow1 &= ~FS_PRPENC_ROT_SRC_SEL_MASK; break; case MEM_PRP_VF_MEM: fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; break; case MEM_VDI_PRP_VF_MEM: fs_proc_flow1 &= ~FS_PRP_SRC_SEL_MASK; break; case MEM_ROT_VF_MEM: fs_proc_flow1 &= ~FS_PRPVF_ROT_SRC_SEL_MASK; break; case MEM_DC_SYNC: fs_disp_flow1 &= ~FS_DC1_SRC_SEL_MASK; break; case MEM_BG_SYNC: fs_disp_flow1 &= ~FS_DP_SYNC0_SRC_SEL_MASK; break; case MEM_FG_SYNC: fs_disp_flow1 &= ~FS_DP_SYNC1_SRC_SEL_MASK; break; case MEM_DC_ASYNC: fs_disp_flow1 &= ~FS_DC2_SRC_SEL_MASK; break; case MEM_BG_ASYNC0: fs_disp_flow1 &= ~FS_DP_ASYNC0_SRC_SEL_MASK; break; case MEM_FG_ASYNC0: fs_disp_flow1 &= ~FS_DP_ASYNC1_SRC_SEL_MASK; break; case MEM_VDI_MEM: fs_proc_flow1 &= ~FS_VDI_SRC_SEL_MASK; break; default: retval = -EINVAL; goto err; } ipu_cm_write(ipu, fs_proc_flow1, IPU_FS_PROC_FLOW1); ipu_cm_write(ipu, fs_proc_flow2, IPU_FS_PROC_FLOW2); ipu_cm_write(ipu, fs_proc_flow3, IPU_FS_PROC_FLOW3); ipu_cm_write(ipu, fs_disp_flow1, IPU_FS_DISP_FLOW1); err: mutex_unlock(&ipu->mutex_lock); return retval; } EXPORT_SYMBOL(ipu_unlink_channels); /*! * This function check whether a logical channel was enabled. * * @param ipu ipu handler * @param channel Input parameter for the logical channel ID. * * @return This function returns 1 while request channel is enabled or * 0 for not enabled. */ int32_t ipu_is_channel_busy(struct ipu_soc *ipu, ipu_channel_t channel) { uint32_t reg; uint32_t in_dma; uint32_t out_dma; out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma)); if (reg & idma_mask(in_dma)) return 1; reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma)); if (reg & idma_mask(out_dma)) return 1; return 0; } EXPORT_SYMBOL(ipu_is_channel_busy); /*! * This function enables a logical channel. * * @param ipu ipu handler * @param channel Input parameter for the logical channel ID. * * @return This function returns 0 on success or negative error code on * fail. */ int32_t ipu_enable_channel(struct ipu_soc *ipu, ipu_channel_t channel) { uint32_t reg; uint32_t ipu_conf; uint32_t in_dma; uint32_t out_dma; uint32_t sec_dma; uint32_t thrd_dma; uint32_t di = 0; mutex_lock(&ipu->mutex_lock); if (ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) { dev_err(ipu->dev, "Warning: channel already enabled %d\n", IPU_CHAN_ID(channel)); mutex_unlock(&ipu->mutex_lock); return -EACCES; } /* Get input and output dma channels */ out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); ipu_conf = ipu_cm_read(ipu, IPU_CONF); switch (channel) { case MEM_BG_SYNC: di = ipu->dc_di_assignment[5]; if (ipu->di_use_count[di] > 0) ipu_conf |= di ? IPU_CONF_DI1_EN : IPU_CONF_DI0_EN; if (ipu->dp_use_count > 0) ipu_conf |= IPU_CONF_DP_EN; if (ipu->dc_use_count > 0) ipu_conf |= IPU_CONF_DC_EN; if (ipu->dmfc_use_count > 0) ipu_conf |= IPU_CONF_DMFC_EN; break; case MEM_DC_SYNC: di = ipu->dc_di_assignment[1]; if (ipu->di_use_count[di] > 0) ipu_conf |= di ? IPU_CONF_DI1_EN : IPU_CONF_DI0_EN; if (ipu->dc_use_count > 0) ipu_conf |= IPU_CONF_DC_EN; if (ipu->dmfc_use_count > 0) ipu_conf |= IPU_CONF_DMFC_EN; break; case MEM_FG_SYNC: if (ipu->dp_use_count > 0) ipu_conf |= IPU_CONF_DP_EN; if (ipu->dc_use_count > 0) ipu_conf |= IPU_CONF_DC_EN; if (ipu->dmfc_use_count > 0) ipu_conf |= IPU_CONF_DMFC_EN; break; case DIRECT_ASYNC0: di = ipu->dc_di_assignment[8]; /* fall through */ case DIRECT_ASYNC1: di = ipu->dc_di_assignment[9]; if (ipu->di_use_count[di] > 0) ipu_conf |= di ? IPU_CONF_DI1_EN : IPU_CONF_DI0_EN; if (ipu->dc_use_count > 0) ipu_conf |= IPU_CONF_DC_EN; break; case MEM_ROT_PP_MEM: case MEM_ROT_ENC_MEM: case MEM_ROT_VF_MEM: if (ipu->rot_use_count > 0) ipu_conf |= IPU_CONF_ROT_EN; /* fall through */ case MEM_PP_MEM: case MEM_PRP_ENC_MEM: case MEM_PRP_VF_MEM: case CSI_PRP_ENC_MEM: case CSI_PRP_VF_MEM: if (ipu->ic_use_count > 0) ipu_conf |= IPU_CONF_IC_EN; break; case CSI_MEM0: case CSI_MEM1: case CSI_MEM2: case CSI_MEM3: if (ipu->smfc_use_count > 0) ipu_conf |= IPU_CONF_SMFC_EN; break; case MEM_VDI_PRP_VF_MEM: case MEM_VDI_MEM: if (ipu->vdi_use_count > 0) { ipu_conf |= IPU_CONF_ISP_EN; ipu_conf |= IPU_CONF_VDI_EN; ipu_conf |= IPU_CONF_IC_INPUT; } if (ipu->ic_use_count > 0) ipu_conf |= IPU_CONF_IC_EN; break; default: break; } ipu_cm_write(ipu, ipu_conf, IPU_CONF); if (idma_is_valid(in_dma)) { reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma)); ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); } if (idma_is_valid(out_dma)) { reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma)); ipu_idmac_write(ipu, reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); } if ((ipu->sec_chan_en[IPU_CHAN_ID(channel)]) && ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM) || (channel == MEM_VDI_PRP_VF_MEM))) { sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(sec_dma)); ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma)); } if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) && ((channel == MEM_PP_MEM) || (channel == MEM_PRP_VF_MEM))) { thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER); reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma)); ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma)); sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); ipu_idmac_write(ipu, reg | idma_mask(sec_dma), IDMAC_SEP_ALPHA); } else if ((ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) && ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC))) { thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER); reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma)); ipu_idmac_write(ipu, reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma)); reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_SEP_ALPHA); } if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC)) { reg = ipu_idmac_read(ipu, IDMAC_WM_EN(in_dma)); ipu_idmac_write(ipu, reg | idma_mask(in_dma), IDMAC_WM_EN(in_dma)); _ipu_dp_dc_enable(ipu, channel); } if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) || _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma) || _ipu_is_vdi_out_chan(out_dma)) _ipu_ic_enable_task(ipu, channel); ipu->channel_enable_mask |= 1L << IPU_CHAN_ID(channel); if (ipu->prg_clk) clk_prepare_enable(ipu->prg_clk); mutex_unlock(&ipu->mutex_lock); return 0; } EXPORT_SYMBOL(ipu_enable_channel); /*! * This function check buffer ready for a logical channel. * * @param ipu ipu handler * @param channel Input parameter for the logical channel ID. * * @param type Input parameter which buffer to clear. * * @param bufNum Input parameter for which buffer number clear * ready state. * */ int32_t ipu_check_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, uint32_t bufNum) { uint32_t dma_chan = channel_2_dma(channel, type); uint32_t reg; unsigned long lock_flags; if (dma_chan == IDMA_CHAN_INVALID) return -EINVAL; spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags); if (bufNum == 0) reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(ipu->devtype, dma_chan)); else if (bufNum == 1) reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(ipu->devtype, dma_chan)); else reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(ipu->devtype, dma_chan)); spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags); if (reg & idma_mask(dma_chan)) return 1; else return 0; } EXPORT_SYMBOL(ipu_check_buffer_ready); /*! * This function clear buffer ready for a logical channel. * * @param ipu ipu handler * @param channel Input parameter for the logical channel ID. * * @param type Input parameter which buffer to clear. * * @param bufNum Input parameter for which buffer number clear * ready state. * */ void _ipu_clear_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, uint32_t bufNum) { uint32_t dma_ch = channel_2_dma(channel, type); if (!idma_is_valid(dma_ch)) return; ipu_cm_write(ipu, 0xF0300000, IPU_GPR); /* write one to clear */ if (bufNum == 0) ipu_cm_write(ipu, idma_mask(dma_ch), IPU_CHA_BUF0_RDY(ipu->devtype, dma_ch)); else if (bufNum == 1) ipu_cm_write(ipu, idma_mask(dma_ch), IPU_CHA_BUF1_RDY(ipu->devtype, dma_ch)); else ipu_cm_write(ipu, idma_mask(dma_ch), IPU_CHA_BUF2_RDY(ipu->devtype, dma_ch)); ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */ } void ipu_clear_buffer_ready(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type, uint32_t bufNum) { unsigned long lock_flags; spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags); _ipu_clear_buffer_ready(ipu, channel, type, bufNum); spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags); } EXPORT_SYMBOL(ipu_clear_buffer_ready); /*! * This function disables a logical channel. * * @param ipu ipu handler * @param channel Input parameter for the logical channel ID. * * @param wait_for_stop Flag to set whether to wait for channel end * of frame or return immediately. * * @return This function returns 0 on success or negative error code on * fail. */ int32_t ipu_disable_channel(struct ipu_soc *ipu, ipu_channel_t channel, bool wait_for_stop) { uint32_t reg; uint32_t in_dma; uint32_t out_dma; uint32_t sec_dma = NO_DMA; uint32_t thrd_dma = NO_DMA; uint16_t fg_pos_x, fg_pos_y; unsigned long lock_flags; mutex_lock(&ipu->mutex_lock); if ((ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) { dev_dbg(ipu->dev, "Channel already disabled %d\n", IPU_CHAN_ID(channel)); mutex_unlock(&ipu->mutex_lock); return -EACCES; } /* Get input and output dma channels */ out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER); in_dma = channel_2_dma(channel, IPU_VIDEO_IN_BUFFER); if ((idma_is_valid(in_dma) && !idma_is_set(ipu, IDMAC_CHA_EN(in_dma), in_dma)) && (idma_is_valid(out_dma) && !idma_is_set(ipu, IDMAC_CHA_EN(out_dma), out_dma))) { mutex_unlock(&ipu->mutex_lock); return -EINVAL; } if (ipu->sec_chan_en[IPU_CHAN_ID(channel)]) sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)]) { sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER); thrd_dma = channel_2_dma(channel, IPU_ALPHA_IN_BUFFER); } if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) || (channel == MEM_DC_SYNC)) { if (channel == MEM_FG_SYNC) { _ipu_disp_get_window_pos(ipu, channel, &fg_pos_x, &fg_pos_y); _ipu_disp_set_window_pos(ipu, channel, 0, 0); } _ipu_dp_dc_disable(ipu, channel, false); /* * wait for BG channel EOF then disable FG-IDMAC, * it avoid FG NFB4EOF error. */ if ((channel == MEM_FG_SYNC) && (ipu_is_channel_busy(ipu, MEM_BG_SYNC))) { int timeout = 50; ipu_cm_write(ipu, IPUIRQ_2_MASK(IPU_IRQ_BG_SYNC_EOF), IPUIRQ_2_STATREG(ipu->devtype, IPU_IRQ_BG_SYNC_EOF)); while ((ipu_cm_read(ipu, IPUIRQ_2_STATREG(ipu->devtype, IPU_IRQ_BG_SYNC_EOF)) & IPUIRQ_2_MASK(IPU_IRQ_BG_SYNC_EOF)) == 0) { msleep(10); timeout -= 10; if (timeout <= 0) { dev_err(ipu->dev, "warning: wait for bg sync eof timeout\n"); break; } } } } else if (wait_for_stop && !_ipu_is_smfc_chan(out_dma) && channel != CSI_PRP_VF_MEM && channel != CSI_PRP_ENC_MEM) { while (idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, in_dma), in_dma) || idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, out_dma), out_dma) || (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, sec_dma), sec_dma)) || (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, thrd_dma), thrd_dma))) { uint32_t irq = 0xffffffff; int timeout = 50000; if (idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, out_dma), out_dma)) irq = out_dma; if (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, sec_dma), sec_dma)) irq = sec_dma; if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, thrd_dma), thrd_dma)) irq = thrd_dma; if (idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, in_dma), in_dma)) irq = in_dma; if (irq == 0xffffffff) { dev_dbg(ipu->dev, "warning: no channel busy, break\n"); break; } ipu_cm_write(ipu, IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(ipu->devtype, irq)); dev_dbg(ipu->dev, "warning: channel %d busy, need wait\n", irq); while (((ipu_cm_read(ipu, IPUIRQ_2_STATREG(ipu->devtype, irq)) & IPUIRQ_2_MASK(irq)) == 0) && (idma_is_set(ipu, IDMAC_CHA_BUSY(ipu->devtype, irq), irq))) { udelay(10); timeout -= 10; if (timeout <= 0) { ipu_dump_registers(ipu); dev_err(ipu->dev, "warning: disable ipu dma channel %d during its busy state\n", irq); break; } } dev_dbg(ipu->dev, "wait_time:%d\n", 50000 - timeout); } } if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) || (channel == MEM_DC_SYNC)) { reg = ipu_idmac_read(ipu, IDMAC_WM_EN(in_dma)); ipu_idmac_write(ipu, reg & ~idma_mask(in_dma), IDMAC_WM_EN(in_dma)); } /* Disable IC task */ if (_ipu_is_ic_chan(in_dma) || _ipu_is_ic_chan(out_dma) || _ipu_is_irt_chan(in_dma) || _ipu_is_irt_chan(out_dma) || _ipu_is_vdi_out_chan(out_dma)) _ipu_ic_disable_task(ipu, channel); /* Disable DMA channel(s) */ if (idma_is_valid(in_dma)) { reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(in_dma)); ipu_idmac_write(ipu, reg & ~idma_mask(in_dma), IDMAC_CHA_EN(in_dma)); ipu_cm_write(ipu, idma_mask(in_dma), IPU_CHA_CUR_BUF(ipu->devtype, in_dma)); ipu_cm_write(ipu, tri_cur_buf_mask(in_dma), IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, in_dma)); } if (idma_is_valid(out_dma)) { reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(out_dma)); ipu_idmac_write(ipu, reg & ~idma_mask(out_dma), IDMAC_CHA_EN(out_dma)); ipu_cm_write(ipu, idma_mask(out_dma), IPU_CHA_CUR_BUF(ipu->devtype, out_dma)); ipu_cm_write(ipu, tri_cur_buf_mask(out_dma), IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, out_dma)); } if (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) { reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(sec_dma)); ipu_idmac_write(ipu, reg & ~idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma)); ipu_cm_write(ipu, idma_mask(sec_dma), IPU_CHA_CUR_BUF(ipu->devtype, sec_dma)); } if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) { reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(thrd_dma)); ipu_idmac_write(ipu, reg & ~idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma)); if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) { reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); ipu_idmac_write(ipu, reg & ~idma_mask(in_dma), IDMAC_SEP_ALPHA); } else { reg = ipu_idmac_read(ipu, IDMAC_SEP_ALPHA); ipu_idmac_write(ipu, reg & ~idma_mask(sec_dma), IDMAC_SEP_ALPHA); } ipu_cm_write(ipu, idma_mask(thrd_dma), IPU_CHA_CUR_BUF(ipu->devtype, thrd_dma)); } if (channel == MEM_FG_SYNC) _ipu_disp_set_window_pos(ipu, channel, fg_pos_x, fg_pos_y); spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags); /* Set channel buffers NOT to be ready */ if (idma_is_valid(in_dma)) { _ipu_clear_buffer_ready(ipu, channel, IPU_VIDEO_IN_BUFFER, 0); _ipu_clear_buffer_ready(ipu, channel, IPU_VIDEO_IN_BUFFER, 1); _ipu_clear_buffer_ready(ipu, channel, IPU_VIDEO_IN_BUFFER, 2); } if (idma_is_valid(out_dma)) { _ipu_clear_buffer_ready(ipu, channel, IPU_OUTPUT_BUFFER, 0); _ipu_clear_buffer_ready(ipu, channel, IPU_OUTPUT_BUFFER, 1); } if (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) { _ipu_clear_buffer_ready(ipu, channel, IPU_GRAPH_IN_BUFFER, 0); _ipu_clear_buffer_ready(ipu, channel, IPU_GRAPH_IN_BUFFER, 1); } if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) { _ipu_clear_buffer_ready(ipu, channel, IPU_ALPHA_IN_BUFFER, 0); _ipu_clear_buffer_ready(ipu, channel, IPU_ALPHA_IN_BUFFER, 1); } spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags); ipu->channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel)); if (ipu->prg_clk) clk_disable_unprepare(ipu->prg_clk); mutex_unlock(&ipu->mutex_lock); return 0; } EXPORT_SYMBOL(ipu_disable_channel); /*! * This function enables CSI. * * @param ipu ipu handler * @param csi csi num 0 or 1 * * @return This function returns 0 on success or negative error code on * fail. */ int32_t ipu_enable_csi(struct ipu_soc *ipu, uint32_t csi) { uint32_t reg; if (csi > 1) { dev_err(ipu->dev, "Wrong csi num_%d\n", csi); return -EINVAL; } _ipu_get(ipu); mutex_lock(&ipu->mutex_lock); ipu->csi_use_count[csi]++; if (ipu->csi_use_count[csi] == 1) { reg = ipu_cm_read(ipu, IPU_CONF); if (csi == 0) ipu_cm_write(ipu, reg | IPU_CONF_CSI0_EN, IPU_CONF); else ipu_cm_write(ipu, reg | IPU_CONF_CSI1_EN, IPU_CONF); } mutex_unlock(&ipu->mutex_lock); _ipu_put(ipu); return 0; } EXPORT_SYMBOL(ipu_enable_csi); /*! * This function disables CSI. * * @param ipu ipu handler * @param csi csi num 0 or 1 * * @return This function returns 0 on success or negative error code on * fail. */ int32_t ipu_disable_csi(struct ipu_soc *ipu, uint32_t csi) { uint32_t reg; if (csi > 1) { dev_err(ipu->dev, "Wrong csi num_%d\n", csi); return -EINVAL; } _ipu_get(ipu); mutex_lock(&ipu->mutex_lock); ipu->csi_use_count[csi]--; if (ipu->csi_use_count[csi] == 0) { _ipu_csi_wait4eof(ipu, ipu->csi_channel[csi]); reg = ipu_cm_read(ipu, IPU_CONF); if (csi == 0) ipu_cm_write(ipu, reg & ~IPU_CONF_CSI0_EN, IPU_CONF); else ipu_cm_write(ipu, reg & ~IPU_CONF_CSI1_EN, IPU_CONF); } mutex_unlock(&ipu->mutex_lock); _ipu_put(ipu); return 0; } EXPORT_SYMBOL(ipu_disable_csi); static irqreturn_t ipu_sync_irq_handler(int irq, void *desc) { struct ipu_soc *ipu = desc; int i; uint32_t line, bit, int_stat, int_ctrl; irqreturn_t result = IRQ_NONE; const int int_reg[] = { 1, 2, 3, 4, 11, 12, 13, 14, 15, 0 }; spin_lock(&ipu->int_reg_spin_lock); for (i = 0; int_reg[i] != 0; i++) { int_stat = ipu_cm_read(ipu, IPU_INT_STAT(ipu->devtype, int_reg[i])); int_ctrl = ipu_cm_read(ipu, IPU_INT_CTRL(int_reg[i])); int_stat &= int_ctrl; ipu_cm_write(ipu, int_stat, IPU_INT_STAT(ipu->devtype, int_reg[i])); while ((line = ffs(int_stat)) != 0) { bit = --line; int_stat &= ~(1UL << line); line += (int_reg[i] - 1) * 32; result |= ipu->irq_list[line].handler(line, ipu->irq_list[line]. dev_id); if (ipu->irq_list[line].flags & IPU_IRQF_ONESHOT) { int_ctrl &= ~(1UL << bit); ipu_cm_write(ipu, int_ctrl, IPU_INT_CTRL(int_reg[i])); } } } spin_unlock(&ipu->int_reg_spin_lock); return result; } static irqreturn_t ipu_err_irq_handler(int irq, void *desc) { struct ipu_soc *ipu = desc; int i; uint32_t int_stat; const int err_reg[] = { 5, 6, 9, 10, 0 }; spin_lock(&ipu->int_reg_spin_lock); for (i = 0; err_reg[i] != 0; i++) { int_stat = ipu_cm_read(ipu, IPU_INT_STAT(ipu->devtype, err_reg[i])); int_stat &= ipu_cm_read(ipu, IPU_INT_CTRL(err_reg[i])); if (int_stat) { ipu_cm_write(ipu, int_stat, IPU_INT_STAT(ipu->devtype, err_reg[i])); dev_warn(ipu->dev, "IPU Warning - IPU_INT_STAT_%d = 0x%08X\n", err_reg[i], int_stat); /* Disable interrupts so we only get error once */ int_stat = ipu_cm_read(ipu, IPU_INT_CTRL(err_reg[i])) & ~int_stat; ipu_cm_write(ipu, int_stat, IPU_INT_CTRL(err_reg[i])); } } spin_unlock(&ipu->int_reg_spin_lock); return IRQ_HANDLED; } /*! * This function enables the interrupt for the specified interrupt line. * The interrupt lines are defined in \b ipu_irq_line enum. * * @param ipu ipu handler * @param irq Interrupt line to enable interrupt for. * * @return This function returns 0 on success or negative error code on * fail. */ int ipu_enable_irq(struct ipu_soc *ipu, uint32_t irq) { uint32_t reg; unsigned long lock_flags; int ret = 0; _ipu_get(ipu); spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags); /* * Check sync interrupt handler only, since we do nothing for * error interrupts but than print out register values in the * error interrupt source handler. */ if (_ipu_is_sync_irq(irq) && (ipu->irq_list[irq].handler == NULL)) { dev_err(ipu->dev, "handler hasn't been registered on sync " "irq %d\n", irq); ret = -EACCES; goto out; } reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq)); reg |= IPUIRQ_2_MASK(irq); ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq)); out: spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags); _ipu_put(ipu); return ret; } EXPORT_SYMBOL(ipu_enable_irq); /*! * This function disables the interrupt for the specified interrupt line. * The interrupt lines are defined in \b ipu_irq_line enum. * * @param ipu ipu handler * @param irq Interrupt line to disable interrupt for. * */ void ipu_disable_irq(struct ipu_soc *ipu, uint32_t irq) { uint32_t reg; unsigned long lock_flags; _ipu_get(ipu); spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags); reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq)); reg &= ~IPUIRQ_2_MASK(irq); ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq)); spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags); _ipu_put(ipu); } EXPORT_SYMBOL(ipu_disable_irq); /*! * This function clears the interrupt for the specified interrupt line. * The interrupt lines are defined in \b ipu_irq_line enum. * * @param ipu ipu handler * @param irq Interrupt line to clear interrupt for. * */ void ipu_clear_irq(struct ipu_soc *ipu, uint32_t irq) { unsigned long lock_flags; _ipu_get(ipu); spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags); ipu_cm_write(ipu, IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(ipu->devtype, irq)); spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags); _ipu_put(ipu); } EXPORT_SYMBOL(ipu_clear_irq); /*! * This function returns the current interrupt status for the specified * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum. * * @param ipu ipu handler * @param irq Interrupt line to get status for. * * @return Returns true if the interrupt is pending/asserted or false if * the interrupt is not pending. */ bool ipu_get_irq_status(struct ipu_soc *ipu, uint32_t irq) { uint32_t reg; unsigned long lock_flags; _ipu_get(ipu); spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags); reg = ipu_cm_read(ipu, IPUIRQ_2_STATREG(ipu->devtype, irq)); spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags); _ipu_put(ipu); if (reg & IPUIRQ_2_MASK(irq)) return true; else return false; } EXPORT_SYMBOL(ipu_get_irq_status); /*! * This function registers an interrupt handler function for the specified * interrupt line. The interrupt lines are defined in \b ipu_irq_line enum. * * @param ipu ipu handler * @param irq Interrupt line to get status for. * * @param handler Input parameter for address of the handler * function. * * @param irq_flags Flags for interrupt mode. Currently not used. * * @param devname Input parameter for string name of driver * registering the handler. * * @param dev_id Input parameter for pointer of data to be * passed to the handler. * * @return This function returns 0 on success or negative error code on * fail. */ int ipu_request_irq(struct ipu_soc *ipu, uint32_t irq, irqreturn_t(*handler) (int, void *), uint32_t irq_flags, const char *devname, void *dev_id) { uint32_t reg; unsigned long lock_flags; int ret = 0; BUG_ON(irq >= IPU_IRQ_COUNT); _ipu_get(ipu); spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags); if (ipu->irq_list[irq].handler != NULL) { dev_err(ipu->dev, "handler already installed on irq %d\n", irq); ret = -EINVAL; goto out; } /* * Check sync interrupt handler only, since we do nothing for * error interrupts but than print out register values in the * error interrupt source handler. */ if (_ipu_is_sync_irq(irq) && (handler == NULL)) { dev_err(ipu->dev, "handler is NULL for sync irq %d\n", irq); ret = -EINVAL; goto out; } ipu->irq_list[irq].handler = handler; ipu->irq_list[irq].flags = irq_flags; ipu->irq_list[irq].dev_id = dev_id; ipu->irq_list[irq].name = devname; /* clear irq stat for previous use */ ipu_cm_write(ipu, IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(ipu->devtype, irq)); /* enable the interrupt */ reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq)); reg |= IPUIRQ_2_MASK(irq); ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq)); out: spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags); _ipu_put(ipu); return ret; } EXPORT_SYMBOL(ipu_request_irq); /*! * This function unregisters an interrupt handler for the specified interrupt * line. The interrupt lines are defined in \b ipu_irq_line enum. * * @param ipu ipu handler * @param irq Interrupt line to get status for. * * @param dev_id Input parameter for pointer of data to be passed * to the handler. This must match value passed to * ipu_request_irq(). * */ void ipu_free_irq(struct ipu_soc *ipu, uint32_t irq, void *dev_id) { uint32_t reg; unsigned long lock_flags; _ipu_get(ipu); spin_lock_irqsave(&ipu->int_reg_spin_lock, lock_flags); /* disable the interrupt */ reg = ipu_cm_read(ipu, IPUIRQ_2_CTRLREG(irq)); reg &= ~IPUIRQ_2_MASK(irq); ipu_cm_write(ipu, reg, IPUIRQ_2_CTRLREG(irq)); if (ipu->irq_list[irq].dev_id == dev_id) memset(&ipu->irq_list[irq], 0, sizeof(ipu->irq_list[irq])); spin_unlock_irqrestore(&ipu->int_reg_spin_lock, lock_flags); _ipu_put(ipu); } EXPORT_SYMBOL(ipu_free_irq); uint32_t ipu_get_cur_buffer_idx(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type) { uint32_t reg, dma_chan; dma_chan = channel_2_dma(channel, type); if (!idma_is_valid(dma_chan)) return -EINVAL; reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(ipu->devtype, dma_chan)); if ((reg & idma_mask(dma_chan)) && _ipu_is_trb_chan(ipu, dma_chan)) { reg = ipu_cm_read(ipu, IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, dma_chan)); return (reg & tri_cur_buf_mask(dma_chan)) >> tri_cur_buf_shift(dma_chan); } else { reg = ipu_cm_read(ipu, IPU_CHA_CUR_BUF(ipu->devtype, dma_chan)); if (reg & idma_mask(dma_chan)) return 1; else return 0; } } EXPORT_SYMBOL(ipu_get_cur_buffer_idx); uint32_t _ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel) { uint32_t stat = 0; uint32_t task_stat_reg = ipu_cm_read(ipu, IPU_PROC_TASK_STAT(ipu->devtype)); switch (channel) { case MEM_PRP_VF_MEM: stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET; break; case MEM_VDI_PRP_VF_MEM: stat = (task_stat_reg & TSTAT_VF_MASK) >> TSTAT_VF_OFFSET; break; case MEM_ROT_VF_MEM: stat = (task_stat_reg & TSTAT_VF_ROT_MASK) >> TSTAT_VF_ROT_OFFSET; break; case MEM_PRP_ENC_MEM: stat = (task_stat_reg & TSTAT_ENC_MASK) >> TSTAT_ENC_OFFSET; break; case MEM_ROT_ENC_MEM: stat = (task_stat_reg & TSTAT_ENC_ROT_MASK) >> TSTAT_ENC_ROT_OFFSET; break; case MEM_PP_MEM: stat = (task_stat_reg & TSTAT_PP_MASK) >> TSTAT_PP_OFFSET; break; case MEM_ROT_PP_MEM: stat = (task_stat_reg & TSTAT_PP_ROT_MASK) >> TSTAT_PP_ROT_OFFSET; break; default: stat = TASK_STAT_IDLE; break; } return stat; } /*! * This function check for a logical channel status * * @param ipu ipu handler * @param channel Input parameter for the logical channel ID. * * @return This function returns 0 on idle and 1 on busy. * */ uint32_t ipu_channel_status(struct ipu_soc *ipu, ipu_channel_t channel) { uint32_t dma_status; _ipu_get(ipu); mutex_lock(&ipu->mutex_lock); dma_status = ipu_is_channel_busy(ipu, channel); mutex_unlock(&ipu->mutex_lock); _ipu_put(ipu); dev_dbg(ipu->dev, "%s, dma_status:%d.\n", __func__, dma_status); return dma_status; } EXPORT_SYMBOL(ipu_channel_status); int32_t ipu_swap_channel(struct ipu_soc *ipu, ipu_channel_t from_ch, ipu_channel_t to_ch) { uint32_t reg; unsigned long lock_flags; int from_dma = channel_2_dma(from_ch, IPU_INPUT_BUFFER); int to_dma = channel_2_dma(to_ch, IPU_INPUT_BUFFER); mutex_lock(&ipu->mutex_lock); /* enable target channel */ reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(to_dma)); ipu_idmac_write(ipu, reg | idma_mask(to_dma), IDMAC_CHA_EN(to_dma)); ipu->channel_enable_mask |= 1L << IPU_CHAN_ID(to_ch); /* switch dp dc */ _ipu_dp_dc_disable(ipu, from_ch, true); /* disable source channel */ reg = ipu_idmac_read(ipu, IDMAC_CHA_EN(from_dma)); ipu_idmac_write(ipu, reg & ~idma_mask(from_dma), IDMAC_CHA_EN(from_dma)); ipu_cm_write(ipu, idma_mask(from_dma), IPU_CHA_CUR_BUF(ipu->devtype, from_dma)); ipu_cm_write(ipu, tri_cur_buf_mask(from_dma), IPU_CHA_TRIPLE_CUR_BUF(ipu->devtype, from_dma)); ipu->channel_enable_mask &= ~(1L << IPU_CHAN_ID(from_ch)); spin_lock_irqsave(&ipu->rdy_reg_spin_lock, lock_flags); _ipu_clear_buffer_ready(ipu, from_ch, IPU_VIDEO_IN_BUFFER, 0); _ipu_clear_buffer_ready(ipu, from_ch, IPU_VIDEO_IN_BUFFER, 1); _ipu_clear_buffer_ready(ipu, from_ch, IPU_VIDEO_IN_BUFFER, 2); spin_unlock_irqrestore(&ipu->rdy_reg_spin_lock, lock_flags); mutex_unlock(&ipu->mutex_lock); return 0; } EXPORT_SYMBOL(ipu_swap_channel); uint32_t bytes_per_pixel(uint32_t fmt) { switch (fmt) { case IPU_PIX_FMT_GENERIC: /*generic data */ case IPU_PIX_FMT_RGB332: case IPU_PIX_FMT_YUV420P: case IPU_PIX_FMT_YVU420P: case IPU_PIX_FMT_YUV422P: case IPU_PIX_FMT_YUV444P: case IPU_PIX_FMT_NV12: case PRE_PIX_FMT_NV21: case IPU_PIX_FMT_NV16: case PRE_PIX_FMT_NV61: return 1; break; case IPU_PIX_FMT_GENERIC_16: /* generic data */ case IPU_PIX_FMT_RGB565: case IPU_PIX_FMT_BGRA4444: case IPU_PIX_FMT_BGRA5551: case IPU_PIX_FMT_YUYV: case IPU_PIX_FMT_UYVY: case IPU_PIX_FMT_GPU16_SB_ST: case IPU_PIX_FMT_GPU16_SB_SRT: case IPU_PIX_FMT_GPU16_ST: case IPU_PIX_FMT_GPU16_SRT: return 2; break; case IPU_PIX_FMT_BGR24: case IPU_PIX_FMT_RGB24: case IPU_PIX_FMT_YUV444: return 3; break; case IPU_PIX_FMT_GENERIC_32: /*generic data */ case IPU_PIX_FMT_BGR32: case IPU_PIX_FMT_BGRA32: case IPU_PIX_FMT_RGB32: case IPU_PIX_FMT_RGBA32: case IPU_PIX_FMT_ABGR32: case IPU_PIX_FMT_GPU32_SB_ST: case IPU_PIX_FMT_GPU32_SB_SRT: case IPU_PIX_FMT_GPU32_ST: case IPU_PIX_FMT_GPU32_SRT: case IPU_PIX_FMT_AYUV: return 4; break; default: return 1; break; } return 0; } EXPORT_SYMBOL(bytes_per_pixel); ipu_color_space_t format_to_colorspace(uint32_t fmt) { switch (fmt) { case IPU_PIX_FMT_RGB666: case IPU_PIX_FMT_RGB565: case IPU_PIX_FMT_BGRA4444: case IPU_PIX_FMT_BGRA5551: case IPU_PIX_FMT_BGR24: case IPU_PIX_FMT_RGB24: case IPU_PIX_FMT_GBR24: case IPU_PIX_FMT_BGR32: case IPU_PIX_FMT_BGRA32: case IPU_PIX_FMT_RGB32: case IPU_PIX_FMT_RGBA32: case IPU_PIX_FMT_ABGR32: case IPU_PIX_FMT_LVDS666: case IPU_PIX_FMT_LVDS888: case IPU_PIX_FMT_GPU32_SB_ST: case IPU_PIX_FMT_GPU32_SB_SRT: case IPU_PIX_FMT_GPU32_ST: case IPU_PIX_FMT_GPU32_SRT: case IPU_PIX_FMT_GPU16_SB_ST: case IPU_PIX_FMT_GPU16_SB_SRT: case IPU_PIX_FMT_GPU16_ST: case IPU_PIX_FMT_GPU16_SRT: return RGB; break; default: return YCbCr; break; } return RGB; } EXPORT_SYMBOL(format_to_colorspace); bool ipu_pixel_format_has_alpha(uint32_t fmt) { switch (fmt) { case IPU_PIX_FMT_RGBA32: case IPU_PIX_FMT_BGRA32: case IPU_PIX_FMT_ABGR32: return true; break; default: return false; break; } return false; } bool ipu_ch_param_bad_alpha_pos(uint32_t pixel_fmt) { return _ipu_ch_param_bad_alpha_pos(pixel_fmt); } EXPORT_SYMBOL(ipu_ch_param_bad_alpha_pos); bool ipu_pixel_format_is_gpu_tile(uint32_t fmt) { switch (fmt) { case IPU_PIX_FMT_GPU32_SB_ST: case IPU_PIX_FMT_GPU32_SB_SRT: case IPU_PIX_FMT_GPU32_ST: case IPU_PIX_FMT_GPU32_SRT: case IPU_PIX_FMT_GPU16_SB_ST: case IPU_PIX_FMT_GPU16_SB_SRT: case IPU_PIX_FMT_GPU16_ST: case IPU_PIX_FMT_GPU16_SRT: return true; default: return false; } } EXPORT_SYMBOL(ipu_pixel_format_is_gpu_tile); bool ipu_pixel_format_is_split_gpu_tile(uint32_t fmt) { switch (fmt) { case IPU_PIX_FMT_GPU32_SB_ST: case IPU_PIX_FMT_GPU32_SB_SRT: case IPU_PIX_FMT_GPU16_SB_ST: case IPU_PIX_FMT_GPU16_SB_SRT: return true; default: return false; } } EXPORT_SYMBOL(ipu_pixel_format_is_split_gpu_tile); bool ipu_pixel_format_is_pre_yuv(uint32_t fmt) { switch (fmt) { case PRE_PIX_FMT_NV21: case PRE_PIX_FMT_NV61: return true; default: return false; } } EXPORT_SYMBOL(ipu_pixel_format_is_pre_yuv); bool ipu_pixel_format_is_multiplanar_yuv(uint32_t fmt) { switch (fmt) { case IPU_PIX_FMT_YVU410P: case IPU_PIX_FMT_YUV420P: case IPU_PIX_FMT_YUV420P2: case IPU_PIX_FMT_YVU420P: case IPU_PIX_FMT_YUV422P: case IPU_PIX_FMT_YVU422P: case IPU_PIX_FMT_YUV444P: case IPU_PIX_FMT_NV12: case PRE_PIX_FMT_NV21: case IPU_PIX_FMT_NV16: case PRE_PIX_FMT_NV61: return true; default: return false; } } EXPORT_SYMBOL(ipu_pixel_format_is_multiplanar_yuv); int ipu_ch_param_get_axi_id(struct ipu_soc *ipu, ipu_channel_t channel, ipu_buffer_t type) { uint32_t dma_chan = channel_2_dma(channel, type); int axi_id; if (!idma_is_valid(dma_chan)) return -EINVAL; _ipu_get(ipu); mutex_lock(&ipu->mutex_lock); axi_id = _ipu_ch_param_get_axi_id(ipu, dma_chan); mutex_unlock(&ipu->mutex_lock); _ipu_put(ipu); return axi_id; } EXPORT_SYMBOL(ipu_ch_param_get_axi_id); #ifdef CONFIG_PM int ipu_runtime_suspend(struct device *dev) { release_bus_freq(BUS_FREQ_HIGH); dev_dbg(dev, "ipu busfreq high release.\n"); return 0; } int ipu_runtime_resume(struct device *dev) { request_bus_freq(BUS_FREQ_HIGH); dev_dbg(dev, "ipu busfreq high requst.\n"); return 0; } static const struct dev_pm_ops ipu_pm_ops = { SET_RUNTIME_PM_OPS(ipu_runtime_suspend, ipu_runtime_resume, NULL) }; #endif /*! * This structure contains pointers to the power management callback functions. */ static struct platform_driver mxcipu_driver = { .driver = { .name = "imx-ipuv3", .of_match_table = imx_ipuv3_dt_ids, #ifdef CONFIG_PM .pm = &ipu_pm_ops, #endif }, .probe = ipu_probe, .remove = ipu_remove, }; int32_t __init ipu_gen_init(void) { int32_t ret; ret = platform_driver_register(&mxcipu_driver); return 0; } subsys_initcall(ipu_gen_init); static void __exit ipu_gen_uninit(void) { platform_driver_unregister(&mxcipu_driver); } module_exit(ipu_gen_uninit);