Files
linux-legacy/drivers/mxc/ipu3/ipu_common.c
Matt Sealey 25a8bf2a03 ENGR00143575 IPUv3:Align IDMAC BS with DMFC FIFO BS
This patch aligns IDMAC burst size and DMFC FIFO burst
size to get better performance and workarounds black
flash issue when playing video on DP-FG with full
screen mode at 1024x768M@60.

Signed-off-by: Liu Ying <Ying.Liu@freescale.com>
(cherry picked from commit 0e81c738566c2c97541dd92a5978c756b5c22e76)
(cherry picked from commit bbf3e59476f8e2b2453bd5a90664514ed3568207)

Conflicts:

	drivers/mxc/ipu3/ipu_prv.h
2011-06-28 17:47:56 -05:00

2792 lines
79 KiB
C

/*
* 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/ipu.h>
#include <linux/clk.h>
#include <mach/clock.h>
#include <mach/hardware.h>
#include <mach/mxc_dvfs.h>
#include "ipu_prv.h"
#include "ipu_regs.h"
#include "ipu_param_mem.h"
struct ipu_irq_node {
irqreturn_t(*handler) (int, void *); /*!< the ISR */
const char *name; /*!< device associated with the interrupt */
void *dev_id; /*!< some unique information for the ISR */
__u32 flags; /*!< not used */
};
/* Globals */
struct clk *g_ipu_clk;
bool g_ipu_clk_enabled;
struct clk *g_di_clk[2];
struct clk *g_pixel_clk[2];
struct clk *g_csi_clk[2];
unsigned char g_dc_di_assignment[10];
ipu_channel_t g_ipu_csi_channel[2];
int g_ipu_irq[2];
int g_ipu_hw_rev;
bool g_sec_chan_en[24];
bool g_thrd_chan_en[24];
bool g_chan_is_interlaced[52];
uint32_t g_channel_init_mask;
uint32_t g_channel_enable_mask;
DEFINE_SPINLOCK(ipu_lock);
struct device *g_ipu_dev;
static struct ipu_irq_node ipu_irq_list[IPU_IRQ_COUNT];
static const char driver_name[] = "mxc_ipu";
static int ipu_dc_use_count;
static int ipu_dp_use_count;
static int ipu_dmfc_use_count;
static int ipu_smfc_use_count;
static int ipu_ic_use_count;
static int ipu_rot_use_count;
static int ipu_vdi_use_count;
static int ipu_di_use_count[2];
static int ipu_csi_use_count[2];
/* Set to the follow using IC direct channel, default non */
static ipu_channel_t using_ic_dirct_ch;
/* for power gating */
static uint32_t ipu_conf_reg;
static uint32_t ic_conf_reg;
static uint32_t ipu_cha_db_mode_reg[4];
static uint32_t ipu_cha_trb_mode_reg[2];
static uint32_t ipu_cha_cur_buf_reg[4];
static uint32_t ipu_cha_triple_cur_buf_reg[4];
static uint32_t idma_sub_addr_reg[5];
static uint32_t idma_enable_reg[2];
static uint32_t buf_ready_reg[10];
u32 *ipu_cm_reg;
u32 *ipu_idmac_reg;
u32 *ipu_dp_reg;
u32 *ipu_ic_reg;
u32 *ipu_dc_reg;
u32 *ipu_dc_tmpl_reg;
u32 *ipu_dmfc_reg;
u32 *ipu_di_reg[2];
u32 *ipu_smfc_reg;
u32 *ipu_csi_reg[2];
u32 *ipu_cpmem_base;
u32 *ipu_tpmem_base;
u32 *ipu_disp_base[2];
u32 *ipu_vdi_reg;
/* 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(reg, dma) (__raw_readl(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 void _ipu_pixel_clk_recalc(struct clk *clk)
{
u32 div = __raw_readl(DI_BS_CLKGEN0(clk->id));
if (div == 0)
clk->rate = 0;
else
clk->rate = (clk_get_rate(clk->parent) * 16) / div;
}
static unsigned long _ipu_pixel_clk_round_rate(struct clk *clk, unsigned long rate)
{
u32 div, div1;
u32 parent_rate = clk_get_rate(clk->parent) * 16;
/*
* Calculate divider
* Fractional part is 4 bits,
* so simply multiply by 2^4 to get fractional part.
*/
div = parent_rate / rate;
if (div < 0x10) /* Min DI disp clock divider is 1 */
div = 0x10;
if (div & ~0xFEF)
div &= 0xFF8;
else {
div1 = div & 0xFE0;
if ((parent_rate / div1 - parent_rate / div) < rate / 4)
div = div1;
else
div &= 0xFF8;
}
return parent_rate / div;
}
static int _ipu_pixel_clk_set_rate(struct clk *clk, unsigned long rate)
{
u32 div = (clk_get_rate(clk->parent) * 16) / rate;
__raw_writel(div, DI_BS_CLKGEN0(clk->id));
/* Setup pixel clock timing */
/* FIXME: needs to be more flexible */
/* Down time is half of period */
__raw_writel((div / 16) << 16, DI_BS_CLKGEN1(clk->id));
clk->rate = (clk->parent->rate * 16) / div;
return 0;
}
static int _ipu_pixel_clk_enable(struct clk *clk)
{
u32 disp_gen = __raw_readl(IPU_DISP_GEN);
disp_gen |= clk->id ? DI1_COUNTER_RELEASE : DI0_COUNTER_RELEASE;
__raw_writel(disp_gen, IPU_DISP_GEN);
start_dvfs_per();
return 0;
}
static void _ipu_pixel_clk_disable(struct clk *clk)
{
u32 disp_gen = __raw_readl(IPU_DISP_GEN);
disp_gen &= clk->id ? ~DI1_COUNTER_RELEASE : ~DI0_COUNTER_RELEASE;
__raw_writel(disp_gen, IPU_DISP_GEN);
start_dvfs_per();
}
static int _ipu_pixel_clk_set_parent(struct clk *clk, struct clk *parent)
{
u32 di_gen = __raw_readl(DI_GENERAL(clk->id));
if (parent == g_ipu_clk)
di_gen &= ~DI_GEN_DI_CLK_EXT;
else if (!IS_ERR(g_di_clk[clk->id]) && parent == g_di_clk[clk->id])
di_gen |= DI_GEN_DI_CLK_EXT;
else
return -EINVAL;
__raw_writel(di_gen, DI_GENERAL(clk->id));
_ipu_pixel_clk_recalc(clk);
return 0;
}
static struct clk pixel_clk[] = {
{
.name = "pixel_clk",
.id = 0,
.recalc = _ipu_pixel_clk_recalc,
.set_rate = _ipu_pixel_clk_set_rate,
.round_rate = _ipu_pixel_clk_round_rate,
.set_parent = _ipu_pixel_clk_set_parent,
.enable = _ipu_pixel_clk_enable,
.disable = _ipu_pixel_clk_disable,
},
{
.name = "pixel_clk",
.id = 1,
.recalc = _ipu_pixel_clk_recalc,
.set_rate = _ipu_pixel_clk_set_rate,
.round_rate = _ipu_pixel_clk_round_rate,
.set_parent = _ipu_pixel_clk_set_parent,
.enable = _ipu_pixel_clk_enable,
.disable = _ipu_pixel_clk_disable,
},
};
/*!
* 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 resource *res;
struct mxc_ipu_config *plat_data = pdev->dev.platform_data;
unsigned long ipu_base;
spin_lock_init(&ipu_lock);
g_ipu_hw_rev = plat_data->rev;
g_ipu_dev = &pdev->dev;
/* Register IPU interrupts */
g_ipu_irq[0] = platform_get_irq(pdev, 0);
if (g_ipu_irq[0] < 0)
return -EINVAL;
if (request_irq(g_ipu_irq[0], ipu_irq_handler, 0, pdev->name, 0) != 0) {
dev_err(g_ipu_dev, "request SYNC interrupt failed\n");
return -EBUSY;
}
/* Some platforms have 2 IPU interrupts */
g_ipu_irq[1] = platform_get_irq(pdev, 1);
if (g_ipu_irq[1] >= 0) {
if (request_irq
(g_ipu_irq[1], ipu_irq_handler, 0, pdev->name, 0) != 0) {
dev_err(g_ipu_dev, "request ERR interrupt failed\n");
return -EBUSY;
}
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (IS_ERR(res))
return -ENODEV;
ipu_base = res->start;
if (g_ipu_hw_rev == 3) /* IPUv3M */
ipu_base += IPUV3M_REG_BASE;
else /* IPUv3D, v3E, v3EX */
ipu_base += IPU_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_disp_base[1] = ioremap(ipu_base + IPU_DISP1_BASE, SZ_4K);
ipu_vdi_reg = ioremap(ipu_base + IPU_VDI_REG_BASE, PAGE_SIZE);
dev_dbg(g_ipu_dev, "IPU VDI Regs = %p\n", ipu_vdi_reg);
dev_dbg(g_ipu_dev, "IPU CM Regs = %p\n", ipu_cm_reg);
dev_dbg(g_ipu_dev, "IPU IC Regs = %p\n", ipu_ic_reg);
dev_dbg(g_ipu_dev, "IPU IDMAC Regs = %p\n", ipu_idmac_reg);
dev_dbg(g_ipu_dev, "IPU DP Regs = %p\n", ipu_dp_reg);
dev_dbg(g_ipu_dev, "IPU DC Regs = %p\n", ipu_dc_reg);
dev_dbg(g_ipu_dev, "IPU DMFC Regs = %p\n", ipu_dmfc_reg);
dev_dbg(g_ipu_dev, "IPU DI0 Regs = %p\n", ipu_di_reg[0]);
dev_dbg(g_ipu_dev, "IPU DI1 Regs = %p\n", ipu_di_reg[1]);
dev_dbg(g_ipu_dev, "IPU SMFC Regs = %p\n", ipu_smfc_reg);
dev_dbg(g_ipu_dev, "IPU CSI0 Regs = %p\n", ipu_csi_reg[0]);
dev_dbg(g_ipu_dev, "IPU CSI1 Regs = %p\n", ipu_csi_reg[1]);
dev_dbg(g_ipu_dev, "IPU CPMem = %p\n", ipu_cpmem_base);
dev_dbg(g_ipu_dev, "IPU TPMem = %p\n", ipu_tpmem_base);
dev_dbg(g_ipu_dev, "IPU DC Template Mem = %p\n", ipu_dc_tmpl_reg);
dev_dbg(g_ipu_dev, "IPU Display Region 1 Mem = %p\n", ipu_disp_base[1]);
g_pixel_clk[0] = &pixel_clk[0];
g_pixel_clk[1] = &pixel_clk[1];
/* Enable IPU and CSI clocks */
/* Get IPU clock freq */
g_ipu_clk = clk_get(&pdev->dev, "ipu_clk");
dev_dbg(g_ipu_dev, "ipu_clk = %lu\n", clk_get_rate(g_ipu_clk));
if (plat_data->reset)
plat_data->reset();
clk_set_parent(g_pixel_clk[0], g_ipu_clk);
clk_set_parent(g_pixel_clk[1], g_ipu_clk);
clk_enable(g_ipu_clk);
g_di_clk[0] = plat_data->di_clk[0];
g_di_clk[1] = plat_data->di_clk[1];
g_csi_clk[0] = plat_data->csi_clk[0];
g_csi_clk[1] = plat_data->csi_clk[1];
__raw_writel(0x807FFFFF, IPU_MEM_RST);
while (__raw_readl(IPU_MEM_RST) & 0x80000000)
;
_ipu_init_dc_mappings();
/* Enable error interrupts by default */
__raw_writel(0xFFFFFFFF, IPU_INT_CTRL(5));
__raw_writel(0xFFFFFFFF, IPU_INT_CTRL(6));
__raw_writel(0xFFFFFFFF, IPU_INT_CTRL(9));
__raw_writel(0xFFFFFFFF, IPU_INT_CTRL(10));
/* DMFC Init */
_ipu_dmfc_init(DMFC_NORMAL, 1);
/* Set sync refresh channels and CSI->mem channel as high priority */
__raw_writel(0x18800001L, IDMAC_CHA_PRI(0));
/* Set MCU_T to divide MCU access window into 2 */
__raw_writel(0x00400000L | (IPU_MCU_T_DEFAULT << 18), IPU_DISP_GEN);
clk_disable(g_ipu_clk);
register_ipu_device();
return 0;
}
int ipu_remove(struct platform_device *pdev)
{
if (g_ipu_irq[0])
free_irq(g_ipu_irq[0], 0);
if (g_ipu_irq[1])
free_irq(g_ipu_irq[1], 0);
clk_put(g_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(void)
{
printk(KERN_DEBUG "IPU_CONF = \t0x%08X\n", __raw_readl(IPU_CONF));
printk(KERN_DEBUG "IDMAC_CONF = \t0x%08X\n", __raw_readl(IDMAC_CONF));
printk(KERN_DEBUG "IDMAC_CHA_EN1 = \t0x%08X\n",
__raw_readl(IDMAC_CHA_EN(0)));
printk(KERN_DEBUG "IDMAC_CHA_EN2 = \t0x%08X\n",
__raw_readl(IDMAC_CHA_EN(32)));
printk(KERN_DEBUG "IDMAC_CHA_PRI1 = \t0x%08X\n",
__raw_readl(IDMAC_CHA_PRI(0)));
printk(KERN_DEBUG "IDMAC_CHA_PRI2 = \t0x%08X\n",
__raw_readl(IDMAC_CHA_PRI(32)));
printk(KERN_DEBUG "IDMAC_BAND_EN1 = \t0x%08X\n",
__raw_readl(IDMAC_BAND_EN(0)));
printk(KERN_DEBUG "IDMAC_BAND_EN2 = \t0x%08X\n",
__raw_readl(IDMAC_BAND_EN(32)));
printk(KERN_DEBUG "IPU_CHA_DB_MODE_SEL0 = \t0x%08X\n",
__raw_readl(IPU_CHA_DB_MODE_SEL(0)));
printk(KERN_DEBUG "IPU_CHA_DB_MODE_SEL1 = \t0x%08X\n",
__raw_readl(IPU_CHA_DB_MODE_SEL(32)));
if (g_ipu_hw_rev >= 2) {
printk(KERN_DEBUG "IPU_CHA_TRB_MODE_SEL0 = \t0x%08X\n",
__raw_readl(IPU_CHA_TRB_MODE_SEL(0)));
printk(KERN_DEBUG "IPU_CHA_TRB_MODE_SEL1 = \t0x%08X\n",
__raw_readl(IPU_CHA_TRB_MODE_SEL(32)));
}
printk(KERN_DEBUG "DMFC_WR_CHAN = \t0x%08X\n",
__raw_readl(DMFC_WR_CHAN));
printk(KERN_DEBUG "DMFC_WR_CHAN_DEF = \t0x%08X\n",
__raw_readl(DMFC_WR_CHAN_DEF));
printk(KERN_DEBUG "DMFC_DP_CHAN = \t0x%08X\n",
__raw_readl(DMFC_DP_CHAN));
printk(KERN_DEBUG "DMFC_DP_CHAN_DEF = \t0x%08X\n",
__raw_readl(DMFC_DP_CHAN_DEF));
printk(KERN_DEBUG "DMFC_IC_CTRL = \t0x%08X\n",
__raw_readl(DMFC_IC_CTRL));
printk(KERN_DEBUG "IPU_FS_PROC_FLOW1 = \t0x%08X\n",
__raw_readl(IPU_FS_PROC_FLOW1));
printk(KERN_DEBUG "IPU_FS_PROC_FLOW2 = \t0x%08X\n",
__raw_readl(IPU_FS_PROC_FLOW2));
printk(KERN_DEBUG "IPU_FS_PROC_FLOW3 = \t0x%08X\n",
__raw_readl(IPU_FS_PROC_FLOW3));
printk(KERN_DEBUG "IPU_FS_DISP_FLOW1 = \t0x%08X\n",
__raw_readl(IPU_FS_DISP_FLOW1));
}
/*!
* This function is called to initialize a logical IPU channel.
*
* @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(ipu_channel_t channel, ipu_channel_params_t *params)
{
int ret = 0;
uint32_t ipu_conf;
uint32_t reg;
unsigned long lock_flags;
dev_dbg(g_ipu_dev, "init channel = %d\n", IPU_CHAN_ID(channel));
/* re-enable error interrupts every time a channel is initialized */
__raw_writel(0xFFFFFFFF, IPU_INT_CTRL(5));
__raw_writel(0xFFFFFFFF, IPU_INT_CTRL(6));
__raw_writel(0xFFFFFFFF, IPU_INT_CTRL(9));
__raw_writel(0xFFFFFFFF, IPU_INT_CTRL(10));
if (g_ipu_clk_enabled == false) {
stop_dvfs_per();
g_ipu_clk_enabled = true;
clk_enable(g_ipu_clk);
}
spin_lock_irqsave(&ipu_lock, lock_flags);
if (g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) {
dev_err(g_ipu_dev, "Warning: channel already initialized %d\n",
IPU_CHAN_ID(channel));
ret = -EINVAL;
goto err;
}
ipu_conf = __raw_readl(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)
g_chan_is_interlaced[channel_2_dma(channel,
IPU_OUTPUT_BUFFER)] = true;
else
g_chan_is_interlaced[channel_2_dma(channel,
IPU_OUTPUT_BUFFER)] = false;
ipu_smfc_use_count++;
g_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(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(channel, 0, params->csi_mem.csi);
}
/*CSI data (include compander) dest*/
_ipu_csi_init(channel, params->csi_mem.csi);
break;
case CSI_PRP_ENC_MEM:
if (params->csi_prp_enc_mem.csi > 1) {
ret = -EINVAL;
goto err;
}
if (using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) {
ret = -EINVAL;
goto err;
}
using_ic_dirct_ch = CSI_PRP_ENC_MEM;
ipu_ic_use_count++;
g_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 = __raw_readl(IPU_FS_PROC_FLOW1);
__raw_writel(reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
/*CSI data (include compander) dest*/
_ipu_csi_init(channel, params->csi_prp_enc_mem.csi);
_ipu_ic_init_prpenc(params, true);
break;
case CSI_PRP_VF_MEM:
if (params->csi_prp_vf_mem.csi > 1) {
ret = -EINVAL;
goto err;
}
if (using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM) {
ret = -EINVAL;
goto err;
}
using_ic_dirct_ch = CSI_PRP_VF_MEM;
ipu_ic_use_count++;
g_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 = __raw_readl(IPU_FS_PROC_FLOW1);
__raw_writel(reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
/*CSI data (include compander) dest*/
_ipu_csi_init(channel, params->csi_prp_vf_mem.csi);
_ipu_ic_init_prpvf(params, true);
break;
case MEM_PRP_VF_MEM:
ipu_ic_use_count++;
reg = __raw_readl(IPU_FS_PROC_FLOW1);
__raw_writel(reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW1);
if (params->mem_prp_vf_mem.graphics_combine_en)
g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
if (params->mem_prp_vf_mem.alpha_chan_en)
g_thrd_chan_en[IPU_CHAN_ID(channel)] = true;
_ipu_ic_init_prpvf(params, false);
break;
case MEM_VDI_PRP_VF_MEM:
if ((using_ic_dirct_ch == CSI_PRP_VF_MEM) ||
(using_ic_dirct_ch == CSI_PRP_ENC_MEM)) {
ret = -EINVAL;
goto err;
}
using_ic_dirct_ch = MEM_VDI_PRP_VF_MEM;
ipu_ic_use_count++;
ipu_vdi_use_count++;
reg = __raw_readl(IPU_FS_PROC_FLOW1);
reg &= ~FS_VDI_SRC_SEL_MASK;
__raw_writel(reg , IPU_FS_PROC_FLOW1);
if (params->mem_prp_vf_mem.graphics_combine_en)
g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
_ipu_ic_init_prpvf(params, false);
_ipu_vdi_init(channel, params);
break;
case MEM_VDI_PRP_VF_MEM_P:
_ipu_vdi_init(channel, params);
break;
case MEM_VDI_PRP_VF_MEM_N:
_ipu_vdi_init(channel, params);
break;
case MEM_ROT_VF_MEM:
ipu_ic_use_count++;
ipu_rot_use_count++;
_ipu_ic_init_rotate_vf(params);
break;
case MEM_PRP_ENC_MEM:
ipu_ic_use_count++;
reg = __raw_readl(IPU_FS_PROC_FLOW1);
__raw_writel(reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW1);
_ipu_ic_init_prpenc(params, false);
break;
case MEM_ROT_ENC_MEM:
ipu_ic_use_count++;
ipu_rot_use_count++;
_ipu_ic_init_rotate_enc(params);
break;
case MEM_PP_MEM:
if (params->mem_pp_mem.graphics_combine_en)
g_sec_chan_en[IPU_CHAN_ID(channel)] = true;
if (params->mem_pp_mem.alpha_chan_en)
g_thrd_chan_en[IPU_CHAN_ID(channel)] = true;
_ipu_ic_init_pp(params);
ipu_ic_use_count++;
break;
case MEM_ROT_PP_MEM:
_ipu_ic_init_rotate_pp(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;
}
g_dc_di_assignment[1] = params->mem_dc_sync.di;
_ipu_dc_init(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)
g_thrd_chan_en[IPU_CHAN_ID(channel)] = true;
g_dc_di_assignment[5] = params->mem_dp_bg_sync.di;
_ipu_dp_init(channel, params->mem_dp_bg_sync.in_pixel_fmt,
params->mem_dp_bg_sync.out_pixel_fmt);
_ipu_dc_init(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(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)
g_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;
}
g_dc_di_assignment[8] = params->direct_async.di;
_ipu_dc_init(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;
}
g_dc_di_assignment[9] = params->direct_async.di;
_ipu_dc_init(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(g_ipu_dev, "Missing channel initialization\n");
ret = -EINVAL;
goto err;
}
/* Enable IPU sub module */
g_channel_init_mask |= 1L << IPU_CHAN_ID(channel);
__raw_writel(ipu_conf, IPU_CONF);
err:
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return ret;
}
EXPORT_SYMBOL(ipu_init_channel);
/*!
* This function is called to uninitialize a logical IPU channel.
*
* @param channel Input parameter for the logical channel ID to uninit.
*/
void ipu_uninit_channel(ipu_channel_t channel)
{
unsigned long lock_flags;
uint32_t reg;
uint32_t in_dma, out_dma = 0;
uint32_t ipu_conf;
spin_lock_irqsave(&ipu_lock, lock_flags);
if ((g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
dev_info(g_ipu_dev, "Channel already uninitialized %d\n",
IPU_CHAN_ID(channel));
spin_unlock_irqrestore(&ipu_lock, lock_flags);
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(IDMAC_CHA_EN, in_dma) ||
idma_is_set(IDMAC_CHA_EN, out_dma)) {
dev_err(g_ipu_dev,
"Channel %d is not disabled, disable first\n",
IPU_CHAN_ID(channel));
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return;
}
ipu_conf = __raw_readl(IPU_CONF);
/* Reset the double buffer */
reg = __raw_readl(IPU_CHA_DB_MODE_SEL(in_dma));
__raw_writel(reg & ~idma_mask(in_dma), IPU_CHA_DB_MODE_SEL(in_dma));
reg = __raw_readl(IPU_CHA_DB_MODE_SEL(out_dma));
__raw_writel(reg & ~idma_mask(out_dma), IPU_CHA_DB_MODE_SEL(out_dma));
/* Reset the triple buffer */
reg = __raw_readl(IPU_CHA_TRB_MODE_SEL(in_dma));
__raw_writel(reg & ~idma_mask(in_dma), IPU_CHA_TRB_MODE_SEL(in_dma));
reg = __raw_readl(IPU_CHA_TRB_MODE_SEL(out_dma));
__raw_writel(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)) {
g_sec_chan_en[IPU_CHAN_ID(channel)] = false;
g_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 (g_ipu_csi_channel[0] == channel) {
g_ipu_csi_channel[0] = CHAN_NONE;
} else if (g_ipu_csi_channel[1] == channel) {
g_ipu_csi_channel[1] = CHAN_NONE;
}
break;
case CSI_PRP_ENC_MEM:
ipu_ic_use_count--;
if (using_ic_dirct_ch == CSI_PRP_ENC_MEM)
using_ic_dirct_ch = 0;
_ipu_ic_uninit_prpenc();
if (g_ipu_csi_channel[0] == channel) {
g_ipu_csi_channel[0] = CHAN_NONE;
} else if (g_ipu_csi_channel[1] == channel) {
g_ipu_csi_channel[1] = CHAN_NONE;
}
break;
case CSI_PRP_VF_MEM:
ipu_ic_use_count--;
if (using_ic_dirct_ch == CSI_PRP_VF_MEM)
using_ic_dirct_ch = 0;
_ipu_ic_uninit_prpvf();
if (g_ipu_csi_channel[0] == channel) {
g_ipu_csi_channel[0] = CHAN_NONE;
} else if (g_ipu_csi_channel[1] == channel) {
g_ipu_csi_channel[1] = CHAN_NONE;
}
break;
case MEM_PRP_VF_MEM:
ipu_ic_use_count--;
_ipu_ic_uninit_prpvf();
reg = __raw_readl(IPU_FS_PROC_FLOW1);
__raw_writel(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 (using_ic_dirct_ch == MEM_VDI_PRP_VF_MEM)
using_ic_dirct_ch = 0;
_ipu_ic_uninit_prpvf();
_ipu_vdi_uninit();
reg = __raw_readl(IPU_FS_PROC_FLOW1);
__raw_writel(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();
break;
case MEM_PRP_ENC_MEM:
ipu_ic_use_count--;
_ipu_ic_uninit_prpenc();
reg = __raw_readl(IPU_FS_PROC_FLOW1);
__raw_writel(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();
break;
case MEM_PP_MEM:
ipu_ic_use_count--;
_ipu_ic_uninit_pp();
break;
case MEM_ROT_PP_MEM:
ipu_rot_use_count--;
ipu_ic_use_count--;
_ipu_ic_uninit_rotate_pp();
break;
case MEM_DC_SYNC:
_ipu_dc_uninit(1);
ipu_di_use_count[g_dc_di_assignment[1]]--;
ipu_dc_use_count--;
ipu_dmfc_use_count--;
break;
case MEM_BG_SYNC:
_ipu_dp_uninit(channel);
_ipu_dc_uninit(5);
ipu_di_use_count[g_dc_di_assignment[5]]--;
ipu_dc_use_count--;
ipu_dp_use_count--;
ipu_dmfc_use_count--;
break;
case MEM_FG_SYNC:
_ipu_dp_uninit(channel);
ipu_dc_use_count--;
ipu_dp_use_count--;
ipu_dmfc_use_count--;
break;
case DIRECT_ASYNC0:
_ipu_dc_uninit(8);
ipu_di_use_count[g_dc_di_assignment[8]]--;
ipu_dc_use_count--;
break;
case DIRECT_ASYNC1:
_ipu_dc_uninit(9);
ipu_di_use_count[g_dc_di_assignment[9]]--;
ipu_dc_use_count--;
break;
default:
break;
}
g_channel_init_mask &= ~(1L << IPU_CHAN_ID(channel));
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;
__raw_writel(ipu_conf, IPU_CONF);
spin_unlock_irqrestore(&ipu_lock, lock_flags);
if (ipu_conf == 0) {
clk_disable(g_ipu_clk);
g_ipu_clk_enabled = false;
}
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 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(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)
{
unsigned long lock_flags;
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(g_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(g_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(g_ipu_dev, "Chan%d doesn't support triple buffer "
"mode\n", dma_chan);
return -EINVAL;
}
if (!phyaddr_1 && phyaddr_2) {
dev_err(g_ipu_dev, "Chan%d's buf1 physical addr is NULL for "
"triple buffer mode\n", dma_chan);
return -EINVAL;
}
/* Build parameter memory data for DMA channel */
_ipu_ch_param_init(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)) &&
(g_thrd_chan_en[IPU_CHAN_ID(channel)] == true)) {
_ipu_ch_param_set_alpha_use_separate_channel(dma_chan, true);
_ipu_ch_param_set_alpha_buffer_memory(dma_chan);
_ipu_ch_param_set_alpha_condition_read(dma_chan);
/* fix alpha width as 8 and burst size as 16*/
_ipu_ch_params_set_alpha_width(dma_chan, 8);
_ipu_ch_param_set_burst_size(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(dma_chan, false);
if (rot_mode)
_ipu_ch_param_set_rotation(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(dma_chan, 16);
else
_ipu_ch_param_set_burst_size(dma_chan, 8);
} else if (_ipu_is_irt_chan(dma_chan)) {
_ipu_ch_param_set_burst_size(dma_chan, 8);
_ipu_ch_param_set_block_mode(dma_chan);
} else if (_ipu_is_dmfc_chan(dma_chan)) {
burst_size = _ipu_ch_param_get_burst_size(dma_chan);
spin_lock_irqsave(&ipu_lock, lock_flags);
_ipu_dmfc_set_wait4eot(dma_chan, width);
_ipu_dmfc_set_burst_size(dma_chan, burst_size);
spin_unlock_irqrestore(&ipu_lock, lock_flags);
}
if (_ipu_chan_is_interlaced(channel) ||
g_chan_is_interlaced[dma_chan]) {
_ipu_ch_param_set_interlaced_scan(dma_chan);
}
if (_ipu_is_ic_chan(dma_chan) || _ipu_is_irt_chan(dma_chan)) {
burst_size = _ipu_ch_param_get_burst_size(dma_chan);
_ipu_ic_idma_init(dma_chan, width, height, burst_size,
rot_mode);
} else if (_ipu_is_smfc_chan(dma_chan)) {
burst_size = _ipu_ch_param_get_burst_size(dma_chan);
if ((pixel_fmt == IPU_PIX_FMT_GENERIC) &&
((_ipu_ch_param_get_bpp(dma_chan) == 5) ||
(_ipu_ch_param_get_bpp(dma_chan) == 3)))
burst_size = burst_size >> 4;
else
burst_size = burst_size >> 2;
_ipu_smfc_set_burst_size(channel, burst_size-1);
}
if (idma_is_set(IDMAC_CHA_PRI, dma_chan) && !cpu_is_mx53())
_ipu_ch_param_set_high_priority(dma_chan);
_ipu_ch_param_dump(dma_chan);
spin_lock_irqsave(&ipu_lock, lock_flags);
if (phyaddr_2 && g_ipu_hw_rev >= 2) {
reg = __raw_readl(IPU_CHA_DB_MODE_SEL(dma_chan));
reg &= ~idma_mask(dma_chan);
__raw_writel(reg, IPU_CHA_DB_MODE_SEL(dma_chan));
reg = __raw_readl(IPU_CHA_TRB_MODE_SEL(dma_chan));
reg |= idma_mask(dma_chan);
__raw_writel(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 */
__raw_writel(0x00444047L, IDMAC_SUB_ADDR_4);
__raw_writel(0x46004241L, IDMAC_SUB_ADDR_3);
__raw_writel(0x00000045L, IDMAC_SUB_ADDR_1);
/* Reset to buffer 0 */
__raw_writel(tri_cur_buf_mask(dma_chan),
IPU_CHA_TRIPLE_CUR_BUF(dma_chan));
} else {
reg = __raw_readl(IPU_CHA_TRB_MODE_SEL(dma_chan));
reg &= ~idma_mask(dma_chan);
__raw_writel(reg, IPU_CHA_TRB_MODE_SEL(dma_chan));
reg = __raw_readl(IPU_CHA_DB_MODE_SEL(dma_chan));
if (phyaddr_1)
reg |= idma_mask(dma_chan);
else
reg &= ~idma_mask(dma_chan);
__raw_writel(reg, IPU_CHA_DB_MODE_SEL(dma_chan));
/* Reset to buffer 0 */
__raw_writel(idma_mask(dma_chan),
IPU_CHA_CUR_BUF(dma_chan));
}
spin_unlock_irqrestore(&ipu_lock, lock_flags);
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 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(ipu_channel_t channel, ipu_buffer_t type,
uint32_t bufNum, dma_addr_t phyaddr)
{
uint32_t reg;
int ret = 0;
unsigned long lock_flags;
uint32_t dma_chan = channel_2_dma(channel, type);
if (dma_chan == IDMA_CHAN_INVALID)
return -EINVAL;
spin_lock_irqsave(&ipu_lock, lock_flags);
if (bufNum == 0)
reg = __raw_readl(IPU_CHA_BUF0_RDY(dma_chan));
else if (bufNum == 1)
reg = __raw_readl(IPU_CHA_BUF1_RDY(dma_chan));
else
reg = __raw_readl(IPU_CHA_BUF2_RDY(dma_chan));
if ((reg & idma_mask(dma_chan)) == 0)
_ipu_ch_param_set_buffer(dma_chan, bufNum, phyaddr);
else
ret = -EACCES;
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return ret;
}
EXPORT_SYMBOL(ipu_update_channel_buffer);
/*!
* This function is called to initialize a buffer for logical IPU channel.
*
* @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(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;
unsigned long lock_flags;
uint32_t dma_chan = channel_2_dma(channel, type);
if (dma_chan == IDMA_CHAN_INVALID)
return -EINVAL;
spin_lock_irqsave(&ipu_lock, lock_flags);
if ((__raw_readl(IPU_CHA_BUF0_RDY(dma_chan)) & idma_mask(dma_chan)) ||
(__raw_readl(IPU_CHA_BUF1_RDY(dma_chan)) & idma_mask(dma_chan)) ||
((__raw_readl(IPU_CHA_BUF2_RDY(dma_chan)) & idma_mask(dma_chan)) &&
(__raw_readl(IPU_CHA_TRB_MODE_SEL(dma_chan)) & idma_mask(dma_chan)) &&
_ipu_is_trb_chan(dma_chan)))
ret = -EACCES;
else
_ipu_ch_offset_update(dma_chan, pixel_fmt, width, height, stride,
u, v, 0, vertical_offset, horizontal_offset);
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return ret;
}
EXPORT_SYMBOL(ipu_update_channel_offset);
/*!
* This function is called to set a channel's buffer as ready.
*
* @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(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;
/* Mark buffer to be ready. */
spin_lock_irqsave(&ipu_lock, lock_flags);
if (bufNum == 0)
__raw_writel(idma_mask(dma_chan),
IPU_CHA_BUF0_RDY(dma_chan));
else if (bufNum == 1)
__raw_writel(idma_mask(dma_chan),
IPU_CHA_BUF1_RDY(dma_chan));
else
__raw_writel(idma_mask(dma_chan),
IPU_CHA_BUF2_RDY(dma_chan));
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return 0;
}
EXPORT_SYMBOL(ipu_select_buffer);
/*!
* This function is called to set a channel's buffer as ready.
*
* @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(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;
/* Mark buffers to be ready. */
spin_lock_irqsave(&ipu_lock, lock_flags);
if (bufNum == 0)
__raw_writel(mask_bit, IPU_CHA_BUF0_RDY(dma_chan));
else
__raw_writel(mask_bit, IPU_CHA_BUF1_RDY(dma_chan));
spin_unlock_irqrestore(&ipu_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 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(ipu_channel_t src_ch, ipu_channel_t dest_ch)
{
int retval = 0;
unsigned long lock_flags;
uint32_t fs_proc_flow1;
uint32_t fs_proc_flow2;
uint32_t fs_proc_flow3;
uint32_t fs_disp_flow1;
spin_lock_irqsave(&ipu_lock, lock_flags);
fs_proc_flow1 = __raw_readl(IPU_FS_PROC_FLOW1);
fs_proc_flow2 = __raw_readl(IPU_FS_PROC_FLOW2);
fs_proc_flow3 = __raw_readl(IPU_FS_PROC_FLOW3);
fs_disp_flow1 = __raw_readl(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;
}
__raw_writel(fs_proc_flow1, IPU_FS_PROC_FLOW1);
__raw_writel(fs_proc_flow2, IPU_FS_PROC_FLOW2);
__raw_writel(fs_proc_flow3, IPU_FS_PROC_FLOW3);
__raw_writel(fs_disp_flow1, IPU_FS_DISP_FLOW1);
err:
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return retval;
}
EXPORT_SYMBOL(ipu_link_channels);
/*!
* This function unlinks 2 channels and disables automatic frame
* synchronization.
*
* @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(ipu_channel_t src_ch, ipu_channel_t dest_ch)
{
int retval = 0;
unsigned long lock_flags;
uint32_t fs_proc_flow1;
uint32_t fs_proc_flow2;
uint32_t fs_proc_flow3;
uint32_t fs_disp_flow1;
spin_lock_irqsave(&ipu_lock, lock_flags);
fs_proc_flow1 = __raw_readl(IPU_FS_PROC_FLOW1);
fs_proc_flow2 = __raw_readl(IPU_FS_PROC_FLOW2);
fs_proc_flow3 = __raw_readl(IPU_FS_PROC_FLOW3);
fs_disp_flow1 = __raw_readl(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;
}
__raw_writel(fs_proc_flow1, IPU_FS_PROC_FLOW1);
__raw_writel(fs_proc_flow2, IPU_FS_PROC_FLOW2);
__raw_writel(fs_proc_flow3, IPU_FS_PROC_FLOW3);
__raw_writel(fs_disp_flow1, IPU_FS_DISP_FLOW1);
err:
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return retval;
}
EXPORT_SYMBOL(ipu_unlink_channels);
/*!
* This function check whether a logical channel was enabled.
*
* @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(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 = __raw_readl(IDMAC_CHA_EN(in_dma));
if (reg & idma_mask(in_dma))
return 1;
reg = __raw_readl(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 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(ipu_channel_t channel)
{
uint32_t reg;
unsigned long lock_flags;
uint32_t ipu_conf;
uint32_t in_dma;
uint32_t out_dma;
uint32_t sec_dma;
uint32_t thrd_dma;
spin_lock_irqsave(&ipu_lock, lock_flags);
if (g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) {
dev_err(g_ipu_dev, "Warning: channel already enabled %d\n",
IPU_CHAN_ID(channel));
spin_unlock_irqrestore(&ipu_lock, lock_flags);
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 = __raw_readl(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;
__raw_writel(ipu_conf, IPU_CONF);
if (idma_is_valid(in_dma)) {
reg = __raw_readl(IDMAC_CHA_EN(in_dma));
__raw_writel(reg | idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
}
if (idma_is_valid(out_dma)) {
reg = __raw_readl(IDMAC_CHA_EN(out_dma));
__raw_writel(reg | idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
}
if ((g_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 = __raw_readl(IDMAC_CHA_EN(sec_dma));
__raw_writel(reg | idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
}
if ((g_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 = __raw_readl(IDMAC_CHA_EN(thrd_dma));
__raw_writel(reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
reg = __raw_readl(IDMAC_SEP_ALPHA);
__raw_writel(reg | idma_mask(sec_dma), IDMAC_SEP_ALPHA);
} else if ((g_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 = __raw_readl(IDMAC_CHA_EN(thrd_dma));
__raw_writel(reg | idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
reg = __raw_readl(IDMAC_SEP_ALPHA);
__raw_writel(reg | idma_mask(in_dma), IDMAC_SEP_ALPHA);
}
if ((channel == MEM_DC_SYNC) || (channel == MEM_BG_SYNC) ||
(channel == MEM_FG_SYNC)) {
reg = __raw_readl(IDMAC_WM_EN(in_dma));
__raw_writel(reg | idma_mask(in_dma), IDMAC_WM_EN(in_dma));
_ipu_dp_dc_enable(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(channel);
g_channel_enable_mask |= 1L << IPU_CHAN_ID(channel);
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return 0;
}
EXPORT_SYMBOL(ipu_enable_channel);
/*!
* This function check buffer ready for a logical channel.
*
* @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(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 = __raw_readl(IPU_CHA_BUF0_RDY(dma_chan));
else if (bufNum == 1)
reg = __raw_readl(IPU_CHA_BUF1_RDY(dma_chan));
else
reg = __raw_readl(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 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(ipu_channel_t channel, ipu_buffer_t type,
uint32_t bufNum)
{
unsigned long lock_flags;
uint32_t dma_ch = channel_2_dma(channel, type);
if (!idma_is_valid(dma_ch))
return;
spin_lock_irqsave(&ipu_lock, lock_flags);
__raw_writel(0xF0300000, IPU_GPR); /* write one to clear */
if (bufNum == 0) {
if (idma_is_set(IPU_CHA_BUF0_RDY, dma_ch)) {
__raw_writel(idma_mask(dma_ch),
IPU_CHA_BUF0_RDY(dma_ch));
}
} else if (bufNum == 1) {
if (idma_is_set(IPU_CHA_BUF1_RDY, dma_ch)) {
__raw_writel(idma_mask(dma_ch),
IPU_CHA_BUF1_RDY(dma_ch));
}
} else {
if (idma_is_set(IPU_CHA_BUF2_RDY, dma_ch)) {
__raw_writel(idma_mask(dma_ch),
IPU_CHA_BUF2_RDY(dma_ch));
}
}
__raw_writel(0x0, IPU_GPR); /* write one to set */
spin_unlock_irqrestore(&ipu_lock, lock_flags);
}
EXPORT_SYMBOL(ipu_clear_buffer_ready);
void _ipu_clear_buffer_ready(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;
__raw_writel(0xF0300000, IPU_GPR); /* write one to clear */
if (bufNum == 0) {
if (idma_is_set(IPU_CHA_BUF0_RDY, dma_ch)) {
__raw_writel(idma_mask(dma_ch),
IPU_CHA_BUF0_RDY(dma_ch));
}
} else if (bufNum == 1) {
if (idma_is_set(IPU_CHA_BUF1_RDY, dma_ch)) {
__raw_writel(idma_mask(dma_ch),
IPU_CHA_BUF1_RDY(dma_ch));
}
} else {
if (idma_is_set(IPU_CHA_BUF2_RDY, dma_ch)) {
__raw_writel(idma_mask(dma_ch),
IPU_CHA_BUF2_RDY(dma_ch));
}
}
__raw_writel(0x0, IPU_GPR); /* write one to set */
}
static irqreturn_t disable_chan_irq_handler(int irq, void *dev_id)
{
struct completion *comp = dev_id;
complete(comp);
return IRQ_HANDLED;
}
/*!
* This function disables a logical channel.
*
* @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(ipu_channel_t channel, bool wait_for_stop)
{
uint32_t reg;
unsigned long lock_flags;
uint32_t in_dma;
uint32_t out_dma;
uint32_t sec_dma = NO_DMA;
uint32_t thrd_dma = NO_DMA;
spin_lock_irqsave(&ipu_lock, lock_flags);
if ((g_channel_enable_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
dev_info(g_ipu_dev, "Channel already disabled %d\n",
IPU_CHAN_ID(channel));
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return -EACCES;
}
spin_unlock_irqrestore(&ipu_lock, lock_flags);
/* 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(IDMAC_CHA_EN, in_dma))
&& (idma_is_valid(out_dma) &&
!idma_is_set(IDMAC_CHA_EN, out_dma)))
return -EINVAL;
if (g_sec_chan_en[IPU_CHAN_ID(channel)])
sec_dma = channel_2_dma(channel, IPU_GRAPH_IN_BUFFER);
if (g_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_set_window_pos(channel, 0, 0);
_ipu_dp_dc_disable(channel, false);
/*
* wait for BG channel EOF then disable FG-IDMAC,
* it avoid FG NFB4EOF error.
*/
if (channel == MEM_FG_SYNC) {
int timeout = 50;
__raw_writel(IPUIRQ_2_MASK(IPU_IRQ_BG_SYNC_EOF),
IPUIRQ_2_STATREG(IPU_IRQ_BG_SYNC_EOF));
while ((__raw_readl(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(g_ipu_dev, "warning: wait for bg sync eof timeout\n");
break;
}
}
}
} else if (wait_for_stop) {
while (idma_is_set(IDMAC_CHA_BUSY, in_dma) ||
idma_is_set(IDMAC_CHA_BUSY, out_dma) ||
(g_sec_chan_en[IPU_CHAN_ID(channel)] &&
idma_is_set(IDMAC_CHA_BUSY, sec_dma)) ||
(g_thrd_chan_en[IPU_CHAN_ID(channel)] &&
idma_is_set(IDMAC_CHA_BUSY, thrd_dma))) {
uint32_t ret, irq = 0xffffffff;
DECLARE_COMPLETION_ONSTACK(disable_comp);
if (idma_is_set(IDMAC_CHA_BUSY, out_dma))
irq = out_dma;
if (g_sec_chan_en[IPU_CHAN_ID(channel)] &&
idma_is_set(IDMAC_CHA_BUSY, sec_dma))
irq = sec_dma;
if (g_thrd_chan_en[IPU_CHAN_ID(channel)] &&
idma_is_set(IDMAC_CHA_BUSY, thrd_dma))
irq = thrd_dma;
if (idma_is_set(IDMAC_CHA_BUSY, in_dma))
irq = in_dma;
if (irq == 0xffffffff) {
dev_err(g_ipu_dev, "warning: no channel busy, break\n");
break;
}
ret = ipu_request_irq(irq, disable_chan_irq_handler, 0, NULL, &disable_comp);
if (ret < 0) {
dev_err(g_ipu_dev, "irq %d in use\n", irq);
break;
} else {
ret = wait_for_completion_timeout(&disable_comp, msecs_to_jiffies(200));
ipu_free_irq(irq, &disable_comp);
if (ret == 0) {
ipu_dump_registers();
dev_err(g_ipu_dev, "warning: disable ipu dma channel %d during its busy state\n", irq);
break;
}
}
}
}
spin_lock_irqsave(&ipu_lock, lock_flags);
if ((channel == MEM_BG_SYNC) || (channel == MEM_FG_SYNC) ||
(channel == MEM_DC_SYNC)) {
reg = __raw_readl(IDMAC_WM_EN(in_dma));
__raw_writel(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(channel);
/* Disable DMA channel(s) */
if (idma_is_valid(in_dma)) {
reg = __raw_readl(IDMAC_CHA_EN(in_dma));
__raw_writel(reg & ~idma_mask(in_dma), IDMAC_CHA_EN(in_dma));
__raw_writel(idma_mask(in_dma), IPU_CHA_CUR_BUF(in_dma));
__raw_writel(tri_cur_buf_mask(in_dma),
IPU_CHA_TRIPLE_CUR_BUF(in_dma));
}
if (idma_is_valid(out_dma)) {
reg = __raw_readl(IDMAC_CHA_EN(out_dma));
__raw_writel(reg & ~idma_mask(out_dma), IDMAC_CHA_EN(out_dma));
__raw_writel(idma_mask(out_dma), IPU_CHA_CUR_BUF(out_dma));
__raw_writel(tri_cur_buf_mask(out_dma),
IPU_CHA_TRIPLE_CUR_BUF(out_dma));
}
if (g_sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) {
reg = __raw_readl(IDMAC_CHA_EN(sec_dma));
__raw_writel(reg & ~idma_mask(sec_dma), IDMAC_CHA_EN(sec_dma));
__raw_writel(idma_mask(sec_dma), IPU_CHA_CUR_BUF(sec_dma));
}
if (g_thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) {
reg = __raw_readl(IDMAC_CHA_EN(thrd_dma));
__raw_writel(reg & ~idma_mask(thrd_dma), IDMAC_CHA_EN(thrd_dma));
if (channel == MEM_BG_SYNC || channel == MEM_FG_SYNC) {
reg = __raw_readl(IDMAC_SEP_ALPHA);
__raw_writel(reg & ~idma_mask(in_dma), IDMAC_SEP_ALPHA);
} else {
reg = __raw_readl(IDMAC_SEP_ALPHA);
__raw_writel(reg & ~idma_mask(sec_dma), IDMAC_SEP_ALPHA);
}
__raw_writel(idma_mask(thrd_dma), IPU_CHA_CUR_BUF(thrd_dma));
}
/* Set channel buffers NOT to be ready */
if (idma_is_valid(in_dma)) {
_ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 0);
_ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 1);
_ipu_clear_buffer_ready(channel, IPU_VIDEO_IN_BUFFER, 2);
}
if (idma_is_valid(out_dma)) {
_ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 0);
_ipu_clear_buffer_ready(channel, IPU_OUTPUT_BUFFER, 1);
}
if (g_sec_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(sec_dma)) {
_ipu_clear_buffer_ready(channel, IPU_GRAPH_IN_BUFFER, 0);
_ipu_clear_buffer_ready(channel, IPU_GRAPH_IN_BUFFER, 1);
}
if (g_thrd_chan_en[IPU_CHAN_ID(channel)] && idma_is_valid(thrd_dma)) {
_ipu_clear_buffer_ready(channel, IPU_ALPHA_IN_BUFFER, 0);
_ipu_clear_buffer_ready(channel, IPU_ALPHA_IN_BUFFER, 1);
}
g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(channel));
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return 0;
}
EXPORT_SYMBOL(ipu_disable_channel);
/*!
* This function enables CSI.
*
* @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(uint32_t csi)
{
uint32_t reg;
unsigned long lock_flags;
if (csi > 1) {
dev_err(g_ipu_dev, "Wrong csi num_%d\n", csi);
return -EINVAL;
}
spin_lock_irqsave(&ipu_lock, lock_flags);
ipu_csi_use_count[csi]++;
if (ipu_csi_use_count[csi] == 1) {
reg = __raw_readl(IPU_CONF);
if (csi == 0)
__raw_writel(reg | IPU_CONF_CSI0_EN, IPU_CONF);
else
__raw_writel(reg | IPU_CONF_CSI1_EN, IPU_CONF);
}
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return 0;
}
EXPORT_SYMBOL(ipu_enable_csi);
/*!
* This function disables CSI.
*
* @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(uint32_t csi)
{
uint32_t reg;
unsigned long lock_flags;
if (csi > 1) {
dev_err(g_ipu_dev, "Wrong csi num_%d\n", csi);
return -EINVAL;
}
spin_lock_irqsave(&ipu_lock, lock_flags);
ipu_csi_use_count[csi]--;
if (ipu_csi_use_count[csi] == 0) {
reg = __raw_readl(IPU_CONF);
if (csi == 0)
__raw_writel(reg & ~IPU_CONF_CSI0_EN, IPU_CONF);
else
__raw_writel(reg & ~IPU_CONF_CSI1_EN, IPU_CONF);
}
spin_unlock_irqrestore(&ipu_lock, lock_flags);
return 0;
}
EXPORT_SYMBOL(ipu_disable_csi);
static irqreturn_t ipu_irq_handler(int irq, void *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 };
for (i = 0;; i++) {
if (err_reg[i] == 0)
break;
int_stat = __raw_readl(IPU_INT_STAT(err_reg[i]));
int_stat &= __raw_readl(IPU_INT_CTRL(err_reg[i]));
if (int_stat) {
__raw_writel(int_stat, IPU_INT_STAT(err_reg[i]));
dev_err(g_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 =
__raw_readl(IPU_INT_CTRL(err_reg[i])) & ~int_stat;
__raw_writel(int_stat, IPU_INT_CTRL(err_reg[i]));
}
}
for (i = 0;; i++) {
if (int_reg[i] == 0)
break;
int_stat = __raw_readl(IPU_INT_STAT(int_reg[i]));
int_stat &= __raw_readl(IPU_INT_CTRL(int_reg[i]));
__raw_writel(int_stat, IPU_INT_STAT(int_reg[i]));
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 irq Interrupt line to enable interrupt for.
*
*/
void ipu_enable_irq(uint32_t irq)
{
uint32_t reg;
unsigned long lock_flags;
if (!g_ipu_clk_enabled)
clk_enable(g_ipu_clk);
spin_lock_irqsave(&ipu_lock, lock_flags);
reg = __raw_readl(IPUIRQ_2_CTRLREG(irq));
reg |= IPUIRQ_2_MASK(irq);
__raw_writel(reg, IPUIRQ_2_CTRLREG(irq));
spin_unlock_irqrestore(&ipu_lock, lock_flags);
if (!g_ipu_clk_enabled)
clk_disable(g_ipu_clk);
}
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 irq Interrupt line to disable interrupt for.
*
*/
void ipu_disable_irq(uint32_t irq)
{
uint32_t reg;
unsigned long lock_flags;
if (!g_ipu_clk_enabled)
clk_enable(g_ipu_clk);
spin_lock_irqsave(&ipu_lock, lock_flags);
reg = __raw_readl(IPUIRQ_2_CTRLREG(irq));
reg &= ~IPUIRQ_2_MASK(irq);
__raw_writel(reg, IPUIRQ_2_CTRLREG(irq));
spin_unlock_irqrestore(&ipu_lock, lock_flags);
if (!g_ipu_clk_enabled)
clk_disable(g_ipu_clk);
}
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 irq Interrupt line to clear interrupt for.
*
*/
void ipu_clear_irq(uint32_t irq)
{
if (!g_ipu_clk_enabled)
clk_enable(g_ipu_clk);
__raw_writel(IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(irq));
if (!g_ipu_clk_enabled)
clk_disable(g_ipu_clk);
}
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 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(uint32_t irq)
{
uint32_t reg;
if (!g_ipu_clk_enabled)
clk_enable(g_ipu_clk);
reg = __raw_readl(IPUIRQ_2_STATREG(irq));
if (!g_ipu_clk_enabled)
clk_disable(g_ipu_clk);
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 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(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);
spin_lock_irqsave(&ipu_lock, lock_flags);
if (ipu_irq_list[irq].handler != NULL) {
dev_err(g_ipu_dev,
"handler already installed on irq %d\n", irq);
spin_unlock_irqrestore(&ipu_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;
spin_unlock_irqrestore(&ipu_lock, lock_flags);
ipu_enable_irq(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 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(uint32_t irq, void *dev_id)
{
ipu_disable_irq(irq); /* disable the interrupt */
if (ipu_irq_list[irq].dev_id == dev_id)
ipu_irq_list[irq].handler = NULL;
}
EXPORT_SYMBOL(ipu_free_irq);
uint32_t ipu_get_cur_buffer_idx(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 = __raw_readl(IPU_CHA_TRB_MODE_SEL(dma_chan));
if ((reg & idma_mask(dma_chan)) && _ipu_is_trb_chan(dma_chan)) {
reg = __raw_readl(IPU_CHA_TRIPLE_CUR_BUF(dma_chan));
return (reg & tri_cur_buf_mask(dma_chan)) >>
tri_cur_buf_shift(dma_chan);
} else {
reg = __raw_readl(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(ipu_channel_t channel)
{
uint32_t stat = 0;
uint32_t task_stat_reg = __raw_readl(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(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);
/* enable target channel */
spin_lock_irqsave(&ipu_lock, lock_flags);
reg = __raw_readl(IDMAC_CHA_EN(to_dma));
__raw_writel(reg | idma_mask(to_dma), IDMAC_CHA_EN(to_dma));
g_channel_enable_mask |= 1L << IPU_CHAN_ID(to_ch);
spin_unlock_irqrestore(&ipu_lock, lock_flags);
/* switch dp dc */
_ipu_dp_dc_disable(from_ch, true);
/* disable source channel */
spin_lock_irqsave(&ipu_lock, lock_flags);
reg = __raw_readl(IDMAC_CHA_EN(from_dma));
__raw_writel(reg & ~idma_mask(from_dma), IDMAC_CHA_EN(from_dma));
__raw_writel(idma_mask(from_dma), IPU_CHA_CUR_BUF(from_dma));
__raw_writel(tri_cur_buf_mask(from_dma),
IPU_CHA_TRIPLE_CUR_BUF(from_dma));
_ipu_clear_buffer_ready(from_ch, IPU_VIDEO_IN_BUFFER, 0);
_ipu_clear_buffer_ready(from_ch, IPU_VIDEO_IN_BUFFER, 1);
_ipu_clear_buffer_ready(from_ch, IPU_VIDEO_IN_BUFFER, 2);
g_channel_enable_mask &= ~(1L << IPU_CHAN_ID(from_ch));
spin_unlock_irqrestore(&ipu_lock, lock_flags);
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;
}
void ipu_set_csc_coefficients(ipu_channel_t channel, int32_t param[][3])
{
_ipu_dp_set_csc_coefficients(channel, param);
}
EXPORT_SYMBOL(ipu_set_csc_coefficients);
static int ipu_suspend(struct platform_device *pdev, pm_message_t state)
{
if (g_ipu_clk_enabled) {
uint32_t chan_should_disable, timeout = 1000, time = 0;
/* save and disable enabled channels*/
idma_enable_reg[0] = __raw_readl(IDMAC_CHA_EN(0));
idma_enable_reg[1] = __raw_readl(IDMAC_CHA_EN(32));
while ((__raw_readl(IDMAC_CHA_BUSY(0)) & idma_enable_reg[0])
|| (__raw_readl(IDMAC_CHA_BUSY(32)) &
idma_enable_reg[1])) {
/* disable channel not busy already */
chan_should_disable =
__raw_readl(IDMAC_CHA_BUSY(0))
^ idma_enable_reg[0];
__raw_writel((~chan_should_disable) &
idma_enable_reg[0], IDMAC_CHA_EN(0));
chan_should_disable =
__raw_readl(IDMAC_CHA_BUSY(1))
^ idma_enable_reg[1];
__raw_writel((~chan_should_disable) &
idma_enable_reg[1], IDMAC_CHA_EN(32));
msleep(2);
time += 2;
if (time >= timeout)
return -1;
}
__raw_writel(0, IDMAC_CHA_EN(0));
__raw_writel(0, IDMAC_CHA_EN(32));
/* save double buffer select regs */
ipu_cha_db_mode_reg[0] = __raw_readl(IPU_CHA_DB_MODE_SEL(0));
ipu_cha_db_mode_reg[1] = __raw_readl(IPU_CHA_DB_MODE_SEL(32));
ipu_cha_db_mode_reg[2] =
__raw_readl(IPU_ALT_CHA_DB_MODE_SEL(0));
ipu_cha_db_mode_reg[3] =
__raw_readl(IPU_ALT_CHA_DB_MODE_SEL(32));
/* save triple buffer select regs */
ipu_cha_trb_mode_reg[0] = __raw_readl(IPU_CHA_TRB_MODE_SEL(0));
ipu_cha_trb_mode_reg[1] = __raw_readl(IPU_CHA_TRB_MODE_SEL(32));
/* save current buffer regs */
ipu_cha_cur_buf_reg[0] = __raw_readl(IPU_CHA_CUR_BUF(0));
ipu_cha_cur_buf_reg[1] = __raw_readl(IPU_CHA_CUR_BUF(32));
ipu_cha_cur_buf_reg[2] = __raw_readl(IPU_ALT_CUR_BUF0);
ipu_cha_cur_buf_reg[3] = __raw_readl(IPU_ALT_CUR_BUF1);
/* save current triple buffer regs */
ipu_cha_triple_cur_buf_reg[0] =
__raw_readl(IPU_CHA_TRIPLE_CUR_BUF(0));
ipu_cha_triple_cur_buf_reg[1] =
__raw_readl(IPU_CHA_TRIPLE_CUR_BUF(32));
ipu_cha_triple_cur_buf_reg[2] =
__raw_readl(IPU_CHA_TRIPLE_CUR_BUF(64));
ipu_cha_triple_cur_buf_reg[3] =
__raw_readl(IPU_CHA_TRIPLE_CUR_BUF(96));
/* save idamc sub addr regs */
idma_sub_addr_reg[0] = __raw_readl(IDMAC_SUB_ADDR_0);
idma_sub_addr_reg[1] = __raw_readl(IDMAC_SUB_ADDR_1);
idma_sub_addr_reg[2] = __raw_readl(IDMAC_SUB_ADDR_2);
idma_sub_addr_reg[3] = __raw_readl(IDMAC_SUB_ADDR_3);
idma_sub_addr_reg[4] = __raw_readl(IDMAC_SUB_ADDR_4);
/* save sub-modules status and disable all */
ic_conf_reg = __raw_readl(IC_CONF);
__raw_writel(0, IC_CONF);
ipu_conf_reg = __raw_readl(IPU_CONF);
__raw_writel(0, IPU_CONF);
/* save buf ready regs */
buf_ready_reg[0] = __raw_readl(IPU_CHA_BUF0_RDY(0));
buf_ready_reg[1] = __raw_readl(IPU_CHA_BUF0_RDY(32));
buf_ready_reg[2] = __raw_readl(IPU_CHA_BUF1_RDY(0));
buf_ready_reg[3] = __raw_readl(IPU_CHA_BUF1_RDY(32));
buf_ready_reg[4] = __raw_readl(IPU_ALT_CHA_BUF0_RDY(0));
buf_ready_reg[5] = __raw_readl(IPU_ALT_CHA_BUF0_RDY(32));
buf_ready_reg[6] = __raw_readl(IPU_ALT_CHA_BUF1_RDY(0));
buf_ready_reg[7] = __raw_readl(IPU_ALT_CHA_BUF1_RDY(32));
buf_ready_reg[8] = __raw_readl(IPU_CHA_BUF2_RDY(0));
buf_ready_reg[9] = __raw_readl(IPU_CHA_BUF2_RDY(32));
}
mxc_pg_enable(pdev);
return 0;
}
static int ipu_resume(struct platform_device *pdev)
{
mxc_pg_disable(pdev);
if (g_ipu_clk_enabled) {
/* restore buf ready regs */
__raw_writel(buf_ready_reg[0], IPU_CHA_BUF0_RDY(0));
__raw_writel(buf_ready_reg[1], IPU_CHA_BUF0_RDY(32));
__raw_writel(buf_ready_reg[2], IPU_CHA_BUF1_RDY(0));
__raw_writel(buf_ready_reg[3], IPU_CHA_BUF1_RDY(32));
__raw_writel(buf_ready_reg[4], IPU_ALT_CHA_BUF0_RDY(0));
__raw_writel(buf_ready_reg[5], IPU_ALT_CHA_BUF0_RDY(32));
__raw_writel(buf_ready_reg[6], IPU_ALT_CHA_BUF1_RDY(0));
__raw_writel(buf_ready_reg[7], IPU_ALT_CHA_BUF1_RDY(32));
__raw_writel(buf_ready_reg[8], IPU_CHA_BUF2_RDY(0));
__raw_writel(buf_ready_reg[9], IPU_CHA_BUF2_RDY(32));
/* re-enable sub-modules*/
__raw_writel(ipu_conf_reg, IPU_CONF);
__raw_writel(ic_conf_reg, IC_CONF);
/* restore double buffer select regs */
__raw_writel(ipu_cha_db_mode_reg[0], IPU_CHA_DB_MODE_SEL(0));
__raw_writel(ipu_cha_db_mode_reg[1], IPU_CHA_DB_MODE_SEL(32));
__raw_writel(ipu_cha_db_mode_reg[2],
IPU_ALT_CHA_DB_MODE_SEL(0));
__raw_writel(ipu_cha_db_mode_reg[3],
IPU_ALT_CHA_DB_MODE_SEL(32));
/* restore triple buffer select regs */
__raw_writel(ipu_cha_trb_mode_reg[0], IPU_CHA_TRB_MODE_SEL(0));
__raw_writel(ipu_cha_trb_mode_reg[1], IPU_CHA_TRB_MODE_SEL(32));
/* restore current buffer select regs */
__raw_writel(~(ipu_cha_cur_buf_reg[0]), IPU_CHA_CUR_BUF(0));
__raw_writel(~(ipu_cha_cur_buf_reg[1]), IPU_CHA_CUR_BUF(32));
__raw_writel(~(ipu_cha_cur_buf_reg[2]), IPU_ALT_CUR_BUF0);
__raw_writel(~(ipu_cha_cur_buf_reg[3]), IPU_ALT_CUR_BUF1);
/* restore triple current buffer select regs */
__raw_writel(~(ipu_cha_triple_cur_buf_reg[0]),
IPU_CHA_TRIPLE_CUR_BUF(0));
__raw_writel(~(ipu_cha_triple_cur_buf_reg[1]),
IPU_CHA_TRIPLE_CUR_BUF(32));
__raw_writel(~(ipu_cha_triple_cur_buf_reg[2]),
IPU_CHA_TRIPLE_CUR_BUF(64));
__raw_writel(~(ipu_cha_triple_cur_buf_reg[3]),
IPU_CHA_TRIPLE_CUR_BUF(96));
/* restore idamc sub addr regs */
__raw_writel(idma_sub_addr_reg[0], IDMAC_SUB_ADDR_0);
__raw_writel(idma_sub_addr_reg[1], IDMAC_SUB_ADDR_1);
__raw_writel(idma_sub_addr_reg[2], IDMAC_SUB_ADDR_2);
__raw_writel(idma_sub_addr_reg[3], IDMAC_SUB_ADDR_3);
__raw_writel(idma_sub_addr_reg[4], IDMAC_SUB_ADDR_4);
/* restart idma channel*/
__raw_writel(idma_enable_reg[0], IDMAC_CHA_EN(0));
__raw_writel(idma_enable_reg[1], IDMAC_CHA_EN(32));
} else {
clk_enable(g_ipu_clk);
_ipu_dmfc_init(dmfc_type_setup, 1);
_ipu_init_dc_mappings();
/* Set sync refresh channels as high priority */
__raw_writel(0x18800001L, IDMAC_CHA_PRI(0));
clk_disable(g_ipu_clk);
}
return 0;
}
/*!
* This structure contains pointers to the power management callback functions.
*/
static struct platform_driver mxcipu_driver = {
.driver = {
.name = "mxc_ipu",
},
.probe = ipu_probe,
.remove = ipu_remove,
.suspend_late = ipu_suspend,
.resume_early = 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);