Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/video/fbdev/omap2/omapfb/dss/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 98 kB image not shown  

Quelle  dispc.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/drivers/video/omap2/dss/dispc.c
 *
 * Copyright (C) 2009 Nokia Corporation
 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.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/sizes.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/of.h>
#include <linux/component.h>

#include <video/omapfb_dss.h>

#include "dss.h"
#include "dss_features.h"
#include "dispc.h"

/* 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(idx, start, end) \
 FLD_GET(dispc_read_reg(idx), start, end)

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

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;
 unsigned long max_lcd_pclk;
 unsigned long max_tv_pclk;
 int (*calc_scaling) (unsigned long pclk, unsigned long lclk,
  const struct omap_video_timings *mgr_timings,
  u16 width, u16 height, u16 out_width, u16 out_height,
  enum omap_color_mode color_mode, 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;

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

#define DISPC_MAX_NR_FIFOS 5

static struct {
 struct platform_device *pdev;
 void __iomem    *base;

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

 const struct dispc_features *feat;

 bool is_enabled;

 struct regmap *syscon_pol;
 u32 syscon_pol_offset;

 /* DISPC_CONTROL & DISPC_CONFIG lock*/
 spinlock_t control_lock;
} dispc;

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
 * OMAP_DSS_COLOR_YUV2, OMAP_DSS_COLOR_UYVY, OMAP_DSS_COLOR_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,
};

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

static const struct {
 const char *name;
 u32 vsync_irq;
 u32 framedone_irq;
 u32 sync_lost_irq;
 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,
  .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,
  .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,
  .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,
  .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 },
  },
 },
};

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

static unsigned long dispc_fclk_rate(void);
static unsigned long dispc_core_clk_rate(void);
static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel);
static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel);

static unsigned long dispc_plane_pclk_rate(enum omap_plane plane);
static unsigned long dispc_plane_lclk_rate(enum omap_plane plane);

static inline void dispc_write_reg(const u16 idx, u32 val)
{
 __raw_writel(val, dispc.base + idx);
}

static inline u32 dispc_read_reg(const u16 idx)
{
 return __raw_readl(dispc.base + idx);
}

static u32 mgr_fld_read(enum omap_channel channel, enum mgr_reg_fields regfld)
{
 const struct dispc_reg_field rfld = mgr_desc[channel].reg_desc[regfld];
 return REG_GET(rfld.reg, rfld.high, rfld.low);
}

static void mgr_fld_write(enum omap_channel channel,
     enum mgr_reg_fields regfld, int val) {
 const struct dispc_reg_field rfld = mgr_desc[channel].reg_desc[regfld];
 const bool need_lock = rfld.reg == DISPC_CONTROL || rfld.reg == DISPC_CONFIG;
 unsigned long flags;

 if (need_lock)
  spin_lock_irqsave(&dispc.control_lock, flags);

 REG_FLD_MOD(rfld.reg, val, rfld.high, rfld.low);

 if (need_lock)
  spin_unlock_irqrestore(&dispc.control_lock, flags);
}

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

static void dispc_save_context(void)
{
 int i, j;

 DSSDBG("dispc_save_context\n");

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

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

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

  if (dss_has_feature(FEAT_CPR)) {
   SR(CPR_COEF_R(i));
   SR(CPR_COEF_G(i));
   SR(CPR_COEF_B(i));
  }
 }

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

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

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

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

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

  if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
   SR(OVL_BA0_UV(i));
   SR(OVL_BA1_UV(i));
   SR(OVL_FIR2(i));
   SR(OVL_ACCU2_0(i));
   SR(OVL_ACCU2_1(i));

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

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

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

 if (dss_has_feature(FEAT_CORE_CLK_DIV))
  SR(DIVISOR);

 dispc.ctx_valid = true;

 DSSDBG("context saved\n");
}

static void dispc_restore_context(void)
{
 int i, j;

 DSSDBG("dispc_restore_context\n");

 if (!dispc.ctx_valid)
  return;

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

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

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

  if (dss_has_feature(FEAT_CPR)) {
   RR(CPR_COEF_R(i));
   RR(CPR_COEF_G(i));
   RR(CPR_COEF_B(i));
  }
 }

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

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

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

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

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

  if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
   RR(OVL_BA0_UV(i));
   RR(OVL_BA1_UV(i));
   RR(OVL_FIR2(i));
   RR(OVL_ACCU2_0(i));
   RR(OVL_ACCU2_1(i));

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

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

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

 if (dss_has_feature(FEAT_CORE_CLK_DIV))
  RR(DIVISOR);

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

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

 RR(IRQENABLE);

 DSSDBG("context restored\n");
}

#undef SR
#undef RR

int dispc_runtime_get(void)
{
 int r;

 DSSDBG("dispc_runtime_get\n");

 r = pm_runtime_resume_and_get(&dispc.pdev->dev);
 if (WARN_ON(r < 0))
  return r;
 return 0;
}
EXPORT_SYMBOL(dispc_runtime_get);

void dispc_runtime_put(void)
{
 int r;

 DSSDBG("dispc_runtime_put\n");

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

u32 dispc_mgr_get_vsync_irq(enum omap_channel channel)
{
 return mgr_desc[channel].vsync_irq;
}
EXPORT_SYMBOL(dispc_mgr_get_vsync_irq);

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

 return mgr_desc[channel].framedone_irq;
}
EXPORT_SYMBOL(dispc_mgr_get_framedone_irq);

