Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  dispc.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2009 Nokia Corporation
 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
 *
 * Some code and ideas taken from drivers/video/omap/ driver
 * by Imre Deak.
 */


#define DSS_SUBSYS_NAME "DISPC"

#include <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <linux/export.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/hardirq.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/sizes.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/component.h>
#include <linux/sys_soc.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_blend.h>

#include "omapdss.h"
#include "dss.h"
#include "dispc.h"

struct dispc_device;

/* DISPC */
#define DISPC_SZ_REGS   SZ_4K

enum omap_burst_size {
 BURST_SIZE_X2 = 0,
 BURST_SIZE_X4 = 1,
 BURST_SIZE_X8 = 2,
};

#define REG_GET(dispc, idx, start, end) \
 FLD_GET(dispc_read_reg(dispc, idx), start, end)

#define REG_FLD_MOD(dispc, idx, val, start, end)   \
 dispc_write_reg(dispc, idx, \
   FLD_MOD(dispc_read_reg(dispc, idx), val, start, end))

/* DISPC has feature id */
enum dispc_feature_id {
 FEAT_LCDENABLEPOL,
 FEAT_LCDENABLESIGNAL,
 FEAT_PCKFREEENABLE,
 FEAT_FUNCGATED,
 FEAT_MGR_LCD2,
 FEAT_MGR_LCD3,
 FEAT_LINEBUFFERSPLIT,
 FEAT_ROWREPEATENABLE,
 FEAT_RESIZECONF,
 /* Independent core clk divider */
 FEAT_CORE_CLK_DIV,
 FEAT_HANDLE_UV_SEPARATE,
 FEAT_ATTR2,
 FEAT_CPR,
 FEAT_PRELOAD,
 FEAT_FIR_COEF_V,
 FEAT_ALPHA_FIXED_ZORDER,
 FEAT_ALPHA_FREE_ZORDER,
 FEAT_FIFO_MERGE,
 /* An unknown HW bug causing the normal FIFO thresholds not to work */
 FEAT_OMAP3_DSI_FIFO_BUG,
 FEAT_BURST_2D,
 FEAT_MFLAG,
};

struct dispc_features {
 u8 sw_start;
 u8 fp_start;
 u8 bp_start;
 u16 sw_max;
 u16 vp_max;
 u16 hp_max;
 u8 mgr_width_start;
 u8 mgr_height_start;
 u16 mgr_width_max;
 u16 mgr_height_max;
 u16 ovl_width_max;
 u16 ovl_height_max;
 unsigned long max_lcd_pclk;
 unsigned long max_tv_pclk;
 unsigned int max_downscale;
 unsigned int max_line_width;
 unsigned int min_pcd;
 int (*calc_scaling)(struct dispc_device *dispc,
  unsigned long pclk, unsigned long lclk,
  const struct videomode *vm,
  u16 width, u16 height, u16 out_width, u16 out_height,
  u32 fourcc, bool *five_taps,
  int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
  u16 pos_x, unsigned long *core_clk, bool mem_to_mem);
 unsigned long (*calc_core_clk) (unsigned long pclk,
  u16 width, u16 height, u16 out_width, u16 out_height,
  bool mem_to_mem);
 u8 num_fifos;
 const enum dispc_feature_id *features;
 unsigned int num_features;
 const struct dss_reg_field *reg_fields;
 const unsigned int num_reg_fields;
 const enum omap_overlay_caps *overlay_caps;
 const u32 **supported_color_modes;
 const u32 *supported_scaler_color_modes;
 unsigned int num_mgrs;
 unsigned int num_ovls;
 unsigned int buffer_size_unit;
 unsigned int burst_size_unit;

 /* swap GFX & WB fifos */
 bool gfx_fifo_workaround:1;

 /* no DISPC_IRQ_FRAMEDONETV on this SoC */
 bool no_framedone_tv:1;

 /* revert to the OMAP4 mechanism of DISPC Smart Standby operation */
 bool mstandby_workaround:1;

 bool set_max_preload:1;

 /* PIXEL_INC is not added to the last pixel of a line */
 bool last_pixel_inc_missing:1;

 /* POL_FREQ has ALIGN bit */
 bool supports_sync_align:1;

 bool has_writeback:1;

 bool supports_double_pixel:1;

 /*
 * Field order for VENC is different than HDMI. We should handle this in
 * some intelligent manner, but as the SoCs have either HDMI or VENC,
 * never both, we can just use this flag for now.
 */

 bool reverse_ilace_field_order:1;

 bool has_gamma_table:1;

 bool has_gamma_i734_bug:1;
};

#define DISPC_MAX_NR_FIFOS 5
#define DISPC_MAX_CHANNEL_GAMMA 4

struct dispc_device {
 struct platform_device *pdev;
 void __iomem    *base;
 struct dss_device *dss;

 struct dss_debugfs_entry *debugfs;

 int irq;
 irq_handler_t user_handler;
 void *user_data;

 unsigned long core_clk_rate;
 unsigned long tv_pclk_rate;

 u32 fifo_size[DISPC_MAX_NR_FIFOS];
 /* maps which plane is using a fifo. fifo-id -> plane-id */
 int fifo_assignment[DISPC_MAX_NR_FIFOS];

 bool  ctx_valid;
 u32  ctx[DISPC_SZ_REGS / sizeof(u32)];

 u32 *gamma_table[DISPC_MAX_CHANNEL_GAMMA];

 const struct dispc_features *feat;

 bool is_enabled;

 struct regmap *syscon_pol;
 u32 syscon_pol_offset;
};

enum omap_color_component {
 /* used for all color formats for OMAP3 and earlier
 * and for RGB and Y color component on OMAP4
 */

 DISPC_COLOR_COMPONENT_RGB_Y  = 1 << 0,
 /* used for UV component for
 * DRM_FORMAT_YUYV, DRM_FORMAT_UYVY, DRM_FORMAT_NV12
 * color formats on OMAP4
 */

 DISPC_COLOR_COMPONENT_UV  = 1 << 1,
};

enum mgr_reg_fields {
 DISPC_MGR_FLD_ENABLE,
 DISPC_MGR_FLD_STNTFT,
 DISPC_MGR_FLD_GO,
 DISPC_MGR_FLD_TFTDATALINES,
 DISPC_MGR_FLD_STALLMODE,
 DISPC_MGR_FLD_TCKENABLE,
 DISPC_MGR_FLD_TCKSELECTION,
 DISPC_MGR_FLD_CPR,
 DISPC_MGR_FLD_FIFOHANDCHECK,
 /* used to maintain a count of the above fields */
 DISPC_MGR_FLD_NUM,
};

/* DISPC register field id */
enum dispc_feat_reg_field {
 FEAT_REG_FIRHINC,
 FEAT_REG_FIRVINC,
 FEAT_REG_FIFOHIGHTHRESHOLD,
 FEAT_REG_FIFOLOWTHRESHOLD,
 FEAT_REG_FIFOSIZE,
 FEAT_REG_HORIZONTALACCU,
 FEAT_REG_VERTICALACCU,
};

struct dispc_reg_field {
 u16 reg;
 u8 high;
 u8 low;
};

struct dispc_gamma_desc {
 u32 len;
 u32 bits;
 u16 reg;
 bool has_index;
};

static const struct {
 const char *name;
 u32 vsync_irq;
 u32 framedone_irq;
 u32 sync_lost_irq;
 struct dispc_gamma_desc gamma;
 struct dispc_reg_field reg_desc[DISPC_MGR_FLD_NUM];
} mgr_desc[] = {
 [OMAP_DSS_CHANNEL_LCD] = {
  .name  = "LCD",
  .vsync_irq = DISPC_IRQ_VSYNC,
  .framedone_irq = DISPC_IRQ_FRAMEDONE,
  .sync_lost_irq = DISPC_IRQ_SYNC_LOST,
  .gamma  = {
   .len = 256,
   .bits = 8,
   .reg = DISPC_GAMMA_TABLE0,
   .has_index = true,
  },
  .reg_desc = {
   [DISPC_MGR_FLD_ENABLE]  = { DISPC_CONTROL,  0,  0 },
   [DISPC_MGR_FLD_STNTFT]  = { DISPC_CONTROL,  3,  3 },
   [DISPC_MGR_FLD_GO]  = { DISPC_CONTROL,  5,  5 },
   [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL,  9,  8 },
   [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL, 11, 11 },
   [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG,  10, 10 },
   [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG,  11, 11 },
   [DISPC_MGR_FLD_CPR]  = { DISPC_CONFIG,  15, 15 },
   [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG,  16, 16 },
  },
 },
 [OMAP_DSS_CHANNEL_DIGIT] = {
  .name  = "DIGIT",
  .vsync_irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN,
  .framedone_irq = DISPC_IRQ_FRAMEDONETV,
  .sync_lost_irq = DISPC_IRQ_SYNC_LOST_DIGIT,
  .gamma  = {
   .len = 1024,
   .bits = 10,
   .reg = DISPC_GAMMA_TABLE2,
   .has_index = false,
  },
  .reg_desc = {
   [DISPC_MGR_FLD_ENABLE]  = { DISPC_CONTROL,  1,  1 },
   [DISPC_MGR_FLD_STNTFT]  = { },
   [DISPC_MGR_FLD_GO]  = { DISPC_CONTROL,  6,  6 },
   [DISPC_MGR_FLD_TFTDATALINES] = { },
   [DISPC_MGR_FLD_STALLMODE] = { },
   [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG,  12, 12 },
   [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG,  13, 13 },
   [DISPC_MGR_FLD_CPR]  = { },
   [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG,  16, 16 },
  },
 },
 [OMAP_DSS_CHANNEL_LCD2] = {
  .name  = "LCD2",
  .vsync_irq = DISPC_IRQ_VSYNC2,
  .framedone_irq = DISPC_IRQ_FRAMEDONE2,
  .sync_lost_irq = DISPC_IRQ_SYNC_LOST2,
  .gamma  = {
   .len = 256,
   .bits = 8,
   .reg = DISPC_GAMMA_TABLE1,
   .has_index = true,
  },
  .reg_desc = {
   [DISPC_MGR_FLD_ENABLE]  = { DISPC_CONTROL2,  0,  0 },
   [DISPC_MGR_FLD_STNTFT]  = { DISPC_CONTROL2,  3,  3 },
   [DISPC_MGR_FLD_GO]  = { DISPC_CONTROL2,  5,  5 },
   [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL2,  9,  8 },
   [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL2, 11, 11 },
   [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG2,  10, 10 },
   [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG2,  11, 11 },
   [DISPC_MGR_FLD_CPR]  = { DISPC_CONFIG2,  15, 15 },
   [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG2,  16, 16 },
  },
 },
 [OMAP_DSS_CHANNEL_LCD3] = {
  .name  = "LCD3",
  .vsync_irq = DISPC_IRQ_VSYNC3,
  .framedone_irq = DISPC_IRQ_FRAMEDONE3,
  .sync_lost_irq = DISPC_IRQ_SYNC_LOST3,
  .gamma  = {
   .len = 256,
   .bits = 8,
   .reg = DISPC_GAMMA_TABLE3,
   .has_index = true,
  },
  .reg_desc = {
   [DISPC_MGR_FLD_ENABLE]  = { DISPC_CONTROL3,  0,  0 },
   [DISPC_MGR_FLD_STNTFT]  = { DISPC_CONTROL3,  3,  3 },
   [DISPC_MGR_FLD_GO]  = { DISPC_CONTROL3,  5,  5 },
   [DISPC_MGR_FLD_TFTDATALINES] = { DISPC_CONTROL3,  9,  8 },
   [DISPC_MGR_FLD_STALLMODE] = { DISPC_CONTROL3, 11, 11 },
   [DISPC_MGR_FLD_TCKENABLE] = { DISPC_CONFIG3,  10, 10 },
   [DISPC_MGR_FLD_TCKSELECTION] = { DISPC_CONFIG3,  11, 11 },
   [DISPC_MGR_FLD_CPR]  = { DISPC_CONFIG3,  15, 15 },
   [DISPC_MGR_FLD_FIFOHANDCHECK] = { DISPC_CONFIG3,  16, 16 },
  },
 },
};

