Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

ipu_common.c

/*
 * Copyright 2005-2010 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/clk.h>
#include <linux/io.h>
#include <linux/ipu.h>

#include "ipu_prv.h"
#include "ipu_regs.h"
#include "ipu_param_mem.h"

/*
 * This type definition is used to define a node in the GPIO interrupt queue for
 * registered interrupts for GPIO pins. Each node contains the GPIO signal number
 * associated with the ISR and the actual ISR function pointer.
 */
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;
struct clk *g_ipu_csi_clk;
static struct clk *dfm_clk;
int g_ipu_irq[2];
int g_ipu_hw_rev;
bool g_sec_chan_en[21];
uint32_t g_channel_init_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 uint32_t g_ipu_config = 0;
static uint32_t g_channel_init_mask_backup = 0;
static bool g_csi_used = false;

/* Static functions */
static irqreturn_t ipu_irq_handler(int irq, void *desc);
static void _ipu_pf_init(ipu_channel_params_t * params);
static void _ipu_pf_uninit(void);

inline static uint32_t channel_2_dma(ipu_channel_t ch, ipu_buffer_t type)
{
      return ((type == IPU_INPUT_BUFFER) ? ((uint32_t) ch & 0xFF) :
            ((type == IPU_OUTPUT_BUFFER) ? (((uint32_t) ch >> 8) & 0xFF)
             : (((uint32_t) ch >> 16) & 0xFF)));
};

inline static uint32_t DMAParamAddr(uint32_t dma_ch)
{
      return (0x10000 | (dma_ch << 4));
};

/*!
 * 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 framework.
 *
 * @return      This function returns 0 on success or negative error code on error
 */
static
int ipu_probe(struct platform_device *pdev)
{
//      struct platform_device *pdev = to_platform_device(dev);
      struct mxc_ipu_config *ipu_conf = pdev->dev.platform_data;

      spin_lock_init(&ipu_lock);

      g_ipu_dev = &pdev->dev;
      g_ipu_hw_rev = ipu_conf->rev;

      /* 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, driver_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, driver_name, 0) != 0) {
                  dev_err(g_ipu_dev, "request ERR interrupt failed\n");
                  return -EBUSY;
            }
      }

      /* 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));

      g_ipu_csi_clk = clk_get(&pdev->dev, "csi_clk");

      dfm_clk = clk_get(NULL, "dfm_clk");

      clk_enable(g_ipu_clk);

      /* resetting the CONF register of the IPU */
      __raw_writel(0x00000000, IPU_CONF);

      __raw_writel(0x00100010L, DI_HSP_CLK_PER);

      /* Set SDC refresh channels as high priority */
      __raw_writel(0x0000C000L, IDMAC_CHA_PRI);

      /* Set to max back to back burst requests */
      __raw_writel(0x00000000L, IDMAC_CONF);

      register_ipu_device();

      return 0;
}

/*!
 * This function is called to initialize a logical IPU channel.
 *
 * @param       channel Input parameter for the logical channel ID to initalize.
 *
 * @param       params  Input parameter containing union of channel initialization
 *                      parameters.
 *
 * @return      This function returns 0 on success or negative error code on fail
 */
int32_t ipu_init_channel(ipu_channel_t channel, ipu_channel_params_t * params)
{
      uint32_t ipu_conf;
      uint32_t reg;
      unsigned long lock_flags;

      dev_dbg(g_ipu_dev, "init channel = %d\n", IPU_CHAN_ID(channel));

      if ((channel != MEM_SDC_BG) && (channel != MEM_SDC_FG) &&
          (channel != MEM_ROT_ENC_MEM) && (channel != MEM_ROT_VF_MEM) &&
          (channel != MEM_ROT_PP_MEM) && (channel != CSI_MEM)
          && (params == NULL)) {
            return -EINVAL;
      }

      spin_lock_irqsave(&ipu_lock, lock_flags);

      ipu_conf = __raw_readl(IPU_CONF);
      if (ipu_conf == 0) {
            clk_enable(g_ipu_clk);
      }

      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));
      }

      switch (channel) {
      case CSI_PRP_VF_MEM:
            reg = __raw_readl(IPU_FS_PROC_FLOW);
            __raw_writel(reg & ~FS_VF_IN_VALID, IPU_FS_PROC_FLOW);

            if (params->mem_prp_vf_mem.graphics_combine_en)
                  g_sec_chan_en[IPU_CHAN_ID(channel)] = true;

            _ipu_ic_init_prpvf(params, true);
            break;
      case CSI_PRP_VF_ADC:
            reg = __raw_readl(IPU_FS_PROC_FLOW);
            __raw_writel(reg | (FS_DEST_ADC << FS_PRPVF_DEST_SEL_OFFSET),
                       IPU_FS_PROC_FLOW);

            _ipu_adc_init_channel(CSI_PRP_VF_ADC,
                              params->csi_prp_vf_adc.disp,
                              WriteTemplateNonSeq,
                              params->csi_prp_vf_adc.out_left,
                              params->csi_prp_vf_adc.out_top);

            _ipu_ic_init_prpvf(params, true);
            break;
      case MEM_PRP_VF_MEM:
            reg = __raw_readl(IPU_FS_PROC_FLOW);
            __raw_writel(reg | FS_VF_IN_VALID, IPU_FS_PROC_FLOW);

            if (params->mem_prp_vf_mem.graphics_combine_en)
                  g_sec_chan_en[IPU_CHAN_ID(channel)] = true;

            _ipu_ic_init_prpvf(params, false);
            break;
      case MEM_ROT_VF_MEM:
            _ipu_ic_init_rotate_vf(params);
            break;
      case CSI_PRP_ENC_MEM:
            reg = __raw_readl(IPU_FS_PROC_FLOW);
            __raw_writel(reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW);
            _ipu_ic_init_prpenc(params, true);
            break;
      case MEM_PRP_ENC_MEM:
            reg = __raw_readl(IPU_FS_PROC_FLOW);
            __raw_writel(reg | FS_ENC_IN_VALID, IPU_FS_PROC_FLOW);
            _ipu_ic_init_prpenc(params, false);
            break;
      case MEM_ROT_ENC_MEM:
            _ipu_ic_init_rotate_enc(params);
            break;
      case MEM_PP_ADC:
            reg = __raw_readl(IPU_FS_PROC_FLOW);
            __raw_writel(reg | (FS_DEST_ADC << FS_PP_DEST_SEL_OFFSET),
                       IPU_FS_PROC_FLOW);

            _ipu_adc_init_channel(MEM_PP_ADC, params->mem_pp_adc.disp,
                              WriteTemplateNonSeq,
                              params->mem_pp_adc.out_left,
                              params->mem_pp_adc.out_top);

            if (params->mem_pp_adc.graphics_combine_en)
                  g_sec_chan_en[IPU_CHAN_ID(channel)] = true;

            _ipu_ic_init_pp(params);
            break;
      case MEM_PP_MEM:
            if (params->mem_pp_mem.graphics_combine_en)
                  g_sec_chan_en[IPU_CHAN_ID(channel)] = true;

            _ipu_ic_init_pp(params);
            break;
      case MEM_ROT_PP_MEM:
            _ipu_ic_init_rotate_pp(params);
            break;
      case CSI_MEM:
            _ipu_ic_init_csi(params);
            break;

      case MEM_PF_Y_MEM:
      case MEM_PF_U_MEM:
      case MEM_PF_V_MEM:
            /* Enable PF block */
            _ipu_pf_init(params);
            break;

      case MEM_SDC_BG:
            break;
      case MEM_SDC_FG:
            break;
      case ADC_SYS1:
            _ipu_adc_init_channel(ADC_SYS1, params->adc_sys1.disp,
                              params->adc_sys1.ch_mode,
                              params->adc_sys1.out_left,
                              params->adc_sys1.out_top);
            break;
      case ADC_SYS2:
            _ipu_adc_init_channel(ADC_SYS2, params->adc_sys2.disp,
                              params->adc_sys2.ch_mode,
                              params->adc_sys2.out_left,
                              params->adc_sys2.out_top);
            break;
      default:
            dev_err(g_ipu_dev, "Missing channel initialization\n");
            spin_unlock_irqrestore(&ipu_lock, lock_flags);
            return -EINVAL;
      }

      /* Enable IPU sub module */
      g_channel_init_mask |= 1L << IPU_CHAN_ID(channel);

      if (g_channel_init_mask & 0x00000066L) {  /*CSI */
            ipu_conf |= IPU_CONF_CSI_EN;
            if (cpu_is_mx31() || cpu_is_mx32()) {
                  g_csi_used = true;
            }
      }
      if (g_channel_init_mask & 0x00001FFFL) {  /*IC */
            ipu_conf |= IPU_CONF_IC_EN;
      }
      if (g_channel_init_mask & 0x00000A10L) {  /*ROT */
            ipu_conf |= IPU_CONF_ROT_EN;
      }
      if (g_channel_init_mask & 0x0001C000L) {  /*SDC */
            ipu_conf |= IPU_CONF_SDC_EN | IPU_CONF_DI_EN;
      }
      if (g_channel_init_mask & 0x00061140L) {  /*ADC */
            ipu_conf |= IPU_CONF_ADC_EN | IPU_CONF_DI_EN;
      }
      if (g_channel_init_mask & 0x00380000L) {  /*PF */
            ipu_conf |= IPU_CONF_PF_EN;
      }
      __raw_writel(ipu_conf, IPU_CONF);

      spin_unlock_irqrestore(&ipu_lock, lock_flags);

      return 0;
}