u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel)
{
 return mgr_desc[channel].sync_lost_irq;
}
EXPORT_SYMBOL(dispc_mgr_get_sync_lost_irq);

bool dispc_mgr_go_busy(enum omap_channel channel)
{
 return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
}
EXPORT_SYMBOL(dispc_mgr_go_busy);

void dispc_mgr_go(enum omap_channel channel)
{
 WARN_ON(!dispc_mgr_is_enabled(channel));
 WARN_ON(dispc_mgr_go_busy(channel));

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

 mgr_fld_write(channel, DISPC_MGR_FLD_GO, 1);
}
EXPORT_SYMBOL(dispc_mgr_go);

static void dispc_ovl_write_firh_reg(enum omap_plane plane, int reg, u32 value)
{
 dispc_write_reg(DISPC_OVL_FIR_COEF_H(plane, reg), value);
}

static void dispc_ovl_write_firhv_reg(enum omap_plane plane, int reg, u32 value)
{
 dispc_write_reg(DISPC_OVL_FIR_COEF_HV(plane, reg), value);
}

static void dispc_ovl_write_firv_reg(enum omap_plane plane, int reg, u32 value)
{
 dispc_write_reg(DISPC_OVL_FIR_COEF_V(plane, reg), value);
}

static void dispc_ovl_write_firh2_reg(enum omap_plane plane, int reg, u32 value)
{
 BUG_ON(plane == OMAP_DSS_GFX);

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

static void dispc_ovl_write_firhv2_reg(enum omap_plane plane, int reg,
  u32 value)
{
 BUG_ON(plane == OMAP_DSS_GFX);

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

static void dispc_ovl_write_firv2_reg(enum omap_plane plane, int reg, u32 value)
{
 BUG_ON(plane == OMAP_DSS_GFX);

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

static void dispc_ovl_set_scale_coef(enum omap_plane 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);

 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(plane, i, h);
   dispc_ovl_write_firhv_reg(plane, i, hv);
  } else {
   dispc_ovl_write_firh2_reg(plane, i, h);
   dispc_ovl_write_firhv2_reg(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(plane, i, v);
   else
    dispc_ovl_write_firv2_reg(plane, i, v);
  }
 }
}


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

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

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

#undef CVAL
}

static void dispc_setup_color_conv_coef(void)
{
 int i;
 int num_ovl = dss_feat_get_num_ovls();
 const struct color_conv_coef ctbl_bt601_5_ovl = {
  /* YUV -> RGB */
  298, 409, 0, 298, -208, -100, 298, 0, 517, 0,
 };
 const struct color_conv_coef ctbl_bt601_5_wb = {
  /* RGB -> YUV */
  66, 129, 25, 112, -94, -18, -38, -74, 112, 0,
 };

 for (i = 1; i < num_ovl; i++)
  dispc_ovl_write_color_conv_coef(i, &ctbl_bt601_5_ovl);

 if (dispc.feat->has_writeback)
  dispc_ovl_write_color_conv_coef(OMAP_DSS_WB, &ctbl_bt601_5_wb);
}

static void dispc_ovl_set_ba0(enum omap_plane plane, u32 paddr)
{
 dispc_write_reg(DISPC_OVL_BA0(plane), paddr);
}

static void dispc_ovl_set_ba1(enum omap_plane plane, u32 paddr)
{
 dispc_write_reg(DISPC_OVL_BA1(plane), paddr);
}

static void dispc_ovl_set_ba0_uv(enum omap_plane plane, u32 paddr)
{
 dispc_write_reg(DISPC_OVL_BA0_UV(plane), paddr);
}

static void dispc_ovl_set_ba1_uv(enum omap_plane plane, u32 paddr)
{
 dispc_write_reg(DISPC_OVL_BA1_UV(plane), paddr);
}

static void dispc_ovl_set_pos(enum omap_plane 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_OVL_POSITION(plane), val);
}

static void dispc_ovl_set_input_size(enum omap_plane 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_OVL_SIZE(plane), val);
 else
  dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val);
}

static void dispc_ovl_set_output_size(enum omap_plane 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_OVL_PICTURE_SIZE(plane), val);
 else
  dispc_write_reg(DISPC_OVL_SIZE(plane), val);
}