static unsigned long dispc_fclk_rate(struct dispc_device *dispc);
static unsigned long dispc_core_clk_rate(struct dispc_device *dispc);
static unsigned long dispc_mgr_lclk_rate(struct dispc_device *dispc,
      enum omap_channel channel);
static unsigned long dispc_mgr_pclk_rate(struct dispc_device *dispc,
      enum omap_channel channel);

static unsigned long dispc_plane_pclk_rate(struct dispc_device *dispc,
        enum omap_plane_id plane);
static unsigned long dispc_plane_lclk_rate(struct dispc_device *dispc,
        enum omap_plane_id plane);

static inline void dispc_write_reg(struct dispc_device *dispc, u16 idx, u32 val)
{
 __raw_writel(val, dispc->base + idx);
}

static inline u32 dispc_read_reg(struct dispc_device *dispc, u16 idx)
{
 return __raw_readl(dispc->base + idx);
}

static u32 mgr_fld_read(struct dispc_device *dispc, enum omap_channel channel,
   enum mgr_reg_fields regfld)
{
 const struct dispc_reg_field *rfld = &mgr_desc[channel].reg_desc[regfld];

 return REG_GET(dispc, rfld->reg, rfld->high, rfld->low);
}

static void mgr_fld_write(struct dispc_device *dispc, enum omap_channel channel,
     enum mgr_reg_fields regfld, int val)
{
 const struct dispc_reg_field *rfld = &mgr_desc[channel].reg_desc[regfld];

 REG_FLD_MOD(dispc, rfld->reg, val, rfld->high, rfld->low);
}

int dispc_get_num_ovls(struct dispc_device *dispc)
{
 return dispc->feat->num_ovls;
}

int dispc_get_num_mgrs(struct dispc_device *dispc)
{
 return dispc->feat->num_mgrs;
}

static void dispc_get_reg_field(struct dispc_device *dispc,
    enum dispc_feat_reg_field id,
    u8 *start, u8 *end)
{
 BUG_ON(id >= dispc->feat->num_reg_fields);

 *start = dispc->feat->reg_fields[id].start;
 *end = dispc->feat->reg_fields[id].end;
}

static bool dispc_has_feature(struct dispc_device *dispc,
         enum dispc_feature_id id)
{
 unsigned int i;

 for (i = 0; i < dispc->feat->num_features; i++) {
  if (dispc->feat->features[i] == id)
   return true;
 }

 return false;
}

#define SR(dispc, reg) \
 dispc->ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(dispc, DISPC_##reg)
#define RR(dispc, reg) \
 dispc_write_reg(dispc, DISPC_##reg, dispc->ctx[DISPC_##reg / sizeof(u32)])

static void dispc_save_context(struct dispc_device *dispc)
{
 int i, j;

 DSSDBG("dispc_save_context\n");

 SR(dispc, IRQENABLE);
 SR(dispc, CONTROL);
 SR(dispc, CONFIG);
 SR(dispc, LINE_NUMBER);
 if (dispc_has_feature(dispc, FEAT_ALPHA_FIXED_ZORDER) ||
   dispc_has_feature(dispc, FEAT_ALPHA_FREE_ZORDER))
  SR(dispc, GLOBAL_ALPHA);
 if (dispc_has_feature(dispc, FEAT_MGR_LCD2)) {
  SR(dispc, CONTROL2);
  SR(dispc, CONFIG2);
 }
 if (dispc_has_feature(dispc, FEAT_MGR_LCD3)) {
  SR(dispc, CONTROL3);
  SR(dispc, CONFIG3);
 }

 for (i = 0; i < dispc_get_num_mgrs(dispc); i++) {
  SR(dispc, DEFAULT_COLOR(i));
  SR(dispc, TRANS_COLOR(i));
  SR(dispc, SIZE_MGR(i));
  if (i == OMAP_DSS_CHANNEL_DIGIT)
   continue;
  SR(dispc, TIMING_H(i));
  SR(dispc, TIMING_V(i));
  SR(dispc, POL_FREQ(i));
  SR(dispc, DIVISORo(i));

  SR(dispc, DATA_CYCLE1(i));
  SR(dispc, DATA_CYCLE2(i));
  SR(dispc, DATA_CYCLE3(i));

  if (dispc_has_feature(dispc, FEAT_CPR)) {
   SR(dispc, CPR_COEF_R(i));
   SR(dispc, CPR_COEF_G(i));
   SR(dispc, CPR_COEF_B(i));
  }
 }

 for (i = 0; i < dispc_get_num_ovls(dispc); i++) {
  SR(dispc, OVL_BA0(i));
  SR(dispc, OVL_BA1(i));
  SR(dispc, OVL_POSITION(i));
  SR(dispc, OVL_SIZE(i));
  SR(dispc, OVL_ATTRIBUTES(i));
  SR(dispc, OVL_FIFO_THRESHOLD(i));
  SR(dispc, OVL_ROW_INC(i));
  SR(dispc, OVL_PIXEL_INC(i));
  if (dispc_has_feature(dispc, FEAT_PRELOAD))
   SR(dispc, OVL_PRELOAD(i));
  if (i == OMAP_DSS_GFX) {
   SR(dispc, OVL_WINDOW_SKIP(i));
   SR(dispc, OVL_TABLE_BA(i));
   continue;
  }
  SR(dispc, OVL_FIR(i));
  SR(dispc, OVL_PICTURE_SIZE(i));
  SR(dispc, OVL_ACCU0(i));
  SR(dispc, OVL_ACCU1(i));

  for (j = 0; j < 8; j++)
   SR(dispc, OVL_FIR_COEF_H(i, j));

  for (j = 0; j < 8; j++)
   SR(dispc, OVL_FIR_COEF_HV(i, j));

  for (j = 0; j < 5; j++)
   SR(dispc, OVL_CONV_COEF(i, j));

  if (dispc_has_feature(dispc, FEAT_FIR_COEF_V)) {
   for (j = 0; j < 8; j++)
    SR(dispc, OVL_FIR_COEF_V(i, j));
  }

  if (dispc_has_feature(dispc, FEAT_HANDLE_UV_SEPARATE)) {
   SR(dispc, OVL_BA0_UV(i));
   SR(dispc, OVL_BA1_UV(i));
   SR(dispc, OVL_FIR2(i));
   SR(dispc, OVL_ACCU2_0(i));
   SR(dispc, OVL_ACCU2_1(i));

   for (j = 0; j < 8; j++)
    SR(dispc, OVL_FIR_COEF_H2(i, j));

   for (j = 0; j < 8; j++)
    SR(dispc, OVL_FIR_COEF_HV2(i, j));

   for (j = 0; j < 8; j++)
    SR(dispc, OVL_FIR_COEF_V2(i, j));
  }
  if (dispc_has_feature(dispc, FEAT_ATTR2))
   SR(dispc, OVL_ATTRIBUTES2(i));
 }

 if (dispc_has_feature(dispc, FEAT_CORE_CLK_DIV))
  SR(dispc, DIVISOR);

 dispc->ctx_valid = true;

 DSSDBG("context saved\n");
}

static noinline_for_stack void dispc_restore_context(struct dispc_device *dispc)
{
 int i, j;

 DSSDBG("dispc_restore_context\n");

 if (!dispc->ctx_valid)
  return;

 /*RR(dispc, IRQENABLE);*/
 /*RR(dispc, CONTROL);*/
 RR(dispc, CONFIG);
 RR(dispc, LINE_NUMBER);
 if (dispc_has_feature(dispc, FEAT_ALPHA_FIXED_ZORDER) ||
   dispc_has_feature(dispc, FEAT_ALPHA_FREE_ZORDER))
  RR(dispc, GLOBAL_ALPHA);
 if (dispc_has_feature(dispc, FEAT_MGR_LCD2))
  RR(dispc, CONFIG2);
 if (dispc_has_feature(dispc, FEAT_MGR_LCD3))
  RR(dispc, CONFIG3);

 for (i = 0; i < dispc_get_num_mgrs(dispc); i++) {
  RR(dispc, DEFAULT_COLOR(i));
  RR(dispc, TRANS_COLOR(i));
  RR(dispc, SIZE_MGR(i));
  if (i == OMAP_DSS_CHANNEL_DIGIT)
   continue;
  RR(dispc, TIMING_H(i));
  RR(dispc, TIMING_V(i));
  RR(dispc, POL_FREQ(i));
  RR(dispc, DIVISORo(i));

  RR(dispc, DATA_CYCLE1(i));
  RR(dispc, DATA_CYCLE2(i));
  RR(dispc, DATA_CYCLE3(i));

  if (dispc_has_feature(dispc, FEAT_CPR)) {
   RR(dispc, CPR_COEF_R(i));
   RR(dispc, CPR_COEF_G(i));
   RR(dispc, CPR_COEF_B(i));
  }
 }

 for (i = 0; i < dispc_get_num_ovls(dispc); i++) {
  RR(dispc, OVL_BA0(i));
  RR(dispc, OVL_BA1(i));
  RR(dispc, OVL_POSITION(i));
  RR(dispc, OVL_SIZE(i));
  RR(dispc, OVL_ATTRIBUTES(i));
  RR(dispc, OVL_FIFO_THRESHOLD(i));
  RR(dispc, OVL_ROW_INC(i));
  RR(dispc, OVL_PIXEL_INC(i));
  if (dispc_has_feature(dispc, FEAT_PRELOAD))
   RR(dispc, OVL_PRELOAD(i));
  if (i == OMAP_DSS_GFX) {
   RR(dispc, OVL_WINDOW_SKIP(i));
   RR(dispc, OVL_TABLE_BA(i));
   continue;
  }
  RR(dispc, OVL_FIR(i));
  RR(dispc, OVL_PICTURE_SIZE(i));
  RR(dispc, OVL_ACCU0(i));
  RR(dispc, OVL_ACCU1(i));

  for (j = 0; j < 8; j++)
   RR(dispc, OVL_FIR_COEF_H(i, j));

  for (j = 0; j < 8; j++)
   RR(dispc, OVL_FIR_COEF_HV(i, j));

  for (j = 0; j < 5; j++)
   RR(dispc, OVL_CONV_COEF(i, j));

  if (dispc_has_feature(dispc, FEAT_FIR_COEF_V)) {
   for (j = 0; j < 8; j++)
    RR(dispc, OVL_FIR_COEF_V(i, j));
  }

  if (dispc_has_feature(dispc, FEAT_HANDLE_UV_SEPARATE)) {
   RR(dispc, OVL_BA0_UV(i));
   RR(dispc, OVL_BA1_UV(i));
   RR(dispc, OVL_FIR2(i));
   RR(dispc, OVL_ACCU2_0(i));
   RR(dispc, OVL_ACCU2_1(i));

   for (j = 0; j < 8; j++)
    RR(dispc, OVL_FIR_COEF_H2(i, j));

   for (j = 0; j < 8; j++)
    RR(dispc, OVL_FIR_COEF_HV2(i, j));

   for (j = 0; j < 8; j++)
    RR(dispc, OVL_FIR_COEF_V2(i, j));
  }
  if (dispc_has_feature(dispc, FEAT_ATTR2))
   RR(dispc, OVL_ATTRIBUTES2(i));
 }

 if (dispc_has_feature(dispc, FEAT_CORE_CLK_DIV))
  RR(dispc, DIVISOR);

 /* enable last, because LCD & DIGIT enable are here */
 RR(dispc, CONTROL);
 if (dispc_has_feature(dispc, FEAT_MGR_LCD2))
  RR(dispc, CONTROL2);
 if (dispc_has_feature(dispc, FEAT_MGR_LCD3))
  RR(dispc, CONTROL3);
 /* clear spurious SYNC_LOST_DIGIT interrupts */
 dispc_clear_irqstatus(dispc, DISPC_IRQ_SYNC_LOST_DIGIT);

 /*
 * enable last so IRQs won't trigger before
 * the context is fully restored
 */

 RR(dispc, IRQENABLE);

 DSSDBG("context restored\n");
}

#undef SR
#undef RR

int dispc_runtime_get(struct dispc_device *dispc)
{
 int r;

 DSSDBG("dispc_runtime_get\n");

 r = pm_runtime_get_sync(&dispc->pdev->dev);
 if (WARN_ON(r < 0)) {
  pm_runtime_put_noidle(&dispc->pdev->dev);
  return r;
 }
 return 0;
}

void dispc_runtime_put(struct dispc_device *dispc)
{
 int r;

 DSSDBG("dispc_runtime_put\n");

 r = pm_runtime_put_sync(&dispc->pdev->dev);
 WARN_ON(r < 0 && r != -ENOSYS);
}

u32 dispc_mgr_get_vsync_irq(struct dispc_device *dispc,
       enum omap_channel channel)
{
 return mgr_desc[channel].vsync_irq;
}

u32 dispc_mgr_get_framedone_irq(struct dispc_device *dispc,
           enum omap_channel channel)
{
 if (channel == OMAP_DSS_CHANNEL_DIGIT && dispc->feat->no_framedone_tv)
  return 0;

 return mgr_desc[channel].framedone_irq;
}

u32 dispc_mgr_get_sync_lost_irq(struct dispc_device *dispc,
           enum omap_channel channel)
{
 return mgr_desc[channel].sync_lost_irq;
}

void dispc_mgr_enable(struct dispc_device *dispc,
        enum omap_channel channel, bool enable)
{
 mgr_fld_write(dispc, channel, DISPC_MGR_FLD_ENABLE, enable);
 /* flush posted write */
 mgr_fld_read(dispc, channel, DISPC_MGR_FLD_ENABLE);
}

static bool dispc_mgr_is_enabled(struct dispc_device *dispc,
     enum omap_channel channel)
{
 return !!mgr_fld_read(dispc, channel, DISPC_MGR_FLD_ENABLE);
}

bool dispc_mgr_go_busy(struct dispc_device *dispc,
         enum omap_channel channel)
{
 return mgr_fld_read(dispc, channel, DISPC_MGR_FLD_GO) == 1;
}

void dispc_mgr_go(struct dispc_device *dispc, enum omap_channel channel)
{
 WARN_ON(!dispc_mgr_is_enabled(dispc, channel));
 WARN_ON(dispc_mgr_go_busy(dispc, channel));

 DSSDBG("GO %s\n", mgr_desc[channel].name);

 mgr_fld_write(dispc, channel, DISPC_MGR_FLD_GO, 1);
}

static void dispc_ovl_write_firh_reg(struct dispc_device *dispc,
         enum omap_plane_id plane, int reg,
         u32 value)
{
 dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_H(plane, reg), value);
}

static void dispc_ovl_write_firhv_reg(struct dispc_device *dispc,
          enum omap_plane_id plane, int reg,
          u32 value)
{
 dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_HV(plane, reg), value);
}

