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

mx27_pp.c

Go to the documentation of this file.
/*
 * Copyright 2005-2007 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 mx27_pp.c
 *
 * @brief MX27 V4L2 Video Output Driver
 *
 * Video4Linux2 Output Device using MX27 eMMA Post-processing functionality.
 *
 * @ingroup MXC_V4L2_OUTPUT
 */
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/module.h>
#include <linux/fb.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <asm/io.h>

#include "mx27_pp.h"
#include "mxc_v4l2_output.h"

#define SCALE_RETRY     32    /* to be more relax, less precise */
#define PP_SKIP         1
#define PP_TBL_MAX      40

static unsigned short scale_tbl[PP_TBL_MAX];
static int g_hlen, g_vlen;

static emma_pp_cfg g_pp_cfg;
static int g_disp_num = 0;
static char pp_dev[] = "emma_pp";

/*!
 * @brief PP resizing routines
 */
static int gcd(int x, int y);
static int ratio(int x, int y, int *den);
static int scale_0d(int k, int coeff, int base, int nxt);
static int scale_1d(int inv, int outv, int k);
static int scale_1d_smart(int *inv, int *outv, int index);
static int scale_2d(emma_pp_scale * sz);

static irqreturn_t pp_isr(int irq, void *dev_id);
static int set_output_addr(emma_pp_cfg * cfg, vout_data * vout);
static int pphw_reset(void);
static int pphw_enable(int flag);
static int pphw_ptr(emma_pp_cfg * cfg);
static int pphw_outptr(emma_pp_cfg * cfg);
static int pphw_cfg(emma_pp_cfg * cfg);
static int pphw_isr(void);
static void pphw_init(void);
static void pphw_exit(void);

#define PP_DUMP(reg)    pr_debug("%s\t = 0x%08X\n", #reg, __raw_readl(reg))
void pp_dump(void)
{
      PP_DUMP(PP_CNTL);
      PP_DUMP(PP_INTRCNTL);
      PP_DUMP(PP_INTRSTATUS);
      PP_DUMP(PP_SOURCE_Y_PTR);
      PP_DUMP(PP_SOURCE_CB_PTR);
      PP_DUMP(PP_SOURCE_CR_PTR);
      PP_DUMP(PP_DEST_RGB_PTR);
      PP_DUMP(PP_QUANTIZER_PTR);
      PP_DUMP(PP_PROCESS_FRAME_PARA);
      PP_DUMP(PP_SOURCE_FRAME_WIDTH);
      PP_DUMP(PP_DEST_DISPLAY_WIDTH);
      PP_DUMP(PP_DEST_IMAGE_SIZE);
      PP_DUMP(PP_DEST_FRAME_FMT_CNTL);
      PP_DUMP(PP_RESIZE_INDEX);
      PP_DUMP(PP_CSC_COEF_0123);
      PP_DUMP(PP_CSC_COEF_4);
}

/*!
 * @brief Set PP input address.
 * @param ptr     The pointer to the Y value of input
 * @return  Zero on success, others on failure
 */
00092 int pp_ptr(unsigned long ptr)
{
      g_pp_cfg.ptr.y = ptr;
      g_pp_cfg.ptr.u = g_pp_cfg.ptr.v = g_pp_cfg.ptr.qp = 0;

      return pphw_ptr(&g_pp_cfg);
}

/*!
 * @brief Enable or disable PP.
 * @param flag    Zero to disable PP, others to enable PP
 * @return  Zero on success, others on failure
 */
00105 int pp_enable(int flag)
{
      return pphw_enable(flag);
}

/*!
 * @brief Get the display No. of last completed PP frame.
 * @return  The display No. of last completed PP frame.
 */
00114 int pp_num_last(void)
{
      return (g_disp_num ? 0 : 1);
}

/*!
 * @brief Initialize PP.
 * @param vout    Pointer to _vout_data structure
 * @return  Zero on success, others on failure
 */
00124 int pp_init(vout_data * vout)
{
      pphw_init();
      pphw_enable(0);
      enable_irq(MXC_INT_EMMAPP);
      return request_irq(MXC_INT_EMMAPP, pp_isr, 0, pp_dev, vout);
}