static void dispc_ovl_set_zorder(enum omap_plane plane,
  enum omap_overlay_caps caps, u8 zorder)
{
 if ((caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
  return;

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

static void dispc_ovl_enable_zorder_planes(void)
{
 int i;

 if (!dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
  return;

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

static void dispc_ovl_set_pre_mult_alpha(enum omap_plane plane,
  enum omap_overlay_caps caps, bool enable)
{
 if ((caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0)
  return;

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

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

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

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

static void dispc_ovl_set_pix_inc(enum omap_plane plane, s32 inc)
{
 dispc_write_reg(DISPC_OVL_PIXEL_INC(plane), inc);
}

static void dispc_ovl_set_row_inc(enum omap_plane plane, s32 inc)
{
 dispc_write_reg(DISPC_OVL_ROW_INC(plane), inc);
}

static void dispc_ovl_set_color_mode(enum omap_plane plane,
  enum omap_color_mode color_mode)
{
 u32 m = 0;
 if (plane != OMAP_DSS_GFX) {
  switch (color_mode) {
  case OMAP_DSS_COLOR_NV12:
   m = 0x0; break;
  case OMAP_DSS_COLOR_RGBX16:
   m = 0x1; break;
  case OMAP_DSS_COLOR_RGBA16:
   m = 0x2; break;
  case OMAP_DSS_COLOR_RGB12U:
   m = 0x4; break;
  case OMAP_DSS_COLOR_ARGB16:
   m = 0x5; break;
  case OMAP_DSS_COLOR_RGB16:
   m = 0x6; break;
  case OMAP_DSS_COLOR_ARGB16_1555:
   m = 0x7; break;
  case OMAP_DSS_COLOR_RGB24U:
   m = 0x8; break;
  case OMAP_DSS_COLOR_RGB24P:
   m = 0x9; break;
  case OMAP_DSS_COLOR_YUV2:
   m = 0xa; break;
  case OMAP_DSS_COLOR_UYVY:
   m = 0xb; break;
  case OMAP_DSS_COLOR_ARGB32:
   m = 0xc; break;
  case OMAP_DSS_COLOR_RGBA32:
   m = 0xd; break;
  case OMAP_DSS_COLOR_RGBX32:
   m = 0xe; break;
  case OMAP_DSS_COLOR_XRGB16_1555:
   m = 0xf; break;
  default:
   BUG(); return;
  }
 } else {
  switch (color_mode) {
  case OMAP_DSS_COLOR_CLUT1:
   m = 0x0; break;
  case OMAP_DSS_COLOR_CLUT2:
   m = 0x1; break;
  case OMAP_DSS_COLOR_CLUT4:
   m = 0x2; break;
  case OMAP_DSS_COLOR_CLUT8:
   m = 0x3; break;
  case OMAP_DSS_COLOR_RGB12U:
   m = 0x4; break;
  case OMAP_DSS_COLOR_ARGB16:
   m = 0x5; break;
  case OMAP_DSS_COLOR_RGB16:
   m = 0x6; break;
  case OMAP_DSS_COLOR_ARGB16_1555:
   m = 0x7; break;
  case OMAP_DSS_COLOR_RGB24U:
   m = 0x8; break;
  case OMAP_DSS_COLOR_RGB24P:
   m = 0x9; break;
  case OMAP_DSS_COLOR_RGBX16:
   m = 0xa; break;
  case OMAP_DSS_COLOR_RGBA16:
   m = 0xb; break;
  case OMAP_DSS_COLOR_ARGB32:
   m = 0xc; break;
  case OMAP_DSS_COLOR_RGBA32:
   m = 0xd; break;
  case OMAP_DSS_COLOR_RGBX32:
   m = 0xe; break;
  case OMAP_DSS_COLOR_XRGB16_1555:
   m = 0xf; break;
  default:
   BUG(); return;
  }
 }

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

static void dispc_ovl_configure_burst_type(enum omap_plane plane,
  enum omap_dss_rotation_type rotation_type)
{
 if (!dss_has_feature(FEAT_BURST_2D))
  return;

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

void dispc_ovl_set_channel_out(enum omap_plane 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_OVL_ATTRIBUTES(plane));
 if (dss_has_feature(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 (dss_has_feature(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_OVL_ATTRIBUTES(plane), val);
}
EXPORT_SYMBOL(dispc_ovl_set_channel_out);

static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane 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_OVL_ATTRIBUTES(plane));

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

 if (!dss_has_feature(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(enum omap_plane plane,
  enum omap_burst_size burst_size)
{
 static const unsigned shifts[] = { 6, 14, 14, 14, 14, };
 int shift;

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

static void dispc_configure_burst_sizes(void)
{
 int i;
 const int burst_size = BURST_SIZE_X8;

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

static u32 dispc_ovl_get_burst_size(enum omap_plane plane)
{
 unsigned unit = dss_feat_get_burst_size_unit();
 /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */
 return unit * 8;
}

void dispc_enable_gamma_table(bool enable)
{
 /*
 * This is partially implemented to support only disabling of
 * the gamma table.
 */

 if (enable) {
  DSSWARN("Gamma table enabling for TV not yet supported");
  return;
 }

 REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9);
}

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

 mgr_fld_write(channel, DISPC_MGR_FLD_CPR, enable);
}

static void dispc_mgr_set_cpr_coef(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_CPR_COEF_R(channel), coef_r);
 dispc_write_reg(DISPC_CPR_COEF_G(channel), coef_g);
 dispc_write_reg(DISPC_CPR_COEF_B(channel), coef_b);
}

static void dispc_ovl_set_vid_color_conv(enum omap_plane plane, bool enable)
{
 u32 val;

 BUG_ON(plane == OMAP_DSS_GFX);

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

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

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

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

static void dispc_mgr_set_size(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_SIZE_MGR(channel), val);
}

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

 unit = dss_feat_get_buffer_size_unit();

 dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end);

 for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) {
  size = REG_GET(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_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_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 < dss_feat_get_num_ovls(); ++i) {
  u32 low, high;
  const bool use_fifomerge = false;
  const bool manual_update = false;

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

  dispc_ovl_set_fifo_threshold(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(OMAP_DSS_WB, &low, &high,
   use_fifomerge, manual_update);

  dispc_ovl_set_fifo_threshold(OMAP_DSS_WB, low, high);
 }
}

static u32 dispc_ovl_get_fifo_size(enum omap_plane 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(enum omap_plane plane, u32 low, u32 high)
{
 u8 hi_start, hi_end, lo_start, lo_end;
 u32 unit;

 unit = dss_feat_get_buffer_size_unit();

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

 low /= unit;
 high /= unit;

 dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end);
 dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end);

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

 dispc_write_reg(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 (dss_has_feature(FEAT_PRELOAD) && dispc.feat->set_max_preload &&
   plane != OMAP_DSS_WB)
  dispc_write_reg(DISPC_OVL_PRELOAD(plane), min(high, 0xfffu));
}

void dispc_ovl_compute_fifo_thresholds(enum omap_plane 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 buf_unit = dss_feat_get_buffer_size_unit();
 unsigned ovl_fifo_size, total_fifo_size, burst_size;
 int i;

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

 if (use_fifomerge) {
  total_fifo_size = 0;
  for (i = 0; i < dss_feat_get_num_ovls(); ++i)
   total_fifo_size += dispc_ovl_get_fifo_size(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 && dss_has_feature(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(enum omap_plane plane, bool enable)
{
 int bit;

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

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

static void dispc_ovl_set_mflag_threshold(enum omap_plane plane,
 int low, int high)
{
 dispc_write_reg(DISPC_OVL_MFLAG_THRESHOLD(plane),
  FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0));
}

static void dispc_init_mflag(void)
{
 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_GLOBAL_MFLAG_ATTRIBUTE,
  (1 << 0) | /* MFLAG_CTRL = force always on */
  (0 << 2)); /* MFLAG_START = disable */

 for (i = 0; i < dss_feat_get_num_ovls(); ++i) {
  u32 size = dispc_ovl_get_fifo_size(i);
  u32 unit = dss_feat_get_buffer_size_unit();
  u32 low, high;

  dispc_ovl_set_mflag(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(i, low, high);
 }

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

  dispc_ovl_set_mflag(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(OMAP_DSS_WB, low, high);
 }
}

static void dispc_ovl_set_fir(enum omap_plane 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;

  dss_feat_get_reg_field(FEAT_REG_FIRHINC,
     &hinc_start, &hinc_end);
  dss_feat_get_reg_field(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_OVL_FIR(plane), val);
 } else {
  val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0);
  dispc_write_reg(DISPC_OVL_FIR2(plane), val);
 }
}

static void dispc_ovl_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu)
{
 u32 val;
 u8 hor_start, hor_end, vert_start, vert_end;

 dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end);
 dss_feat_get_reg_field(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_OVL_ACCU0(plane), val);
}

static void dispc_ovl_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu)
{
 u32 val;
 u8 hor_start, hor_end, vert_start, vert_end;

 dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end);
 dss_feat_get_reg_field(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_OVL_ACCU1(plane), val);
}

static void dispc_ovl_set_vid_accu2_0(enum omap_plane plane, int haccu,
  int vaccu)
{
 u32 val;

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

static void dispc_ovl_set_vid_accu2_1(enum omap_plane plane, int haccu,
  int vaccu)
{
 u32 val;

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

static void dispc_ovl_set_scale_param(enum omap_plane 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(plane, fir_hinc, fir_vinc, five_taps,
    color_comp);
 dispc_ovl_set_fir(plane, fir_hinc, fir_vinc, color_comp);
}

static void dispc_ovl_set_accu_uv(enum omap_plane plane,
  u16 orig_width, u16 orig_height, u16 out_width, u16 out_height,
  bool ilace, enum omap_color_mode color_mode, 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 },
 };

 switch (rotation) {
 case OMAP_DSS_ROT_0:
  idx = 0;
  break;
 case OMAP_DSS_ROT_90:
  idx = 1;
  break;
 case OMAP_DSS_ROT_180:
  idx = 2;
  break;
 case OMAP_DSS_ROT_270:
  idx = 3;
  break;
 default:
  BUG();
  return;
 }

 switch (color_mode) {
 case OMAP_DSS_COLOR_NV12:
  if (ilace)
   accu_table = accu_nv12_ilace;
  else
   accu_table = accu_nv12;
  break;
 case OMAP_DSS_COLOR_YUV2:
 case OMAP_DSS_COLOR_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(plane, h_accu2_0, v_accu2_0);
 dispc_ovl_set_vid_accu2_1(plane, h_accu2_1, v_accu2_1);
}

static void dispc_ovl_set_scaling_common(enum omap_plane plane,
  u16 orig_width, u16 orig_height,
  u16 out_width, u16 out_height,
  bool ilace, bool five_taps,
  bool fieldmode, enum omap_color_mode color_mode,
  u8 rotation)
{
 int accu0 = 0;
 int accu1 = 0;
 u32 l;

 dispc_ovl_set_scale_param(plane, orig_width, orig_height,
    out_width, out_height, five_taps,
    rotation, DISPC_COLOR_COMPONENT_RGB_Y);
 l = dispc_read_reg(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 (dss_has_feature(FEAT_RESIZECONF)) {
  l &= ~(0x3 << 7);
  l |= (orig_width <= out_width) ? 0 : (1 << 7);
  l |= (orig_height <= out_height) ? 0 : (1 << 8);
 }

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

 dispc_write_reg(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(plane, 0, accu0);
 dispc_ovl_set_vid_accu1(plane, 0, accu1);
}

static void dispc_ovl_set_scaling_uv(enum omap_plane plane,
  u16 orig_width, u16 orig_height,
  u16 out_width, u16 out_height,
  bool ilace, bool five_taps,
  bool fieldmode, enum omap_color_mode color_mode,
  u8 rotation)
{
 int scale_x = out_width != orig_width;
 int scale_y = out_height != orig_height;
 bool chroma_upscale = plane != OMAP_DSS_WB;

 if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE))
  return;
 if ((color_mode != OMAP_DSS_COLOR_YUV2 &&
   color_mode != OMAP_DSS_COLOR_UYVY &&
   color_mode != OMAP_DSS_COLOR_NV12)) {
  /* reset chroma resampling for RGB formats  */
  if (plane != OMAP_DSS_WB)
   REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8);
  return;
 }

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

 switch (color_mode) {
 case OMAP_DSS_COLOR_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 OMAP_DSS_COLOR_YUV2:
 case OMAP_DSS_COLOR_UYVY:
  /* For YUV422 with 90/270 rotation, we don't upsample chroma */
  if (rotation == OMAP_DSS_ROT_0 ||
    rotation == OMAP_DSS_ROT_180) {
   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 != OMAP_DSS_ROT_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(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_OVL_ATTRIBUTES2(plane),
   (scale_x || scale_y) ? 1 : 0, 8, 8);

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

static void dispc_ovl_set_scaling(enum omap_plane plane,
  u16 orig_width, u16 orig_height,
  u16 out_width, u16 out_height,
  bool ilace, bool five_taps,
  bool fieldmode, enum omap_color_mode color_mode,
  u8 rotation)
{
 BUG_ON(plane == OMAP_DSS_GFX);

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

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

static void dispc_ovl_set_rotation_attrs(enum omap_plane plane, u8 rotation,
  enum omap_dss_rotation_type rotation_type,
  bool mirroring, enum omap_color_mode color_mode)
{
 bool row_repeat = false;
 int vidrot = 0;

 if (color_mode == OMAP_DSS_COLOR_YUV2 ||
   color_mode == OMAP_DSS_COLOR_UYVY) {

  if (mirroring) {
   switch (rotation) {
   case OMAP_DSS_ROT_0:
    vidrot = 2;
    break;
   case OMAP_DSS_ROT_90:
    vidrot = 1;
    break;
   case OMAP_DSS_ROT_180:
    vidrot = 0;
    break;
   case OMAP_DSS_ROT_270:
    vidrot = 3;
    break;
   }
  } else {
   switch (rotation) {
   case OMAP_DSS_ROT_0:
    vidrot = 0;
    break;
   case OMAP_DSS_ROT_90:
    vidrot = 1;
    break;
   case OMAP_DSS_ROT_180:
    vidrot = 2;
    break;
   case OMAP_DSS_ROT_270:
    vidrot = 3;
    break;
   }
  }

  if (rotation == OMAP_DSS_ROT_90 || rotation == OMAP_DSS_ROT_270)
   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 (color_mode == OMAP_DSS_COLOR_NV12 &&
   rotation_type != OMAP_DSS_ROT_TILER)
  vidrot = 1;

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

 if (color_mode == OMAP_DSS_COLOR_NV12) {
  bool doublestride = (rotation_type == OMAP_DSS_ROT_TILER) &&
     (rotation == OMAP_DSS_ROT_0 ||
     rotation == OMAP_DSS_ROT_180);
  /* DOUBLESTRIDE */
  REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), doublestride, 22, 22);
 }

}

static int color_mode_to_bpp(enum omap_color_mode color_mode)
{
 switch (color_mode) {
 case OMAP_DSS_COLOR_CLUT1:
  return 1;
 case OMAP_DSS_COLOR_CLUT2:
  return 2;
 case OMAP_DSS_COLOR_CLUT4:
  return 4;
 case OMAP_DSS_COLOR_CLUT8:
 case OMAP_DSS_COLOR_NV12:
  return 8;
 case OMAP_DSS_COLOR_RGB12U:
 case OMAP_DSS_COLOR_RGB16:
 case OMAP_DSS_COLOR_ARGB16:
 case OMAP_DSS_COLOR_YUV2:
 case OMAP_DSS_COLOR_UYVY:
 case OMAP_DSS_COLOR_RGBA16:
 case OMAP_DSS_COLOR_RGBX16:
 case OMAP_DSS_COLOR_ARGB16_1555:
 case OMAP_DSS_COLOR_XRGB16_1555:
  return 16;
 case OMAP_DSS_COLOR_RGB24P:
  return 24;
 case OMAP_DSS_COLOR_RGB24U:
 case OMAP_DSS_COLOR_ARGB32:
 case OMAP_DSS_COLOR_RGBA32:
 case OMAP_DSS_COLOR_RGBX32:
  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;
 else
  BUG();
 return 0;
}

static void calc_vrfb_rotation_offset(u8 rotation, bool mirror,
  u16 screen_width,
  u16 width, u16 height,
  enum omap_color_mode color_mode, bool fieldmode,
  unsigned int field_offset,
  unsigned *offset0, unsigned *offset1,
  s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim)
{
 u8 ps;

 /* FIXME CLUT formats */
 switch (color_mode) {
 case OMAP_DSS_COLOR_CLUT1:
 case OMAP_DSS_COLOR_CLUT2:
 case OMAP_DSS_COLOR_CLUT4:
 case OMAP_DSS_COLOR_CLUT8:
  BUG();
  return;
 case OMAP_DSS_COLOR_YUV2:
 case OMAP_DSS_COLOR_UYVY:
  ps = 4;
  break;
 default:
  ps = color_mode_to_bpp(color_mode) / 8;
  break;
 }

 DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width,
   width, height);

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

 switch (rotation + mirror * 4) {
 case OMAP_DSS_ROT_0:
 case OMAP_DSS_ROT_180:
  /*
 * If the pixel format is YUV or UYVY divide the width
 * of the image by 2 for 0 and 180 degree rotation.
 */

  if (color_mode == OMAP_DSS_COLOR_YUV2 ||
   color_mode == OMAP_DSS_COLOR_UYVY)
   width = width >> 1;
  fallthrough;
 case OMAP_DSS_ROT_90:
 case OMAP_DSS_ROT_270:
  *offset1 = 0;
  if (field_offset)
   *offset0 = field_offset * screen_width * ps;
  else
   *offset0 = 0;

  *row_inc = pixinc(1 +
   (y_predecim * screen_width - x_predecim * width) +
   (fieldmode ? screen_width : 0), ps);
  *pix_inc = pixinc(x_predecim, ps);
  break;

 case OMAP_DSS_ROT_0 + 4:
 case OMAP_DSS_ROT_180 + 4:
  /* If the pixel format is YUV or UYVY divide the width
 * of the image by 2  for 0 degree and 180 degree
 */

  if (color_mode == OMAP_DSS_COLOR_YUV2 ||
   color_mode == OMAP_DSS_COLOR_UYVY)
   width = width >> 1;
  fallthrough;
 case OMAP_DSS_ROT_90 + 4:
 case OMAP_DSS_ROT_270 + 4:
  *offset1 = 0;
  if (field_offset)
   *offset0 = field_offset * screen_width * ps;
  else
   *offset0 = 0;
  *row_inc = pixinc(1 -
   (y_predecim * screen_width + x_predecim * width) -
   (fieldmode ? screen_width : 0), ps);
  *pix_inc = pixinc(x_predecim, ps);
  break;

 default:
  BUG();
  return;
 }
}

static void calc_dma_rotation_offset(u8 rotation, bool mirror,
  u16 screen_width,
  u16 width, u16 height,
  enum omap_color_mode color_mode, bool fieldmode,
  unsigned int field_offset,
  unsigned *offset0, unsigned *offset1,
  s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim)
{
 u8 ps;
 u16 fbw, fbh;

 /* FIXME CLUT formats */
 switch (color_mode) {
 case OMAP_DSS_COLOR_CLUT1:
 case OMAP_DSS_COLOR_CLUT2:
 case OMAP_DSS_COLOR_CLUT4:
 case OMAP_DSS_COLOR_CLUT8:
  BUG();
  return;
 default:
  ps = color_mode_to_bpp(color_mode) / 8;
  break;
 }

 DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width,
   width, height);

 /* width & height are overlay sizes, convert to fb sizes */

 if (rotation == OMAP_DSS_ROT_0 || rotation == OMAP_DSS_ROT_180) {
  fbw = width;
  fbh = height;
 } else {
  fbw = height;
  fbh = width;
 }

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

 switch (rotation + mirror * 4) {
 case OMAP_DSS_ROT_0:
  *offset1 = 0;
  if (field_offset)
   *offset0 = *offset1 + field_offset * screen_width * ps;
  else
   *offset0 = *offset1;
  *row_inc = pixinc(1 +
   (y_predecim * screen_width - fbw * x_predecim) +
   (fieldmode ? screen_width : 0), ps);
  if (color_mode == OMAP_DSS_COLOR_YUV2 ||
   color_mode == OMAP_DSS_COLOR_UYVY)
   *pix_inc = pixinc(x_predecim, 2 * ps);
  else
   *pix_inc = pixinc(x_predecim, ps);
  break;
 case OMAP_DSS_ROT_90:
  *offset1 = screen_width * (fbh - 1) * ps;
  if (field_offset)
   *offset0 = *offset1 + field_offset * ps;
  else
   *offset0 = *offset1;
  *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) +
    y_predecim + (fieldmode ? 1 : 0), ps);
  *pix_inc = pixinc(-x_predecim * screen_width, ps);
  break;
 case OMAP_DSS_ROT_180:
  *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps;
  if (field_offset)
   *offset0 = *offset1 - field_offset * screen_width * ps;
  else
   *offset0 = *offset1;
  *row_inc = pixinc(-1 -
   (y_predecim * screen_width - fbw * x_predecim) -
   (fieldmode ? screen_width : 0), ps);
  if (color_mode == OMAP_DSS_COLOR_YUV2 ||
   color_mode == OMAP_DSS_COLOR_UYVY)
   *pix_inc = pixinc(-x_predecim, 2 * ps);
  else
   *pix_inc = pixinc(-x_predecim, ps);
  break;
 case OMAP_DSS_ROT_270:
  *offset1 = (fbw - 1) * ps;
  if (field_offset)
   *offset0 = *offset1 - field_offset * ps;
  else
   *offset0 = *offset1;
  *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) -
    y_predecim - (fieldmode ? 1 : 0), ps);
  *pix_inc = pixinc(x_predecim * screen_width, ps);
  break;

 /* mirroring */
 case OMAP_DSS_ROT_0 + 4:
  *offset1 = (fbw - 1) * ps;
  if (field_offset)
   *offset0 = *offset1 + field_offset * screen_width * ps;
  else
   *offset0 = *offset1;
  *row_inc = pixinc(y_predecim * screen_width * 2 - 1 +
    (fieldmode ? screen_width : 0),
    ps);
  if (color_mode == OMAP_DSS_COLOR_YUV2 ||
   color_mode == OMAP_DSS_COLOR_UYVY)
   *pix_inc = pixinc(-x_predecim, 2 * ps);
  else
   *pix_inc = pixinc(-x_predecim, ps);
  break;

 case OMAP_DSS_ROT_90 + 4:
  *offset1 = 0;
  if (field_offset)
   *offset0 = *offset1 + field_offset * ps;
  else
   *offset0 = *offset1;
  *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) +
    y_predecim + (fieldmode ? 1 : 0),
    ps);
  *pix_inc = pixinc(x_predecim * screen_width, ps);
  break;

 case OMAP_DSS_ROT_180 + 4:
  *offset1 = screen_width * (fbh - 1) * ps;
  if (field_offset)
   *offset0 = *offset1 - field_offset * screen_width * ps;
  else
   *offset0 = *offset1;
  *row_inc = pixinc(1 - y_predecim * screen_width * 2 -
    (fieldmode ? screen_width : 0),
    ps);
  if (color_mode == OMAP_DSS_COLOR_YUV2 ||
   color_mode == OMAP_DSS_COLOR_UYVY)
   *pix_inc = pixinc(x_predecim, 2 * ps);
  else
   *pix_inc = pixinc(x_predecim, ps);
  break;

 case OMAP_DSS_ROT_270 + 4:
  *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps;
  if (field_offset)
   *offset0 = *offset1 - field_offset * ps;
  else
   *offset0 = *offset1;
  *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) -
    y_predecim - (fieldmode ? 1 : 0),
    ps);
  *pix_inc = pixinc(-x_predecim * screen_width, ps);
  break;

 default:
  BUG();
  return;
 }
}

static void calc_tiler_rotation_offset(u16 screen_width, u16 width,
  enum omap_color_mode color_mode, bool fieldmode,
  unsigned int field_offset, unsigned *offset0, unsigned *offset1,
  s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim)
{
 u8 ps;

 switch (color_mode) {
 case OMAP_DSS_COLOR_CLUT1:
 case OMAP_DSS_COLOR_CLUT2:
 case OMAP_DSS_COLOR_CLUT4:
 case OMAP_DSS_COLOR_CLUT8:
  BUG();
  return;
 default:
  ps = color_mode_to_bpp(color_mode) / 8;
  break;
 }

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

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

 *offset1 = 0;
 if (field_offset)
  *offset0 = *offset1 + field_offset * screen_width * ps;
 else
  *offset0 = *offset1;
 *row_inc = pixinc(1 + (y_predecim * screen_width - width * x_predecim) +
   (fieldmode ? screen_width : 0), ps);
 if (color_mode == OMAP_DSS_COLOR_YUV2 ||
  color_mode == OMAP_DSS_COLOR_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 omap_video_timings *t, 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 = t->x_res + t->hfp + t->hsw + t->hbp - out_width;

 i = 0;
 if (out_height < height)
  i++;
 if (out_width < width)
  i++;
 blank = div_u64((u64)(t->hbp + t->hsw + t->hfp) * 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 omap_video_timings *mgr_timings, u16 width,
  u16 height, u16 out_width, u16 out_height,
  enum omap_color_mode color_mode)
{
 u32 core_clk = 0;
 u64 tmp;

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

 if (height > out_height) {
  unsigned int ppl = mgr_timings->x_res;

  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 (color_mode == OMAP_DSS_COLOR_RGB24U)
   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(unsigned long pclk, unsigned long lclk,
  const struct omap_video_timings *mgr_timings,
  u16 width, u16 height, u16 out_width, u16 out_height,
  enum omap_color_mode color_mode, 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 =
   dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);

 *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());
  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");
  return -EINVAL;
 }
 return 0;
}

static int dispc_ovl_calc_scaling_34xx(unsigned long pclk, unsigned long lclk,
  const struct omap_video_timings *mgr_timings,
  u16 width, u16 height, u16 out_width, u16 out_height,
  enum omap_color_mode color_mode, 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 =
   dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);

 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, mgr_timings,
      in_width, in_height, out_width,
      out_height, color_mode);
  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, mgr_timings,
    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());

  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, mgr_timings, 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");
  DSSERR("width exceeds maximum width possible");
  return -EINVAL;
 }

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