static void dispc_ovl_write_firv_reg(struct dispc_device *dispc,
         enum omap_plane_id plane, int reg,
         u32 value)
{
 dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_V(plane, reg), value);
}

static void dispc_ovl_write_firh2_reg(struct dispc_device *dispc,
          enum omap_plane_id plane, int reg,
          u32 value)
{
 BUG_ON(plane == OMAP_DSS_GFX);

 dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_H2(plane, reg), value);
}

static void dispc_ovl_write_firhv2_reg(struct dispc_device *dispc,
           enum omap_plane_id plane, int reg,
           u32 value)
{
 BUG_ON(plane == OMAP_DSS_GFX);

 dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_HV2(plane, reg), value);
}

static void dispc_ovl_write_firv2_reg(struct dispc_device *dispc,
          enum omap_plane_id plane, int reg,
          u32 value)
{
 BUG_ON(plane == OMAP_DSS_GFX);

 dispc_write_reg(dispc, DISPC_OVL_FIR_COEF_V2(plane, reg), value);
}

static void dispc_ovl_set_scale_coef(struct dispc_device *dispc,
         enum omap_plane_id plane, int fir_hinc,
         int fir_vinc, int five_taps,
         enum omap_color_component color_comp)
{
 const struct dispc_coef *h_coef, *v_coef;
 int i;

 h_coef = dispc_ovl_get_scale_coef(fir_hinc, true);
 v_coef = dispc_ovl_get_scale_coef(fir_vinc, five_taps);

 if (!h_coef || !v_coef) {
  dev_err(&dispc->pdev->dev, "%s: failed to find scale coefs\n",
   __func__);
  return;
 }

 for (i = 0; i < 8; i++) {
  u32 h, hv;

  h = FLD_VAL(h_coef[i].hc0_vc00, 7, 0)
   | FLD_VAL(h_coef[i].hc1_vc0, 15, 8)
   | FLD_VAL(h_coef[i].hc2_vc1, 23, 16)
   | FLD_VAL(h_coef[i].hc3_vc2, 31, 24);
  hv = FLD_VAL(h_coef[i].hc4_vc22, 7, 0)
   | FLD_VAL(v_coef[i].hc1_vc0, 15, 8)
   | FLD_VAL(v_coef[i].hc2_vc1, 23, 16)
   | FLD_VAL(v_coef[i].hc3_vc2, 31, 24);

  if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) {
   dispc_ovl_write_firh_reg(dispc, plane, i, h);
   dispc_ovl_write_firhv_reg(dispc, plane, i, hv);
  } else {
   dispc_ovl_write_firh2_reg(dispc, plane, i, h);
   dispc_ovl_write_firhv2_reg(dispc, plane, i, hv);
  }

 }

 if (five_taps) {
  for (i = 0; i < 8; i++) {
   u32 v;
   v = FLD_VAL(v_coef[i].hc0_vc00, 7, 0)
    | FLD_VAL(v_coef[i].hc4_vc22, 15, 8);
   if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y)
    dispc_ovl_write_firv_reg(dispc, plane, i, v);
   else
    dispc_ovl_write_firv2_reg(dispc, plane, i, v);
  }
 }
}

struct csc_coef_yuv2rgb {
 int ry, rcb, rcr, gy, gcb, gcr, by, bcb, bcr;
 bool full_range;
};

static void dispc_ovl_write_color_conv_coef(struct dispc_device *dispc,
         enum omap_plane_id plane,
         const struct csc_coef_yuv2rgb *ct)
{
#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0))

 dispc_write_reg(dispc, DISPC_OVL_CONV_COEF(plane, 0), CVAL(ct->rcr, ct->ry));
 dispc_write_reg(dispc, DISPC_OVL_CONV_COEF(plane, 1), CVAL(ct->gy,  ct->rcb));
 dispc_write_reg(dispc, DISPC_OVL_CONV_COEF(plane, 2), CVAL(ct->gcb, ct->gcr));
 dispc_write_reg(dispc, DISPC_OVL_CONV_COEF(plane, 3), CVAL(ct->bcr, ct->by));
 dispc_write_reg(dispc, DISPC_OVL_CONV_COEF(plane, 4), CVAL(0, ct->bcb));

 REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), ct->full_range, 11, 11);

#undef CVAL
}

/* YUV -> RGB, ITU-R BT.601, full range */
static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt601_full = {
 256,   0,  358,  /* ry, rcb, rcr |1.000  0.000  1.402|*/
 256, -88, -182,  /* gy, gcb, gcr |1.000 -0.344 -0.714|*/
 256, 452,    0,  /* by, bcb, bcr |1.000  1.772  0.000|*/
 true,   /* full range */
};

/* YUV -> RGB, ITU-R BT.601, limited range */
static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt601_lim = {
 298,    0,  409, /* ry, rcb, rcr |1.164  0.000  1.596|*/
 298, -100, -208, /* gy, gcb, gcr |1.164 -0.392 -0.813|*/
 298,  516,    0, /* by, bcb, bcr |1.164  2.017  0.000|*/
 false,   /* limited range */
};

/* YUV -> RGB, ITU-R BT.709, full range */
static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt709_full = {
 256,    0,  402,        /* ry, rcb, rcr |1.000  0.000  1.570|*/
 256,  -48, -120,        /* gy, gcb, gcr |1.000 -0.187 -0.467|*/
 256,  475,    0,        /* by, bcb, bcr |1.000  1.856  0.000|*/
 true,                   /* full range */
};