/*!
 * @brief Deinitialize PP.
 * @param vout    Pointer to _vout_data structure
 */
00136 void pp_exit(vout_data * vout)
{
      disable_irq(MXC_INT_EMMAPP);
      free_irq(MXC_INT_EMMAPP, vout);
      pphw_enable(0);
      pphw_exit();
}

/*!
 * @brief Configure PP.
 * @param vout    Pointer to _vout_data structure
 * @return  Zero on success, others on failure
 */
00149 int pp_cfg(vout_data * vout)
{
      if (!vout)
            return -1;

      /* PP accepts YUV420 input only */
      if (vout->v2f.fmt.pix.pixelformat != V4L2_PIX_FMT_YUV420) {
            pr_debug("unsupported pixel format.\n");
            return -1;
      }

      g_pp_cfg.operation = 0;

      memset(g_pp_cfg.csc_table, 0, sizeof(g_pp_cfg.csc_table));

      /* Convert output pixel format to PP required format */
      switch (vout->v4l2_fb.fmt.pixelformat) {
      case V4L2_PIX_FMT_BGR32:
            g_pp_cfg.red_width = 8;
            g_pp_cfg.green_width = 8;
            g_pp_cfg.blue_width = 8;
            g_pp_cfg.red_offset = 8;
            g_pp_cfg.green_offset = 16;
            g_pp_cfg.blue_offset = 24;
            g_pp_cfg.rgb_resolution = 32;
            break;
      case V4L2_PIX_FMT_RGB32:
            g_pp_cfg.red_width = 8;
            g_pp_cfg.green_width = 8;
            g_pp_cfg.blue_width = 8;
            g_pp_cfg.red_offset = 24;
            g_pp_cfg.green_offset = 16;
            g_pp_cfg.blue_offset = 8;
            g_pp_cfg.rgb_resolution = 32;
            break;
      case V4L2_PIX_FMT_YUYV:
            g_pp_cfg.red_width = 0;
            g_pp_cfg.green_width = 0;
            g_pp_cfg.blue_width = 0;
            g_pp_cfg.red_offset = 0;
            g_pp_cfg.green_offset = 0;
            g_pp_cfg.blue_offset = PP_PIX_YUYV;
            g_pp_cfg.rgb_resolution = 16;
            break;
      case V4L2_PIX_FMT_UYVY:
            g_pp_cfg.red_width = 0;
            g_pp_cfg.green_width = 0;
            g_pp_cfg.blue_width = 0;
            g_pp_cfg.red_offset = 0;
            g_pp_cfg.green_offset = 0;
            g_pp_cfg.blue_offset = PP_PIX_UYVY;
            g_pp_cfg.rgb_resolution = 16;
            break;
      case V4L2_PIX_FMT_RGB565:
      default:
            g_pp_cfg.red_width = 5;
            g_pp_cfg.green_width = 6;
            g_pp_cfg.blue_width = 5;
            g_pp_cfg.red_offset = 11;
            g_pp_cfg.green_offset = 5;
            g_pp_cfg.blue_offset = 0;
            g_pp_cfg.rgb_resolution = 16;
            break;
      }

      if (vout->ipu_buf[0] != -1)
            g_pp_cfg.ptr.y =
                (unsigned int)vout->queue_buf_paddr[vout->ipu_buf[0]];
      else
            g_pp_cfg.ptr.y = 0;

      g_pp_cfg.ptr.u = g_pp_cfg.ptr.v = g_pp_cfg.ptr.qp = 0;

      g_pp_cfg.dim.in.width = vout->v2f.fmt.pix.width;
      g_pp_cfg.dim.in.height = vout->v2f.fmt.pix.height;
      g_pp_cfg.dim.out.width = vout->crop_current.width;
      g_pp_cfg.dim.out.height = vout->crop_current.height;
      g_pp_cfg.dim.num.width = 0;
      g_pp_cfg.dim.num.height = 0;
      g_pp_cfg.dim.den.width = 0;
      g_pp_cfg.dim.den.height = 0;

      if (scale_2d(&g_pp_cfg.dim)) {
            pr_debug("unsupported resize ratio.\n");
            return -1;
      }

      g_pp_cfg.dim.out.width = vout->crop_current.width;
      g_pp_cfg.dim.out.height = vout->crop_current.height;

      g_pp_cfg.in_y_stride = 0;
      if (set_output_addr(&g_pp_cfg, vout)) {
            pr_debug("failed to set pp output address.\n");
            return -1;
      }

      return pphw_cfg(&g_pp_cfg);
}