static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk,
  const struct omap_video_timings *mgr_timings,
  u16 width, u16 height, u16 out_width, u16 out_height,
  enum omap_color_mode color_mode, 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 =
    dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
 const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE);

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

 *decim_x = DIV_ROUND_UP(width, in_width_max);

 *decim_x = *decim_x > decim_x_min ? *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");
  return -EINVAL;
 }

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

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

static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk,
  enum omap_overlay_caps caps,
  const struct omap_video_timings *mgr_timings,
  u16 width, u16 height, u16 out_width, u16 out_height,
  enum omap_color_mode color_mode, bool *five_taps,
  int *x_predecim, int *y_predecim, u16 pos_x,
  enum omap_dss_rotation_type rotation_type, bool mem_to_mem)
{
 const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_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 (!mem_to_mem && (pclk == 0 || mgr_timings->pixelclock == 0)) {
  DSSERR("cannot calculate scaling settings: pclk is zero\n");
  return -EINVAL;
 }

 if ((caps & OMAP_DSS_OVL_CAP_SCALE) == 0)
  return -EINVAL;

 if (mem_to_mem) {
  *x_predecim = *y_predecim = 1;
 } else {
  *x_predecim = max_decim_limit;
  *y_predecim = (rotation_type == OMAP_DSS_ROT_TILER &&
    dss_has_feature(FEAT_BURST_2D)) ?
    2 : max_decim_limit;
 }