/* YUV -> RGB, ITU-R BT.709, limited range */
static const struct csc_coef_yuv2rgb coefs_yuv2rgb_bt709_lim = {
 298,    0,  459, /* ry, rcb, rcr |1.164  0.000  1.793|*/
 298,  -55, -136, /* gy, gcb, gcr |1.164 -0.213 -0.533|*/
 298,  541,    0, /* by, bcb, bcr |1.164  2.112  0.000|*/
 false,   /* limited range */
};

static void dispc_ovl_set_csc(struct dispc_device *dispc,
         enum omap_plane_id plane,
         enum drm_color_encoding color_encoding,
         enum drm_color_range color_range)
{
 const struct csc_coef_yuv2rgb *csc;

 switch (color_encoding) {
 default:
 case DRM_COLOR_YCBCR_BT601:
  if (color_range == DRM_COLOR_YCBCR_FULL_RANGE)
   csc = &coefs_yuv2rgb_bt601_full;
  else
   csc = &coefs_yuv2rgb_bt601_lim;
  break;
 case DRM_COLOR_YCBCR_BT709:
  if (color_range == DRM_COLOR_YCBCR_FULL_RANGE)
   csc = &coefs_yuv2rgb_bt709_full;
  else
   csc = &coefs_yuv2rgb_bt709_lim;
  break;
 }

 dispc_ovl_write_color_conv_coef(dispc, plane, csc);
}

static void dispc_ovl_set_ba0(struct dispc_device *dispc,
         enum omap_plane_id plane, u32 paddr)
{
 dispc_write_reg(dispc, DISPC_OVL_BA0(plane), paddr);
}

static void dispc_ovl_set_ba1(struct dispc_device *dispc,
         enum omap_plane_id plane, u32 paddr)
{
 dispc_write_reg(dispc, DISPC_OVL_BA1(plane), paddr);
}

static void dispc_ovl_set_ba0_uv(struct dispc_device *dispc,
     enum omap_plane_id plane, u32 paddr)
{
 dispc_write_reg(dispc, DISPC_OVL_BA0_UV(plane), paddr);
}

static void dispc_ovl_set_ba1_uv(struct dispc_device *dispc,
     enum omap_plane_id plane, u32 paddr)
{
 dispc_write_reg(dispc, DISPC_OVL_BA1_UV(plane), paddr);
}

static void dispc_ovl_set_pos(struct dispc_device *dispc,
         enum omap_plane_id plane,
         enum omap_overlay_caps caps, int x, int y)
{
 u32 val;

 if ((caps & OMAP_DSS_OVL_CAP_POS) == 0)
  return;

 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0);

 dispc_write_reg(dispc, DISPC_OVL_POSITION(plane), val);
}

static void dispc_ovl_set_input_size(struct dispc_device *dispc,
         enum omap_plane_id plane, int width,
         int height)
{
 u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);

 if (plane == OMAP_DSS_GFX || plane == OMAP_DSS_WB)
  dispc_write_reg(dispc, DISPC_OVL_SIZE(plane), val);
 else
  dispc_write_reg(dispc, DISPC_OVL_PICTURE_SIZE(plane), val);
}

static void dispc_ovl_set_output_size(struct dispc_device *dispc,
          enum omap_plane_id plane, int width,
          int height)
{
 u32 val;

 BUG_ON(plane == OMAP_DSS_GFX);

 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);

 if (plane == OMAP_DSS_WB)
  dispc_write_reg(dispc, DISPC_OVL_PICTURE_SIZE(plane), val);
 else
  dispc_write_reg(dispc, DISPC_OVL_SIZE(plane), val);
}

static void dispc_ovl_set_zorder(struct dispc_device *dispc,
     enum omap_plane_id plane,
     enum omap_overlay_caps caps, u8 zorder)
{
 if ((caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
  return;

 REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), zorder, 27, 26);
}

static void dispc_ovl_enable_zorder_planes(struct dispc_device *dispc)
{
 int i;

 if (!dispc_has_feature(dispc, FEAT_ALPHA_FREE_ZORDER))
  return;

 for (i = 0; i < dispc_get_num_ovls(dispc); i++)
  REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(i), 1, 25, 25);
}

static void dispc_ovl_set_pre_mult_alpha(struct dispc_device *dispc,
      enum omap_plane_id plane,
      enum omap_overlay_caps caps,
      bool enable)
{
 if ((caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0)
  return;

 REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28);
}

static void dispc_ovl_setup_global_alpha(struct dispc_device *dispc,
      enum omap_plane_id plane,
      enum omap_overlay_caps caps,
      u8 global_alpha)
{
 static const unsigned int shifts[] = { 0, 8, 16, 24, };
 int shift;

 if ((caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
  return;

 shift = shifts[plane];
 REG_FLD_MOD(dispc, DISPC_GLOBAL_ALPHA, global_alpha, shift + 7, shift);
}

static void dispc_ovl_set_pix_inc(struct dispc_device *dispc,
      enum omap_plane_id plane, s32 inc)
{
 dispc_write_reg(dispc, DISPC_OVL_PIXEL_INC(plane), inc);
}

static void dispc_ovl_set_row_inc(struct dispc_device *dispc,
      enum omap_plane_id plane, s32 inc)
{
 dispc_write_reg(dispc, DISPC_OVL_ROW_INC(plane), inc);
}

static void dispc_ovl_set_color_mode(struct dispc_device *dispc,
         enum omap_plane_id plane, u32 fourcc)
{
 u32 m = 0;
 if (plane != OMAP_DSS_GFX) {
  switch (fourcc) {
  case DRM_FORMAT_NV12:
   m = 0x0; break;
  case DRM_FORMAT_XRGB4444:
   m = 0x1; break;
  case DRM_FORMAT_RGBA4444:
   m = 0x2; break;
  case DRM_FORMAT_RGBX4444:
   m = 0x4; break;
  case DRM_FORMAT_ARGB4444:
   m = 0x5; break;
  case DRM_FORMAT_RGB565:
   m = 0x6; break;
  case DRM_FORMAT_ARGB1555:
   m = 0x7; break;
  case DRM_FORMAT_XRGB8888:
   m = 0x8; break;
  case DRM_FORMAT_RGB888:
   m = 0x9; break;
  case DRM_FORMAT_YUYV:
   m = 0xa; break;
  case DRM_FORMAT_UYVY:
   m = 0xb; break;
  case DRM_FORMAT_ARGB8888:
   m = 0xc; break;
  case DRM_FORMAT_RGBA8888:
   m = 0xd; break;
  case DRM_FORMAT_RGBX8888:
   m = 0xe; break;
  case DRM_FORMAT_XRGB1555:
   m = 0xf; break;
  default:
   BUG(); return;
  }
 } else {
  switch (fourcc) {
  case DRM_FORMAT_RGBX4444:
   m = 0x4; break;
  case DRM_FORMAT_ARGB4444:
   m = 0x5; break;
  case DRM_FORMAT_RGB565:
   m = 0x6; break;
  case DRM_FORMAT_ARGB1555:
   m = 0x7; break;
  case DRM_FORMAT_XRGB8888:
   m = 0x8; break;
  case DRM_FORMAT_RGB888:
   m = 0x9; break;
  case DRM_FORMAT_XRGB4444:
   m = 0xa; break;
  case DRM_FORMAT_RGBA4444:
   m = 0xb; break;
  case DRM_FORMAT_ARGB8888:
   m = 0xc; break;
  case DRM_FORMAT_RGBA8888:
   m = 0xd; break;
  case DRM_FORMAT_RGBX8888:
   m = 0xe; break;
  case DRM_FORMAT_XRGB1555:
   m = 0xf; break;
  default:
   BUG(); return;
  }
 }

 REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), m, 4, 1);
}

static void dispc_ovl_configure_burst_type(struct dispc_device *dispc,
        enum omap_plane_id plane,
        enum omap_dss_rotation_type rotation)
{
 if (dispc_has_feature(dispc, FEAT_BURST_2D) == 0)
  return;

 if (rotation == OMAP_DSS_ROT_TILER)
  REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), 1, 29, 29);
 else
  REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), 0, 29, 29);
}

static void dispc_ovl_set_channel_out(struct dispc_device *dispc,
          enum omap_plane_id plane,
          enum omap_channel channel)
{
 int shift;
 u32 val;
 int chan = 0, chan2 = 0;

 switch (plane) {
 case OMAP_DSS_GFX:
  shift = 8;
  break;
 case OMAP_DSS_VIDEO1:
 case OMAP_DSS_VIDEO2:
 case OMAP_DSS_VIDEO3:
  shift = 16;
  break;
 default:
  BUG();
  return;
 }

 val = dispc_read_reg(dispc, DISPC_OVL_ATTRIBUTES(plane));
 if (dispc_has_feature(dispc, FEAT_MGR_LCD2)) {
  switch (channel) {
  case OMAP_DSS_CHANNEL_LCD:
   chan = 0;
   chan2 = 0;
   break;
  case OMAP_DSS_CHANNEL_DIGIT:
   chan = 1;
   chan2 = 0;
   break;
  case OMAP_DSS_CHANNEL_LCD2:
   chan = 0;
   chan2 = 1;
   break;
  case OMAP_DSS_CHANNEL_LCD3:
   if (dispc_has_feature(dispc, FEAT_MGR_LCD3)) {
    chan = 0;
    chan2 = 2;
   } else {
    BUG();
    return;
   }
   break;
  case OMAP_DSS_CHANNEL_WB:
   chan = 0;
   chan2 = 3;
   break;
  default:
   BUG();
   return;
  }

  val = FLD_MOD(val, chan, shift, shift);
  val = FLD_MOD(val, chan2, 31, 30);
 } else {
  val = FLD_MOD(val, channel, shift, shift);
 }
 dispc_write_reg(dispc, DISPC_OVL_ATTRIBUTES(plane), val);
}

static enum omap_channel dispc_ovl_get_channel_out(struct dispc_device *dispc,
         enum omap_plane_id plane)
{
 int shift;
 u32 val;

 switch (plane) {
 case OMAP_DSS_GFX:
  shift = 8;
  break;
 case OMAP_DSS_VIDEO1:
 case OMAP_DSS_VIDEO2:
 case OMAP_DSS_VIDEO3:
  shift = 16;
  break;
 default:
  BUG();
  return 0;
 }

 val = dispc_read_reg(dispc, DISPC_OVL_ATTRIBUTES(plane));

 if (FLD_GET(val, shift, shift) == 1)
  return OMAP_DSS_CHANNEL_DIGIT;

 if (!dispc_has_feature(dispc, FEAT_MGR_LCD2))
  return OMAP_DSS_CHANNEL_LCD;

 switch (FLD_GET(val, 31, 30)) {
 case 0:
 default:
  return OMAP_DSS_CHANNEL_LCD;
 case 1:
  return OMAP_DSS_CHANNEL_LCD2;
 case 2:
  return OMAP_DSS_CHANNEL_LCD3;
 case 3:
  return OMAP_DSS_CHANNEL_WB;
 }
}