irqreturn_t mxc_v4l2out_pp_in_irq_handler(int irq, void *dev_id);

/*!
 * @brief PP IRQ handler.
 */
00253 static irqreturn_t pp_isr(int irq, void *dev_id)
{
      int status;
      vout_data *vout = dev_id;

      status = pphw_isr();
      if ((status & 0x1) == 0) {    /* Not frame complete interrupt */
            pr_debug("not pp frame complete interrupt\n");
            return IRQ_HANDLED;
      }

      if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
            g_disp_num = g_disp_num ? 0 : 1;
            g_pp_cfg.outptr = (unsigned int)vout->display_bufs[g_disp_num];
            pphw_outptr(&g_pp_cfg);
      }

      return mxc_v4l2out_pp_in_irq_handler(irq, dev_id);
}

/*!
 * @brief Set PP output address.
 * @param cfg     Pointer to emma_pp_cfg structure
 * @param vout    Pointer to _vout_data structure
 * @return  Zero on success, others on failure
 */
00279 static int set_output_addr(emma_pp_cfg * cfg, vout_data * vout)
{
      if (vout->v4l2_fb.flags == V4L2_FBUF_FLAG_OVERLAY) {
            g_disp_num = 0;
            cfg->outptr = (unsigned int)vout->display_bufs[g_disp_num];
            cfg->out_stride = vout->crop_current.width;
            return 0;
      } else {
            struct fb_info *fb;

            fb = registered_fb[vout->output_fb_num[vout->cur_disp_output]];
            if (!fb)
                  return -1;

            cfg->outptr = fb->fix.smem_start;
            cfg->outptr += vout->crop_current.top * fb->var.xres_virtual
                * (fb->var.bits_per_pixel >> 3)
                + vout->crop_current.left * (fb->var.bits_per_pixel >> 3);
            cfg->out_stride = fb->var.xres_virtual;

            return 0;
      }
}

/*!
 * @brief Get maximum common divisor.
 * @param x First input value
 * @param y Second input value
 * @return  Maximum common divisor of x and y
 */
00309 static int gcd(int x, int y)
{
      int k;

      if (x < y) {
            k = x;
            x = y;
            y = k;
      }

      while ((k = x % y)) {
            x = y;
            y = k;
      }

      return y;
}

/*!
 * @brief Get ratio.
 * @param x First input value
 * @param y Second input value
 * @param den     Denominator of the ratio (corresponding to y)
 * @return  Numerator of the ratio (corresponding to x)
 */
00334 static int ratio(int x, int y, int *den)
{
      int g;

      if (!x || !y)
            return 0;

      g = gcd(x, y);
      *den = y / g;

      return x / g;
}

/*!
 * @brief Build PP coefficient entry
 * Build one or more coefficient entries for PP coefficient table based
 * on given coefficient.
 *
 * @param k The index of the coefficient in coefficient table
 * @param coeff   The weighting coefficient
 * @param base    The base of the coefficient
 * @param nxt     Number of pixels to be read
 *
 * @return  The index of the next coefficient entry on success
 *          -1 on failure
 */