 if (color_mode == OMAP_DSS_COLOR_CLUT1 ||
     color_mode == OMAP_DSS_COLOR_CLUT2 ||
     color_mode == OMAP_DSS_COLOR_CLUT4 ||
     color_mode == OMAP_DSS_COLOR_CLUT8) {
  *x_predecim = 1;
  *y_predecim = 1;
  *five_taps = false;
  return 0;
 }

 decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxdownscale);
 decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxdownscale);

 if (decim_x > *x_predecim || out_width > width * 8)
  return -EINVAL;

 if (decim_y > *y_predecim || out_height > height * 8)
  return -EINVAL;

 ret = dispc.feat->calc_scaling(pclk, lclk, mgr_timings, width, height,
  out_width, out_height, color_mode, five_taps,
  x_predecim, y_predecim, &decim_x, &decim_y, pos_x, &core_clk,
  mem_to_mem);
 if (ret)
  return ret;

 DSSDBG("%dx%d -> %dx%d (%d.%02d x %d.%02d), decim %dx%d %dx%d (%d.%02d x %d.%02d), taps %d, req clk %lu, cur clk %lu\n",
  width, height,
  out_width, out_height,
  out_width / width, DIV_FRAC(out_width, width),
  out_height / height, DIV_FRAC(out_height, height),

  decim_x, decim_y,
  width / decim_x, height / decim_y,
  out_width / (width / decim_x), DIV_FRAC(out_width, width / decim_x),
  out_height / (height / decim_y), DIV_FRAC(out_height, height / decim_y),

  *five_taps ? 5 : 3,
  core_clk, dispc_core_clk_rate());

 if (!core_clk || core_clk > dispc_core_clk_rate()) {
  DSSERR("failed to set up scaling, "
   "required core clk rate = %lu Hz, "
   "current core clk rate = %lu Hz\n",
   core_clk, dispc_core_clk_rate());
  return -EINVAL;
 }

 *x_predecim = decim_x;
 *y_predecim = decim_y;
 return 0;
}