/*!
 * This function is called to uninitialize a logical IPU channel.
 *
 * @param       channel Input parameter for the logical channel ID to uninitalize.
 */
void ipu_uninit_channel(ipu_channel_t channel)
{
      unsigned long lock_flags;
      uint32_t reg;
      uint32_t dma, mask = 0;
      uint32_t ipu_conf;

      spin_lock_irqsave(&ipu_lock, lock_flags);

      if ((g_channel_init_mask & (1L << IPU_CHAN_ID(channel))) == 0) {
            dev_err(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 */
      dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
      if (dma != IDMA_CHAN_INVALID)
            mask |= 1UL << dma;
      dma = channel_2_dma(channel, IPU_INPUT_BUFFER);
      if (dma != IDMA_CHAN_INVALID)
            mask |= 1UL << dma;
      /* Get secondary input dma channel */
      if (g_sec_chan_en[IPU_CHAN_ID(channel)]) {
            dma = channel_2_dma(channel, IPU_SEC_INPUT_BUFFER);
            if (dma != IDMA_CHAN_INVALID) {
                  mask |= 1UL << dma;
            }
      }
      if (mask & __raw_readl(IDMAC_CHA_EN)) {
            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;
      }

      /* Reset the double buffer */
      reg = __raw_readl(IPU_CHA_DB_MODE_SEL);
      __raw_writel(reg & ~mask, IPU_CHA_DB_MODE_SEL);

      g_sec_chan_en[IPU_CHAN_ID(channel)] = false;

      switch (channel) {
      case CSI_MEM:
            _ipu_ic_uninit_csi();
            break;
      case CSI_PRP_VF_ADC:
            reg = __raw_readl(IPU_FS_PROC_FLOW);
            __raw_writel(reg & ~FS_PRPVF_DEST_SEL_MASK, IPU_FS_PROC_FLOW);

            _ipu_adc_uninit_channel(CSI_PRP_VF_ADC);

            /* Fall thru */
      case CSI_PRP_VF_MEM:
      case MEM_PRP_VF_MEM:
            _ipu_ic_uninit_prpvf();
            break;
      case MEM_PRP_VF_ADC:
            break;
      case MEM_ROT_VF_MEM:
            _ipu_ic_uninit_rotate_vf();
            break;
      case CSI_PRP_ENC_MEM:
      case MEM_PRP_ENC_MEM:
            _ipu_ic_uninit_prpenc();
            break;
      case MEM_ROT_ENC_MEM:
            _ipu_ic_uninit_rotate_enc();
            break;
      case MEM_PP_ADC:
            reg = __raw_readl(IPU_FS_PROC_FLOW);
            __raw_writel(reg & ~FS_PP_DEST_SEL_MASK, IPU_FS_PROC_FLOW);

            _ipu_adc_uninit_channel(MEM_PP_ADC);

            /* Fall thru */
      case MEM_PP_MEM:
            _ipu_ic_uninit_pp();
            break;
      case MEM_ROT_PP_MEM:
            _ipu_ic_uninit_rotate_pp();
            break;

      case MEM_PF_Y_MEM:
            _ipu_pf_uninit();
            break;
      case MEM_PF_U_MEM:
      case MEM_PF_V_MEM:
            break;

      case MEM_SDC_BG:
            break;
      case MEM_SDC_FG:
            break;
      case ADC_SYS1:
            _ipu_adc_uninit_channel(ADC_SYS1);
            break;
      case ADC_SYS2:
            _ipu_adc_uninit_channel(ADC_SYS2);
            break;
      case MEM_SDC_MASK:
      case CHAN_NONE:
            break;
      }

      g_channel_init_mask &= ~(1L << IPU_CHAN_ID(channel));

      ipu_conf = __raw_readl(IPU_CONF);
      if ((g_channel_init_mask & 0x00000066L) == 0) { /*CSI */
            ipu_conf &= ~IPU_CONF_CSI_EN;
      }
      if ((g_channel_init_mask & 0x00001FFFL) == 0) { /*IC */
            ipu_conf &= ~IPU_CONF_IC_EN;
      }
      if ((g_channel_init_mask & 0x00000A10L) == 0) { /*ROT */
            ipu_conf &= ~IPU_CONF_ROT_EN;
      }
      if ((g_channel_init_mask & 0x0001C000L) == 0) { /*SDC */
            ipu_conf &= ~IPU_CONF_SDC_EN;
      }
      if ((g_channel_init_mask & 0x00061140L) == 0) { /*ADC */
            ipu_conf &= ~IPU_CONF_ADC_EN;
      }
      if ((g_channel_init_mask & 0x0007D140L) == 0) { /*DI */
            ipu_conf &= ~IPU_CONF_DI_EN;
      }
      if ((g_channel_init_mask & 0x00380000L) == 0) { /*PF */
            ipu_conf &= ~IPU_CONF_PF_EN;
      }
      __raw_writel(ipu_conf, IPU_CONF);
      if (ipu_conf == 0) {
            clk_disable(g_ipu_clk);
      }

      spin_unlock_irqrestore(&ipu_lock, lock_flags);
}

/*!
 * 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       rot_mode        Input parameter for rotation setting of buffer.
 *                              A rotation setting other than \b 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       u                   private u offset for additional cropping,
 *                                              zero if not used.
 *
 * @param       v                   private v offset for additional cropping,
 *                                              zero if not used.
 *
 * @return      This function 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,
                        uint32_t u, uint32_t v)
{
      uint32_t params[10];
      unsigned long lock_flags;
      uint32_t reg;
      uint32_t dma_chan;

      dma_chan = channel_2_dma(channel, type);

      if (stride < width * bytes_per_pixel(pixel_fmt))
            stride = width * bytes_per_pixel(pixel_fmt);

      if (dma_chan == IDMA_CHAN_INVALID)
            return -EINVAL;

      if (stride % 4) {
            dev_err(g_ipu_dev,
                  "Stride must be 32-bit aligned, stride = %d\n", stride);
            return -EINVAL;
      }
      /* IC channels' width must be multiple of 8 pixels     */
      if ((dma_chan <= 13) && (width % 8)) {
            dev_err(g_ipu_dev, "width must be 8 pixel multiple\n");
            return -EINVAL;
      }
      /* Build parameter memory data for DMA channel */
      _ipu_ch_param_set_size(params, pixel_fmt, width, height, stride, u, v);
      _ipu_ch_param_set_buffer(params, phyaddr_0, phyaddr_1);
      _ipu_ch_param_set_rotation(params, rot_mode);
      /* Some channels (rotation) have restriction on burst length */
      if ((dma_chan == 10) || (dma_chan == 11) || (dma_chan == 13)) {
            _ipu_ch_param_set_burst_size(params, 8);
      } else if (dma_chan == 24) {  /* PF QP channel */
            _ipu_ch_param_set_burst_size(params, 4);
      } else if (dma_chan == 25) {  /* PF H264 BS channel */
            _ipu_ch_param_set_burst_size(params, 16);
      } else if (((dma_chan == 14) || (dma_chan == 15)) &&
               pixel_fmt == IPU_PIX_FMT_RGB565) {
            _ipu_ch_param_set_burst_size(params, 16);
      }

      spin_lock_irqsave(&ipu_lock, lock_flags);

      _ipu_write_param_mem(DMAParamAddr(dma_chan), params, 10);

      reg = __raw_readl(IPU_CHA_DB_MODE_SEL);
      if (phyaddr_1) {
            reg |= 1UL << dma_chan;
      } else {
            reg &= ~(1UL << dma_chan);
      }
      __raw_writel(reg, IPU_CHA_DB_MODE_SEL);

      /* Reset to buffer 0 */
      __raw_writel(1UL << dma_chan, IPU_CHA_CUR_BUF);

      spin_unlock_irqrestore(&ipu_lock, lock_flags);

      return 0;
}

/*!
 * 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 which 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;
      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);
            if (reg & (1UL << dma_chan)) {
                  spin_unlock_irqrestore(&ipu_lock, lock_flags);
                  return -EACCES;
            }
            __raw_writel(DMAParamAddr(dma_chan) + 0x0008UL, IPU_IMA_ADDR);
            __raw_writel(phyaddr, IPU_IMA_DATA);
      } else {
            reg = __raw_readl(IPU_CHA_BUF1_RDY);
            if (reg & (1UL << dma_chan)) {
                  spin_unlock_irqrestore(&ipu_lock, lock_flags);
                  return -EACCES;
            }
            __raw_writel(DMAParamAddr(dma_chan) + 0x0009UL, IPU_IMA_ADDR);
            __raw_writel(phyaddr, IPU_IMA_DATA);
      }

      spin_unlock_irqrestore(&ipu_lock, lock_flags);
      dev_dbg(g_ipu_dev, "IPU: update IDMA ch %d buf %d = 0x%08X\n",
            dma_chan, bufNum, phyaddr);
      return 0;
}

/*!
 * 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)
{
      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);

      ret = -EACCES;
      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      This function 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);

      if (dma_chan == IDMA_CHAN_INVALID)
            return -EINVAL;

      if (bufNum == 0) {
            /*Mark buffer 0 as ready. */
            __raw_writel(1UL << dma_chan, IPU_CHA_BUF0_RDY);
      } else {
            /*Mark buffer 1 as ready. */
            __raw_writel(1UL << dma_chan, IPU_CHA_BUF1_RDY);
      }
      return 0;
}