00360 static int scale_0d(int k, int coeff, int base, int nxt)
{
      if (k >= PP_TBL_MAX) {
            /* no more space in table */
            pr_debug("no space in scale table, k = %d\n", k);
            return -1;
      }

      coeff = ((coeff << BC_COEF) + (base >> 1)) / base;

      /*
       * Valid values for weighting coefficient are 0, 2 to 30, and 31.
       * A value of 31 is treated as 32 and therefore 31 is an
       * invalid co-efficient.
       */
      if (coeff >= SZ_COEF - 1)
            coeff--;
      else if (coeff == 1)
            coeff++;
      coeff = coeff << BC_NXT;

      if (nxt < SZ_NXT) {
            coeff |= nxt;
            coeff <<= 1;
            coeff |= 1;
      } else {
            /*
             * src inc field is 2 bit wide, for 4+, use special
             * code 0:0:1 to prevent dest inc
             */
            coeff |= PP_SKIP;
            coeff <<= 1;
            coeff |= 1;
            nxt -= PP_SKIP;
            do {
                  pr_debug("tbl = %03X\n", coeff);
                  scale_tbl[k++] = coeff;
                  coeff = (nxt > PP_SKIP) ? PP_SKIP : nxt;
                  coeff <<= 1;
            } while ((nxt -= PP_SKIP) > 0);
      }
      pr_debug("tbl = %03X\n", coeff);
      scale_tbl[k++] = coeff;

      return k;
}

/*
 * @brief Build PP coefficient table
 * Build PP coefficient table for one dimension (width or height)
 * based on given input and output resolution
 *
 * @param inv     input resolution
 * @param outv    output resolution
 * @param k index of free table entry
 *
 * @return  The index of the next free coefficient entry on success
 *          -1 on failure
 */
static int scale_1d(int inv, int outv, int k)
{
      int v;                  /* overflow counter */
      int coeff, nxt;         /* table output */

      if (inv == outv)
            return scale_0d(k, 1, 1, 1);  /* force scaling */

      if (inv * 4 < outv) {
            pr_debug("upscale err: ratio should be in range 1:1 to 1:4\n");
            return -1;
      }

      v = 0;
      if (inv < outv) {
            /* upscale: mix <= 2 input pixels per output pixel */
            do {
                  coeff = outv - v;
                  v += inv;
                  if (v >= outv) {
                        v -= outv;
                        nxt = 1;
                  } else
                        nxt = 0;
                  pr_debug("upscale: coeff = %d/%d nxt = %d\n", coeff,
                         outv, nxt);
                  k = scale_0d(k, coeff, outv, nxt);
                  if (k < 0)
                        return -1;
            } while (v);
      } else if (inv >= 2 * outv) {
            /* PP doesn't support resize ratio > 2:1 except 4:1. */
            if ((inv != 2 * outv) && (inv != 4 * outv))
                  return -1;
            /* downscale: >=2:1 bilinear approximation */
            coeff = inv - 2 * outv;
            v = 0;
            nxt = 0;
            do {
                  v += coeff;
                  nxt = 2;
                  while (v >= outv) {
                        v -= outv;
                        nxt++;
                  }
                  pr_debug("downscale: coeff = 1/2 nxt = %d\n", nxt);
                  k = scale_0d(k, 1, 2, nxt);
                  if (k < 0)
                        return -1;
            } while (v);
      } else {
            /* downscale: bilinear */
            int in_pos_inc = 2 * outv;
            int out_pos = inv;
            int out_pos_inc = 2 * inv;
            int init_carry = inv - outv;
            int carry = init_carry;

            v = outv + in_pos_inc;
            do {
                  coeff = v - out_pos;
                  out_pos += out_pos_inc;
                  carry += out_pos_inc;
                  for (nxt = 0; v < out_pos; nxt++) {
                        v += in_pos_inc;
                        carry -= in_pos_inc;
                  }
                  pr_debug("downscale: coeff = %d/%d nxt = %d\n", coeff,
                         in_pos_inc, nxt);
                  k = scale_0d(k, coeff, in_pos_inc, nxt);
                  if (k < 0)
                        return -1;
            } while (carry != init_carry);
      }
      return k;
}

/*
 * @brief Build PP coefficient table
 * Build PP coefficient table for one dimension (width or height)
 * based on given input and output resolution. The given input
 * and output resolution might be not supported due to hardware
 * limits. In this case this functin rounds the input and output
 * to closest possible values and return them to caller.
 *
 * @param inv     input resolution, might be modified after the call
 * @param outv    output resolution, might be modified after the call
 * @param k index of free table entry
 *
 * @return  The index of the next free coefficient entry on success
 *          -1 on failure
 */