int dispc_ovl_check(enum omap_plane plane, enum omap_channel channel,
  const struct omap_overlay_info *oi,
  const struct omap_video_timings *timings,
  int *x_predecim, int *y_predecim)
{
 enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane);
 bool five_taps = true;
 bool fieldmode = false;
 u16 in_height = oi->height;
 u16 in_width = oi->width;
 bool ilace = timings->interlace;
 u16 out_width, out_height;
 int pos_x = oi->pos_x;
 unsigned long pclk = dispc_mgr_pclk_rate(channel);
 unsigned long lclk = dispc_mgr_lclk_rate(channel);

 out_width = oi->out_width == 0 ? oi->width : oi->out_width;
 out_height = oi->out_height == 0 ? oi->height : oi->out_height;

 if (ilace && oi->height == out_height)
  fieldmode = true;

 if (ilace) {
  if (fieldmode)
   in_height /= 2;
  out_height /= 2;

  DSSDBG("adjusting for ilace: height %d, out_height %d\n",
    in_height, out_height);
 }

 if (!dss_feat_color_mode_supported(plane, oi->color_mode))
  return -EINVAL;

 return dispc_ovl_calc_scaling(pclk, lclk, caps, timings, in_width,
   in_height, out_width, out_height, oi->color_mode,
   &five_taps, x_predecim, y_predecim, pos_x,
   oi->rotation_type, false);
}
EXPORT_SYMBOL(dispc_ovl_check);

