| /* |
| * Copyright 2005-2011 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 <linux/types.h> |
| #include <linux/init.h> |
| #include <linux/platform_device.h> |
| #include <linux/err.h> |
| #include <linux/spinlock.h> |
| #include <linux/delay.h> |
| #include <linux/interrupt.h> |
| #include <linux/io.h> |
| #include <linux/irq.h> |
| #include <linux/irqdesc.h> |
| #include <linux/ipu.h> |
| #include <linux/clk.h> |
| #include <mach/clock.h> |
| #include <mach/hardware.h> |
| #include <mach/ipu-v3.h> |
| #include <mach/devices-common.h> |
| #include <asm/cacheflush.h> |
| #include <linux/delay.h> |
| |
| #include "ipu_prv.h" |
| #include "ipu_regs.h" |
| #include "ipu_param_mem.h" |
| |
| static struct ipu_soc ipu_array[MXC_IPU_MAX_NUM]; |
| int g_ipu_hw_rev; |
| |
| /* Static functions */ |
| static irqreturn_t ipu_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_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_smfc_chan(uint32_t dma_chan) |
| { |
| return ((dma_chan >= 0) && (dma_chan <= 3)); |
| } |
| |
| static inline int _ipu_is_trb_chan(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)) && |
| (g_ipu_hw_rev >= 2)); |
| } |
| |
| #define idma_is_valid(ch) (ch != NO_DMA) |
| #define idma_mask(ch) (idma_is_valid(ch) ? (1UL << (ch & 0x1F)) : 0) |
| #define idma_is_set(ipu, reg, dma) (ipu_idmac_read(ipu, reg(dma)) & idma_mask(dma)) |
| #define tri_cur_buf_mask(ch) (idma_mask(ch*2) * 3) |
| #define tri_cur_buf_shift(ch) (ffs(idma_mask(ch*2)) - 1) |
| |
| static int ipu_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; |
| } |
| |
| static int __devinit ipu_clk_setup_enable(struct ipu_soc *ipu, |
| struct platform_device *pdev) |
| { |
| struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data; |
| char ipu_clk[] = "ipu1_clk"; |
| char di0_clk[] = "ipu1_di0_clk"; |
| char di1_clk[] = "ipu1_di1_clk"; |
| |
| ipu_clk[3] += pdev->id; |
| di0_clk[3] += pdev->id; |
| di1_clk[3] += pdev->id; |
| |
| ipu->ipu_clk = clk_get(ipu->dev, ipu_clk); |
| if (IS_ERR(ipu->ipu_clk)) { |
| dev_err(ipu->dev, "clk_get failed"); |
| return PTR_ERR(ipu->ipu_clk); |
| } |
| dev_dbg(ipu->dev, "ipu_clk = %lu\n", clk_get_rate(ipu->ipu_clk)); |
| |
| ipu->pixel_clk[0] = ipu_pixel_clk[0]; |
| ipu->pixel_clk[1] = ipu_pixel_clk[1]; |
| |
| ipu_lookups[0].clk = &ipu->pixel_clk[0]; |
| ipu_lookups[1].clk = &ipu->pixel_clk[1]; |
| clkdev_add(&ipu_lookups[0]); |
| clkdev_add(&ipu_lookups[1]); |
| |
| clk_debug_register(&ipu->pixel_clk[0]); |
| clk_debug_register(&ipu->pixel_clk[1]); |
| |
| clk_enable(ipu->ipu_clk); |
| |
| clk_set_parent(&ipu->pixel_clk[0], ipu->ipu_clk); |
| clk_set_parent(&ipu->pixel_clk[1], ipu->ipu_clk); |
| |
| ipu->di_clk[0] = clk_get(ipu->dev, di0_clk); |
| ipu->di_clk[1] = clk_get(ipu->dev, di1_clk); |
| |
| ipu->csi_clk[0] = clk_get(ipu->dev, plat_data->csi_clk[0]); |
| ipu->csi_clk[1] = clk_get(ipu->dev, plat_data->csi_clk[1]); |
| |
| return 0; |
| } |
| |
| #if 0 |
| static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc) |
| { |
| struct ipu_soc *ipu = irq_desc_get_handler_data(desc); |
| const int int_reg[] = { 1, 2, 3, 4, 11, 12, 13, 14, 15, 0 }; |
| u32 status; |
| int i, line; |
| |
| for (i = 0;; i++) { |
| if (int_reg[i] == 0) |
| break; |
| |
| status = ipu_cm_read(ipu, IPU_INT_STAT(int_reg[i])); |
| status &= ipu_cm_read(ipu, IPU_INT_CTRL(int_reg[i])); |
| |
| while ((line = ffs(status))) { |
| line--; |
| status &= ~(1UL << line); |
| line += ipu->irq_start + (int_reg[i] - 1) * 32; |
| generic_handle_irq(line); |
| } |
| |
| } |
| } |
| |
| static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc) |
| { |
| struct ipu_soc *ipu = irq_desc_get_handler_data(desc); |
| const int int_reg[] = { 5, 6, 9, 10, 0 }; |
| u32 status; |
| int i, line; |
| |
| for (i = 0;; i++) { |
| if (int_reg[i] == 0) |
| break; |
| |
| status = ipu_cm_read(ipu, IPU_INT_STAT(int_reg[i])); |
| status &= ipu_cm_read(ipu, IPU_INT_CTRL(int_reg[i])); |
| |
| while ((line = ffs(status))) { |
| line--; |
| status &= ~(1UL << line); |
| line += ipu->irq_start + (int_reg[i] - 1) * 32; |
| generic_handle_irq(line); |
| } |
| |
| } |
| } |
| |
| static void ipu_ack_irq(struct irq_data *d) |
| { |
| struct ipu_soc *ipu = irq_data_get_irq_chip_data(d); |
| unsigned int irq = d->irq - ipu->irq_start; |
| unsigned long flags; |
| |
| spin_lock_irqsave(&ipu->ipu_lock, flags); |
| ipu_cm_write(ipu, 1 << (irq % 32), IPU_INT_STAT(irq / 32 + 1)); |
| spin_unlock_irqrestore(&ipu->ipu_lock, flags); |
| } |
| |
| static void ipu_unmask_irq(struct irq_data *d) |
| { |
| struct ipu_soc *ipu = irq_data_get_irq_chip_data(d); |
| unsigned int irq = d->irq - ipu->irq_start; |
| unsigned long flags; |
| u32 reg; |
| |
| spin_lock_irqsave(&ipu->ipu_lock, flags); |
| reg = ipu_cm_read(ipu, IPU_INT_CTRL(irq / 32 + 1)); |
| reg |= 1 << (irq % 32); |
| ipu_cm_write(ipu, reg, IPU_INT_CTRL(irq / 32 + 1)); |
| spin_unlock_irqrestore(&ipu->ipu_lock, flags); |
| } |
| |
| static void ipu_mask_irq(struct irq_data *d) |
| { |
| struct ipu_soc *ipu = irq_data_get_irq_chip_data(d); |
| unsigned int irq = d->irq - ipu->irq_start; |
| unsigned long flags; |
| u32 reg; |
| |
| spin_lock_irqsave(&ipu->ipu_lock, flags); |
| reg = ipu_cm_read(ipu, IPU_INT_CTRL(irq / 32 + 1)); |
| reg &= ~(1 << (irq % 32)); |
| ipu_cm_write(ipu, reg, IPU_INT_CTRL(irq / 32 + 1)); |
| spin_unlock_irqrestore(&ipu->ipu_lock, flags); |
| } |
| |
| static struct irq_chip ipu_irq_chip = { |
| .name = "IPU", |
| .irq_ack = ipu_ack_irq, |
| .irq_mask = ipu_mask_irq, |
| .irq_unmask = ipu_unmask_irq, |
| }; |
| |
| static void __devinit ipu_irq_setup(struct ipu_soc *ipu) |
| { |
| int i; |
| |
| for (i = ipu->irq_start; i < ipu->irq_start + MX5_IPU_IRQS; i++) { |
| irq_set_chip_and_handler(i, &ipu_irq_chip, handle_level_irq); |
| set_irq_flags(i, IRQF_VALID); |
| irq_set_chip_data(i, ipu); |
| } |
| |
| irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler); |
| irq_set_handler_data(ipu->irq_sync, ipu); |
| irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler); |
| irq_set_handler_data(ipu->irq_err, ipu); |
| } |
| |
| int ipu_request_irq(struct ipu_soc *ipu, unsigned int irq, |
| irq_handler_t handler, unsigned long flags, |
| const char *name, void *dev) |
| { |
| return request_irq(ipu->irq_start + irq, handler, flags, name, dev); |
| } |
| EXPORT_SYMBOL_GPL(ipu_request_irq); |
| |
| void ipu_enable_irq(struct ipu_soc *ipu, unsigned int irq) |
| { |
| return enable_irq(ipu->irq_start + irq); |
| } |
| EXPORT_SYMBOL_GPL(ipu_disable_irq); |
| |
| void ipu_disable_irq(struct ipu_soc *ipu, unsigned int irq) |
| { |
| return disable_irq(ipu->irq_start + irq); |
| } |
| EXPORT_SYMBOL_GPL(ipu_disable_irq); |
| |
| void ipu_free_irq(struct ipu_soc *ipu, unsigned int irq, void *dev_id) |
| { |
| free_irq(ipu->irq_start + irq, dev_id); |
| } |
| EXPORT_SYMBOL_GPL(ipu_free_irq); |
| |
| static irqreturn_t ipu_completion_handler(int irq, void *dev) |
| { |
| struct completion *completion = dev; |
| |
| complete(completion); |
| return IRQ_HANDLED; |
| } |
| |
| int ipu_wait_for_interrupt(struct ipu_soc *ipu, int interrupt, int timeout_ms) |
| { |
| DECLARE_COMPLETION_ONSTACK(completion); |
| int ret; |
| |
| ret = ipu_request_irq(ipu, interrupt, ipu_completion_handler, |
| 0, NULL, &completion); |
| if (ret) { |
| dev_err(ipu->dev, |
| "ipu request irq %d fail\n", interrupt); |
| return ret; |
| } |
| |
| ret = wait_for_completion_timeout(&completion, |
| msecs_to_jiffies(timeout_ms)); |
| |
| ipu_free_irq(ipu, interrupt, &completion); |
| |
| return ret > 0 ? 0 : -ETIMEDOUT; |
| } |
| EXPORT_SYMBOL_GPL(ipu_wait_for_interrupt); |
| #endif |
| |
| 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_lock(struct ipu_soc *ipu) |
| { |
| /*TODO:remove in_irq() condition after v4l2 driver rewrite*/ |
| if (!in_irq() && !in_softirq()) |
| mutex_lock(&ipu->mutex_lock); |
| } |
| |
| void _ipu_unlock(struct ipu_soc *ipu) |
| { |
| /*TODO:remove in_irq() condition after v4l2 driver rewrite*/ |
| if (!in_irq() && !in_softirq()) |
| mutex_unlock(&ipu->mutex_lock); |
| } |
| |
| void _ipu_get(struct ipu_soc *ipu) |
| { |
| if (atomic_inc_return(&ipu->ipu_use_count) == 1) |
| clk_enable(ipu->ipu_clk); |
| } |
| |
| void _ipu_put(struct ipu_soc *ipu) |
| { |
| if (atomic_dec_return(&ipu->ipu_use_count) == 0) |
| clk_disable(ipu->ipu_clk); |
| } |
| |
| /*! |
| * 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 __devinit ipu_probe(struct platform_device *pdev) |
| { |
| struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data; |
| struct ipu_soc *ipu; |
| struct resource *res; |
| unsigned long ipu_base; |
| int ret = 0; |
| |
| if (pdev->id >= MXC_IPU_MAX_NUM) |
| return -ENODEV; |
| |
| ipu = &ipu_array[pdev->id]; |
| memset(ipu, 0, sizeof(struct ipu_soc)); |
| |
| spin_lock_init(&ipu->spin_lock); |
| mutex_init(&ipu->mutex_lock); |
| atomic_set(&ipu->ipu_use_count, 0); |
| |
| g_ipu_hw_rev = plat_data->rev; |
| |
| ipu->dev = &pdev->dev; |
| |
| if (plat_data->init) |
| plat_data->init(pdev->id); |
| |
| 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) { |
| ret = -ENODEV; |
| goto failed_get_res; |
| } |
| |
| if (request_irq(ipu->irq_sync, ipu_irq_handler, 0, pdev->name, ipu) != 0) { |
| dev_err(ipu->dev, "request SYNC interrupt failed\n"); |
| ret = -EBUSY; |
| goto failed_req_irq_sync; |
| } |
| /* Some platforms have 2 IPU interrupts */ |
| if (ipu->irq_err >= 0) { |
| if (request_irq |
| (ipu->irq_err, ipu_irq_handler, 0, pdev->name, ipu) != 0) { |
| dev_err(ipu->dev, "request ERR interrupt failed\n"); |
| ret = -EBUSY; |
| goto failed_req_irq_err; |
| } |
| } |
| |
| ipu_base = res->start; |
| /* base fixup */ |
| if (g_ipu_hw_rev == 4) /* IPUv3H */ |
| ipu_base += IPUV3H_REG_BASE; |
| else if (g_ipu_hw_rev == 3) /* IPUv3M */ |
| ipu_base += IPUV3M_REG_BASE; |
| else /* IPUv3D, v3E, v3EX */ |
| ipu_base += IPUV3DEX_REG_BASE; |
| |
| ipu->cm_reg = ioremap(ipu_base + IPU_CM_REG_BASE, PAGE_SIZE); |
| ipu->ic_reg = ioremap(ipu_base + IPU_IC_REG_BASE, PAGE_SIZE); |
| ipu->idmac_reg = ioremap(ipu_base + IPU_IDMAC_REG_BASE, PAGE_SIZE); |
| /* DP Registers are accessed thru the SRM */ |
| ipu->dp_reg = ioremap(ipu_base + IPU_SRM_REG_BASE, PAGE_SIZE); |
| ipu->dc_reg = ioremap(ipu_base + IPU_DC_REG_BASE, PAGE_SIZE); |
| ipu->dmfc_reg = ioremap(ipu_base + IPU_DMFC_REG_BASE, PAGE_SIZE); |
| ipu->di_reg[0] = ioremap(ipu_base + IPU_DI0_REG_BASE, PAGE_SIZE); |
| ipu->di_reg[1] = ioremap(ipu_base + IPU_DI1_REG_BASE, PAGE_SIZE); |
| ipu->smfc_reg = ioremap(ipu_base + IPU_SMFC_REG_BASE, PAGE_SIZE); |
| ipu->csi_reg[0] = ioremap(ipu_base + IPU_CSI0_REG_BASE, PAGE_SIZE); |
| ipu->csi_reg[1] = ioremap(ipu_base + IPU_CSI1_REG_BASE, PAGE_SIZE); |
| ipu->cpmem_base = ioremap(ipu_base + IPU_CPMEM_REG_BASE, SZ_128K); |
| ipu->tpmem_base = ioremap(ipu_base + IPU_TPM_REG_BASE, SZ_64K); |
| ipu->dc_tmpl_reg = ioremap(ipu_base + IPU_DC_TMPL_REG_BASE, SZ_128K); |
| ipu->vdi_reg = ioremap(ipu_base + IPU_VDI_REG_BASE, PAGE_SIZE); |
| ipu->disp_base[1] = ioremap(ipu_base + IPU_DISP1_BASE, SZ_4K); |
| |
| 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->disp_base[1] |
| || !ipu->vdi_reg) { |
| ret = -ENOMEM; |
| goto failed_ioremap; |
| } |
| |
| 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 Display Region 1 Mem = %p\n", ipu->disp_base[1]); |
| dev_dbg(ipu->dev, "IPU VDI Regs = %p\n", ipu->vdi_reg); |
| |
| ret = ipu_clk_setup_enable(ipu, pdev); |
| if (ret < 0) { |
| dev_err(ipu->dev, "ipu clk setup failed\n"); |
| goto failed_clk_setup; |
| } |
| |
| platform_set_drvdata(pdev, ipu); |
| |
| ipu_reset(ipu); |
| |
| ipu_disp_init(ipu); |
| |
| /* Set sync refresh channels and CSI->mem channel as high priority */ |
| ipu_idmac_write(ipu, 0x18800001L, IDMAC_CHA_PRI(0)); |
| |
| /* Set MCU_T to divide MCU access window into 2 */ |
| ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN); |
| |
| clk_disable(ipu->ipu_clk); |
| |
| register_ipu_device(ipu, pdev->id); |
| |
| ipu->online = true; |
| |
| return ret; |
| |
| failed_clk_setup: |
| iounmap(ipu->cm_reg); |
| iounmap(ipu->ic_reg); |
| iounmap(ipu->idmac_reg); |
| iounmap(ipu->dc_reg); |
| iounmap(ipu->dp_reg); |
| iounmap(ipu->dmfc_reg); |
| iounmap(ipu->di_reg[0]); |
| iounmap(ipu->di_reg[1]); |
| iounmap(ipu->smfc_reg); |
| iounmap(ipu->csi_reg[0]); |
| iounmap(ipu->csi_reg[1]); |
| iounmap(ipu->cpmem_base); |
| iounmap(ipu->tpmem_base); |
| iounmap(ipu->dc_tmpl_reg); |
| iounmap(ipu->disp_base[1]); |
| iounmap(ipu->vdi_reg); |
| failed_ioremap: |
| if (ipu->irq_sync) |
| free_irq(ipu->irq_err, ipu); |
| failed_req_irq_err: |
| free_irq(ipu->irq_sync, ipu); |
| failed_req_irq_sync: |
| failed_get_res: |
| return ret; |
| } |
| |
| int __devexit ipu_remove(struct platform_device *pdev) |
| { |
| struct ipu_soc *ipu = platform_get_drvdata(pdev); |
| |
| unregister_ipu_device(ipu, pdev->id); |
| |
| if (ipu->irq_sync) |
| free_irq(ipu->irq_sync, ipu); |
| if (ipu->irq_err) |
| free_irq(ipu->irq_err, ipu); |
| |
| clk_put(ipu->ipu_clk); |
| |
| iounmap(ipu->cm_reg); |
| iounmap(ipu->ic_reg); |
| iounmap(ipu->idmac_reg); |
| iounmap(ipu->dc_reg); |
| iounmap(ipu->dp_reg); |
| iounmap(ipu->dmfc_reg); |
| iounmap(ipu->di_reg[0]); |
| iounmap(ipu->di_reg[1]); |
| iounmap(ipu->smfc_reg); |
| iounmap(ipu->csi_reg[0]); |
| iounmap(ipu->csi_reg[1]); |
| iounmap(ipu->cpmem_base); |
| iounmap(ipu->tpmem_base); |
| iounmap(ipu->dc_tmpl_reg); |
| iounmap(ipu->disp_base[1]); |
| iounmap(ipu->vdi_reg); |
| |
| 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(0))); |
| dev_dbg(ipu->dev, "IDMAC_BAND_EN2 = \t0x%08X\n", |
| ipu_idmac_read(ipu, IDMAC_BAND_EN(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 (g_ipu_hw_rev >= 2) { |
| dev_dbg(ipu->dev, "IPU_CHA_TRB_MODE_SEL0 = \t0x%08X\n", |
| ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(0))); |
| dev_dbg(ipu->dev, "IPU_CHA_TRB_MODE_SEL1 = \t0x%08X\n", |
| ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(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)); |
| } |
| |
| /*! |
| * 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; |
| uint32_t ipu_conf; |
| uint32_t reg; |
| |
| dev_dbg(ipu->dev, "init channel = %d\n", IPU_CHAN_ID(channel)); |
| |
| _ipu_get(ipu); |
| |
| _ipu_lock(ipu); |
| |
| 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_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) { |
| 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; |
| |
| /*Without SMFC, CSI only support parallel data source*/ |
| 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) { |
| 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; |
| |
| /*Without SMFC, CSI only support parallel data source*/ |
| 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: |
| ipu->ic_use_count++; |
| reg = ipu_cm_read(ipu, IPU_FS_PROC_FLOW1); |
| ipu_cm_write(ipu, reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW1); |
| |
| if (params->mem_prp_vf_mem.graphics_combine_en) |
| ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; |
| if (params->mem_prp_vf_mem.alpha_chan_en) |
| ipu->thrd_chan_en[IPU_CHAN_ID(channel)] = true; |
| |
| _ipu_ic_init_prpvf(ipu, params, false); |
| break; |
| case MEM_VDI_PRP_VF_MEM: |
| if ((ipu->using_ic_dirct_ch == CSI_PRP_VF_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: |
| _ipu_vdi_init(ipu, channel, params); |
| break; |
| case MEM_VDI_PRP_VF_MEM_N: |
| _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) |
| ipu->sec_chan_en[IPU_CHAN_ID(channel)] = true; |
| if (params->mem_pp_mem.alpha_chan_en) |
| ipu->thrd_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: |
| _ipu_unlock(ipu); |
| 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; |
| |
| _ipu_lock(ipu); |
| |
| if ((ipu->channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) { |
| dev_err(ipu->dev, "Channel already uninitialized %d\n", |
| IPU_CHAN_ID(channel)); |
| _ipu_unlock(ipu); |
| 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) || |
| idma_is_set(ipu, IDMAC_CHA_EN, out_dma)) { |
| dev_err(ipu->dev, |
| "Channel %d is not disabled, disable first\n", |
| IPU_CHAN_ID(channel)); |
| _ipu_unlock(ipu); |
| 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(in_dma)); |
| ipu_cm_write(ipu, reg & ~idma_mask(in_dma), IPU_CHA_TRB_MODE_SEL(in_dma)); |
| reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(out_dma)); |
| ipu_cm_write(ipu, reg & ~idma_mask(out_dma), IPU_CHA_TRB_MODE_SEL(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_PRP_VF_MEM_P: |
| case MEM_VDI_PRP_VF_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: |
| _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: |
| _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: |
| _ipu_dc_uninit(ipu, 8); |
| ipu->di_use_count[ipu->dc_di_assignment[8]]--; |
| ipu->dc_use_count--; |
| break; |
| case DIRECT_ASYNC1: |
| _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)); |
| |
| _ipu_unlock(ipu); |
| |
| _ipu_put(ipu); |
| |
| 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; |
| } |
| |
| /* IPUv3EX and IPUv3M support triple buffer */ |
| if ((!_ipu_is_trb_chan(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; |
| } |
| |
| _ipu_lock(ipu); |
| |
| /* 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)) { |
| 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)) { |
| 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); |
| if ((pixel_fmt == IPU_PIX_FMT_GENERIC) && |
| ((_ipu_ch_param_get_bpp(ipu, dma_chan) == 5) || |
| (_ipu_ch_param_get_bpp(ipu, dma_chan) == 3))) |
| burst_size = burst_size >> 4; |
| else |
| burst_size = burst_size >> 2; |
| _ipu_smfc_set_burst_size(ipu, channel, burst_size-1); |
| } |
| |
| /* AXI-id */ |
| if (idma_is_set(ipu, IDMAC_CHA_PRI, dma_chan)) { |
| unsigned reg = IDMAC_CH_LOCK_EN_1; |
| uint32_t value = 0; |
| if (cpu_is_mx53() || cpu_is_mx6q()) { |
| _ipu_ch_param_set_axi_id(ipu, dma_chan, 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; |
| value = 0x3 << 0; |
| break; |
| case 46: |
| reg = IDMAC_CH_LOCK_EN_2; |
| value = 0x3 << 2; |
| break; |
| case 47: |
| reg = IDMAC_CH_LOCK_EN_2; |
| value = 0x3 << 4; |
| break; |
| case 48: |
| reg = IDMAC_CH_LOCK_EN_2; |
| value = 0x3 << 6; |
| break; |
| case 49: |
| reg = IDMAC_CH_LOCK_EN_2; |
| value = 0x3 << 8; |
| break; |
| case 50: |
| reg = IDMAC_CH_LOCK_EN_2; |
| value = 0x3 << 10; |
| break; |
| default: |
| break; |
| } |
| value |= ipu_idmac_read(ipu, reg); |
| ipu_idmac_write(ipu, value, reg); |
| } else |
| _ipu_ch_param_set_axi_id(ipu, dma_chan, 1); |
| } else { |
| if (cpu_is_mx6q()) |
| _ipu_ch_param_set_axi_id(ipu, dma_chan, 1); |
| } |
| |
| _ipu_ch_param_dump(ipu, dma_chan); |
| |
| if (phyaddr_2 && g_ipu_hw_rev >= 2) { |
| 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(dma_chan)); |
| reg |= idma_mask(dma_chan); |
| ipu_cm_write(ipu, reg, IPU_CHA_TRB_MODE_SEL(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_idmac_write(ipu, 0x46004241L, IDMAC_SUB_ADDR_3); |
| ipu_idmac_write(ipu, 0x00000045L, IDMAC_SUB_ADDR_1); |
| |
| /* Reset to buffer 0 */ |
| ipu_cm_write(ipu, tri_cur_buf_mask(dma_chan), |
| IPU_CHA_TRIPLE_CUR_BUF(dma_chan)); |
| } else { |
| reg = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(dma_chan)); |
| reg &= ~idma_mask(dma_chan); |
| ipu_cm_write(ipu, reg, IPU_CHA_TRB_MODE_SEL(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(dma_chan)); |
| |
| } |
| |
| _ipu_unlock(ipu); |
| |
| 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); |
| |
| if (dma_chan == IDMA_CHAN_INVALID) |
| return -EINVAL; |
| |
| _ipu_lock(ipu); |
| |
| if (bufNum == 0) |
| reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(dma_chan)); |
| else if (bufNum == 1) |
| reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(dma_chan)); |
| else |
| reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(dma_chan)); |
| |
| if ((reg & idma_mask(dma_chan)) == 0) |
| _ipu_ch_param_set_buffer(ipu, dma_chan, bufNum, phyaddr); |
| else |
| ret = -EACCES; |
| |
| _ipu_unlock(ipu); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL(ipu_update_channel_buffer); |
| |
| |
| /*! |
| * 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); |
| |
| if (dma_chan == IDMA_CHAN_INVALID) |
| return -EINVAL; |
| |
| _ipu_lock(ipu); |
| |
| if ((ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(dma_chan)) & idma_mask(dma_chan)) || |
| (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(dma_chan)) & idma_mask(dma_chan)) || |
| ((ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(dma_chan)) & idma_mask(dma_chan)) && |
| (ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(dma_chan)) & idma_mask(dma_chan)) && |
| _ipu_is_trb_chan(dma_chan))) |
| ret = -EACCES; |
| else |
| _ipu_ch_offset_update(ipu, dma_chan, pixel_fmt, width, height, stride, |
| u, v, 0, vertical_offset, horizontal_offset); |
| |
| _ipu_unlock(ipu); |
| return ret; |
| } |
| EXPORT_SYMBOL(ipu_update_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); |
| |
| if (dma_chan == IDMA_CHAN_INVALID) |
| return -EINVAL; |
| |
| /* Mark buffer to be ready. */ |
| _ipu_lock(ipu); |
| if (bufNum == 0) |
| ipu_cm_write(ipu, idma_mask(dma_chan), |
| IPU_CHA_BUF0_RDY(dma_chan)); |
| else if (bufNum == 1) |
| ipu_cm_write(ipu, idma_mask(dma_chan), |
| IPU_CHA_BUF1_RDY(dma_chan)); |
| else |
| ipu_cm_write(ipu, idma_mask(dma_chan), |
| IPU_CHA_BUF2_RDY(dma_chan)); |
| _ipu_unlock(ipu); |
| 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)); |
| |
| /* Mark buffers to be ready. */ |
| _ipu_lock(ipu); |
| if (bufNum == 0) |
| ipu_cm_write(ipu, mask_bit, IPU_CHA_BUF0_RDY(dma_chan)); |
| else |
| ipu_cm_write(ipu, mask_bit, IPU_CHA_BUF1_RDY(dma_chan)); |
| _ipu_unlock(ipu); |
| 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; |
| |
| _ipu_lock(ipu); |
| |
| 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; |
| default: |
| retval = -EINVAL; |
| goto err; |
| } |
| |
| switch (dest_ch) { |
| case MEM_PP_MEM: |
| fs_proc_flow1 &= ~FS_PP_SRC_SEL_MASK; |
| 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; |
| 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: |
| _ipu_unlock(ipu); |
| 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; |
| |
| _ipu_lock(ipu); |
| |
| 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; |
| 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; |
| 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: |
| _ipu_unlock(ipu); |
| 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; |
| |
| _ipu_lock(ipu); |
| |
| if (ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) { |
| dev_err(ipu->dev, "Warning: channel already enabled %d\n", |
| IPU_CHAN_ID(channel)); |
| _ipu_unlock(ipu); |
| 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); |
| 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->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; |
| 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->smfc_use_count > 0) |
| ipu_conf |= IPU_CONF_SMFC_EN; |
| 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_ic_enable_task(ipu, channel); |
| |
| ipu->channel_enable_mask |= 1L << IPU_CHAN_ID(channel); |
| |
| _ipu_unlock(ipu); |
| |
| 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; |
| |
| if (dma_chan == IDMA_CHAN_INVALID) |
| return -EINVAL; |
| |
| if (bufNum == 0) |
| reg = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(dma_chan)); |
| else if (bufNum == 1) |
| reg = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(dma_chan)); |
| else |
| reg = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(dma_chan)); |
| |
| 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(dma_ch)); |
| else if (bufNum == 1) |
| ipu_cm_write(ipu, idma_mask(dma_ch), |
| IPU_CHA_BUF1_RDY(dma_ch)); |
| else |
| ipu_cm_write(ipu, idma_mask(dma_ch), |
| IPU_CHA_BUF2_RDY(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) |
| { |
| _ipu_lock(ipu); |
| _ipu_clear_buffer_ready(ipu, channel, type, bufNum); |
| _ipu_unlock(ipu); |
| } |
| 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; |
| |
| _ipu_lock(ipu); |
| |
| if ((ipu->channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) { |
| dev_err(ipu->dev, "Channel already disabled %d\n", |
| IPU_CHAN_ID(channel)); |
| _ipu_unlock(ipu); |
| 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)) |
| && (idma_is_valid(out_dma) && |
| !idma_is_set(ipu, IDMAC_CHA_EN, out_dma))) { |
| _ipu_unlock(ipu); |
| 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_IRQ_BG_SYNC_EOF)); |
| while ((ipu_cm_read(ipu, IPUIRQ_2_STATREG(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) { |
| while (idma_is_set(ipu, IDMAC_CHA_BUSY, in_dma) || |
| idma_is_set(ipu, IDMAC_CHA_BUSY, out_dma) || |
| (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && |
| idma_is_set(ipu, IDMAC_CHA_BUSY, sec_dma)) || |
| (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && |
| idma_is_set(ipu, IDMAC_CHA_BUSY, thrd_dma))) { |
| uint32_t irq = 0xffffffff; |
| int timeout = 50; |
| |
| if (idma_is_set(ipu, IDMAC_CHA_BUSY, out_dma)) |
| irq = out_dma; |
| if (ipu->sec_chan_en[IPU_CHAN_ID(channel)] && |
| idma_is_set(ipu, IDMAC_CHA_BUSY, sec_dma)) |
| irq = sec_dma; |
| if (ipu->thrd_chan_en[IPU_CHAN_ID(channel)] && |
| idma_is_set(ipu, IDMAC_CHA_BUSY, thrd_dma)) |
| irq = thrd_dma; |
| if (idma_is_set(ipu, IDMAC_CHA_BUSY, 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(irq)); |
| |
| dev_dbg(ipu->dev, "warning: channel %d busy, need wait\n", irq); |
| |
| while (((ipu_cm_read(ipu, IPUIRQ_2_STATREG(irq)) |
| & IPUIRQ_2_MASK(irq)) == 0) && |
| (idma_is_set(ipu, IDMAC_CHA_BUSY, irq))) { |
| msleep(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; |
| } |
| } |
| |
| } |
| } |
| |
| 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_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(in_dma)); |
| ipu_cm_write(ipu, tri_cur_buf_mask(in_dma), |
| IPU_CHA_TRIPLE_CUR_BUF(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(out_dma)); |
| ipu_cm_write(ipu, tri_cur_buf_mask(out_dma), |
| IPU_CHA_TRIPLE_CUR_BUF(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(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(thrd_dma)); |
| } |
| |
| if (channel == MEM_FG_SYNC) |
| _ipu_disp_set_window_pos(ipu, channel, fg_pos_x, fg_pos_y); |
| |
| /* 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); |
| } |
| |
| ipu->channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel)); |
| |
| _ipu_unlock(ipu); |
| |
| 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_lock(ipu); |
| 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); |
| } |
| _ipu_unlock(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_lock(ipu); |
| ipu->csi_use_count[csi]--; |
| if (ipu->csi_use_count[csi] == 0) { |
| 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); |
| } |
| _ipu_unlock(ipu); |
| return 0; |
| } |
| EXPORT_SYMBOL(ipu_disable_csi); |
| |
| static irqreturn_t ipu_irq_handler(int irq, void *desc) |
| { |
| struct ipu_soc *ipu = desc; |
| int i; |
| uint32_t line; |
| irqreturn_t result = IRQ_NONE; |
| uint32_t int_stat; |
| const int err_reg[] = { 5, 6, 9, 10, 0 }; |
| const int int_reg[] = { 1, 2, 3, 4, 11, 12, 13, 14, 15, 0 }; |
| unsigned long lock_flags; |
| |
| for (i = 0;; i++) { |
| if (err_reg[i] == 0) |
| break; |
| |
| spin_lock_irqsave(&ipu->spin_lock, lock_flags); |
| |
| int_stat = ipu_cm_read(ipu, IPU_INT_STAT(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(err_reg[i])); |
| dev_err(ipu->dev, |
| "IPU Error - 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_irqrestore(&ipu->spin_lock, lock_flags); |
| } |
| |
| for (i = 0;; i++) { |
| if (int_reg[i] == 0) |
| break; |
| spin_lock_irqsave(&ipu->spin_lock, lock_flags); |
| int_stat = ipu_cm_read(ipu, IPU_INT_STAT(int_reg[i])); |
| int_stat &= ipu_cm_read(ipu, IPU_INT_CTRL(int_reg[i])); |
| ipu_cm_write(ipu, int_stat, IPU_INT_STAT(int_reg[i])); |
| spin_unlock_irqrestore(&ipu->spin_lock, lock_flags); |
| while ((line = ffs(int_stat)) != 0) { |
| line--; |
| int_stat &= ~(1UL << line); |
| line += (int_reg[i] - 1) * 32; |
| result |= |
| ipu->irq_list[line].handler(line, |
| ipu->irq_list[line]. |
| dev_id); |
| } |
| } |
| |
| return result; |
| } |
| |
| /*! |
| * 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. |
| * |
| */ |
| void ipu_enable_irq(struct ipu_soc *ipu, uint32_t irq) |
| { |
| uint32_t reg; |
| unsigned long lock_flags; |
| |
| _ipu_get(ipu); |
| |
| spin_lock_irqsave(&ipu->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->spin_lock, lock_flags); |
| |
| _ipu_put(ipu); |
| } |
| 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->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->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->spin_lock, lock_flags); |
| |
| ipu_cm_write(ipu, IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(irq)); |
| |
| spin_unlock_irqrestore(&ipu->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; |
| |
| _ipu_get(ipu); |
| |
| reg = ipu_cm_read(ipu, IPUIRQ_2_STATREG(irq)); |
| |
| _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) |
| { |
| unsigned long lock_flags; |
| |
| BUG_ON(irq >= IPU_IRQ_COUNT); |
| |
| _ipu_get(ipu); |
| |
| spin_lock_irqsave(&ipu->spin_lock, lock_flags); |
| |
| if (ipu->irq_list[irq].handler != NULL) { |
| dev_err(ipu->dev, |
| "handler already installed on irq %d\n", irq); |
| spin_unlock_irqrestore(&ipu->spin_lock, lock_flags); |
| return -EINVAL; |
| } |
| |
| 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(irq)); |
| |
| spin_unlock_irqrestore(&ipu->spin_lock, lock_flags); |
| |
| _ipu_put(ipu); |
| |
| ipu_enable_irq(ipu, irq); /* enable the interrupt */ |
| |
| return 0; |
| } |
| 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) |
| { |
| unsigned long lock_flags; |
| |
| ipu_disable_irq(ipu, irq); /* disable the interrupt */ |
| |
| spin_lock_irqsave(&ipu->spin_lock, lock_flags); |
| if (ipu->irq_list[irq].dev_id == dev_id) |
| ipu->irq_list[irq].handler = NULL; |
| spin_unlock_irqrestore(&ipu->spin_lock, lock_flags); |
| } |
| 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(dma_chan)); |
| if ((reg & idma_mask(dma_chan)) && _ipu_is_trb_chan(dma_chan)) { |
| reg = ipu_cm_read(ipu, IPU_CHA_TRIPLE_CUR_BUF(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(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); |
| |
| 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; |
| } |
| |
| int32_t ipu_swap_channel(struct ipu_soc *ipu, ipu_channel_t from_ch, ipu_channel_t to_ch) |
| { |
| uint32_t reg; |
| |
| int from_dma = channel_2_dma(from_ch, IPU_INPUT_BUFFER); |
| int to_dma = channel_2_dma(to_ch, IPU_INPUT_BUFFER); |
| |
| _ipu_lock(ipu); |
| |
| /* 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(from_dma)); |
| ipu_cm_write(ipu, tri_cur_buf_mask(from_dma), |
| IPU_CHA_TRIPLE_CUR_BUF(from_dma)); |
| |
| ipu->channel_enable_mask &= ~(1L << IPU_CHAN_ID(from_ch)); |
| |
| _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); |
| |
| _ipu_unlock(ipu); |
| |
| 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: |
| return 1; |
| break; |
| case IPU_PIX_FMT_RGB565: |
| case IPU_PIX_FMT_YUYV: |
| case IPU_PIX_FMT_UYVY: |
| return 2; |
| break; |
| case IPU_PIX_FMT_BGR24: |
| case IPU_PIX_FMT_RGB24: |
| 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: |
| 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_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: |
| return RGB; |
| break; |
| |
| default: |
| return YCbCr; |
| break; |
| } |
| return RGB; |
| } |
| |
| 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; |
| } |
| |
| static int ipu_suspend(struct platform_device *pdev, pm_message_t state) |
| { |
| struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data; |
| struct ipu_soc *ipu = platform_get_drvdata(pdev); |
| |
| if (atomic_read(&ipu->ipu_use_count)) { |
| /* save and disable enabled channels*/ |
| ipu->idma_enable_reg[0] = ipu_idmac_read(ipu, IDMAC_CHA_EN(0)); |
| ipu->idma_enable_reg[1] = ipu_idmac_read(ipu, IDMAC_CHA_EN(32)); |
| while ((ipu_idmac_read(ipu, IDMAC_CHA_BUSY(0)) |
| & ipu->idma_enable_reg[0]) |
| || (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(32)) |
| & ipu->idma_enable_reg[1])) { |
| /* disable channel not busy already */ |
| uint32_t chan_should_disable, timeout = 1000, time = 0; |
| |
| chan_should_disable = |
| ipu_idmac_read(ipu, IDMAC_CHA_BUSY(0)) |
| ^ ipu->idma_enable_reg[0]; |
| ipu_idmac_write(ipu, (~chan_should_disable) & |
| ipu->idma_enable_reg[0], IDMAC_CHA_EN(0)); |
| chan_should_disable = |
| ipu_idmac_read(ipu, IDMAC_CHA_BUSY(1)) |
| ^ ipu->idma_enable_reg[1]; |
| ipu_idmac_write(ipu, (~chan_should_disable) & |
| ipu->idma_enable_reg[1], IDMAC_CHA_EN(32)); |
| msleep(2); |
| time += 2; |
| if (time >= timeout) |
| return -1; |
| } |
| ipu_idmac_write(ipu, 0, IDMAC_CHA_EN(0)); |
| ipu_idmac_write(ipu, 0, IDMAC_CHA_EN(32)); |
| |
| /* save double buffer select regs */ |
| ipu->cha_db_mode_reg[0] = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(0)); |
| ipu->cha_db_mode_reg[1] = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(32)); |
| ipu->cha_db_mode_reg[2] = |
| ipu_cm_read(ipu, IPU_ALT_CHA_DB_MODE_SEL(0)); |
| ipu->cha_db_mode_reg[3] = |
| ipu_cm_read(ipu, IPU_ALT_CHA_DB_MODE_SEL(32)); |
| |
| /* save triple buffer select regs */ |
| ipu->cha_trb_mode_reg[0] = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(0)); |
| ipu->cha_trb_mode_reg[1] = ipu_cm_read(ipu, IPU_CHA_TRB_MODE_SEL(32)); |
| |
| /* save idamc sub addr regs */ |
| ipu->idma_sub_addr_reg[0] = ipu_idmac_read(ipu, IDMAC_SUB_ADDR_0); |
| ipu->idma_sub_addr_reg[1] = ipu_idmac_read(ipu, IDMAC_SUB_ADDR_1); |
| ipu->idma_sub_addr_reg[2] = ipu_idmac_read(ipu, IDMAC_SUB_ADDR_2); |
| ipu->idma_sub_addr_reg[3] = ipu_idmac_read(ipu, IDMAC_SUB_ADDR_3); |
| ipu->idma_sub_addr_reg[4] = ipu_idmac_read(ipu, IDMAC_SUB_ADDR_4); |
| |
| /* save sub-modules status and disable all */ |
| ipu->ic_conf_reg = ipu_ic_read(ipu, IC_CONF); |
| ipu_ic_write(ipu, 0, IC_CONF); |
| ipu->ipu_conf_reg = ipu_cm_read(ipu, IPU_CONF); |
| ipu_cm_write(ipu, 0, IPU_CONF); |
| |
| /* save buf ready regs */ |
| ipu->buf_ready_reg[0] = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(0)); |
| ipu->buf_ready_reg[1] = ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(32)); |
| ipu->buf_ready_reg[2] = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(0)); |
| ipu->buf_ready_reg[3] = ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(32)); |
| ipu->buf_ready_reg[4] = ipu_cm_read(ipu, IPU_ALT_CHA_BUF0_RDY(0)); |
| ipu->buf_ready_reg[5] = ipu_cm_read(ipu, IPU_ALT_CHA_BUF0_RDY(32)); |
| ipu->buf_ready_reg[6] = ipu_cm_read(ipu, IPU_ALT_CHA_BUF1_RDY(0)); |
| ipu->buf_ready_reg[7] = ipu_cm_read(ipu, IPU_ALT_CHA_BUF1_RDY(32)); |
| ipu->buf_ready_reg[8] = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(0)); |
| ipu->buf_ready_reg[9] = ipu_cm_read(ipu, IPU_CHA_BUF2_RDY(32)); |
| } |
| |
| if (plat_data->pg) |
| plat_data->pg(1); |
| |
| return 0; |
| } |
| |
| static int ipu_resume(struct platform_device *pdev) |
| { |
| struct imx_ipuv3_platform_data *plat_data = pdev->dev.platform_data; |
| struct ipu_soc *ipu = platform_get_drvdata(pdev); |
| |
| if (plat_data->pg) |
| plat_data->pg(0); |
| |
| if (atomic_read(&ipu->ipu_use_count)) { |
| /* restore buf ready regs */ |
| ipu_cm_write(ipu, ipu->buf_ready_reg[0], IPU_CHA_BUF0_RDY(0)); |
| ipu_cm_write(ipu, ipu->buf_ready_reg[1], IPU_CHA_BUF0_RDY(32)); |
| ipu_cm_write(ipu, ipu->buf_ready_reg[2], IPU_CHA_BUF1_RDY(0)); |
| ipu_cm_write(ipu, ipu->buf_ready_reg[3], IPU_CHA_BUF1_RDY(32)); |
| ipu_cm_write(ipu, ipu->buf_ready_reg[4], IPU_ALT_CHA_BUF0_RDY(0)); |
| ipu_cm_write(ipu, ipu->buf_ready_reg[5], IPU_ALT_CHA_BUF0_RDY(32)); |
| ipu_cm_write(ipu, ipu->buf_ready_reg[6], IPU_ALT_CHA_BUF1_RDY(0)); |
| ipu_cm_write(ipu, ipu->buf_ready_reg[7], IPU_ALT_CHA_BUF1_RDY(32)); |
| ipu_cm_write(ipu, ipu->buf_ready_reg[8], IPU_CHA_BUF2_RDY(0)); |
| ipu_cm_write(ipu, ipu->buf_ready_reg[9], IPU_CHA_BUF2_RDY(32)); |
| |
| /* re-enable sub-modules*/ |
| ipu_cm_write(ipu, ipu->ipu_conf_reg, IPU_CONF); |
| ipu_ic_write(ipu, ipu->ic_conf_reg, IC_CONF); |
| |
| /* restore double buffer select regs */ |
| ipu_cm_write(ipu, ipu->cha_db_mode_reg[0], IPU_CHA_DB_MODE_SEL(0)); |
| ipu_cm_write(ipu, ipu->cha_db_mode_reg[1], IPU_CHA_DB_MODE_SEL(32)); |
| ipu_cm_write(ipu, ipu->cha_db_mode_reg[2], |
| IPU_ALT_CHA_DB_MODE_SEL(0)); |
| ipu_cm_write(ipu, ipu->cha_db_mode_reg[3], |
| IPU_ALT_CHA_DB_MODE_SEL(32)); |
| |
| /* restore triple buffer select regs */ |
| ipu_cm_write(ipu, ipu->cha_trb_mode_reg[0], IPU_CHA_TRB_MODE_SEL(0)); |
| ipu_cm_write(ipu, ipu->cha_trb_mode_reg[1], IPU_CHA_TRB_MODE_SEL(32)); |
| |
| /* restore idamc sub addr regs */ |
| ipu_idmac_write(ipu, ipu->idma_sub_addr_reg[0], IDMAC_SUB_ADDR_0); |
| ipu_idmac_write(ipu, ipu->idma_sub_addr_reg[1], IDMAC_SUB_ADDR_1); |
| ipu_idmac_write(ipu, ipu->idma_sub_addr_reg[2], IDMAC_SUB_ADDR_2); |
| ipu_idmac_write(ipu, ipu->idma_sub_addr_reg[3], IDMAC_SUB_ADDR_3); |
| ipu_idmac_write(ipu, ipu->idma_sub_addr_reg[4], IDMAC_SUB_ADDR_4); |
| |
| /* restart idma channel*/ |
| ipu_idmac_write(ipu, ipu->idma_enable_reg[0], IDMAC_CHA_EN(0)); |
| ipu_idmac_write(ipu, ipu->idma_enable_reg[1], IDMAC_CHA_EN(32)); |
| } else { |
| _ipu_get(ipu); |
| _ipu_dmfc_init(ipu, dmfc_type_setup, 1); |
| _ipu_init_dc_mappings(ipu); |
| /* Set sync refresh channels as high priority */ |
| ipu_idmac_write(ipu, 0x18800001L, IDMAC_CHA_PRI(0)); |
| _ipu_put(ipu); |
| } |
| |
| return 0; |
| } |
| |
| /*! |
| * This structure contains pointers to the power management callback functions. |
| */ |
| static struct platform_driver mxcipu_driver = { |
| .driver = { |
| .name = "imx-ipuv3", |
| }, |
| .probe = ipu_probe, |
| .remove = ipu_remove, |
| .suspend = ipu_suspend, |
| .resume = ipu_resume, |
| }; |
| |
| 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); |