static int scale_1d_smart(int *inv, int *outv, int index)
{
      int len, num, den, retry;
      static int num1, den1;

      if (!inv || !outv)
            return -1;

      /* Both should be non-zero */
      if (!(*inv) || !(*outv))
            return -1;

      retry = SCALE_RETRY;

      do {
            num = ratio(*inv, *outv, &den);
            pr_debug("num = %d, den = %d\n", num, den);
            if (!num)
                  continue;

            if (index != 0) {
                  /*
                   * We are now resizing height. Check to see if the
                   * resize ratio for width can be reused by height
                   */
                  if ((num == num1) && (den == den1))
                        return index;
            }

            if ((len = scale_1d(num, den, index)) < 0)
                  /* increase output dimension to try another ratio */
                  (*outv)++;
            else {
                  if (index == 0) {
                        /*
                         * We are now resizing width. The same resize
                         * ratio may be reused by height, so save the
                         * ratio.
                         */
                        num1 = num;
                        den1 = den;
                  }
                  return len;
            }
      } while (retry--);

      pr_debug("pp scale err\n");
      return -1;
}

/*
 * @brief Build PP coefficient table for both width and height
 * Build PP coefficient table for both width and height based on
 * given resizing ratios.
 *
 * @param sz      Structure contains resizing ratio informations
 *
 * @return  0 on success, others on failure
 */
static int scale_2d(emma_pp_scale * sz)
{
      int inv, outv;

      /* horizontal resizing. parameter check - must provide in size */
      if (!sz->in.width)
            return -1;

      /* Resizing based on num:den */
      inv = sz->num.width;
      outv = sz->den.width;

      if ((g_hlen = scale_1d_smart(&inv, &outv, 0)) > 0) {
            /* Resizing succeeded */
            sz->den.width = outv;
            sz->out.width = (sz->in.width * outv) / inv;
      } else {
            /* Resizing based on in:out */
            inv = sz->in.width;
            outv = sz->out.width;

            if ((g_hlen = scale_1d_smart(&inv, &outv, 0)) > 0) {
                  /* Resizing succeeded */
                  sz->out.width = outv;
                  sz->num.width = ratio(sz->in.width, sz->out.width,
                                    &sz->den.width);
            } else
                  return -1;
      }

      sz->out.width &= ~1;

      /* vertical resizing. parameter check - must provide in size */
      if (!sz->in.height)
            return -1;

      /* Resizing based on num:den */
      inv = sz->num.height;
      outv = sz->den.height;

      if ((g_vlen = scale_1d_smart(&inv, &outv, g_hlen)) > 0) {
            /* Resizing succeeded */
            sz->den.height = outv;
            sz->out.height = (sz->in.height * outv) / inv;
      } else {
            /* Resizing based on in:out */
            inv = sz->in.height;
            outv = sz->out.height;

            if ((g_vlen = scale_1d_smart(&inv, &outv, g_hlen)) > 0) {
                  /* Resizing succeeded */
                  sz->out.height = outv;
                  sz->num.height = ratio(sz->in.height, sz->out.height,
                                     &sz->den.height);
            } else
                  return -1;
      }

      return 0;
}

/*!
 * @brief Set PP resizing registers.
 * @param sz      Pointer to pp scaling structure
 * @return  Zero on success, others on failure
 */
00636 static int pphw_scale(emma_pp_scale * sz)
{
      __raw_writel((sz->out.width << 16) | sz->out.height,
                 PP_DEST_IMAGE_SIZE);
      __raw_writel(((g_hlen - 1) << 16) | (g_vlen ==
                                   g_hlen ? 0 : (g_hlen << 8)) |
                 (g_vlen - 1), PP_RESIZE_INDEX);
      for (g_hlen = 0; g_hlen < g_vlen; g_hlen++)
            __raw_writel(scale_tbl[g_hlen],
                       PP_RESIZE_COEF_TBL + g_hlen * 4);

      return 0;
}

/*!
 * @brief Reset PP.
 * @return  Zero on success, others on failure
 */