static int dispc_ovl_setup_common(enum omap_plane plane,
  enum omap_overlay_caps caps, u32 paddr, u32 p_uv_addr,
  u16 screen_width, int pos_x, int pos_y, u16 width, u16 height,
  u16 out_width, u16 out_height, enum omap_color_mode color_mode,
  u8 rotation, bool mirror, u8 zorder, u8 pre_mult_alpha,
  u8 global_alpha, enum omap_dss_rotation_type rotation_type,
  bool replication, const struct omap_video_timings *mgr_timings,
  bool mem_to_mem)
{
 bool five_taps = true;
 bool fieldmode = false;
 int r, cconv = 0;
 unsigned offset0, offset1;
 s32 row_inc;
 s32 pix_inc;
 u16 frame_width, frame_height;
 unsigned int field_offset = 0;
 u16 in_height = height;
 u16 in_width = width;
 int x_predecim = 1, y_predecim = 1;
 bool ilace = mgr_timings->interlace;
 unsigned long pclk = dispc_plane_pclk_rate(plane);
 unsigned long lclk = dispc_plane_lclk_rate(plane);

 if (paddr == 0 && rotation_type != OMAP_DSS_ROT_TILER)
  return -EINVAL;

 switch (color_mode) {
 case OMAP_DSS_COLOR_YUV2:
 case OMAP_DSS_COLOR_UYVY:
 case OMAP_DSS_COLOR_NV12:
  if (in_width & 1) {
   DSSERR("input width %d is not even for YUV format\n",
    in_width);
   return -EINVAL;
  }
  break;

 default:
  break;
 }

 out_width = out_width == 0 ? width : out_width;
 out_height = out_height == 0 ? height : out_height;

 if (ilace && height == out_height)
  fieldmode = true;

 if (ilace) {
  if (fieldmode)
   in_height /= 2;
--> --------------------

--> maximum size reached

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

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

¤ Dauer der Verarbeitung: 0.35 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.