static void dispc_ovl_set_burst_size(struct dispc_device *dispc,
         enum omap_plane_id plane,
         enum omap_burst_size burst_size)
{
 static const unsigned int shifts[] = { 6, 14, 14, 14, 14, };
 int shift;

 shift = shifts[plane];
 REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), burst_size,
      shift + 1, shift);
}

static void dispc_configure_burst_sizes(struct dispc_device *dispc)
{
 int i;
 const int burst_size = BURST_SIZE_X8;

 /* Configure burst size always to maximum size */
 for (i = 0; i < dispc_get_num_ovls(dispc); ++i)
  dispc_ovl_set_burst_size(dispc, i, burst_size);
 if (dispc->feat->has_writeback)
  dispc_ovl_set_burst_size(dispc, OMAP_DSS_WB, burst_size);
}

static u32 dispc_ovl_get_burst_size(struct dispc_device *dispc,
        enum omap_plane_id plane)
{
 /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */
 return dispc->feat->burst_size_unit * 8;
}

bool dispc_ovl_color_mode_supported(struct dispc_device *dispc,
        enum omap_plane_id plane, u32 fourcc)
{
 const u32 *modes;
 unsigned int i;

 modes = dispc->feat->supported_color_modes[plane];

 for (i = 0; modes[i]; ++i) {
  if (modes[i] == fourcc)
   return true;
 }

 return false;
}

const u32 *dispc_ovl_get_color_modes(struct dispc_device *dispc,
         enum omap_plane_id plane)
{
 return dispc->feat->supported_color_modes[plane];
}

static void dispc_mgr_enable_cpr(struct dispc_device *dispc,
     enum omap_channel channel, bool enable)
{
 if (channel == OMAP_DSS_CHANNEL_DIGIT)
  return;

 mgr_fld_write(dispc, channel, DISPC_MGR_FLD_CPR, enable);
}

static void dispc_mgr_set_cpr_coef(struct dispc_device *dispc,
       enum omap_channel channel,
       const struct omap_dss_cpr_coefs *coefs)
{
 u32 coef_r, coef_g, coef_b;

 if (!dss_mgr_is_lcd(channel))
  return;

 coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) |
  FLD_VAL(coefs->rb, 9, 0);
 coef_g = FLD_VAL(coefs->gr, 31, 22) | FLD_VAL(coefs->gg, 20, 11) |
  FLD_VAL(coefs->gb, 9, 0);
 coef_b = FLD_VAL(coefs->br, 31, 22) | FLD_VAL(coefs->bg, 20, 11) |
  FLD_VAL(coefs->bb, 9, 0);

 dispc_write_reg(dispc, DISPC_CPR_COEF_R(channel), coef_r);
 dispc_write_reg(dispc, DISPC_CPR_COEF_G(channel), coef_g);
 dispc_write_reg(dispc, DISPC_CPR_COEF_B(channel), coef_b);
}

static void dispc_ovl_set_vid_color_conv(struct dispc_device *dispc,
      enum omap_plane_id plane, bool enable)
{
 u32 val;

 BUG_ON(plane == OMAP_DSS_GFX);

 val = dispc_read_reg(dispc, DISPC_OVL_ATTRIBUTES(plane));
 val = FLD_MOD(val, enable, 9, 9);
 dispc_write_reg(dispc, DISPC_OVL_ATTRIBUTES(plane), val);
}

static void dispc_ovl_enable_replication(struct dispc_device *dispc,
      enum omap_plane_id plane,
      enum omap_overlay_caps caps,
      bool enable)
{
 static const unsigned int shifts[] = { 5, 10, 10, 10 };
 int shift;

 if ((caps & OMAP_DSS_OVL_CAP_REPLICATION) == 0)
  return;

 shift = shifts[plane];
 REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift);
}

static void dispc_mgr_set_size(struct dispc_device *dispc,
          enum omap_channel channel, u16 width, u16 height)
{
 u32 val;

 val = FLD_VAL(height - 1, dispc->feat->mgr_height_start, 16) |
  FLD_VAL(width - 1, dispc->feat->mgr_width_start, 0);

 dispc_write_reg(dispc, DISPC_SIZE_MGR(channel), val);
}

static void dispc_init_fifos(struct dispc_device *dispc)
{
 u32 size;
 int fifo;
 u8 start, end;
 u32 unit;
 int i;

 unit = dispc->feat->buffer_size_unit;

 dispc_get_reg_field(dispc, FEAT_REG_FIFOSIZE, &start, &end);

 for (fifo = 0; fifo < dispc->feat->num_fifos; ++fifo) {
  size = REG_GET(dispc, DISPC_OVL_FIFO_SIZE_STATUS(fifo),
          start, end);
  size *= unit;
  dispc->fifo_size[fifo] = size;

  /*
 * By default fifos are mapped directly to overlays, fifo 0 to
 * ovl 0, fifo 1 to ovl 1, etc.
 */

  dispc->fifo_assignment[fifo] = fifo;
 }

 /*
 * The GFX fifo on OMAP4 is smaller than the other fifos. The small fifo
 * causes problems with certain use cases, like using the tiler in 2D
 * mode. The below hack swaps the fifos of GFX and WB planes, thus
 * giving GFX plane a larger fifo. WB but should work fine with a
 * smaller fifo.
 */

 if (dispc->feat->gfx_fifo_workaround) {
  u32 v;

  v = dispc_read_reg(dispc, DISPC_GLOBAL_BUFFER);

  v = FLD_MOD(v, 4, 2, 0); /* GFX BUF top to WB */
  v = FLD_MOD(v, 4, 5, 3); /* GFX BUF bottom to WB */
  v = FLD_MOD(v, 0, 26, 24); /* WB BUF top to GFX */
  v = FLD_MOD(v, 0, 29, 27); /* WB BUF bottom to GFX */

  dispc_write_reg(dispc, DISPC_GLOBAL_BUFFER, v);

  dispc->fifo_assignment[OMAP_DSS_GFX] = OMAP_DSS_WB;
  dispc->fifo_assignment[OMAP_DSS_WB] = OMAP_DSS_GFX;
 }

 /*
 * Setup default fifo thresholds.
 */

 for (i = 0; i < dispc_get_num_ovls(dispc); ++i) {
  u32 low, high;
  const bool use_fifomerge = false;
  const bool manual_update = false;

  dispc_ovl_compute_fifo_thresholds(dispc, i, &low, &high,
        use_fifomerge, manual_update);

  dispc_ovl_set_fifo_threshold(dispc, i, low, high);
 }

 if (dispc->feat->has_writeback) {
  u32 low, high;
  const bool use_fifomerge = false;
  const bool manual_update = false;

  dispc_ovl_compute_fifo_thresholds(dispc, OMAP_DSS_WB,
        &low, &high, use_fifomerge,
        manual_update);

  dispc_ovl_set_fifo_threshold(dispc, OMAP_DSS_WB, low, high);
 }
}

static u32 dispc_ovl_get_fifo_size(struct dispc_device *dispc,
       enum omap_plane_id plane)
{
 int fifo;
 u32 size = 0;

 for (fifo = 0; fifo < dispc->feat->num_fifos; ++fifo) {
  if (dispc->fifo_assignment[fifo] == plane)
   size += dispc->fifo_size[fifo];
 }

 return size;
}

void dispc_ovl_set_fifo_threshold(struct dispc_device *dispc,
      enum omap_plane_id plane,
      u32 low, u32 high)
{
 u8 hi_start, hi_end, lo_start, lo_end;
 u32 unit;

 unit = dispc->feat->buffer_size_unit;

 WARN_ON(low % unit != 0);
 WARN_ON(high % unit != 0);

 low /= unit;
 high /= unit;

 dispc_get_reg_field(dispc, FEAT_REG_FIFOHIGHTHRESHOLD,
       &hi_start, &hi_end);
 dispc_get_reg_field(dispc, FEAT_REG_FIFOLOWTHRESHOLD,
       &lo_start, &lo_end);

 DSSDBG("fifo(%d) threshold (bytes), old %u/%u, new %u/%u\n",
   plane,
   REG_GET(dispc, DISPC_OVL_FIFO_THRESHOLD(plane),
    lo_start, lo_end) * unit,
   REG_GET(dispc, DISPC_OVL_FIFO_THRESHOLD(plane),
    hi_start, hi_end) * unit,
   low * unit, high * unit);

 dispc_write_reg(dispc, DISPC_OVL_FIFO_THRESHOLD(plane),
   FLD_VAL(high, hi_start, hi_end) |
   FLD_VAL(low, lo_start, lo_end));

 /*
 * configure the preload to the pipeline's high threhold, if HT it's too
 * large for the preload field, set the threshold to the maximum value
 * that can be held by the preload register
 */

 if (dispc_has_feature(dispc, FEAT_PRELOAD) &&
     dispc->feat->set_max_preload && plane != OMAP_DSS_WB)
  dispc_write_reg(dispc, DISPC_OVL_PRELOAD(plane),
    min(high, 0xfffu));
}

void dispc_ovl_compute_fifo_thresholds(struct dispc_device *dispc,
           enum omap_plane_id plane,
           u32 *fifo_low, u32 *fifo_high,
           bool use_fifomerge, bool manual_update)
{
 /*
 * All sizes are in bytes. Both the buffer and burst are made of
 * buffer_units, and the fifo thresholds must be buffer_unit aligned.
 */

 unsigned int buf_unit = dispc->feat->buffer_size_unit;
 unsigned int ovl_fifo_size, total_fifo_size, burst_size;
 int i;

 burst_size = dispc_ovl_get_burst_size(dispc, plane);
 ovl_fifo_size = dispc_ovl_get_fifo_size(dispc, plane);

 if (use_fifomerge) {
  total_fifo_size = 0;
  for (i = 0; i < dispc_get_num_ovls(dispc); ++i)
   total_fifo_size += dispc_ovl_get_fifo_size(dispc, i);
 } else {
  total_fifo_size = ovl_fifo_size;
 }

 /*
 * We use the same low threshold for both fifomerge and non-fifomerge
 * cases, but for fifomerge we calculate the high threshold using the
 * combined fifo size
 */


 if (manual_update && dispc_has_feature(dispc, FEAT_OMAP3_DSI_FIFO_BUG)) {
  *fifo_low = ovl_fifo_size - burst_size * 2;
  *fifo_high = total_fifo_size - burst_size;
 } else if (plane == OMAP_DSS_WB) {
  /*
 * Most optimal configuration for writeback is to push out data
 * to the interconnect the moment writeback pushes enough pixels
 * in the FIFO to form a burst
 */

  *fifo_low = 0;
  *fifo_high = burst_size;
 } else {
  *fifo_low = ovl_fifo_size - burst_size;
  *fifo_high = total_fifo_size - buf_unit;
 }
}

static void dispc_ovl_set_mflag(struct dispc_device *dispc,
    enum omap_plane_id plane, bool enable)
{
 int bit;