00654 static int pphw_reset(void)
{
      int i;

      __raw_writel(0x100, PP_CNTL);

      /* timeout */
      for (i = 0; i < 1000; i++) {
            if (!(__raw_readl(PP_CNTL) & 0x100)) {
                  pr_debug("pp reset over\n");
                  break;
            }
      }

      /* check reset value */
      if (__raw_readl(PP_CNTL) != 0x876) {
            pr_debug("pp reset value err = 0x%08X\n", __raw_readl(PP_CNTL));
            return -1;
      }

      return 0;
}

/*!
 * @brief Enable or disable PP.
 * @param flag    Zero to disable PP, others to enable PP
 * @return  Zero on success, others on failure
 */
00682 static int pphw_enable(int flag)
{
      int ret = 0;

      if (flag)
            __raw_writel(__raw_readl(PP_CNTL) | 1, PP_CNTL);
      else
            ret = pphw_reset();

      return ret;
}

/*!
 * @brief Set PP input address.
 * @param cfg     The pointer to PP configuration parameter
 * @return  Zero on success, others on failure
 */
00699 static int pphw_ptr(emma_pp_cfg * cfg)
{
      if (!cfg->ptr.u) {
            int size;

            /* yuv - packed */
            size = PP_CALC_Y_SIZE(cfg);
            cfg->ptr.u = cfg->ptr.y + size;
            cfg->ptr.v = cfg->ptr.u + (size >> 2);

            /* yuv packed with qp appended */
            if (!cfg->ptr.qp)
                  cfg->ptr.qp = cfg->ptr.v + (size >> 2);
      }
      __raw_writel(cfg->ptr.y, PP_SOURCE_Y_PTR);
      __raw_writel(cfg->ptr.u, PP_SOURCE_CB_PTR);
      __raw_writel(cfg->ptr.v, PP_SOURCE_CR_PTR);
      __raw_writel(cfg->ptr.qp, PP_QUANTIZER_PTR);

      return 0;
}

/*!
 * @brief Set PP output address.
 * @param cfg     The pointer to PP configuration parameter
 * @return  Zero on success, others on failure
 */
00726 static int pphw_outptr(emma_pp_cfg * cfg)
{
      __raw_writel(cfg->outptr, PP_DEST_RGB_PTR);
      return 0;
}

/*!
 * @brief Configuration PP.
 * @param cfg     The pointer to PP configuration parameter
 * @return  Zero on success, others on failure
 */