/*!
 * 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)
{
      unsigned long lock_flags;
      uint32_t out_dma;
      uint32_t in_dma;
      bool isProc;
      uint32_t value;
      uint32_t mask;
      uint32_t offset;
      uint32_t fs_proc_flow;
      uint32_t fs_disp_flow;

      spin_lock_irqsave(&ipu_lock, lock_flags);

      fs_proc_flow = __raw_readl(IPU_FS_PROC_FLOW);
      fs_disp_flow = __raw_readl(IPU_FS_DISP_FLOW);

      out_dma = (1UL << channel_2_dma(src_ch, IPU_OUTPUT_BUFFER));
      in_dma = (1UL << channel_2_dma(dest_ch, IPU_INPUT_BUFFER));

      /* PROCESS THE OUTPUT DMA CH */
      switch (out_dma) {
            /*VF-> */
      case IDMA_IC_1:
            pr_debug("Link VF->");
            isProc = true;
            mask = FS_PRPVF_DEST_SEL_MASK;
            offset = FS_PRPVF_DEST_SEL_OFFSET;
            value = (in_dma == IDMA_IC_11) ? FS_DEST_ROT :  /*->VF_ROT */
                (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 :     /* ->ADC1 */
                (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 :     /* ->ADC2 */
                (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG :  /*->SDC_BG */
                (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG :  /*->SDC_FG */
                (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 :     /*->ADC1 */
                /* ->ADCDirect */
                0;
            break;

            /*VF_ROT-> */
      case IDMA_IC_9:
            pr_debug("Link VF_ROT->");
            isProc = true;
            mask = FS_PRPVF_ROT_DEST_SEL_MASK;
            offset = FS_PRPVF_ROT_DEST_SEL_OFFSET;
            value = (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 : /*->ADC1 */
                (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 :     /* ->ADC2 */
                (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG :  /*->SDC_BG */
                (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG :  /*->SDC_FG */
                0;
            break;

            /*ENC-> */
      case IDMA_IC_0:
            pr_debug("Link ENC->");
            isProc = true;
            mask = 0;
            offset = 0;
            value = (in_dma == IDMA_IC_10) ? FS_PRPENC_DEST_SEL : /*->ENC_ROT */
                0;
            break;

            /*PP-> */
      case IDMA_IC_2:
            pr_debug("Link PP->");
            isProc = true;
            mask = FS_PP_DEST_SEL_MASK;
            offset = FS_PP_DEST_SEL_OFFSET;
            value = (in_dma == IDMA_IC_13) ? FS_DEST_ROT :  /*->PP_ROT */
                (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 :     /* ->ADC1 */
                (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 :     /* ->ADC2 */
                (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG :  /*->SDC_BG */
                (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG :  /*->SDC_FG */
                /* ->ADCDirect */
                0;
            break;

            /*PP_ROT-> */
      case IDMA_IC_12:
            pr_debug("Link PP_ROT->");
            isProc = true;
            mask = FS_PP_ROT_DEST_SEL_MASK;
            offset = FS_PP_ROT_DEST_SEL_OFFSET;
            value = (in_dma == IDMA_IC_5) ? FS_DEST_ROT :   /*->PP */
                (in_dma == IDMA_ADC_SYS1_WR) ? FS_DEST_ADC1 :     /* ->ADC1 */
                (in_dma == IDMA_ADC_SYS2_WR) ? FS_DEST_ADC2 :     /* ->ADC2 */
                (in_dma == IDMA_SDC_BG) ? FS_DEST_SDC_BG :  /*->SDC_BG */
                (in_dma == IDMA_SDC_FG) ? FS_DEST_SDC_FG :  /*->SDC_FG */
                0;
            break;

            /*PF-> */
      case IDMA_PF_Y_OUT:
      case IDMA_PF_U_OUT:
      case IDMA_PF_V_OUT:
            pr_debug("Link PF->");
            isProc = true;
            mask = FS_PF_DEST_SEL_MASK;
            offset = FS_PF_DEST_SEL_OFFSET;
            value = (in_dma == IDMA_IC_5) ? FS_PF_DEST_PP :
                (in_dma == IDMA_IC_13) ? FS_PF_DEST_ROT : 0;
            break;

            /* Invalid Chainings: ENC_ROT-> */
      default:
            pr_debug("Link Invalid->");
            value = 0;
            break;

      }

      if (value) {
            if (isProc) {
                  fs_proc_flow &= ~mask;
                  fs_proc_flow |= (value << offset);
            } else {
                  fs_disp_flow &= ~mask;
                  fs_disp_flow |= (value << offset);
            }
      } else {
            dev_err(g_ipu_dev, "Invalid channel chaining %d -> %d\n",
                  out_dma, in_dma);
            return -EINVAL;
      }

      /* PROCESS THE INPUT DMA CH */
      switch (in_dma) {
            /* ->VF_ROT */
      case IDMA_IC_11:
            pr_debug("VF_ROT\n");
            isProc = true;
            mask = 0;
            offset = 0;
            value = (out_dma == IDMA_IC_1) ? FS_PRPVF_ROT_SRC_SEL :     /*VF-> */
                0;
            break;

            /* ->ENC_ROT */
      case IDMA_IC_10:
            pr_debug("ENC_ROT\n");
            isProc = true;
            mask = 0;
            offset = 0;
            value = (out_dma == IDMA_IC_0) ? FS_PRPENC_ROT_SRC_SEL :    /*ENC-> */
                0;
            break;

            /* ->PP */
      case IDMA_IC_5:
            pr_debug("PP\n");
            isProc = true;
            mask = FS_PP_SRC_SEL_MASK;
            offset = FS_PP_SRC_SEL_OFFSET;
            value = (out_dma == IDMA_PF_Y_OUT) ? FS_PP_SRC_PF :   /*PF-> */
                (out_dma == IDMA_PF_U_OUT) ? FS_PP_SRC_PF : /*PF-> */
                (out_dma == IDMA_PF_V_OUT) ? FS_PP_SRC_PF : /*PF-> */
                (out_dma == IDMA_IC_12) ? FS_PP_SRC_ROT :   /*PP_ROT-> */
                0;
            break;

            /* ->PP_ROT */
      case IDMA_IC_13:
            pr_debug("PP_ROT\n");
            isProc = true;
            mask = FS_PP_ROT_SRC_SEL_MASK;
            offset = FS_PP_ROT_SRC_SEL_OFFSET;
            value = (out_dma == IDMA_PF_Y_OUT) ? FS_PP_SRC_PF :   /*PF-> */
                (out_dma == IDMA_PF_U_OUT) ? FS_PP_SRC_PF : /*PF-> */
                (out_dma == IDMA_PF_V_OUT) ? FS_PP_SRC_PF : /*PF-> */
                (out_dma == IDMA_IC_2) ? FS_ROT_SRC_PP :    /*PP-> */
                0;
            break;

            /* ->SDC_BG */
      case IDMA_SDC_BG:
            pr_debug("SDC_BG\n");
            isProc = false;
            mask = FS_SDC_BG_SRC_SEL_MASK;
            offset = FS_SDC_BG_SRC_SEL_OFFSET;
            value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF :      /*VF_ROT-> */
                (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP :   /*PP_ROT-> */
                (out_dma == IDMA_IC_1) ? FS_SRC_VF :  /*VF-> */
                (out_dma == IDMA_IC_2) ? FS_SRC_PP :  /*PP-> */
                0;
            break;

            /* ->SDC_FG */
      case IDMA_SDC_FG:
            pr_debug("SDC_FG\n");
            isProc = false;
            mask = FS_SDC_FG_SRC_SEL_MASK;
            offset = FS_SDC_FG_SRC_SEL_OFFSET;
            value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF :      /*VF_ROT-> */
                (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP :   /*PP_ROT-> */
                (out_dma == IDMA_IC_1) ? FS_SRC_VF :  /*VF-> */
                (out_dma == IDMA_IC_2) ? FS_SRC_PP :  /*PP-> */
                0;
            break;

            /* ->ADC1 */
      case IDMA_ADC_SYS1_WR:
            pr_debug("ADC_SYS1\n");
            isProc = false;
            mask = FS_ADC1_SRC_SEL_MASK;
            offset = FS_ADC1_SRC_SEL_OFFSET;
            value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF :      /*VF_ROT-> */
                (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP :   /*PP_ROT-> */
                (out_dma == IDMA_IC_1) ? FS_SRC_VF :  /*VF-> */
                (out_dma == IDMA_IC_2) ? FS_SRC_PP :  /*PP-> */
                0;
            break;

            /* ->ADC2 */
      case IDMA_ADC_SYS2_WR:
            pr_debug("ADC_SYS2\n");
            isProc = false;
            mask = FS_ADC2_SRC_SEL_MASK;
            offset = FS_ADC2_SRC_SEL_OFFSET;
            value = (out_dma == IDMA_IC_9) ? FS_SRC_ROT_VF :      /*VF_ROT-> */
                (out_dma == IDMA_IC_12) ? FS_SRC_ROT_PP :   /*PP_ROT-> */
                (out_dma == IDMA_IC_1) ? FS_SRC_VF :  /*VF-> */
                (out_dma == IDMA_IC_2) ? FS_SRC_PP :  /*PP-> */
                0;
            break;

            /*Invalid chains: */
            /* ->ENC, ->VF, ->PF, ->VF_COMBINE, ->PP_COMBINE */
      default:
            pr_debug("Invalid\n");
            value = 0;
            break;

      }

      if (value) {
            if (isProc) {
                  fs_proc_flow &= ~mask;
                  fs_proc_flow |= (value << offset);
            } else {
                  fs_disp_flow &= ~mask;
                  fs_disp_flow |= (value << offset);
            }
      } else {
            dev_err(g_ipu_dev, "Invalid channel chaining %d -> %d\n",
                  out_dma, in_dma);
            return -EINVAL;
      }

      __raw_writel(fs_proc_flow, IPU_FS_PROC_FLOW);
      __raw_writel(fs_disp_flow, IPU_FS_DISP_FLOW);

      spin_unlock_irqrestore(&ipu_lock, lock_flags);
      return 0;
}

/*!
 * 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)
{
      unsigned long lock_flags;
      uint32_t out_dma;
      uint32_t in_dma;
      uint32_t fs_proc_flow;
      uint32_t fs_disp_flow;

      spin_lock_irqsave(&ipu_lock, lock_flags);

      fs_proc_flow = __raw_readl(IPU_FS_PROC_FLOW);
      fs_disp_flow = __raw_readl(IPU_FS_DISP_FLOW);

      out_dma = (1UL << channel_2_dma(src_ch, IPU_OUTPUT_BUFFER));
      in_dma = (1UL << channel_2_dma(dest_ch, IPU_INPUT_BUFFER));

      /*clear the src_ch's output destination */
      switch (out_dma) {
            /*VF-> */
      case IDMA_IC_1:
            pr_debug("Unlink VF->");
            fs_proc_flow &= ~FS_PRPVF_DEST_SEL_MASK;
            break;

            /*VF_ROT-> */
      case IDMA_IC_9:
            pr_debug("Unlink VF_Rot->");
            fs_proc_flow &= ~FS_PRPVF_ROT_DEST_SEL_MASK;
            break;

            /*ENC-> */
      case IDMA_IC_0:
            pr_debug("Unlink ENC->");
            fs_proc_flow &= ~FS_PRPENC_DEST_SEL;
            break;

            /*PP-> */
      case IDMA_IC_2:
            pr_debug("Unlink PP->");
            fs_proc_flow &= ~FS_PP_DEST_SEL_MASK;
            break;

            /*PP_ROT-> */
      case IDMA_IC_12:
            pr_debug("Unlink PP_ROT->");
            fs_proc_flow &= ~FS_PP_ROT_DEST_SEL_MASK;
            break;

            /*PF-> */
      case IDMA_PF_Y_OUT:
      case IDMA_PF_U_OUT:
      case IDMA_PF_V_OUT:
            pr_debug("Unlink PF->");
            fs_proc_flow &= ~FS_PF_DEST_SEL_MASK;
            break;

      default:          /*ENC_ROT->     */
            pr_debug("Unlink Invalid->");
            break;
      }

      /*clear the dest_ch's input source */
      switch (in_dma) {
      /*->VF_ROT*/
      case IDMA_IC_11:
            pr_debug("VF_ROT\n");
            fs_proc_flow &= ~FS_PRPVF_ROT_SRC_SEL;
            break;

      /*->Enc_ROT*/
      case IDMA_IC_10:
            pr_debug("ENC_ROT\n");
            fs_proc_flow &= ~FS_PRPENC_ROT_SRC_SEL;
            break;

      /*->PP*/
      case IDMA_IC_5:
            pr_debug("PP\n");
            fs_proc_flow &= ~FS_PP_SRC_SEL_MASK;
            break;

      /*->PP_ROT*/
      case IDMA_IC_13:
            pr_debug("PP_ROT\n");
            fs_proc_flow &= ~FS_PP_ROT_SRC_SEL_MASK;
            break;

      /*->SDC_FG*/
      case IDMA_SDC_FG:
            pr_debug("SDC_FG\n");
            fs_disp_flow &= ~FS_SDC_FG_SRC_SEL_MASK;
            break;

      /*->SDC_BG*/
      case IDMA_SDC_BG:
            pr_debug("SDC_BG\n");
            fs_disp_flow &= ~FS_SDC_BG_SRC_SEL_MASK;
            break;

      /*->ADC1*/
      case IDMA_ADC_SYS1_WR:
            pr_debug("ADC_SYS1\n");
            fs_disp_flow &= ~FS_ADC1_SRC_SEL_MASK;
            break;

      /*->ADC2*/
      case IDMA_ADC_SYS2_WR:
            pr_debug("ADC_SYS2\n");
            fs_disp_flow &= ~FS_ADC2_SRC_SEL_MASK;
            break;

      default:          /*->VF, ->ENC, ->VF_COMBINE, ->PP_COMBINE, ->PF*/
            pr_debug("Invalid\n");
            break;
      }

      __raw_writel(fs_proc_flow, IPU_FS_PROC_FLOW);
      __raw_writel(fs_disp_flow, IPU_FS_DISP_FLOW);

      spin_unlock_irqrestore(&ipu_lock, lock_flags);
      return 0;
}

/*!
 * 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_INPUT_BUFFER);

      reg = __raw_readl(IDMAC_CHA_EN);

      if (reg & ((1UL << out_dma) | (1UL << in_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 in_dma;
      uint32_t sec_dma;
      uint32_t out_dma;
      uint32_t chan_mask = 0;

      spin_lock_irqsave(&ipu_lock, lock_flags);

      reg = __raw_readl(IDMAC_CHA_EN);

      /* Get input and output dma channels */
      out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
      if (out_dma != IDMA_CHAN_INVALID)
            reg |= 1UL << out_dma;
      in_dma = channel_2_dma(channel, IPU_INPUT_BUFFER);
      if (in_dma != IDMA_CHAN_INVALID)
            reg |= 1UL << in_dma;

      /* Get secondary input dma channel */
      if (g_sec_chan_en[IPU_CHAN_ID(channel)]) {
            sec_dma = channel_2_dma(channel, IPU_SEC_INPUT_BUFFER);
            if (sec_dma != IDMA_CHAN_INVALID) {
                  reg |= 1UL << sec_dma;
            }
      }

      __raw_writel(reg | chan_mask, IDMAC_CHA_EN);

      if (IPU_CHAN_ID(channel) <= IPU_CHAN_ID(MEM_PP_ADC)) {
            _ipu_ic_enable_task(channel);
      } else if (channel == MEM_SDC_BG) {
            dev_dbg(g_ipu_dev, "Initializing SDC BG\n");
            _ipu_sdc_bg_init(NULL);
      } else if (channel == MEM_SDC_FG) {
            dev_dbg(g_ipu_dev, "Initializing SDC FG\n");
            _ipu_sdc_fg_init(NULL);
      }

      spin_unlock_irqrestore(&ipu_lock, lock_flags);
      return 0;
}

/*!
 * 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)
{
      /*TODO*/
}

/*!
 * 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 sec_dma;
      uint32_t in_dma;
      uint32_t out_dma;
      uint32_t chan_mask = 0;
      uint32_t timeout;
      uint32_t eof_intr;
      uint32_t enabled;

      /* Get input and output dma channels */
      out_dma = channel_2_dma(channel, IPU_OUTPUT_BUFFER);
      if (out_dma != IDMA_CHAN_INVALID)
            chan_mask = 1UL << out_dma;
      in_dma = channel_2_dma(channel, IPU_INPUT_BUFFER);
      if (in_dma != IDMA_CHAN_INVALID)
            chan_mask |= 1UL << in_dma;
      sec_dma = channel_2_dma(channel, IPU_SEC_INPUT_BUFFER);
      if (sec_dma != IDMA_CHAN_INVALID)
            chan_mask |= 1UL << sec_dma;

      if (wait_for_stop && channel != MEM_SDC_FG && channel != MEM_SDC_BG) {
            timeout = 40;
            while ((__raw_readl(IDMAC_CHA_BUSY) & chan_mask) ||
                   (_ipu_channel_status(channel) == TASK_STAT_ACTIVE)) {
                  timeout--;
                  msleep(10);
                  if (timeout == 0) {
                        printk
                            (KERN_INFO
                             "MXC IPU: Warning - timeout waiting for channel to stop,\n"
                             "\tbuf0_rdy = 0x%08X, buf1_rdy = 0x%08X\n"
                             "\tbusy = 0x%08X, tstat = 0x%08X\n\tmask = 0x%08X\n",
                             __raw_readl(IPU_CHA_BUF0_RDY),
                             __raw_readl(IPU_CHA_BUF1_RDY),
                             __raw_readl(IDMAC_CHA_BUSY),
                             __raw_readl(IPU_TASKS_STAT), chan_mask);
                        break;
                  }
            }
            dev_dbg(g_ipu_dev, "timeout = %d * 10ms\n", 40 - timeout);
      }
      /* SDC BG and FG must be disabled before DMA is disabled */
      if ((channel == MEM_SDC_BG) || (channel == MEM_SDC_FG)) {

            if (channel == MEM_SDC_BG)
                  eof_intr = IPU_IRQ_SDC_BG_EOF;
            else
                  eof_intr = IPU_IRQ_SDC_FG_EOF;

            /* Wait for any buffer flips to finsh */
            timeout = 4;
            while (timeout &&
                   ((__raw_readl(IPU_CHA_BUF0_RDY) & chan_mask) ||
                  (__raw_readl(IPU_CHA_BUF1_RDY) & chan_mask))) {
                  msleep(10);
                  timeout--;
            }

            spin_lock_irqsave(&ipu_lock, lock_flags);
            ipu_clear_irq(eof_intr);
            if (channel == MEM_SDC_BG)
                  enabled = _ipu_sdc_bg_uninit();
            else
                  enabled = _ipu_sdc_fg_uninit();
            spin_unlock_irqrestore(&ipu_lock, lock_flags);

            if (enabled && wait_for_stop) {
                  timeout = 5;
            } else {
                  timeout = 0;
            }
            while (timeout && !ipu_get_irq_status(eof_intr)) {
                  msleep(5);
                  timeout--;
            }
      }

      spin_lock_irqsave(&ipu_lock, lock_flags);

      /* Disable IC task */
      if (IPU_CHAN_ID(channel) <= IPU_CHAN_ID(MEM_PP_ADC)) {
            _ipu_ic_disable_task(channel);
      }

      /* Disable DMA channel(s) */
      reg = __raw_readl(IDMAC_CHA_EN);
      __raw_writel(reg & ~chan_mask, IDMAC_CHA_EN);

      /* Clear DMA related interrupts */
      __raw_writel(chan_mask, IPU_INT_STAT_1);
      __raw_writel(chan_mask, IPU_INT_STAT_2);
      __raw_writel(chan_mask, IPU_INT_STAT_4);

      spin_unlock_irqrestore(&ipu_lock, lock_flags);

      return 0;
}

static
irqreturn_t ipu_irq_handler(int irq, void *desc)
{
      uint32_t line_base = 0;
      uint32_t line;
      irqreturn_t result = IRQ_NONE;
      uint32_t int_stat;

      int_stat = __raw_readl(IPU_INT_STAT_1);
      int_stat &= __raw_readl(IPU_INT_CTRL_1);
      __raw_writel(int_stat, IPU_INT_STAT_1);
      while ((line = ffs(int_stat)) != 0) {
            int_stat &= ~(1UL << (line - 1));
            line += line_base - 1;
            result |=
                ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
      }

      line_base = 32;
      int_stat = __raw_readl(IPU_INT_STAT_2);
      int_stat &= __raw_readl(IPU_INT_CTRL_2);
      __raw_writel(int_stat, IPU_INT_STAT_2);
      while ((line = ffs(int_stat)) != 0) {
            int_stat &= ~(1UL << (line - 1));
            line += line_base - 1;
            result |=
                ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
      }

      line_base = 64;
      int_stat = __raw_readl(IPU_INT_STAT_3);
      int_stat &= __raw_readl(IPU_INT_CTRL_3);
      __raw_writel(int_stat, IPU_INT_STAT_3);
      while ((line = ffs(int_stat)) != 0) {
            int_stat &= ~(1UL << (line - 1));
            line += line_base - 1;
            result |=
                ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
      }

      line_base = 96;
      int_stat = __raw_readl(IPU_INT_STAT_4);
      int_stat &= __raw_readl(IPU_INT_CTRL_4);
      __raw_writel(int_stat, IPU_INT_STAT_4);
      while ((line = ffs(int_stat)) != 0) {
            int_stat &= ~(1UL << (line - 1));
            line += line_base - 1;
            result |=
                ipu_irq_list[line].handler(line, ipu_irq_list[line].dev_id);
      }

      line_base = 128;
      int_stat = __raw_readl(IPU_INT_STAT_5);
      int_stat &= __raw_readl(IPU_INT_CTRL_5);
      __raw_writel(int_stat, IPU_INT_STAT_5);
      while ((line = ffs(int_stat)) != 0) {
            int_stat &= ~(1UL << (line - 1));
            line += line_base - 1;
            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;

      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);
}

/*!
 * 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;

      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);
}

/*!
 * 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)
{
      __raw_writel(IPUIRQ_2_MASK(irq), IPUIRQ_2_STATREG(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 = __raw_readl(IPUIRQ_2_STATREG(irq));

      if (reg & IPUIRQ_2_MASK(irq))
            return true;
      else
            return false;
}

/*!
 * 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,
                  "ipu_request_irq - 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;
}

/*!
 * 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;
      }
}

/*!
 * This function sets the post-filter pause row for h.264 mode.
 *
 * @param       pause_row       The last row to process before pausing.
 *
 * @return      This function returns 0 on success or negative error code on
 *              fail.
 *
 */
int32_t ipu_pf_set_pause_row(uint32_t pause_row)
{
      int32_t retval = 0;
      uint32_t timeout = 5;
      unsigned long lock_flags;
      uint32_t reg;

      reg = __raw_readl(IPU_TASKS_STAT);
      while ((reg & TSTAT_PF_MASK) && ((reg & TSTAT_PF_H264_PAUSE) == 0)) {
            timeout--;
            msleep(5);
            if (timeout == 0) {
                  dev_err(g_ipu_dev, "PF Timeout - tstat = 0x%08X\n",
                        __raw_readl(IPU_TASKS_STAT));
                  retval = -ETIMEDOUT;
                  goto err0;
            }
      }

      spin_lock_irqsave(&ipu_lock, lock_flags);

      reg = __raw_readl(PF_CONF);

      /* Set the pause row */
      if (pause_row) {
            reg &= ~PF_CONF_PAUSE_ROW_MASK;
            reg |= PF_CONF_PAUSE_EN | pause_row << PF_CONF_PAUSE_ROW_SHIFT;
      } else {
            reg &= ~(PF_CONF_PAUSE_EN | PF_CONF_PAUSE_ROW_MASK);
      }
      __raw_writel(reg, PF_CONF);

      spin_unlock_irqrestore(&ipu_lock, lock_flags);
      err0:
      return retval;
}

/* Private functions */
void _ipu_write_param_mem(uint32_t addr, uint32_t * data, uint32_t numWords)
{
      for (; numWords > 0; numWords--) {
            dev_dbg(g_ipu_dev,
                  "write param mem - addr = 0x%08X, data = 0x%08X\n",
                  addr, *data);
            __raw_writel(addr, IPU_IMA_ADDR);
            __raw_writel(*data++, IPU_IMA_DATA);
            addr++;
            if ((addr & 0x7) == 5) {
                  addr &= ~0x7;     /* set to word 0 */
                  addr += 8;  /* increment to next row */
            }
      }
}

static void _ipu_pf_init(ipu_channel_params_t * params)
{
      uint32_t reg;

      /*Setup the type of filtering required */
      switch (params->mem_pf_mem.operation) {
      case PF_MPEG4_DEBLOCK:
      case PF_MPEG4_DERING:
      case PF_MPEG4_DEBLOCK_DERING:
            g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = true;
            g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = false;
            break;
      case PF_H264_DEBLOCK:
            g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = true;
            g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = true;
            break;
      default:
            g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = false;
            g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = false;
            return;
            break;
      }
      reg = params->mem_pf_mem.operation;
      __raw_writel(reg, PF_CONF);
}

static void _ipu_pf_uninit(void)
{
      __raw_writel(0x0L, PF_CONF);
      g_sec_chan_en[IPU_CHAN_ID(MEM_PF_Y_MEM)] = false;
      g_sec_chan_en[IPU_CHAN_ID(MEM_PF_U_MEM)] = false;
}

uint32_t _ipu_channel_status(ipu_channel_t channel)
{
      uint32_t stat = 0;
      uint32_t task_stat_reg = __raw_readl(IPU_TASKS_STAT);

      switch (channel) {
      case CSI_MEM:
            stat =
                (task_stat_reg & TSTAT_CSI2MEM_MASK) >>
                TSTAT_CSI2MEM_OFFSET;
            break;
      case CSI_PRP_VF_ADC:
      case CSI_PRP_VF_MEM:
      case MEM_PRP_VF_ADC:
      case MEM_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 CSI_PRP_ENC_MEM:
      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_ADC:
      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;

      case MEM_PF_Y_MEM:
      case MEM_PF_U_MEM:
      case MEM_PF_V_MEM:
            stat = (task_stat_reg & TSTAT_PF_MASK) >> TSTAT_PF_OFFSET;
            break;
      case MEM_SDC_BG:
            break;
      case MEM_SDC_FG:
            break;
      case ADC_SYS1:
            stat =
                (task_stat_reg & TSTAT_ADCSYS1_MASK) >>
                TSTAT_ADCSYS1_OFFSET;
            break;
      case ADC_SYS2:
            stat =
                (task_stat_reg & TSTAT_ADCSYS2_MASK) >>
                TSTAT_ADCSYS2_OFFSET;
            break;
      case MEM_SDC_MASK:
      default:
            stat = TASK_STAT_IDLE;
            break;
      }
      return stat;
}

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_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_RGB32:
      case IPU_PIX_FMT_ABGR32:
            return 4;
            break;
      default:
            return 1;
            break;
      }
      return 0;
}

void ipu_set_csc_coefficients(ipu_channel_t channel, int32_t param[][3])
{
      /* TODO */
}
EXPORT_SYMBOL(ipu_set_csc_coefficients);

ipu_color_space_t format_to_colorspace(uint32_t fmt)
{
      switch (fmt) {
      case IPU_PIX_FMT_RGB565:
      case IPU_PIX_FMT_BGR24:
      case IPU_PIX_FMT_RGB24:
      case IPU_PIX_FMT_BGR32:
      case IPU_PIX_FMT_RGB32:
            return RGB;
            break;

      default:
            return YCbCr;
            break;
      }
      return RGB;

}

static u32 saved_disp3_time_conf;
static bool in_lpdr_mode;
static struct clk *default_ipu_parent_clk;

int ipu_lowpwr_display_enable(void)
{
      unsigned long rate, div;
      struct clk *parent_clk = g_ipu_clk;

      if (in_lpdr_mode || IS_ERR(dfm_clk)) {
            return -EINVAL;
      }

      if (g_channel_init_mask != (1L << IPU_CHAN_ID(MEM_SDC_BG))) {
            dev_err(g_ipu_dev, "LPDR mode requires only SDC BG active.\n");
            return -EINVAL;
      }

      default_ipu_parent_clk = clk_get_parent(g_ipu_clk);
      in_lpdr_mode = true;

      /* Calculate current pixel clock rate */
      rate = clk_get_rate(g_ipu_clk) * 16;
      saved_disp3_time_conf = __raw_readl(DI_DISP3_TIME_CONF);
      div = saved_disp3_time_conf & 0xFFF;
      rate /= div;
      rate *= 4;        /* min hsp clk is 4x pixel clk */

      /* Initialize DFM clock */
      rate = clk_round_rate(dfm_clk, rate);
      clk_set_rate(dfm_clk, rate);
      clk_enable(dfm_clk);

      /* Wait for next VSYNC */
      __raw_writel(IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC),
                 IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC));
      while ((__raw_readl(IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC)) &
            IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC)) == 0)
            msleep_interruptible(1);

      /* Set display clock divider to divide by 4 */
      __raw_writel(((0x8) << 22) | 0x40, DI_DISP3_TIME_CONF);

      clk_set_parent(parent_clk, dfm_clk);

      return 0;
}

int ipu_lowpwr_display_disable(void)
{
      struct clk *parent_clk = g_ipu_clk;

      if (!in_lpdr_mode || IS_ERR(dfm_clk)) {
            return -EINVAL;
      }

      if (g_channel_init_mask != (1L << IPU_CHAN_ID(MEM_SDC_BG))) {
            dev_err(g_ipu_dev, "LPDR mode requires only SDC BG active.\n");
            return -EINVAL;
      }

      in_lpdr_mode = false;

      /* Wait for next VSYNC */
      __raw_writel(IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC),
                 IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC));
      while ((__raw_readl(IPUIRQ_2_STATREG(IPU_IRQ_SDC_DISP3_VSYNC)) &
            IPUIRQ_2_MASK(IPU_IRQ_SDC_DISP3_VSYNC)) == 0)
            msleep_interruptible(1);

      __raw_writel(saved_disp3_time_conf, DI_DISP3_TIME_CONF);
      clk_set_parent(parent_clk, default_ipu_parent_clk);
      clk_disable(dfm_clk);

      return 0;
}

static int ipu_suspend(struct platform_device *pdev, pm_message_t state)
{
      if (cpu_is_mx31() || cpu_is_mx32()) {
            /* work-around for i.Mx31 SR mode after camera related test */
            if (g_csi_used) {
                  g_ipu_config = __raw_readl(IPU_CONF);
                  clk_enable(g_ipu_csi_clk);
                  __raw_writel(0x51, IPU_CONF);
                  g_channel_init_mask_backup = g_channel_init_mask;
                  g_channel_init_mask |= 2;
            }
      } else if (cpu_is_mx35()) {
            g_ipu_config = __raw_readl(IPU_CONF);
            /* Disable the clock of display otherwise the backlight cannot
             * be close after camera/tvin related test */
            __raw_writel(g_ipu_config & 0xfbf, IPU_CONF);
      }

      return 0;
}

static int ipu_resume(struct platform_device *pdev)
{
      if (cpu_is_mx31() || cpu_is_mx32()) {
            /* work-around for i.Mx31 SR mode after camera related test */
            if (g_csi_used) {
                  __raw_writel(g_ipu_config, IPU_CONF);
                  clk_disable(g_ipu_csi_clk);
                  g_channel_init_mask = g_channel_init_mask_backup;
            }
      } else if (cpu_is_mx35()) {
            __raw_writel(g_ipu_config, IPU_CONF);
    }

      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,
      .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)
{
      if (g_ipu_irq[0])
            free_irq(g_ipu_irq[0], 0);
      if (g_ipu_irq[1])
            free_irq(g_ipu_irq[1], 0);

      platform_driver_unregister(&mxcipu_driver);
}

module_exit(ipu_gen_uninit);

EXPORT_SYMBOL(ipu_init_channel);
EXPORT_SYMBOL(ipu_uninit_channel);
EXPORT_SYMBOL(ipu_init_channel_buffer);
EXPORT_SYMBOL(ipu_unlink_channels);
EXPORT_SYMBOL(ipu_update_channel_buffer);
EXPORT_SYMBOL(ipu_select_buffer);
EXPORT_SYMBOL(ipu_link_channels);
EXPORT_SYMBOL(ipu_enable_channel);
EXPORT_SYMBOL(ipu_disable_channel);
EXPORT_SYMBOL(ipu_enable_irq);
EXPORT_SYMBOL(ipu_disable_irq);
EXPORT_SYMBOL(ipu_clear_irq);
EXPORT_SYMBOL(ipu_get_irq_status);
EXPORT_SYMBOL(ipu_request_irq);
EXPORT_SYMBOL(ipu_free_irq);
EXPORT_SYMBOL(ipu_pf_set_pause_row);
EXPORT_SYMBOL(bytes_per_pixel);

Generated by  Doxygen 1.6.0   Back to index