 if (plane == OMAP_DSS_GFX)
  bit = 14;
 else
  bit = 23;

 REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit);
}

static void dispc_ovl_set_mflag_threshold(struct dispc_device *dispc,
       enum omap_plane_id plane,
       int low, int high)
{
 dispc_write_reg(dispc, DISPC_OVL_MFLAG_THRESHOLD(plane),
  FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0));
}

static void dispc_init_mflag(struct dispc_device *dispc)
{
 int i;

 /*
 * HACK: NV12 color format and MFLAG seem to have problems working
 * together: using two displays, and having an NV12 overlay on one of
 * the displays will cause underflows/synclosts when MFLAG_CTRL=2.
 * Changing MFLAG thresholds and PRELOAD to certain values seem to
 * remove the errors, but there doesn't seem to be a clear logic on
 * which values work and which not.
 *
 * As a work-around, set force MFLAG to always on.
 */

 dispc_write_reg(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE,
  (1 << 0) | /* MFLAG_CTRL = force always on */
  (0 << 2)); /* MFLAG_START = disable */

 for (i = 0; i < dispc_get_num_ovls(dispc); ++i) {
  u32 size = dispc_ovl_get_fifo_size(dispc, i);
  u32 unit = dispc->feat->buffer_size_unit;
  u32 low, high;

  dispc_ovl_set_mflag(dispc, i, true);

  /*
 * Simulation team suggests below thesholds:
 * HT = fifosize * 5 / 8;
 * LT = fifosize * 4 / 8;
 */


  low = size * 4 / 8 / unit;
  high = size * 5 / 8 / unit;

  dispc_ovl_set_mflag_threshold(dispc, i, low, high);
 }

 if (dispc->feat->has_writeback) {
  u32 size = dispc_ovl_get_fifo_size(dispc, OMAP_DSS_WB);
  u32 unit = dispc->feat->buffer_size_unit;
  u32 low, high;

  dispc_ovl_set_mflag(dispc, OMAP_DSS_WB, true);

  /*
 * Simulation team suggests below thesholds:
 * HT = fifosize * 5 / 8;
 * LT = fifosize * 4 / 8;
 */


  low = size * 4 / 8 / unit;
  high = size * 5 / 8 / unit;

  dispc_ovl_set_mflag_threshold(dispc, OMAP_DSS_WB, low, high);
 }
}

static void dispc_ovl_set_fir(struct dispc_device *dispc,
         enum omap_plane_id plane,
         int hinc, int vinc,
         enum omap_color_component color_comp)
{
 u32 val;

 if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) {
  u8 hinc_start, hinc_end, vinc_start, vinc_end;

  dispc_get_reg_field(dispc, FEAT_REG_FIRHINC,
        &hinc_start, &hinc_end);
  dispc_get_reg_field(dispc, FEAT_REG_FIRVINC,
        &vinc_start, &vinc_end);
  val = FLD_VAL(vinc, vinc_start, vinc_end) |
    FLD_VAL(hinc, hinc_start, hinc_end);

  dispc_write_reg(dispc, DISPC_OVL_FIR(plane), val);
 } else {
  val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0);
  dispc_write_reg(dispc, DISPC_OVL_FIR2(plane), val);
 }
}

static void dispc_ovl_set_vid_accu0(struct dispc_device *dispc,
        enum omap_plane_id plane, int haccu,
        int vaccu)
{
 u32 val;
 u8 hor_start, hor_end, vert_start, vert_end;

 dispc_get_reg_field(dispc, FEAT_REG_HORIZONTALACCU,
       &hor_start, &hor_end);
 dispc_get_reg_field(dispc, FEAT_REG_VERTICALACCU,
       &vert_start, &vert_end);

 val = FLD_VAL(vaccu, vert_start, vert_end) |
   FLD_VAL(haccu, hor_start, hor_end);

 dispc_write_reg(dispc, DISPC_OVL_ACCU0(plane), val);
}

static void dispc_ovl_set_vid_accu1(struct dispc_device *dispc,
        enum omap_plane_id plane, int haccu,
        int vaccu)
{
 u32 val;
 u8 hor_start, hor_end, vert_start, vert_end;

 dispc_get_reg_field(dispc, FEAT_REG_HORIZONTALACCU,
       &hor_start, &hor_end);
 dispc_get_reg_field(dispc, FEAT_REG_VERTICALACCU,
       &vert_start, &vert_end);

 val = FLD_VAL(vaccu, vert_start, vert_end) |
   FLD_VAL(haccu, hor_start, hor_end);

 dispc_write_reg(dispc, DISPC_OVL_ACCU1(plane), val);
}

static void dispc_ovl_set_vid_accu2_0(struct dispc_device *dispc,
          enum omap_plane_id plane, int haccu,
          int vaccu)
{
 u32 val;

 val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0);
 dispc_write_reg(dispc, DISPC_OVL_ACCU2_0(plane), val);
}

static void dispc_ovl_set_vid_accu2_1(struct dispc_device *dispc,
          enum omap_plane_id plane, int haccu,
          int vaccu)
{
 u32 val;

 val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0);
 dispc_write_reg(dispc, DISPC_OVL_ACCU2_1(plane), val);
}

static void dispc_ovl_set_scale_param(struct dispc_device *dispc,
          enum omap_plane_id plane,
          u16 orig_width, u16 orig_height,
          u16 out_width, u16 out_height,
          bool five_taps, u8 rotation,
          enum omap_color_component color_comp)
{
 int fir_hinc, fir_vinc;

 fir_hinc = 1024 * orig_width / out_width;
 fir_vinc = 1024 * orig_height / out_height;

 dispc_ovl_set_scale_coef(dispc, plane, fir_hinc, fir_vinc, five_taps,
     color_comp);
 dispc_ovl_set_fir(dispc, plane, fir_hinc, fir_vinc, color_comp);
}

static void dispc_ovl_set_accu_uv(struct dispc_device *dispc,
      enum omap_plane_id plane,
      u16 orig_width, u16 orig_height,
      u16 out_width, u16 out_height,
      bool ilace, u32 fourcc, u8 rotation)
{
 int h_accu2_0, h_accu2_1;
 int v_accu2_0, v_accu2_1;
 int chroma_hinc, chroma_vinc;
 int idx;

 struct accu {
  s8 h0_m, h0_n;
  s8 h1_m, h1_n;
  s8 v0_m, v0_n;
  s8 v1_m, v1_n;
 };

 const struct accu *accu_table;
 const struct accu *accu_val;

 static const struct accu accu_nv12[4] = {
  {  0, 1,  0, 1 , -1, 2, 0, 1 },
  {  1, 2, -3, 4 ,  0, 1, 0, 1 },
  { -1, 1,  0, 1 , -1, 2, 0, 1 },
  { -1, 2, -1, 2 , -1, 1, 0, 1 },
 };

 static const struct accu accu_nv12_ilace[4] = {
  {  0, 1,  0, 1 , -3, 4, -1, 4 },
  { -1, 4, -3, 4 ,  0, 1,  0, 1 },
  { -1, 1,  0, 1 , -1, 4, -3, 4 },
  { -3, 4, -3, 4 , -1, 1,  0, 1 },
 };

 static const struct accu accu_yuv[4] = {
  {  0, 1, 0, 1,  0, 1, 0, 1 },
  {  0, 1, 0, 1,  0, 1, 0, 1 },
  { -1, 1, 0, 1,  0, 1, 0, 1 },
  {  0, 1, 0, 1, -1, 1, 0, 1 },
 };

 /* Note: DSS HW rotates clockwise, DRM_MODE_ROTATE_* counter-clockwise */
 switch (rotation & DRM_MODE_ROTATE_MASK) {
 default:
 case DRM_MODE_ROTATE_0:
  idx = 0;
  break;
 case DRM_MODE_ROTATE_90:
  idx = 3;
  break;
 case DRM_MODE_ROTATE_180:
  idx = 2;
  break;
 case DRM_MODE_ROTATE_270:
  idx = 1;
  break;
 }

 switch (fourcc) {
 case DRM_FORMAT_NV12:
  if (ilace)
   accu_table = accu_nv12_ilace;
  else
   accu_table = accu_nv12;
  break;
 case DRM_FORMAT_YUYV:
 case DRM_FORMAT_UYVY:
  accu_table = accu_yuv;
  break;
 default:
  BUG();
  return;
 }

 accu_val = &accu_table[idx];

 chroma_hinc = 1024 * orig_width / out_width;
 chroma_vinc = 1024 * orig_height / out_height;

 h_accu2_0 = (accu_val->h0_m * chroma_hinc / accu_val->h0_n) % 1024;
 h_accu2_1 = (accu_val->h1_m * chroma_hinc / accu_val->h1_n) % 1024;
 v_accu2_0 = (accu_val->v0_m * chroma_vinc / accu_val->v0_n) % 1024;
 v_accu2_1 = (accu_val->v1_m * chroma_vinc / accu_val->v1_n) % 1024;

 dispc_ovl_set_vid_accu2_0(dispc, plane, h_accu2_0, v_accu2_0);
 dispc_ovl_set_vid_accu2_1(dispc, plane, h_accu2_1, v_accu2_1);
}

static void dispc_ovl_set_scaling_common(struct dispc_device *dispc,
      enum omap_plane_id plane,
      u16 orig_width, u16 orig_height,
      u16 out_width, u16 out_height,
      bool ilace, bool five_taps,
      bool fieldmode, u32 fourcc,
      u8 rotation)
{
 int accu0 = 0;
 int accu1 = 0;
 u32 l;

 dispc_ovl_set_scale_param(dispc, plane, orig_width, orig_height,
      out_width, out_height, five_taps,
      rotation, DISPC_COLOR_COMPONENT_RGB_Y);
 l = dispc_read_reg(dispc, DISPC_OVL_ATTRIBUTES(plane));

 /* RESIZEENABLE and VERTICALTAPS */
 l &= ~((0x3 << 5) | (0x1 << 21));
 l |= (orig_width != out_width) ? (1 << 5) : 0;
 l |= (orig_height != out_height) ? (1 << 6) : 0;
 l |= five_taps ? (1 << 21) : 0;

 /* VRESIZECONF and HRESIZECONF */
 if (dispc_has_feature(dispc, FEAT_RESIZECONF)) {
  l &= ~(0x3 << 7);
  l |= (orig_width <= out_width) ? 0 : (1 << 7);
  l |= (orig_height <= out_height) ? 0 : (1 << 8);
 }

 /* LINEBUFFERSPLIT */
 if (dispc_has_feature(dispc, FEAT_LINEBUFFERSPLIT)) {
  l &= ~(0x1 << 22);
  l |= five_taps ? (1 << 22) : 0;
 }

 dispc_write_reg(dispc, DISPC_OVL_ATTRIBUTES(plane), l);

 /*
 * field 0 = even field = bottom field
 * field 1 = odd field = top field
 */

 if (ilace && !fieldmode) {
  accu1 = 0;
  accu0 = ((1024 * orig_height / out_height) / 2) & 0x3ff;
  if (accu0 >= 1024/2) {
   accu1 = 1024/2;
   accu0 -= accu1;
  }
 }

 dispc_ovl_set_vid_accu0(dispc, plane, 0, accu0);
 dispc_ovl_set_vid_accu1(dispc, plane, 0, accu1);
}