00737 static int pphw_cfg(emma_pp_cfg * cfg)
{
      int rt;
      register int r;

      pphw_scale(&cfg->dim);

      if (!cfg->in_y_stride)
            cfg->in_y_stride = cfg->dim.in.width;

      if (!cfg->out_stride)
            cfg->out_stride = cfg->dim.out.width;

      r = __raw_readl(PP_CNTL) & ~EN_MASK;

      /* config parms */
      r |= cfg->operation & EN_MASK;
      if (cfg->operation & EN_MACROBLOCK) {
            /* Macroblock Mode */
            r |= 0x0200;
            __raw_writel(0x06, PP_INTRCNTL);
      } else {
            /* Frame mode */
            __raw_writel(0x05, PP_INTRCNTL);
      }

      if (cfg->red_width | cfg->green_width | cfg->blue_width) {
            /* color conversion to be performed */
            r |= EN_CSC;
            if (!(cfg->red_offset | cfg->green_offset)) {
                  /* auto offset B:G:R LSb to Msb */
                  cfg->green_offset = cfg->blue_offset + cfg->blue_width;
                  cfg->red_offset = cfg->green_offset + cfg->green_width;
            }
            if (!cfg->rgb_resolution) {
                  /* derive minimum resolution required */
                  int w, w2;

                  w = cfg->red_offset + cfg->red_width;
                  w2 = cfg->blue_offset + cfg->blue_width;
                  if (w < w2)
                        w = w2;
                  w2 = cfg->green_offset + cfg->green_width;
                  if (w < w2)
                        w = w2;
                  if (w > 16)
                        w = 24;
                  else if (w > 8)
                        w = 16;
                  else
                        w = 8;
                  cfg->rgb_resolution = w;
            }
            /* 00,11 - 32 bpp, 10 - 16 bpp, 01 - 8 bpp */
            r &= ~0xC00;
            if (cfg->rgb_resolution < 32)
                  r |= (cfg->rgb_resolution << 7);
            __raw_writel((cfg->red_offset << 26) |
                       (cfg->green_offset << 21) |
                       (cfg->blue_offset << 16) |
                       (cfg->red_width << 8) |
                       (cfg->green_width << 4) |
                       cfg->blue_width, PP_DEST_FRAME_FMT_CNTL);
      } else {
            /* add YUV422 formatting */
            static const unsigned int _422[] = {
                  0x62000888,
                  0x60100888,
                  0x43080888,
                  0x41180888
            };

            __raw_writel(_422[(cfg->blue_offset >> 3) & 3],
                       PP_DEST_FRAME_FMT_CNTL);
            cfg->rgb_resolution = 16;
            r &= ~0xC00;
            r |= (cfg->rgb_resolution << 7);
      }

      /* add csc formatting */
      if (!cfg->csc_table[1]) {
            static const unsigned short _csc[][6] = {
                  {0x80, 0xb4, 0x2c, 0x5b, 0x0e4, 0},
                  {0x95, 0xcc, 0x32, 0x68, 0x104, 1},
                  {0x80, 0xca, 0x18, 0x3c, 0x0ec, 0},
                  {0x95, 0xe5, 0x1b, 0x44, 0x10e, 1},
            };
            memcpy(cfg->csc_table, _csc[cfg->csc_table[0]],
                   sizeof(_csc[0]));
      }
      __raw_writel((cfg->csc_table[0] << 24) |
                 (cfg->csc_table[1] << 16) |
                 (cfg->csc_table[2] << 8) |
                 cfg->csc_table[3], PP_CSC_COEF_0123);
      __raw_writel((cfg->csc_table[5] ? (1 << 9) : 0) | cfg->csc_table[4],
                 PP_CSC_COEF_4);

      __raw_writel(r, PP_CNTL);

      pphw_ptr(cfg);
      pphw_outptr(cfg);

      /*
       * #MB in a row = input_width / 16pix
       * 1 byte per QP per MB
       * QP must be formatted to be 4-byte aligned
       * YUV lines are to be 4-byte aligned as well
       * So Y is 8 byte aligned, as U = V = Y/2 for 420
       * MPEG MBs are 16x16 anyway
       */
      __raw_writel((cfg->dim.in.width << 16) | cfg->dim.in.height,
                 PP_PROCESS_FRAME_PARA);
      __raw_writel(cfg->in_y_stride | (PP_CALC_QP_WIDTH(cfg) << 16),
                 PP_SOURCE_FRAME_WIDTH);

      /* in bytes */
      rt = cfg->rgb_resolution >> 3;
      if (rt == 3)
            rt = 4;
      __raw_writel(cfg->out_stride * rt, PP_DEST_DISPLAY_WIDTH);

      pp_dump();
      return 0;
}

/*!
 * @brief Check PP interrupt status.
 * @return  PP interrupt status
 */
00866 static int pphw_isr(void)
{
      unsigned long status;

      pr_debug("pp: in isr.\n");
      status = __raw_readl(PP_INTRSTATUS) & 7;
      if (!status) {
            pr_debug("pp: not my isr err.\n");
            return status;
      }

      if (status & 4)
            pr_debug("pp: isr state error.\n");

      /* clear interrupt status */
      __raw_writel(status, PP_INTRSTATUS);

      return status;
}

static struct clk *emma_clk;

/*!
 * @brief PP module clock enable
 */
00891 static void pphw_init(void)
{
      emma_clk = clk_get(NULL, "emma_clk");
      clk_enable(emma_clk);
}

/*!
 * @brief PP module clock disable
 */
00900 static void pphw_exit(void)
{
      clk_disable(emma_clk);
      clk_put(emma_clk);
}

Generated by  Doxygen 1.6.0   Back to index