static void dispc_ovl_set_scaling_uv(struct dispc_device *dispc,
         enum omap_plane_id plane,
         u16 orig_width, u16 orig_height,
         u16 out_width, u16 out_height,
         bool ilace, bool five_taps,
         bool fieldmode, u32 fourcc,
         u8 rotation)
{
 int scale_x = out_width != orig_width;
 int scale_y = out_height != orig_height;
 bool chroma_upscale = plane != OMAP_DSS_WB;
 const struct drm_format_info *info;

 info = drm_format_info(fourcc);

 if (!dispc_has_feature(dispc, FEAT_HANDLE_UV_SEPARATE))
  return;

 if (!info->is_yuv) {
  /* reset chroma resampling for RGB formats  */
  if (plane != OMAP_DSS_WB)
   REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES2(plane),
        0, 8, 8);
  return;
 }

 dispc_ovl_set_accu_uv(dispc, plane, orig_width, orig_height, out_width,
         out_height, ilace, fourcc, rotation);

 switch (fourcc) {
 case DRM_FORMAT_NV12:
  if (chroma_upscale) {
   /* UV is subsampled by 2 horizontally and vertically */
   orig_height >>= 1;
   orig_width >>= 1;
  } else {
   /* UV is downsampled by 2 horizontally and vertically */
   orig_height <<= 1;
   orig_width <<= 1;
  }

  break;
 case DRM_FORMAT_YUYV:
 case DRM_FORMAT_UYVY:
  /* For YUV422 with 90/270 rotation, we don't upsample chroma */
  if (!drm_rotation_90_or_270(rotation)) {
   if (chroma_upscale)
    /* UV is subsampled by 2 horizontally */
    orig_width >>= 1;
   else
    /* UV is downsampled by 2 horizontally */
    orig_width <<= 1;
  }

  /* must use FIR for YUV422 if rotated */
  if ((rotation & DRM_MODE_ROTATE_MASK) != DRM_MODE_ROTATE_0)
   scale_x = scale_y = true;

  break;
 default:
  BUG();
  return;
 }

 if (out_width != orig_width)
  scale_x = true;
 if (out_height != orig_height)
  scale_y = true;

 dispc_ovl_set_scale_param(dispc, plane, orig_width, orig_height,
      out_width, out_height, five_taps,
      rotation, DISPC_COLOR_COMPONENT_UV);

 if (plane != OMAP_DSS_WB)
  REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES2(plane),
   (scale_x || scale_y) ? 1 : 0, 8, 8);

 /* set H scaling */
 REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5);
 /* set V scaling */
 REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6);
}

static void dispc_ovl_set_scaling(struct dispc_device *dispc,
      enum omap_plane_id plane,
      u16 orig_width, u16 orig_height,
      u16 out_width, u16 out_height,
      bool ilace, bool five_taps,
      bool fieldmode, u32 fourcc,
      u8 rotation)
{
 BUG_ON(plane == OMAP_DSS_GFX);

 dispc_ovl_set_scaling_common(dispc, plane, orig_width, orig_height,
         out_width, out_height, ilace, five_taps,
         fieldmode, fourcc, rotation);

 dispc_ovl_set_scaling_uv(dispc, plane, orig_width, orig_height,
     out_width, out_height, ilace, five_taps,
     fieldmode, fourcc, rotation);
}

static void dispc_ovl_set_rotation_attrs(struct dispc_device *dispc,
      enum omap_plane_id plane, u8 rotation,
      enum omap_dss_rotation_type rotation_type,
      u32 fourcc)
{
 bool row_repeat = false;
 int vidrot = 0;

 /* Note: DSS HW rotates clockwise, DRM_MODE_ROTATE_* counter-clockwise */
 if (fourcc == DRM_FORMAT_YUYV || fourcc == DRM_FORMAT_UYVY) {

  if (rotation & DRM_MODE_REFLECT_X) {
   switch (rotation & DRM_MODE_ROTATE_MASK) {
   case DRM_MODE_ROTATE_0:
    vidrot = 2;
    break;
   case DRM_MODE_ROTATE_90:
    vidrot = 1;
    break;
   case DRM_MODE_ROTATE_180:
    vidrot = 0;
    break;
   case DRM_MODE_ROTATE_270:
    vidrot = 3;
    break;
   }
  } else {
   switch (rotation & DRM_MODE_ROTATE_MASK) {
   case DRM_MODE_ROTATE_0:
    vidrot = 0;
    break;
   case DRM_MODE_ROTATE_90:
    vidrot = 3;
    break;
   case DRM_MODE_ROTATE_180:
    vidrot = 2;
    break;
   case DRM_MODE_ROTATE_270:
    vidrot = 1;
    break;
   }
  }

  if (drm_rotation_90_or_270(rotation))
   row_repeat = true;
  else
   row_repeat = false;
 }

 /*
 * OMAP4/5 Errata i631:
 * NV12 in 1D mode must use ROTATION=1. Otherwise DSS will fetch extra
 * rows beyond the framebuffer, which may cause OCP error.
 */

 if (fourcc == DRM_FORMAT_NV12 && rotation_type != OMAP_DSS_ROT_TILER)
  vidrot = 1;

 REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12);
 if (dispc_has_feature(dispc, FEAT_ROWREPEATENABLE))
  REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane),
   row_repeat ? 1 : 0, 18, 18);

 if (dispc_ovl_color_mode_supported(dispc, plane, DRM_FORMAT_NV12)) {
  bool doublestride =
   fourcc == DRM_FORMAT_NV12 &&
   rotation_type == OMAP_DSS_ROT_TILER &&
   !drm_rotation_90_or_270(rotation);

  /* DOUBLESTRIDE */
  REG_FLD_MOD(dispc, DISPC_OVL_ATTRIBUTES(plane),
       doublestride, 22, 22);
 }
}

static int color_mode_to_bpp(u32 fourcc)
{
 switch (fourcc) {
 case DRM_FORMAT_NV12:
  return 8;
 case DRM_FORMAT_RGBX4444:
 case DRM_FORMAT_RGB565:
 case DRM_FORMAT_ARGB4444:
 case DRM_FORMAT_YUYV:
 case DRM_FORMAT_UYVY:
 case DRM_FORMAT_RGBA4444:
 case DRM_FORMAT_XRGB4444:
 case DRM_FORMAT_ARGB1555:
 case DRM_FORMAT_XRGB1555:
  return 16;
 case DRM_FORMAT_RGB888:
  return 24;
 case DRM_FORMAT_XRGB8888:
 case DRM_FORMAT_ARGB8888:
 case DRM_FORMAT_RGBA8888:
 case DRM_FORMAT_RGBX8888:
  return 32;
 default:
  BUG();
  return 0;
 }
}

static s32 pixinc(int pixels, u8 ps)
{
 if (pixels == 1)
  return 1;
 else if (pixels > 1)
  return 1 + (pixels - 1) * ps;
 else if (pixels < 0)
  return 1 - (-pixels + 1) * ps;

 BUG();
}

static void calc_offset(u16 screen_width, u16 width,
  u32 fourcc, bool fieldmode, unsigned int field_offset,
  unsigned int *offset0, unsigned int *offset1,
  s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim,
  enum omap_dss_rotation_type rotation_type, u8 rotation)
{
 u8 ps;

 ps = color_mode_to_bpp(fourcc) / 8;

 DSSDBG("scrw %d, width %d\n", screen_width, width);

 if (rotation_type == OMAP_DSS_ROT_TILER &&
     (fourcc == DRM_FORMAT_UYVY || fourcc == DRM_FORMAT_YUYV) &&
     drm_rotation_90_or_270(rotation)) {
  /*
 * HACK: ROW_INC needs to be calculated with TILER units.
 * We get such 'screen_width' that multiplying it with the
 * YUV422 pixel size gives the correct TILER container width.
 * However, 'width' is in pixels and multiplying it with YUV422
 * pixel size gives incorrect result. We thus multiply it here
 * with 2 to match the 32 bit TILER unit size.
 */

  width *= 2;
 }

 /*
 * field 0 = even field = bottom field
 * field 1 = odd field = top field
 */

 *offset0 = field_offset * screen_width * ps;
 *offset1 = 0;

 *row_inc = pixinc(1 + (y_predecim * screen_width - width * x_predecim) +
   (fieldmode ? screen_width : 0), ps);
 if (fourcc == DRM_FORMAT_YUYV || fourcc == DRM_FORMAT_UYVY)
  *pix_inc = pixinc(x_predecim, 2 * ps);
 else
  *pix_inc = pixinc(x_predecim, ps);
}

/*
 * This function is used to avoid synclosts in OMAP3, because of some
 * undocumented horizontal position and timing related limitations.
 */

static int check_horiz_timing_omap3(unsigned long pclk, unsigned long lclk,
  const struct videomode *vm, u16 pos_x,
  u16 width, u16 height, u16 out_width, u16 out_height,
  bool five_taps)
{
 const int ds = DIV_ROUND_UP(height, out_height);
 unsigned long nonactive;
 static const u8 limits[3] = { 8, 10, 20 };
 u64 val, blank;
 int i;

 nonactive = vm->hactive + vm->hfront_porch + vm->hsync_len +
      vm->hback_porch - out_width;

 i = 0;
 if (out_height < height)
  i++;
 if (out_width < width)
  i++;
 blank = div_u64((u64)(vm->hback_porch + vm->hsync_len + vm->hfront_porch) *
   lclk, pclk);
 DSSDBG("blanking period + ppl = %llu (limit = %u)\n", blank, limits[i]);
 if (blank <= limits[i])
  return -EINVAL;

 /* FIXME add checks for 3-tap filter once the limitations are known */
 if (!five_taps)
  return 0;

 /*
 * Pixel data should be prepared before visible display point starts.
 * So, atleast DS-2 lines must have already been fetched by DISPC
 * during nonactive - pos_x period.
 */

 val = div_u64((u64)(nonactive - pos_x) * lclk, pclk);
 DSSDBG("(nonactive - pos_x) * pcd = %llu max(0, DS - 2) * width = %d\n",
  val, max(0, ds - 2) * width);
 if (val < max(0, ds - 2) * width)
  return -EINVAL;

 /*
 * All lines need to be refilled during the nonactive period of which
 * only one line can be loaded during the active period. So, atleast
 * DS - 1 lines should be loaded during nonactive period.
 */

 val =  div_u64((u64)nonactive * lclk, pclk);
 DSSDBG("nonactive * pcd = %llu, max(0, DS - 1) * width = %d\n",
  val, max(0, ds - 1) * width);
 if (val < max(0, ds - 1) * width)
  return -EINVAL;

 return 0;
}

static unsigned long calc_core_clk_five_taps(unsigned long pclk,
  const struct videomode *vm, u16 width,
  u16 height, u16 out_width, u16 out_height,
  u32 fourcc)
{
 u32 core_clk = 0;
 u64 tmp;

 if (height <= out_height && width <= out_width)
  return (unsigned long) pclk;

 if (height > out_height) {
  unsigned int ppl = vm->hactive;

  tmp = (u64)pclk * height * out_width;
  do_div(tmp, 2 * out_height * ppl);
  core_clk = tmp;

  if (height > 2 * out_height) {
   if (ppl == out_width)
    return 0;

   tmp = (u64)pclk * (height - 2 * out_height) * out_width;
   do_div(tmp, 2 * out_height * (ppl - out_width));
   core_clk = max_t(u32, core_clk, tmp);
  }
 }

 if (width > out_width) {
  tmp = (u64)pclk * width;
  do_div(tmp, out_width);
  core_clk = max_t(u32, core_clk, tmp);

  if (fourcc == DRM_FORMAT_XRGB8888)
   core_clk <<= 1;
 }

 return core_clk;
}

static unsigned long calc_core_clk_24xx(unsigned long pclk, u16 width,
  u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
{
 if (height > out_height && width > out_width)
  return pclk * 4;
 else
  return pclk * 2;
}

static unsigned long calc_core_clk_34xx(unsigned long pclk, u16 width,
  u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
{
 unsigned int hf, vf;

 /*
 * FIXME how to determine the 'A' factor
 * for the no downscaling case ?
 */


 if (width > 3 * out_width)
  hf = 4;
 else if (width > 2 * out_width)
  hf = 3;
 else if (width > out_width)
  hf = 2;
 else
  hf = 1;
 if (height > out_height)
  vf = 2;
 else
  vf = 1;

 return pclk * vf * hf;
}

static unsigned long calc_core_clk_44xx(unsigned long pclk, u16 width,
  u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
{
 /*
 * If the overlay/writeback is in mem to mem mode, there are no
 * downscaling limitations with respect to pixel clock, return 1 as
 * required core clock to represent that we have sufficient enough
 * core clock to do maximum downscaling
 */

 if (mem_to_mem)
  return 1;

 if (width > out_width)
  return DIV_ROUND_UP(pclk, out_width) * width;
 else
  return pclk;
}

static int dispc_ovl_calc_scaling_24xx(struct dispc_device *dispc,
           unsigned long pclk, unsigned long lclk,
           const struct videomode *vm,
           u16 width, u16 height,
           u16 out_width, u16 out_height,
           u32 fourcc, bool *five_taps,
           int *x_predecim, int *y_predecim,
           int *decim_x, int *decim_y,
           u16 pos_x, unsigned long *core_clk,
           bool mem_to_mem)
{
 int error;
 u16 in_width, in_height;
 int min_factor = min(*decim_x, *decim_y);
 const int maxsinglelinewidth = dispc->feat->max_line_width;

 *five_taps = false;

 do {
  in_height = height / *decim_y;
  in_width = width / *decim_x;
  *core_clk = dispc->feat->calc_core_clk(pclk, in_width,
    in_height, out_width, out_height, mem_to_mem);
  error = (in_width > maxsinglelinewidth || !*core_clk ||
   *core_clk > dispc_core_clk_rate(dispc));
  if (error) {
   if (*decim_x == *decim_y) {
    *decim_x = min_factor;
    ++*decim_y;
   } else {
    swap(*decim_x, *decim_y);
    if (*decim_x < *decim_y)
     ++*decim_x;
   }
  }
 } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);

 if (error) {
  DSSERR("failed to find scaling settings\n");
  return -EINVAL;
 }

 if (in_width > maxsinglelinewidth) {
  DSSERR("Cannot scale max input width exceeded\n");
  return -EINVAL;
 }
 return 0;
}

static int dispc_ovl_calc_scaling_34xx(struct dispc_device *dispc,
           unsigned long pclk, unsigned long lclk,
           const struct videomode *vm,
           u16 width, u16 height,
           u16 out_width, u16 out_height,
           u32 fourcc, bool *five_taps,
           int *x_predecim, int *y_predecim,
           int *decim_x, int *decim_y,
           u16 pos_x, unsigned long *core_clk,
           bool mem_to_mem)
{
 int error;
 u16 in_width, in_height;
 const int maxsinglelinewidth = dispc->feat->max_line_width;

 do {
  in_height = height / *decim_y;
  in_width = width / *decim_x;
  *five_taps = in_height > out_height;

  if (in_width > maxsinglelinewidth)
   if (in_height > out_height &&
      in_height < out_height * 2)
    *five_taps = false;
again:
  if (*five_taps)
   *core_clk = calc_core_clk_five_taps(pclk, vm,
      in_width, in_height, out_width,
      out_height, fourcc);
  else
   *core_clk = dispc->feat->calc_core_clk(pclk, in_width,
     in_height, out_width, out_height,
     mem_to_mem);

  error = check_horiz_timing_omap3(pclk, lclk, vm,
    pos_x, in_width, in_height, out_width,
    out_height, *five_taps);
  if (error && *five_taps) {
   *five_taps = false;
   goto again;
  }

  error = (error || in_width > maxsinglelinewidth * 2 ||
   (in_width > maxsinglelinewidth && *five_taps) ||
   !*core_clk || *core_clk > dispc_core_clk_rate(dispc));

  if (!error) {
   /* verify that we're inside the limits of scaler */
   if (in_width / 4 > out_width)
     error = 1;

   if (*five_taps) {
    if (in_height / 4 > out_height)
     error = 1;
   } else {
    if (in_height / 2 > out_height)
     error = 1;
   }
  }

  if (error)
   ++*decim_y;
 } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);

 if (error) {
  DSSERR("failed to find scaling settings\n");
  return -EINVAL;
 }

 if (check_horiz_timing_omap3(pclk, lclk, vm, pos_x, in_width,
    in_height, out_width, out_height, *five_taps)) {
   DSSERR("horizontal timing too tight\n");
   return -EINVAL;
 }

 if (in_width > (maxsinglelinewidth * 2)) {
  DSSERR("Cannot setup scaling\n");
  DSSERR("width exceeds maximum width possible\n");
  return -EINVAL;
 }

 if (in_width > maxsinglelinewidth && *five_taps) {
  DSSERR("cannot setup scaling with five taps\n");
  return -EINVAL;
 }
 return 0;
}

static int dispc_ovl_calc_scaling_44xx(struct dispc_device *dispc,
           unsigned long pclk, unsigned long lclk,
           const struct videomode *vm,
           u16 width, u16 height,
           u16 out_width, u16 out_height,
           u32 fourcc, bool *five_taps,
           int *x_predecim, int *y_predecim,
           int *decim_x, int *decim_y,
           u16 pos_x, unsigned long *core_clk,
           bool mem_to_mem)
{
 u16 in_width, in_width_max;
 int decim_x_min = *decim_x;
 u16 in_height = height / *decim_y;
 const int maxsinglelinewidth = dispc->feat->max_line_width;
 const int maxdownscale = dispc->feat->max_downscale;

 if (mem_to_mem) {
  in_width_max = out_width * maxdownscale;
 } else {
  in_width_max = dispc_core_clk_rate(dispc)
        / DIV_ROUND_UP(pclk, out_width);
 }

 *decim_x = DIV_ROUND_UP(width, in_width_max);

 *decim_x = max(*decim_x, decim_x_min);
 if (*decim_x > *x_predecim)
  return -EINVAL;

 do {
  in_width = width / *decim_x;
 } while (*decim_x <= *x_predecim &&
   in_width > maxsinglelinewidth && ++*decim_x);

 if (in_width > maxsinglelinewidth) {
  DSSERR("Cannot scale width exceeds max line width\n");
  return -EINVAL;
 }

 if (*decim_x > 4 && fourcc != DRM_FORMAT_NV12) {
  /*
 * Let's disable all scaling that requires horizontal
 * decimation with higher factor than 4, until we have
 * better estimates of what we can and can not
 * do. However, NV12 color format appears to work Ok
 * with all decimation factors.
 *
 * When decimating horizontally by more that 4 the dss
 * is not able to fetch the data in burst mode. When
 * this happens it is hard to tell if there enough
 * bandwidth. Despite what theory says this appears to
 * be true also for 16-bit color formats.
 */

  DSSERR("Not enough bandwidth, too much downscaling (x-decimation factor %d > 4)\n", *decim_x);

  return -EINVAL;
 }

 *core_clk = dispc->feat->calc_core_clk(pclk, in_width, in_height,
    out_width, out_height, mem_to_mem);
 return 0;
}

enum omap_overlay_caps dispc_ovl_get_caps(struct dispc_device *dispc, enum omap_plane_id plane)
{
 return dispc->feat->overlay_caps[plane];
}

#define DIV_FRAC(dividend, divisor) \
 ((dividend) * 100 / (divisor) - ((dividend) / (divisor) * 100))

static int dispc_ovl_calc_scaling(struct dispc_device *dispc,
      enum omap_plane_id plane,
      unsigned long pclk, unsigned long lclk,
      enum omap_overlay_caps caps,
      const struct videomode *vm,
      u16 width, u16 height,
      u16 out_width, u16 out_height,
      u32 fourcc, bool *five_taps,
      int *x_predecim, int *y_predecim, u16 pos_x,
      enum omap_dss_rotation_type rotation_type,
      bool mem_to_mem)
{
 int maxhdownscale = dispc->feat->max_downscale;
 int maxvdownscale = dispc->feat->max_downscale;
 const int max_decim_limit = 16;
 unsigned long core_clk = 0;
 int decim_x, decim_y, ret;

 if (width == out_width && height == out_height)
  return 0;

 if (dispc->feat->supported_scaler_color_modes) {
  const u32 *modes = dispc->feat->supported_scaler_color_modes;
  unsigned int i;

  for (i = 0; modes[i]; ++i) {
   if (modes[i] == fourcc)
    break;
  }

  if (modes[i] == 0)
   return -EINVAL;
 }

 if (plane == OMAP_DSS_WB) {
  switch (fourcc) {
  case DRM_FORMAT_NV12:
   maxhdownscale = maxvdownscale = 2;
   break;
  case DRM_FORMAT_YUYV:
  case DRM_FORMAT_UYVY:
   maxhdownscale = 2;
   maxvdownscale = 4;
   break;
  default:
   break;
  }
 }
 if (!mem_to_mem && (pclk == 0 || vm->pixelclock == 0)) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=96 G=95

¤ Dauer der Verarbeitung: 0.29 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge